0.3 and 0.4 changes

This commit is contained in:
Persephone Bubblegum-Holidy 2025-04-14 12:06:12 -07:00
parent 9c764b1271
commit e3270b14cd
29 changed files with 731 additions and 191 deletions

View file

@ -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"]

View file

@ -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"]

View file

@ -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"]

View file

@ -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="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"] [ext_resource type="Script" uid="uid://y8j8wap2o4oe" path="res://Scripts/GL_Mouse_Wheel.gd" id="2_o85ib"]

View file

@ -7,13 +7,15 @@ offset_right = 68.0
offset_bottom = 31.0 offset_bottom = 31.0
selected = 0 selected = 0
allow_reselect = true allow_reselect = true
item_count = 3 item_count = 4
popup/item_0/text = "+Add Float" popup/item_0/text = "+Add Float"
popup/item_0/id = 0 popup/item_0/id = 0
popup/item_1/text = "+Add Bool" popup/item_1/text = "+Add Bool"
popup/item_1/id = 1 popup/item_1/id = 1
popup/item_2/text = "+Add Color" popup/item_2/text = "+Add Color"
popup/item_2/id = 2 popup/item_2/id = 2
popup/item_3/text = "+Add Audio"
popup/item_3/id = 3
script = ExtResource("1_vw1dw") script = ExtResource("1_vw1dw")
[node name="Panel" type="PanelContainer" parent="."] [node name="Panel" type="PanelContainer" parent="."]

View file

@ -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://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://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://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://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"] [node name="Node Row" type="HBoxContainer"]
@ -41,6 +42,30 @@ custom_minimum_size = Vector2(50, 0)
layout_mode = 2 layout_mode = 2
script = ExtResource("4_yal7b") 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"]] [node name="Output" type="Button" parent="." groups=["Outputs"]]
layout_mode = 2 layout_mode = 2
mouse_default_cursor_shape = 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="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="color_changed" from="Pick Color" to="Pick Color" method="color_changed"]
[connection signal="toggled" from="Pick Bool" to="Pick Bool" method="toggled"] [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"] [connection signal="button_down" from="Output" to="Output" method="_start_drag"]

View file

@ -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"] [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"] [node name="Node" type="PanelContainer"]
clip_contents = true clip_contents = true
custom_minimum_size = Vector2(200, 100) custom_minimum_size = Vector2(200, 100)
@ -21,12 +23,10 @@ layout_mode = 2
[node name="Title" type="HBoxContainer" parent="Margins/Holder"] [node name="Title" type="HBoxContainer" parent="Margins/Holder"]
layout_mode = 2 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 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
text = "Test" theme_override_styles/normal = SubResource("StyleBoxEmpty_arhwt")
horizontal_alignment = 1
clip_text = true
[node name="Exit Button" type="Button" parent="Margins/Holder/Title"] [node name="Exit Button" type="Button" parent="Margins/Holder/Title"]
layout_mode = 2 layout_mode = 2

View file

@ -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="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="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"] [sub_resource type="Gradient" id="Gradient_xwfut"]
offsets = PackedFloat32Array(0) colors = PackedColorArray(0.448074, 0.0582233, 0.099986, 1, 0.330802, 0.066494, 0.0423852, 1)
colors = PackedColorArray(0.121569, 0.121569, 0.121569, 0.501961)
[sub_resource type="GradientTexture1D" id="GradientTexture1D_2eix6"] [sub_resource type="GradientTexture1D" id="GradientTexture1D_jyqbx"]
gradient = SubResource("Gradient_jyqbx") gradient = SubResource("Gradient_xwfut")
[node name="NodeMap" type="Control"] [node name="NodeMap" type="Control" groups=["Node Map"]]
layout_mode = 3 layout_mode = 3
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
@ -26,22 +26,72 @@ anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
texture = SubResource("GradientTexture1D_2eix6") texture = SubResource("GradientTexture1D_jyqbx")
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
[node name="Holder" type="Control" parent="."] [node name="Holder" type="Control" parent="."]
anchors_preset = 0 anchors_preset = 0
offset_right = 40.0 offset_right = 40.0
offset_bottom = 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"]

