feat(items): add chest-return priority to HoldableItem and GameState v3 chest states
- HoldableItem._try_return_to_chest() snaps item back if within CHEST_RETURN_RADIUS (80px) - _on_drag_released checks chest return before hand-slot fallback - OutfitItem._on_drag_released checks chest return before outfit/hand-slot logic - GameState: _chest_states dict + get/set/clear_chest_state methods - GameState.get_save_data() bumped to version 3, includes chest_states - GameState.apply_save_data() restores chest_states from save data
This commit is contained in:
@@ -8,6 +8,7 @@ var _character_positions: Dictionary = {}
|
|||||||
var _character_outfits: Dictionary = {}
|
var _character_outfits: Dictionary = {}
|
||||||
var _character_held_items: Dictionary = {}
|
var _character_held_items: Dictionary = {}
|
||||||
var _object_states: Dictionary = {}
|
var _object_states: Dictionary = {}
|
||||||
|
var _chest_states: Dictionary = {}
|
||||||
var current_room: String = "reception"
|
var current_room: String = "reception"
|
||||||
var music_volume: float = 0.6
|
var music_volume: float = 0.6
|
||||||
var sfx_volume: float = 1.0
|
var sfx_volume: float = 1.0
|
||||||
@@ -58,17 +59,32 @@ func set_object_state(id: String, state: String) -> void:
|
|||||||
state_changed.emit()
|
state_changed.emit()
|
||||||
|
|
||||||
|
|
||||||
|
func get_chest_state(chest_id: String) -> Array:
|
||||||
|
return _chest_states.get(chest_id, [])
|
||||||
|
|
||||||
|
|
||||||
|
func set_chest_state(chest_id: String, spawned_item_ids: Array) -> void:
|
||||||
|
_chest_states[chest_id] = spawned_item_ids
|
||||||
|
state_changed.emit()
|
||||||
|
|
||||||
|
|
||||||
|
func clear_chest_state(chest_id: String) -> void:
|
||||||
|
_chest_states.erase(chest_id)
|
||||||
|
state_changed.emit()
|
||||||
|
|
||||||
|
|
||||||
func get_save_data() -> Dictionary:
|
func get_save_data() -> Dictionary:
|
||||||
var positions: Dictionary = {}
|
var positions: Dictionary = {}
|
||||||
for key: String in _character_positions:
|
for key: String in _character_positions:
|
||||||
var pos: Vector2 = _character_positions[key]
|
var pos: Vector2 = _character_positions[key]
|
||||||
positions[key] = [pos.x, pos.y]
|
positions[key] = [pos.x, pos.y]
|
||||||
return {
|
return {
|
||||||
"version": 2,
|
"version": 3,
|
||||||
"character_positions": positions,
|
"character_positions": positions,
|
||||||
"character_outfits": _character_outfits.duplicate(true),
|
"character_outfits": _character_outfits.duplicate(true),
|
||||||
"character_held_items": _character_held_items.duplicate(true),
|
"character_held_items": _character_held_items.duplicate(true),
|
||||||
"object_states": _object_states,
|
"object_states": _object_states,
|
||||||
|
"chest_states": _chest_states.duplicate(true),
|
||||||
"current_room": current_room,
|
"current_room": current_room,
|
||||||
"music_volume": music_volume,
|
"music_volume": music_volume,
|
||||||
"sfx_volume": sfx_volume,
|
"sfx_volume": sfx_volume,
|
||||||
@@ -98,3 +114,7 @@ func apply_save_data(data: Dictionary) -> void:
|
|||||||
music_volume = data["music_volume"]
|
music_volume = data["music_volume"]
|
||||||
if data.has("sfx_volume"):
|
if data.has("sfx_volume"):
|
||||||
sfx_volume = data["sfx_volume"]
|
sfx_volume = data["sfx_volume"]
|
||||||
|
if data.has("chest_states"):
|
||||||
|
_chest_states = data["chest_states"].duplicate(true)
|
||||||
|
else:
|
||||||
|
_chest_states = {}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ signal item_picked_up(item: HoldableItem)
|
|||||||
signal item_placed(item: HoldableItem)
|
signal item_placed(item: HoldableItem)
|
||||||
|
|
||||||
const HAND_SLOT_RADIUS: float = 60.0
|
const HAND_SLOT_RADIUS: float = 60.0
|
||||||
|
const CHEST_RETURN_RADIUS: float = 80.0
|
||||||
|
|
||||||
@export var item_id: String = ""
|
@export var item_id: String = ""
|
||||||
|
|
||||||
@@ -27,6 +28,8 @@ func _on_drag_picked_up(_pos: Vector2) -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _on_drag_released(_pos: Vector2) -> void:
|
func _on_drag_released(_pos: Vector2) -> void:
|
||||||
|
if _try_return_to_chest():
|
||||||
|
return
|
||||||
var result: Array = _find_nearest_free_hand_slot()
|
var result: Array = _find_nearest_free_hand_slot()
|
||||||
if not result.is_empty():
|
if not result.is_empty():
|
||||||
var character: Character = result[0] as Character
|
var character: Character = result[0] as Character
|
||||||
@@ -35,6 +38,15 @@ func _on_drag_released(_pos: Vector2) -> void:
|
|||||||
item_placed.emit(self)
|
item_placed.emit(self)
|
||||||
|
|
||||||
|
|
||||||
|
func _try_return_to_chest() -> bool:
|
||||||
|
if home_chest == null:
|
||||||
|
return false
|
||||||
|
if global_position.distance_to(home_chest.global_position) >= CHEST_RETURN_RADIUS:
|
||||||
|
return false
|
||||||
|
(home_chest as RoomChest).receive_item(self)
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
func is_in_hand_slot() -> bool:
|
func is_in_hand_slot() -> bool:
|
||||||
var p: Node = get_parent()
|
var p: Node = get_parent()
|
||||||
if p == null:
|
if p == null:
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ const OUTFIT_APPLY_RADIUS: float = 80.0
|
|||||||
|
|
||||||
|
|
||||||
func _on_drag_released(_pos: Vector2) -> void:
|
func _on_drag_released(_pos: Vector2) -> void:
|
||||||
|
if _try_return_to_chest():
|
||||||
|
return
|
||||||
var character: Character = _find_nearest_character()
|
var character: Character = _find_nearest_character()
|
||||||
if character != null:
|
if character != null:
|
||||||
character.apply_outfit_item(outfit_layer, item_id, outfit_sprite, self)
|
character.apply_outfit_item(outfit_layer, item_id, outfit_sprite, self)
|
||||||
|
|||||||
@@ -144,4 +144,40 @@ func test_apply_save_data_restores_held_items() -> void:
|
|||||||
|
|
||||||
func test_save_data_has_version_two() -> void:
|
func test_save_data_has_version_two() -> void:
|
||||||
var data: Dictionary = _state.get_save_data()
|
var data: Dictionary = _state.get_save_data()
|
||||||
assert_eq(data.get("version", 0), 2)
|
assert_eq(data.get("version", 0), 3)
|
||||||
|
|
||||||
|
|
||||||
|
func test_get_chest_state_returns_empty_for_unknown_id() -> void:
|
||||||
|
assert_eq(GameState.get_chest_state("nonexistent_chest_xyz"), [])
|
||||||
|
|
||||||
|
|
||||||
|
func test_set_and_get_chest_state() -> void:
|
||||||
|
GameState.set_chest_state("pharmacy_medicine_test", ["pill_bottle", "syrup"])
|
||||||
|
assert_eq(GameState.get_chest_state("pharmacy_medicine_test"), ["pill_bottle", "syrup"])
|
||||||
|
|
||||||
|
|
||||||
|
func test_clear_chest_state_removes_entry() -> void:
|
||||||
|
GameState.set_chest_state("lab_bench_test", ["test_tube"])
|
||||||
|
GameState.clear_chest_state("lab_bench_test")
|
||||||
|
assert_eq(GameState.get_chest_state("lab_bench_test"), [])
|
||||||
|
|
||||||
|
|
||||||
|
func test_chest_state_included_in_save_data() -> void:
|
||||||
|
GameState.set_chest_state("xray_cabinet_test", ["xray_sheet"])
|
||||||
|
var data: Dictionary = GameState.get_save_data()
|
||||||
|
assert_true(data.has("chest_states"))
|
||||||
|
assert_eq(data["chest_states"]["xray_cabinet_test"], ["xray_sheet"])
|
||||||
|
|
||||||
|
|
||||||
|
func test_save_data_version_is_three() -> void:
|
||||||
|
var data: Dictionary = GameState.get_save_data()
|
||||||
|
assert_eq(data["version"], 3)
|
||||||
|
|
||||||
|
|
||||||
|
func test_apply_save_data_restores_chest_state() -> void:
|
||||||
|
var data: Dictionary = {
|
||||||
|
"version": 3,
|
||||||
|
"chest_states": {"reception_desk_test": ["clipboard", "pen"]},
|
||||||
|
}
|
||||||
|
GameState.apply_save_data(data)
|
||||||
|
assert_eq(GameState.get_chest_state("reception_desk_test"), ["clipboard", "pen"])
|
||||||
|
|||||||
@@ -69,3 +69,33 @@ func test_holdable_item_detach_preserves_global_position() -> void:
|
|||||||
var hand_pos: Vector2 = character.get_node("HandLeft").global_position
|
var hand_pos: Vector2 = character.get_node("HandLeft").global_position
|
||||||
item._on_drag_picked_up(hand_pos)
|
item._on_drag_picked_up(hand_pos)
|
||||||
assert_eq(item.global_position, hand_pos)
|
assert_eq(item.global_position, hand_pos)
|
||||||
|
|
||||||
|
|
||||||
|
func test_try_return_to_chest_false_when_no_home_chest() -> void:
|
||||||
|
var item: HoldableItem = HoldableItem.new()
|
||||||
|
add_child_autofree(item)
|
||||||
|
assert_false(item._try_return_to_chest())
|
||||||
|
|
||||||
|
|
||||||
|
func test_try_return_to_chest_false_when_beyond_radius() -> void:
|
||||||
|
var chest: RoomChest = RoomChest.new()
|
||||||
|
chest.chest_id = "reception_desk"
|
||||||
|
add_child_autofree(chest)
|
||||||
|
chest.global_position = Vector2.ZERO
|
||||||
|
var item: HoldableItem = HoldableItem.new()
|
||||||
|
add_child_autofree(item)
|
||||||
|
item.home_chest = chest
|
||||||
|
item.global_position = Vector2(200.0, 0.0)
|
||||||
|
assert_false(item._try_return_to_chest())
|
||||||
|
|
||||||
|
|
||||||
|
func test_try_return_to_chest_true_when_within_radius() -> void:
|
||||||
|
var chest: RoomChest = RoomChest.new()
|
||||||
|
chest.chest_id = "reception_desk"
|
||||||
|
add_child_autofree(chest)
|
||||||
|
chest.global_position = Vector2.ZERO
|
||||||
|
var item: HoldableItem = HoldableItem.new()
|
||||||
|
add_child_autofree(item)
|
||||||
|
item.home_chest = chest
|
||||||
|
item.global_position = Vector2(40.0, 0.0)
|
||||||
|
assert_true(item._try_return_to_chest())
|
||||||
|
|||||||
Reference in New Issue
Block a user