From 07c3b996d71b056310a5e473615659dc0f2beca0 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Sat, 9 May 2026 00:12:33 +0200 Subject: [PATCH] =?UTF-8?q?feat(save):=20extend=20GameState=20to=20v2=20?= =?UTF-8?q?=E2=80=94=20outfit=20and=20held=20items=20persisted=20per=20cha?= =?UTF-8?q?racter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add held_left/held_right fields to CharacterData - Add get/set_character_outfit and get/set_character_held_item to GameState - get_save_data now returns version:2 with character_outfits and character_held_items - apply_save_data resets both dicts when keys absent (empty-dict reset safe) - 11 new tests in test_game_state.gd — 147/147 passing --- scripts/autoload/GameState.gd | 37 ++++++++++++++- scripts/characters/character_data.gd | 2 + test/unit/test_game_state.gd | 68 ++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/scripts/autoload/GameState.gd b/scripts/autoload/GameState.gd index 00eb00b..8f7d4eb 100644 --- a/scripts/autoload/GameState.gd +++ b/scripts/autoload/GameState.gd @@ -1,10 +1,12 @@ -## GameState — global game state: character positions, object states, current room. +## GameState — global game state: character positions, outfit, held items, object states, current room. extends Node signal state_changed signal character_moved(character_id: String, position: Vector2) var _character_positions: Dictionary = {} +var _character_outfits: Dictionary = {} +var _character_held_items: Dictionary = {} var _object_states: Dictionary = {} var current_room: String = "reception" var music_volume: float = 0.6 @@ -25,6 +27,28 @@ func set_character_position(id: String, pos: Vector2) -> void: state_changed.emit() +func get_character_outfit(id: String) -> Array: + return _character_outfits.get(id, ["", "", ""]) + + +func set_character_outfit(id: String, outfit: Array) -> void: + _character_outfits[id] = outfit + state_changed.emit() + + +func get_character_held_item(id: String, hand: String) -> String: + if not _character_held_items.has(id): + return "" + return _character_held_items[id].get(hand, "") + + +func set_character_held_item(id: String, hand: String, item_id: String) -> void: + if not _character_held_items.has(id): + _character_held_items[id] = {"left": "", "right": ""} + _character_held_items[id][hand] = item_id + state_changed.emit() + + func get_object_state(id: String) -> String: return _object_states.get(id, "idle") @@ -40,7 +64,10 @@ func get_save_data() -> Dictionary: var pos: Vector2 = _character_positions[key] positions[key] = [pos.x, pos.y] return { + "version": 2, "character_positions": positions, + "character_outfits": _character_outfits.duplicate(true), + "character_held_items": _character_held_items.duplicate(true), "object_states": _object_states, "current_room": current_room, "music_volume": music_volume, @@ -55,6 +82,14 @@ func apply_save_data(data: Dictionary) -> void: var val: Variant = data["character_positions"][key] if val is Array and val.size() >= 2: _character_positions[key] = Vector2(val[0], val[1]) + if data.has("character_outfits"): + _character_outfits = data["character_outfits"].duplicate(true) + else: + _character_outfits = {} + if data.has("character_held_items"): + _character_held_items = data["character_held_items"].duplicate(true) + else: + _character_held_items = {} if data.has("object_states"): _object_states = data["object_states"] if data.has("current_room"): diff --git a/scripts/characters/character_data.gd b/scripts/characters/character_data.gd index ac9f820..a01dbe2 100644 --- a/scripts/characters/character_data.gd +++ b/scripts/characters/character_data.gd @@ -11,3 +11,5 @@ enum Species { BUNNY, KITTEN } @export var current_floor: int = 0 @export var position: Vector2 = Vector2.ZERO @export var outfit: Array[String] = ["", "", ""] +@export var held_left: String = "" +@export var held_right: String = "" diff --git a/test/unit/test_game_state.gd b/test/unit/test_game_state.gd index a89616d..a8a8f9a 100644 --- a/test/unit/test_game_state.gd +++ b/test/unit/test_game_state.gd @@ -77,3 +77,71 @@ func test_apply_save_data_with_empty_dict_does_not_crash() -> void: _state.set_character_position("bunny_01", Vector2(10.0, 20.0)) _state.apply_save_data({}) assert_eq(_state.get_character_position("bunny_01"), Vector2(10.0, 20.0)) + + +func test_character_data_has_held_left_field() -> void: + var cd: CharacterData = CharacterData.new() + assert_eq(cd.held_left, "") + + +func test_character_data_has_held_right_field() -> void: + var cd: CharacterData = CharacterData.new() + assert_eq(cd.held_right, "") + + +func test_set_character_outfit_stores_value() -> void: + _state.set_character_outfit("bunny_f", ["white_coat", "", "stethoscope"]) + assert_eq(_state.get_character_outfit("bunny_f"), ["white_coat", "", "stethoscope"]) + + +func test_get_character_outfit_returns_empty_array_for_unknown() -> void: + var result: Array = _state.get_character_outfit("unknown_id") + assert_eq(result, ["", "", ""]) + + +func test_set_character_held_item_left() -> void: + _state.set_character_held_item("bunny_f", "left", "medicine_blue") + assert_eq(_state.get_character_held_item("bunny_f", "left"), "medicine_blue") + + +func test_get_character_held_item_returns_empty_for_unknown() -> void: + assert_eq(_state.get_character_held_item("unknown", "left"), "") + + +func test_save_data_includes_outfit() -> void: + _state.set_character_outfit("bunny_f", ["white_coat", "", ""]) + var data: Dictionary = _state.get_save_data() + assert_true(data.has("character_outfits")) + assert_eq(data["character_outfits"]["bunny_f"], ["white_coat", "", ""]) + + +func test_save_data_includes_held_items() -> void: + _state.set_character_held_item("bunny_f", "right", "medicine_blue") + var data: Dictionary = _state.get_save_data() + assert_true(data.has("character_held_items")) + assert_eq(data["character_held_items"]["bunny_f"]["right"], "medicine_blue") + + +func test_apply_save_data_restores_outfit() -> void: + var save: Dictionary = { + "character_outfits": { + "bunny_f": ["doctor_coat", "", "stethoscope"] + } + } + _state.apply_save_data(save) + assert_eq(_state.get_character_outfit("bunny_f"), ["doctor_coat", "", "stethoscope"]) + + +func test_apply_save_data_restores_held_items() -> void: + var save: Dictionary = { + "character_held_items": { + "kitten_f": {"left": "gel_tube", "right": ""} + } + } + _state.apply_save_data(save) + assert_eq(_state.get_character_held_item("kitten_f", "left"), "gel_tube") + + +func test_save_data_has_version_two() -> void: + var data: Dictionary = _state.get_save_data() + assert_eq(data.get("version", 0), 2)