DEV Community

Pawel Sloboda
Pawel Sloboda

Posted on • Originally published at secuspark.com

How I Built an RPG Battle System for a CompTIA Exam Prep App

How I Built an RPG Battle System for a CompTIA Exam Prep App

What if studying for your CompTIA Security+ exam felt like playing an RPG? That's the question that led me to build SecuSpark — a gamified certification prep platform where answering questions correctly deals damage to cybersecurity enemies.

In this post, I'll walk through the battle system architecture, the damage formula, and the PvP arena I just shipped.

The Stack

  • Frontend: Next.js 16 (App Router), React 19, Tailwind CSS 4
  • Animations: Framer Motion 12 for battle sequences
  • Data: Dexie.js (IndexedDB) for offline-first question storage
  • Backend: Supabase for auth, profiles, and PvP matchmaking
  • AI: GPT-4o-mini via Edge Functions for answer explanations
  • Sprites: PixelLab.ai for 30 levels of character evolution

The Damage Formula

Every correct answer triggers damage calculation:

baseDamage = 1.0 + weaponATK
hitType = 1.0 (normal) | 1.5 (heavy) | 2.0 (critical)
speedMult = 0.75 to 1.0 (based on answer speed)

totalDamage = baseDamage * hitType * speedMult
Enter fullscreen mode Exit fullscreen mode

Four stats modify this: Precision (crit chance), Agility (speed window), Fortitude (damage reduction), and Luck (graze chance + loot quality). Stats scale with sqrt and have caps to prevent late-game dominance.

The Turn Engine

The core of the battle system lives in a shared executeTurn() function:

// Simplified turn engine
function executeTurn(answer: Answer, stats: PlayerStats): TurnResult {
  const isCorrect = answer.selectedIndex === answer.correctIndex;

  if (!isCorrect) {
    // Enemy attacks back
    const defense = computeDefense(stats.fortitude);
    const enemyDamage = enemy.atk * (1 - defense);
    return { playerDamage: enemyDamage, enemyDamage: 0, type: 'miss' };
  }

  // Calculate player damage
  const hitType = rollHitType(stats.precision);
  const speedMult = calculateSpeedMultiplier(answer.timeMs, stats.agility);
  const damage = (1.0 + weapon.atk) * hitType * speedMult;

  return { playerDamage: 0, enemyDamage: damage, type: hitType };
}
Enter fullscreen mode Exit fullscreen mode

This same function powers both PvE (story battles against 103 enemies) and PvP (duels against other players). One engine, two modes.

Hook Decomposition

The PvP orchestration hook started at 1,062 lines. After extracting shared logic into focused hooks:

  • useBattleHP — shared HP state + damage analytics
  • useBattleTimer — question countdown
  • usePvPMatchCompletion — ELO, streaks, persistence
  • usePvPOfflineRound + usePvPSimultaneousRound — round processing

The main hook dropped to 487 lines. Each extracted hook is independently testable with 91 new tests.

Character Evolution

Players evolve through 30 visual stages. Each level has 8 directional rotations and 13 animation states (idle, attack, hurt, cast, defend, dodge, charge, death, and more).

The sprites are generated via PixelLab.ai using inpainting with a body-only mask. The head stays frozen (consistent identity) while the body regenerates with increasing complexity per tier:

  • L01-05: Size growth
  • L06-10: Limb development
  • L11-15: Surface detail
  • L16-20: Shoulder definition
  • L21-25: Elemental effects
  • L26-30: Final form

PvP Arena

The newest addition. Players can:

  • Challenge friends to asynchronous duels
  • Get matched via ELO (start 1000, K=32, +/-200 range)
  • Compete in weekly leagues
  • Form clans with fortress bases on a world map

PvP uses the same turn engine as PvE, so all stat investments transfer. The difference is simultaneous answers — both players answer the same question, and the faster correct answer strikes first.

What I Learned

  1. Share everything between modes. Having one executeTurn function prevents PvE and PvP from drifting apart.
  2. Sqrt scaling with caps prevents late-game stat stacking from breaking balance.
  3. Hook decomposition makes complex state manageable. Extract when a hook exceeds 500 lines.
  4. Offline-first is non-negotiable for study apps. Students study everywhere — buses, planes, bad wifi.

Try It

SecuSpark has 1,514 practice questions across Security+, A+, and Network+. It's free, works offline after first load, and no signup is required to start practicing.

Try it free at secuspark.com — no signup required.


Originally published on SecuSpark. SecuSpark is a free, gamified CompTIA certification prep platform with AI explanations and RPG mechanics.

Top comments (0)