Construindo RPG Textual Dragon Ball Z em JavaScript: Código e Conceitos

Abaixo está um framework JavaScript fundamental para RPG DBZ por turnos rodando em console (Node.js ou dev tools do navegador). Cobre sistemas centrais: stats, inimigos, loop de combate e progressão.

Código Completo Executável

// Estado do Jogo
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 });

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

// Loop Principal
async function startGame() {
  log("\n=== DRAGON BALL Z: CONSOLE RPG ===");
  log(`Bem-vindo, ${player.name}. Sua jornada começa.`);
  await mainMenu();
}

async function mainMenu() {
  log(`\n--- ${player.name} | Nv.${player.level} | HP: ${player.hp}/${player.maxHp} | Ki: ${player.ki} ---`);
  log("1. Treinar\n2. Lutar\n3. Status\n4. Sair");
  const choice = await waitForInput("Escolha ação: ");
  switch (choice) {
    case "1": await train(); break;
    case "2": await selectEnemy(); break;
    case "3": showStatus(); await mainMenu(); break;
    case "4": log("Adeus, guerreiro."); readline.close(); process.exit(); break;
    default: log("Entrada inválida."); await mainMenu();
  }
}

// Sistemas
async function train() {
  log("\nVocê treina intensamente... seu poder sobe!");
  player.level++; player.atk += 2; player.def += 1; player.maxHp += 10; player.hp = player.maxHp; player.ki += 5;
  log(`Level Up! -> Nv.${player.level} | ATK: ${player.atk} | DEF: ${player.def} | MAX HP: ${player.maxHp}`);
  await mainMenu();
}

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

async function selectEnemy() {
  log("\nEscolha um oponente:");
  enemies.forEach((e, i) => log(`${i+1}. ${e.name} (HP: ${e.maxHp}, ATK: ${e.atk})`));
  const idx = parseInt(await waitForInput("Número: ")) - 1;
  if (enemies[idx]) {
    currentEnemy = { ...enemies[idx] };
    log(`\n>>> BATALHA INICIADA: ${currentEnemy.name} <<<`);
    await battleLoop();
  } else { log("Seleção inválida."); await mainMenu(); }
}

async function battleLoop() {
  while (player.hp > 0 && currentEnemy.hp > 0) {
    // Turno do Jogador
    log(`\n--- SEU TURNO (HP: ${player.hp}) ---`);
    log("1. Atacar\n2. Carregar Ki\n3. Curar (Custa 20 Ki)");
    const action = await waitForInput("Ação: ");
    let dmg = 0;
    if (action === "1") dmg = calcDamage(player.atk, currentEnemy.def);
    else if (action === "2") { player.ki += 10; log("Ki carregado (+10)."); }
    else if (action === "3") {
      if (player.ki >= 20) { player.ki -= 20; player.hp = Math.min(player.maxHp, player.hp + 30); log("Curou 30 HP."); }
      else log("Ki insuficiente!");
    } else { log("Hesitação..."); }
    if (dmg > 0) { currentEnemy.hp -= dmg; log(`Você atingiu ${currentEnemy.name} por ${dmg} de dano! (HP Inimigo: ${Math.max(0, currentEnemy.hp)})`); }
    if (currentEnemy.hp <= 0) break;
    // Turno do Inimigo
    const eDmg = calcDamage(currentEnemy.atk, player.def);
    player.hp -= eDmg;
    log(`\n${currentEnemy.name} contra-ataca por ${eDmg} de dano! (Seu HP: ${Math.max(0, player.hp)})`);
  }
  if (player.hp <= 0) { log("\n>>> VOCÊ CAIU. GAME OVER <<<"); readline.close(); process.exit(); }
  else { log(`\n>>> VITÓRIA! ${currentEnemy.name} DERROTADO <<<`); player.xp += currentEnemy.xpReward; log(`Ganhou ${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); }

// Iniciar
startGame();

Conceitos-Chave Explicados

  • Gerenciamento de Estado: Objetos `player` e `enemies` guardam todos dados mutáveis.
  • Async/Await: Usado com `readline` para input síncrono no Node.js.
  • Fórmula de Combate: `Dano = max(Ataque - Defesa + Variância, 1)` garante acerto mínimo de 1.
  • Clonagem de Inimigos: `currentEnemy = { ...enemies[idx] }` cria instância nova por batalha para HP resetar.

Ideias de Expansão para Sensação "Real" DBZ

  • Mecânica de Ki: Gaste Ki em golpes especiais (Kamehameha, Solar Flare).
  • Transformações: Desbloqueie Super Saiyajin no Nível 10 (2x stats, dreno Ki/turno).
  • Árvore de Habilidades: Desbloqueie "Teletransporte" (esquiva) ou "Kaio-ken" (ATK temp, custo HP).
  • Eventos de História: Encontros roteirizados com escolhas de diálogo afetando alinhamento (Herói/Vilão).
  • Persistência: Save/Load via `localStorage` (browser) ou `fs` (Node).

Este esqueleto é intencionalmente minimalista. Expanda peça por peça — adicione lista de golpes, depois transformações, depois mapa-múndi — para construir um fan game completo.

HT Jogos © direito autoral [www.ptjogos.com]