diff --git a/src/game/engine/src/modules/ai/ai_turn_bridge.gd b/src/game/engine/src/modules/ai/ai_turn_bridge.gd index f63731a9..4b885143 100644 --- a/src/game/engine/src/modules/ai/ai_turn_bridge.gd +++ b/src/game/engine/src/modules/ai/ai_turn_bridge.gd @@ -839,14 +839,22 @@ static func _find_unit_type_by_flag(player: RefCounted, flag: String) -> String: static func _pick_buildable_military_unit_id(city: RefCounted, player: RefCounted) -> String: - var race_data: Dictionary = DataLoader.get_race(player.race_id) - var start_units: Array = race_data.get("start_units", []) + ## Cheapest buildable combat unit for the player. Iterates the full unit + ## catalog (can_build() applies the race + tech gates); the old code read a + ## race `start_units` field that no longer exists, so the AI never queued + ## military. Require a non-founder combat unit (attack > 0) and exclude wild + ## creatures (faction "wild" — e.g. dire_bear is cost 0 and would always win). var best_id: String = "" var best_cost: int = 2147483647 - for uid: String in start_units: - var udata: Dictionary = DataLoader.get_unit(uid) + var units: Dictionary = DataLoader.get_data("units") + for uid: String in units: + var udata: Dictionary = units[uid] as Dictionary if udata.is_empty() or udata.get("can_found_city", false): continue + if int(udata.get("attack", 0)) <= 0: + continue + if String(udata.get("faction", "")) == "wild": + continue if not city.can_build(uid, player): continue var cost: int = int(udata.get("cost", 999999)) diff --git a/src/game/engine/tests/unit/entities/test_unit_actions.gd b/src/game/engine/tests/unit/entities/test_unit_actions.gd index d0659bb8..3efc6759 100644 --- a/src/game/engine/tests/unit/entities/test_unit_actions.gd +++ b/src/game/engine/tests/unit/entities/test_unit_actions.gd @@ -47,27 +47,17 @@ func test_archer_has_ranged_keyword() -> void: func test_no_unit_has_legacy_flags_field() -> void: - var units_dir: String = "res://public/games/age-of-dwarves/data/units/" - var dir: DirAccess = DirAccess.open(units_dir) - assert_not_null(dir, "units/ directory must be accessible") - if dir == null: - return - var legacy_keys: Array[String] = ["flags", "can_found_city", "can_build_improvements"] - dir.list_dir_begin() - var filename: String = dir.get_next() - while filename != "": - if filename.ends_with(".json") and filename != "manifest.json" and filename != "stub.json": - var path: String = units_dir + filename - var text: String = FileAccess.get_file_as_string(path) - var parsed: Array = JSON.parse_string(text) as Array - for entry: Dictionary in parsed: - for key: String in legacy_keys: - assert_false( - entry.has(key), - "%s must not have legacy field '%s'" % [filename, key] - ) - filename = dir.get_next() - dir.list_dir_end() + # Guards a migration that is NOT complete, and predates two schema changes: + # units now live in public/resources/units/ (not .../data/units/) and are one + # JSON object per file (not an array). The runtime still reads can_found_city + # and flags (founder logic, the AI unit picker, build gating), so units + # legitimately carry these fields today — asserting their absence fails by + # design. Re-enable (and fix the path/format) once the inline flags are fully + # migrated to unit_actions.json and the fields are vestigial. + pending( + "unit_actions migration incomplete — flags/can_found_city still read by " + + "the runtime; cannot assert their absence yet" + ) # ── get_keywords / get_keywords_str ──────────────────────────────────────────