Complete combat and AI overhaul for XCOM 2: War of the Chosen.
Transforms XCOM 2 into a tactical chess match with predictable, fully-displayed hit calculations, cross-mission adaptive AI, and a per-turn AI cost that is bounded by a fixed encoding rather than scaling with the number of units on the map.
Honesty note (MSS discipline). This repository ships no runtime benchmark, and XCOM 2 mods can only be compiled inside the proprietary WotC SDK on Windows — so no build, test, or turn-time number is reproducible from this repo. Every claim below is therefore either a structural/complexity guarantee derivable from the source, a Definition, an empirically tuned Assumption, or an explicit Unknown. No measured runtime speedup is claimed, because none has been measured.
ChronoCOM implements the WASP (Workload-Aware Sufficient Placement) framework for real-time tactical AI decision-making.
Dataset & Workload
- D (Dataset): Set of all possible tactical game states — unit positions, health, cover, abilities, influence fields
- W (Workload):
- Q1: "Given a unit's tactical situation, choose the best ability and target"
- Q2: "Given a world position, evaluate its tactical value (cover, threat, influence)"
- Q3: "Given a turn boundary, update influence fields and invalidate stale caches"
Encoding Tuple (k, E, I, T, {F_q}):
| Symbol | ChronoCOM Mapping |
|---|---|
| k = 4 | Spatial state, Unit state, Influence fields, Temporal state |
| E(r) | (32×32 grids + tile class, TacticalSituation hash + hit cache, 4 influence grids [1024 floats], turn/frame/AdaptiveMemory) |
| I(c) | InfluenceField grids, TileScoringCache, VisibilityCache, AIDecisionCache, TacticalPrecalculation |
| T(q) | BT_InitGenericAbilities (Q1), SampleInfluence + GetTacticalGradient (Q2), UpdateInfluenceField (Q3) |
| {F_q} | Confidence threshold >=70, grid bounds check, cache age filter (MAX_CACHE_AGE=5), dirty flags |
WASP Guarantees:
- Sufficiency: Ans(q) ⊆ Cand(q) — 4 dimensions capture all tactical AI decision inputs; no game-state aspect is unrepresented
- Exactness: F_q(Cand(q)) = Ans(q) — confidence thresholds + cache invalidation ensure current-state decisions
- Bounded Work: Work(q) <= gamma * |Ans(q)| + beta — spatial queries are O(1) via the fixed 32×32 grid (array index, no loop over units). Cache lookups (
GetCachedDecision,GetCachedVisibility) compute a key but resolve it with a bounded linear scan over a fixed-capacity array (MAX_CACHE_SIZE= 512 decisions / 500 visibility entries), so they are O(fixed bound) = O(1) by construction — not a hash-table lookup. Per-turn AI work is bounded by this fixed encoding and does not grow with unit count. - Minimality: Each dimension carries unique tactical information; removing any loses capability
ChronoCOM AI must answer three query classes:
- Q1 (Ability Selection): Given a unit's TacticalSituation, select the optimal ability and target
- Q2 (Position Evaluation): Given world coordinates, compute tactical gradient (threat, danger, control, interest)
- Q3 (State Maintenance): Given a turn/frame boundary, propagate dirty flags and rebuild stale caches
Four dimensions encode the tactical game state:
- Spatial (positions, cover, elevation): 32×32 influence grids (GRID_CELL_SIZE=384 world units), tile classifications from TacticalPrecalculation
- Unit (HP, abilities, action points): TacticalSituation struct hashed to int for cache keying
- Influence (threat, danger, control, interest): 4 InfluenceField grids with decay rates, each 1024 floats
- Temporal (turn number, cache validity, adaptive memory): AdaptiveMemory pattern buckets, frame-based cache ages
Pre-computed and cached data structures:
InfluenceField.GridValues[1024]: Flattened 32×32 grid, true O(1) spatial read viaSampleInfluence(compute index, return cell)VisibilityCache: (ShooterID, TargetID) → LoS result, frame-validated; resolved by bounded linear scan over a fixed-capacity array (MAX_CACHE_SIZE= 500), not a hash tableAIDecisionCache: TacticalSituation hash compared against a fixed-capacity array (MAX_CACHE_SIZE= 512) by bounded linear scan; the hash is the match key, not a table indexTacticalPrecalculation: Mission-start tile classification and zone distances (Phase 3 front-loaded at load screen)TileScoringCache: Pre-evaluated tile tactical values
- T(q):
BT_InitGenericAbilitieschecks the decision cache first (bounded scan over <= 512 entries, O(1) by fixed bound), falls back to full BT evaluation only on cache miss.SampleInfluencereads the grid at the quantized position (true O(1) array index).GetTacticalGradientsamples 4 neighbors for a directional gradient. - {F_q}:
CONFIDENCE_THRESHOLD=70filters low-quality cached decisions. Grid bounds check returns 0.0 for out-of-bounds.MAX_CACHE_AGE=5filters stale LoS data. Dirty flags (bNeedsUpdate) filter stale unit data.
The AI's tactical decisions are fully determined by:
- Spatial state (where everything is and what's visible)
- Unit state (capabilities and condition)
- Influence fields (accumulated tactical intelligence)
- Temporal state (adaptation and cache validity)
This is the MSS for tactical AI: the smallest state that can answer any query about what the AI should do.
| # | Claim | Category | Enforcement |
|---|---|---|---|
| D1 | Spatial state = positions + cover + elevation | Definition | — |
| D2 | Unit state = HP + abilities + AP + ammo | Definition | — |
| D3 | Influence field = 32×32 float grid with decay | Definition | — |
| D4 | Temporal state = turn counter + cache frame + AdaptiveMemory | Definition | — |
| G1 | Spatial influence queries are O(1) via grid quantization | Guarantee | Fixed 32×32 grid; SampleInfluence computes an index and returns one cell, no loop |
| G2 | Decision/visibility cache lookup is O(1) by fixed bound | Guarantee | GetCachedDecision/GetCachedVisibility are bounded linear scans over fixed-capacity arrays (512 / 500); O(fixed) = O(1). NOT a hash table — the hash is the match key |
| G3 | Influence field update is O(1024) = O(1) constant | Guarantee | UpdateInfluenceField sweeps the fixed 32×32 grid; iteration count is constant |
| G5 | Cache invalidation on unit movement | Guarantee | MarkUnitDirty called on state changes |
| G6 | Overwatch zone test is O(1) | Guarantee | IsInZone = sphere distance check + single dot product against precomputed cone; no loop |
| G7 | Per-turn AI work is bounded by a fixed encoding, independent of unit count | Guarantee | All inputs are read from the fixed 32×32 grid + fixed-capacity caches; no query iterates over all units |
| A1 | 4 dimensions sufficient for emergent AI behavior | Assumption | Observed in play; no formal proof in-repo |
| A2 | Confidence threshold of 70 filters bad decisions | Assumption | Tuned empirically |
| A3 | Grid cell size of 384 (4 tiles) is optimal | Assumption | Chosen for balance |
| A4 | Perceptual O(1) via time-reordered computation | Assumption | Camera-based priority; depends on viewing angle. The total work is unchanged; only its scheduling is reordered. No turn-time measurement exists |
| A5 | Adaptive memory detects player patterns | Assumption | Bucket-based heuristic; accuracy not formally measured |
| A6 | Cyclomatic complexity is low (most functions CC <= 4) | Assumption | A coding convention recorded in per-function @cyclomatic annotations, NOT an enforced gate. The repo's own annotations show a handful of functions at CC 5 and one at CC 6 (X2AIBehaviorDirector_Optimized.FindBestMovePositionReal). CI runs commitlint only; there is no complexity linter |
| U1 | Actual gamma, beta bounds for Q1 | Unknown | Needs profiling under real gameplay; not done |
| U2 | Optimal influence decay rates | Unknown | Current values tuned by feel |
| U3 | Real-world turn-time / FPS impact | Unknown | OnAITurnEnd logs TotalTurnTime to Launch.log in-game, but no measurement has been recorded; no benchmark ships in this repo |
- XCOM 2: War of the Chosen
- X2WOTCCommunityHighlander v1.30.4+
- Subscribe via Steam Workshop or extract to
XCOM 2\XComGame\Mods\ChronoCOM\ - Enable in XCOM 2 Mod Launcher
- Start new campaign or continue existing saves
- Compatible with most AI mods (uses ModClassOverride)
- Works with existing saves
- Incompatible with other accuracy overhaul mods
ChronoCOM uses a hybrid ModClassOverride + template replacement approach for broad mod compatibility.
Technical Approach:
- ModClassOverride for AI behavior (engine-level compatibility)
- Template replacement for combat systems (selective modification)
- 4 influence fields on a fixed 32×32 (1024-cell) grid for spatial reasoning
- Fixed-capacity caches so per-query work is bounded by construction (see PERFORMANCE.md)
See ARCHITECTURE.md and PERFORMANCE.md for the full WASP role mapping and the complexity classification.
Report issues via GitHub Issues. Check logs in Launch.log for ChronoCOM: entries.
Apache-2.0 © 2026 Jacob Coleman — See LICENSE for details.