14
Scripts/GL_Audio.gd Normal file
View file

@ -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")

1
Scripts/GL_Audio.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://bmukxrwmoyc20

3
Scripts/GL_AudioType.gd Normal file
View file

@ -0,0 +1,3 @@
extends Node
class_name GL_AudioType
var value:String = ""

View file

@ -0,0 +1 @@
uid://dfxs3vmqxy1eu

37
Scripts/GL_Keystrokes.gd Normal file
View file

@ -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)

View file

@ -0,0 +1 @@
uid://e6v6exlrhtaq

16
Scripts/GL_Mix_Floats.gd Normal file
View file

@ -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")

View file

@ -0,0 +1 @@
uid://beit3xudynjdl

View file

@ -13,7 +13,7 @@ func _process(delta):
_send_input("Output") _send_input("Output")
func _input(event): func _unhandled_input(event):
# Check if the mouse wheel up or down button is pressed # Check if the mouse wheel up or down button is pressed
if event is InputEventMouseButton: if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed: if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed:

View file

@ -1,7 +1,8 @@
extends PanelContainer extends PanelContainer
class_name GL_Node class_name GL_Node
var rows : Dictionary var rows : Dictionary
var uuid : int #REMEMBER TO SET THIS ON CREATION var uuid : String
var nodePath:String
var dragging : bool var dragging : bool
var canDrag : bool var canDrag : bool
var dragOffset : Vector2 var dragOffset : Vector2
@ -18,6 +19,15 @@ func _ready():
func _process(delta): func _process(delta):
if dragging: if dragging:
position = get_viewport().get_mouse_position() + dragOffset 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): func _input(event):
if event is InputEventMouseButton: if event is InputEventMouseButton:
@ -30,7 +40,7 @@ func _input(event):
func _create_uuid(): func _create_uuid():
var rand = RandomNumberGenerator.new() var rand = RandomNumberGenerator.new()
rand.seed = Time.get_unix_time_from_system() rand.seed = Time.get_unix_time_from_system()
uuid = rand.randi() uuid = str(rand.randi())
func _update_visuals(): func _update_visuals():
var holder = get_node("Margins").get_node("Holder") var holder = get_node("Margins").get_node("Holder")
@ -65,6 +75,10 @@ func _update_visuals():
TYPE_BOOL: TYPE_BOOL:
assignPick(nodeRow.get_node("Pick Bool"),str(key)) assignPick(nodeRow.get_node("Pick Bool"),str(key))
(nodeRow.get_node("Pick Bool") as CheckButton).button_pressed = rows[key]["pickValue"] (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: else:
(nodeRow.get_node("Label") as Label).size_flags_horizontal = Control.SIZE_EXPAND_FILL (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: TYPE_COLOR:
label.text = "" label.text = ""
label.add_theme_color_override("font_color", Color.WHITE_SMOKE) label.add_theme_color_override("font_color", Color.WHITE_SMOKE)
_: if value is GL_AudioType:
label.text = "🔈"
label.add_theme_color_override("font_color", Color.BLUE_VIOLET)
if value == null:
label.visible = false label.visible = false
func _set_title(name:String): 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): func _create_row(name:String,input,output,picker:bool,pickDefault,pickFloatMaximum:float):
if rows.has(name): if rows.has(name):
@ -126,11 +147,11 @@ func _send_input(output_name: String):
if not rows.has(output_name): if not rows.has(output_name):
return return
var connections = rows[output_name].get("connections", []) for conn in rows[output_name].get("connections", []):
for conn in connections:
var target = conn.get("target", null) var target = conn.get("target", null)
var input_name = conn.get("input_name", null) var input_name = conn.get("input_name", null)
if target and input_name: if target and input_name:
if typeof(target) != TYPE_INT:
target._recieve_input(input_name, rows[output_name]["output"]) target._recieve_input(input_name, rows[output_name]["output"])
func _confirm_backConnection(input_name:String): func _confirm_backConnection(input_name:String):
@ -146,8 +167,9 @@ func _create_connection(target:GL_Node,input_name:String,output_name:String):
if item == null: if item == null:
return return
if typeof(rows[output_name].get("output", null)) != typeof(target.rows[input_name].get("input",null)): var typeA = typeof(rows[output_name].get("output", null))
if !(typeof(rows[output_name].get("output", null)) == TYPE_BOOL && typeof(target.rows[input_name].get("input",null)) == TYPE_FLOAT): 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) print("Type mismatch: cannot connect " + output_name + " to " + target.name)
return return
@ -156,7 +178,7 @@ func _create_connection(target:GL_Node,input_name:String,output_name:String):
"input_name": input_name "input_name": input_name
} }
var connections = rows[output_name].get("connections",[]) var connections = rows[output_name].get("connections", [])
for connection in connections: for connection in connections:
if connection.target == thenew.target and connection.input_name == thenew.input_name: 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: if node is GL_Node_Point:
for key in rows: for key in rows:
node.mainNode.destroy_connection(self,key) node.mainNode.destroy_connection(self,key)
queue_free() get_parent().queue_free()

