diff --git a/src/game/engine/scenes/tests/auto_play.gd b/src/game/engine/scenes/tests/auto_play.gd index 8f6540f4..38d858f3 100644 --- a/src/game/engine/scenes/tests/auto_play.gd +++ b/src/game/engine/scenes/tests/auto_play.gd @@ -397,11 +397,7 @@ func _process(_delta: float) -> void: # Force Pangaea so all players share one landmass (no water barriers) GameState.game_settings["map_type"] = "pangaea" GameState.game_settings["num_players"] = 2 - var diff_env: String = EnvConfig.get_var("AI_DIFFICULTY", "") - if not diff_env.is_empty(): - GameState.game_settings["difficulty"] = diff_env - print("AutoPlay: AI_DIFFICULTY=%s applied" % diff_env) - GameState.apply_ai_difficulty() + GameState.game_settings["map_type"] = "pangaea" _state = "wait_loading" _frame = 0 if _frame > 120: @@ -671,37 +667,7 @@ func _play_turn() -> void: if player.researching.is_empty(): _pick_research(player) - # Refresh attack-phase signals and stack-sustain telemetry for this turn. - # _attack_commitment_turns reflects prior-turn commitment; rush-buy and - # building scoring both key off it so they respond mid-siege. - _in_attack_phase = _attack_commitment_turns > 0 and _locked_target != Vector2i(-1, -1) - _active_attack_mil_count = 0 - if _in_attack_phase: - for u_stk: RefCounted in player.units: - if not u_stk.is_alive(): - continue - if u_stk.get("can_found_city") == true: - continue - if u_stk.get("can_build_improvements") == true: - continue - if HexUtilsScript.hex_distance(u_stk.position, _locked_target) <= 8: - _active_attack_mil_count += 1 - if _turn_count % 10 == 0: - print( - ( - " [STACK] turn=%d at_target=%d locked=%s commit=%d" - % [ - _turn_count, - _active_attack_mil_count, - str(_locked_target), - _attack_commitment_turns, - ] - ) - ) - - # 0b. Gold rush-buy warriors — spawn at city nearest to attack target. - # During active siege, lower the threshold so we can replace losses fast. - # Stack critical (<=1 near target) drops the threshold further. + # 0b. Gold rush-buy warriors — spawn at city nearest to attack target var mil_pre: int = 0 for u_pre: RefCounted in player.units: if u_pre.is_alive() and u_pre.get("can_found_city") != true: @@ -858,7 +824,7 @@ func _play_turn() -> void: u.fortified_turns = 0 _move_toward(u, target, game_map) else: - # BUILD PHASE: garrison at city, don't engage. Only scouts explore. + # BUILD PHASE (<3 military): rally at city, attack adjacent enemies for u: Variant in units_snapshot: if not u.is_alive() or u.movement_remaining <= 0: continue @@ -869,7 +835,11 @@ func _play_turn() -> void: if u.type_id == "dwarf_scout" and _turn_count <= 20: _explore(u, player, game_map) else: - # Fortify at city — do NOT attack or chase enemies + # Always attack adjacent enemies first + _try_attack_adjacent(u, game_map) + if not u.is_alive() or u.movement_remaining <= 0: + continue + # Rally at city — no fortifying, stay mobile if u.position != city_pos: _move_toward(u, city_pos, game_map) elif not u.is_fortified: @@ -1207,139 +1177,6 @@ func _command_unit(unit: Variant, player: RefCounted, game_map: RefCounted) -> v _explore(unit, player, game_map) -func _decide_founder(unit: Variant, player: RefCounted, game_map: RefCounted) -> void: - ## State-based founding: score reachable sites, move to the best; only found at a local max. - const MIN_FOUND_SCORE: float = 4.0 - const MAX_WAIT_TURNS: int = 40 - const LOOK_AHEAD_MULT: int = 3 - var water: Array = ["ocean", "coast", "deep_ocean", "lake"] - var reach: int = max(1, int(unit.max_movement) * LOOK_AHEAD_MULT) - var reachable: Dictionary = PathfinderScript.movement_range(game_map, unit.position, reach, "land") - var best_pos: Vector2i = unit.position - var best_score: float = _score_site(unit.position, game_map) - var current_score: float = best_score - for pos: Vector2i in reachable: - var tile: Resource = game_map.get_tile(pos) - if tile == null or tile.biome_id in water: - continue - var too_close: bool = false - for c: Variant in player.cities: - if HexUtilsScript.hex_distance(pos, c.position) < 5: - too_close = true - break - if too_close: - continue - var s: float = _score_site(pos, game_map) - if s > best_score: - best_score = s - best_pos = pos - print("[SITE] turn=%d current=%.1f best=%.1f chosen=%s" % [ - _turn_count, current_score, best_score, str(best_pos) - ]) - var current_valid: bool = true - var cur_tile: Resource = game_map.get_tile(unit.position) - if cur_tile == null or cur_tile.biome_id in water: - current_valid = false - for c: Variant in player.cities: - if HexUtilsScript.hex_distance(unit.position, c.position) < 5: - current_valid = false - break - if best_pos == unit.position and current_valid: - if _world_map != null and _world_map.has_method("_select_unit"): - _world_map._select_unit(unit) - if _world_map != null and _world_map.has_method("_on_found_city_pressed"): - _world_map._on_found_city_pressed() - print(" Founded city at %s (score %.1f)" % [unit.position, current_score]) - return - var waiting: bool = current_score < MIN_FOUND_SCORE and _turn_count < MAX_WAIT_TURNS - var should_wait: bool = (not current_valid) or waiting - if best_pos != unit.position and should_wait: - _move_toward(unit, best_pos, game_map) - return - if current_valid: - if _world_map != null and _world_map.has_method("_select_unit"): - _world_map._select_unit(unit) - if _world_map != null and _world_map.has_method("_on_found_city_pressed"): - _world_map._on_found_city_pressed() - print(" Founded city at %s (good-enough, score %.1f)" % [unit.position, current_score]) - else: - _explore(unit, player, game_map) - - -func _command_worker(unit: Variant, player: RefCounted, game_map: RefCounted) -> void: - ## Build an improvement on the current tile if possible; - ## else walk toward the nearest owned unimproved tile; else toward a city. - if _improvement_manager == null: - return - var buildable: Array[Dictionary] = _improvement_manager.get_buildable_improvements( - unit, game_map, player - ) - if not buildable.is_empty(): - var tile: Resource = game_map.get_tile(unit.position) - var preferred: String = "farm" - if tile != null and tile.biome_id in ["hills", "mountains"]: - preferred = "mine" - elif tile != null and tile.biome_id in ["forest", "boreal_forest", "jungle", "tundra"]: - preferred = "hunting_grounds" - var pick: String = str(buildable[0].get("id", "")) - for entry: Dictionary in buildable: - if entry.get("id", "") == preferred: - pick = preferred - break - if not pick.is_empty(): - _improvement_manager.start_improvement(unit, pick, player) - return - # Seek the nearest buildable tile within 4 hexes: owned or unclaimed, non-water, - # unimproved, no pending build. Unclaimed tiles work — they convert on build. - var best_target: Vector2i = Vector2i(-1, -1) - var best_dist: int = 9999 - for dq: int in range(-4, 5): - for dr: int in range(-4, 5): - var tpos: Vector2i = unit.position + Vector2i(dq, dr) - var d: int = HexUtilsScript.hex_distance(unit.position, tpos) - if d == 0 or d > 4 or d >= best_dist: - continue - var t: Resource = game_map.get_tile(tpos) - if t == null or t.is_water() or str(t.improvement) != "": - continue - if t.owner != -1 and t.owner != player.index: - continue - if _improvement_manager.get_pending_at(tpos, player).size() > 0: - continue - best_dist = d - best_target = tpos - if best_target != Vector2i(-1, -1): - _worker_step_toward(unit, best_target, game_map) - return - if not player.cities.is_empty(): - _worker_step_toward(unit, player.cities[0].position, game_map) - else: - _explore(unit, player, game_map) - - -func _worker_step_toward(unit: Variant, target: Vector2i, game_map: RefCounted) -> void: - # Workers must step ONTO the target tile to build on it — do not use the - # attack-adjacent shortcut in _move_toward which skips movement entirely. - if unit.position == target: - return - var reachable: Dictionary = PathfinderScript.movement_range( - game_map, unit.position, unit.movement_remaining, "land" - ) - if reachable.is_empty(): - return - var best_pos: Vector2i = unit.position - var best_dist: int = HexUtilsScript.hex_distance(unit.position, target) - for pos: Vector2i in reachable: - if pos == unit.position: - continue - var dist: int = HexUtilsScript.hex_distance(pos, target) - if dist < best_dist: - best_dist = dist - best_pos = pos - if best_pos != unit.position: - _do_move(unit, best_pos, game_map) - - func _nearest_city_to_target(player: RefCounted) -> RefCounted: ## Return the player's city closest to the locked attack target. ## Falls back to city[0] if no target is locked.