Six player classes — linear three-stage advancement at the standard breakpoints. Stat sums S1 ≈ 12 · S2 ≈ 16 · S3 ≈ 20 across Attack / Defense / Speed (max 10 per axis). Art pipeline mirrors the creature codex: raw generations placed for approval, crops after sign-off.
One per line, upgrading with stage. Learned at class level 10 (authored as Learn_Level). Costs the avatar's action, once per battle, and resolves in a priority bracket ahead of every normal action regardless of Speed (two in one round tie on the round seed). Authored in codex/ultimates.csv. Full mechanics — redirect scope, vanish retargeting, echo rounding, autoplay — on the battle system page.
| Line | S1 / S2 / S3 | Effect |
|---|---|---|
| WARRIOR | Hold the Line / Iron Bulwark / Martyr’s Aegis | Taunt for the round. S2 adds +10% Defense for the rest of the battle; S3 adds 5% of maximum HP healed per hit taken this round. |
| MAGE | Shardburst / Stonefall / Cataclysm | Strikes every enemy. Power 75 / 100 / 125 per target. |
| ARCHER | Sure Shot / Deadeye / Godpiercer | Strikes one enemy. Power 180 / 240 / 300. |
| BRUTE | Blood Price / Tyrant’s Toll / Wrath of the Pit | Retaliation stance for the round: each hit taken answers with a 50 / 65 / 80 power counter and raises Attack 4 / 5 / 6% for the rest of the battle, capped at +40 / 50 / 60%. |
| PROFESSOR | Recitation / Grand Thesis / Revelation | Echo: every support ability in the hero’s soul slots fires at once on the whole party, the hero included — self-only and ally-type abilities alike, and the type gate does not apply. Bonuses land at 40 / 50 / 60% strength (a +50% buff becomes +20% / +25% / +30%) and durations are halved, rounded up. Whole-number effects of one step round away to nothing at 40%. |
| ASSASSIN | Slip Away / Shadowmeld / Vanish | Strikes one enemy. Power 120 / 180 / 240, then untargetable by single-target moves for 1 / 2 / 3 rounds. |
A class line advances at class level 33 and 66
(CLASS_STAGE_LV). The stage is computed from the class level every time it is needed —
clsStageOf(lvl) returns 3 at level 66+, 2 at 33+, else 1 — and there is no stored stage anywhere that could
drift. A save already past a breakpoint is therefore correct the instant it loads. clsAdv records which stage the player has
accepted (clicked through the stage-up), so the advancement scene does not replay. Since #102 (2026-08-13) the avatar’s stats, art and name use the shown stage, clsStageShown() = min(clsStageOf(lvl), clsAdv): a class past a breakpoint keeps its old stage’s stats until the stage-up is accepted. The leaderboard power (src/power.js) reads it the same way since 2026-09-30. Only the class being played
advances: clsStageNow(id) returns 1 for any line other than the active charSoul, because classXp
belongs to the line in play.
Everything the player sees must derive its own
stage. The client's CLASSLINES table carries curStage:1 and img:'CLS-<LINE>-1';
those two fields are fresh-save defaults and are not the stage. Reading them renders stage-1 content for a fully advanced
character. Report #22 (2026-08-03) was exactly this: the equipment portrait read the pinned img while the name
beside it derived correctly, so a class-Lv75 Paladin was shown the Warrior stage-1 art. The portrait now builds its
path as CLS-<LINE>-<clsStageNow()>, the same way the battle avatar always did. Art exists for all three
stages of all six lines (codex/images/classes/CLS-<LINE>-1|2|3.png), so a stage-1 portrait on an advanced
character is always a binding defect, never missing art.
Each class line levels on its own (D 2026-10-01). classXp is the XP of the class being played and clsAdv its accepted stage; clsXpBy and clsAdvBy hold every other class’s. Switching banks the class left and loads the class taken — a newly bought class is level 1. Stats, ultimates, stage art, power and the PvP team builder all read the active class’s level; gear requirements read the account level, which stays shared. The server credits each battle’s class XP to the class that fought and clamps each class’s XP to its own credit.
120 / 180 / 240 (the Archer’s 180 / 240 / 300 ladder one stage down, the vanish being the difference), and the vanish status lands on her after the hit exactly as before: same duration, single-target moves retarget away, area moves still connect, no cleanse. The ultimate is aimed like any one-foe attack (a vanished foe is refused); autoplay and the PvP defense brain pick a living, visible enemy. With every foe out of sight the strike is lost and she still vanishes. ultimates.csv carries the Power; the engine is the shared src/battle_round.js (ultVanishAfter), so the client and the worker’s Auto PvP resolve it alike once the bake re-pins. dfmc-client lane vanish, gate tools/probe_t54_vanish.py 7/17 → 17/17.tools/probe_t66_classes.py 1/3 → 3/3.tools/codex_tables.py block cls-ultimates (the same generator as the GDD’s); every power, percent and duration is a data-csv binding on its cell. No number changed.classes.csv row of the stage the card shows (Grade_Attack / Grade_Defense / Grade_Speed; no utility axis, the CSV has no Grade_Utility). It sits in the lower-left corner of the art, so the card keeps its height beside the UPGRADE badge and the lock plate; a locked class shows it too, above the chain, so the radar says what an unlock buys. Gate tools/probe_b8_clsradar.py (client).clsXpBy, the server’s per-class ledger view) and the stage is the accepted one, the same figures the soul bag shows. The card art is bigger (250px, was 170). A class you do not own is greyed out and bound by two iron chains and the padlock (play/img/ui/chain_lock.webp, drawn in code by tools/make_chain_art.py). Buying a class and class XP are unchanged and stay the server’s. Gate tools/probe_classcards.py 10/10.classlevel: “Every class is a different level. Only the account level stays shared.”). LIVE on release since 2026-10-23 (0.89). Since 2026-07-27 the build kept one classXp for every class, so switching class kept the number and every class shared one level, against what this design always said. Now classXp and clsAdv are the active class’s, and clsXpBy / clsAdvBy bank every other class. A newly bought class starts at level 1 and earns its own XP on the same curve (cap at level 100). Switching banks the class you leave and loads the class you take; its soul in the bag shows its own level. The server owns it: /api/battle credits class XP to the class the battle was fought as (if the account owns it), in a per-class ledger (ledger_cls), and the gap penalty uses that class’s level; the save door clamps each class’s XP to what that class was credited plus the usual grace, grow-only per class. Account XP and the account level are unchanged and shared. No migration: a save from before this has its classXp on its active class and every other class at level 1 until the wipe. Gates tools/probe_classlevel.py 1/9 → 9/9 and tools/probe_classlevel.mjs 6/19 → 19/19.classdoor). The client used to take the Unlock_Cost out of its own wallet, add the line to clsOwned and hand itself the two Tier-1 weapons, and the save door unioned whatever clsOwned it was sent. Now POST /api/unlock (kind class) prices the line from classes.csv, checks the stored wallet, debits it, adds the line to the stored clsOwned and mints both starter weapons with server uids (origin class, dismantlable like any server grant); the client flushes first and adopts the answer. Owning the line is the idempotency record: a second or double-clicked buy answers already and charges nothing. The save door no longer unions clsOwned: the stored list wins, a class the server never sold is dropped and flagged (class_unowned); an account with no stored list keeps one class, its stored charSoul or the character-creation pick.clsAdv is not display-only. Since #102 the avatar’s stats use the accepted stage, min(level stage, clsAdv), and the leaderboard now does too (client review F145 / F146).tools/probe_clscap.py 4/4 (red 2/4 against 0.57 — an over-cap answer clamps to exactly the cap; an under-cap answer lands on exactly the server’s gain), tools/probe_creditretry.py back to 11/11 (7/11 on 0.57).CLASSLINES.curStage/.img are fresh-save defaults rather than the stage. Fixes report #22: the equipment portrait was reading the pinned stage-1 image, so an advanced class showed its stage-1 art next to a correct stage-3 name. Reproduced against the reporter's own save and verified before/after. LIVE on release since 2026-08-08 (0.42).