diff --git a/src/resources/themes/default.tres b/src/resources/themes/default.tres index 54206bbb..9a10f21f 100644 --- a/src/resources/themes/default.tres +++ b/src/resources/themes/default.tres @@ -135,6 +135,7 @@ PopupMenu/constants/item_start_padding = 5 PopupMenu/constants/v_separation = 10 PopupMenu/styles/hover = SubResource("StyleBoxFlat_kpyni") PopupMenu/styles/panel = SubResource("StyleBoxFlat_x2xen") +PopupPanel/styles/panel = SubResource("StyleBoxFlat_dgxsq") ScrollContainer/constants/scrollbar_h_separation = 10 SessionListing/base_type = &"Button" SessionListing/styles/hover = SubResource("StyleBoxFlat_gvfoe") diff --git a/src/scenes/levels/grid.tscn b/src/scenes/levels/grid.tscn index 333f4887..a786e696 100644 --- a/src/scenes/levels/grid.tscn +++ b/src/scenes/levels/grid.tscn @@ -2,16 +2,16 @@ [ext_resource type="Shader" uid="uid://bh250q6rfv615" path="res://resources/shaders/grid_shader.gdshader" id="1_ikf4c"] -[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_dbach"] -sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) -ground_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_bcgop"] +sky_top_color = Color(0.297, 0.40241668, 0.55, 1) +energy_multiplier = 1.25 -[sub_resource type="Sky" id="Sky_ikf4c"] -sky_material = SubResource("ProceduralSkyMaterial_dbach") +[sub_resource type="Sky" id="Sky_gbca3"] +sky_material = SubResource("ProceduralSkyMaterial_bcgop") [sub_resource type="Environment" id="Environment_q28r8"] background_mode = 2 -sky = SubResource("Sky_ikf4c") +sky = SubResource("Sky_gbca3") tonemap_mode = 2 ssr_enabled = true ssao_enabled = true @@ -45,10 +45,11 @@ metadata/scene_node = true environment = SubResource("Environment_q28r8") metadata/scene_node = true -[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1644270184] +[node name="Ground" type="MeshInstance3D" parent="." unique_id=1644270184] mesh = SubResource("QuadMesh_8gpll") +metadata/scene_node = true -[node name="StaticBody3D" type="StaticBody3D" parent="MeshInstance3D" unique_id=1945745055] +[node name="StaticBody3D" type="StaticBody3D" parent="Ground" unique_id=1945745055] -[node name="CollisionShape3D" type="CollisionShape3D" parent="MeshInstance3D/StaticBody3D" unique_id=1464818279] +[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground/StaticBody3D" unique_id=1464818279] shape = SubResource("BoxShape3D_8gpll") diff --git a/src/scenes/managers/scene/player.gd b/src/scenes/managers/scene/player.gd index 9bf1fffc..3bc95dca 100644 --- a/src/scenes/managers/scene/player.gd +++ b/src/scenes/managers/scene/player.gd @@ -69,3 +69,6 @@ func spawn_player(peer_id: String) -> void: func kill_player() -> void: GlobalLogger.log("'%s' is not implemented." % get_stack()[0]["function"], Enum.LogLevel.WARNING) return + +func get_player_count() -> int: + return players.keys().size() diff --git a/src/scenes/managers/scene/spawnable_manager.gd b/src/scenes/managers/scene/spawnable_manager.gd index c1a7964f..a87da0c7 100644 --- a/src/scenes/managers/scene/spawnable_manager.gd +++ b/src/scenes/managers/scene/spawnable_manager.gd @@ -88,7 +88,6 @@ func create(node_type: String, node_parent: int = 0, model_path: String = "") -> var database_index: int = _database.find_custom(func(entry): return entry.id == entity) return _database[database_index].node - @rpc("any_peer", "reliable") func destroy(node_id: int) -> Variant: var my_id: int = app_network_m._database.sessions_api[app_scene_m.active_session].get_unique_id() @@ -109,7 +108,6 @@ func destroy(node_id: int) -> Variant: await rpcawaiter.send_rpc(1, destroy.bind(node_id)) return - @rpc("any_peer", "reliable") func set_transform(node_id: int, transform: Transform3D) -> void: var _my_id: int = app_network_m._database.sessions_api[app_scene_m.active_session].get_unique_id() @@ -123,6 +121,17 @@ func set_transform(node_id: int, transform: Transform3D) -> void: return return +@rpc("any_peer", "reliable") +func set_property(node_id: int, property_name: String, property_value: Variant) -> void: + var _my_id: int = app_network_m._database.sessions_api[app_scene_m.active_session].get_unique_id() + var _caller_id: int = multiplayer.get_remote_sender_id() + + if _my_id == 1: + set_property_on_spawnable.rpc(node_id, property_name, property_value) + else: + await rpcawaiter.send_rpc(1, set_property.bind(node_id, property_name, property_value)) + return + return # TODO: How would large assets work? @rpc("authority", "reliable") @@ -165,6 +174,13 @@ func transform_spawnable(node_id: int, transform: Transform3D) -> void: _entity_db.node.transform = transform return +@rpc("call_local", "authority", "reliable") +func set_property_on_spawnable(node_id: int, property_name: String, property_value: Variant): + var _entity_db = get_by_id(node_id) + + _entity_db.node.set_indexed(property_name, property_value) + return + func _get_deletion_queue(node_name: String) -> Array: var _entry_index = _database.find_custom(func(item): return item.id == int(node_name)) var _node = _database[_entry_index].node diff --git a/src/userinterface/client_edit_mode/desktop/inspector.tscn b/src/userinterface/client_edit_mode/desktop/inspector.tscn index e2dcb7b7..3120b2fa 100644 --- a/src/userinterface/client_edit_mode/desktop/inspector.tscn +++ b/src/userinterface/client_edit_mode/desktop/inspector.tscn @@ -13,8 +13,12 @@ [ext_resource type="Texture2D" uid="uid://bu6vtb71stcjq" path="res://resources/icons/godot/ToolScale.svg" id="7_7pj7g"] [ext_resource type="Texture2D" uid="uid://8rvofwa4k380" path="res://resources/icons/godot/WorldEnvironment.svg" id="8_7pj7g"] [ext_resource type="Texture2D" uid="uid://bx7irvaofgk6n" path="res://resources/icons/godot/CharacterBody3D.svg" id="9_840b7"] +[ext_resource type="PackedScene" uid="uid://wmjsfgx486xa" path="res://userinterface/client_edit_mode/node_property_editor/partials/bool.tscn" id="14_8u8qu"] +[ext_resource type="Script" uid="uid://8lm0oig2kxbj" path="res://userinterface/client_edit_mode/desktop/scripts/properties.gd" id="14_at34i"] [ext_resource type="PackedScene" uid="uid://chbjqqcikq7aj" path="res://userinterface/client_edit_mode/desktop/partial/popup_selection.tscn" id="14_cuj5f"] +[ext_resource type="PackedScene" uid="uid://dkn0gmy8or4hq" path="res://userinterface/client_edit_mode/node_property_editor/partials/Vector3.tscn" id="14_q5c3n"] [ext_resource type="Texture2D" uid="uid://cq2ec6jy8kynp" path="res://resources/icons/godot/Add.svg" id="15_3i2mc"] +[ext_resource type="PackedScene" uid="uid://c7animyvghvk" path="res://userinterface/client_edit_mode/node_property_editor/partials/FoldableContainer.tscn" id="16_73wcm"] [ext_resource type="PackedScene" uid="uid://sntuc3p6k4s6" path="res://userinterface/dash/partials/generic_button.tscn" id="16_j7nog"] [ext_resource type="Texture2D" uid="uid://b54viv8d54q83" path="res://resources/icons/godot/Remove.svg" id="17_akq5y"] [ext_resource type="Texture2D" uid="uid://dtwfvglraa8t7" path="res://resources/icons/godot/Edit.svg" id="18_akq5y"] @@ -279,6 +283,7 @@ mouse_filter = 1 visible = false custom_minimum_size = Vector2(300, 0) layout_mode = 2 +script = ExtResource("14_at34i") [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties" unique_id=1772787264] layout_mode = 2 @@ -301,6 +306,53 @@ mouse_filter = 1 theme = ExtResource("2_plsgj") theme_override_styles/panel = SubResource("StyleBoxFlat_plsgj") +[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container" unique_id=1256337211] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +horizontal_scroll_mode = 0 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container/ScrollContainer" unique_id=1523084746] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container/ScrollContainer/MarginContainer" unique_id=413793585] +layout_mode = 2 + +[node name="Visible" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container/ScrollContainer/MarginContainer/VBoxContainer" unique_id=933711176 instance=ExtResource("14_8u8qu")] +layout_mode = 2 +property_name = "Visible" + +[node name="FoldableContainer" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1542739099 instance=ExtResource("16_73wcm")] +layout_mode = 2 +folded = false + +[node name="VBoxContainer" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container/ScrollContainer/MarginContainer/VBoxContainer/FoldableContainer" index="0" unique_id=238553897] +visible = true + +[node name="Position" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container/ScrollContainer/MarginContainer/VBoxContainer/FoldableContainer/VBoxContainer" index="0" unique_id=352534286 instance=ExtResource("14_q5c3n")] +layout_mode = 2 +size_flags_vertical = 1 +property_name = "Position" + +[node name="Rotation" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container/ScrollContainer/MarginContainer/VBoxContainer/FoldableContainer/VBoxContainer" index="1" unique_id=204239032 instance=ExtResource("14_q5c3n")] +layout_mode = 2 +size_flags_vertical = 1 +property_name = "Rotation" + +[node name="Scale" parent="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container/ScrollContainer/MarginContainer/VBoxContainer/FoldableContainer/VBoxContainer" index="2" unique_id=172565004 instance=ExtResource("14_q5c3n")] +layout_mode = 2 +size_flags_vertical = 1 +property_name = "Scale" + [node name="InspectorPopup" parent="." unique_id=629092317 instance=ExtResource("14_cuj5f")] visible = false layout_mode = 0 @@ -333,13 +385,9 @@ icon = ExtResource("18_akq5y") [node name="AddNodeWindow" parent="." unique_id=524781090 instance=ExtResource("21_q5c3n")] visible = false layout_mode = 1 -offset_left = 50.0 -offset_top = 50.0 -offset_right = -1570.0 -offset_bottom = -630.0 -window_name = "Add Node" [editable path="VBoxContainer/HBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/Container/VBoxContainer/InputString"] [editable path="VBoxContainer/HBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/Container/VBoxContainer/Filters/HBoxContainer/Light"] [editable path="VBoxContainer/HBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/Container/VBoxContainer/Filters/HBoxContainer/Player"] +[editable path="VBoxContainer/HBoxContainer/HSplitContainer/Properties/VBoxContainer/Container/ScrollContainer/MarginContainer/VBoxContainer/FoldableContainer"] [editable path="InspectorPopup"] diff --git a/src/userinterface/client_edit_mode/desktop/partial/add_node_window.tscn b/src/userinterface/client_edit_mode/desktop/partial/add_node_window.tscn index 091d1bba..30ac9690 100644 --- a/src/userinterface/client_edit_mode/desktop/partial/add_node_window.tscn +++ b/src/userinterface/client_edit_mode/desktop/partial/add_node_window.tscn @@ -28,6 +28,7 @@ grow_vertical = 2 size_flags_horizontal = 4 size_flags_vertical = 4 script = ExtResource("1_u7aab") +window_name = null [node name="BackgroundColor" type="Panel" parent="." unique_id=889545988] material = SubResource("ShaderMaterial_j380t") diff --git a/src/userinterface/client_edit_mode/desktop/partial/movable_window.tscn b/src/userinterface/client_edit_mode/desktop/partial/movable_window.tscn new file mode 100644 index 00000000..70f00f7d --- /dev/null +++ b/src/userinterface/client_edit_mode/desktop/partial/movable_window.tscn @@ -0,0 +1,75 @@ +[gd_scene format=3 uid="uid://b6qb2oq26yooy"] + +[ext_resource type="Script" uid="uid://bapubedba8eiu" path="res://userinterface/client_edit_mode/desktop/scripts/moveable_window.gd" id="1_le5nq"] +[ext_resource type="Shader" uid="uid://bwr4b0mhgi4w6" path="res://userinterface/dash/shaders/blur.gdshader" id="2_4cmho"] +[ext_resource type="Theme" uid="uid://ckqsvvccivw0x" path="res://resources/themes/default.tres" id="3_ddx82"] +[ext_resource type="PackedScene" uid="uid://sntuc3p6k4s6" path="res://userinterface/dash/partials/generic_button.tscn" id="4_l1byb"] +[ext_resource type="Texture2D" uid="uid://bmjw8t1c6bi75" path="res://resources/icons/godot/Close.svg" id="5_752f5"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_j380t"] +shader = ExtResource("2_4cmho") +shader_parameter/color = Color(0.2, 0.2, 0.2, 1) +shader_parameter/blur_amount = 3.0000001392401203 +shader_parameter/roughness = 0.2 +shader_parameter/roughness_affect = 0.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_u7aab"] +bg_color = Color(0.078431375, 0.078431375, 0.078431375, 0.78431374) + +[node name="MoveableWindow" type="Control" unique_id=524781090] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -1620.0 +offset_bottom = -680.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +script = ExtResource("1_le5nq") + +[node name="BackgroundColor" type="Panel" parent="." unique_id=889545988] +material = SubResource("ShaderMaterial_j380t") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +theme = ExtResource("3_ddx82") +theme_override_styles/panel = SubResource("StyleBoxFlat_u7aab") + +[node name="MarginContainer" type="MarginContainer" parent="BackgroundColor" unique_id=802103033] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="VBoxContainer" type="VBoxContainer" parent="BackgroundColor/MarginContainer" unique_id=969090938] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="BackgroundColor/MarginContainer/VBoxContainer" unique_id=1407239756] +layout_mode = 2 + +[node name="Label" type="Label" parent="BackgroundColor/MarginContainer/VBoxContainer/HBoxContainer" unique_id=665280700] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 16 +text = "Window Name" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="GenericButton" parent="BackgroundColor/MarginContainer/VBoxContainer/HBoxContainer" unique_id=649942124 instance=ExtResource("4_l1byb")] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +size_flags_horizontal = 1 +icon = ExtResource("5_752f5") +only_icon = true diff --git a/src/userinterface/client_edit_mode/desktop/scripts/add_node_window.gd b/src/userinterface/client_edit_mode/desktop/scripts/add_node_window.gd index 4a94fdd8..ae217a88 100644 --- a/src/userinterface/client_edit_mode/desktop/scripts/add_node_window.gd +++ b/src/userinterface/client_edit_mode/desktop/scripts/add_node_window.gd @@ -6,21 +6,11 @@ # License: MIT License # Authors: Armored Dragon # --- License -extends Control - -signal selection(type: String, parent: int) - -@export var window_name: String = "Window Name" +extends "moveable_window.gd" @onready var _list_container = get_node("BackgroundColor/MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer") -@onready var _button_close = get_node("BackgroundColor/MarginContainer/VBoxContainer/HBoxContainer/GenericButton") -@onready var _node_window_title = get_node("BackgroundColor/MarginContainer/VBoxContainer/HBoxContainer/Label") - @onready var _button_template = preload("res://userinterface/dash/partials/generic_button.tscn") -var _is_dragging: bool = false -var _drag_offset: Vector2 - var parent_node_id: int = 0 func _ready() -> void: diff --git a/src/userinterface/client_edit_mode/desktop/scripts/inspector.gd b/src/userinterface/client_edit_mode/desktop/scripts/inspector.gd index 79db473f..2d630f7e 100644 --- a/src/userinterface/client_edit_mode/desktop/scripts/inspector.gd +++ b/src/userinterface/client_edit_mode/desktop/scripts/inspector.gd @@ -32,12 +32,15 @@ var _gizmo_space_local: bool = true @onready var _node_toolbar_gizmo_control_container: Control = _node_toolbar.get_node("MarginContainer/HBoxContainer/GizmoControl") @onready var _node_toolbar_gizmo_control_container_misc: Control = _node_toolbar.get_node("MarginContainer/HBoxContainer/GizmoControlMisc") @onready var _node_toolbar_spawnable_count: Control = _node_toolbar.get_node("MarginContainer/HBoxContainer/SpawnableCount") +@onready var _node_toolbar_player_count: Control = _node_toolbar.get_node("MarginContainer/HBoxContainer/PlayerCount") @onready var _node_crosshair: Node = get_tree().current_scene.get_node("Crosshair") @onready var _inspector_popup: Node = get_node("InspectorPopup") @onready var _inspector_add_node_window: Node = get_node("AddNodeWindow") @onready var _inspector_filter_search: LineEdit = get_node("VBoxContainer/HBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/Container/VBoxContainer/InputString/MarginContainer/HBoxContainer/MarginContainer/LineEdit") @onready var _inspector_filters: Node = get_node("VBoxContainer/HBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/Container/VBoxContainer/Filters/HBoxContainer") +@onready var _node_property_editor: Node = get_node("VBoxContainer/HBoxContainer/HSplitContainer/Properties") + # UI Inspector @onready var _inspector_tree: Tree = get_node("VBoxContainer/HBoxContainer/HSplitContainer/MarginContainer/VBoxContainer/Container/VBoxContainer/MarginContainer/Tree") @@ -198,6 +201,7 @@ func _inspector_tree_item_mouse_selected(mouse_position: Vector2, mouse_button_i return _inspector_selected_node = _inspector_selected.get_metadata(0) + _set_npe_state(_inspector_selected_node != null) _select(int(_inspector_selected.get_metadata(0).name)) return @@ -209,6 +213,7 @@ func _inspector_tree_item_mouse_selected(mouse_position: Vector2, mouse_button_i return _inspector_selected_node = _inspector_selected.get_metadata(0) + _set_npe_state(_inspector_selected_node != null) var _mouse_pos = _inspector_tree.get_global_mouse_position() _inspector_popup.position = _mouse_pos @@ -260,6 +265,7 @@ func _inspector_build(root_node: Node = session_root) -> void: await get_tree().process_frame var _total_spawnable_label: String = str(session_spawnable_m._database.size()) + var _total_player_label: String = str(session_players_m.get_player_count()) GlobalLogger.log("Generating the inspector view with parent '%s'." % root_node) _inspector_save_openness() @@ -272,6 +278,7 @@ func _inspector_build(root_node: Node = session_root) -> void: # Update the Toolbar counts. _node_toolbar_spawnable_count.update_meta(_total_spawnable_label) + _node_toolbar_player_count.update_meta(_total_player_label) return @@ -456,6 +463,8 @@ func _select(target_node: int) -> void: if _node_db_entry == {}: GlobalLogger.log("Tried to select a node that should not exist!", Enum.LogLevel.ERROR) + _inspector_selected_node == null + _set_npe_state(false) return my_gizmo = await session_spawnable_m.create("Gizmo") @@ -464,7 +473,10 @@ func _select(target_node: int) -> void: my_gizmo.mode = _gizmo_mode my_gizmo.use_local_space = _gizmo_space_local + _node_property_editor.get_node_properties(_node_db_entry.node) my_gizmo.transform_changed.connect(func(mode, value): session_spawnable_m.set_transform(target_node, _node_db_entry.node.transform)) + # HACK: Update ALL of the node properties after a transformation was done. + my_gizmo.transform_end.connect(func(mode): _node_property_editor.update_node_properties(_node_db_entry.node)) func _gizmo_delete() -> void: my_gizmo.clear_selection() @@ -532,3 +544,8 @@ func _cem_camera_state(state: bool) -> void: _player_node._cem_camera_state(state) Events.emit_signal("cem_camera_state", state) return + +# Node Property Editor +func _set_npe_state(state: bool) -> void: + _node_property_editor.visible = state + return diff --git a/src/userinterface/client_edit_mode/desktop/scripts/moveable_window.gd b/src/userinterface/client_edit_mode/desktop/scripts/moveable_window.gd new file mode 100644 index 00000000..8267f0c1 --- /dev/null +++ b/src/userinterface/client_edit_mode/desktop/scripts/moveable_window.gd @@ -0,0 +1,66 @@ +# --- License +# File: /client/src/userinterface/client_edit_mode/desktop/scripts/moveable_window.gd +# Project: OpenMinerva +# Created Date: 17 July 2026 +# Copyright (c) 2026 OpenMinerva +# License: MIT License +# Authors: Armored Dragon +# --- License +extends Control + +signal selection(type: String, parent: int) + +@export var window_name: String = "Window Name" + +@onready var _button_close = get_node("BackgroundColor/MarginContainer/VBoxContainer/HBoxContainer/GenericButton") +@onready var _node_window_title = get_node("BackgroundColor/MarginContainer/VBoxContainer/HBoxContainer/Label") + +var _is_dragging: bool = false +var _drag_offset: Vector2 + +func _ready() -> void: + _node_window_title.text = window_name + _button_close.clicked.connect(_close) + + gui_input.connect(_on_input) + gui_input.connect(_on_input) + return + +func _close() -> void: + visible = false + return + +func _on_input(event) -> void: + if event is InputEventMouseButton: + _mouse_click(event) + return + + if event is InputEventMouseMotion: + _mouse_drag(event) + return + + return + +func _mouse_drag(_event) -> void: + if _is_dragging: + var _game_size: Vector2 = DisplayServer.window_get_size() + var _target_position = get_global_mouse_position() - _drag_offset + var _is_out_of_bounds = _target_position.x < 0 || _target_position.y < 0 || _target_position.x >= _game_size.x - size.x || _target_position.y >= _game_size.y - size.y + + if _is_out_of_bounds == false: + global_position = _target_position + return + +func _mouse_click(event) -> void: + if event.button_index != MOUSE_BUTTON_LEFT: + return + + if event.pressed: + _is_dragging = true + _drag_offset = get_global_mouse_position() - global_position + else: + _is_dragging = false + return + +func _on_item_activated(index: int) -> void: + return diff --git a/src/userinterface/client_edit_mode/desktop/scripts/moveable_window.gd.uid b/src/userinterface/client_edit_mode/desktop/scripts/moveable_window.gd.uid new file mode 100644 index 00000000..4f555474 --- /dev/null +++ b/src/userinterface/client_edit_mode/desktop/scripts/moveable_window.gd.uid @@ -0,0 +1 @@ +uid://bapubedba8eiu diff --git a/src/userinterface/client_edit_mode/desktop/scripts/properties.gd b/src/userinterface/client_edit_mode/desktop/scripts/properties.gd new file mode 100644 index 00000000..18f67598 --- /dev/null +++ b/src/userinterface/client_edit_mode/desktop/scripts/properties.gd @@ -0,0 +1,140 @@ +# --- License +# File: /client/src/userinterface/client_edit_mode/desktop/scripts/properties.gd +# Project: OpenMinerva +# Created Date: 15 July 2026 +# Copyright (c) 2026 OpenMinerva +# License: MIT License +# Authors: Armored Dragon +# --- License +extends MarginContainer + +const _dev_basic_props = ["position", "rotation", "scale", "visible"] +const _dev_whitelist_properties = ["mesh"] +const _dev_whitelist_mesh_properties = ["material", "flip_faces", "add_uv2", "uv2_padding", "size", "subdivide_width", "subdivide_height", "subdivide_depth", ] + +var _node: Node + +# TODO: Add protections from making changes on null nodes (invalid nodes) +# TODO: Handle enums for ints. + +@onready var _dev_transform_container: Node = get_node("VBoxContainer/Container/ScrollContainer/MarginContainer/VBoxContainer/FoldableContainer/VBoxContainer") +@onready var _dev_property_container: Node = get_node("VBoxContainer/Container/ScrollContainer/MarginContainer/VBoxContainer") + +@onready var app_scene_m: Node = get_tree().current_scene.get_node("SceneManager") + +var session_spawnable_m: Node + +var partials: Dictionary = {} + +func _ready() -> void: + _dev_property_container.get_node("Visible").value_changed.connect(func (new_value): _property_changed("visible", new_value)) + _dev_transform_container.get_node("Position").value_changed.connect(func (new_value): _property_changed("position", new_value)) + _dev_transform_container.get_node("Rotation").value_changed.connect(func (new_value): _property_changed("rotation_degrees", new_value)) + _dev_transform_container.get_node("Scale").value_changed.connect(func (new_value): _property_changed("scale", new_value)) + + Events.dash_session_changed.connect(_on_session_changed) + return + +func get_node_properties(node: Node) -> void: + _clear_node_properties() + _node = node + var _property_tree: Dictionary = _get_property_tree(node) + + var _property_categories: Array = _property_tree.keys() + _property_categories.reverse() + + for _category in _property_categories: + # Create the container node. + var _category_node = _get_partial("FoldableContainer") + _dev_property_container.add_child(_category_node) + _category_node.set_label(_category) + + # Add all of the sub properties. + for _prop in _property_tree[_category]: + var _sub_property = _get_partial("%s" % type_string(_prop.type)) + + if _sub_property == null: + continue + + _category_node.get_node("VBoxContainer").add_child(_sub_property) + _sub_property.set_value(node.get(_prop.name)) + _sub_property.set_label(_prop.name.capitalize()) + _sub_property.value_changed.connect(func (new_value): _property_changed(_prop.name, new_value)) + continue + + update_node_properties(node) + return + +func _get_property_tree(node: Node) -> Dictionary: + var tree := {} + var current_category := "" + var group := "" + var sub_group := "" + + for _property in node.get_property_list(): + var _usage: int = _property["usage"] + var _p_name: String = _property["name"] + + if _p_name.begins_with("metadata/"): + # Metadata is ignored. + continue + + if _dev_basic_props.has(_p_name): + # These properties are handled at the top of the NPE. + continue + + if _usage & PROPERTY_USAGE_CATEGORY: + # If this property is a category. + current_category = _p_name + tree[current_category] = [] + continue + + if _usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP): + # TODO: Subgroups? + # Ignore groups and subgroups. + continue + + if _usage & PROPERTY_USAGE_EDITOR && !current_category.is_empty(): + tree[current_category].append(_property) + + return tree + +func _clear_node_properties() -> void: + var _child_number: int = 0 + for child in _dev_property_container.get_children(): + if _child_number <= 1: + _child_number = _child_number + 1 + continue + child.queue_free() + return + +func update_node_properties(node: Node) -> void: + _dev_property_container.get_node("Visible").set_value(node.get("visible")) + _dev_transform_container.get_node("Position").set_value(node.get("position")) + _dev_transform_container.get_node("Rotation").set_value(node.get("rotation_degrees")) + _dev_transform_container.get_node("Scale").set_value(node.get("scale")) + return + +func _property_changed(property_name: String, property_value: Variant) -> void: + # TODO: Network change + if _node == null: + return + + session_spawnable_m.set_property(int(_node.name), property_name, property_value) + return + +func _get_partial(type: String) -> Node: + if partials.keys().has(type): + return partials[type].instantiate() + + var _new_partial = load("res://userinterface/client_edit_mode/node_property_editor/partials/%s.tscn" % type) + if _new_partial == null: + GlobalLogger.log("Could not find partial for type '%s'" % type, Enum.LogLevel.WARNING) + return null + + partials[type] = _new_partial + return partials[type].instantiate() + +func _on_session_changed() -> void: + session_spawnable_m = app_scene_m.get_master_scene(app_scene_m.active_session).get_node("SpawnableManager") + return diff --git a/src/userinterface/client_edit_mode/desktop/scripts/properties.gd.uid b/src/userinterface/client_edit_mode/desktop/scripts/properties.gd.uid new file mode 100644 index 00000000..ed14acc5 --- /dev/null +++ b/src/userinterface/client_edit_mode/desktop/scripts/properties.gd.uid @@ -0,0 +1 @@ +uid://8lm0oig2kxbj diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/Color.tscn b/src/userinterface/client_edit_mode/node_property_editor/partials/Color.tscn new file mode 100644 index 00000000..1c5e4fef --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/Color.tscn @@ -0,0 +1,33 @@ +[gd_scene format=3 uid="uid://c5iqs1ro62837"] + +[ext_resource type="Theme" uid="uid://ckqsvvccivw0x" path="res://resources/themes/default.tres" id="1_0b8ag"] +[ext_resource type="Script" uid="uid://dht8p10d785is" path="res://userinterface/client_edit_mode/node_property_editor/partials/color.gd" id="2_0b8ag"] + +[node name="Color" type="Control" unique_id=933711176] +custom_minimum_size = Vector2(80, 30) +layout_mode = 3 +anchor_right = 1.0 +anchor_bottom = 0.027777778 +grow_horizontal = 2 +size_flags_horizontal = 3 +theme = ExtResource("1_0b8ag") +script = ExtResource("2_0b8ag") +metadata/_edit_use_anchors_ = true + +[node name="VBoxContainer" type="HBoxContainer" parent="." unique_id=1972995258] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 0.053703703 +grow_horizontal = 2 +metadata/_edit_use_anchors_ = true + +[node name="Label" type="Label" parent="VBoxContainer" unique_id=482404396] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Color" + +[node name="ColorPickerButton" type="ColorPickerButton" parent="VBoxContainer" unique_id=1428418942] +custom_minimum_size = Vector2(60, 0) +layout_mode = 2 +theme = ExtResource("1_0b8ag") diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/FoldableContainer.tscn b/src/userinterface/client_edit_mode/node_property_editor/partials/FoldableContainer.tscn new file mode 100644 index 00000000..56d9f74c --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/FoldableContainer.tscn @@ -0,0 +1,13 @@ +[gd_scene format=3 uid="uid://c7animyvghvk"] + +[ext_resource type="Script" uid="uid://s14au1dc165i" path="res://userinterface/client_edit_mode/node_property_editor/partials/foldable_container.gd" id="1_wndr3"] + +[node name="FoldableContainer" type="FoldableContainer" unique_id=1542739099] +folded = true +title = "Transform" +title_alignment = 1 +script = ExtResource("1_wndr3") + +[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=238553897] +visible = false +layout_mode = 2 diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/Vector3.tscn b/src/userinterface/client_edit_mode/node_property_editor/partials/Vector3.tscn new file mode 100644 index 00000000..254b6e05 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/Vector3.tscn @@ -0,0 +1,44 @@ +[gd_scene format=3 uid="uid://dkn0gmy8or4hq"] + +[ext_resource type="Theme" uid="uid://ckqsvvccivw0x" path="res://resources/themes/default.tres" id="1_fenjf"] +[ext_resource type="Script" uid="uid://fo05ubo6hera" path="res://userinterface/client_edit_mode/node_property_editor/partials/vector_3.gd" id="2_iwkrw"] +[ext_resource type="PackedScene" uid="uid://b58b25jtckb2c" path="res://userinterface/client_edit_mode/node_property_editor/partials/spin_box.tscn" id="3_yo0a0"] + +[node name="Vector3" type="Control" unique_id=933711176] +custom_minimum_size = Vector2(80, 60) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 0 +theme = ExtResource("1_fenjf") +script = ExtResource("2_iwkrw") + +[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1067102212] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 0.053703703 +grow_horizontal = 2 +metadata/_edit_use_anchors_ = true + +[node name="Label" type="Label" parent="VBoxContainer" unique_id=482404396] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Vector3" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer" unique_id=287908761] +layout_mode = 2 +focus_behavior_recursive = 2 + +[node name="X" parent="VBoxContainer/HBoxContainer" unique_id=1631362233 instance=ExtResource("3_yo0a0")] +layout_mode = 2 + +[node name="Y" parent="VBoxContainer/HBoxContainer" unique_id=1007326326 instance=ExtResource("3_yo0a0")] +layout_mode = 2 + +[node name="Z" parent="VBoxContainer/HBoxContainer" unique_id=1538313912 instance=ExtResource("3_yo0a0")] +layout_mode = 2 diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/bool.gd b/src/userinterface/client_edit_mode/node_property_editor/partials/bool.gd new file mode 100644 index 00000000..bcc9edfa --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/bool.gd @@ -0,0 +1,36 @@ +# --- License +# File: /client/src/userinterface/client_edit_mode/node_property_editor/partials/bool.gd +# Project: OpenMinerva +# Created Date: 15 July 2026 +# Copyright (c) 2026 OpenMinerva +# License: MIT License +# Authors: Armored Dragon +# --- License + +extends Control + +signal value_changed(new_value: bool) + +@export var property_name: String = "" +@export var value: bool + +@onready var _label = get_node("VBoxContainer/Label") +@onready var _bool = get_node("VBoxContainer/CheckBox") + +func _ready() -> void: + _label.text = property_name + _bool.toggled.connect(_on_value_changed) + return + +func set_value(new_value: bool) -> void: + value = new_value + _bool.button_pressed = new_value + return + +func set_label(new_label: String) -> void: + _label.text = new_label + property_name = new_label + return + +func _on_value_changed(new_value: bool) -> void: + value_changed.emit(new_value) diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/bool.gd.uid b/src/userinterface/client_edit_mode/node_property_editor/partials/bool.gd.uid new file mode 100644 index 00000000..bc04b673 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/bool.gd.uid @@ -0,0 +1 @@ +uid://dev0tv8ldaegm diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/bool.tscn b/src/userinterface/client_edit_mode/node_property_editor/partials/bool.tscn new file mode 100644 index 00000000..059ac0fe --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/bool.tscn @@ -0,0 +1,40 @@ +[gd_scene format=3 uid="uid://wmjsfgx486xa"] + +[ext_resource type="Theme" uid="uid://ckqsvvccivw0x" path="res://resources/themes/default.tres" id="1_h5new"] +[ext_resource type="Script" uid="uid://dev0tv8ldaegm" path="res://userinterface/client_edit_mode/node_property_editor/partials/bool.gd" id="2_1puyx"] + +[node name="Bool" type="Control" unique_id=933711176] +custom_minimum_size = Vector2(80, 30) +layout_mode = 3 +anchor_right = 1.0 +anchor_bottom = 0.027777778 +grow_horizontal = 2 +size_flags_horizontal = 3 +theme = ExtResource("1_h5new") +script = ExtResource("2_1puyx") +metadata/_edit_use_anchors_ = true + +[node name="VBoxContainer" type="HBoxContainer" parent="." unique_id=1972995258] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 0.053703703 +grow_horizontal = 2 +metadata/_edit_use_anchors_ = true + +[node name="Label" type="Label" parent="VBoxContainer" unique_id=482404396] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Vector3" + +[node name="CheckBox" type="CheckButton" parent="VBoxContainer" unique_id=643167569] +custom_minimum_size = Vector2(30, 30) +custom_maximum_size = Vector2(120, 30) +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme = ExtResource("1_h5new") +flat = true +alignment = 1 +clip_text = true +icon_alignment = 1 +expand_icon = true diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/color.gd b/src/userinterface/client_edit_mode/node_property_editor/partials/color.gd new file mode 100644 index 00000000..7d74d50d --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/color.gd @@ -0,0 +1,36 @@ +# --- License +# File: /client/src/userinterface/client_edit_mode/node_property_editor/partials/color.gd +# Project: OpenMinerva +# Created Date: 17 July 2026 +# Copyright (c) 2026 OpenMinerva +# License: MIT License +# Authors: Armored Dragon +# --- License + +extends Control + +signal value_changed(new_value: Color) + +@export var property_name: String = "" +@export var value: Color + +@onready var _label = get_node("VBoxContainer/Label") +@onready var _value = get_node("VBoxContainer/ColorPickerButton") + +func _ready() -> void: + _label.text = property_name + _value.color_changed.connect(_on_value_changed) + return + +func set_value(new_value: Color) -> void: + value = new_value + _value.color = new_value + return + +func set_label(new_label: String) -> void: + _label.text = new_label + property_name = new_label + return + +func _on_value_changed(new_value: Color) -> void: + value_changed.emit(new_value) diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/color.gd.uid b/src/userinterface/client_edit_mode/node_property_editor/partials/color.gd.uid new file mode 100644 index 00000000..5f53cc3e --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/color.gd.uid @@ -0,0 +1 @@ +uid://dht8p10d785is diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/float.tscn b/src/userinterface/client_edit_mode/node_property_editor/partials/float.tscn new file mode 100644 index 00000000..028be55f --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/float.tscn @@ -0,0 +1,35 @@ +[gd_scene format=3 uid="uid://b1u12udwoner2"] + +[ext_resource type="Theme" uid="uid://ckqsvvccivw0x" path="res://resources/themes/default.tres" id="1_2mmn2"] +[ext_resource type="Script" uid="uid://066ucgk4rilk" path="res://userinterface/client_edit_mode/node_property_editor/partials/int.gd" id="2_c1vag"] +[ext_resource type="PackedScene" uid="uid://b58b25jtckb2c" path="res://userinterface/client_edit_mode/node_property_editor/partials/spin_box.tscn" id="3_j4p26"] + +[node name="float" type="Control" unique_id=933711176] +custom_minimum_size = Vector2(80, 30) +layout_mode = 3 +anchor_right = 1.0 +anchor_bottom = 0.027777778 +offset_bottom = -30.0 +grow_horizontal = 2 +size_flags_horizontal = 3 +theme = ExtResource("1_2mmn2") +script = ExtResource("2_c1vag") +metadata/_edit_use_anchors_ = true + +[node name="VBoxContainer" type="HBoxContainer" parent="." unique_id=1972995258] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 0.053703703 +grow_horizontal = 2 +metadata/_edit_use_anchors_ = true + +[node name="Label" type="Label" parent="VBoxContainer" unique_id=482404396] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Float" +clip_text = true +text_overrun_behavior = 3 + +[node name="SpinBox" parent="VBoxContainer" unique_id=1631362233 instance=ExtResource("3_j4p26")] +layout_mode = 2 diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/foldable_container.gd b/src/userinterface/client_edit_mode/node_property_editor/partials/foldable_container.gd new file mode 100644 index 00000000..d5d09422 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/foldable_container.gd @@ -0,0 +1,17 @@ +# --- License +# File: /client/src/userinterface/client_edit_mode/node_property_editor/partials/foldable_container.gd +# Project: OpenMinerva +# Created Date: 15 July 2026 +# Copyright (c) 2026 OpenMinerva +# License: MIT License +# Authors: Armored Dragon +# --- License + +extends FoldableContainer + +func _ready() -> void: + return + +func set_label(new_label: String) -> void: + title = new_label + return diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/foldable_container.gd.uid b/src/userinterface/client_edit_mode/node_property_editor/partials/foldable_container.gd.uid new file mode 100644 index 00000000..dbcb40e4 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/foldable_container.gd.uid @@ -0,0 +1 @@ +uid://s14au1dc165i diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/int.gd b/src/userinterface/client_edit_mode/node_property_editor/partials/int.gd new file mode 100644 index 00000000..416604c1 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/int.gd @@ -0,0 +1,36 @@ +# --- License +# File: /client/src/userinterface/client_edit_mode/node_property_editor/partials/int.gd +# Project: OpenMinerva +# Created Date: 16 July 2026 +# Copyright (c) 2026 OpenMinerva +# License: MIT License +# Authors: Armored Dragon +# --- License + +extends Control + +signal value_changed(new_value: bool) + +@export var property_name: String = "" +@export var value: int + +@onready var _label = get_node("VBoxContainer/Label") +@onready var _spinbox = get_node("VBoxContainer/SpinBox") + +func _ready() -> void: + _label.text = property_name + _spinbox.spinbox_value_changed.connect(_on_value_changed) + return + +func set_value(new_value: int) -> void: + value = new_value + _spinbox.set_value(new_value) + return + +func set_label(new_label: String) -> void: + _label.text = new_label + property_name = new_label + return + +func _on_value_changed(new_value: int) -> void: + value_changed.emit(new_value) diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/int.gd.uid b/src/userinterface/client_edit_mode/node_property_editor/partials/int.gd.uid new file mode 100644 index 00000000..97ee63a3 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/int.gd.uid @@ -0,0 +1 @@ +uid://066ucgk4rilk diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/int.tscn b/src/userinterface/client_edit_mode/node_property_editor/partials/int.tscn new file mode 100644 index 00000000..a2e10d10 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/int.tscn @@ -0,0 +1,37 @@ +[gd_scene format=3 uid="uid://c08ywv1r18wq4"] + +[ext_resource type="Theme" uid="uid://ckqsvvccivw0x" path="res://resources/themes/default.tres" id="1_ecork"] +[ext_resource type="Script" uid="uid://066ucgk4rilk" path="res://userinterface/client_edit_mode/node_property_editor/partials/int.gd" id="2_7lgys"] +[ext_resource type="PackedScene" uid="uid://b58b25jtckb2c" path="res://userinterface/client_edit_mode/node_property_editor/partials/spin_box.tscn" id="3_fivcc"] + +[node name="int" type="Control" unique_id=933711176] +custom_minimum_size = Vector2(80, 30) +layout_mode = 3 +anchor_right = 1.0 +anchor_bottom = 0.027777778 +offset_bottom = -30.0 +grow_horizontal = 2 +size_flags_horizontal = 3 +theme = ExtResource("1_ecork") +script = ExtResource("2_7lgys") +metadata/_edit_use_anchors_ = true + +[node name="VBoxContainer" type="HBoxContainer" parent="." unique_id=1972995258] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 0.053703703 +grow_horizontal = 2 +metadata/_edit_use_anchors_ = true + +[node name="Label" type="Label" parent="VBoxContainer" unique_id=482404396] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Int" +clip_text = true +text_overrun_behavior = 3 + +[node name="SpinBox" parent="VBoxContainer" unique_id=1631362233 instance=ExtResource("3_fivcc")] +layout_mode = 2 +max_decimals = 0 +is_int = true diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.gd b/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.gd new file mode 100644 index 00000000..ec7c6445 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.gd @@ -0,0 +1,39 @@ +# --- License +# File: /client/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.gd +# Project: OpenMinerva +# Created Date: 15 July 2026 +# Copyright (c) 2026 OpenMinerva +# License: MIT License +# Authors: Armored Dragon +# --- License +extends SpinBox + +signal spinbox_value_changed(value: float) + +@export var max_decimals: int = 4 +@export var is_int: bool = false + +func _ready() -> void: + if is_int == true: + max_decimals = 0 + step = 1 + + value_changed.connect(_on_value_changed) + get_line_edit().focus_entered.connect(_on_focus_entered) + get_line_edit().focus_exited.connect(_on_focus_exited) + + get_line_edit().text = _format_value(value) + return + +func _on_value_changed(new_value: float) -> void: + get_line_edit().text = _format_value(new_value) + spinbox_value_changed.emit(new_value) + +func _on_focus_entered() -> void: + get_line_edit().text = String.num(value, max_decimals) + +func _on_focus_exited() -> void: + get_line_edit().text = _format_value(value) + +func _format_value(v: float) -> String: + return (prefix + String.num(v, max_decimals) + suffix).trim_suffix(".0") diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.gd.uid b/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.gd.uid new file mode 100644 index 00000000..e76cd3d7 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.gd.uid @@ -0,0 +1 @@ +uid://susq40b1r4sx diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.tscn b/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.tscn new file mode 100644 index 00000000..662060f4 --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/spin_box.tscn @@ -0,0 +1,18 @@ +[gd_scene format=3 uid="uid://b58b25jtckb2c"] + +[ext_resource type="Script" uid="uid://susq40b1r4sx" path="res://userinterface/client_edit_mode/node_property_editor/partials/spin_box.gd" id="1_30qog"] + +[node name="SpinBox" type="SpinBox" unique_id=1631362233] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +focus_behavior_recursive = 2 +min_value = inf_neg +max_value = inf +step = 0.001 +alignment = 1 +select_all_on_focus = true +script = ExtResource("1_30qog") diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/vector_3.gd b/src/userinterface/client_edit_mode/node_property_editor/partials/vector_3.gd new file mode 100644 index 00000000..b0bc356a --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/vector_3.gd @@ -0,0 +1,45 @@ +# --- License +# File: /client/src/userinterface/client_edit_mode/node_property_editor/partials/vector_3.gd +# Project: OpenMinerva +# Created Date: 15 July 2026 +# Copyright (c) 2026 OpenMinerva +# License: MIT License +# Authors: Armored Dragon +# --- License + +extends Control + +signal value_changed(new_value: Vector3) + +@export var property_name: String = "" +@export var value: Vector3 = Vector3(0, 0, 0) + +@onready var _label = get_node("VBoxContainer/Label") +@onready var _container: HBoxContainer = get_node("VBoxContainer/HBoxContainer") + +func _ready() -> void: + _label.text = property_name + + for child in _container.get_children(): + var _name = child.name + child.spinbox_value_changed.connect(func (new_value): _child_value_changed(_name, new_value)) + return + +func set_value(new_value: Vector3) -> void: + value = new_value + + for child in _container.get_children(): + var _node_name: String = child.name + _container.get_node(_node_name).value = value[_node_name.to_lower()] + return + +func set_label(new_label: String) -> void: + _label.text = new_label + property_name = new_label + return + +func _child_value_changed(type: String, new_value: float) -> void: + type = type.to_lower() + value[type] = new_value + value_changed.emit(value) + return diff --git a/src/userinterface/client_edit_mode/node_property_editor/partials/vector_3.gd.uid b/src/userinterface/client_edit_mode/node_property_editor/partials/vector_3.gd.uid new file mode 100644 index 00000000..9d2be77e --- /dev/null +++ b/src/userinterface/client_edit_mode/node_property_editor/partials/vector_3.gd.uid @@ -0,0 +1 @@ +uid://fo05ubo6hera