← Back to the Lab
Combat Variants
— Grounding + Constraint Charter
Version: 1.0 Date: 2026-07-31 Owner: Design / Combat Status:
Binding on every prototype in
prototypes/combat-variants/.
[!important] This document designs nothing. It records (a) what the
combat core factually is today, (b) why elements currently fail, and (c)
the hard constraints every prototype must satisfy. Any prototype that
violates a numbered constraint in §3 is rejected without evaluation.
Sources read
docs/base-game-rules-canon.md (marked STALE for combat;
still the assembled rule map)
docs/core/combat-empty-slot-energy-proposal.md (v0.2,
Draft, sim-validated — the live direction, "ADR-0006")
docs/core/combat-rework-spec.md (v0.2, marked STALE —
the prior either-proc core)
wiki/concepts/dice.md,
wiki/concepts/slot.md,
wiki/concepts/combat-math.md
wiki/concepts/element.md (read for element
grounding)
[!warning] The docs disagree with each other by design.
combat-rework-spec.md and the combat sections of
base-game-rules-canon.md both carry STALE banners pointing
at the Empty-Slot proposal. The wiki concept pages
(dice.md, slot.md) are older still —
dice.md states a 1/3/5 escalating reroll curve and a free
first roll, which no live doc supports. Where they conflict, §1 below
states the live rule and names the stale variant.
1. The current core,
precisely
1.1 Dice — exactly
two, both d6 with blanks
| Number die |
1, 2, 3, 4, blank, blank |
Faces 1–4 each target the slot of the same index. Blanks target
nothing. |
| Element die |
A, A, B, B, blank, blank |
A and B are the run's chosen element pair. Blanks match
nothing. |
- Both are fixed d6 physical dice; the "4 values"
live on 4 of the 6 faces.
- At run start the player picks an element pair from Agni / Jal / Vayu
/ Prithvi (6 pairs), then picks a dice weight that
fills 2 of the 4 blanks: Balanced (1 number + 1 element), Element Focus
(0 + 2), Number Focus (2 + 0).
- Blessing fill pool is Agni, Jal, Prithvi, Vayu only
— Akasha is excluded (
DiceFactory.BlessingElementPool).
combat-rework-spec.md §Random fill rules still claims
20%-each across all five including Akasha; that is the
stale variant.
- Blanks are load-bearing, not dead faces. Per
wiki/concepts/dice.md, a blank rolled in the
reward phase unlocks an Akasha card option in the reward pool.
A player with zero blanks cannot be offered Akasha. Blanks are the
price/key of Akasha access.
- Rigging (dice tiles, relics, card effects, slot Apsaras) converts
blanks into duplicate slot numbers or into elements (Akasha included).
Full rig ≈ 1.5× base proc rates.
- Live roll action: there is exactly
one player roll and it rolls both dice
together (
DiceRoll.RollBoth). No single-die roll,
no per-die lock. The Gamble/Tactical "roll one die" table in
base-game-rules-canon.md and the whole Sequencing Modes
section of combat-rework-spec.md are explicitly
stale.
1.2 Slots — four,
number-linked, one card each
- Exactly 4 slots, numbered 1–4. The number die face
is the slot index.
- One card per slot. Occupied slots cannot be overwritten. The player
cannot manually pull a card off a slot without an explicit effect.
- Two independent axes (
wiki/concepts/slot.md):
mount (Empty / Occupied / Cleared) and
enhancement (Plain / Enhanced by an Apsara blessing).
An Empty slot can be Enhanced.
- Because a roll addresses exactly one slot index, at most one
card can resolve per roll.
slot.md also asserts "combat ends a turn when 3 of 4
slots have been cleared" — this appears in no combat spec and should be
treated as an unverified wiki artefact.
1.3 Proc
/ match rule — both-match, deferred, non-latching
- Live rule: both-match. A card locks in only when
the element die matches the card's element AND the
number die matches the slot's effective number. One proc; no partial or
single-lane effect (
[DECISION LOCKED]). Per-card
probability under the idealised 4×4 model is 1/16;
under the real d6 faces it is 2/6 × 1/6 = 5.5% for a
base-pair element in an unrigged slot.
- This supersedes ADR-0005's either-proc model
(
wiki/decisions/0005-fold-procmodes-to-either.md).
- Authoring label ≠ runtime rule. Designers author
only
EitherMatch and Placed. At runtime every
mode except NumberProc and Placed routes
through the both-match branch. Do not migrate EitherMatch
assets to BothMatch — functional no-op.
NumberProc (Akasha exception): locks on number alone,
element die ignored. Exists in the enum and resolvers, set on no
card asset today, and the inspector cannot author it.
Placed: resolves immediately on placement, no dice,
100%.
- Matching locks; it does not fire.
BaseSlot.LockProcIfMatch sets the lock; the effect is
deferred to turn end via ResolveLockedProcAtTurnEnd from
DiceRoll.EndTurnSequence.
- Locks do not accumulate across rerolls (canonical
2026-07-20). A lock is re-derived from the current dice on every roll; a
reroll that breaks a match releases the lock. Only
cards matching the final dice state of the turn resolve.
Rerolling is a gamble on your current match, not a way to bank
procs.
- A card that never matches is not discarded — it
clogs, holding its slot.
1.4
Energy model — board-as-battery (empty-slot snapshot)
- Base energy is 0. At turn start, count empty slots;
that count is your energy for the whole turn. Clear
board = 4; full board = 0.
- Snapshot happens before placement, so placing cards
does not drain this turn's budget — filling the board is free now and
paid as a next-turn energy tax if the cards clog.
- Full-board clog reset: if the board is full and
nothing procced, all four cards exhaust at the start of next
turn and the player gets the full 4 energy.
- Sinks: first roll
1; each consecutive reroll
3; draw from discard 1 energy → 2 cards;
voluntary exhaust of a clogged slot 1 (Issue-A fix, "in for
v1" per plans/quizzical-weaving-balloon.md, sim says
optional polish not a blocker).
- Stale variant: the old model was
1 base energy per turn, card play cost 0, plus a Sustain
sink at incremental cost 1, 2, 3… returning previous-turn
consumed cards to hand. Sustain belongs to the pre-rework core.
1.5 Reroll cost
curve — 1 then 3, flat thereafter
- First dice roll of a turn: 1. Every consecutive
reroll that turn: 3 (
RollEnergyPool).
- Non-roll spends (card actions, clog clearing) draw the same pool but
do not escalate the reroll price.
- Consequence: from a clean 4-energy start the roll
budget is
1 + 3 = 4 → exactly 2 rolls.
Budget table (sim-confirmed): 0→0, 1–3→1, 4→2, 7→3.
- Stale variants:
combat-rework-spec.md
says flat 1 per roll, single die, no escalation.
wiki/concepts/dice.md says first roll free then
1, 3, 5…. Neither is live.
1.6 Element roster — five,
one gated
| Agni (fire) |
aggression, conversion |
Strength scaling, Ritual ramp, block-to-damage, burn |
| Jal (water) |
control |
Weak, Frail, debuff stacking |
| Vayu (wind) |
speed, manipulation |
Energy, hand cycling, Venom, Dosha removal |
| Prithvi (earth) |
defense |
Thorns, Block, Barricade / PlatedArmor / Dexterity |
| Akasha (ether) |
rare, conditional |
Home of every Dosha; gated |
- Every card belongs to exactly one element.
- Akasha is never on the base element die, excluded from the blessing
pool, and reachable only through deliberate rigging (targeted dice
tiles, specific relics) or a blank-face reward roll. It is absent from
starter decks.
- Starter decks: 12 cards, one per two-element pair, 6 pairs.
1.7
Card flow — sticky hand, no cycling, clog is the pressure
- Hand starts combat at 6 cards (Empty-Slot
proposal). The stale rework spec says draw 4 on turn 1.
- Draw 1 free per turn from the draw pile; optionally
spend energy to pull from the discard pile at 1 energy
→ 2 cards.
- Cards cost 0 to play into a slot.
- Hand is sticky — no end-of-turn discard.
- Resolved cards leave the board to discard (older
core called this the consumed pile, recoverable only via
Sustain / effects). Unresolved cards
clog — they stay on the board into the next turn,
occupying a slot and therefore taxing next turn's energy.
- Exhaust is reached by the full-board clog reset and
by voluntary exhaust. It is not an authored card keyword in the current
core; consumed-vs-exhaust is treated as migration debt, not a
design-facing zone distinction.
- The draw pile does not auto-refill; there is no
default discard-to-draw cycle in the legacy core. This is the combat
clock. (The Empty-Slot proposal's paid discard-draw is the one exception
and is energy-priced.)
- Dosha mercy rule: 3+ unplayable Dosha in hand at
turn start → auto-exhaust 1 random Dosha, gain 1 energy. Fires before
the player's first action.
1.8
Turn structure — strict phases, placement locks on first roll
- Turn start / energy snapshot — count empty slots →
that is this turn's energy.
- Draw — 1 free; optional paid draws from
discard.
- Place — place cards from hand into empty
slots.
- Roll — first roll costs 1. Once you start
rolling you can no longer place. Each consecutive reroll costs
3. Every roll re-derives all locks from the current dice.
- Resolve — at turn end, cards locked against the
final dice state fire their effect and leave to
discard. Everything else clogs.
- Clog reset — full board + zero procs → exhaust all
four at start of next turn, 4 energy.
Sim-measured this-turn proc odds from a clean 4-energy start (2
rolls): 1 card down 11.6–12.1%, 2 → 23.3%, 3 →
34.0–34.5%, 4 → 43.8%. Commit strictly dominates
turtling.
1.9
Damage pipeline (unchanged by any rework —
wiki/concepts/combat-math.md)
Outgoing:
clamp0 → +Strength → ×Weak (floor) → ×Vulnerable (ceil) → Boot chip floor → clamp0.
Incoming: Intangible caps to 1 → Block absorbs → remainder to Health.
Block gain: (base + Dexterity) × Frail, floored. Thorns
fires per hit, only on isAttack=true, and bypasses the
attacker's Block. Venom decays Intensity, not Duration,
and skips Block. Prototypes must not silently change this
pipeline — it is shared with relics, enemies, and every status
effect.
2. What is WEAK about
elements today
This is the central design problem. Today an element is a
colour-match key and nothing more at the level of the
core loop. Concretely:
- The element die contributes exactly one bit to resolution:
match / no match. Under the live both-match rule the element
lane is a pure AND-gate. It has no magnitude, no direction, no ordering,
no board effect. Swapping every Agni face for Jal changes which
cards fire, never what firing does.
- All five elements are mechanically interchangeable in the
core. There is no rule anywhere in the resolution path that
reads "if the element is Agni, then…". Elements are symmetric tokens;
the engine treats them as equal-probability colours.
- Element identity lives entirely in card text, one layer
below the core. Agni means Strength/Ritual, Prithvi means
Block/Thorns — but only because designers wrote those effects onto
cards tagged with that element. Retag the cards and nothing in the
system objects. The element is a label on the payload, not a behaviour
of the board.
- The four elements share one identical proc
probability.
2/6 element faces for both members of
the pair. There is no element-differentiated frequency, cost, or timing,
so there is no reason to prefer one element's mechanics over
another's — only its card list.
- Elements do not interact with each other. No
reaction, no combo, no counter, no suppression, no conversion. Agni next
to Jal does nothing. A four-element board is just four independent 1/16
lottery tickets. There is no elemental state on the board at
all.
- Elements do not touch slots, energy, dice, or turn
order — the four other core nouns. The element lane is
orthogonal to every economy in the game.
- Akasha's only distinctive mechanic is access, not
behaviour.
element.md says it outright:
"Mechanically it lives where the other four live… What makes Akasha
distinct is access." Its one true mechanical differentiator,
NumberProc (fire on number alone), is defined in
code, authored on zero cards, and unauthorable from the
inspector. The single element with a real verb has that verb
switched off.
- The strongest element-shaped signal in the system is a
balance bug, not a design. The sim's finding that Akasha
number-proc cards fire ~3.6× more often (43.8% vs 12.1%) is a
probability asymmetry — the game's biggest element
differentiator is "this colour procs more", which is still not a
verb.
- The player's element choice is therefore a deck-list choice,
not a play choice. Picking Agni+Jal at run start selects 12
cards and 4 die faces. Once combat begins, no turn-to-turn decision is
ever framed as "what does fire do here?" — only "does the die show my
colour?"
Charter position: an element that only functions as
a colour-match key is a violation (see §3.7). Every prototype must give
each element something that changes what happens on the board,
not whether something happens.
3. The
Constraint Charter (hard — violation = rejection)
Each constraint is stated, then expanded into a check a QA auditor
could verify by reading the prototype's code and data.
C1 — EXACTLY 2 DICE, ALWAYS
Base 2, max 2. The player never gains a third die and never loses
one. No die-splitting, no temporary extra dice, no phantom dice. Both
dice exist from turn 1 to the end of combat.
Testable checks
- C1.1 The dice collection is a fixed-length-2 structure (or two named
fields). No
Add, Remove, Insert,
Clear, or resize call exists on it anywhere in the
prototype.
- C1.2
grep for die-count arithmetic finds no expression
that can yield ≠ 2 — no diceCount +=, no
while (dice.Count < n), no config field named
extraDice, bonusDie, diceCount,
or similar.
- C1.3 No card, relic, upgrade, status effect, or event data row has
an effect type that grants, removes, duplicates, splits, or suppresses a
die.
- C1.4 An assertion/invariant
dice.length == 2 holds at
combat start, after every roll, after every card resolution, and at
combat end. A test drives a full combat and asserts it every phase.
- C1.5 "Inactive die" is not used as a disguised die-removal: if a
mode makes one die not count, that is a resolution rule, and
both dice must still be rolled, displayed, and present in state. (The
stale Tactical mode failed this in spirit.)
C2 — ONE DIE
IS A D4 (the other is the element die)
It has 4 value faces (1,2,3,4). Blank faces may replace values, but
the die is never larger than a d4 in value range. The second die is the
element die.
Testable checks
- C2.1 The number die's value domain is exactly
{1,2,3,4}
∪ {blank}. No face is ever assigned 0, 5, 6, or any value
> 4. (Note: the physical die today is a d6 with two blanks —
that satisfies this, since blanks carry no value. A d6 with a live
5 face would violate it.)
- C2.2 Face-rigging / upgrade code clamps or whitelists writes to the
number die against
{1,2,3,4,blank}; there is no path that
writes an arbitrary integer to a face.
- C2.3 The number die's face array length never changes at
runtime.
- C2.4 Exactly one of the two dice is typed/tagged as the element die
and its faces draw only from the element enum ∪
{blank}.
- C2.5 The two dice are not interchangeable: there is no code path
that turns the element die into a second number die or vice versa.
C3 — SLOTS EXIST AND NEVER
EXCEED 4
Fewer than 4 is allowed. What a slot means may be redefined.
The count must never go above 4 under any circumstance, upgrade, or
effect.
Testable checks
- C3.1 A single named constant
MAX_SLOTS = 4 (or
equivalent) exists, and every slot allocation, loop bound, and UI layout
derives from it. No literal 5, 6, or higher is
used as a slot count anywhere.
- C3.2 Every slot-adding path is guarded by
if (slots.Count < MAX_SLOTS) — or better, no slot-adding
path exists at all after combat init.
- C3.3 No card, relic, tile, Apsara, event, or enemy effect data row
has an effect that increases slot count. Grep the effect-type enum and
the data CSVs for
AddSlot, ExtraSlot,
SlotCount, UnlockSlot.
- C3.4 A test drives every upgrade/reward/effect in the prototype and
asserts
slots.Count <= 4 after each.
- C3.5 If the prototype redefines what a slot is (timing track,
element anchor, etc.), the ceiling still applies to whatever the
countable unit is; the doc must name that unit explicitly so the auditor
knows what to count.
C4 — CARDS EXIST as the
played resource
There is a deck, a hand, and cards go into slots.
Testable checks
- C4.1 Three distinct zones exist and are addressable in state: a draw
source (deck), a hand, and the slot board. A test can enumerate the
contents of each at any point.
- C4.2 The only way a card reaches a slot is a
Place/Play action originating from hand. No
path places a card directly from deck or discard into a slot without
passing through hand (unless the prototype documents that path as a
deliberate, named exception).
- C4.3 Card count is conserved: every card is in exactly one zone
(deck / hand / slot / discard / exhaust) at all times. A test sums zone
sizes each phase and asserts a constant.
- C4.4 Slot occupancy is bounded: no slot holds more than the
prototype's declared capacity (1 unless explicitly redefined), and
overwriting is either forbidden or explicitly specced.
- C4.5 Cards, not dice or energy, are the object that carries the
effect payload. The auditor can point at a card definition and read its
effect.
C5 — ENERGY
EXISTS as a visible spendable resource
With a stated income and stated sinks.
Testable checks
- C5.1 A single named energy value exists in combat state, is
non-negative, and is rendered in the UI/log every turn.
- C5.2 Income is enumerable: the auditor can list every code path that
increases energy and each has a documented rate (e.g. "+1 per empty slot
at turn start", "+4 on clog reset").
- C5.3 Sinks are enumerable: every code path that decreases energy has
a documented price. At minimum, rerolling is one of them (see C6).
- C5.4 No spend succeeds when
energy < cost; a test
attempts an unaffordable action and asserts it is rejected and energy is
unchanged.
- C5.5 Energy cannot go negative and cannot be silently created — a
test replays a turn and asserts
endEnergy == startEnergy + Σincome − Σspends.
- C5.6 The number is visible: it is not an internal-only
heuristic the player must infer.
C6 — REROLLS EXIST and
cost something
The player can choose to reroll.
Testable checks
- C6.1 A player-invokable reroll action exists and is reachable from
the turn's action set.
- C6.2 Every reroll debits a stated, non-zero cost from a stated
resource. There is no branch in which a reroll is free and
unlimited. (A free first roll is permitted only if subsequent
rerolls are priced.)
- C6.3 Reroll count per turn is finitely bounded by the resource — a
test asserts that with a fixed income the player cannot exceed the
derived roll budget (today: 4 energy → 2 rolls).
- C6.4 The reroll is a choice: a test confirms a legal turn
exists in which the player ends the turn without rerolling.
- C6.5 The cost curve (flat / escalating / per-die / element-priced)
is stated in the prototype doc and matches the code constant, not a
magic number buried in a method.
C7 — ELEMENTS ARE MORE THAN
SUITS
Each of Agni, Jal, Vayu, Prithvi (and gated Akasha) must have a
distinct mechanical verb or behaviour that changes what
happens on the board. An element that only functions as a colour-match
key is a charter violation.
Testable checks
- C7.1 The grep test. Searching the resolution/board
code for each element name returns at least one branch outside
of equality/matching comparisons. If every hit is of the form
card.Element == die.Element, the prototype fails.
- C7.2 The swap test. Globally permuting the element
labels (Agni↔︎Jal, Vayu↔︎Prithvi) across cards and die faces must produce
observably different board behaviour, not merely a relabelled
log. A test that permutes labels and asserts identical outcomes is a
failing witness.
- C7.3 The four-verbs test. Each of the four base
elements has a named, documented verb that is distinct from the other
three, and the auditor can point at the code that implements each. Two
elements sharing one verb is a fail.
- C7.4 The board-state test. At least one element's
verb writes to board/slot/dice/ energy/turn state — i.e. it changes
what happens, not only how much damage a card deals.
Damage/block numbers alone do not satisfy C7 (that is card text, the
current failure mode).
- C7.5 Akasha. Akasha has a stated verb and a stated
access gate, and the verb is actually reachable — no repeat of
NumberProc, which is implemented but authored on zero cards
and unauthorable from tooling. A test must be able to construct an
Akasha card and observe its verb fire.
- C7.6 The no-frequency-substitute test. Giving an
element a different proc probability (the current Akasha
situation) does not satisfy C7. The differentiator must survive holding
all match probabilities equal.
C8 — EXTREMELY INTUITIVE
The core must be graspable in under 30 seconds by a new player,
expressible in one teaching sentence.
Testable checks
- C8.1 The prototype doc opens with a single sentence
teaching the core loop. If it needs a second sentence or a subclause
chain, it fails.
- C8.2 The rules a player must hold to make a turn-1 decision fit on
one screen: ≤ 7 rules, no cross-references to other documents.
- C8.3 No rule requires the player to compute a probability, remember
a hidden counter, or track state that is not rendered on the board.
- C8.4 No two names in the player-facing vocabulary mean different
things in different contexts (the live core fails this today: an
authored
EitherMatch label that means both-match at
runtime, and "slot" also meaning a lantern-UI tile cell).
- C8.5 Every resource and every match condition is visible on the
board at the moment the player must decide — nothing is derivable only
from memory of prior turns.
- C8.6 The teaching sequence is stated: what turn 1 teaches, what turn
2 teaches, what turn 3 teaches. If a core rule is first felt after turn
3, it is not core.
4. Explicitly FREE to vary
These are open. A prototype may change any of them without
justification, and should say which it changed.
- The proc / match / resolution condition —
both-match, either-match, thresholds, sums, adjacency, sequencing,
ordering, direction. Anything.
- The energy model — empty-slot battery, flat income,
element-charged, card-generated, dice-generated. Anything, provided C5's
visibility/income/sink checks hold.
- The reroll cost curve — flat, escalating, per-die,
element-priced, first-free. Anything, provided C6 holds.
- Turn structure — phase order, whether placement
locks on first roll, whether resolution is immediate or deferred to turn
end, win conditions.
- Card flow — draw counts, hand size, hand
stickiness, discard/exhaust rules, whether a discard pile cycles, and
clog behaviour (including whether clog exists at
all).
- What a SLOT MEANS — board position, timing track,
element anchor, queue index, anything. Only the count ceiling of
4 is fixed (C3).
- Blank die faces — may be used, repurposed, or
dropped entirely. Note that dropping blanks removes the current Akasha
reward gate, so a prototype that drops blanks must state its replacement
Akasha access rule (C7.5).
- Whether locks latch across rerolls — the live rule
is non-latching; this is a design choice, not a constraint.
- Element proc probabilities — but see C7.6:
probability differences do not count as element verbs.
5. Not
free to vary (outside the charter but still binding)
- The damage/block/status pipeline in
wiki/concepts/combat-math.md is shared with relics,
enemies, and every status effect. A prototype may add to it but must not
silently reorder Strength-before-multipliers, the Weak-floor /
Vulnerable-ceil asymmetry, Thorns-per-hit, or Venom-skips-Block.
- Akasha remains gated — never a free starter
element, never in the blessing random pool.
- Element roster is exactly five: Agni, Jal, Vayu, Prithvi, Akasha. No
new elements.
Changelog
- 1.0 (2026-07-31) — initial grounding pass and charter. Sources:
base-game-rules-canon, combat-empty-slot-energy-proposal v0.2,
combat-rework-spec v0.2, wiki dice/slot/ combat-math/element.