From e3270b14cd725b8ff6f4852a9334c72d2b988e21 Mon Sep 17 00:00:00 2001 From: Persephone Bubblegum-Holiday Date: Mon, 14 Apr 2025 12:06:12 -0700 Subject: [PATCH] 0.3 and 0.4 changes --- Scenes/Node Types/Audio.tscn | 17 ++ Scenes/Node Types/Direct Output.tscn | 20 +++ Scenes/Node Types/Mix Floats.tscn | 17 ++ Scenes/Node Types/Mouse Wheel.tscn | 2 +- Scenes/Nodes/Node Add.tscn | 4 +- Scenes/Nodes/Node Row.tscn | 29 +++- Scenes/Nodes/Node.tscn | 10 +- Scenes/UI/Node Map.tscn | 92 ++++++++--- Scripts/GL_Audio.gd | 14 ++ Scripts/GL_Audio.gd.uid | 1 + Scripts/GL_AudioType.gd | 3 + Scripts/GL_AudioType.gd.uid | 1 + Scripts/GL_Keystrokes.gd | 37 +++++ Scripts/GL_Keystrokes.gd.uid | 1 + Scripts/GL_Mix_Floats.gd | 16 ++ Scripts/GL_Mix_Floats.gd.uid | 1 + Scripts/GL_Mouse_Wheel.gd | 2 +- Scripts/GL_Node.gd | 48 ++++-- Scripts/GL_Node_Add.gd | 2 + Scripts/GL_Node_Map.gd | 203 ++++++++++++++++++++++- Scripts/GL_Node_Picker_Audio.gd | 81 +++++++++ Scripts/GL_Node_Picker_Audio.gd.uid | 1 + Scripts/GL_Node_Point.gd | 10 +- Scripts/GL_Output.gd | 3 + Scripts/GL_Record.gd | 20 ++- Scripts/GL_Search.gd | 3 + Scripts/GL_Speaker.gd | 45 +++++ Scripts/GL_Speaker.gd.uid | 1 + Scripts/freecam.gd | 238 ++++++++++++--------------- 29 files changed, 731 insertions(+), 191 deletions(-) create mode 100644 Scenes/Node Types/Audio.tscn create mode 100644 Scenes/Node Types/Direct Output.tscn create mode 100644 Scenes/Node Types/Mix Floats.tscn create mode 100644 Scripts/GL_Audio.gd create mode 100644 Scripts/GL_Audio.gd.uid create mode 100644 Scripts/GL_AudioType.gd create mode 100644 Scripts/GL_AudioType.gd.uid create mode 100644 Scripts/GL_Keystrokes.gd create mode 100644 Scripts/GL_Keystrokes.gd.uid create mode 100644 Scripts/GL_Mix_Floats.gd create mode 100644 Scripts/GL_Mix_Floats.gd.uid create mode 100644 Scripts/GL_Node_Picker_Audio.gd create mode 100644 Scripts/GL_Node_Picker_Audio.gd.uid create mode 100644 Scripts/GL_Speaker.gd create mode 100644 Scripts/GL_Speaker.gd.uid diff --git a/Scenes/Node Types/Audio.tscn b/Scenes/Node Types/Audio.tscn new file mode 100644 index 0000000..c0aea4a --- /dev/null +++ b/Scenes/Node Types/Audio.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=3 uid="uid://lossothjt5ye"] + +[ext_resource type="PackedScene" uid="uid://b0arjg8r75f8y" path="res://Scenes/Nodes/Node.tscn" id="1_db1pk"] +[ext_resource type="Script" uid="uid://bmukxrwmoyc20" path="res://Scripts/GL_Audio.gd" id="2_db1pk"] + +[node name="Node" type="Control"] +layout_mode = 3 +anchors_preset = 0 +mouse_filter = 1 + +[node name="Node" parent="." groups=["GL Node"] instance=ExtResource("1_db1pk")] +layout_mode = 0 +tooltip_text = "Outputs the path to an audio source saved in the workspace. Does not output live audio- playback on nodes this is plugged into must be handled by a Timeline node or similar." +script = ExtResource("2_db1pk") + +[connection signal="mouse_entered" from="Node" to="Node" method="mouse_enter"] +[connection signal="mouse_exited" from="Node" to="Node" method="mouse_exit"] diff --git a/Scenes/Node Types/Direct Output.tscn b/Scenes/Node Types/Direct Output.tscn new file mode 100644 index 0000000..629abdc --- /dev/null +++ b/Scenes/Node Types/Direct Output.tscn @@ -0,0 +1,20 @@ +[gd_scene load_steps=3 format=3 uid="uid://b03x861ratqbj"] + +[ext_resource type="PackedScene" uid="uid://b0arjg8r75f8y" path="res://Scenes/Nodes/Node.tscn" id="1_uifvy"] +[ext_resource type="Script" uid="uid://t8bsiegtsiwo" path="res://Scripts/GL_Output.gd" id="2_nkf8v"] + +[node name="Node" type="Control"] +layout_mode = 3 +anchors_preset = 0 +mouse_filter = 1 + +[node name="Node" parent="." groups=["GL Node"] instance=ExtResource("1_uifvy")] +layout_mode = 0 +tooltip_text = "Controls Chica's movements. All eyelid movements can be set to a particular position using values of 0.0 to 1.0." +script = ExtResource("2_nkf8v") +identification = "DIRECT_OUTPUT" +names = PackedStringArray("Audio", "Volume", "Current Time") +types = PackedStringArray("audio", "float", "float") + +[connection signal="mouse_entered" from="Node" to="Node" method="mouse_enter"] +[connection signal="mouse_exited" from="Node" to="Node" method="mouse_exit"] diff --git a/Scenes/Node Types/Mix Floats.tscn b/Scenes/Node Types/Mix Floats.tscn new file mode 100644 index 0000000..e90b6e0 --- /dev/null +++ b/Scenes/Node Types/Mix Floats.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=3 uid="uid://b83i85vl6gd01"] + +[ext_resource type="PackedScene" uid="uid://b0arjg8r75f8y" path="res://Scenes/Nodes/Node.tscn" id="1_njggm"] +[ext_resource type="Script" uid="uid://beit3xudynjdl" path="res://Scripts/GL_Mix_Floats.gd" id="2_njggm"] + +[node name="Node" type="Control"] +layout_mode = 3 +anchors_preset = 0 +mouse_filter = 1 + +[node name="Node" parent="." groups=["GL Node"] instance=ExtResource("1_njggm")] +layout_mode = 0 +tooltip_text = "Mixes two floats (numbers) together using the 'Factor', with 0.0 being fully Float A, and 1.0 being fully Float B." +script = ExtResource("2_njggm") + +[connection signal="mouse_entered" from="Node" to="Node" method="mouse_enter"] +[connection signal="mouse_exited" from="Node" to="Node" method="mouse_exit"] diff --git a/Scenes/Node Types/Mouse Wheel.tscn b/Scenes/Node Types/Mouse Wheel.tscn index b909118..1a921fd 100644 --- a/Scenes/Node Types/Mouse Wheel.tscn +++ b/Scenes/Node Types/Mouse Wheel.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=3 format=3 uid="uid://bhkp4bfwm1agf"] +[gd_scene load_steps=3 format=3 uid="uid://d2da0nd32yqo7"] [ext_resource type="PackedScene" uid="uid://b0arjg8r75f8y" path="res://Scenes/Nodes/Node.tscn" id="1_o85ib"] [ext_resource type="Script" uid="uid://y8j8wap2o4oe" path="res://Scripts/GL_Mouse_Wheel.gd" id="2_o85ib"] diff --git a/Scenes/Nodes/Node Add.tscn b/Scenes/Nodes/Node Add.tscn index e421276..923de08 100644 --- a/Scenes/Nodes/Node Add.tscn +++ b/Scenes/Nodes/Node Add.tscn @@ -7,13 +7,15 @@ offset_right = 68.0 offset_bottom = 31.0 selected = 0 allow_reselect = true -item_count = 3 +item_count = 4 popup/item_0/text = "+Add Float" popup/item_0/id = 0 popup/item_1/text = "+Add Bool" popup/item_1/id = 1 popup/item_2/text = "+Add Color" popup/item_2/id = 2 +popup/item_3/text = "+Add Audio" +popup/item_3/id = 3 script = ExtResource("1_vw1dw") [node name="Panel" type="PanelContainer" parent="."] diff --git a/Scenes/Nodes/Node Row.tscn b/Scenes/Nodes/Node Row.tscn index 88e9a20..aa5e458 100644 --- a/Scenes/Nodes/Node Row.tscn +++ b/Scenes/Nodes/Node Row.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=3 uid="uid://bdcxusbd86oox"] +[gd_scene load_steps=6 format=3 uid="uid://bdcxusbd86oox"] [ext_resource type="Script" uid="uid://dwl36vn5chqmq" path="res://Scripts/GL_Node_Point.gd" id="1_fygh4"] [ext_resource type="Script" uid="uid://q5qlhwvjb16w" path="res://Scripts/GL_Node_Picker_Float.gd" id="2_vlx6y"] [ext_resource type="Script" uid="uid://dflftb37a7ind" path="res://Scripts/GL_Node_Picker_Color.gd" id="3_tf34m"] [ext_resource type="Script" uid="uid://b7ysqwtxh8pf" path="res://Scripts/GL_Node_Picker_Bool.gd" id="4_yal7b"] +[ext_resource type="Script" uid="uid://rmlqvxot3kys" path="res://Scripts/GL_Node_Picker_Audio.gd" id="5_yal7b"] [node name="Node Row" type="HBoxContainer"] @@ -41,6 +42,30 @@ custom_minimum_size = Vector2(50, 0) layout_mode = 2 script = ExtResource("4_yal7b") +[node name="Pick Audio" type="PanelContainer" parent="."] +visible = false +layout_mode = 2 +script = ExtResource("5_yal7b") + +[node name="HBox" type="HBoxContainer" parent="Pick Audio"] +layout_mode = 2 + +[node name="OptionButton" type="OptionButton" parent="Pick Audio/HBox"] +layout_mode = 2 +allow_reselect = true + +[node name="Button" type="Button" parent="Pick Audio/HBox"] +layout_mode = 2 +text = "🗀" + +[node name="FileDialog" type="FileDialog" parent="Pick Audio"] +title = "Open a File" +initial_position = 1 +size = Vector2i(960, 480) +ok_button_text = "Open" +file_mode = 0 +access = 2 + [node name="Output" type="Button" parent="." groups=["Outputs"]] layout_mode = 2 mouse_default_cursor_shape = 2 @@ -53,4 +78,6 @@ script = ExtResource("1_fygh4") [connection signal="value_changed" from="Pick Float" to="Pick Float" method="value_changed"] [connection signal="color_changed" from="Pick Color" to="Pick Color" method="color_changed"] [connection signal="toggled" from="Pick Bool" to="Pick Bool" method="toggled"] +[connection signal="item_selected" from="Pick Audio/HBox/OptionButton" to="Pick Audio" method="_on_audio_option_selected"] +[connection signal="pressed" from="Pick Audio/HBox/Button" to="Pick Audio" method="_on_audio_button_pressed"] [connection signal="button_down" from="Output" to="Output" method="_start_drag"] diff --git a/Scenes/Nodes/Node.tscn b/Scenes/Nodes/Node.tscn index a6a437f..5637091 100644 --- a/Scenes/Nodes/Node.tscn +++ b/Scenes/Nodes/Node.tscn @@ -1,7 +1,9 @@ -[gd_scene load_steps=2 format=3 uid="uid://b0arjg8r75f8y"] +[gd_scene load_steps=3 format=3 uid="uid://b0arjg8r75f8y"] [ext_resource type="Theme" uid="uid://b3wjoiiv6sq22" path="res://UI/Themes/Default.tres" id="1_arhwt"] +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_arhwt"] + [node name="Node" type="PanelContainer"] clip_contents = true custom_minimum_size = Vector2(200, 100) @@ -21,12 +23,10 @@ layout_mode = 2 [node name="Title" type="HBoxContainer" parent="Margins/Holder"] layout_mode = 2 -[node name="Title Label" type="Label" parent="Margins/Holder/Title"] +[node name="Title Label" type="LineEdit" parent="Margins/Holder/Title"] layout_mode = 2 size_flags_horizontal = 3 -text = "Test" -horizontal_alignment = 1 -clip_text = true +theme_override_styles/normal = SubResource("StyleBoxEmpty_arhwt") [node name="Exit Button" type="Button" parent="Margins/Holder/Title"] layout_mode = 2 diff --git a/Scenes/UI/Node Map.tscn b/Scenes/UI/Node Map.tscn index c7b94b7..83849f3 100644 --- a/Scenes/UI/Node Map.tscn +++ b/Scenes/UI/Node Map.tscn @@ -1,16 +1,16 @@ -[gd_scene load_steps=5 format=3 uid="uid://c57u187iciexi"] +[gd_scene load_steps=6 format=3 uid="uid://c57u187iciexi"] [ext_resource type="Script" uid="uid://i4p62x8fnqpn" path="res://Scripts/GL_Node_Map.gd" id="1_jyqbx"] [ext_resource type="PackedScene" uid="uid://mowdu1i1rldt" path="res://Scenes/UI/Search.tscn" id="1_xwfut"] +[ext_resource type="Theme" uid="uid://b3wjoiiv6sq22" path="res://UI/Themes/Default.tres" id="2_2eix6"] -[sub_resource type="Gradient" id="Gradient_jyqbx"] -offsets = PackedFloat32Array(0) -colors = PackedColorArray(0.121569, 0.121569, 0.121569, 0.501961) +[sub_resource type="Gradient" id="Gradient_xwfut"] +colors = PackedColorArray(0.448074, 0.0582233, 0.099986, 1, 0.330802, 0.066494, 0.0423852, 1) -[sub_resource type="GradientTexture1D" id="GradientTexture1D_2eix6"] -gradient = SubResource("Gradient_jyqbx") +[sub_resource type="GradientTexture1D" id="GradientTexture1D_jyqbx"] +gradient = SubResource("Gradient_xwfut") -[node name="NodeMap" type="Control"] +[node name="NodeMap" type="Control" groups=["Node Map"]] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -26,22 +26,72 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -texture = SubResource("GradientTexture1D_2eix6") -stretch_mode = 1 - -[node name="Label" type="Label" parent="."] -layout_mode = 0 -offset_left = 4.0 -offset_top = 4.0 -offset_right = 249.0 -offset_bottom = 27.0 -text = "Editing nodes, press ESC to exit." - -[node name="Search" parent="." instance=ExtResource("1_xwfut")] -visible = false -layout_mode = 1 +texture = SubResource("GradientTexture1D_jyqbx") [node name="Holder" type="Control" parent="."] anchors_preset = 0 offset_right = 40.0 offset_bottom = 40.0 + +[node name="Label" type="Label" parent="."] +z_index = 1000 +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -1143.0 +offset_top = -97.0 +offset_right = -11.0 +offset_bottom = -48.0 +grow_horizontal = 0 +grow_vertical = 0 +theme = ExtResource("2_2eix6") +theme_override_font_sizes/font_size = 8 +text = "Give LIFE: TEST_C_ETCHINGS +Press Esc for Nodes, Right Click to search node. +Tab toggles background.Middle Click hold to pan. +Scroll to Zoom. Hover things for tooltips." +horizontal_alignment = 2 + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 8 + +[node name="OptionButton" type="OptionButton" parent="MarginContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 8 +selected = 0 +item_count = 1 +popup/item_0/text = "Test" +popup/item_0/id = 0 + +[node name="Button" type="Button" parent="MarginContainer/HBoxContainer"] +visible = false +layout_mode = 2 +text = "Edit" + +[node name="Button2" type="Button" parent="MarginContainer/HBoxContainer"] +layout_mode = 2 +text = "Save" + +[node name="Search" parent="." instance=ExtResource("1_xwfut")] +layout_mode = 1 + +[connection signal="pressed" from="MarginContainer/HBoxContainer/Button2" to="." method="save_everything"] diff --git a/Scripts/GL_Audio.gd b/Scripts/GL_Audio.gd new file mode 100644 index 0000000..2342b98 --- /dev/null +++ b/Scripts/GL_Audio.gd @@ -0,0 +1,14 @@ +extends GL_Node + +func _ready(): + super._ready() + _set_title("Audio") + _create_row("Output",null,GL_AudioType.new(),true,GL_AudioType.new(),0) + _update_visuals() + +func _process(delta): + super._process(delta) + apply_pick_values() + for key in rows: + rows[key]["output"] = rows[key]["input"] + _send_input("Output") diff --git a/Scripts/GL_Audio.gd.uid b/Scripts/GL_Audio.gd.uid new file mode 100644 index 0000000..6af1349 --- /dev/null +++ b/Scripts/GL_Audio.gd.uid @@ -0,0 +1 @@ +uid://bmukxrwmoyc20 diff --git a/Scripts/GL_AudioType.gd b/Scripts/GL_AudioType.gd new file mode 100644 index 0000000..6c2fa18 --- /dev/null +++ b/Scripts/GL_AudioType.gd @@ -0,0 +1,3 @@ +extends Node +class_name GL_AudioType +var value:String = "" diff --git a/Scripts/GL_AudioType.gd.uid b/Scripts/GL_AudioType.gd.uid new file mode 100644 index 0000000..a45c19a --- /dev/null +++ b/Scripts/GL_AudioType.gd.uid @@ -0,0 +1 @@ +uid://dfxs3vmqxy1eu diff --git a/Scripts/GL_Keystrokes.gd b/Scripts/GL_Keystrokes.gd new file mode 100644 index 0000000..2b916ce --- /dev/null +++ b/Scripts/GL_Keystrokes.gd @@ -0,0 +1,37 @@ +extends GL_Node + +func _ready(): + super._ready() + _set_title("Keystrokes") + _create_row("KEY #1",null,false,false,0.0,1) + _create_row("KEY #2",null,false,false,0.0,1) + _create_row("KEY #3",null,false,false,0.0,1) + _create_row("KEY #4",null,false,false,0.0,1) + _create_row("KEY #5",null,false,false,0.0,1) + _create_row("KEY #6",null,false,false,0.0,1) + _create_row("KEY #7",null,false,false,0.0,1) + _create_row("KEY #8",null,false,false,0.0,1) + _create_row("KEY #9",null,false,false,0.0,1) + _create_row("KEY #0",null,false,false,0.0,1) + _update_visuals() + +func _process(delta): + super._process(delta) + + var key_map = { + "KEY #1": KEY_1, + "KEY #2": KEY_2, + "KEY #3": KEY_3, + "KEY #4": KEY_4, + "KEY #5": KEY_5, + "KEY #6": KEY_6, + "KEY #7": KEY_7, + "KEY #8": KEY_8, + "KEY #9": KEY_9, + "KEY #0": KEY_0, + } + + for key_name in key_map.keys(): + var is_pressed = Input.is_key_pressed(key_map[key_name]) or Input.is_key_pressed(key_map[key_name] + (KEY_KP_0 - KEY_0)) + rows[key_name]["output"] = is_pressed + _send_input(key_name) diff --git a/Scripts/GL_Keystrokes.gd.uid b/Scripts/GL_Keystrokes.gd.uid new file mode 100644 index 0000000..adb7631 --- /dev/null +++ b/Scripts/GL_Keystrokes.gd.uid @@ -0,0 +1 @@ +uid://e6v6exlrhtaq diff --git a/Scripts/GL_Mix_Floats.gd b/Scripts/GL_Mix_Floats.gd new file mode 100644 index 0000000..cb18139 --- /dev/null +++ b/Scripts/GL_Mix_Floats.gd @@ -0,0 +1,16 @@ +extends GL_Node + +func _ready(): + super._ready() + _set_title("Mix Floats") + _create_row("Factor",0.0,0.0,false,null,0) + _create_row("Float A",0.0,null,true,0.0,1.0) + _create_row("Float B",0.0,null,true,0.0,1.0) + _update_visuals() + +func _process(delta): + super._process(delta) + apply_pick_values() + + rows["Factor"]["output"] = lerp(float(rows["Float A"]["input"]),float(rows["Float B"]["input"]),rows["Factor"]["input"]) + _send_input("Factor") diff --git a/Scripts/GL_Mix_Floats.gd.uid b/Scripts/GL_Mix_Floats.gd.uid new file mode 100644 index 0000000..473ba1d --- /dev/null +++ b/Scripts/GL_Mix_Floats.gd.uid @@ -0,0 +1 @@ +uid://beit3xudynjdl diff --git a/Scripts/GL_Mouse_Wheel.gd b/Scripts/GL_Mouse_Wheel.gd index 3dec311..7872464 100644 --- a/Scripts/GL_Mouse_Wheel.gd +++ b/Scripts/GL_Mouse_Wheel.gd @@ -13,7 +13,7 @@ func _process(delta): _send_input("Output") -func _input(event): +func _unhandled_input(event): # Check if the mouse wheel up or down button is pressed if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed: diff --git a/Scripts/GL_Node.gd b/Scripts/GL_Node.gd index 81f068c..d57d874 100644 --- a/Scripts/GL_Node.gd +++ b/Scripts/GL_Node.gd @@ -1,7 +1,8 @@ extends PanelContainer class_name GL_Node var rows : Dictionary -var uuid : int #REMEMBER TO SET THIS ON CREATION +var uuid : String +var nodePath:String var dragging : bool var canDrag : bool var dragOffset : Vector2 @@ -18,6 +19,15 @@ func _ready(): func _process(delta): if dragging: position = get_viewport().get_mouse_position() + dragOffset + for key in rows: + for connection in rows[key].get("connections",[]): + if typeof(connection.target) == TYPE_STRING: + for node in get_tree().get_nodes_in_group("GL Node"): + if node is GL_Node: + if node.uuid == connection.target: + connection.target = node + break + func _input(event): if event is InputEventMouseButton: @@ -30,7 +40,7 @@ func _input(event): func _create_uuid(): var rand = RandomNumberGenerator.new() rand.seed = Time.get_unix_time_from_system() - uuid = rand.randi() + uuid = str(rand.randi()) func _update_visuals(): var holder = get_node("Margins").get_node("Holder") @@ -65,6 +75,10 @@ func _update_visuals(): TYPE_BOOL: assignPick(nodeRow.get_node("Pick Bool"),str(key)) (nodeRow.get_node("Pick Bool") as CheckButton).button_pressed = rows[key]["pickValue"] + if rows[key]["pickValue"] is GL_AudioType: + assignPick(nodeRow.get_node("Pick Audio"),str(key)) + if rows[key]["pickValue"] == null: + rows[key]["pickValue"] = GL_AudioType.new() else: (nodeRow.get_node("Label") as Label).size_flags_horizontal = Control.SIZE_EXPAND_FILL @@ -104,11 +118,18 @@ func _set_inout_type(label:Button, value): TYPE_COLOR: label.text = "▲" label.add_theme_color_override("font_color", Color.WHITE_SMOKE) - _: - label.visible = false + if value is GL_AudioType: + label.text = "🔈" + label.add_theme_color_override("font_color", Color.BLUE_VIOLET) + if value == null: + label.visible = false func _set_title(name:String): - (get_node("Margins").get_node("Holder").get_node("Title").get_node("Title Label") as Label).text = name + (get_node("Margins").get_node("Holder").get_node("Title").get_node("Title Label") as LineEdit).text = name + +func _get_title() -> String: + return (get_node("Margins").get_node("Holder").get_node("Title").get_node("Title Label") as LineEdit).text + func _create_row(name:String,input,output,picker:bool,pickDefault,pickFloatMaximum:float): if rows.has(name): @@ -126,12 +147,12 @@ func _send_input(output_name: String): if not rows.has(output_name): return - var connections = rows[output_name].get("connections", []) - for conn in connections: + for conn in rows[output_name].get("connections", []): var target = conn.get("target", null) var input_name = conn.get("input_name", null) if target and input_name: - target._recieve_input(input_name, rows[output_name]["output"]) + if typeof(target) != TYPE_INT: + target._recieve_input(input_name, rows[output_name]["output"]) func _confirm_backConnection(input_name:String): if !rows.has(input_name): @@ -145,9 +166,10 @@ func _create_connection(target:GL_Node,input_name:String,output_name:String): var item = target.rows.get(input_name, null) if item == null: return - - if typeof(rows[output_name].get("output", null)) != typeof(target.rows[input_name].get("input",null)): - if !(typeof(rows[output_name].get("output", null)) == TYPE_BOOL && typeof(target.rows[input_name].get("input",null)) == TYPE_FLOAT): + + var typeA = typeof(rows[output_name].get("output", null)) + var typeB = typeof(target.rows[input_name].get("input",null)) + if (typeA != typeB) && !(typeA == TYPE_BOOL && typeB == TYPE_FLOAT) && !(typeA == TYPE_INT && typeB == TYPE_FLOAT)&& !(typeA == TYPE_FLOAT && typeB == TYPE_INT): print("Type mismatch: cannot connect " + output_name + " to " + target.name) return @@ -156,7 +178,7 @@ func _create_connection(target:GL_Node,input_name:String,output_name:String): "input_name": input_name } - var connections = rows[output_name].get("connections",[]) + var connections = rows[output_name].get("connections", []) for connection in connections: if connection.target == thenew.target and connection.input_name == thenew.input_name: @@ -201,4 +223,4 @@ func delete_whole_node(): if node is GL_Node_Point: for key in rows: node.mainNode.destroy_connection(self,key) - queue_free() + get_parent().queue_free() diff --git a/Scripts/GL_Node_Add.gd b/Scripts/GL_Node_Add.gd index 4073c27..6350ecb 100644 --- a/Scripts/GL_Node_Add.gd +++ b/Scripts/GL_Node_Add.gd @@ -21,6 +21,8 @@ func _named(name:String): mainNode._create_row(name,false,false,true,false,0) 2: mainNode._create_row(name,Color.WHITE,Color.WHITE,true,Color.WHITE,0) + 3: + mainNode._create_row(name,GL_AudioType.new(),GL_AudioType.new(),true,GL_AudioType.new(),0) mainNode._update_visuals() func _cancelled(): disabled = false diff --git a/Scripts/GL_Node_Map.gd b/Scripts/GL_Node_Map.gd index ba8a701..d34a807 100644 --- a/Scripts/GL_Node_Map.gd +++ b/Scripts/GL_Node_Map.gd @@ -1,35 +1,63 @@ extends Control +class_name GL_Node_Map +var background: TextureRect var holder: Control var is_panning: bool = false var last_mouse_pos: Vector2 var is_hovered: bool = false +#Workspace shenanigans +var optionsVar:OptionButton + +#Workspaces +var _workspace_ID:String +var save_name: String = "My Save" +var author_name: String = "Unnamed Author" +var version: String = ProjectSettings.get_setting("application/config/version") +var game_title: String = ProjectSettings.get_setting("application/config/name") +var time_created: String = "" +var last_updated: String = "" + + +func _notification(what): + if what == NOTIFICATION_EXIT_TREE: + save_everything() + func _ready(): visible = false + background = get_node("Background") holder = get_node("Holder") + optionsVar = get_node("MarginContainer/HBoxContainer/OptionButton") + Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) connect("mouse_entered", _on_mouse_entered) connect("mouse_exited", _on_mouse_exited) + _workspace_ID = generate_new_workspace_id() + populate_workspace_options() + optionsVar.connect("item_selected", Callable(self, "_on_workspace_selected")) + + func _on_mouse_entered(): is_hovered = true func _on_mouse_exited(): is_hovered = false + func _input(event: InputEvent) -> void: if event is InputEventKey and event.pressed: match event.keycode: KEY_ESCAPE: visible = not visible - + KEY_TAB: + background.self_modulate.a = abs(background.self_modulate.a - 1) if not visible: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) - return else: Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) - + if not is_hovered: return @@ -50,14 +78,11 @@ func _input(event: InputEvent) -> void: elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN: zoom_factor = 0.9 - # Apply scale holder.scale *= zoom_factor - # Recalculate the new local position of the mouse after scaling var new_global_xform = holder.get_global_transform() var new_local_mouse_pos = new_global_xform.affine_inverse().basis_xform(mouse_pos) - # Calculate offset to shift so the mouse stays “anchored” var delta = (new_local_mouse_pos - local_mouse_pos) holder.position += delta * holder.scale @@ -65,3 +90,169 @@ func _input(event: InputEvent) -> void: var delta = event.position - last_mouse_pos holder.position += delta last_mouse_pos = event.position + +func save_everything(): + var saveDict := {} + var rng = RandomNumberGenerator.new() + rng.seed = Time.get_ticks_msec() + + if holder.get_child_count() == 0: + return + + for child in holder.get_children(): + child = child.get_child(0) + if child is not GL_Node: + print(child.name) + continue + + var id = "SAVE_" + str(rng.randi()) + var node_data = { + "path": child.nodePath, + "name": child._get_title(), + "uuid": child.uuid, + "rows": child.rows.duplicate(true), + "position": child.position + } + + # Save recording if it's a GL_Record and has enough data + if child is GL_Record and child.recording != null: + if child.recording.size() >= 3: + var recording_file_path = "user://My Precious Save Files/" + str(_workspace_ID) + "/" + child.uuid + "_recording.tres" + var recording_config = ConfigFile.new() + recording_config.set_value("recording", "data", child.recording) + var err = recording_config.save(recording_file_path) + if err != OK: + push_error("Failed to save recording for " + child.uuid + ": " + str(err)) + else: + print("Saved recording for node ", child.uuid) + + # Convert connections to uuid references + for key in node_data["rows"]: + if node_data["rows"][key].has("connections"): + var connections = node_data["rows"][key]["connections"] + for i in range(connections.size()): + if connections[i]["target"] is GL_Node: + connections[i]["target"] = connections[i]["target"].uuid + + saveDict[id] = node_data + + var save_dir = "user://My Precious Save Files/" + str(_workspace_ID) + DirAccess.make_dir_recursive_absolute(save_dir) + var file_path = save_dir + "/node_workspace.tres" + + var resource = ConfigFile.new() + + # Metadata section + if time_created == "": + time_created = Time.get_datetime_string_from_system(true) + last_updated = Time.get_datetime_string_from_system(true) + + resource.set_value("meta", "save_name", save_name) + resource.set_value("meta", "author", author_name) + resource.set_value("meta", "version", ProjectSettings.get_setting("application/config/version")) + resource.set_value("meta", "game_title", ProjectSettings.get_setting("application/config/name")) + resource.set_value("meta", "time_created", time_created) + resource.set_value("meta", "last_updated", last_updated) + + # Main save data + resource.set_value("workspace", "data", saveDict) + + var err = resource.save(file_path) + if err != OK: + push_error("Failed to save workspace: " + str(err)) + else: + print("Saved workspace to: ", file_path) + + populate_workspace_options() + + + +func load_everything(): + var file_path = "user://My Precious Save Files/" + str(_workspace_ID) + "/node_workspace.tres" + var resource = ConfigFile.new() + var err = resource.load(file_path) + if err != OK: + push_error("Failed to load workspace: " + str(err)) + return {} + + # Load metadata + save_name = resource.get_value("meta", "save_name", "Unnamed Save") + author_name = resource.get_value("meta", "author", "Unknown Author") + version = resource.get_value("meta", "version", "0.0") + game_title = resource.get_value("meta", "game_title", "Untitled Game") + time_created = resource.get_value("meta", "time_created", "") + last_updated = resource.get_value("meta", "last_updated", "") + + print("Loaded workspace metadata:") + print("Save Name: ", save_name) + print("Author: ", author_name) + print("Version: ", version) + print("Game Title: ", game_title) + print("Time Created: ", time_created) + print("Last Updated: ", last_updated) + + # Load nodes + var data = resource.get_value("workspace", "data", {}) + for key in data: + var packed_scene = load(data[key]["path"]) + if packed_scene == null: + printerr("Could not load resource at: " + data[key]["path"]) + continue + var node = packed_scene.instantiate() as Control + holder.add_child(node) + node = node.get_child(0) as GL_Node + node.position = data[key].get("position",Vector2.ZERO) + node.nodePath = data[key].get("path","ERR") + node.uuid = data[key].get("uuid","ERR_" + key + str(Time.get_ticks_msec())) + node._set_title(data[key].get("name","???")) + node.rows = data[key].get("rows",{}) + node._update_visuals() + if node is GL_Record: + var recording_file = "user://My Precious Save Files/" + str(_workspace_ID) + "/" + node.uuid + "_recording.tres" + var config = ConfigFile.new() + if config.load(recording_file) == OK: + node.recording = config.get_value("recording", "data", {}) + + +func generate_new_workspace_id() -> String: + var rng = RandomNumberGenerator.new() + rng.seed = Time.get_ticks_msec() + return str(rng.randi()) + +func clear_holder(): + for node in holder.get_children(): + node.queue_free() + await get_tree().process_frame # ensure all nodes are freed + +func populate_workspace_options(): + optionsVar.clear() + optionsVar.add_item("New Workspace") + + var dir := DirAccess.open("user://My Precious Save Files") + if dir: + dir.list_dir_begin() + var name = dir.get_next() + while name != "": + if dir.current_is_dir() and name != "." and name != "..": + optionsVar.add_item(name) + name = dir.get_next() + dir.list_dir_end() + +func _on_workspace_selected(index: int): + save_everything() + + if index == 0: # New Workspace + clear_holder() + _workspace_ID = generate_new_workspace_id() + save_name = "My Save" + author_name = "Unnamed Author" + version = ProjectSettings.get_setting("application/config/version") + game_title = ProjectSettings.get_setting("application/config/name") + time_created = "" + last_updated = "" + print("Created new workspace: ", _workspace_ID) + else: + var selected_name = optionsVar.get_item_text(index) + _workspace_ID = selected_name + clear_holder() + load_everything() diff --git a/Scripts/GL_Node_Picker_Audio.gd b/Scripts/GL_Node_Picker_Audio.gd new file mode 100644 index 0000000..6c3c2e4 --- /dev/null +++ b/Scripts/GL_Node_Picker_Audio.gd @@ -0,0 +1,81 @@ +extends GL_Node_Picker + +var audio_selector:OptionButton +var file_dialog:FileDialog + +func _ready(): + file_dialog = get_node("FileDialog") + audio_selector = get_node("HBox").get_node("OptionButton") + DirAccess.make_dir_recursive_absolute(find_audio_path()) + file_dialog.file_selected.connect(_on_audio_file_selected) + _update_audio_options() + +func _on_audio_button_pressed(): + file_dialog.clear_filters() + file_dialog.current_path = OS.get_system_dir(OS.SYSTEM_DIR_DOWNLOADS) + file_dialog.add_filter("*.wav ; WAV Audio") + file_dialog.add_filter("*.mp3 ; MP3 Audio") + file_dialog.add_filter("*.ogg ; OGG Audio") + file_dialog.popup_centered() + +func _on_audio_file_selected(path: String): + var filename = path.get_file() + var dest_path = find_audio_path() + "/" + filename + + var file = FileAccess.open(path, FileAccess.READ) + if file: + var data = file.get_buffer(file.get_length()) + file.close() + + var save_file = FileAccess.open(dest_path, FileAccess.WRITE) + if save_file: + save_file.store_buffer(data) + save_file.close() + print("Saved audio to: ", dest_path) + _update_audio_options(filename) # repopulate and select this one + else: + push_error("Failed to write audio file.") + else: + push_error("Failed to read selected audio.") + +func _update_audio_options(select_filename := ""): + audio_selector.clear() + var audio_files := [] + + var dir := DirAccess.open(find_audio_path()) + if dir: + dir.list_dir_begin() + var file = dir.get_next() + while file != "": + if not dir.current_is_dir(): + audio_files.append(file) + file = dir.get_next() + dir.list_dir_end() + + audio_files.sort() + for i in range(audio_files.size()): + audio_selector.add_item(audio_files[i]) + if audio_files[i] == select_filename: + audio_selector.select(i) + _set_audio_path(audio_files[i]) + +func _on_audio_option_selected(index: int): + var file = audio_selector.get_item_text(index) + _set_audio_path(file) + +func _set_audio_path(file: String): + var path = find_audio_path() + "/" + file + if mainNode and mainNode.rows.has(valueName): + var audio = GL_AudioType.new() + audio.value = path + mainNode.rows[valueName]["pickValue"] = audio + print("Audio set: ", path) + else: + push_error("mainNode or rows[valueName] not found.") + +func find_audio_path() -> String: + for node in get_tree().get_nodes_in_group("Node Map"): + if node is GL_Node_Map: + return "user://My Precious Save Files/" + node._workspace_ID + "/Audio" + printerr("Uhhhhh") + return "" diff --git a/Scripts/GL_Node_Picker_Audio.gd.uid b/Scripts/GL_Node_Picker_Audio.gd.uid new file mode 100644 index 0000000..1849fd0 --- /dev/null +++ b/Scripts/GL_Node_Picker_Audio.gd.uid @@ -0,0 +1 @@ +uid://rmlqvxot3kys diff --git a/Scripts/GL_Node_Point.gd b/Scripts/GL_Node_Point.gd index 4847ebf..ad1c497 100644 --- a/Scripts/GL_Node_Point.gd +++ b/Scripts/GL_Node_Point.gd @@ -26,6 +26,8 @@ func _process(delta): previewLine.default_color = Color.BLACK TYPE_COLOR: previewLine.default_color = output + if output is GL_AudioType: + previewLine.default_color = Color.BLUE_VIOLET var connections = mainNode.rows[valueName].get("connections",[]) if connections != []: @@ -43,8 +45,14 @@ func _process(delta): child.default_color = Color.BLACK TYPE_COLOR: child.default_color = output + if output is GL_AudioType: + if output.value == "": + child.default_color = Color.BLACK + else: + child.default_color = Color.BLUE_VIOLET child.points[0] = global_position + Vector2(size.x / 2, size.y / 2) - child.points[1] = (connections[iter]["target"] as GL_Node).give_input_point_pos(connections[iter]["input_name"])# - child.global_position + if typeof(connections[iter]["target"]) != TYPE_INT: + child.points[1] = (connections[iter]["target"] as GL_Node).give_input_point_pos(connections[iter]["input_name"])# - child.global_position iter += 1 func _create_line() -> Line2D: diff --git a/Scripts/GL_Output.gd b/Scripts/GL_Output.gd index d7a5b52..30b8639 100644 --- a/Scripts/GL_Output.gd +++ b/Scripts/GL_Output.gd @@ -6,6 +6,7 @@ extends GL_Node func _ready(): super._ready() + _set_title(identification) for i in names.size(): match(types[i].to_lower()): "float": @@ -14,6 +15,8 @@ func _ready(): _create_row(str(names[i]),Color.WHITE,null,true,Color.WHITE,0) "bool": _create_row(str(names[i]),false,null,true,false,0) + "audio": + _create_row(str(names[i]),GL_AudioType.new(),null,true,GL_AudioType.new(),0) _update_visuals() func _process(delta): diff --git a/Scripts/GL_Record.gd b/Scripts/GL_Record.gd index 5d63f7f..71c40c4 100644 --- a/Scripts/GL_Record.gd +++ b/Scripts/GL_Record.gd @@ -1,4 +1,5 @@ extends GL_Node +class_name GL_Record var timer:float const sampleRate = 0.05 var recording:Dictionary @@ -59,7 +60,14 @@ func _traverse(): recording[key]["lastUsed"] = current recording[key]["current"] = newCurrent if recording[key]["lastUsed"] != null && recording[key]["current"] != recording[key]["end"]: - rows[key]["output"] = lerp(recording[key]["list"][recording[key]["lastUsed"]]["value"],recording[key]["list"][recording[key]["current"]]["value"],remap_time(time,recording[key]["list"][recording[key]["lastUsed"]]["time"],recording[key]["list"][recording[key]["current"]]["time"])) + if(rows[key]["output"] is float): + rows[key]["output"] = lerp(float(recording[key]["list"][recording[key]["lastUsed"]]["value"]),float(recording[key]["list"][recording[key]["current"]]["value"]),remap_time(time,recording[key]["list"][recording[key]["lastUsed"]]["time"],recording[key]["list"][recording[key]["current"]]["time"])) + elif(rows[key]["output"] is bool || rows[key]["output"] is GL_AudioType): + rows[key]["output"] = recording[key]["current"] + elif(rows[key]["output"] is Color): + rows[key]["output"] = lerp(recording[key]["list"][recording[key]["lastUsed"]]["value"],recording[key]["list"][recording[key]["current"]]["value"],remap_time(time,recording[key]["list"][recording[key]["lastUsed"]]["time"],recording[key]["list"][recording[key]["current"]]["time"])) + + func remap_time(value: float, start: float, end: float) -> float: if start == end: return 0.0 @@ -80,10 +88,6 @@ func _record(): for key in recording: if key == "Recording" || key == "Current Time": continue - if defaultValues[key] == rows[key]["input"]: - continue - elif defaultValues[key] != null: - defaultValues[key] == null #is this gonna bite me back if I allow null values to pass var currentSave = recording[key]["current"] if currentSave == null: var id = "ID_" + str(rng.randi()) @@ -98,7 +102,11 @@ func _record(): recording[key]["end"] = id rows[key]["output"] = recording[key]["list"][id]["value"] continue - else: + if defaultValues[key] == rows[key]["input"]: + continue + elif defaultValues[key] != null: + defaultValues[key] = null #is this gonna bite me back if I allow null values to pass + if currentSave != null: if time < oldTime: #rewind continue #fix pls else: #forward diff --git a/Scripts/GL_Search.gd b/Scripts/GL_Search.gd index 19f566f..30f22ce 100644 --- a/Scripts/GL_Search.gd +++ b/Scripts/GL_Search.gd @@ -7,14 +7,17 @@ var rows : Dictionary = { "ChuckSpot":1, "HelenSpot":1, "MunchSpot":1, + "Audio":1, "Bool":1, "Color":1, + "Direct Output":1, "Float":1, "Invert":1, "MiscKeys":1, "NumberKeys":1, "Keystroke Ramp":1, "Lerp":1, + "Mix Floats":1, "Mix Colors":1, "Mouse Wheel":1, "Random":1, diff --git a/Scripts/GL_Speaker.gd b/Scripts/GL_Speaker.gd new file mode 100644 index 0000000..f9af39a --- /dev/null +++ b/Scripts/GL_Speaker.gd @@ -0,0 +1,45 @@ +extends GL_Animatable + +var speaker:AudioStreamPlayer +var oldPath:String +var oldTime:float + +func _ready(): + speaker = get_child(0) + +func _sent_signals(anim_name: String, value): + if speaker == null: + printerr("Can't find Animatable Speaker, needs to be the first child of node") + return + + match(anim_name): + "Audio": + print(value.value) + if value is not GL_AudioType: + return + var path = (value as GL_AudioType).value + if path != "" && path != oldPath: + var stream + match(path.get_extension().to_lower()): + "mp3": + stream = AudioStreamMP3.load_from_file(path) + "wav": + stream = AudioStreamWAV.load_from_file(path) + "ogg": + stream = AudioStreamOggVorbis.load_from_file(path) + if stream and stream is AudioStream: + speaker.stream = stream + oldPath = path + else: + printerr("Invalid audio stream at path: ", path) + "Volume": + speaker.volume_linear = value + "Current Time": + if speaker.stream != null: + if abs(speaker.get_playback_position() - value) > 0.05 && value < speaker.stream.get_length(): + speaker.play(value) + if oldTime == value: + speaker.stop() + oldTime = value + + diff --git a/Scripts/GL_Speaker.gd.uid b/Scripts/GL_Speaker.gd.uid new file mode 100644 index 0000000..273cb9c --- /dev/null +++ b/Scripts/GL_Speaker.gd.uid @@ -0,0 +1 @@ +uid://c5kxam0k3beml diff --git a/Scripts/freecam.gd b/Scripts/freecam.gd index 7848c95..926eb24 100644 --- a/Scripts/freecam.gd +++ b/Scripts/freecam.gd @@ -1,146 +1,114 @@ -extends Camera3D +class_name FreeLookCamera extends Camera3D -## Camera with flying script attached to it. -class_name Freecam3D - -## -## Camera with toggleable freecam mode for prototyping when creating levels, shaders, lighting, etc. -## -## Usage: Run your game, press and fly around freely. Uses Minecraft-like controls. -## - -## Customize your own toggle key to avoid collisions with your current mappings. -@export var toggle_key: Key = KEY_TAB -## Speed up / down by scrolling the mouse whell down / up -@export var invert_speed_controls: bool = false - -@export var overlay_text: bool = true - -## Pivot node for camera looking around -@onready var pivot := Node3D.new() -## Main parent for camera overlay. -@onready var screen_overlay := VBoxContainer.new() -## Container for the chat-like event log. -@onready var event_log := VBoxContainer.new() - -const MAX_SPEED := 0.25 -const MIN_SPEED := 0.01 -const ACCELERATION := 0.1 -const MOUSE_SENSITIVITY := 0.002 - -## Whether or not the camera can move. -var movement_active := false: - set(val): - movement_active = val - display_message("[Movement ON]" if movement_active else "[Movement OFF]") - -## The current maximum speed. Lower or higher it by scrolling the mouse wheel. -var target_speed := MIN_SPEED -## Movement velocity. -var velocity := Vector3.ZERO +# Modifier keys' speed multiplier +const SHIFT_MULTIPLIER = 2.5 +const ALT_MULTIPLIER = 1.0 / SHIFT_MULTIPLIER -## Sets up pivot and UI overlay elements. -func _setup_nodes() -> void: - self.add_sibling(pivot) - pivot.position = position - pivot.rotation = rotation - pivot.name = "FreecamPivot" - self.reparent(pivot) - self.position = Vector3.ZERO - self.rotation = Vector3.ZERO - # UI stuff - screen_overlay.add_theme_constant_override("Separation", 8) - self.add_child(screen_overlay) - screen_overlay.add_child(_make_label("Debug Camera")) - screen_overlay.add_spacer(false) +@export_range(0.0, 1.0) var sensitivity: float = 0.25 + +# Mouse state +var _mouse_position = Vector2(0.0, 0.0) +var _total_pitch = 0.0 + +# Movement state +var _direction = Vector3(0.0, 0.0, 0.0) +var _velocity = Vector3(0.0, 0.0, 0.0) +var _acceleration = 30 +var _deceleration = -10 +var _vel_multiplier = 4 + +# Keyboard state +var _w = false +var _s = false +var _a = false +var _d = false +var _q = false +var _e = false +var _shift = false +var _alt = false + +func _input(event): + # Receives mouse motion + if event is InputEventMouseMotion: + _mouse_position = event.relative - screen_overlay.add_child(event_log) - screen_overlay.visible = overlay_text + # Receives mouse button input + if event is InputEventMouseButton: + match event.button_index: + MOUSE_BUTTON_WHEEL_UP: # Increases max velocity + _vel_multiplier = clamp(_vel_multiplier * 1.1, 0.2, 20) + MOUSE_BUTTON_WHEEL_DOWN: # Decereases max velocity + _vel_multiplier = clamp(_vel_multiplier / 1.1, 0.2, 20) + # Receives key input + if event is InputEventKey: + match event.keycode: + KEY_W: + _w = event.pressed + KEY_S: + _s = event.pressed + KEY_A: + _a = event.pressed + KEY_D: + _d = event.pressed + KEY_Q: + _q = event.pressed + KEY_E: + _e = event.pressed + KEY_SHIFT: + _shift = event.pressed + KEY_ALT: + _alt = event.pressed -func _ready() -> void: - _setup_nodes.call_deferred() - _add_keybindings() - movement_active = true - Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) +# Updates mouselook and movement every frame +func _process(delta): + _update_mouselook() + _update_movement(delta) - - -func _process(delta: float) -> void: +# Updates camera movement +func _update_movement(delta): + # Computes desired direction from key states + _direction = Vector3( + (_d as float) - (_a as float), + (_e as float) - (_q as float), + (_s as float) - (_w as float) + ) - if Input.is_action_just_released("__debug_camera_toggle"): - movement_active = not movement_active + # Computes the change in velocity due to desired direction and "drag" + # The "drag" is a constant acceleration on the camera to bring it's velocity to 0 + var offset = _direction.normalized() * _acceleration * _vel_multiplier * delta \ + + _velocity.normalized() * _deceleration * _vel_multiplier * delta - if movement_active: - var dir = Vector3.ZERO - if Input.is_action_pressed("__debug_camera_forward"): dir.z -= 1 - if Input.is_action_pressed("__debug_camera_back"): dir.z += 1 - if Input.is_action_pressed("__debug_camera_left"): dir.x -= 1 - if Input.is_action_pressed("__debug_camera_right"): dir.x += 1 - if Input.is_action_pressed("__debug_camera_up"): dir.y += 1 - if Input.is_action_pressed("__debug_camera_down"): dir.y -= 1 + # Compute modifiers' speed multiplier + var speed_multi = 1 + if _shift: speed_multi *= SHIFT_MULTIPLIER + if _alt: speed_multi *= ALT_MULTIPLIER + + # Checks if we should bother translating the camera + if _direction == Vector3.ZERO and offset.length_squared() > _velocity.length_squared(): + # Sets the velocity to 0 to prevent jittering due to imperfect deceleration + _velocity = Vector3.ZERO + else: + # Clamps speed to stay within maximum value (_vel_multiplier) + _velocity.x = clamp(_velocity.x + offset.x, -_vel_multiplier, _vel_multiplier) + _velocity.y = clamp(_velocity.y + offset.y, -_vel_multiplier, _vel_multiplier) + _velocity.z = clamp(_velocity.z + offset.z, -_vel_multiplier, _vel_multiplier) + + translate(_velocity * delta * speed_multi) + +# Updates mouse look +func _update_mouselook(): + # Only rotates mouse if the mouse is captured + if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: + _mouse_position *= sensitivity + var yaw = _mouse_position.x + var pitch = _mouse_position.y + _mouse_position = Vector2(0, 0) - dir = dir.normalized() - dir = dir.rotated(Vector3.UP, pivot.rotation.y) - - velocity = lerp(velocity, dir * target_speed, ACCELERATION) - pivot.position += velocity - - -func _input(event: InputEvent) -> void: - if movement_active: - # Turn around - if event is InputEventMouseMotion: - pivot.rotate_y(-event.relative.x * MOUSE_SENSITIVITY) - rotate_x(-event.relative.y * MOUSE_SENSITIVITY) - rotation.x = clamp(rotation.x, -PI/2, PI/2) - - var speed_up = func(): - target_speed = clamp(target_speed + 0.015, MIN_SPEED, MAX_SPEED) - display_message("[Speed up] " + str(target_speed)) - - var slow_down = func(): - target_speed = clamp(target_speed - 0.015, MIN_SPEED, MAX_SPEED) - display_message("[Slow down] " + str(target_speed)) - - # Speed up and down with the mouse wheel - if event is InputEventMouseButton: - if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed: - slow_down.call() if invert_speed_controls else speed_up.call() - - if event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed: - speed_up.call() if invert_speed_controls else slow_down.call() - - -## Pushes new message label into "chat" and removes the old ones if necessary -func display_message(text: String) -> void: - while event_log.get_child_count() >= 3: - event_log.remove_child(event_log.get_child(0)) + # Prevents looking up/down too far + pitch = clamp(pitch, -90 - _total_pitch, 90 - _total_pitch) + _total_pitch += pitch - event_log.add_child(_make_label(text)) - - -func _make_label(text: String) -> Label: - var l = Label.new() - l.text = text - return l - - -func _add_keybindings() -> void: - var actions = InputMap.get_actions() - if "__debug_camera_forward" not in actions: _add_key_input_action("__debug_camera_forward", KEY_W) - if "__debug_camera_back" not in actions: _add_key_input_action("__debug_camera_back", KEY_S) - if "__debug_camera_left" not in actions: _add_key_input_action("__debug_camera_left", KEY_A) - if "__debug_camera_right" not in actions: _add_key_input_action("__debug_camera_right", KEY_D) - if "__debug_camera_up" not in actions: _add_key_input_action("__debug_camera_up", KEY_E) - if "__debug_camera_down" not in actions: _add_key_input_action("__debug_camera_down", KEY_Q) - if "__debug_camera_toggle" not in actions: _add_key_input_action("__debug_camera_toggle", toggle_key) - - -func _add_key_input_action(name: String, key: Key) -> void: - var ev = InputEventKey.new() - ev.physical_keycode = key - - InputMap.add_action(name) - InputMap.action_add_event(name, ev) + rotate_y(deg_to_rad(-yaw)) + rotate_object_local(Vector3(1,0,0), deg_to_rad(-pitch))