Our auto-play now mirrors the AI's build order:
1. Walls (defense first)
2. Settler to expand to 3 cities
3. Forge (+2 production)
4. Warriors for military
Settlers move away from existing cities (5+ hex distance) before founding.
Removed debug prints from production tracking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
City center now produces 2 food, 2 production, 1 gold, 1 science (was 2/1/0/0).
This doubles the pace of the game — warriors in ~20 turns instead of ~40,
walls in ~35 instead of ~70.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AI production now follows a priority chain:
1. Build walls (defense first — every city gets walls)
2. Build settler if < 3 cities (expansion)
3. Build warriors to maintain 2 per city (military)
4. Build forge (+2 production for faster future builds)
5. Build castle (upgrades walls, enables bombard)
6. Build any remaining available building
7. Fallback: more military
This makes the AI significantly more challenging:
- Cities have walls (0.75x melee penalty, +50 HP)
- AI expands to 2-3 cities (domination requires capturing ALL)
- Forge accelerates subsequent production
- Castle adds city bombardment capability
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cities can bombard one enemy unit within range (2 hexes) per turn
- Human player: click own city → click enemy within range to bombard
- AI player: automatically bombards nearest enemy in range
- Damage formula: 20 * (city_str / def_str), clamped [5, 40]
- city_str = population * 3 + castle bonus (12)
- has_bombarded flag resets each turn via city.refresh_turn()
- New AI action type: "city_bombard" in simple_heuristic_ai + ai_turn_bridge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- City max_hp scales with population: base 200 + 10 per pop (pop 1 = 210)
- max_hp recalculated on growth/starvation, building bonuses preserved
- Garrison unit's attack adds to city bombard combat strength
- Renamed DEFAULT_CITY_HP → BASE_CITY_HP, added HP_PER_POP constant
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- City bombard: melee attackers take 5-30 damage based on city population
and castle bombard bonus (city_str = pop*3 + castle bonus)
- Building HP bonuses: when walls/castle complete, increase city max_hp
and heal by hp_bonus value from building data
- Castle data: added city_bombard_strength: 12, city_bombard_range: 2
Combined with prior commit's city healing (20 HP/turn) and tiered wall
penalties (walls=0.75x, castle=0.60x), cities now require sustained
multi-turn sieges instead of 1-2 turn captures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cities heal 20 HP per turn (heal_per_turn in Rust, exposed via GDExtension,
called from turn_processor after unit healing)
- Wall penalty scales by tier: none=1.0, walls=0.75, castle=0.60
- Added city_bombard_damage() function: 15 * (city_str/attacker_str), [5,30]
- Fixed "fortress"→"castle" references in combat_resolver.gd and combat_preview.gd
- Fixed defender.city_hp→defender.hp already done in prior commit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Production is production, hammers are weapons. Single source of truth.
Rust: hammer_cost→production_cost, hammers_invested→production_invested,
hammers params→production (with serde aliases for JSON backward compat)
GDScript: hammers vars→prod, comments updated
Guide: "Hammers" label→"Production"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The City class has no 'city_hp' property — it uses 'hp' (backed by Rust
GDExtension). The combat resolver was reading/writing 'city_hp' which
created a dynamic property that never affected real city health. This
caused:
- City HP always passed as 0 to Rust resolver
- City damage never applied to actual city HP
- Cities could never be captured (HP never decreased)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Neighbor-based enemy detection failed due to normalize_position changing
coordinates on wrapped maps. Now simply checks hex_distance <= 1 for all
enemy units and cities.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When already adjacent to the target, skip movement and attack directly.
Previously the deadlock-breaker would move the warrior AWAY from the
adjacent enemy to a random hex.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Default settings create 4 players even on a Duel (2) map. With 3 AI vs 1
human, we're permanently outnumbered. Force 2 players for a 1v1 match.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set fortified_turns=2 immediately instead of waiting 2 turns to build up.
With max fortification (+4 defense), a warrior should take only ~3 damage
per enemy attack instead of ~34, allowing accumulation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Build phase: attack adjacent enemies before fortifying (counter-attack)
- Only march when 5+ warriors accumulated (no more premature attacks)
- Warriors at city fortify for defense bonus AND fight back
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Warriors were marching toward enemies across water with no land path.
Now uses PathfinderScript.find_path with budget=999 to verify land
reachability before selecting attack targets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Lock onto one enemy target instead of switching every turn
- When stuck (no closer hex), try any valid move to break deadlock
- Verify locked target still exists before continuing march
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Warriors now check for adjacent enemy units/cities after moving and
initiate combat via CombatResolver. Also increased max turns to 500
and added unit position logging during attack phase.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
With 1 hammer/turn and 40 cost warriors, accumulating 4 warriors is impossible
since each gets killed within ~20 turns. Attack with any available warrior
after turn 30 to make progress toward domination.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The AI's set_production action set queue items without a cost field, causing
all production to complete instantly (cost defaults to 0, progress >= 0 is
always true). This made AI warriors spawn every turn while the human player
waited 40 turns per warrior.
Now looks up cost from DataLoader for both units and buildings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Warriors now fortify at the city position (+2 def/turn, max +4) instead of
sitting idle. This should help them survive enemy attacks while building up
to the 4-unit threshold for the attack march.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Early game: found city, build warriors, keep military at home for defense
- Only scout explores (other units garrison)
- Once 4+ warriors accumulated, march all toward nearest enemy city
- Should survive enemy attacks with numerical advantage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The -s SceneTree mode caused scripts to compile before autoloads were
registered, breaking any file referencing autoloads by name. Now auto_play
runs as a regular autoload that activates only when AUTO_PLAY=true env var
is set. All GameState, DataLoader, EventBus, EnvConfig references work
because autoloads are fully initialized before _ready() runs.
Also includes tile.gd lazy accessor fixes for DataLoader and EnvConfig.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tile.gd referenced DataLoader by autoload name, which fails during initial
script compilation before autoloads are registered. This cascaded to break
game_map.gd, pathfinder.gd, and prevented tile creation during map generation.
Replace with static _data_loader() that resolves via Engine.get_main_loop().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The game_map.tiles dictionary is empty in _play_turn on the first frame
but populated by frame 10 when the world_map scene has fully initialized.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
get_terrain_flags() calls DataLoader which fails in -s script context
due to class_name resolution order.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The map generator's start position selection returns empty on some maps.
When detected, spread players across available land tiles and relocate
their units to the assigned positions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>