From 3cb7a2d70402b7fb5090ec766ba34c7b4bf8c7a2 Mon Sep 17 00:00:00 2001 From: Tiger Jove Date: Fri, 16 Jan 2026 18:11:22 +0100 Subject: [PATCH] feat: interaction cursor, fix: cards didn't always load at right position --- src/logic-scenes/board/card-board.gd | 18 ++++----- .../player_controller/player_controller.gd | 40 +++++++++++++++---- .../player_controller/player_controller.tscn | 20 +++++++++- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/logic-scenes/board/card-board.gd b/src/logic-scenes/board/card-board.gd index a0dd5d0..c28fba2 100644 --- a/src/logic-scenes/board/card-board.gd +++ b/src/logic-scenes/board/card-board.gd @@ -379,7 +379,7 @@ func insert_area(parent: Control, node: Area2D): var i = 0 if not node in parent.get_children(): - node.reparent(parent) + node.reparent(parent, false) # Don't preserve global transform - we set positions explicitly if node is StickyNote: node.on_board = true node.owner = self @@ -630,16 +630,16 @@ func initialise_from_save(savegame: SaveGame) -> void: # Add all cards print_debug(" Loading %d cards..." % card_pile["cards"].size()) for card: Card in card_pile["cards"]: - add_child(card) - insert_area(dropzone, card) - - # Set position from save, or generate random if missing (must be after insert_area) + # Set position BEFORE adding to scene tree to avoid reparent position issues if savegame.board_positions.has(card.name): card.position = savegame.board_positions[card.name] print_debug(" Card '%s' at %s" % [card.name, card.position]) else: card.position = _generate_random_position() print_debug(" Card '%s' - generated random position: %s" % [card.name, card.position]) + + add_child(card) + insert_area(dropzone, card) card.set_owner(self) card.is_dragable = true @@ -670,16 +670,16 @@ func initialise_from_save(savegame: SaveGame) -> void: # Sticky is loose on board else: - add_child(sticky) - insert_area(dropzone, sticky) - - # Set position from save, or generate random if missing (must be after insert_area) + # Set position BEFORE adding to scene tree to avoid reparent position issues if savegame.board_positions.has(sticky.name): sticky.position = savegame.board_positions[sticky.name] print_debug(" Loose sticky '%s' at %s" % [sticky.name, sticky.position]) else: sticky.position = _generate_random_position() print_debug(" Loose sticky '%s' - generated random position: %s" % [sticky.name, sticky.position]) + + add_child(sticky) + insert_area(dropzone, sticky) sticky.set_owner(self) sticky.current_handle = self # Required for input handling diff --git a/src/logic-scenes/player_controller/player_controller.gd b/src/logic-scenes/player_controller/player_controller.gd index 58314cf..8285c6c 100644 --- a/src/logic-scenes/player_controller/player_controller.gd +++ b/src/logic-scenes/player_controller/player_controller.gd @@ -22,25 +22,31 @@ func _apply_enabled_state() -> void: jitter_tween.tween_property(self, "jitter_strength", 1.0, 1.0) if has_entered: ui_exited.emit() + # Show hand cursor when player is enabled + if hand_cursor: + hand_cursor.visible = true else: Input.mouse_mode = Input.MOUSE_MODE_VISIBLE jitter_tween = create_tween() jitter_tween.tween_property(self, "jitter_strength", 0.0, 0.5) if has_entered: ui_exited.emit() + # Hide hand cursor when player is disabled + if hand_cursor: + hand_cursor.visible = false sleeping = not enabled @export var mouse_sensitivity: Vector2 = Vector2(6, 5) @export var initial_pitch: float = 50 -@export_range (0, 10) var max_speed: float = 3 -@export_range (0, 10) var max_acceleration: float = 5 -@export_range (0, 20) var damp: float = 10 -@export_range (0.1, 1) var mouse_jerk:float = 0.5 -@export_range (10, 100) var gamepad_response:float = 50.0 -@export_range (50, 500) var mouse_jerk_rejection:float = 200.0 -@export var max_angle:float = 75 +@export_range (0.0, 10.0) var max_speed: float = 3 +@export_range (0.0, 10.0) var max_acceleration: float = 5 +@export_range (0.0, 20.0) var damp: float = 10 +@export_range (0.1, 1.0) var mouse_jerk:float = 0.5 +@export_range (10.0, 100.0) var gamepad_response:float = 50.0 +@export_range (50.0, 500.0) var mouse_jerk_rejection:float = 200.0 +@export var max_angle:float = 75.0 @export var camera_jitter_speed:float = 3 @export var angular_jitter:Vector3 = Vector3(0.1, 0, 0.05) @@ -87,6 +93,11 @@ var crouched:bool = false: @onready var camera: Camera3D = $Yaw/Pitch/Mount/Camera3D @onready var focus_ray: RayCast3D = $Yaw/Pitch/Mount/Camera3D/RayCast3D @onready var ui_prober: Area3D = $Yaw/Pitch/Mount/Camera3D/UiProber +@onready var hand_cursor: TextureRect = %Cursor + +# Cursor textures (preloaded for performance) +const cursor_default: Texture2D = preload("res://import/interface-elements/cursor_point.png") +const cursor_point: Texture2D = preload("res://import/interface-elements/cursor_grab.png") @onready var base_fov := camera.fov var zoomed:bool = false: @@ -116,6 +127,9 @@ func _ready(): $CrouchDetector.area_entered.connect(enter_crouch) $CrouchDetector.area_exited.connect(exit_crouch) + # Setup hand cursor + _setup_hand_cursor() + # Connect to central player enable signal. # Guard for standalone test scenes without autoloads. if get_node_or_null("/root/Scenes"): @@ -128,6 +142,12 @@ func _ready(): func _on_player_enable(enable: bool) -> void: enabled = enable +## Setup the hand cursor in the center of the screen +func _setup_hand_cursor() -> void: + # Configure the existing TextureRect for cursor display + hand_cursor.texture = cursor_default # Start with default cursor + hand_cursor.visible = false + ## Restores player position and camera rotation from save game func restore_from_save(save: SaveGame) -> void: if save.player_position != Vector3.ZERO: @@ -162,11 +182,17 @@ func _on_ray_entered(_area): assert(parent != null, "Ray entered non-interactable area!") printt("ray entered", parent.name, parent) parent.hover = true + # Switch to pointing hand cursor when hovering over interactable + if hand_cursor: + hand_cursor.texture = cursor_point func _on_ray_exited(_area): var parent := _area.get_parent() as Interactable printt("ray exited", parent.name, parent) parent.hover = false + # Switch back to default cursor when not hovering + if hand_cursor: + hand_cursor.texture = cursor_default func _physics_process(delta: float): diff --git a/src/logic-scenes/player_controller/player_controller.tscn b/src/logic-scenes/player_controller/player_controller.tscn index b3e3b85..fbc8c40 100644 --- a/src/logic-scenes/player_controller/player_controller.tscn +++ b/src/logic-scenes/player_controller/player_controller.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=16 format=3 uid="uid://mkccbig41bqb"] +[gd_scene load_steps=17 format=3 uid="uid://mkccbig41bqb"] [ext_resource type="Script" uid="uid://bk618uyhghswx" path="res://logic-scenes/player_controller/player_controller.gd" id="1_0b4mi"] +[ext_resource type="Texture2D" uid="uid://d005qvnbnishb" path="res://import/interface-elements/cursor_grab.png" id="2_x6v75"] [sub_resource type="PhysicsMaterial" id="10"] friction = 0.0 @@ -657,6 +658,23 @@ mouse_filter = 2 texture = SubResource("GradientTexture2D_x6v75") expand_mode = 4 +[node name="Cursor" type="TextureRect" parent="Yaw/Pitch/Mount/Camera3D"] +unique_name_in_owner = true +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +texture = ExtResource("2_x6v75") +stretch_mode = 3 + [node name="PlayerCollision" type="CollisionShape3D" parent="."] transform = Transform3D(1, 0, 0, 0, -1, 8.74228e-08, 0, -8.74228e-08, -1, 0, 0.6, 0) shape = SubResource("CapsuleShape3D_hpoj0")