Extract pure game engine + Synergy Capability Resolution Layer#29
Merged
Conversation
Engine/View Split — Creates a pure, stateless game engine layer that takes (GameState, Action) → (GameState, Events). GameSession becomes a thin wrapper that delegates to the engine and processes events into UI feedback. New files: - web/src/game/engine/types.ts — EngineResult, EngineEvent, EngineAction, GameEngine interface, CombatPreviewResult, AiActivationResult - web/src/game/engine/engine.ts — All 24 action handlers as pure functions (move_unit, end_turn, set_city_production, queue_move, terraform, prepare_ability, board_transport, disembark_unit, cancel_production, start/cancel research, sacrifice, bastion, maelstrom, oasis, submerge, destroy_fort, build_city, summon_unit, etc.) - web/src/game/engine/index.ts — barrel exports Changes to GameSession.ts (1520 → 1127 lines, -26%): - dispatch() delegates 22 of 24 actions to engine.applyAction() - attack_unit stays session-level (two-phase: preview → animate → apply) - undo stays session-level (snapshot management) - processEngineEvents() converts engine events into session feedback (audio, animations, UI state) - All private action handlers removed (now in engine) - AI loop orchestration stays in GameSession (setTimeout yielding, combat queue, animation coordination) Verified: - All 1308 game tests pass (0 regressions) - Web build succeeds (tsc + vite) - No circular dependencies - No type errors across engine + session files
- Add canSubmerge guard to applySubmerge to prevent false events on failure - Remove 6 dead EngineEvent kinds never emitted by engine (combat_resolved, domain_learned, domains_absorbed, hit_and_run_retreat, enemy_synergy_contact, log) - Emit research_completed for all newly completed nodes, not just the last - Add position to unit_sacrificed event (populated before unit deletion) - Add unitId to bastion_built, maelstrom_declared, oasis_declared events - Add unitId + position to unit_submerged event - Update processEngineEvents to use new fields instead of hardcoded empty/zero values - Fix duplicate terraform_failed union member
- New src/systems/synergyCapabilities.ts with SynergyCapabilityName union (8 capability names), SynergyCapabilityGrant interface, and ResolvedSynergyCapabilities class with terrain-gated queries. - Extended PairSynergyConfig, EmergentRuleConfig, and ActiveSynergy with optional capabilityGrants field in synergyTypes.ts. - Propagated capabilityGrants at all 4 ActiveSynergy construction sites in synergyEngine.ts. - All existing tests pass (88/88).
- New src/systems/synergyCapabilityResolver.ts with resolveSynergyCapabilities() that collects capability grants from pair synergies and triple/emergent stack. - Evaluates grant conditions against BASE combat context only (non-recursive). - Grants do not trigger other grants. - Uses evaluateCondition() from primitiveEvaluator.ts for condition evaluation. - Returns ResolvedSynergyCapabilities with terrain-gated capability queries. - All existing tests pass (73/73).
- applyCombatSynergies() now resolves capability grants against base context before building an enriched CombatContext with OR'd combat-facet booleans. - Enriched context used only for primitive resolution; ambush bonus uses base. - resolveRetreatSynergyEffects() in resolveAftermath.ts also wired with capability enrichment (fixes bypass identified in review). - countsAsWaterForSynergy documented as needing dedicated integration (terrain-based, not boolean-OR-able). - All 1308 tests pass (1 pre-existing optuna failure unrelated).
- Added unitHasSynergyCapability() for quick boolean capability checks from non-combat systems (fog, movement, ZoC). - Added getTerrainSynergyCapabilities() for terrain-gated capability queries. - Shared hasAnyCapabilityGrants() guard for early-return optimization. - Shared buildMinimalContext() to reduce CombatContext construction duplication. - Helpers are stubs ready for future integration when content needs them. - All tests pass (73/73).
- Added capabilityGrants to charge+river_stealth pair synergy: - countsAsChargeForSynergy when isStealthAttack - countsAsStealthAttackForSynergy when isCharge on river/swamp terrain - Existing damage/cooldown-waiver effects unchanged, now fire when either base facts OR capability grants satisfy the isCharge AND isStealthAttack condition. - Granted charge-for-synergy does NOT trigger global charge mechanics (enriched context only used for primitive resolution). - All tests pass (156/156 across synergy test files, 48/48 wiring tests).
- 42 new tests in tests/synergyCapability.test.ts covering: - ResolvedSynergyCapabilities unit tests (7 tests) - resolveSynergyCapabilities resolver tests (8 tests) - Enriched combat context integration tests (5 tests) - Primitive evaluator isolation test (1 test) - Stealth Charge behavior tests (7 tests) - Ambush bonus isolation tests (2 tests) - Regression tests for existing synergies (5 tests) - System capability query helper tests (7 tests) - All 198 synergy tests pass (42 new + 156 existing). - Full suite: 1350/1351 pass (1 pre-existing optuna failure).
Auto-formatting (quotes, indentation) applied by biome during the capability resolution layer implementation phases.
Migrate synergy mechanisms from setFlag/statMod primitives to the declarative capability grant system (synergyCapabilities.ts): - Content layer (synergies/index.ts): countsAsCity, transportedTroopsStealth, slaveHordeIgnoresZoc, stealthAuraShareRadius, caravanRelayVisionRange, bombardmentRange, amphibiousMovementOnTerrain, positionSwapVerb, caravanPassengerActive, ignoreZoc now use capabilityGrants - Consumer layer: zocSystem, fogSystem, movementSystem, activateUnit, combat-action/helpers updated to use unitHasCapabilityFromState() instead of resolveEffectiveSynergies+manual flag scanning - Emergent rules: juggernaut and iron_turtle use ignoreZoc capability grant; many_faced phantom's dead emergentIgnoreZoc flag removed - synergyCapabilities.ts: add 8 new capability names, terrain merge on add(), getField/getFields helpers for parameterized grants - synergyCapabilityResolver.ts: new unitHasCapabilityFromState() convenience helper for GameState+Unit consumers - All 188 synergy/activation/awareness tests pass - Reformatted all touched files from 2-space to tab indentation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
10 fields migrated from effects primitives to capabilityGrants are now correctly classified as orphan (no longer written by content, but still have dispatcher/consumer code): bombardmentRange, amphibiousMovementBonus, stealthAuraShareRadius, caravanRelayVisionRange, caravanPassengerActive, slaveHordeIgnoresZoc, countsAsCity, transportedTroopsStealth, positionSwap, emergentIgnoreZoc. Plus pre-existing orphan multiplierStackValue = 11 total.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two major features on this branch:
1. Pure Game Engine Extraction (Phase 0–2)
GameSessioninto a standaloneGameEngineclass (web/src/game/engine/)docs/multiplayer-engine-split.md2. Synergy Capability Resolution Layer (Phase 1–6)
synergyCapabilities.ts,synergyCapabilityResolver.ts) replacing ad-hocsetFlag/statModprimitive scanningcapabilityGrants:countsAsCity,transportedTroopsStealth,slaveHordeIgnoresZoc,stealthAuraShareRadius,caravanRelayVisionRange,bombardmentRange,amphibiousMovementOnTerrain,positionSwapVerb,caravanPassengerActive,ignoreZocunitHasCapabilityFromState()instead of manualresolveEffectiveSynergies+ flag scanningignoreZoccapability; deademergentIgnoreZocremoved from phantom modesynergyCapability.test.ts(736 lines, full resolver coverage)docs/Project Plan Synergy Capability.md,docs/Project Plan Synergy Migration.mdCommits (10)
fd994df— feat: extract pure game engine from GameSession (Phase 0-2)1e899ce— fix: address review findings from engine extraction3621f80— Phase 1: Add Synergy Capability Resolution Layer types9c84315— Phase 2: Add Synergy Capability Resolver68b539a— Phase 3: Combat Context Enrichment via Capability Resolution94cd706— Phase 4: Add system capability query helpersf443624— Phase 5: Migrate Stealth Charge to capability grants6c244a3— Phase 6: Add comprehensive tests for Synergy Capability Resolution Layerc80bc0e— Style: biome auto-formatting from capability layer implementation6af166c— Migrate synergy flags to capability grants; reformat to tabsTest Status
synergyCapability.test.tsfull resolver coveragenpm run buildclean