Battle
Turn-based 1v1 battles driven by a state machine: the player picks Fight / Switch / Item / Flee, states play out both sides' actions with tweens and dialog, and the battle ends in victory experience, a capture, a flee, or a party wipe.
How it works
StateMachine is a minimal generic class — a map of named states with onEnter/onExit hooks and an async setState — instantiated once as battleStateMachine over the 17 battle states. Each state file under models/dungeons/state/battle/states/ owns one phase and decides the next transition, so a turn is a chain of small, testable steps rather than a monolithic update loop.
stateDiagram-v2 [*] --> Intro Intro --> PreBattleInfo PreBattleInfo --> BringOutMonster BringOutMonster --> PlayerInput PlayerInput --> Battle: Fight PlayerInput --> SwitchAttempt: Switch PlayerInput --> ItemAttempt: Item PlayerInput --> FleeAttempt: Flee ItemAttempt --> CatchMonster: ball ItemAttempt --> EnemyInput: heal CatchMonster --> Finished: success CatchMonster --> EnemyInput: failure SwitchAttempt --> SwitchMonster SwitchMonster --> EnemyInput FleeAttempt --> Finished: escaped FleeAttempt --> EnemyInput: failed EnemyInput --> Battle Battle --> PlayerAttack: player first Battle --> EnemyAttack: enemy first PlayerAttack --> PlayerPostAttackCheck PlayerPostAttackCheck --> GainExperience: enemy fainted PlayerPostAttackCheck --> EnemyAttack: enemy still to act PlayerPostAttackCheck --> PlayerInput: turn over EnemyAttack --> EnemyPostAttackCheck EnemyPostAttackCheck --> SwitchMonster: player monster fainted EnemyPostAttackCheck --> PlayerAttack: player still to act EnemyPostAttackCheck --> PlayerInput: turn over GainExperience --> Finished Finished --> [*]
The math, all in small pure services under services/dungeons/monster/ and services/dungeons/item/:
- Damage —
ceil(random(0.85, 1.01) × attacker.stats.attack × power / (power + defense)). Each attack carries apower(Slash 40, Ice Shard 55) and every monster adefensestat, so move choice and bulk both matter. The saturatingpower / (power + defense)factor means defense meaningfully reduces damage without ever granting immunity, and tuning stays two-knob. - Experience — on a kill,
round(baseExp × enemyLevel / 7);useExperienceapplies it and loopslevelUpwhile the threshold (calculateLevelExperience) is crossed. Level-ups add randomizedmaxHp(+5–8),attack(+1–2), anddefense(+1–2). - Capture — ball items roll
0.5 + (1 − hp/maxHp) × 0.2against a uniform random: success joins the party, a near miss (within 0.1) gets its own dialog, otherwise failure — so weakening the enemy first genuinely helps. - Flee — a random escape attempt; failure forfeits the turn.
Attack visuals are Vue components (Battle/Attack/Slash.vue, IceShard.vue) selected by AttackComponentMap and animated with spritesheet tweens; monster HP/EXP bars are the shared UI/Bar components.
Key files
Paths relative to packages/app/app.
| File | Role |
|---|---|
models/dungeons/state/StateMachine.ts | generic state machine |
models/dungeons/state/battle/StateMap.ts | the 17 battle states |
services/dungeons/scene/battle/battleStateMachine.ts | the singleton instance |
services/dungeons/monster/calculateDamage.ts | damage roll |
assets/dungeons/data/attacks.ts | per-attack power values |
services/dungeons/monster/calculateExperienceGain.ts | experience award |
services/dungeons/monster/levelUp.ts | stat growth |
services/dungeons/item/createCaptureResult.ts | capture roll |
store/dungeons/battle/ | per-battle stores (player, enemy, dialog, action) |
Notes
- The
Battlestate orders the two attacks throughuseAttackStatePriorityMapand the post-attack checks route to the other side's attack via the same map. The enemy AI is trivial:EnemyAttackpicks a uniformly random attack from the monster'sattackIds. - Power and defense values are tuned so early battles keep their pre-defense length: with base defense 5,
power / (power + defense)sits near 0.9 at low levels, close to the old raw-attack damage. - A type-effectiveness chart is deliberately out of scope until the roster grows beyond the current five differentiated species — see the deferred status effects.