feat: spawn enemies from reusable definitions
This commit is contained in:
@@ -111,6 +111,7 @@ func _test_defend_selection() -> void:
|
||||
"A strong idle villager should rally to defend during an active raid"
|
||||
)
|
||||
var worker: SimNPC = manager.npcs[1]
|
||||
worker.profession = SimulationIds.PROFESSION_FARMER
|
||||
worker.strength = 1.0
|
||||
worker.hunger = 20.0
|
||||
worker.energy = 90.0
|
||||
|
||||
@@ -51,7 +51,7 @@ func _test_core_pack() -> void:
|
||||
"Core items should include food, wood, sword, and claw in stable-ID order"
|
||||
)
|
||||
_check(
|
||||
_enemy_ids(catalog.get_enemies()) == [&"enemy_raider", &"enemy_wolf"],
|
||||
_enemy_ids(catalog.get_enemies()) == [&"enemy_boar", &"enemy_raider", &"enemy_wolf"],
|
||||
"Core enemies should enumerate by stable ID"
|
||||
)
|
||||
var food := catalog.get_item(SimulationIds.RESOURCE_FOOD)
|
||||
@@ -69,10 +69,15 @@ func _test_core_pack() -> void:
|
||||
"Claw should preserve its weapon contract"
|
||||
)
|
||||
var wolf := catalog.get_enemy(&"enemy_wolf")
|
||||
var boar := catalog.get_enemy(&"enemy_boar")
|
||||
_check(
|
||||
wolf != null and wolf.weapon_id == SimulationIds.ITEM_CLAW,
|
||||
"Enemy references should resolve against catalog items"
|
||||
)
|
||||
_check(
|
||||
boar != null and boar.behavior_profile_id == wolf.behavior_profile_id,
|
||||
"New enemy types should be able to reuse an existing behavior profile"
|
||||
)
|
||||
|
||||
|
||||
func _test_deterministic_enumeration_and_dependencies() -> void:
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
extends GutTest
|
||||
|
||||
|
||||
func test_core_boar_reuses_wolf_behavior_and_spawns_from_data() -> void:
|
||||
SimulationEnemies.invalidate_cache()
|
||||
var wolf := SimulationEnemies.get_enemy(&"enemy_wolf")
|
||||
var boar := SimulationEnemies.get_enemy(&"enemy_boar")
|
||||
|
||||
assert_not_null(wolf)
|
||||
assert_not_null(boar)
|
||||
assert_eq(boar.behavior_profile_id, wolf.behavior_profile_id)
|
||||
assert_eq(boar.behavior_profile_id, EnemyDefinition.BEHAVIOR_PROFILE_WOLF_HUNT)
|
||||
assert_eq(boar.combatant_id_prefix, "boar")
|
||||
var factory := CombatantFactory.new()
|
||||
var combatant := factory.create_enemy(boar.enemy_id, Vector3(1.0, 2.0, 3.0))
|
||||
assert_not_null(combatant)
|
||||
assert_eq(combatant.get_combatant_id(), &"boar_0")
|
||||
assert_eq(combatant.get_enemy_definition_id(), boar.enemy_id)
|
||||
assert_eq(combatant.get_kind(), boar.kind)
|
||||
assert_eq(combatant.get_faction_id(), boar.faction_id)
|
||||
assert_eq(combatant.get_weapon_id(), boar.weapon_id)
|
||||
assert_eq(combatant.get_max_health(), boar.health)
|
||||
assert_eq(combatant.is_hostile(), boar.hostile)
|
||||
|
||||
|
||||
func test_factory_accepts_a_definition_without_enemy_specific_code() -> void:
|
||||
var definition := EnemyDefinition.new()
|
||||
definition.enemy_id = &"enemy_test_hound"
|
||||
definition.display_name = "Test Hound"
|
||||
definition.kind = SimulationIds.COMBATANT_KIND_WOLF
|
||||
definition.behavior_profile_id = EnemyDefinition.BEHAVIOR_PROFILE_WOLF_HUNT
|
||||
definition.combatant_id_prefix = "test_hound"
|
||||
definition.faction_id = SimulationIds.FACTION_TRIBE
|
||||
definition.hostile = true
|
||||
definition.weapon_id = SimulationIds.ITEM_CLAW
|
||||
definition.health = 37.0
|
||||
var factory := CombatantFactory.new()
|
||||
|
||||
assert_true(definition.validate().is_empty())
|
||||
var combatant := factory.create_from_definition(definition, Vector3.ZERO)
|
||||
assert_not_null(combatant)
|
||||
assert_eq(combatant.get_combatant_id(), &"test_hound_0")
|
||||
assert_eq(combatant.get_enemy_definition_id(), definition.enemy_id)
|
||||
assert_eq(combatant.get_max_health(), 37.0)
|
||||
|
||||
|
||||
func test_enemy_definition_id_round_trips_and_v1_infers_legacy_ids() -> void:
|
||||
var current := CombatantStateRecord.create(
|
||||
&"boar_7",
|
||||
SimulationIds.COMBATANT_KIND_WOLF,
|
||||
SimulationIds.FACTION_TRIBE,
|
||||
"Wild Boar",
|
||||
Vector3.ZERO,
|
||||
55.0,
|
||||
SimulationIds.ITEM_CLAW,
|
||||
CombatantStateRecord.NO_NPC_ID,
|
||||
true,
|
||||
&"enemy_boar"
|
||||
)
|
||||
var restored := CombatantStateRecord.from_dictionary(current.to_dictionary())
|
||||
assert_not_null(restored)
|
||||
assert_eq(restored.get_enemy_definition_id(), &"enemy_boar")
|
||||
|
||||
var legacy_data := current.to_dictionary()
|
||||
legacy_data["schema_version"] = CombatantStateRecord.LEGACY_SCHEMA_VERSION
|
||||
legacy_data.erase("enemy_definition_id")
|
||||
var migrated := CombatantStateRecord.from_dictionary(legacy_data)
|
||||
assert_not_null(migrated)
|
||||
assert_eq(migrated.get_enemy_definition_id(), &"enemy_wolf")
|
||||
assert_eq(int(migrated.to_dictionary()["schema_version"]), CombatantStateRecord.SCHEMA_VERSION)
|
||||
|
||||
|
||||
func test_conflict_restore_rebuilds_collision_free_definition_allocators() -> void:
|
||||
var records: Array[CombatantStateRecord] = [
|
||||
_enemy_record(&"wolf_0", &"enemy_wolf"),
|
||||
_enemy_record(&"wolf_4", &"enemy_wolf"),
|
||||
_enemy_record(&"boar_2", &"enemy_boar"),
|
||||
]
|
||||
var no_factions: Array[FactionStateRecord] = []
|
||||
var conflict := ConflictSystem.new()
|
||||
conflict.restore_state_records(records, no_factions, 12)
|
||||
|
||||
var wolf_id := conflict.spawn_wolf(Vector3.ZERO)
|
||||
var boar_id := conflict.spawn_enemy(&"enemy_boar", Vector3.ONE)
|
||||
assert_eq(wolf_id, &"wolf_5")
|
||||
assert_eq(boar_id, &"boar_3")
|
||||
assert_eq(conflict.get_combatant(wolf_id).get_enemy_definition_id(), &"enemy_wolf")
|
||||
assert_eq(conflict.get_combatant(boar_id).get_enemy_definition_id(), &"enemy_boar")
|
||||
assert_eq(conflict.get_all_combatants().size(), 6)
|
||||
|
||||
|
||||
func _enemy_record(
|
||||
combatant_id: StringName, enemy_definition_id: StringName
|
||||
) -> CombatantStateRecord:
|
||||
var definition := SimulationEnemies.get_enemy(enemy_definition_id)
|
||||
return CombatantStateRecord.create(
|
||||
combatant_id,
|
||||
definition.kind,
|
||||
definition.faction_id,
|
||||
definition.display_name,
|
||||
Vector3.ZERO,
|
||||
definition.health,
|
||||
definition.weapon_id,
|
||||
CombatantStateRecord.NO_NPC_ID,
|
||||
definition.hostile,
|
||||
definition.enemy_id
|
||||
)
|
||||
Reference in New Issue
Block a user