feat(core): implement room navigation, character states, save/load and settings menu
- RoomNavigator autoload: smooth camera pan between floors - Floor1 and Floor2 placeholder rooms with elevator buttons - CharacterData Resource with State enum (HEALTHY/SICK/SLEEPING/TIRED) - Character visual state feedback via ColorRect color - Main scene loads saved state on startup - SettingsMenu with music/sfx sliders and game reset - HUD settings button to open SettingsMenu Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,18 +3,55 @@ class_name Character extends Node2D
|
||||
|
||||
signal character_picked_up(character: Character)
|
||||
signal character_placed(character: Character, position: Vector2)
|
||||
signal state_changed(new_state: CharacterData.State)
|
||||
|
||||
@export var character_id: String = ""
|
||||
@export var display_name: String = ""
|
||||
@export var data: CharacterData
|
||||
|
||||
var _is_held: bool = false
|
||||
|
||||
const _STATE_COLORS: Dictionary = {
|
||||
CharacterData.State.HEALTHY: Color(0.6, 0.8, 1.0),
|
||||
CharacterData.State.SICK: Color(0.7, 0.9, 0.7),
|
||||
CharacterData.State.SLEEPING: Color(0.8, 0.7, 0.95),
|
||||
CharacterData.State.TIRED: Color(1.0, 0.95, 0.6),
|
||||
CharacterData.State.PREGNANT: Color(1.0, 0.85, 0.9),
|
||||
CharacterData.State.BABY: Color(0.9, 0.95, 1.0),
|
||||
}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var drag: DragDropComponent = get_node_or_null("DragDropComponent") as DragDropComponent
|
||||
if drag != null:
|
||||
drag.drag_picked_up.connect(_on_drag_picked_up)
|
||||
drag.drag_released.connect(_on_drag_released)
|
||||
if data != null:
|
||||
_update_visual_state()
|
||||
|
||||
|
||||
func set_state(new_state: CharacterData.State) -> void:
|
||||
if data == null:
|
||||
return
|
||||
data.state = new_state
|
||||
_update_visual_state()
|
||||
state_changed.emit(new_state)
|
||||
|
||||
|
||||
func _update_visual_state() -> void:
|
||||
if data == null:
|
||||
return
|
||||
var visual: ColorRect = get_node_or_null("Visual") as ColorRect
|
||||
if visual == null:
|
||||
return
|
||||
var color: Color = _STATE_COLORS.get(data.state, Color(0.6, 0.8, 1.0))
|
||||
visual.color = color
|
||||
var ear_left: ColorRect = get_node_or_null("Ears/EarLeft") as ColorRect
|
||||
var ear_right: ColorRect = get_node_or_null("Ears/EarRight") as ColorRect
|
||||
if ear_left != null:
|
||||
ear_left.color = color
|
||||
if ear_right != null:
|
||||
ear_right.color = color
|
||||
|
||||
|
||||
func _on_drag_picked_up(_pos: Vector2) -> void:
|
||||
|
||||
12
scripts/characters/character_data.gd
Normal file
12
scripts/characters/character_data.gd
Normal file
@@ -0,0 +1,12 @@
|
||||
## CharacterData — Resource holding all persistent state for one character.
|
||||
class_name CharacterData extends Resource
|
||||
|
||||
enum State { HEALTHY, SICK, SLEEPING, TIRED, PREGNANT, BABY }
|
||||
enum Species { BUNNY, KITTEN }
|
||||
|
||||
@export var id: String = ""
|
||||
@export var display_name: String = ""
|
||||
@export var species: Species = Species.BUNNY
|
||||
@export var state: State = State.HEALTHY
|
||||
@export var current_floor: int = 0
|
||||
@export var position: Vector2 = Vector2.ZERO
|
||||
16
scripts/main/main.gd
Normal file
16
scripts/main/main.gd
Normal file
@@ -0,0 +1,16 @@
|
||||
## Main — scene root: wires up RoomNavigator and restores saved character positions.
|
||||
extends Node2D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
RoomNavigator.initialize($Camera2D)
|
||||
SaveManager.load_game()
|
||||
_apply_saved_state()
|
||||
|
||||
|
||||
func _apply_saved_state() -> void:
|
||||
for character in $Characters.get_children():
|
||||
if character is Character and character.data != null:
|
||||
var pos: Vector2 = GameState.get_character_position(character.data.id)
|
||||
if pos != Vector2.ZERO:
|
||||
character.global_position = pos
|
||||
30
scripts/objects/elevator_button.gd
Normal file
30
scripts/objects/elevator_button.gd
Normal file
@@ -0,0 +1,30 @@
|
||||
## ElevatorButton — tappable button that navigates the camera to a target floor.
|
||||
class_name ElevatorButton extends Node2D
|
||||
|
||||
@export var target_floor: int = 0
|
||||
|
||||
var _area: Area2D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_area = get_node_or_null("CollisionArea") as Area2D
|
||||
if _area != null:
|
||||
_area.input_event.connect(_on_input_event)
|
||||
|
||||
|
||||
func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int) -> void:
|
||||
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
_on_pressed()
|
||||
elif event is InputEventScreenTouch and event.pressed:
|
||||
_on_pressed()
|
||||
|
||||
|
||||
func _on_pressed() -> void:
|
||||
RoomNavigator.go_to_floor(target_floor)
|
||||
_play_bounce()
|
||||
|
||||
|
||||
func _play_bounce() -> void:
|
||||
var tween: Tween = create_tween()
|
||||
tween.tween_property(self, "scale", Vector2(1.15, 1.15), 0.08)
|
||||
tween.tween_property(self, "scale", Vector2(1.0, 1.0), 0.08)
|
||||
@@ -1,10 +1,11 @@
|
||||
## HUD — heads-up display with back button and music toggle.
|
||||
## HUD — heads-up display with back button, music toggle, and settings access.
|
||||
extends CanvasLayer
|
||||
|
||||
const MUSIC_ON_SYMBOL: String = "♪"
|
||||
const MUSIC_OFF_SYMBOL: String = "✕"
|
||||
|
||||
var _music_enabled: bool = true
|
||||
@onready var _settings_menu: SettingsMenu = get_node_or_null("/root/Main/UI/SettingsMenu") as SettingsMenu
|
||||
|
||||
|
||||
func _on_back_button_pressed() -> void:
|
||||
@@ -18,3 +19,8 @@ func _on_music_toggle_pressed() -> void:
|
||||
var btn: Button = get_node_or_null("MusicToggle") as Button
|
||||
if btn != null:
|
||||
btn.text = MUSIC_ON_SYMBOL if _music_enabled else MUSIC_OFF_SYMBOL
|
||||
|
||||
|
||||
func _on_settings_button_pressed() -> void:
|
||||
if _settings_menu != null:
|
||||
_settings_menu.show_menu()
|
||||
|
||||
30
scripts/systems/room_navigator.gd
Normal file
30
scripts/systems/room_navigator.gd
Normal file
@@ -0,0 +1,30 @@
|
||||
## RoomNavigator — autoload that moves the Camera2D smoothly between hospital floors.
|
||||
extends Node
|
||||
|
||||
signal room_changed(floor_index: int)
|
||||
|
||||
const FLOOR_HEIGHT: float = 720.0
|
||||
const CAMERA_TWEEN_DURATION: float = 0.6
|
||||
|
||||
var _current_floor: int = 0
|
||||
var _camera: Camera2D
|
||||
|
||||
|
||||
func initialize(camera: Camera2D) -> void:
|
||||
_camera = camera
|
||||
|
||||
|
||||
func go_to_floor(floor_index: int) -> void:
|
||||
if _camera == null or floor_index == _current_floor:
|
||||
return
|
||||
_current_floor = floor_index
|
||||
var target_y: float = floor_index * -FLOOR_HEIGHT
|
||||
var tween: Tween = _camera.create_tween()
|
||||
tween.set_ease(Tween.EASE_IN_OUT)
|
||||
tween.set_trans(Tween.TRANS_SINE)
|
||||
tween.tween_property(_camera, "position:y", target_y, CAMERA_TWEEN_DURATION)
|
||||
room_changed.emit(floor_index)
|
||||
|
||||
|
||||
func get_current_floor() -> int:
|
||||
return _current_floor
|
||||
37
scripts/systems/settings_menu.gd
Normal file
37
scripts/systems/settings_menu.gd
Normal file
@@ -0,0 +1,37 @@
|
||||
## SettingsMenu — overlay panel for audio volume and game reset.
|
||||
class_name SettingsMenu extends CanvasLayer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
visible = false
|
||||
|
||||
|
||||
func show_menu() -> void:
|
||||
visible = true
|
||||
|
||||
|
||||
func hide_menu() -> void:
|
||||
visible = false
|
||||
|
||||
|
||||
func _on_music_slider_value_changed(value: float) -> void:
|
||||
AudioManager.set_music_volume(value)
|
||||
|
||||
|
||||
func _on_sfx_slider_value_changed(value: float) -> void:
|
||||
AudioManager.set_sfx_volume(value)
|
||||
|
||||
|
||||
func _on_reset_button_pressed() -> void:
|
||||
var dialog: ConfirmationDialog = get_node_or_null("Panel/ResetConfirmDialog") as ConfirmationDialog
|
||||
if dialog != null:
|
||||
dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_reset_confirmed() -> void:
|
||||
SaveManager.reset_game()
|
||||
get_tree().reload_current_scene()
|
||||
|
||||
|
||||
func _on_close_button_pressed() -> void:
|
||||
hide_menu()
|
||||
Reference in New Issue
Block a user