Skip to content

Chapter 50 — Combat States — The Four-State Doctrine

Chapter 50: Combat States — The Four-State Doctrine

Section titled “Chapter 50: Combat States — The Four-State Doctrine”

Exactly four visible combat states. No status zoo. Each state has a single tactical role, a clear counter, and predictable duration. If a pack needs genre-specific effects, use resources and abilities — not new combat states.

Role: Short-horizon defense. Absorb the next hit.

PropertyValue
Duration2 ticks
Applied byguard action, brace action
Cleared byabsorbing a hit, attacking (self-clear), disengaging (self-clear), outflanked by reposition, tick expiration
Hit chanceNo direct modifier
Damage50-75% reduction (scales with resolve stat) on first hit, then clears
Counter30-48% chance to apply OFF_BALANCE to attacker (scales with resolve)
AI responseAttack -10 (avoid), Reposition +20 (outflank opportunity)

Key behavior: Guard absorbs exactly one hit. The damage reduction scales with the resolve stat: base 50%, increasing by 3% per point of resolve above 3, capped at 75%. After absorbing, GUARDED clears automatically. Attacking or disengaging also clears own GUARDED — you cannot defend and act offensively simultaneously.

Role: Tempo punishment. You overcommitted and lost your footing.

PropertyValue
Duration1 tick
Applied bycounter (attacking into a guarded target, 30-48% chance)
Cleared byguard (stabilization), brace (stabilization), successful untargeted reposition, tick expiration
Hit chance-15 as attacker, +10 as target
Damage+1 damage taken
Reposition-20 penalty to success chance
AI responseBrace +25 (strong recovery signal), Reposition -15 (risky while unstable), Attack +8 vs off-balance enemy

Key behavior: OFF_BALANCE is the rarest state — it only comes from the counter mechanic when you attack into a guarded target. Braced entities resist OFF_BALANCE application with 70% chance. The primary recovery is brace (AI strongly prefers it at +25), though guard also clears it.

Role: One-time vulnerability window. You’re open and everyone knows it.

PropertyValue
Duration1 tick
Applied byfailed disengage, failed reposition, being outflanked by reposition
Cleared bysuccessful untargeted reposition, damage consumed (auto-clears after being hit)
Hit chance+20 as target
Damage+2 damage taken, then auto-clears
AI responseAttack +15 (high-value target), Guard +20 (self-recovery), Brace +15 (self-stabilize), Finish +10

Key behavior: EXPOSED is consumed by damage — the first hit against an exposed target gets the full bonus (+20 hit, +2 damage), then EXPOSED clears. This makes it a one-shot vulnerability, not a lingering debuff. Multiple attackers competing for the same exposed target only benefit one attacker.

Role: Escape commitment. You’re running and vulnerable to pursuit.

PropertyValue
Duration2 ticks
Applied bysuccessful disengage (entity moves to neighbor zone)
Cleared bytick expiration only
Hit chance+10 as target
DamageNo direct modifier
Cognitive lockEntity can ONLY disengage while fleeing (all other intents suppressed)
AI responseAttack +5 (pursue), Finish +20 (priority elimination target)

Key behavior: FLEEING is the only state with cognitive suppression — a fleeing entity is locked into disengaging until the state expires or they successfully exit the zone. This makes fleeing a commitment, not a free escape. Enemies see fleeing targets as high-priority finisher candidates (+20 finish scoring).

States can overlap. Here’s what happens:

CombinationResult
GUARDED + EXPOSEDBoth active. Guard absorbs the hit (with reduction), but EXPOSED bonus still applies. After absorb, both clear.
GUARDED + OFF_BALANCEBoth active. Entity is guarded but unstable. Guard still absorbs, but the entity’s own attacks suffer -15 hit chance.
EXPOSED + OFF_BALANCEBoth active. Entity is extremely vulnerable: +20 hit, +10 hit (from off-balance as target), +2 damage, +1 damage. Brace recovers off-balance; untargeted reposition recovers exposed.
FLEEING + anyFLEEING dominates: cognitive lock forces disengage regardless of other states. Other states still apply their passive modifiers but the entity cannot act on recovery options.

RoundFlags in combat-tactics are per-entity per-tick tracking flags. They are NOT visible combat states:

FlagPurposeLifetime
bracedGrants displacement resistance, enables OFF_BALANCE resistance (70%)Cleared at tick start
guardingTracked for counter effect timingCleared at tick start
attemptedDisengagePrevents double-disengage in one tickCleared at tick start
attemptedRepositionPrevents double-reposition in one tickCleared at tick start

These flags exist for cross-referencing within a single tick. They don’t appear in the entity’s statuses array and are invisible to the narrator and AI intent system.

  1. Do not add combat states. Four is the cap. If you need pack-specific effects, use resources (crowd-favor, ki, bloodlust) and abilities (focused strike, blood frenzy).

  2. Resources can resist states. Use resistState and resistChance in ResourceSpendModifier to let genre resources interact with the state system without adding new states.

  3. Resources can modify state effects. Use TacticalHooks.preAction and defenseModifier to change how states behave for specific packs without adding states.

  4. Test your AI. Run selectNpcCombatAction() with your pack biases and verify enemies respond sensibly to all 4 states.

The combat-state-narration module enriches status events with description text routed to the narrator channel:

EventGUARDEDOFF_BALANCEEXPOSEDFLEEING
Applied”{name} raises their guard""{name} is thrown off balance""{name} is left exposed""{name} breaks away and flees”
Removed”{name} guard drops""{name} recovers their footing""{name} is no longer exposed""{name} stops fleeing”
Expired”{name} guard stance fades""{name} steadies themselves""{name} regains composure""{name} slows their retreat”

The module is optional. Combat works without it, but narrators receive no state change text without it registered.

import { createCombatStateNarration } from '@ai-rpg-engine/modules';
const engine = createEngine({
modules: [
statusCore,
createCombatCore(formulas),
createCombatTactics(),
createCombatStateNarration(), // optional, adds narrator text
],
});