Files
heros/tools/smoke_equipment_unequip.gd
2026-06-18 15:13:31 +09:00

213 lines
9.2 KiB
GDScript

extends SceneTree
const BattleStateScript := preload("res://scripts/core/battle_state.gd")
const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
func _init() -> void:
var failures: Array[String] = []
_check_core_unequip_contract(failures)
_check_unequip_guards(failures)
_check_armor_bonus_removal(failures)
_check_snapshot_reload_and_sell_stock(failures)
_check_unequip_ui_entries(failures)
if failures.is_empty():
print("equipment unequip smoke ok")
quit(0)
return
for failure in failures:
push_error(failure)
quit(1)
func _check_core_unequip_contract(failures: Array[String]) -> void:
var state = _loaded_state(failures, "core unequip")
if state == null:
return
var cao_cao: Dictionary = state.get_unit("cao_cao")
var before_atk := int(cao_cao.get("atk", 0))
var before_inventory: Dictionary = state.get_inventory_snapshot()
var weapon_item: Dictionary = state.get_item_def("bronze_sword")
var weapon_bonus := int(weapon_item.get("bonuses", {}).get("atk", 0))
if not state.get_equipped_equipment_slots("cao_cao").has("weapon"):
failures.append("Cao Cao should expose an equipped weapon slot")
if not state.try_unequip_item("cao_cao", "weapon"):
failures.append("Cao Cao should be able to unequip Bronze Sword before acting")
return
var after_unit: Dictionary = state.get_unit("cao_cao")
var after_equipment: Dictionary = state.get_equipment_snapshot("cao_cao")
var after_inventory: Dictionary = state.get_inventory_snapshot()
if after_equipment.has("weapon"):
failures.append("weapon slot should be removed after unequip: %s" % str(after_equipment))
if int(after_inventory.get("bronze_sword", 0)) != int(before_inventory.get("bronze_sword", 0)) + 1:
failures.append("unequip should return Bronze Sword to inventory")
if int(after_unit.get("atk", 0)) != before_atk - weapon_bonus:
failures.append("unequip should remove weapon ATK bonus: before %d after %d bonus %d" % [
before_atk,
int(after_unit.get("atk", 0)),
weapon_bonus
])
if int(after_unit.get("range", 0)) != 1 or int(after_unit.get("min_range", 0)) != 1:
failures.append("unequipped Cao Cao should return to class attack range 1-1")
var inventory_after_first: Dictionary = state.get_inventory_snapshot()
if state.try_unequip_item("cao_cao", "weapon"):
failures.append("unequipping an empty weapon slot should fail")
var inventory_after_repeat: Dictionary = state.get_inventory_snapshot()
if int(inventory_after_repeat.get("bronze_sword", 0)) != int(inventory_after_first.get("bronze_sword", 0)):
failures.append("failed repeat unequip should not duplicate inventory")
if not state.try_equip_item("cao_cao", "bronze_sword"):
failures.append("returned Bronze Sword should be equippable again")
var reequip_unit: Dictionary = state.get_unit("cao_cao")
var reequip_inventory: Dictionary = state.get_inventory_snapshot()
if int(reequip_unit.get("atk", 0)) != before_atk:
failures.append("re-equipping should restore weapon ATK")
if int(reequip_inventory.get("bronze_sword", 0)) != int(before_inventory.get("bronze_sword", 0)):
failures.append("re-equipping should consume the returned weapon stock")
func _check_unequip_guards(failures: Array[String]) -> void:
var state = _loaded_state(failures, "unequip guards")
if state == null:
return
var before_equipment: Dictionary = state.get_equipment_snapshot("cao_cao")
var before_inventory: Dictionary = state.get_inventory_snapshot()
var cao_cao: Dictionary = state.get_unit("cao_cao")
cao_cao["moved"] = true
if state.try_unequip_item("cao_cao", "weapon"):
failures.append("moved units should not be able to unequip")
cao_cao["moved"] = false
cao_cao["acted"] = true
if state.try_unequip_item("cao_cao", "weapon"):
failures.append("acted units should not be able to unequip")
cao_cao["acted"] = false
var enemy: Dictionary = state.get_unit("yellow_turban_1")
enemy["equipment"] = {"weapon": "bronze_sword"}
if state.try_unequip_item("yellow_turban_1", "weapon"):
failures.append("enemy units should not be able to unequip")
var protected_state = BattleStateScript.new()
if protected_state.load_battle("res://data/scenarios/007_xian_emperor_escort.json"):
var envoy: Dictionary = protected_state.get_unit("protected_envoy")
envoy["equipment"] = {"weapon": "bronze_sword"}
if protected_state.try_unequip_item("protected_envoy", "weapon"):
failures.append("non-controllable protected units should not be able to unequip")
else:
failures.append("could not load protected-unit scenario for unequip guard")
if state.get_equipment_snapshot("cao_cao") != before_equipment:
failures.append("failed guard unequips should not alter Cao Cao equipment")
if state.get_inventory_snapshot() != before_inventory:
failures.append("failed guard unequips should not alter inventory")
func _check_armor_bonus_removal(failures: Array[String]) -> void:
var state = _loaded_state(failures, "armor bonus removal")
if state == null:
return
var cao_cao: Dictionary = state.get_unit("cao_cao")
var base_max_mp := int(cao_cao.get("max_mp", 0))
var base_mp := int(cao_cao.get("mp", 0))
var base_def := int(cao_cao.get("def", 0))
var base_int := int(cao_cao.get("int", 0))
state.set_inventory_snapshot({"black_tortoise_robe": 1})
if not state.try_equip_item("cao_cao", "black_tortoise_robe"):
failures.append("Cao Cao should be able to equip Black Tortoise Robe for armor unequip test")
return
var equipped: Dictionary = state.get_unit("cao_cao")
if int(equipped.get("max_mp", 0)) != base_max_mp + 2:
failures.append("Black Tortoise Robe should add max MP")
if int(equipped.get("def", 0)) != base_def + 2:
failures.append("Black Tortoise Robe should replace Cloth Robe with net DEF +2")
if int(equipped.get("int", 0)) != base_int + 1:
failures.append("Black Tortoise Robe should add INT")
if not state.try_unequip_item("cao_cao", "armor"):
failures.append("Cao Cao should be able to unequip armor")
return
var unequipped: Dictionary = state.get_unit("cao_cao")
if int(unequipped.get("max_mp", 0)) != base_max_mp:
failures.append("armor unequip should remove max MP bonus")
if int(unequipped.get("mp", 0)) != mini(base_mp, base_max_mp):
failures.append("armor unequip should clamp current MP to restored max")
if int(unequipped.get("def", 0)) != base_def - 1:
failures.append("empty armor slot should remove all armor DEF bonus")
if int(unequipped.get("int", 0)) != base_int:
failures.append("armor unequip should remove INT bonus")
var inventory: Dictionary = state.get_inventory_snapshot()
if int(inventory.get("black_tortoise_robe", 0)) != 1:
failures.append("armor unequip should return Black Tortoise Robe to inventory")
func _check_snapshot_reload_and_sell_stock(failures: Array[String]) -> void:
var state = _loaded_state(failures, "snapshot reload")
if state == null:
return
if not state.try_unequip_item("cao_cao", "weapon"):
failures.append("could not unequip weapon before snapshot reload")
return
var roster_snapshot: Dictionary = state.get_player_roster_snapshot()
var inventory_snapshot: Dictionary = state.get_inventory_snapshot()
var reloaded = BattleStateScript.new()
if not reloaded.load_battle("res://data/scenarios/001_yellow_turbans.json", roster_snapshot, inventory_snapshot):
failures.append("could not reload battle from unequipped roster snapshot")
return
var reloaded_equipment: Dictionary = reloaded.get_equipment_snapshot("cao_cao")
if reloaded_equipment.has("weapon"):
failures.append("roster snapshot reload should keep weapon slot empty: %s" % str(reloaded_equipment))
if int(reloaded.get_inventory_snapshot().get("bronze_sword", 0)) != int(inventory_snapshot.get("bronze_sword", 0)):
failures.append("inventory snapshot reload should keep returned weapon stock")
var scene = BattleSceneScript.new()
scene._create_hud()
scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json", roster_snapshot, inventory_snapshot)
scene.campaign_state.inventory = inventory_snapshot.duplicate(true)
var sellable: Array = scene._get_sellable_item_ids()
if not sellable.has("bronze_sword"):
failures.append("returned unequipped weapon should appear in shop sell stock")
scene.free()
func _check_unequip_ui_entries(failures: Array[String]) -> void:
var scene = BattleSceneScript.new()
scene._create_hud()
scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json")
var selected: Dictionary = scene.state.get_unit("cao_cao")
scene._rebuild_equip_menu(selected)
if not _container_has_button_text(scene.equip_list, "Unequip Weapon:"):
failures.append("battle equipment menu should include an Unequip Weapon button")
scene.armory_unit_id = "cao_cao"
scene._rebuild_armory_menu()
if not _container_has_button_text(scene.armory_list, "Unequip Weapon:"):
failures.append("pre-battle Armory should include an Unequip Weapon button")
scene.free()
func _container_has_button_text(container: Node, fragment: String) -> bool:
for child in container.get_children():
if child is Button and str(child.text).contains(fragment):
return true
return false
func _loaded_state(failures: Array[String], label: String):
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("%s could not load opening scenario" % label)
return null
return state