View file

@ -21,6 +21,8 @@ func _named(name:String):
mainNode._create_row(name,false,false,true,false,0) mainNode._create_row(name,false,false,true,false,0)
2: 2:
mainNode._create_row(name,Color.WHITE,Color.WHITE,true,Color.WHITE,0) 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() mainNode._update_visuals()
func _cancelled(): func _cancelled():
disabled = false disabled = false

View file

@ -1,32 +1,60 @@
extends Control extends Control
class_name GL_Node_Map
var background: TextureRect
var holder: Control var holder: Control
var is_panning: bool = false var is_panning: bool = false
var last_mouse_pos: Vector2 var last_mouse_pos: Vector2
var is_hovered: bool = false 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(): func _ready():
visible = false visible = false
background = get_node("Background")
holder = get_node("Holder") 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_entered", _on_mouse_entered)
connect("mouse_exited", _on_mouse_exited) 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(): func _on_mouse_entered():
is_hovered = true is_hovered = true
func _on_mouse_exited(): func _on_mouse_exited():
is_hovered = false is_hovered = false
func _input(event: InputEvent) -> void: func _input(event: InputEvent) -> void:
if event is InputEventKey and event.pressed: if event is InputEventKey and event.pressed:
match event.keycode: match event.keycode:
KEY_ESCAPE: KEY_ESCAPE:
visible = not visible visible = not visible
KEY_TAB:
background.self_modulate.a = abs(background.self_modulate.a - 1)
if not visible: if not visible:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
return
else: else:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
@ -50,14 +78,11 @@ func _input(event: InputEvent) -> void:
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN: elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
zoom_factor = 0.9 zoom_factor = 0.9
# Apply scale
holder.scale *= zoom_factor holder.scale *= zoom_factor
# Recalculate the new local position of the mouse after scaling
var new_global_xform = holder.get_global_transform() var new_global_xform = holder.get_global_transform()
var new_local_mouse_pos = new_global_xform.affine_inverse().basis_xform(mouse_pos) 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) var delta = (new_local_mouse_pos - local_mouse_pos)
holder.position += delta * holder.scale holder.position += delta * holder.scale
@ -65,3 +90,169 @@ func _input(event: InputEvent) -> void:
var delta = event.position - last_mouse_pos var delta = event.position - last_mouse_pos
holder.position += delta holder.position += delta
last_mouse_pos = event.position 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()

View file

@ -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 ""

View file

@ -0,0 +1 @@
uid://rmlqvxot3kys

View file

