From 937845f061ea22c191c82fa40d24d2920fc507d0 Mon Sep 17 00:00:00 2001 From: Natalie Date: Sat, 18 Apr 2026 19:03:42 -0700 Subject: [PATCH] =?UTF-8?q?fix(@projects/@magic-civilization):=20?= =?UTF-8?q?=F0=9F=90=9B=20fix=20auto-play=20tier=20misclassification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- src/game/engine/scenes/tests/auto_play.gd | 34 ++++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/game/engine/scenes/tests/auto_play.gd b/src/game/engine/scenes/tests/auto_play.gd index b8aaabc2..bd06d9cc 100644 --- a/src/game/engine/scenes/tests/auto_play.gd +++ b/src/game/engine/scenes/tests/auto_play.gd @@ -1232,8 +1232,12 @@ func _manage_production(city: Variant) -> void: best_wonder = wid if not best_wonder.is_empty(): built = best_wonder - var unit_ids: Array[String] = ["warrior", "founder", "worker"] - if built in unit_ids: + # Use DataLoader.get_unit to classify built item as unit vs building — + # previously a hardcoded list `["warrior", "founder", "worker"]` silently + # mis-queued tier-2+ melee units (p0-39) as buildings → add_to_queue + # rejected them and tier progression never manifested. + var unit_entry: Dictionary = DataLoader.get_unit(built) + if not unit_entry.is_empty(): city.add_to_queue("unit", built) else: city.add_to_queue("building", built) @@ -1437,14 +1441,20 @@ func _next_building(city: Variant, player: Variant, city_count: int, has_founder func _best_melee_for_player(player: RefCounted, city: Variant) -> String: - # Highest-tier buildable melee unit per p0-39 rules: + # Strongest buildable melee unit per p0-39 rules: # - unit_type == "military" AND not in the ranged-specialist set # - tech_required satisfied (player.has_tech) if set # - requires_resource satisfied (BuildableHelper) if set # - race_required matches player.race_id if set - # Returns "warrior" when no higher-tier candidate qualifies. + # Strength proxy is `cost` (costlier unit = stronger = later game) since + # dwarf unit JSONs (public/resources/units/*.json) don't carry an explicit + # `tier` field — cost ordering matches the tech-chain ordering empirically + # (warrior=40 < spearman=56 < iron_vanguard=90 < berserker=100 < + ## graven=145 < bulwark=165 < ironwarden=190 < mithril_vanguard=280). + # Falls back to `tier` when present; callers with explicit tiers get ranked + # that way for forward-compatibility. var best_id: String = "warrior" - var best_tier: int = 1 + var best_rank: int = 0 var ranged_keywords: Array[String] = [ "archer", "ranger", "arbalest", "crossbow", "flying", "catapult", "ballista", ] @@ -1462,9 +1472,6 @@ func _best_melee_for_player(player: RefCounted, city: Variant) -> String: break if is_ranged: continue - var tier: int = int(u.get("tier", 1)) - if tier <= best_tier: - continue var tech_req: String = str(u.get("tech_required", "")) if not tech_req.is_empty() and tech_req != "" and not player.has_tech(tech_req): continue @@ -1477,10 +1484,17 @@ func _best_melee_for_player(player: RefCounted, city: Variant) -> String: if not res_req.is_empty() and res_req != "": if not BuildableHelperScript.player_owns_resource(player, res_req): continue - # Also require the engine agrees this city can build it. if not city.can_build(uid, player): continue - best_tier = tier + # Rank by explicit tier when present, otherwise by cost (dwarf units + # don't carry tier). `tier * 1000 + cost` keeps explicit tiers from + # colliding with cost-ranked peers in the same tier bucket. + var tier_hint: int = int(u.get("tier", 0)) + var cost: int = int(u.get("cost", 0)) + var rank: int = tier_hint * 1000 + cost + if rank <= best_rank: + continue + best_rank = rank best_id = uid return best_id