feat: interaction cursor, fix: cards didn't always load at right position

This commit is contained in:
tiger tiger tiger 2026-01-16 18:11:22 +01:00
parent 971c162236
commit 3cb7a2d704
3 changed files with 61 additions and 17 deletions

View File

@ -379,7 +379,7 @@ func insert_area(parent: Control, node: Area2D):
var i = 0 var i = 0
if not node in parent.get_children(): 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: if node is StickyNote:
node.on_board = true node.on_board = true
node.owner = self node.owner = self
@ -630,16 +630,16 @@ func initialise_from_save(savegame: SaveGame) -> void:
# Add all cards # Add all cards
print_debug(" Loading %d cards..." % card_pile["cards"].size()) print_debug(" Loading %d cards..." % card_pile["cards"].size())
for card: Card in card_pile["cards"]: for card: Card in card_pile["cards"]:
add_child(card) # Set position BEFORE adding to scene tree to avoid reparent position issues
insert_area(dropzone, card)
# Set position from save, or generate random if missing (must be after insert_area)
if savegame.board_positions.has(card.name): if savegame.board_positions.has(card.name):
card.position = savegame.board_positions[card.name] card.position = savegame.board_positions[card.name]
print_debug(" Card '%s' at %s" % [card.name, card.position]) print_debug(" Card '%s' at %s" % [card.name, card.position])
else: else:
card.position = _generate_random_position() card.position = _generate_random_position()
print_debug(" Card '%s' - generated random position: %s" % [card.name, card.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.set_owner(self)
card.is_dragable = true card.is_dragable = true
@ -670,16 +670,16 @@ func initialise_from_save(savegame: SaveGame) -> void:
# Sticky is loose on board # Sticky is loose on board
else: else:
add_child(sticky) # Set position BEFORE adding to scene tree to avoid reparent position issues
insert_area(dropzone, sticky)
# Set position from save, or generate random if missing (must be after insert_area)
if savegame.board_positions.has(sticky.name): if savegame.board_positions.has(sticky.name):
sticky.position = savegame.board_positions[sticky.name] sticky.position = savegame.board_positions[sticky.name]
print_debug(" Loose sticky '%s' at %s" % [sticky.name, sticky.position]) print_debug(" Loose sticky '%s' at %s" % [sticky.name, sticky.position])
else: else:
sticky.position = _generate_random_position() sticky.position = _generate_random_position()
print_debug(" Loose sticky '%s' - generated random position: %s" % [sticky.name, sticky.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.set_owner(self)
sticky.current_handle = self # Required for input handling sticky.current_handle = self # Required for input handling

View File

@ -22,25 +22,31 @@ func _apply_enabled_state() -> void:
jitter_tween.tween_property(self, "jitter_strength", 1.0, 1.0) jitter_tween.tween_property(self, "jitter_strength", 1.0, 1.0)
if has_entered: if has_entered:
ui_exited.emit() ui_exited.emit()
# Show hand cursor when player is enabled
if hand_cursor:
hand_cursor.visible = true
else: else:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
jitter_tween = create_tween() jitter_tween = create_tween()
jitter_tween.tween_property(self, "jitter_strength", 0.0, 0.5) jitter_tween.tween_property(self, "jitter_strength", 0.0, 0.5)
if has_entered: if has_entered:
ui_exited.emit() ui_exited.emit()
# Hide hand cursor when player is disabled
if hand_cursor:
hand_cursor.visible = false
sleeping = not enabled sleeping = not enabled
@export var mouse_sensitivity: Vector2 = Vector2(6, 5) @export var mouse_sensitivity: Vector2 = Vector2(6, 5)
@export var initial_pitch: float = 50 @export var initial_pitch: float = 50
@export_range (0, 10) var max_speed: float = 3 @export_range (0.0, 10.0) var max_speed: float = 3
@export_range (0, 10) var max_acceleration: float = 5 @export_range (0.0, 10.0) var max_acceleration: float = 5
@export_range (0, 20) var damp: float = 10 @export_range (0.0, 20.0) var damp: float = 10
@export_range (0.1, 1) var mouse_jerk:float = 0.5 @export_range (0.1, 1.0) var mouse_jerk:float = 0.5
@export_range (10, 100) var gamepad_response:float = 50.0 @export_range (10.0, 100.0) var gamepad_response:float = 50.0
@export_range (50, 500) var mouse_jerk_rejection:float = 200.0 @export_range (50.0, 500.0) var mouse_jerk_rejection:float = 200.0
@export var max_angle:float = 75 @export var max_angle:float = 75.0
@export var camera_jitter_speed:float = 3 @export var camera_jitter_speed:float = 3
@export var angular_jitter:Vector3 = Vector3(0.1, 0, 0.05) @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 camera: Camera3D = $Yaw/Pitch/Mount/Camera3D
@onready var focus_ray: RayCast3D = $Yaw/Pitch/Mount/Camera3D/RayCast3D @onready var focus_ray: RayCast3D = $Yaw/Pitch/Mount/Camera3D/RayCast3D
@onready var ui_prober: Area3D = $Yaw/Pitch/Mount/Camera3D/UiProber @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 @onready var base_fov := camera.fov
var zoomed:bool = false: var zoomed:bool = false:
@ -116,6 +127,9 @@ func _ready():
$CrouchDetector.area_entered.connect(enter_crouch) $CrouchDetector.area_entered.connect(enter_crouch)
$CrouchDetector.area_exited.connect(exit_crouch) $CrouchDetector.area_exited.connect(exit_crouch)
# Setup hand cursor
_setup_hand_cursor()
# Connect to central player enable signal. # Connect to central player enable signal.
# Guard for standalone test scenes without autoloads. # Guard for standalone test scenes without autoloads.
if get_node_or_null("/root/Scenes"): if get_node_or_null("/root/Scenes"):
@ -128,6 +142,12 @@ func _ready():
func _on_player_enable(enable: bool) -> void: func _on_player_enable(enable: bool) -> void:
enabled = enable 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 ## Restores player position and camera rotation from save game
func restore_from_save(save: SaveGame) -> void: func restore_from_save(save: SaveGame) -> void:
if save.player_position != Vector3.ZERO: if save.player_position != Vector3.ZERO:
@ -162,11 +182,17 @@ func _on_ray_entered(_area):
assert(parent != null, "Ray entered non-interactable area!") assert(parent != null, "Ray entered non-interactable area!")
printt("ray entered", parent.name, parent) printt("ray entered", parent.name, parent)
parent.hover = true 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): func _on_ray_exited(_area):
var parent := _area.get_parent() as Interactable var parent := _area.get_parent() as Interactable
printt("ray exited", parent.name, parent) printt("ray exited", parent.name, parent)
parent.hover = false parent.hover = false
# Switch back to default cursor when not hovering
if hand_cursor:
hand_cursor.texture = cursor_default
func _physics_process(delta: float): func _physics_process(delta: float):

View File

@ -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="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"] [sub_resource type="PhysicsMaterial" id="10"]
friction = 0.0 friction = 0.0
@ -657,6 +658,23 @@ mouse_filter = 2
texture = SubResource("GradientTexture2D_x6v75") texture = SubResource("GradientTexture2D_x6v75")
expand_mode = 4 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="."] [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) transform = Transform3D(1, 0, 0, 0, -1, 8.74228e-08, 0, -8.74228e-08, -1, 0, 0.6, 0)
shape = SubResource("CapsuleShape3D_hpoj0") shape = SubResource("CapsuleShape3D_hpoj0")