Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // =============================
- // Incremental Dice Game (All Upgrades Merged)
- // Features: Third Die, Crits, Risk, Random Events, Smart Scaling, Items & Inventory,
- // Prestige Shop + PP, Prestige Shop Rotation, Auto-unlocks, Rebirths, Slots, Relics,
- // Leaderboards, Save/Load JSON export, Autosave, STEAL/GIVE 💰
- // =============================
- // NOTE: This script assumes a Node-like environment for saving (fs) OR a browser environment (localStorage).
- // -----------------------------
- // Configuration & Constants
- // -----------------------------
- const fs = (typeof require !== "undefined") ? require('fs') : null;
- const SAVE_PATH = '/tmp/dice_game_save.json'; // change to a writable path for your environment
- const LOCAL_STORAGE_KEY = 'diceGameSaveData'; // Key for localStorage
- const AUTOSAVE_INTERVAL_MS = 60000; // 30s
- let players = {}; // main in-memory store
- const ROLL_COOLDOWN_MS_BASE = 30000;
- const RISK_COOLDOWN_MS = 25000;
- const STEAL_COOLDOWN_MS = 60000; // 1 minute cooldown on stealing
- const STEAL_BASE_MAX_PERCENT = 0.20; // Max 5% of target's points
- const STEAL_SUCCESS_CHANCE = 0.4; // 40% chance of successful steal
- const PRESTIGE_REQUIREMENT = 2500;
- const PRESTIGE_BONUS = 0.75;
- const THIRD_DIE_BASE_COST = 3500;
- const BASE_CRIT_CHANCE = 0.05;
- const CRIT_MULTIPLIER = 3;
- const CRIT_UPGRADE_BASE_COST = 500;
- const RANDOM_EVENT_CHANCE_BASE = 0.050;
- const ITEM_DROP_CHANCE_BASE = 0.15;
- const RISK_LOSS_PERCENT = 0.75;
- const JACKPOT_FLAT = 150;
- const LUCKY_PERCENT = 0.20;
- const ADMINS = ["dominoguy_"]; // replace with your admin nicknames
- function isAdmin(nick) {
- return ADMINS.includes(nick);
- }
- // Smart scaling
- const LEVEL_SOFT_CAP_MULT = 50;
- const MULT_EXP_BASE = 1.075;
- const DICE_SOFT_CAP = 100;
- const DICE_EXP_BASE = 1.06;
- const CRIT_SOFT_CAP = 20;
- const CRIT_EXP_BASE = 1.12;
- // Items & Pool
- const ITEM_POOL = [
- { id: "lucky_charm", name: "Lucky Charm", weight: 50, description: "+5% crit for next 10 rolls", type: "consumable" },
- { id: "iron_dice", name: "Iron Dice", weight: 35, description: "+2 dice bonus for next 20 rolls", type: "consumable" },
- { id: "wind_token", name: "Wind Token", weight: 14, description: "Removes roll cooldown once", type: "consumable" },
- { id: "golden_sphere", name: "Golden Sphere", weight: 1, nameDisplay: "Golden Sphere", description: "Permanent +1 multiplier (ultra-rare)", type: "perm" }
- ];
- const LUCKY_CHARM_ROLLS = 7;
- const LUCKY_CHARM_CRIT_BONUS = 0.05;
- const IRON_DICE_ROLLS = 20;
- const IRON_DICE_BONUS = 2;
- // Prestige Shop full pool (items include auto-unlock prestige requirement)
- const PRESTIGE_SHOP_POOL = [
- { id: "perm_mult1", cost: 3, name: "Permanent Multiplier +1", desc: "Permanent +1 to multiplier.", unlockPrestige: 0, effect: { permMult: 1 }, rarity: "common" },
- { id: "perm_mult2", cost: 6, name: "Permanent Multiplier +2", desc: "Permanent +2 to multiplier.", unlockPrestige: 5, effect: { permMult: 2 }, rarity: "uncommon" },
- { id: "perm_crit1", cost: 4, name: "Permanent +1% Crit", desc: "Permanent +1% crit chance.", unlockPrestige: 0, effect: { permCrit: 1 }, rarity: "common" },
- { id: "perm_die1", cost: 5, name: "Permanent +1 Dice Bonus", desc: "Permanent +1 dice bonus.", unlockPrestige: 1, effect: { permDice: 1 }, rarity: "common" },
- { id: "event_boost", cost: 10, name: "Random Events +50%", desc: "Permanently +50% random event chance.", unlockPrestige: 3, effect: { permEventBoost: 0.5 }, rarity: "rare" },
- { id: "item_luck", cost: 8, name: "Item Drop Rate +50%", desc: "Permanently +50% item drop chance.", unlockPrestige: 2, effect: { permItemBoost: 0.5 }, rarity: "rare" },
- // Relics (tier 2, high cost)
- { id: "relic_fury", cost: 25, name: "Relic of Fury", desc: "Permanent +25% crit multiplier (multiplicative to CRIT_MULTIPLIER).", unlockPrestige: 8, effect: { relic: "fury" }, rarity: "legendary" },
- { id: "relic_titan", cost: 30, name: "Relic of Titans", desc: "Permanent +2 multiplier.", unlockPrestige: 10, effect: { relic: "titan" }, rarity: "legendary" }
- ];
- // Shop rotation config
- const SHOP_ROTATION_INTERVAL_MS = 24 * 60 * 60 * 1000; // rotate daily
- let shopRotationSeed = Date.now(); // changes periodically to rotate items
- // Slots config
- const SLOTS_COST = 850;
- const SLOTS_PAYOUTS = {
- 3: 10000, // triple match highest
- 2: 2000, // two match
- "jackpot": 50000 // rare same symbol special
- };
- const SLOTS_SYMBOLS = [1,2,3,4,5,6]; // simple numeric symbols
- // Leaderboard config
- const LEADERBOARD_MAX = 10;
- // Utility
- function int(x) { return Math.max(0, Math.floor(x)); }
- function randRange(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
- // -----------------------------
- // Persistence: Save / Load / Export (UPDATED FOR LOCALSTORAGE FALLBACK)
- // -----------------------------
- function savePlayersToDisk(path = SAVE_PATH) {
- const data = JSON.stringify(players);
- // 1. Node.js (fs) Save
- if (fs) {
- try {
- fs.writeFileSync(path, data, 'utf8');
- return true;
- } catch (e) {
- console.error("FS Save failed:", e);
- return false;
- }
- }
- // 2. LocalStorage (Browser Fallback) Save
- if (typeof localStorage !== "undefined") {
- try {
- localStorage.setItem(LOCAL_STORAGE_KEY, data);
- return true;
- } catch (e) {
- console.error("LocalStorage Save failed:", e);
- return false;
- }
- }
- return false; // No persistence method available
- }
- function loadPlayersFromDisk(path = SAVE_PATH) {
- let raw = null;
- // 1. Node.js (fs) Load
- if (fs) {
- try {
- if (fs.existsSync(path)) {
- raw = fs.readFileSync(path, 'utf8');
- }
- } catch (e) {
- console.error("FS Load failed:", e);
- }
- }
- // 2. LocalStorage (Browser Fallback) Load
- if (!raw && typeof localStorage !== "undefined") {
- try {
- raw = localStorage.getItem(LOCAL_STORAGE_KEY);
- } catch (e) {
- console.error("LocalStorage Load failed:", e);
- }
- }
- if (raw) {
- try {
- players = JSON.parse(raw);
- return true;
- } catch (e) {
- console.error("JSON parsing failed:", e);
- return false;
- }
- }
- return false; // No save data found
- }
- function exportPlayers(path) {
- // For local storage, return the JSON string so the user can copy it.
- if (typeof localStorage !== "undefined" && !fs) {
- return JSON.stringify(players);
- }
- // For fs environments, stick to the original file export.
- return savePlayersToDisk(path);
- }
- // Load on startup
- loadPlayersFromDisk();
- // autosave - updated to use both fs and localStorage check
- if (typeof setInterval !== "undefined" && (fs || typeof localStorage !== "undefined")) {
- setInterval(() => {
- const ok = savePlayersToDisk();
- if (!ok) console.warn("Autosave failed.");
- }, AUTOSAVE_INTERVAL_MS);
- }
- // -----------------------------
- // Player init and helpers
- // -----------------------------
- function getPlayer(nick) {
- if (!nick) return null;
- if (!players[nick]) {
- players[nick] = {
- points: 0,
- multiplier: 1,
- diceBonus: 0,
- prestige: 0,
- pp: 0, // Prestige Points
- rebirths: 0, // multi-prestige rebirth currency
- lastRoll: 0,
- extraDie: 0,
- critUpgrades: 0,
- lastRisk: 0,
- lastSteal: 0, // NEW: Steal Cooldown Tracker
- goldenNextRoll: false,
- brokenDiceTurns: 0,
- inventory: {},
- luckyCharmRolls: 0,
- ironDiceRolls: 0,
- windTokens: 0,
- goldenSpheres: 0,
- permMult: 0,
- permCrit: 0,
- permDice: 0,
- permEventBoost: 0,
- permItemBoost: 0,
- cdReduction: 0, // seconds
- relics: {}, // id -> true
- meta: { rolls: 0, crits: 0 } // metrics for achievements/leaderboard
- };
- }
- // Ensure new properties exist for players loaded from old saves
- if (typeof players[nick].lastSteal === 'undefined') players[nick].lastSteal = 0;
- return players[nick];
- }
- function getCritChance(player) {
- let base = BASE_CRIT_CHANCE + (player.critUpgrades * 0.01) + (player.permCrit * 0.01);
- if (player.luckyCharmRolls > 0) base += LUCKY_CHARM_CRIT_BONUS;
- return base;
- }
- // Smart scaling costs
- function getMultCost(player) {
- const level = player.multiplier;
- const base = 50;
- if (level <= LEVEL_SOFT_CAP_MULT) return int(level * base);
- const over = level - LEVEL_SOFT_CAP_MULT;
- const linear = LEVEL_SOFT_CAP_MULT * base;
- return int(linear * Math.pow(MULT_EXP_BASE, over));
- }
- function getDiceCost(player) {
- const nextBonus = player.diceBonus + 1;
- const base = 100;
- if (nextBonus <= DICE_SOFT_CAP) return int(nextBonus * base);
- const over = nextBonus - DICE_SOFT_CAP;
- const linear = DICE_SOFT_CAP * base;
- return int(linear * Math.pow(DICE_EXP_BASE, over));
- }
- function getCritUpgradeCost(player) {
- const next = player.critUpgrades + 1;
- const base = CRIT_UPGRADE_BASE_COST;
- if (next <= CRIT_SOFT_CAP) return int(next * base);
- const over = next - CRIT_SOFT_CAP;
- const linear = CRIT_SOFT_CAP * base;
- return int(linear * Math.pow(CRIT_EXP_BASE, over));
- }
- function getThirdDieCost(player) { return int(THIRD_DIE_BASE_COST); }
- // Item helpers
- function addItemToInventory(player, itemId, count = 1) {
- if (!player.inventory[itemId]) player.inventory[itemId] = 0;
- player.inventory[itemId] += count;
- if (itemId === "wind_token") player.windTokens = player.inventory[itemId];
- if (itemId === "golden_sphere") player.goldenSpheres = player.inventory[itemId];
- }
- function removeItemFromInventory(player, itemId, count = 1) {
- if (!player.inventory[itemId]) return false;
- player.inventory[itemId] = Math.max(0, player.inventory[itemId] - count);
- if (player.inventory[itemId] === 0) delete player.inventory[itemId];
- if (itemId === "wind_token") player.windTokens = player.inventory[itemId] || 0;
- if (itemId === "golden_sphere") player.goldenSpheres = player.inventory[itemId] || 0;
- return true;
- }
- function getItemDefById(itemId) { return ITEM_POOL.find(it => it.id === itemId); }
- function weightedItemDrop() {
- const total = ITEM_POOL.reduce((s, it) => s + (it.weight || 0), 0);
- let r = Math.random() * total;
- for (const it of ITEM_POOL) {
- if (r < (it.weight || 0)) return it;
- r -= (it.weight || 0);
- }
- return null;
- }
- // Shop rotation / availability
- function rotateShopSeed() {
- shopRotationSeed = Date.now();
- }
- // compute available shop items given rotation + player's prestige unlocks
- function getAvailableShopForPlayer(player) {
- const now = Date.now();
- // rotate index based on days since epoch and seed
- const days = Math.floor((now + shopRotationSeed) / SHOP_ROTATION_INTERVAL_MS);
- // simple deterministic shuffle selection: pick items where hash%3 == days%3 to rotate in/out
- const pickBucket = days % 3;
- const pool = PRESTIGE_SHOP_POOL.filter(it => (it.unlockPrestige || 0) <= (player.prestige || 0));
- // apply rotation filter for variety
- const available = pool.filter(it => {
- // simple hash: sum char codes mod 3
- const h = (it.id.split('').reduce((s,c)=>s+c.charCodeAt(0),0)) % 3;
- return h === pickBucket || it.rarity === 'common';
- });
- // ensure there is always at least a few items
- return available.length ? available : pool.slice(0,4);
- }
- // Leaderboards
- function topPlayersByPoints(limit = LEADERBOARD_MAX) {
- const arr = Object.entries(players).map(([nick, p]) => ({ nick, points: p.points || 0 }));
- arr.sort((a,b) => b.points - a.points);
- return arr.slice(0, limit);
- }
- function topPlayersByPrestige(limit = LEADERBOARD_MAX) {
- const arr = Object.entries(players).map(([nick,p]) => ({ nick, prestige: p.prestige || 0 }));
- arr.sort((a,b) => b.prestige - a.prestige);
- return arr.slice(0, limit);
- }
- // Apply relic effects on runtime multipliers
- function applyRelicEffects(player, baseCritMultiplier = CRIT_MULTIPLIER) {
- let critMult = baseCritMultiplier;
- if (player.relics && player.relics["relic_fury"]) {
- critMult = Math.floor(critMult * 1.25); // 25% stronger
- }
- return critMult;
- }
- function relicsGrantMultiplier(player) {
- let add = 0;
- if (player.relics && player.relics["relic_titan"]) add += 2;
- return add;
- }
- // -----------------------------
- // Core message handler
- // -----------------------------
- w.on("msg", (data) => {
- if (!data.msg || !data.msg.startsWith("=")) return;
- const nick = data.nick;
- if (!nick) return;
- const args = data.msg.slice(1).trim().split(/\s+/);
- const cmd = args[0].toLowerCase();
- const player = getPlayer(nick);
- switch (cmd) {
- // ----- ROLL -----
- case "roll": {
- const now = Date.now();
- const baseCooldown = Math.max(1000, ROLL_COOLDOWN_MS_BASE - (player.cdReduction * 1000)); // ms
- const timeLeft = player.lastRoll + baseCooldown - now;
- if (timeLeft > 0) {
- const seconds = (timeLeft / 1000).toFixed(1);
- w.chat.send(`${nick}, wait ${seconds}s. cooldown > 30 sec.`);
- break;
- }
- player.lastRoll = now;
- // roll dice
- const d1 = randRange(1,6);
- const d2 = randRange(1,6);
- const d3 = player.extraDie ? randRange(1,6) : 0;
- const tempDicePenalty = player.brokenDiceTurns > 0 ? 1 : 0;
- const ironDiceTemp = player.ironDiceRolls > 0 ? IRON_DICE_BONUS : 0;
- const effectiveDiceBonus = Math.max(0, player.diceBonus + ironDiceTemp + (player.permDice || 0) - tempDicePenalty);
- const b1 = Math.max(1, d1 + effectiveDiceBonus);
- const b2 = Math.max(1, d2 + effectiveDiceBonus);
- const b3 = player.extraDie ? Math.max(1, d3 + effectiveDiceBonus) : 0;
- const totalRoll = b1 + b2 + b3;
- const goldenPermanentMult = player.goldenSpheres || 0;
- const effectiveMultiplier = (player.multiplier + (player.permMult || 0) + goldenPermanentMult + relicsGrantMultiplier(player));
- const prestigeMult = 1 + (player.prestige * PRESTIGE_BONUS);
- const critChance = getCritChance(player);
- const isCrit = player.goldenNextRoll || (Math.random() < critChance);
- if (player.goldenNextRoll) player.goldenNextRoll = false;
- let critMult = applyRelicEffects(player, CRIT_MULTIPLIER);
- let gained = Math.floor(totalRoll * effectiveMultiplier * prestigeMult);
- if (isCrit) {
- gained = Math.floor(gained * critMult);
- player.meta.crits = (player.meta.crits || 0) + 1;
- }
- player.points += gained;
- player.meta.rolls = (player.meta.rolls || 0) + 1;
- // decrement temporary item-based counters
- if (player.ironDiceRolls > 0) player.ironDiceRolls = Math.max(0, player.ironDiceRolls - 1);
- if (player.luckyCharmRolls > 0) player.luckyCharmRolls = Math.max(0, player.luckyCharmRolls - 1);
- if (player.brokenDiceTurns > 0) player.brokenDiceTurns = Math.max(0, player.brokenDiceTurns - 1);
- // build message
- let msg = `${nick} rolled ${d1} & ${d2}` + (player.extraDie ? ` & ${d3}` : "") +
- ` (+${effectiveDiceBonus} each) → ${b1} & ${b2}` + (player.extraDie ? ` & ${b3}` : "") +
- `. Total ${totalRoll}. Mult: x${effectiveMultiplier}; P mult: x${prestigeMult.toFixed(2)}.`;
- if (isCrit) msg += ` CRIT! x${critMult} applied.`;
- msg += ` Gained ${gained}. Points: ${player.points}.`;
- w.chat.send(msg);
- // Random events
- const eventChance = RANDOM_EVENT_CHANCE_BASE * (1 + (player.permEventBoost || 0));
- if (Math.random() < eventChance) {
- const ev = randRange(0,3);
- switch(ev) {
- case 0: {
- const bonus = Math.max(1, Math.floor(player.points * LUCKY_PERCENT));
- player.points += bonus;
- w.chat.send(`🌟 LUCKY FIND! +${bonus} points. New total: ${player.points}.`);
- break;
- }
- case 1: {
- player.points += JACKPOT_FLAT;
- w.chat.send(`🎰 JACKPOT! +${JACKPOT_FLAT} points. New total: ${player.points}.`);
- break;
- }
- case 2: {
- player.goldenNextRoll = true;
- w.chat.send(`✨ GOLDEN ROLL! Next roll guaranteed crit.`);
- break;
- }
- case 3: {
- player.brokenDiceTurns = Math.max(player.brokenDiceTurns, 1);
- w.chat.send(`⚠️ BROKEN DICE! -1 dice bonus for next roll.`);
- break;
- }
- }
- }
- // Item drops
- const itemDropChance = ITEM_DROP_CHANCE_BASE * (1 + (player.permItemBoost || 0));
- if (Math.random() < itemDropChance) {
- const item = weightedItemDrop();
- if (item) {
- addItemToInventory(player, item.id, 1);
- w.chat.send(`📦 ITEM DROP! You found a ${item.name}: ${item.description}. Use =inventory or =use ${item.id}.`);
- }
- }
- break;
- }
- // ----- BUY MULTIPLIER -----
- case "buymult": {
- const cost = getMultCost(player);
- if (player.points < cost) { w.chat.send(`${nick}, need ${cost} points to upgrade multiplier.`); break; }
- player.points -= cost;
- player.multiplier++;
- w.chat.send(`${nick} upgraded multiplier to x${player.multiplier}. Points left: ${player.points}`);
- break;
- }
- // ----- BUY DICE -----
- case "buydice": {
- const cost = getDiceCost(player);
- if (player.points < cost) { w.chat.send(`${nick}, need ${cost} points to upgrade dice bonus.`); break; }
- player.points -= cost;
- player.diceBonus++;
- w.chat.send(`${nick} upgraded dice bonus to +${player.diceBonus}. Points left: ${player.points}`);
- break;
- }
- // ----- BUY THIRD DIE -----
- case "buy3rd": {
- if (player.extraDie) { w.chat.send(`${nick}, third die already unlocked.`); break; }
- const cost = getThirdDieCost(player);
- if (player.points < cost) { w.chat.send(`${nick}, need ${cost} points to unlock third die.`); break; }
- player.points -= cost;
- player.extraDie = 1;
- w.chat.send(`${nick} unlocked the THIRD DIE! Points left: ${player.points}`);
- break;
- }
- // ----- BUY CRIT UPGRADE -----
- case "buycrit": {
- const cost = getCritUpgradeCost(player);
- if (player.points < cost) { w.chat.send(`${nick}, need ${cost} points to buy crit upgrade.`); break; }
- player.points -= cost;
- player.critUpgrades++;
- w.chat.send(`${nick} bought crit upgrade. Crit chance: ${(getCritChance(player)*100).toFixed(2)}%. Points left: ${player.points}`);
- break;
- }
- // ----- RISK -----
- case "risk": {
- const now = Date.now();
- const timeLeft = player.lastRisk + RISK_COOLDOWN_MS - now;
- if (timeLeft > 0) { const s = (timeLeft/1000).toFixed(1); w.chat.send(`${nick}, wait ${s}s. cooldown > 25 sec.`); break; }
- player.lastRisk = now;
- const d1 = randRange(1,6);
- const d2 = randRange(1,6);
- const d3 = player.extraDie ? randRange(1,6) : 0;
- const effectiveDiceBonus = Math.max(0, player.diceBonus - (player.brokenDiceTurns>0?1:0) + (player.permDice||0));
- const b1 = Math.max(1, d1 + effectiveDiceBonus);
- const b2 = Math.max(1, d2 + effectiveDiceBonus);
- const b3 = player.extraDie ? Math.max(1, d3 + effectiveDiceBonus) : 0;
- const total = b1 + b2 + b3;
- const effMulti = player.multiplier + (player.permMult||0) + (player.goldenSpheres||0) + relicsGrantMultiplier(player);
- const baseGain = Math.floor(total * effMulti * (1 + player.prestige * PRESTIGE_BONUS));
- if (Math.random() < 0.5) {
- const gain = baseGain * 2;
- player.points += gain;
- w.chat.send(`${nick} <start #00A368>RISK WIN!<end> +${gain} points. Total: ${player.points}.`);
- } else {
- const loss = Math.floor(player.points * RISK_LOSS_PERCENT);
- player.points = Math.max(0, player.points - loss);
- w.chat.send(`${nick} <start #BE0039>RISK LOSS!<end> -${loss} points. Total: ${player.points}.`);
- }
- break;
- }
- // ----- STEAL (NEW) -----
- case "steal": {
- const targetNick = args[1];
- if (!targetNick || targetNick.toLowerCase() === nick.toLowerCase()) {
- w.chat.send(`${nick}, usage: =steal <targetNick>`);
- break;
- }
- const target = getPlayer(targetNick);
- if (!target) {
- w.chat.send(`${nick}, target player ${targetNick} not found.`);
- break;
- }
- // Cooldown check
- const now = Date.now();
- const timeLeft = player.lastSteal + STEAL_COOLDOWN_MS - now;
- if (timeLeft > 0) {
- const s = (timeLeft / 1000).toFixed(1);
- w.chat.send(`${nick}, wait ${s}s before attempting another steal.`);
- break;
- }
- player.lastSteal = now;
- // Target points check
- if (target.points < 1) { // allow stealing if target has at least 1 point
- w.chat.send(`${nick} failed to steal from ${targetNick}: Target has too few points.`);
- break;
- }
- // Steal attempt
- if (Math.random() < STEAL_SUCCESS_CHANCE) {
- // Success — no cap
- const stolen = randRange(1, target.points); // steal up to all points
- target.points = Math.max(0, target.points - stolen);
- player.points += stolen;
- w.chat.send(`<start #00A368>SUCCESS!<end> ${nick} stole ${stolen} points from ${targetNick}. ${nick} now has ${player.points}.`);
- } else {
- // Failure - scaled penalty
- const penaltyPercent = 0.15; // lose 15% of own points on failure
- const penalty = Math.max(1, int(player.points * penaltyPercent));
- player.points = Math.max(0, player.points - penalty);
- w.chat.send(`<start #BE0039>FAILURE!<end> ${nick} was caught trying to steal from ${targetNick} and lost ${penalty} points as a fine. Total: ${player.points}.`);
- }
- break;
- }
- // ----- GIVE (NEW) -----
- case "give": {
- const targetNick = args[1];
- const amountStr = args[2];
- const amount = parseInt(amountStr, 10);
- if (!targetNick || isNaN(amount) || amount <= 0) {
- w.chat.send(`${nick}, usage: =give <targetNick> <amount>`);
- break;
- }
- if (targetNick.toLowerCase() === nick.toLowerCase()) {
- w.chat.send(`${nick}, you can't give points to yourself.`);
- break;
- }
- const target = getPlayer(targetNick);
- if (!target) {
- w.chat.send(`${nick}, target player ${targetNick} not found.`);
- break;
- }
- if (player.points < amount) {
- w.chat.send(`${nick}, you only have ${player.points} points. You can't give ${amount}.`);
- break;
- }
- player.points -= amount;
- target.points += amount;
- w.chat.send(`${nick} gave ${amount} points to ${targetNick}. ${nick} left with ${player.points}.`);
- break;
- }
- // ----- PRESTIGE -----
- case "prestige": {
- if (player.points < PRESTIGE_REQUIREMENT) { w.chat.send(`${nick}, need ${PRESTIGE_REQUIREMENT} points to prestige.`); break; }
- player.prestige++;
- player.pp = (player.pp || 0) + 1; // Option A: +1 PP per prestige
- // core reset but preserve perm upgrades, pp, relics, inventory
- player.points = 0;
- player.multiplier = 1;
- player.diceBonus = 0;
- player.extraDie = 0;
- player.critUpgrades = 0;
- player.goldenNextRoll = false;
- player.brokenDiceTurns = 0;
- player.lastRoll = 0;
- player.lastRisk = 0; // Reset risk cooldown
- player.lastSteal = 0; // Reset steal cooldown
- w.chat.send(`${nick} PRESTIGED! Level: ${player.prestige}. +1 PP (total: ${player.pp}).`);
- break;
- }
- // ----- REBIRTH (multi-prestige rebirths) -----
- case "rebirth": {
- // requires at least 10 prestige levels to convert 10 prestige into 1 rebirth (example)
- if (player.prestige < 10) { w.chat.send(`${nick}, you need 10 prestige levels to rebirth.`); break; }
- // consume 10 prestige levels -> +1 rebirth, reset prestige (but keep PP and perm upgrades)
- player.prestige -= 10;
- player.rebirths = (player.rebirths || 0) + 1;
- // apply rebirth permanent bonus: +5% global multiplier boost stored in permMultFraction maybe, we use permMult
- player.permMult = (player.permMult || 0) + 1; // simple powerful bonus
- w.chat.send(`${nick} performed a REBIRTH! Rebirths: ${player.rebirths}. Permanent +1 multiplier granted.`);
- break;
- }
- // ----- SHOP (rotating + auto-unlock per prestige) -----
- case "shop": {
- const avail = getAvailableShopForPlayer(player);
- if (!avail || avail.length === 0) { w.chat.send("Prestige shop is empty."); break; }
- const lines = ["=== PRESTIGE SHOP ==="];
- for (const it of avail) {
- lines.push(`ID:${it.id} Cost:${it.cost} PP — ${it.name} — ${it.desc}`);
- }
- w.chat.send(lines.join(" | "));
- break;
- }
- // ----- BUY (PP) -----
- case "buy": {
- const id = args[1];
- if (!id) { w.chat.send(`${nick}, usage: !buy <id>`); break; }
- // search in available pool (so players can't buy items locked by prestige)
- const avail = getAvailableShopForPlayer(player);
- const item = avail.find(it => it.id === id) || PRESTIGE_SHOP_POOL.find(it => it.id === id);
- if (!item) { w.chat.send(`${nick}, item ${id} not found.`); break; }
- if ((player.pp || 0) < item.cost) { w.chat.send(`${nick}, need ${item.cost} PP. You have ${player.pp || 0}.`); break; }
- player.pp -= item.cost;
- // apply effects
- const eff = item.effect || {};
- if (eff.permMult) player.permMult = (player.permMult || 0) + eff.permMult;
- if (eff.permCrit) player.permCrit = (player.permCrit || 0) + eff.permCrit;
- if (eff.permDice) player.permDice = (player.permDice || 0) + eff.permDice;
- if (eff.permEventBoost) player.permEventBoost = (player.permEventBoost || 0) + eff.permEventBoost;
- if (eff.permItemBoost) player.permItemBoost = (player.permItemBoost || 0) + eff.permItemBoost;
- if (eff.relic) {
- // add relic to player.relics
- player.relics = player.relics || {};
- player.relics[eff.relic] = true;
- player.relics[eff.relic + "_bought_at"] = Date.now();
- }
- w.chat.send(`${nick} purchased ${item.name} for ${item.cost} PP.`);
- break;
- }
- // ----- RELICS LIST -----
- case "relics": {
- const owned = Object.keys(player.relics || {}).filter(k => !k.endsWith('_bought_at'));
- if (!owned.length) { w.chat.send(`${nick}, you own no relics.`); break; }
- w.chat.send(`${nick} relics: ${owned.join(", ")}`);
- break;
- }
- // ----- SHOP ROTATE (admin) -----
- case "rotateshop": {
- // admin command sometimes - here anyone can trigger rotation
- rotateShopSeed();
- w.chat.send("Shop rotation updated.");
- break;
- }
- // ----- SLOTS (slot machine) -----
- case "slots":
- case "slot": {
- // cost to play
- if ((player.points || 0) < SLOTS_COST) { w.chat.send(`${nick}, ${SLOTS_COST} points required to play slots.`); break; }
- player.points -= SLOTS_COST;
- const s1 = SLOTS_SYMBOLS[Math.floor(Math.random() * SLOTS_SYMBOLS.length)];
- const s2 = SLOTS_SYMBOLS[Math.floor(Math.random() * SLOTS_SYMBOLS.length)];
- const s3 = SLOTS_SYMBOLS[Math.floor(Math.random() * SLOTS_SYMBOLS.length)];
- let payout = 0;
- if (s1 === s2 && s2 === s3) payout = SLOTS_PAYOUTS[3];
- else if (s1 === s2 || s1 === s3 || s2 === s3) payout = SLOTS_PAYOUTS[2];
- // super rare jackpot if all three are '6'
- if (s1 === 6 && s2 === 6 && s3 === 6) payout = SLOTS_PAYOUTS["jackpot"];
- if (payout > 0) { player.points += payout; w.chat.send(`${nick} spun ${s1}-${s2}-${s3} and won ${payout} points! Total: ${player.points}.`); }
- else w.chat.send(`${nick} spun ${s1}-${s2}-${s3}. No win.`);
- break;
- }
- // ----- LEADERBOARDS -----
- case "top":
- case "toppoints": {
- const top = topPlayersByPoints(5);
- if (!top.length) { w.chat.send("No players yet."); break; }
- const lines = top.map((t,i) => `${i+1}. ${t.nick}: ${t.points}`);
- w.chat.send("Top Points — " + lines.join(" | "));
- break;
- }
- case "topprestige": {
- const top = topPlayersByPrestige(5);
- if (!top.length) { w.chat.send("No players yet."); break; }
- const lines = top.map((t,i) => `${i+1}. ${t.nick}: Prestige ${t.prestige}`);
- w.chat.send("Top Prestige — " + lines.join(" | "));
- break;
- }
- // ----- INVENTORY & ITEMS -----
- case "items": {
- const list = ITEM_POOL.map(it => `${it.id} — ${it.name}: ${it.description}`).join(" | ");
- w.chat.send(`Available items: ${list}`);
- break;
- }
- case "inventory": {
- const inv = player.inventory;
- if (!inv || Object.keys(inv).length === 0) { w.chat.send(`${nick}, your inventory is empty.`); break; }
- const parts = Object.entries(inv).map(([id,c]) => {
- const def = getItemDefById(id);
- return `${def ? def.name : id} x${c}`;
- });
- w.chat.send(`${nick}'s Inventory: ${parts.join(" — ")}`);
- break;
- }
- case "use": {
- const itemId = args[1];
- if (!itemId) { w.chat.send(`${nick}, usage: !use <itemId>`); break; }
- const def = getItemDefById(itemId);
- if (!def) { w.chat.send(`${nick}, unknown item ${itemId}.`); break; }
- if (!player.inventory[itemId] || player.inventory[itemId] <= 0) { w.chat.send(`${nick}, you don't have ${def.name}.`); break; }
- removeItemFromInventory(player, itemId, 1);
- switch (itemId) {
- case "lucky_charm":
- player.luckyCharmRolls += LUCKY_CHARM_ROLLS;
- w.chat.send(`${nick} used Lucky Charm — +${(LUCKY_CHARM_CRIT_BONUS*100).toFixed(0)}% crit for ${LUCKY_CHARM_ROLLS} rolls.`);
- break;
- case "iron_dice":
- player.ironDiceRolls += IRON_DICE_ROLLS;
- w.chat.send(`${nick} used Iron Dice — +${IRON_DICE_BONUS} dice bonus for ${IRON_DICE_ROLLS} rolls.`);
- break;
- case "wind_token":
- player.lastRoll = 0;
- w.chat.send(`${nick} used Wind Token — roll cooldown reset. Use !roll now.`);
- break;
- case "golden_sphere":
- player.goldenSpheres = (player.goldenSpheres || 0) + 1;
- w.chat.send(`${nick} used Golden Sphere — permanent +1 multiplier acquired.`);
- break;
- default:
- w.chat.send(`${nick} used ${def.name}.`);
- }
- break;
- }
- // ----- STATS -----
- case "stats": {
- const prestigeMult = (1 + player.prestige * PRESTIGE_BONUS + (player.goldenSpheres||0)).toFixed(2);
- const critPct = (getCritChance(player) * 100).toFixed(2);
- w.chat.send(`${nick}'s Stats — Points:${player.points}, Mult:x${player.multiplier}+${player.permMult}, Dice:+${player.diceBonus}+${player.permDice}, Prestige:${player.prestige} (x${prestigeMult}), PP:${player.pp||0}, Rebirths:${player.rebirths||0}, Crit:${critPct}%`);
- break;
- }
- // ----- SAVE / LOAD / EXPORT (UPDATED) -----
- case "save": {
- if (!isAdmin(nick)) {
- w.chat.send(`${nick}, you don't have permission to save the game.`);
- break;
- }
- const ok = savePlayersToDisk();
- if (ok) { w.chat.send("Save successful."); }
- else if (!fs && typeof localStorage === "undefined") { w.chat.send("Save not available in this environment."); }
- else { w.chat.send("Save failed (check server logs/console)."); }
- break;
- }
- case "load": {
- if (!isAdmin(nick)) {
- w.chat.send(`${nick}, you don't have permission to load the game.`);
- break;
- }
- const ok = loadPlayersFromDisk();
- if (ok) { w.chat.send("Load successful."); }
- else if (!fs && typeof localStorage === "undefined") { w.chat.send("Load not available in this environment."); }
- else { w.chat.send("Load failed or no save found."); }
- break;
- }
- // ----- QUIT (permanent self-delete) -----
- case "quit": {
- const confirm = args[1];
- if (confirm !== "confirm") {
- w.chat.send(`${nick}, type "=quit confirm" to permanently delete your data.`);
- break;
- }
- if (!players[nick]) {
- w.chat.send(`${nick}, you are not in the game.`);
- break;
- }
- // Remove from memory
- delete players[nick];
- // Save updated players to disk / localStorage
- const ok = savePlayersToDisk();
- if (!ok) console.warn(`Failed to save after ${nick} quit.`);
- w.chat.send(`${nick} has quit the game permanently. ${nick}'s data deleted.`);
- break;
- }
- // ----- REMOVE PLAYER (admin-only) -----
- case "remove": {
- if (!isAdmin(nick)) {
- w.chat.send(`${nick}, you don't have permission to remove players.`);
- break;
- }
- const targetNick = args[1];
- if (!targetNick) {
- w.chat.send(`${nick}, usage: =remove <playerNick>`);
- break;
- }
- if (!players[targetNick]) {
- w.chat.send(`${nick}, player ${targetNick} not found.`);
- break;
- }
- // Remove the target player from memory
- delete players[targetNick];
- // Save updated players to disk / localStorage
- const ok = savePlayersToDisk();
- if (!ok) console.warn(`Failed to save after ${nick} removed ${targetNick}.`);
- w.chat.send(`Player ${targetNick} has been permanently removed from the game by ${nick}.`);
- break;
- }
- case "export": {
- const path = args[1] || SAVE_PATH;
- const result = exportPlayers(path);
- if (typeof result === 'string') {
- // LocalStorage / Browser environment export
- w.chat.send(`Exported Player Data: Copy the following JSON string to save your game manually: ${result}`);
- } else if (result === true) {
- // FS environment export
- w.chat.send(`Exported save to ${path}`);
- } else {
- w.chat.send("Export failed.");
- }
- break;
- }
- // ----- HELP -----
- case "help": {
- w.chat.send("Commands: =roll =stats =buymult =buydice =buy3rd =buycrit =risk =prestige =rebirth =shop =buy <id> =slots =items =inventory =use <id> **=steal <nick> =give <nick> <amt>** =toppoints =topprestige =save =load =export");
- break;
- }
- default:
- // unknown command — ignore silently to match previous behavior
- break;
- }
- });
- // -----------------------------
- // End of script
- // -----------------------------
Add Comment
Please, Sign In to add comment