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.
// 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();
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.