HT Games

Building a Text-Based Dragon Ball Z RPG in JavaScript: Code & Concepts

Below is a foundational JavaScript framework for a turn-based DBZ RPG running in a console environment (Node.js or browser dev tools). It covers core systems: stats, enemies, combat loop, and progression.

Complete Runnable Code

// Game State
const player = {
  name: "Goku",
  level: 1,
  maxHp: 100,
  hp: 100,
  atk: 10,
  def: 5,
  ki: 0,
  xp: 0,
  inventory: []
};
const enemies = [
  { name: "Raditz", maxHp: 150, hp: 150, atk: 15, def: 8, xpReward: 50 },
  { name: "Nappa", maxHp: 200, hp: 200, atk: 20, def: 12, xpReward: 100 },
  { name: "Vegeta", maxHp: 250, hp: 250, atk: 25, def: 15, xpReward: 200 }
];
let currentEnemy = null;
const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});

// Utility
const log = (...args) => console.log(...args);
const waitForInput = (promptText) => new Promise(resolve => readline.question(promptText, resolve));

// Core Loop
async function startGame() {
  log("\n=== DRAGON BALL Z: CONSOLE RPG ===");
  log(`Welcome, ${player.name}. Your journey begins.`);
  await mainMenu();
}

async function mainMenu() {
  log(`\n--- ${player.name} | Lv.${player.level} | HP: ${player.hp}/${player.maxHp} | Ki: ${player.ki} ---`);
  log("1. Train\n2. Fight\n3. Status\n4. Quit");
  const choice = await waitForInput("Choose action: ");
  switch (choice) {
    case "1": await train(); break;
    case "2": await selectEnemy(); break;
    case "3": showStatus(); await mainMenu(); break;
    case "4": log("Farewell, warrior."); readline.close(); process.exit(); break;
    default: log("Invalid input."); await mainMenu();
  }
}

// Systems
async function train() {
  log("\nYou train intensely... your power rises!");
  player.level++;
  player.atk += 2;
  player.def += 1;
  player.maxHp += 10;
  player.hp = player.maxHp;
  player.ki += 5;
  log(`Level Up! -> Lv.${player.level} | ATK: ${player.atk} | DEF: ${player.def} | MAX HP: ${player.maxHp}`);
  await mainMenu();
}

function showStatus() {
  log(`\n--- STATUS ---\nLevel: ${player.level}\nATK: ${player.atk}\nDEF: ${player.def}\nHP: ${player.hp}/${player.maxHp}\nKi: ${player.ki}\nXP: ${player.xp}`);
}

async function selectEnemy() {
  log("\nChoose an opponent:");
  enemies.forEach((e, i) => log(`${i+1}. ${e.name} (HP: ${e.maxHp}, ATK: ${e.atk})`));
  const idx = parseInt(await waitForInput("Enter number: ")) - 1;
  if (enemies[idx]) {
    currentEnemy = { ...enemies[idx] }; // Clone to not modify original stats
    log(`\n>>> BATTLE START: ${currentEnemy.name} <<<`);
    await battleLoop();
  } else {
    log("Invalid selection.");
    await mainMenu();
  }
}

async function battleLoop() {
  while (player.hp > 0 && currentEnemy.hp > 0) {
    // Player Turn
    log(`\n--- YOUR TURN (HP: ${player.hp}) ---`);
    log("1. Attack\n2. Charge Ki\n3. Heal (Costs 20 Ki)");
    const action = await waitForInput("Action: ");
    let dmg = 0;
    if (action === "1") dmg = calcDamage(player.atk, currentEnemy.def);
    else if (action === "2") {
      player.ki += 10;
      log("Ki charged (+10).");
    } else if (action === "3") {
      if (player.ki >= 20) {
        player.ki -= 20;
        player.hp = Math.min(player.maxHp, player.hp + 30);
        log("Healed 30 HP.");
      } else log("Not enough Ki!");
    } else {
      log("Hesitation...");
    }
    if (dmg > 0) {
      currentEnemy.hp -= dmg;
      log(`You hit ${currentEnemy.name} for ${dmg} damage! (Enemy HP: ${Math.max(0, currentEnemy.hp)})`);
    }
    if (currentEnemy.hp <= 0) break;
    // Enemy Turn
    const eDmg = calcDamage(currentEnemy.atk, player.def);
    player.hp -= eDmg;
    log(`\n${currentEnemy.name} counters for ${eDmg} damage! (Your HP: ${Math.max(0, player.hp)})`);
  }
  if (player.hp <= 0) {
    log("\n>>> YOU HAVE FALLEN. GAME OVER <<<");
    readline.close();
    process.exit();
  } else {
    log(`\n>>> VICTORY! ${currentEnemy.name} DEFEATED <<<`);
    player.xp += currentEnemy.xpReward;
    log(`Gained ${currentEnemy.xpReward} XP. Total: ${player.xp}`);
    currentEnemy = null;
    await mainMenu();
  }
}

function calcDamage(atk, def) {
  return Math.max(atk - def + Math.floor(Math.random() * 3), 1); // Base dmg + small variance
}

// Start
startGame();

Key Concepts Explained

  • State Management: `player` and `enemies` objects hold all mutable data.
  • Async/Await: Used with `readline` to handle synchronous-feeling user input in Node.js.
  • Combat Formula: `Damage = max(Attack - Defense + Variance, 1)` ensures hits always connect for at least 1 damage.
  • Cloning Enemies: `currentEnemy = { ...enemies[idx] }` creates a fresh instance per battle so enemy HP resets.

Expansion Ideas for a "Real" DBZ Feel

  • Ki Mechanics: Spend Ki for special moves (Kamehameha, Solar Flare).
  • Transformations: Unlock Super Saiyan at Level 10 (2x stats, Ki drain/turn).
  • Skill Tree: Unlock moves like "Instant Transmission" (dodge) or "Kaio-ken" (temp ATK boost, HP cost).
  • Story Events: Scripted encounters with dialogue choices affecting alignment (Hero/Villain).
  • Persistence: Save/Load using `localStorage` (browser) or `fs` (Node).

This skeleton is intentionally minimal. Expand it piece by piece—add a move list, then a transformation system, then a world map—to build a fully featured fan game.


https://www.htfbw.com © HT Games