luzosa-cli
Version:
An interactive One Piece quiz game with pirate-themed styling and multiple difficulty levels
407 lines (394 loc) • 12 kB
JavaScript
import inquirer from "inquirer";
import chalk from "chalk";
import chalkAnimation from "chalk-animation";
import figlet from "figlet";
import gradient from "gradient-string";
// Check for non-interactive mode flag
const isNonInteractive = process.argv.includes("--non-interactive");
// Check if running in interactive environment
if (!process.stdin.isTTY && !isNonInteractive) {
console.log("This CLI tool requires an interactive terminal.");
console.log("Please run: npm install -g luzosa-cli && luzosa");
console.log("Or use: luzosa --non-interactive for testing");
process.exit(1);
}
// Luffy ASCII Art (simple, fits console)
const onePieceQuestions = {
easy: [
{
question: "What is the name of Luffy’s ship?",
options: ["Going Merry", "Red Force", "Thousand Sunny", "Moby Dick"],
answer: "Thousand Sunny",
},
{
question: "Who is the navigator of the Straw Hat Pirates?",
options: ["Robin", "Nami", "Sanji", "Chopper"],
answer: "Nami",
},
{
question: "What is Luffy’s signature attack?",
options: ["Gomu Gomu no Pistol", "Jet Pistol", "Red Hawk", "King Cobra"],
answer: "Gomu Gomu no Pistol",
},
{
question: "Who is the doctor of the Straw Hat crew?",
options: ["Usopp", "Chopper", "Brook", "Franky"],
answer: "Chopper",
},
{
question: "Which fruit did Luffy eat?",
options: [
"Mera Mera no Mi",
"Gomu Gomu no Mi",
"Hie Hie no Mi",
"Ope Ope no Mi",
],
answer: "Gomu Gomu no Mi",
},
{
question: "Who is the swordsman in the crew?",
options: ["Sanji", "Zoro", "Franky", "Jinbe"],
answer: "Zoro",
},
{
question: "What animal is Tony Tony Chopper?",
options: ["Monkey", "Dog", "Reindeer", "Cat"],
answer: "Reindeer",
},
{
question: "Which crew is Luffy the captain of?",
options: [
"Red Hair Pirates",
"Heart Pirates",
"Straw Hat Pirates",
"Whitebeard Pirates",
],
answer: "Straw Hat Pirates",
},
{
question: "Who is the cook of the Straw Hat Pirates?",
options: ["Zoro", "Sanji", "Brook", "Franky"],
answer: "Sanji",
},
{
question: "Who gave Luffy his straw hat?",
options: ["Dragon", "Shanks", "Rayleigh", "Ace"],
answer: "Shanks",
},
],
medium: [
{
question: "What is Nico Robin’s Devil Fruit power?",
options: [
"Flower-Flower Fruit",
"Shadow-Shadow Fruit",
"Soul-Soul Fruit",
"Clone-Clone Fruit",
],
answer: "Flower-Flower Fruit",
},
{
question: "What is the name of Zoro’s three swords style?",
options: ["Santoryu", "Nitoryu", "Ichitoryu", "Zantoryu"],
answer: "Santoryu",
},
{
question: "Who is the current Fleet Admiral of the Marines?",
options: ["Akainu", "Aokiji", "Kizaru", "Garp"],
answer: "Akainu",
},
{
question: "What is the real name of Luffy’s Devil Fruit?",
options: [
"Gomu Gomu no Mi",
"Hito Hito no Mi: Model Nika",
"Nika Nika no Mi",
"Mythical Gum Fruit",
],
answer: "Hito Hito no Mi: Model Nika",
},
{
question:
"Which island was Sanji trained on after the Sabaody Archipelago?",
options: [
"Momoiro Island",
"Rusukaina",
"Karate Island",
"Kamabakka Kingdom",
],
answer: "Kamabakka Kingdom",
},
{
question: "Who defeated Gecko Moria?",
options: ["Luffy", "Jinbe", "Doflamingo", "Kaido"],
answer: "Doflamingo",
},
{
question: "What is the name of the prison where Ace was held?",
options: ["Enies Lobby", "Impel Down", "Marineford", "Punk Hazard"],
answer: "Impel Down",
},
{
question: "Which Yonko was defeated by Luffy in Wano?",
options: ["Shanks", "Kaido", "Big Mom", "Blackbeard"],
answer: "Kaido",
},
{
question: "Who is the archaeologist of the Straw Hat crew?",
options: ["Brook", "Nami", "Robin", "Franky"],
answer: "Robin",
},
{
question: "Who trained Luffy in Haki during the time skip?",
options: ["Shanks", "Dragon", "Rayleigh", "Sabo"],
answer: "Rayleigh",
},
],
hard: [
{
question: "What was the name of the fish-man Arlong's crew?",
options: [
"Sun Pirates",
"Fish-Man Force",
"Arlong Pirates",
"Blue Shark Crew",
],
answer: "Arlong Pirates",
},
{
question: "What is the full name of Trafalgar Law?",
options: [
"Trafalgar D. Water Law",
"Trafalgar Rosinante Law",
"Trafalgar D. Rosinante",
"Trafalgar Law D.",
],
answer: "Trafalgar D. Water Law",
},
{
question: "What is the name of the sword Enma's previous owner?",
options: ["Oden", "Zoro", "Kozuki Toki", "Ryuma"],
answer: "Oden",
},
{
question: "What race does King from the Beast Pirates belong to?",
options: ["Giant", "Lunarian", "Skypiean", "Oni"],
answer: "Lunarian",
},
{
question: "What is the name of the ancient weapon that Shirahoshi is?",
options: ["Poseidon", "Uranus", "Pluton", "Neptune"],
answer: "Poseidon",
},
{
question: "Who was the first Warlord defeated by Luffy?",
options: ["Crocodile", "Moria", "Doflamingo", "Buggy"],
answer: "Crocodile",
},
{
question: "Which scientist created the Pacifistas?",
options: ["Vegapunk", "Caesar Clown", "Judge", "Franky"],
answer: "Vegapunk",
},
{
question: "What is the codename of CP0’s strongest agent?",
options: ["Lucci", "Spandam", "Blueno", "Jabra"],
answer: "Lucci",
},
{
question: "What title did Luffy get after defeating Doflamingo?",
options: [
"Fifth Emperor",
"Conqueror",
"The Pirate King",
"King of Dressrosa",
],
answer: "Fifth Emperor",
},
{
question: "What island was the final Poneglyph found on?",
options: ["Zou", "Wano", "Raftel", "Ohara"],
answer: "Wano",
},
],
};
async function luffyIntro() {
return new Promise((resolve) => {
// Print One Piece Quiz title with figlet and gradient
figlet("One Piece Quiz!", (err, data) => {
if (!err) {
console.log(gradient.cristal.multiline(data));
}
// Animate Luffy's message
const animation = chalkAnimation.karaoke(
chalk.yellow.bold(
"Yohohoho! I am Monkey D. Luffy, and welcome to the Grand One Piece Quiz Adventure!\n"
) +
chalk.blue(
"Test your knowledge and see if you have what it takes to join the Straw Hat crew!\n"
)
);
setTimeout(() => {
animation.stop();
console.log(chalk.red.bold("\nLet's set sail!\n"));
resolve();
}, 3500);
});
});
}
async function askQuestion(questionObj, qNum, level) {
console.log(
chalk.cyan(`\n[${level.toUpperCase()} Q${qNum + 1}] `) +
chalk.bold(questionObj.question)
);
const { userAnswer } = await inquirer.prompt([
{
type: "list",
name: "userAnswer",
message: chalk.magenta("Choose your answer:"),
choices: questionObj.options,
},
]);
const correct = userAnswer === questionObj.answer;
if (correct) {
console.log(chalk.greenBright("Correct! 🍖 Luffy is proud!"));
} else {
console.log(
chalk.redBright("Wrong!") +
chalk.yellow(` The right answer was: ${questionObj.answer}`)
);
}
return { correct, userAnswer };
}
async function playLevel(levelName, questions) {
let score = 0;
let lastQuestion = "";
let lastUserAnswer = "";
for (let i = 0; i < questions.length; i++) {
const { correct, userAnswer } = await askQuestion(
questions[i],
i,
levelName
);
lastQuestion = questions[i].question;
lastUserAnswer = userAnswer;
if (correct) score++;
console.log(chalk.blueBright(`Current Score: ${score}/${i + 1}`));
}
return { score, lastQuestion, lastUserAnswer };
}
async function main() {
try {
await luffyIntro();
let exitGame = false;
// Handle Ctrl+C (SIGINT) gracefully
let sigintPromptActive = false;
process.on("SIGINT", async () => {
if (sigintPromptActive) return; // Prevent multiple prompts
sigintPromptActive = true;
const { exitChoice } = await inquirer.prompt([
{
type: "input",
name: "exitChoice",
message: chalk.yellow(
"Do you want to exit? (y/n)\n(Tip: Press Ctrl+C again to force quit immediately.)"
),
validate: (input) => {
if (!["y", "n", "Y", "N"].includes(input.trim())) {
return "Please enter 'y' or 'n'.";
}
return true;
},
},
]);
if (exitChoice.trim().toLowerCase() === "y") {
console.log(
chalk.yellow("\nThanks for playing! Set sail for more adventures!")
);
process.exit(0);
} else {
sigintPromptActive = false;
// Resume the game
}
});
while (!exitGame) {
console.log(chalk.bgBlue.bold("Choose your difficulty, nakama!"));
const { level } = await inquirer.prompt([
{
type: "list",
name: "level",
message: chalk.yellow("Select your level:"),
choices: [
{ name: "Easy (East Blue)", value: "easy" },
{ name: "Medium (Grand Line)", value: "medium" },
{ name: "Hard (New World)", value: "hard" },
],
},
]);
console.log(
chalk.bgMagenta.bold(
`\nYou chose: ${level.toUpperCase()}! Get ready for 10 questions!\n`
)
);
const { score, lastQuestion, lastUserAnswer } = await playLevel(
level,
onePieceQuestions[level]
);
console.log(chalk.bgYellow.bold("\n--- Quiz Complete! ---"));
console.log(chalk.greenBright(`\nYour final score: ${score}/10`));
console.log(
chalk.cyanBright("Last question you answered: ") +
chalk.white(lastQuestion)
);
console.log(
chalk.cyanBright("Your answer: ") + chalk.white(lastUserAnswer)
);
if (score === 10) {
console.log(chalk.bgGreen.bold("\nYou're a true Pirate King! 🏴☠️"));
} else if (score >= 7) {
console.log(chalk.bgBlue.bold("\nYou'd make a great Straw Hat!"));
} else {
console.log(
chalk.bgRed.bold("\nYou might need to study the Grand Line more!")
);
// Prompt user if they want to exit or continue
const { exitChoice } = await inquirer.prompt([
{
type: "input",
name: "exitChoice",
message: chalk.yellow("You lost! Do you want to exit? (y/n)"),
validate: (input) => {
if (!["y", "n", "Y", "N"].includes(input.trim())) {
return "Please enter 'y' or 'n'.";
}
return true;
},
},
]);
if (exitChoice.trim().toLowerCase() === "y") {
exitGame = true;
console.log(
chalk.yellow("\nThanks for playing! Set sail for more adventures!")
);
break;
} else {
// Continue the loop (play again)
continue;
}
}
// If not lost, always thank and break
console.log(
chalk.yellow("\nThanks for playing! Set sail for more adventures!")
);
break;
}
} catch (error) {
console.error("Error running the CLI:", error.message);
console.log(
"Please ensure you have Node.js 18+ installed and are running in an interactive terminal."
);
process.exit(1);
}
}
main();