diff --git a/overworld/camera/OverworldCamera.gd b/overworld/camera/OverworldCamera.gd index 4b368d5..7a4ae16 100644 --- a/overworld/camera/OverworldCamera.gd +++ b/overworld/camera/OverworldCamera.gd @@ -1,6 +1,6 @@ class_name OverworldCamera extends Camera3D -enum CameraMode { FREE, CENTERED } +enum CameraMode { FREE, CENTERED, MANUAL_CENTER } # const COLLISION_MARGIN:float = 0.1 const COLLISION_MARGIN:float = 0 @@ -28,6 +28,7 @@ const COLLISION_MARGIN:float = 0 @export var centeredAcceleration:float = 180.0 @export var centeredMaxYawDiff:float = 120.0 @export var centeredPitch:float = 30.0 +@export var manualCenterMultiplier:float = 10.0 @export_category("Collision") @export_flags_3d_physics var collisionMask:int = 1 @@ -84,9 +85,9 @@ func _process(delta:float) -> void: _pitch += _mouseDelta.y * mouseSensitivity * SETTINGS.cameraSpeedMouse * yMult _mouseDelta = Vector2.ZERO - # center_camera input → switch to CENTERED immediately + # center_camera input → switch to MANUAL_CENTER immediately if Input.is_action_just_pressed("center_camera"): - _mode = CameraMode.CENTERED + _mode = CameraMode.MANUAL_CENTER # In FREE mode, accumulate time toward auto-centering while the player is moving if _mode == CameraMode.FREE: @@ -102,16 +103,28 @@ func _process(delta:float) -> void: # In CENTERED mode, accelerate a yaw velocity toward behind the player (using # actual movement velocity for direction) then friction-decay when idle — # mirrors how controller input works so there's no sudden stop. - if _mode == CameraMode.CENTERED: + if _mode == CameraMode.CENTERED or _mode == CameraMode.MANUAL_CENTER: var centerBody := targetNode as CharacterBody3D var vel3d:Vector3 = Vector3.ZERO if centerBody == null else centerBody.velocity + var behindYaw:float = 0.0 + var hasTarget:bool = false + if vel3d.length_squared() > 0.1: - var behindYaw:float = rad_to_deg(atan2(-vel3d.x, -vel3d.z)) + behindYaw = rad_to_deg(atan2(-vel3d.x, -vel3d.z)) + hasTarget = true + elif _mode == CameraMode.MANUAL_CENTER and targetNode != null: + behindYaw = rad_to_deg(targetNode.rotation.y) + hasTarget = true + + if hasTarget: var centerYawDiff:float = fposmod(behindYaw - _yaw + 180.0, 360.0) - 180.0 + if _mode == CameraMode.MANUAL_CENTER and abs(centerYawDiff) < centeredMaxYawDiff * 0.05: + _mode = CameraMode.FREE var totalAngle:float = abs(centerYawDiff) + abs(centeredPitch - _pitch) - var dynamicRate:float = minf(centeredFollowRate * (1.0 + totalAngle / 90.0), centeredMaxFollowRate) + var speedMult:float = manualCenterMultiplier if _mode == CameraMode.MANUAL_CENTER else 1.0 + var dynamicRate:float = minf(centeredFollowRate * (1.0 + totalAngle / 90.0), centeredMaxFollowRate) * speedMult if abs(centerYawDiff) <= centeredMaxYawDiff: - _centerVelocity = move_toward(_centerVelocity, centerYawDiff * dynamicRate, centeredAcceleration * delta) + _centerVelocity = move_toward(_centerVelocity, centerYawDiff * dynamicRate, centeredAcceleration * speedMult * delta) var tPitch:float = minf(dynamicRate * 3.0 * delta, 1.0) _pitch = lerpf(_pitch, centeredPitch, tPitch) else: diff --git a/project.godot b/project.godot index ed5929f..4f23640 100644 --- a/project.godot +++ b/project.godot @@ -65,6 +65,47 @@ theme/custom_font="uid://ck4rsg0nvvxtq" [input] +ui_accept={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":false,"script":null) +] +} +ui_cancel={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":false,"script":null) +] +} +ui_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":13,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null) +] +} +ui_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":14,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null) +] +} +ui_up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":11,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null) +] +} +ui_down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":12,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null) +] +} move_left={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) @@ -164,47 +205,6 @@ center_camera={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":false,"script":null) ] } -ui_accept={ -"deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) -, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":false,"script":null) -] -} -ui_cancel={ -"deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":false,"script":null) -] -} -ui_up={ -"deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":11,"pressure":0.0,"pressed":false,"script":null) -, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null) -] -} -ui_down={ -"deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":12,"pressure":0.0,"pressed":false,"script":null) -, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null) -] -} -ui_left={ -"deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":13,"pressure":0.0,"pressed":false,"script":null) -, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null) -] -} -ui_right={ -"deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":14,"pressure":0.0,"pressed":false,"script":null) -, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null) -] -} [internationalization] diff --git a/ui/pause/PauseMain.gd b/ui/pause/PauseMain.gd index d360092..281b387 100644 --- a/ui/pause/PauseMain.gd +++ b/ui/pause/PauseMain.gd @@ -7,15 +7,36 @@ signal quitRequested @export var btnResume:Button @export var btnSettings:Button @export var btnQuit:Button +@export var mainButtons:VBoxContainer +@export var confirmQuit:VBoxContainer +@export var btnQuitConfirm:Button +@export var btnQuitCancel:Button func _ready() -> void: visible = false btnResume.pressed.connect(resumeRequested.emit) btnSettings.pressed.connect(settingsRequested.emit) - btnQuit.pressed.connect(quitRequested.emit) + btnQuit.pressed.connect(_showConfirm) + btnQuitConfirm.pressed.connect(quitRequested.emit) + btnQuitCancel.pressed.connect(cancelConfirm) + +func _showConfirm() -> void: + mainButtons.visible = false + confirmQuit.visible = true + btnQuitCancel.grab_focus() + +func cancelConfirm() -> void: + mainButtons.visible = true + confirmQuit.visible = false + btnQuit.grab_focus() + +func isConfirming() -> bool: + return confirmQuit.visible func open() -> void: visible = true + if isConfirming(): + cancelConfirm() btnResume.grab_focus() func close() -> void: diff --git a/ui/pause/PauseMain.tscn b/ui/pause/PauseMain.tscn index a7c046b..fb04e74 100644 --- a/ui/pause/PauseMain.tscn +++ b/ui/pause/PauseMain.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" uid="uid://c7kvg0jw6w340" path="res://ui/pause/PauseMain.gd" id="1_b5xfl"] -[node name="PauseMain" type="VBoxContainer" node_paths=PackedStringArray("btnResume", "btnSettings", "btnQuit")] +[node name="PauseMain" type="VBoxContainer" node_paths=PackedStringArray("btnResume", "btnSettings", "btnQuit", "mainButtons", "confirmQuit", "btnQuitConfirm", "btnQuitCancel")] anchors_preset = 8 anchor_left = 0.5 anchor_top = 0.5 @@ -12,23 +12,47 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_b5xfl") metadata/_custom_type_script = "uid://c7kvg0jw6w340" -btnResume = NodePath("Resume") -btnSettings = NodePath("Settings") -btnQuit = NodePath("Quit") +btnResume = NodePath("MainButtons/Resume") +btnSettings = NodePath("MainButtons/Settings") +btnQuit = NodePath("MainButtons/Quit") +mainButtons = NodePath("MainButtons") +confirmQuit = NodePath("ConfirmQuit") +btnQuitConfirm = NodePath("ConfirmQuit/Yes") +btnQuitCancel = NodePath("ConfirmQuit/No") [node name="Title" type="Label" parent="."] layout_mode = 2 text = "Paused" horizontal_alignment = 1 -[node name="Resume" type="Button" parent="."] +[node name="MainButtons" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="Resume" type="Button" parent="MainButtons"] layout_mode = 2 text = "Resume" -[node name="Settings" type="Button" parent="."] +[node name="Settings" type="Button" parent="MainButtons"] layout_mode = 2 text = "Settings" -[node name="Quit" type="Button" parent="."] +[node name="Quit" type="Button" parent="MainButtons"] layout_mode = 2 text = "Quit Game" + +[node name="ConfirmQuit" type="VBoxContainer" parent="."] +layout_mode = 2 +visible = false + +[node name="Label" type="Label" parent="ConfirmQuit"] +layout_mode = 2 +text = "Quit to desktop?" +horizontal_alignment = 1 + +[node name="Yes" type="Button" parent="ConfirmQuit"] +layout_mode = 2 +text = "Yes" + +[node name="No" type="Button" parent="ConfirmQuit"] +layout_mode = 2 +text = "No" diff --git a/ui/pause/PauseMenu.gd b/ui/pause/PauseMenu.gd index db6406f..3dc70ed 100644 --- a/ui/pause/PauseMenu.gd +++ b/ui/pause/PauseMenu.gd @@ -31,7 +31,9 @@ func _unhandled_input(event:InputEvent) -> void: if !visible: return if event.is_action_pressed("ui_cancel"): - if settingsPanel.isOpen(): + if MAIN.isConfirming(): + MAIN.cancelConfirm() + elif settingsPanel.isOpen(): settingsPanel.close() MAIN.open() else: