Initial commit: Enhanced IRC bot with duck hunt game
- Added hunt/feed duck mechanics (80% hunt, 20% feed) - Implemented persistent scoring system - Added channel control commands (\!stopducks/\!startducks) - Enhanced duck hunt with wrong action penalties - Organized bot structure with botmain.js as main file - Added comprehensive documentation (README.md) - Included 17 plugins with various games and utilities 🦆 Duck Hunt Features: - Hunt ducks with \!shoot/\!bang (80% of spawns) - Feed ducks with \!feed (20% of spawns) - Persistent scores saved to JSON - Channel-specific controls for #bakedbeans - Reaction time tracking and special achievements 🎮 Other Games: - Casino games (slots, coinflip, hi-lo, scratch cards) - Multiplayer games (pigs, zombie dice, quiplash) - Text generation (babble, conspiracy, drunk historian) - Interactive features (story writing, emojify, combos) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
a3ed25f8dd
39 changed files with 12360 additions and 0 deletions
692
plugins/zombie_dice_plugin(2).js
Normal file
692
plugins/zombie_dice_plugin(2).js
Normal file
|
|
@ -0,0 +1,692 @@
|
|||
// plugins/zombiedice.js - Zombie Dice multiplayer game plugin
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
init(bot) {
|
||||
console.log('Zombie Dice plugin initialized');
|
||||
this.bot = bot;
|
||||
|
||||
// Persistent player statistics (saved to file)
|
||||
this.playerStats = new Map(); // nick -> { totalBrains, bestGame, totalGames, totalTurns, name }
|
||||
|
||||
// Current game scores (reset each game)
|
||||
this.gameScores = new Map(); // nick -> { brains, eliminated }
|
||||
|
||||
// Game state with multiplayer support
|
||||
this.gameState = {
|
||||
phase: 'idle', // 'idle', 'joining', 'active'
|
||||
players: [],
|
||||
joinTimer: null,
|
||||
startTime: null,
|
||||
currentPlayer: 0,
|
||||
channel: null,
|
||||
joinTimeLimit: 30000, // 30 seconds
|
||||
warningTime: 25000, // Warning at 25 seconds (5 sec left)
|
||||
// Current turn state
|
||||
turnBrains: 0,
|
||||
turnShotguns: 0,
|
||||
footstepsDice: [], // Actual dice objects that rolled footsteps
|
||||
diceCup: []
|
||||
};
|
||||
|
||||
// Zombie dice definitions
|
||||
this.diceTypes = {
|
||||
green: {
|
||||
count: 6,
|
||||
faces: ['🧠', '🧠', '🧠', '👣', '👣', '💥'],
|
||||
color: 'Green'
|
||||
},
|
||||
yellow: {
|
||||
count: 4,
|
||||
faces: ['🧠', '🧠', '👣', '👣', '👣', '💥'],
|
||||
color: 'Yellow'
|
||||
},
|
||||
red: {
|
||||
count: 3,
|
||||
faces: ['🧠', '👣', '👣', '💥', '💥', '💥'],
|
||||
color: 'Red'
|
||||
}
|
||||
};
|
||||
|
||||
// Set up score file path
|
||||
this.scoresFile = path.join(__dirname, 'zombiedice_scores.json');
|
||||
|
||||
// Load existing scores
|
||||
this.loadScores();
|
||||
},
|
||||
|
||||
cleanup(bot) {
|
||||
console.log('Zombie Dice plugin cleaned up');
|
||||
// Clear any timers
|
||||
if (this.gameState.joinTimer) {
|
||||
clearTimeout(this.gameState.joinTimer);
|
||||
}
|
||||
// Save scores before cleanup
|
||||
this.saveScores();
|
||||
// Clear any active games on cleanup
|
||||
this.resetGameState();
|
||||
// Clear game scores
|
||||
if (this.gameScores) {
|
||||
this.gameScores.clear();
|
||||
}
|
||||
},
|
||||
|
||||
// Reset game state to idle
|
||||
resetGameState() {
|
||||
if (this.gameState.joinTimer) {
|
||||
clearTimeout(this.gameState.joinTimer);
|
||||
}
|
||||
this.gameState = {
|
||||
phase: 'idle',
|
||||
players: [],
|
||||
joinTimer: null,
|
||||
startTime: null,
|
||||
currentPlayer: 0,
|
||||
channel: null,
|
||||
joinTimeLimit: 30000,
|
||||
warningTime: 25000,
|
||||
turnBrains: 0,
|
||||
turnShotguns: 0,
|
||||
footstepsDice: [],
|
||||
diceCup: []
|
||||
};
|
||||
},
|
||||
|
||||
// Load scores from file
|
||||
loadScores() {
|
||||
try {
|
||||
if (fs.existsSync(this.scoresFile)) {
|
||||
const data = fs.readFileSync(this.scoresFile, 'utf8');
|
||||
const scoresObject = JSON.parse(data);
|
||||
|
||||
// Convert back to Map
|
||||
this.playerStats = new Map(Object.entries(scoresObject));
|
||||
console.log(`🧟 Loaded ${this.playerStats.size} zombie dice player stats from ${this.scoresFile}`);
|
||||
|
||||
// Log top 3 players if any exist
|
||||
if (this.playerStats.size > 0) {
|
||||
const topPlayers = Array.from(this.playerStats.values())
|
||||
.sort((a, b) => b.totalBrains - a.totalBrains)
|
||||
.slice(0, 3);
|
||||
console.log('🏆 Top zombie players:', topPlayers.map(p => `${p.name}(${p.totalBrains})`).join(', '));
|
||||
}
|
||||
} else {
|
||||
console.log(`🧟 No existing zombie dice scores file found, starting fresh`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ Error loading zombie dice scores:`, error);
|
||||
this.playerStats = new Map(); // Reset to empty if load fails
|
||||
}
|
||||
},
|
||||
|
||||
// Save scores to file
|
||||
saveScores() {
|
||||
try {
|
||||
// Convert Map to plain object for JSON serialization
|
||||
const scoresObject = Object.fromEntries(this.playerStats);
|
||||
const data = JSON.stringify(scoresObject, null, 2);
|
||||
|
||||
fs.writeFileSync(this.scoresFile, data, 'utf8');
|
||||
console.log(`💾 Saved ${this.playerStats.size} zombie dice player stats to ${this.scoresFile}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Error saving zombie dice scores:`, error);
|
||||
}
|
||||
},
|
||||
|
||||
// Update a player's persistent stats and save to file
|
||||
updatePlayerStats(nick, updates) {
|
||||
// Ensure playerStats Map exists
|
||||
if (!this.playerStats) {
|
||||
this.playerStats = new Map();
|
||||
}
|
||||
|
||||
if (!this.playerStats.has(nick)) {
|
||||
this.playerStats.set(nick, {
|
||||
totalBrains: 0,
|
||||
bestGame: 0,
|
||||
totalGames: 0,
|
||||
totalTurns: 0,
|
||||
name: nick
|
||||
});
|
||||
}
|
||||
|
||||
const player = this.playerStats.get(nick);
|
||||
Object.assign(player, updates);
|
||||
|
||||
// Save to file after each update
|
||||
this.saveScores();
|
||||
},
|
||||
|
||||
// Initialize player for current game
|
||||
initGamePlayer(nick) {
|
||||
// Ensure gameScores Map exists
|
||||
if (!this.gameScores) {
|
||||
this.gameScores = new Map();
|
||||
}
|
||||
|
||||
this.gameScores.set(nick, {
|
||||
brains: 0,
|
||||
eliminated: false
|
||||
});
|
||||
|
||||
// Initialize persistent stats if new player
|
||||
if (!this.playerStats.has(nick)) {
|
||||
this.updatePlayerStats(nick, {
|
||||
totalBrains: 0,
|
||||
bestGame: 0,
|
||||
totalGames: 0,
|
||||
totalTurns: 0,
|
||||
name: nick
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Start the join timer
|
||||
startJoinTimer(target) {
|
||||
this.gameState.startTime = Date.now();
|
||||
|
||||
// Set warning timer (25 seconds)
|
||||
setTimeout(() => {
|
||||
if (this.gameState.phase === 'joining') {
|
||||
this.bot.say(target, '⏰ 5 seconds left to join the zombie hunt!');
|
||||
}
|
||||
}, this.gameState.warningTime);
|
||||
|
||||
// Set game start timer (30 seconds)
|
||||
this.gameState.joinTimer = setTimeout(() => {
|
||||
this.startGame(target);
|
||||
}, this.gameState.joinTimeLimit);
|
||||
},
|
||||
|
||||
// Start the actual game
|
||||
startGame(target) {
|
||||
if (this.gameState.players.length < 2) {
|
||||
this.bot.say(target, '😞 Nobody else joined the zombie hunt. Game cancelled.');
|
||||
this.resetGameState();
|
||||
this.gameScores.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize all players
|
||||
this.gameState.players.forEach(nick => {
|
||||
this.initGamePlayer(nick);
|
||||
});
|
||||
|
||||
// Initialize dice cup
|
||||
this.initializeDiceCup();
|
||||
|
||||
// Start the game
|
||||
this.gameState.phase = 'active';
|
||||
this.gameState.currentPlayer = 0;
|
||||
|
||||
const playerList = this.gameState.players.join(' vs ');
|
||||
this.bot.say(target, `🧟 Zombie hunt starting! ${playerList} - ${this.gameState.players[0]} goes first!`);
|
||||
this.bot.say(target, `🎯 Goal: Collect 13 brains to win! But 3 shotguns = bust!`);
|
||||
},
|
||||
|
||||
// Initialize the dice cup
|
||||
initializeDiceCup() {
|
||||
this.gameState.diceCup = [];
|
||||
|
||||
// Add all dice to cup
|
||||
Object.entries(this.diceTypes).forEach(([type, data]) => {
|
||||
for (let i = 0; i < data.count; i++) {
|
||||
this.gameState.diceCup.push({ type, faces: data.faces, color: data.color });
|
||||
}
|
||||
});
|
||||
|
||||
// Shuffle the cup
|
||||
this.shuffleDiceCup();
|
||||
},
|
||||
|
||||
// Shuffle dice cup
|
||||
shuffleDiceCup() {
|
||||
for (let i = this.gameState.diceCup.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[this.gameState.diceCup[i], this.gameState.diceCup[j]] = [this.gameState.diceCup[j], this.gameState.diceCup[i]];
|
||||
}
|
||||
},
|
||||
|
||||
// Draw dice from cup
|
||||
drawDice(count) {
|
||||
const drawn = [];
|
||||
for (let i = 0; i < count && this.gameState.diceCup.length > 0; i++) {
|
||||
drawn.push(this.gameState.diceCup.pop());
|
||||
}
|
||||
return drawn;
|
||||
},
|
||||
|
||||
// Roll dice and return results
|
||||
rollDice(dice) {
|
||||
return dice.map(die => {
|
||||
const face = die.faces[Math.floor(Math.random() * die.faces.length)];
|
||||
return { ...die, result: face };
|
||||
});
|
||||
},
|
||||
|
||||
// Start a new turn
|
||||
startTurn(target, playerName) {
|
||||
this.gameState.turnBrains = 0;
|
||||
this.gameState.turnShotguns = 0;
|
||||
this.gameState.footstepsDice = [];
|
||||
|
||||
// If cup is low, refill and shuffle
|
||||
if (this.gameState.diceCup.length < 3) {
|
||||
this.initializeDiceCup();
|
||||
}
|
||||
|
||||
// Draw 3 dice and roll for first roll of turn
|
||||
const dice = this.drawDice(3);
|
||||
const results = this.rollDice(dice);
|
||||
|
||||
this.processRoll(target, playerName, results, true); // true = first roll
|
||||
},
|
||||
|
||||
// Process dice roll results
|
||||
processRoll(target, playerName, results, isFirstRoll = false) {
|
||||
let brains = 0;
|
||||
let shotguns = 0;
|
||||
let newFootstepsDice = [];
|
||||
|
||||
// Count results and track footsteps dice
|
||||
results.forEach(die => {
|
||||
switch (die.result) {
|
||||
case '🧠':
|
||||
brains++;
|
||||
break;
|
||||
case '💥':
|
||||
shotguns++;
|
||||
break;
|
||||
case '👣':
|
||||
newFootstepsDice.push(die); // Keep the actual die object
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Update turn totals
|
||||
this.gameState.turnBrains += brains;
|
||||
this.gameState.turnShotguns += shotguns;
|
||||
this.gameState.footstepsDice = newFootstepsDice; // Replace with new footsteps dice
|
||||
|
||||
// Show roll results with colored dice - IRC color codes: Green=3, Yellow=8, Red=4
|
||||
const rollDisplay = results.map(die => {
|
||||
let colorCode;
|
||||
switch (die.color) {
|
||||
case 'Green': colorCode = '\x0303'; break; // Green text
|
||||
case 'Yellow': colorCode = '\x0308'; break; // Yellow text
|
||||
case 'Red': colorCode = '\x0304'; break; // Red text
|
||||
default: colorCode = '';
|
||||
}
|
||||
return `${die.result}${colorCode}(${die.color.charAt(0)})\x0F`; // \x0F resets color
|
||||
}).join(' ');
|
||||
|
||||
// Send detailed results to player
|
||||
let detailMsg = `Roll: ${rollDisplay} | Brains: ${this.gameState.turnBrains} | Shotguns: ${this.gameState.turnShotguns}`;
|
||||
if (this.gameState.footstepsDice.length > 0) {
|
||||
const footstepsDisplay = this.gameState.footstepsDice.map(die => {
|
||||
let colorCode;
|
||||
switch (die.color) {
|
||||
case 'Green': colorCode = '\x0303'; break;
|
||||
case 'Yellow': colorCode = '\x0308'; break;
|
||||
case 'Red': colorCode = '\x0304'; break;
|
||||
default: colorCode = '';
|
||||
}
|
||||
return `${colorCode}👣(${die.color.charAt(0)})\x0F`;
|
||||
}).join(' ');
|
||||
detailMsg += ` | Holding: ${footstepsDisplay}`;
|
||||
}
|
||||
this.bot.notice(playerName, detailMsg);
|
||||
|
||||
// Check for bust (3+ shotguns)
|
||||
if (this.gameState.turnShotguns >= 3) {
|
||||
this.bot.say(target, `${playerName}: 💥💥💥 SHOTGUNNED! Lost ${this.gameState.turnBrains} brains!`);
|
||||
this.nextTurn(target);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if can continue
|
||||
const totalDiceAvailable = this.gameState.footstepsDice.length + this.gameState.diceCup.length;
|
||||
if (totalDiceAvailable < 3) {
|
||||
// Not enough dice to make 3, must stop
|
||||
this.bot.say(target, `${playerName}: Not enough dice left! Auto-banking ${this.gameState.turnBrains} brains.`);
|
||||
this.bankBrains(target, playerName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.gameState.turnBrains === 0) {
|
||||
this.bot.say(target, `${playerName}: No brains yet - use !zombie to keep rolling!`);
|
||||
} else {
|
||||
this.bot.say(target, `${playerName}: Use !zombie to keep rolling or !brain to bank your ${this.gameState.turnBrains} brains!`);
|
||||
}
|
||||
},
|
||||
|
||||
// Bank brains and end turn
|
||||
bankBrains(target, playerName) {
|
||||
const playerGame = this.gameScores.get(playerName);
|
||||
playerGame.brains += this.gameState.turnBrains;
|
||||
|
||||
this.bot.say(target, `${playerName}: Banked ${this.gameState.turnBrains} brains! Total: ${playerGame.brains}`);
|
||||
|
||||
// Check for win
|
||||
if (playerGame.brains >= 13) {
|
||||
this.bot.say(target, `🧟♂️ ${playerName} WINS with ${playerGame.brains} brains! 🧟♂️`);
|
||||
this.endGame(target);
|
||||
return;
|
||||
}
|
||||
|
||||
this.nextTurn(target);
|
||||
},
|
||||
|
||||
// Move to next player's turn
|
||||
nextTurn(target) {
|
||||
// Update turn stats for current player
|
||||
const currentPlayerName = this.gameState.players[this.gameState.currentPlayer];
|
||||
const playerStats = this.playerStats.get(currentPlayerName);
|
||||
this.updatePlayerStats(currentPlayerName, {
|
||||
totalTurns: playerStats.totalTurns + 1
|
||||
});
|
||||
|
||||
// IMPORTANT: Reset all turn state before moving to next player
|
||||
this.gameState.turnBrains = 0;
|
||||
this.gameState.turnShotguns = 0;
|
||||
this.gameState.footstepsDice = [];
|
||||
|
||||
// Move to next player
|
||||
this.gameState.currentPlayer = (this.gameState.currentPlayer + 1) % this.gameState.players.length;
|
||||
const nextPlayer = this.gameState.players[this.gameState.currentPlayer];
|
||||
|
||||
this.bot.say(target, `🧟 ${nextPlayer}'s turn to hunt zombies!`);
|
||||
this.startTurn(target, nextPlayer);
|
||||
},
|
||||
|
||||
// End the game
|
||||
endGame(target) {
|
||||
// Ensure gameScores exists before processing
|
||||
if (!this.gameScores) {
|
||||
this.gameScores = new Map();
|
||||
}
|
||||
|
||||
// Save final scores to persistent stats
|
||||
this.gameState.players.forEach(playerName => {
|
||||
const gameScore = this.gameScores.get(playerName);
|
||||
const playerStats = this.playerStats.get(playerName);
|
||||
|
||||
if (gameScore && playerStats) {
|
||||
const updates = {
|
||||
totalBrains: playerStats.totalBrains + gameScore.brains,
|
||||
totalGames: playerStats.totalGames + 1
|
||||
};
|
||||
|
||||
// Update best game if this was better
|
||||
if (gameScore.brains > playerStats.bestGame) {
|
||||
updates.bestGame = gameScore.brains;
|
||||
this.bot.say(target, `🏆 ${playerName} set a new personal best: ${gameScore.brains} brains!`);
|
||||
}
|
||||
|
||||
this.updatePlayerStats(playerName, updates);
|
||||
console.log(`🧟 Saved game stats for ${playerName}: brains=${gameScore.brains}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Clear current game data
|
||||
this.gameScores.clear();
|
||||
|
||||
this.bot.say(target, '🏁 Zombie hunt ended! Use !zombie to start new hunt.');
|
||||
this.resetGameState();
|
||||
},
|
||||
|
||||
commands: [
|
||||
{
|
||||
name: 'zombie',
|
||||
description: 'Start/join zombie dice game or roll dice during play',
|
||||
execute: function(context, bot) {
|
||||
const plugin = module.exports;
|
||||
const target = context.replyTo;
|
||||
const from = context.nick;
|
||||
const to = context.channel || context.replyTo;
|
||||
|
||||
// Only allow channel play
|
||||
if (!context.channel) {
|
||||
bot.say(target, 'Zombie dice games can only be played in channels!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle different game phases
|
||||
switch (plugin.gameState.phase) {
|
||||
case 'idle':
|
||||
// Start new game and join timer
|
||||
plugin.gameState.phase = 'joining';
|
||||
plugin.gameState.players = [from];
|
||||
plugin.gameState.channel = to;
|
||||
|
||||
bot.say(target, `🧟 ${from} started a zombie hunt! Others have 30 seconds to join with !zombie`);
|
||||
plugin.startJoinTimer(target);
|
||||
break;
|
||||
|
||||
case 'joining':
|
||||
// Player wants to join during countdown
|
||||
if (plugin.gameState.players.includes(from)) {
|
||||
bot.say(target, `${from}: You're already in the hunt!`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.gameState.players.length >= 6) { // Max 6 players
|
||||
bot.say(target, `${from}: Hunt is full! (Max 6 players)`);
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.gameState.players.push(from);
|
||||
bot.say(target, `🧟 ${from} joined the hunt! (${plugin.gameState.players.length} players)`);
|
||||
break;
|
||||
|
||||
case 'active':
|
||||
// Game is running - handle continue rolling
|
||||
if (!plugin.gameState.players.includes(from)) {
|
||||
bot.say(target, `${from}: You're not in the current hunt!`);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPlayerName = plugin.gameState.players[plugin.gameState.currentPlayer];
|
||||
if (from !== currentPlayerName) {
|
||||
bot.say(target, `${from}: It's ${currentPlayerName}'s turn!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Continue rolling - re-roll footsteps dice + draw new ones to make 3 total
|
||||
const footstepsCount = plugin.gameState.footstepsDice.length;
|
||||
const neededDice = 3 - footstepsCount;
|
||||
|
||||
// Check if we have enough dice
|
||||
if (neededDice > plugin.gameState.diceCup.length) {
|
||||
bot.say(target, `${from}: Not enough dice left to continue! Auto-banking.`);
|
||||
plugin.bankBrains(target, from);
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw new dice and combine with footsteps
|
||||
const newDice = plugin.drawDice(neededDice);
|
||||
const allDice = [...plugin.gameState.footstepsDice, ...newDice];
|
||||
|
||||
// Roll all 3 dice (footsteps + new)
|
||||
const results = plugin.rollDice(allDice);
|
||||
plugin.processRoll(target, from, results, false); // false = not first roll
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'brain',
|
||||
description: 'Bank your brains and end your turn in zombie dice',
|
||||
execute: function(context, bot) {
|
||||
const plugin = module.exports;
|
||||
const target = context.replyTo;
|
||||
const from = context.nick;
|
||||
|
||||
if (plugin.gameState.phase !== 'active' || !plugin.gameState.players.includes(from)) {
|
||||
bot.say(target, `${from}: You're not in an active zombie hunt!`);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPlayerName = plugin.gameState.players[plugin.gameState.currentPlayer];
|
||||
if (from !== currentPlayerName) {
|
||||
bot.say(target, `${from}: It's ${currentPlayerName}'s turn!`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.gameState.turnBrains === 0) {
|
||||
bot.say(target, `${from}: No brains to bank!`);
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.bankBrains(target, from);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'quitzombie',
|
||||
description: 'Quit the current zombie dice game',
|
||||
execute: function(context, bot) {
|
||||
const plugin = module.exports;
|
||||
const target = context.replyTo;
|
||||
const from = context.nick;
|
||||
|
||||
if (plugin.gameState.phase === 'idle') {
|
||||
bot.say(target, `${from}: No zombie hunt to quit!`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!plugin.gameState.players.includes(from)) {
|
||||
bot.say(target, `${from}: You're not in the current hunt!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove player from game
|
||||
plugin.gameState.players = plugin.gameState.players.filter(p => p !== from);
|
||||
bot.say(target, `${from} fled the zombie hunt!`);
|
||||
|
||||
// Check if game should continue
|
||||
if (plugin.gameState.phase === 'joining') {
|
||||
if (plugin.gameState.players.length === 0) {
|
||||
bot.say(target, 'Hunt cancelled - no survivors left.');
|
||||
plugin.resetGameState();
|
||||
}
|
||||
} else if (plugin.gameState.phase === 'active') {
|
||||
if (plugin.gameState.players.length <= 1) {
|
||||
if (plugin.gameState.players.length === 1) {
|
||||
bot.say(target, `${plugin.gameState.players[0]} wins by survival!`);
|
||||
}
|
||||
plugin.endGame(target);
|
||||
} else {
|
||||
// Adjust current player if needed
|
||||
if (plugin.gameState.currentPlayer >= plugin.gameState.players.length) {
|
||||
plugin.gameState.currentPlayer = 0;
|
||||
}
|
||||
plugin.nextTurn(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'topzombie',
|
||||
description: 'Show top zombie dice players',
|
||||
execute: function(context, bot) {
|
||||
const plugin = module.exports;
|
||||
const target = context.replyTo;
|
||||
|
||||
if (plugin.playerStats.size === 0) {
|
||||
bot.say(target, 'No zombie dice scores recorded yet! Use !zombie to start hunting.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to array and sort by total brains
|
||||
const sortedScores = Array.from(plugin.playerStats.values())
|
||||
.sort((a, b) => b.totalBrains - a.totalBrains)
|
||||
.slice(0, 5); // Top 5
|
||||
|
||||
bot.say(target, '🧟♂️ Top Zombie Hunters:');
|
||||
|
||||
sortedScores.forEach((player, index) => {
|
||||
const rank = index + 1;
|
||||
const trophy = rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : `${rank}.`;
|
||||
bot.say(target, `${trophy} ${player.name}: ${player.totalBrains} brains (best: ${player.bestGame}, ${player.totalGames} hunts)`);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'zombiestatus',
|
||||
description: 'Show current zombie dice game status',
|
||||
execute: function(context, bot) {
|
||||
const plugin = module.exports;
|
||||
const target = context.replyTo;
|
||||
|
||||
if (plugin.gameState.phase === 'idle') {
|
||||
bot.say(target, 'No active zombie hunt. Use !zombie to start hunting!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.gameState.phase === 'joining') {
|
||||
const timeLeft = Math.ceil((plugin.gameState.joinTimeLimit - (Date.now() - plugin.gameState.startTime)) / 1000);
|
||||
bot.say(target, `🧟 Joining phase: ${plugin.gameState.players.join(', ')} (${timeLeft}s left)`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.gameState.phase === 'active') {
|
||||
const currentPlayer = plugin.gameState.players[plugin.gameState.currentPlayer];
|
||||
let statusMsg = `🧟♂️ Active hunt: `;
|
||||
|
||||
plugin.gameState.players.forEach((player, i) => {
|
||||
const gameScore = plugin.gameScores.get(player);
|
||||
if (i > 0) statusMsg += ' vs ';
|
||||
statusMsg += `${player}(${gameScore.brains})`;
|
||||
});
|
||||
|
||||
bot.say(target, statusMsg);
|
||||
|
||||
let turnMsg = `🎲 ${currentPlayer}'s turn | Turn brains: ${plugin.gameState.turnBrains} | Shotguns: ${plugin.gameState.turnShotguns}/3`;
|
||||
if (plugin.gameState.footstepsDice.length > 0) {
|
||||
const footstepsDisplay = plugin.gameState.footstepsDice.map(die => {
|
||||
let colorCode;
|
||||
switch (die.color) {
|
||||
case 'Green': colorCode = '\x0303'; break;
|
||||
case 'Yellow': colorCode = '\x0308'; break;
|
||||
case 'Red': colorCode = '\x0304'; break;
|
||||
default: colorCode = '';
|
||||
}
|
||||
return `${colorCode}👣(${die.color.charAt(0)})\x0F`;
|
||||
}).join(' ');
|
||||
turnMsg += ` | Holding: ${footstepsDisplay}`;
|
||||
}
|
||||
bot.say(target, turnMsg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'resetzombie',
|
||||
description: 'Reset all zombie dice scores (admin command)',
|
||||
execute: function(context, bot) {
|
||||
const plugin = module.exports;
|
||||
const target = context.replyTo;
|
||||
const from = context.nick;
|
||||
|
||||
// Simple admin check
|
||||
const adminNicks = ['admin', 'owner', 'cancerbot', 'megasconed'];
|
||||
|
||||
if (!adminNicks.includes(from)) {
|
||||
bot.say(target, `${from}: Access denied - admin only command`);
|
||||
return;
|
||||
}
|
||||
|
||||
const playerCount = plugin.playerStats.size;
|
||||
plugin.playerStats.clear();
|
||||
plugin.saveScores();
|
||||
|
||||
bot.say(target, `🗑️ ${from}: Reset ${playerCount} zombie dice player stats`);
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue