feat(items): add RoomChest with spawn and receive logic
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,8 @@ const HAND_SLOT_RADIUS: float = 60.0
|
|||||||
|
|
||||||
@export var item_id: String = ""
|
@export var item_id: String = ""
|
||||||
|
|
||||||
|
var home_chest: Node2D = null
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
var drag: DragDropComponent = get_node_or_null("DragDropComponent") as DragDropComponent
|
var drag: DragDropComponent = get_node_or_null("DragDropComponent") as DragDropComponent
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
## RoomChest — tappable storage node. Spawns HoldableItem/OutfitItem instances on demand.
|
||||||
|
## Items fly out with a tween. Receives items back via receive_item().
|
||||||
|
class_name RoomChest extends Node2D
|
||||||
|
|
||||||
|
signal items_spawned(chest: RoomChest)
|
||||||
|
signal item_received(chest: RoomChest, item_id: String)
|
||||||
|
|
||||||
|
const SPAWN_TWEEN_DURATION: float = 0.3
|
||||||
|
|
||||||
|
@export var chest_id: String = ""
|
||||||
|
|
||||||
|
var _spawned_items: Array = []
|
||||||
|
var _item_configs: Array[ChestItemData] = []
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
add_to_group("room_chests")
|
||||||
|
_item_configs = RoomChestConfig.get_items(chest_id)
|
||||||
|
if not chest_id.is_empty() and GameState.has_method("get_chest_state"):
|
||||||
|
if not GameState.get_chest_state(chest_id).is_empty():
|
||||||
|
spawn_items()
|
||||||
|
|
||||||
|
|
||||||
|
func spawn_items() -> void:
|
||||||
|
if not _spawned_items.is_empty():
|
||||||
|
return
|
||||||
|
var parent: Node = get_parent()
|
||||||
|
for config: ChestItemData in _item_configs:
|
||||||
|
var item: HoldableItem = _create_item(config)
|
||||||
|
item.home_chest = self
|
||||||
|
if parent != null:
|
||||||
|
parent.add_child(item)
|
||||||
|
else:
|
||||||
|
add_child(item)
|
||||||
|
item.global_position = global_position
|
||||||
|
_spawned_items.append(item)
|
||||||
|
_tween_item_out(item, config.spawn_offset)
|
||||||
|
if GameState.has_method("set_chest_state"):
|
||||||
|
GameState.set_chest_state(chest_id, _get_spawned_ids())
|
||||||
|
items_spawned.emit(self)
|
||||||
|
|
||||||
|
|
||||||
|
func receive_item(item: HoldableItem) -> void:
|
||||||
|
_spawned_items.erase(item)
|
||||||
|
if GameState.has_method("set_chest_state"):
|
||||||
|
if _spawned_items.is_empty():
|
||||||
|
GameState.clear_chest_state(chest_id)
|
||||||
|
else:
|
||||||
|
GameState.set_chest_state(chest_id, _get_spawned_ids())
|
||||||
|
item_received.emit(self, item.item_id)
|
||||||
|
_tween_item_in(item)
|
||||||
|
|
||||||
|
|
||||||
|
func are_items_spawned() -> bool:
|
||||||
|
return not _spawned_items.is_empty()
|
||||||
|
|
||||||
|
|
||||||
|
func get_spawned_count() -> int:
|
||||||
|
return _spawned_items.size()
|
||||||
|
|
||||||
|
|
||||||
|
func get_item_config_count() -> int:
|
||||||
|
return _item_configs.size()
|
||||||
|
|
||||||
|
|
||||||
|
func _create_item(config: ChestItemData) -> HoldableItem:
|
||||||
|
var item: HoldableItem
|
||||||
|
if config.item_type == ChestItemData.ItemType.OUTFIT:
|
||||||
|
var outfit: OutfitItem = OutfitItem.new()
|
||||||
|
outfit.outfit_layer = config.outfit_layer
|
||||||
|
item = outfit
|
||||||
|
else:
|
||||||
|
item = HoldableItem.new()
|
||||||
|
item.item_id = config.item_id
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
func _get_spawned_ids() -> Array:
|
||||||
|
var ids: Array = []
|
||||||
|
for item: HoldableItem in _spawned_items:
|
||||||
|
ids.append(item.item_id)
|
||||||
|
return ids
|
||||||
|
|
||||||
|
|
||||||
|
func _tween_item_out(item: HoldableItem, offset: Vector2) -> void:
|
||||||
|
var tween: Tween = create_tween()
|
||||||
|
tween.tween_property(item, "global_position", global_position + offset, SPAWN_TWEEN_DURATION)
|
||||||
|
|
||||||
|
|
||||||
|
func _tween_item_in(item: HoldableItem) -> void:
|
||||||
|
var tween: Tween = create_tween()
|
||||||
|
tween.tween_property(item, "global_position", global_position, SPAWN_TWEEN_DURATION)
|
||||||
|
tween.tween_callback(item.queue_free)
|
||||||
@@ -35,3 +35,51 @@ func test_room_chest_config_patient_cabinet_stethoscope_outfit_layer_two() -> vo
|
|||||||
assert_eq(stethoscope.item_id, "stethoscope")
|
assert_eq(stethoscope.item_id, "stethoscope")
|
||||||
assert_eq(stethoscope.item_type, ChestItemData.ItemType.OUTFIT)
|
assert_eq(stethoscope.item_type, ChestItemData.ItemType.OUTFIT)
|
||||||
assert_eq(stethoscope.outfit_layer, 2)
|
assert_eq(stethoscope.outfit_layer, 2)
|
||||||
|
|
||||||
|
|
||||||
|
func test_are_items_spawned_false_initially() -> void:
|
||||||
|
var chest: RoomChest = RoomChest.new()
|
||||||
|
chest.chest_id = "reception_desk"
|
||||||
|
add_child_autofree(chest)
|
||||||
|
assert_false(chest.are_items_spawned())
|
||||||
|
|
||||||
|
|
||||||
|
func test_get_spawned_count_zero_initially() -> void:
|
||||||
|
var chest: RoomChest = RoomChest.new()
|
||||||
|
chest.chest_id = "reception_desk"
|
||||||
|
add_child_autofree(chest)
|
||||||
|
assert_eq(chest.get_spawned_count(), 0)
|
||||||
|
|
||||||
|
|
||||||
|
func test_get_item_config_count_matches_config() -> void:
|
||||||
|
var chest: RoomChest = RoomChest.new()
|
||||||
|
chest.chest_id = "reception_desk"
|
||||||
|
add_child_autofree(chest)
|
||||||
|
assert_eq(chest.get_item_config_count(), 3)
|
||||||
|
|
||||||
|
|
||||||
|
func test_spawn_items_creates_correct_count() -> void:
|
||||||
|
var chest: RoomChest = RoomChest.new()
|
||||||
|
chest.chest_id = "reception_desk"
|
||||||
|
add_child_autofree(chest)
|
||||||
|
chest.spawn_items()
|
||||||
|
assert_eq(chest.get_spawned_count(), 3)
|
||||||
|
|
||||||
|
|
||||||
|
func test_double_spawn_is_noop() -> void:
|
||||||
|
var chest: RoomChest = RoomChest.new()
|
||||||
|
chest.chest_id = "reception_desk"
|
||||||
|
add_child_autofree(chest)
|
||||||
|
chest.spawn_items()
|
||||||
|
chest.spawn_items()
|
||||||
|
assert_eq(chest.get_spawned_count(), 3)
|
||||||
|
|
||||||
|
|
||||||
|
func test_receive_item_decrements_spawned_count() -> void:
|
||||||
|
var chest: RoomChest = RoomChest.new()
|
||||||
|
chest.chest_id = "reception_desk"
|
||||||
|
add_child_autofree(chest)
|
||||||
|
chest.spawn_items()
|
||||||
|
var item: HoldableItem = chest._spawned_items[0] as HoldableItem
|
||||||
|
chest.receive_item(item)
|
||||||
|
assert_eq(chest.get_spawned_count(), 2)
|
||||||
|
|||||||
Reference in New Issue
Block a user