diff --git a/dupeicon.png b/dupeicon.png new file mode 100644 index 0000000..4aae694 Binary files /dev/null and b/dupeicon.png differ diff --git a/dupeicon.png.import b/dupeicon.png.import new file mode 100644 index 0000000..fd22150 --- /dev/null +++ b/dupeicon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bam5fhr8naphp" +path="res://.godot/imported/dupeicon.png-0f99285c7ee2e76447820ba231196a9c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://dupeicon.png" +dest_files=["res://.godot/imported/dupeicon.png-0f99285c7ee2e76447820ba231196a9c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/LineEditReset.gd b/scenes/LineEditReset.gd new file mode 100644 index 0000000..8d46731 --- /dev/null +++ b/scenes/LineEditReset.gd @@ -0,0 +1,20 @@ +extends LineEdit + +var lastchange: String = "" + +# Called when the node enters the scene tree for the first time. +func _ready(): + focus_exited.connect(reset_text) + text_submitted.connect(set_reset_text) + pass # Replace with function body. + +func reset_text(): + text = lastchange + +func set_reset_text(value): + lastchange = value + release_focus() + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + pass diff --git a/scenes/MicRecord.tscn b/scenes/MicRecord.tscn index 39cc967..00a18e7 100644 --- a/scenes/MicRecord.tscn +++ b/scenes/MicRecord.tscn @@ -1,5 +1,6 @@ -[gd_scene load_steps=9 format=3 uid="uid://dvjlkpjvjxn0h"] +[gd_scene load_steps=10 format=3 uid="uid://dvjlkpjvjxn0h"] +[ext_resource type="Script" path="res://scenes/LineEditReset.gd" id="2_4pwma"] [ext_resource type="Texture2D" uid="uid://c5fl4pf7cxwvc" path="res://gizmo.png" id="3_kgjae"] [ext_resource type="Theme" uid="uid://d18y4voshb0um" path="res://Menu Theme.tres" id="3_lar1l"] [ext_resource type="Script" path="res://scripts/gizmo.gd" id="4_6j4ar"] @@ -46,6 +47,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 2 color = Color(0, 1, 0, 1) [node name="AudioStreamRecord" type="AudioStreamPlayer" parent="."] @@ -56,6 +58,7 @@ bus = &"Record" [node name="ObjectsRoot" type="Control" parent="."] layout_mode = 1 anchors_preset = 0 +mouse_filter = 2 [node name="Menu" type="Control" parent="."] unique_name_in_owner = true @@ -65,6 +68,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 2 [node name="PanelContainer" type="PanelContainer" parent="Menu"] layout_mode = 1 @@ -72,6 +76,7 @@ anchors_preset = 9 anchor_bottom = 1.0 offset_right = 191.0 grow_vertical = 2 +mouse_force_pass_scroll_events = false theme_override_styles/panel = SubResource("StyleBoxFlat_qbb78") [node name="MainMenu" type="VBoxContainer" parent="Menu/PanelContainer"] @@ -79,11 +84,20 @@ unique_name_in_owner = true layout_mode = 2 theme_override_constants/separation = 6 +[node name="TitleEdit" type="LineEdit" parent="Menu/PanelContainer/MainMenu"] +unique_name_in_owner = true +layout_mode = 2 +placeholder_text = "[Profile Name]" +alignment = 1 +caret_blink = true +script = ExtResource("2_4pwma") + [node name="DeviceDropdown" type="MenuButton" parent="Menu/PanelContainer/MainMenu"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 4 text = "Input Device" +flat = false item_count = 1 popup/item_0/text = "" popup/item_0/id = 0 @@ -309,6 +323,7 @@ filters = PackedStringArray("*.json") show_hidden_files = true use_native_dialog = true +[connection signal="text_submitted" from="Menu/PanelContainer/MainMenu/TitleEdit" to="." method="_set_profile_name"] [connection signal="value_changed" from="Menu/PanelContainer/MainMenu/HBoxContainer/ThresholdSlider" to="." method="_on_v_slider_drag_ended"] [connection signal="value_changed" from="Menu/PanelContainer/MainMenu/Input Gain/VBoxContainer/InputGainSlider" to="." method="_on_input_gain_change"] [connection signal="pressed" from="Menu/PanelContainer/MainMenu/ScrollContainer/VBoxContainer/Button" to="." method="_create_new_object"] diff --git a/scenes/screen_object_menu.tscn b/scenes/screen_object_menu.tscn index 533ad57..b311c33 100644 --- a/scenes/screen_object_menu.tscn +++ b/scenes/screen_object_menu.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=3 uid="uid://crst8lnhaxjoo"] +[gd_scene load_steps=6 format=3 uid="uid://crst8lnhaxjoo"] [ext_resource type="Script" path="res://scripts/screen_object_menu.gd" id="1_0piso"] [ext_resource type="Theme" uid="uid://d18y4voshb0um" path="res://Menu Theme.tres" id="1_dxbbx"] [ext_resource type="Texture2D" uid="uid://c5fl4pf7cxwvc" path="res://gizmo.png" id="3_nubdv"] [ext_resource type="Texture2D" uid="uid://c0opx84ix6y28" path="res://contract.png" id="3_rgfwj"] +[ext_resource type="Texture2D" uid="uid://bam5fhr8naphp" path="res://dupeicon.png" id="5_esao5"] [node name="ScreenObjectMenu" type="PanelContainer" node_paths=PackedStringArray("talkbox", "bouncebox", "blinkbox", "filterbox")] offset_right = 159.0 @@ -35,6 +36,14 @@ size_flags_horizontal = 3 theme = ExtResource("1_dxbbx") text = "Change Image" +[node name="Gizmo" type="TextureButton" parent="VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(26, 0) +layout_mode = 2 +theme = ExtResource("1_dxbbx") +texture_normal = ExtResource("3_nubdv") +ignore_texture_size = true +stretch_mode = 5 + [node name="Recenter" type="TextureButton" parent="VBoxContainer/HBoxContainer"] custom_minimum_size = Vector2(26, 0) layout_mode = 2 @@ -43,11 +52,11 @@ texture_normal = ExtResource("3_rgfwj") ignore_texture_size = true stretch_mode = 5 -[node name="Gizmo" type="TextureButton" parent="VBoxContainer/HBoxContainer"] +[node name="Duplicate" type="TextureButton" parent="VBoxContainer/HBoxContainer"] custom_minimum_size = Vector2(26, 0) layout_mode = 2 theme = ExtResource("1_dxbbx") -texture_normal = ExtResource("3_nubdv") +texture_normal = ExtResource("5_esao5") ignore_texture_size = true stretch_mode = 5 @@ -135,8 +144,9 @@ theme = ExtResource("1_dxbbx") [connection signal="pressed" from="VBoxContainer/HBoxContainer/Close" to="." method="_delete_object"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/Change Image" to="." method="_request_file"] -[connection signal="pressed" from="VBoxContainer/HBoxContainer/Recenter" to="." method="_recenter"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/Gizmo" to="." method="_request_gizmo"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/Recenter" to="." method="_recenter"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/Duplicate" to="." method="_duplicate"] [connection signal="toggled" from="VBoxContainer/Booleans/HBoxContainer/PanelContainer/VBoxContainer/CheckBox" to="." method="_set_talks"] [connection signal="toggled" from="VBoxContainer/Booleans/HBoxContainer/PanelContainer2/VBoxContainer/CheckBox" to="." method="_set_bounces"] [connection signal="toggled" from="VBoxContainer/Booleans/HBoxContainer/PanelContainer3/VBoxContainer/CheckBox" to="." method="_set_blinks"] diff --git a/scripts/Menu.gd b/scripts/Menu.gd index 8c4c29c..09b5995 100644 --- a/scripts/Menu.gd +++ b/scripts/Menu.gd @@ -1,12 +1,15 @@ class_name Menu extends Control -const VERSION = 0.3 +const VERSION = 0.4 # Window Management +@onready var titleedit: LineEdit = %TitleEdit +@onready var profilename: String = "GDTuber Avatar" @onready var mainmenu: Control = %MainMenu @onready var settingsmenu: Control = %SettingsMenu @onready var background = %Background @onready var bgcolor = %BackgroundColor +@onready var menu = %Menu var menu_shown = false: set(value): _set_menu_shown( value ) @@ -31,8 +34,12 @@ var default_avatar_texture: Texture2D = preload(DEFAULT_IMAGE) var somenuscene = preload("res://scenes/screen_object_menu.tscn") # Screen Object Editing +const SNAP_ANGLE = PI/4 @export var gizmo: Gizmo var drag_target: ScreenObject +var rotating = false +var rotation_center: Vector2 = Vector2() +var starting_rotation: float = 0 # File Management @onready var file_dialog := %ImageOpenDialog @@ -96,6 +103,7 @@ func _save_data(): "version":VERSION, "threshold":threshold, "input_gain":input_gain, + "profile_name":profilename, "objects":[] } for obj in ObjectsRoot.get_children(): @@ -110,6 +118,7 @@ func _save_data(): "reactive": obj.reactive, "talking": obj.talking, "filter": obj.filter, + "rotation": obj.user_rotation, }) savedata = JSON.stringify(savedict) json_save_dialog.popup_centered() @@ -124,6 +133,9 @@ func _validate_save_json(dict, v) -> bool: 0.3:{ "threshold":TYPE_FLOAT, "input_gain":TYPE_FLOAT + }, + 0.4:{ + "profile_name":TYPE_STRING, } } for version in versions: @@ -149,6 +161,9 @@ func _validate_object_json(dict, v) -> bool: }, 0.2:{ "filter":TYPE_BOOL + }, + 0.4:{ + "rotation":TYPE_FLOAT } } for version in versions: @@ -200,6 +215,9 @@ func _load_data(path): if version >= 0.2: newobj.filter = obj["filter"] newobj.update_menu.emit() + # 0.4 + if version >= 0.4: + newobj.user_rotation = obj["rotation"] else: print("ERROR: object does not contain required fields") # Load Program Settings @@ -209,6 +227,10 @@ func _load_data(path): threshold_slider.value = threshold input_gain = save_dict["input_gain"] input_gain_slider.value = input_gain + #0.4 + if version >= 0.4: + titleedit.text = save_dict["profile_name"] + _set_profile_name(save_dict["profile_name"]) else: print("ERROR: Required Fields for Save File Version not Found") else: @@ -232,7 +254,7 @@ func _request_image(path): ### Window Management func _set_menu_shown(value: bool): - visible = value + menu.visible = value set_process_input(value) func _on_button_button_down(): @@ -259,24 +281,33 @@ func _toggle_transparent(value): func _change_background_color(color): background.color = color +func _set_profile_name(pname: String): + profilename = pname + get_tree().get_root().title = pname + + ### Screen Object Management func _create_new_object(): if MenusRoot and ObjectsRoot: var newmenu: ScreenObjectMenu = somenuscene.instantiate() as ScreenObjectMenu var newobject: ScreenObject = ScreenObject.new() - newmenu.object = newobject newobject.texture = default_avatar_texture - newmenu.request_file.connect(_on_file_button_button_down) - newmenu.tree_exiting.connect(clear_gizmo) MenusRoot.add_child(newmenu) ObjectsRoot.add_child(newobject) - newmenu.request_gizmo.connect(_on_drag_requested) - newmenu.grab_gizmo.connect(_grab_gizmo) + newmenu.object = newobject + _connect_menu(newmenu) newobject.user_position = get_viewport_rect().size/2 newmenu.update_menu() return newobject +func _connect_menu(smenu: ScreenObjectMenu): + smenu.request_file.connect(_on_file_button_button_down) + smenu.tree_exiting.connect(clear_gizmo) + smenu.duplicate_object.connect(_duplicate_object) + smenu.request_gizmo.connect(_on_drag_requested) + smenu.grab_gizmo.connect(_grab_gizmo) + func clear_gizmo(): gizmo.target = null gizmo.visible = false @@ -295,6 +326,20 @@ func _on_drag_requested(object: ScreenObject): gizmo.target = object drag_target = object +func _duplicate_object(obj: ScreenObject): + var newobj = _create_new_object() + newobj.user_position = obj.user_position + newobj.user_rotation = obj.user_rotation + newobj.user_scale = obj.user_scale + newobj.texturepath = obj.texturepath + newobj.texture = obj.texture + newobj.filter = obj.filter + newobj.reactive = obj.reactive + newobj.talking = obj.talking + newobj.blinking = obj.blinking + newobj.update_menu.emit() + pass + ### Audio Management @@ -337,7 +382,33 @@ func _on_input_gain_change(_new_input_gain : float): ### Input +func _input(event): + + if event is InputEventMouseMotion and drag_target and rotating: + var rotvector = event.global_position-rotation_center + var rotangle = atan2(rotvector.y, rotvector.x) + var targetrot = rotangle+starting_rotation + if Input.is_key_pressed(KEY_CTRL): + drag_target.user_rotation = round(targetrot/SNAP_ANGLE)*SNAP_ANGLE + else: + drag_target.user_rotation = targetrot + + if event is InputEventMouseButton and drag_target: + print("mouse button pressed please help") + match event.button_index: + MOUSE_BUTTON_RIGHT: + if event.is_pressed(): + if !rotating: + rotation_center = drag_target.global_position + var rotvector = event.global_position-rotation_center + var rotangle = atan2(rotvector.y, rotvector.x) + starting_rotation = drag_target.global_rotation - rotangle + rotating = true + else: + rotating = false + func _unhandled_input(event): + # Scroll to zoom if event is InputEventMouseButton and drag_target: if is_instance_valid(drag_target): @@ -350,4 +421,3 @@ func _unhandled_input(event): if event is InputEventKey or event is InputEventMouse: if event.is_pressed(): menu_shown = true - diff --git a/scripts/gizmo.gd b/scripts/gizmo.gd index 2ef57ac..b70fbc5 100644 --- a/scripts/gizmo.gd +++ b/scripts/gizmo.gd @@ -5,8 +5,8 @@ var target: ScreenObject # Called every frame. 'delta' is the elapsed time since the previous frame. func _input(event): - if event is InputEventMouseMotion: - if dragging: + if dragging: + if event is InputEventMouseMotion: position += event.relative if target: target.global_position = global_position diff --git a/scripts/screen_object.gd b/scripts/screen_object.gd index 32a55d1..ea3234b 100644 --- a/scripts/screen_object.gd +++ b/scripts/screen_object.gd @@ -10,7 +10,12 @@ var filter: bool = true: set(value): sprite.texture_filter = TEXTURE_FILTER_LINEAR if value else TEXTURE_FILTER_NEAREST filter = value - + +var user_rotation: float = 0: + set(value): + global_rotation = value + user_rotation = value + var id: String @export var blinking := true: set(value): diff --git a/scripts/screen_object_menu.gd b/scripts/screen_object_menu.gd index e054fe8..345b34e 100644 --- a/scripts/screen_object_menu.gd +++ b/scripts/screen_object_menu.gd @@ -2,6 +2,7 @@ class_name ScreenObjectMenu extends PanelContainer signal request_gizmo(ScreenObject) signal grab_gizmo(ScreenObject) +signal duplicate_object(ScreenObject) # signal set_filter(ScreenObject, bool) @export var talkbox: CheckBox @@ -49,7 +50,8 @@ func _request_gizmo(): emit_signal("request_gizmo", object) func _set_filter(value): - object.filter = value + if object: + object.filter = value # set_filter.emit(object, value) func update_menu(): @@ -62,7 +64,11 @@ func update_menu(): if filterbox: filterbox.button_pressed = object.filter pass - + +func _duplicate(): + duplicate_object.emit(object) + pass + func _recenter(): var viewportsize = object.get_viewport_rect().size object.user_position = viewportsize/2