@ -26,6 +26,8 @@ func _process(delta):
previewLine.default_color = Color.BLACK previewLine.default_color = Color.BLACK
TYPE_COLOR: TYPE_COLOR:
previewLine.default_color = output previewLine.default_color = output
if output is GL_AudioType:
previewLine.default_color = Color.BLUE_VIOLET
var connections = mainNode.rows[valueName].get("connections",[]) var connections = mainNode.rows[valueName].get("connections",[])
if connections != []: if connections != []:
@ -43,7 +45,13 @@ func _process(delta):
child.default_color = Color.BLACK child.default_color = Color.BLACK
TYPE_COLOR: TYPE_COLOR:
child.default_color = output 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[0] = global_position + Vector2(size.x / 2, size.y / 2)
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 child.points[1] = (connections[iter]["target"] as GL_Node).give_input_point_pos(connections[iter]["input_name"])# - child.global_position
iter += 1 iter += 1

View file

@ -6,6 +6,7 @@ extends GL_Node
func _ready(): func _ready():
super._ready() super._ready()
_set_title(identification)
for i in names.size(): for i in names.size():
match(types[i].to_lower()): match(types[i].to_lower()):
"float": "float":
@ -14,6 +15,8 @@ func _ready():
_create_row(str(names[i]),Color.WHITE,null,true,Color.WHITE,0) _create_row(str(names[i]),Color.WHITE,null,true,Color.WHITE,0)
"bool": "bool":
_create_row(str(names[i]),false,null,true,false,0) _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() _update_visuals()
func _process(delta): func _process(delta):

View file

@ -1,4 +1,5 @@
extends GL_Node extends GL_Node
class_name GL_Record
var timer:float var timer:float
const sampleRate = 0.05 const sampleRate = 0.05
var recording:Dictionary var recording:Dictionary
@ -59,7 +60,14 @@ func _traverse():
recording[key]["lastUsed"] = current recording[key]["lastUsed"] = current
recording[key]["current"] = newCurrent recording[key]["current"] = newCurrent
if recording[key]["lastUsed"] != null && recording[key]["current"] != recording[key]["end"]: if recording[key]["lastUsed"] != null && recording[key]["current"] != recording[key]["end"]:
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"])) 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: func remap_time(value: float, start: float, end: float) -> float:
if start == end: if start == end:
return 0.0 return 0.0
@ -80,10 +88,6 @@ func _record():
for key in recording: for key in recording:
if key == "Recording" || key == "Current Time": if key == "Recording" || key == "Current Time":
continue 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"] var currentSave = recording[key]["current"]
if currentSave == null: if currentSave == null:
var id = "ID_" + str(rng.randi()) var id = "ID_" + str(rng.randi())
@ -98,7 +102,11 @@ func _record():
recording[key]["end"] = id recording[key]["end"] = id
rows[key]["output"] = recording[key]["list"][id]["value"] rows[key]["output"] = recording[key]["list"][id]["value"]
continue 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 if time < oldTime: #rewind
continue #fix pls continue #fix pls
else: #forward else: #forward

View file

