See it running
The short version
- Made for a client. I built the combat rules the battle uses: hit, crit, armor, morale, turn order.
- One rules object owns the numbers. Units and abilities do not hide magic values in their scripts.
- Seeded random rolls: same seed, same fight. That is what makes the tests possible.
- On screen: isometric grid, ability bar, selected-unit card, overhead HP and shield icons.
- Campaign layer: health persists, death is permanent until an explicit heal point.
- Tests cover RNG, turn order, morale, campaign save, and a check that combat never auto-heals.
What it does
- Isometric grid battle: click a unit, pick an ability, resolve the attack
- Abilities the client authored (Bolt, Stunbolt, Stab, Axeblade) run through the same rules object
- Ability panel shows range, strength %, status, and chance (Stunbolt: 50% daze at range 4)
- Overhead HP plus silver shield icons for remaining armor. Selected-unit card shows class and current / max health
- Accuracy 1–10 sets hit and crit. A physical hit spends one armor shield, then health
- Faster units act first. Speed above 10 always goes before everyone else
- End of a real turn: morale check. Fail: -2 speed next round. Morale 10 cannot fail
- Between battles, health carries over. Dead units cannot be deployed again
- Unit tests prove the same seed produces the same rolls. Combat is not allowed to auto-heal
Built for a client
Made for a client from a written spec: the combat rules layer inside their tactics game. I owned hit/crit/armor/morale math, seeded RNG, the battle state machine, and the tests. Art, units, and the isometric map are theirs.
The thought
The client needed combat a designer can tune without opening unit scripts, and that I can re-run in tests. Hit chance, crits, armor, and morale sit in one rules object. Random rolls go through a seeded RNG. Campaign health is attrition: wounded stays wounded, dead stays dead. Their isometric battle is the skin. The rules file is the job.
Mechanics
Click a unit. The selected-unit panel shows class and health (Tank 14/20, Squire Crossbow 7/11). The ability bar offers that unit's attacks plus Skip Turn. Hovering Stunbolt shows range 4, 50% strength, 50% chance to daze. A pink tile marks the target. An attack rolls to hit from Accuracy, then rolls to crit. A physical hit spends one armor shield before it touches health. That is the silver icon next to the HP bar. Optional penetration lets a fraction through. Crits double damage after armor. Miss and crit show as on-screen indicators. Magic bypasses armor in this version. Turn order is speed, including morale buffs. Ties break at random. Speed above 10 always acts before anyone at 10 or below. At the end of a real turn (skipped units do not roll), morale is checked against a table. Fail: -2 speed until the next round. Morale 10 cannot fail, and grants +2 speed.
Systems
A unit has a definition (class, faction, stats, abilities) and live combat state (current health, armor, morale, buffs, effective speed). Health ranges are clamped by class. Strength scales melee. Arcane Might scales mage damage. Accuracy is the only crit stat. A corpse cannot be killed twice. Speed buffs apply after the queue rebuilds, so a morale fail shows up next round, not mid-turn. Between battles a campaign object stores health by unit id, a dead list, and the last seed. Heal only happens at points the campaign chooses. Dead units cannot be deployed.
Under the hood
Three globals: events, random rolls, and the combat tables. A state machine runs the fight: setup, start turn, wait for action, resolve attack, check victory, end turn. Wait-for-action is the SELECT ABILITY panel. Resolve builds an attack, asks the rules object for the result, then plays hit or die. Unit tests cover roll sequences, the accuracy table, turn-order distribution, morale fail rates, campaign save/load, and a lint that combat must not heal on its own. Design can change numbers without touching resolution.
Controls
How it came together
Rules spec
Six stats, armor shields, accuracy table, morale table, seeded RNG. One autoload owns every threshold.
Battle loop
State machine: setup, start turn, wait for action, resolve attack, check victory, end turn + morale.
Campaign attrition
Health persists between fights. Dead units leave the roster. Heal only at explicit campaign points.
Client battle
Wired into the client's isometric map: ability bar, selected-unit panel, HP and shield icons, wait-for-action state.
What I would do differently
One rules file beats magic numbers in unit scripts. Seeded rolls are what made the test suite possible. The client's map and sprites made the systems readable; the job was keeping every threshold out of those sprites. Next is freeze the tables so design can tune without rewriting resolution.