@ -7,14 +7,17 @@ var rows : Dictionary = {
"ChuckSpot":1, "ChuckSpot":1,
"HelenSpot":1, "HelenSpot":1,
"MunchSpot":1, "MunchSpot":1,
"Audio":1,
"Bool":1, "Bool":1,
"Color":1, "Color":1,
"Direct Output":1,
"Float":1, "Float":1,
"Invert":1, "Invert":1,
"MiscKeys":1, "MiscKeys":1,
"NumberKeys":1, "NumberKeys":1,
"Keystroke Ramp":1, "Keystroke Ramp":1,
"Lerp":1, "Lerp":1,
"Mix Floats":1,
"Mix Colors":1, "Mix Colors":1,
"Mouse Wheel":1, "Mouse Wheel":1,
"Random":1, "Random":1,

45
Scripts/GL_Speaker.gd Normal file
View file

@ -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

View file

@ -0,0 +1 @@
uid://c5kxam0k3beml

View file

@ -1,146 +1,114 @@
extends Camera3D class_name FreeLookCamera extends Camera3D
## Camera with flying script attached to it. # Modifier keys' speed multiplier
class_name Freecam3D const SHIFT_MULTIPLIER = 2.5
const ALT_MULTIPLIER = 1.0 / SHIFT_MULTIPLIER
##
## Camera with toggleable freecam mode for prototyping when creating levels, shaders, lighting, etc.
##
## Usage: Run your game, press <TAB> 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
## Sets up pivot and UI overlay elements. @export_range(0.0, 1.0) var sensitivity: float = 0.25
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)
screen_overlay.add_child(event_log) # Mouse state
screen_overlay.visible = overlay_text 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
func _ready() -> void: # Keyboard state
_setup_nodes.call_deferred() var _w = false
_add_keybindings() var _s = false
movement_active = true var _a = false
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) var _d = false
var _q = false
var _e = false
var _shift = false
var _alt = false
func _input(event):
# Receives mouse motion
func _process(delta: float) -> void:
if Input.is_action_just_released("__debug_camera_toggle"):
movement_active = not movement_active
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
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: if event is InputEventMouseMotion:
pivot.rotate_y(-event.relative.x * MOUSE_SENSITIVITY) _mouse_position = event.relative
rotate_x(-event.relative.y * MOUSE_SENSITIVITY)
rotation.x = clamp(rotation.x, -PI/2, PI/2)
var speed_up = func(): # Receives mouse button input
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 is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed: match event.button_index:
slow_down.call() if invert_speed_controls else speed_up.call() 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)
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed: # Receives key input
speed_up.call() if invert_speed_controls else slow_down.call() 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
# Updates mouselook and movement every frame
func _process(delta):
_update_mouselook()
_update_movement(delta)
## Pushes new message label into "chat" and removes the old ones if necessary # Updates camera movement
func display_message(text: String) -> void: func _update_movement(delta):
while event_log.get_child_count() >= 3: # Computes desired direction from key states
event_log.remove_child(event_log.get_child(0)) _direction = Vector3(
(_d as float) - (_a as float),
(_e as float) - (_q as float),
(_s as float) - (_w as float)
)
event_log.add_child(_make_label(text)) # 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
# Compute modifiers' speed multiplier
var speed_multi = 1
if _shift: speed_multi *= SHIFT_MULTIPLIER
if _alt: speed_multi *= ALT_MULTIPLIER
func _make_label(text: String) -> Label: # Checks if we should bother translating the camera
var l = Label.new() if _direction == Vector3.ZERO and offset.length_squared() > _velocity.length_squared():
l.text = text # Sets the velocity to 0 to prevent jittering due to imperfect deceleration
return l _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)
func _add_keybindings() -> void: # Updates mouse look
var actions = InputMap.get_actions() func _update_mouselook():
if "__debug_camera_forward" not in actions: _add_key_input_action("__debug_camera_forward", KEY_W) # Only rotates mouse if the mouse is captured
if "__debug_camera_back" not in actions: _add_key_input_action("__debug_camera_back", KEY_S) if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
if "__debug_camera_left" not in actions: _add_key_input_action("__debug_camera_left", KEY_A) _mouse_position *= sensitivity
if "__debug_camera_right" not in actions: _add_key_input_action("__debug_camera_right", KEY_D) var yaw = _mouse_position.x
if "__debug_camera_up" not in actions: _add_key_input_action("__debug_camera_up", KEY_E) var pitch = _mouse_position.y
if "__debug_camera_down" not in actions: _add_key_input_action("__debug_camera_down", KEY_Q) _mouse_position = Vector2(0, 0)
if "__debug_camera_toggle" not in actions: _add_key_input_action("__debug_camera_toggle", toggle_key)
# Prevents looking up/down too far
pitch = clamp(pitch, -90 - _total_pitch, 90 - _total_pitch)
_total_pitch += pitch
func _add_key_input_action(name: String, key: Key) -> void: rotate_y(deg_to_rad(-yaw))
var ev = InputEventKey.new() rotate_object_local(Vector3(1,0,0), deg_to_rad(-pitch))
ev.physical_keycode = key
InputMap.add_action(name)
InputMap.action_add_event(name, ev)