const fs = require('fs') const jokes = require('../jokes') const achievements = require('./achievements'); const buyableItems = require("./buyableItems"); const upgrades = require("./upgrades"); const quackStore = require("./quackstore"); const saveFile = 'hvacoins.json' const logError = msg => msg ? console.error(msg) : () => { /* Don't log empty message */ } const loadGame = () => parseOr(fs.readFileSync('./' + saveFile, 'utf-8'), () => ({ users: {}, nfts: [], squad: {} })) const parseOr = (parseable, orFunc) => { try { return JSON.parse(parseable) } catch (e) { logError(e) return orFunc() } } let saves = 0 const saveGame = () => { if (saves % 100 === 0) { fs.writeFileSync('./backups/' + saveFile + new Date().toLocaleString().replace(/[^a-z0-9]/gi, '_'), JSON.stringify(game)) } saves += 1 fs.writeFileSync('./' + saveFile, JSON.stringify(game, null, 2)) } const maybeNews = say => { const random = Math.random() if (random > 0.98) { const prefixedSay = msg => console.log(`Sent news update: '${msg}'`) || say('_Breaking news:_\n' + msg) setTimeout(() => jokes.newsAlert(prefixedSay).catch(logError), 3000) } else if (random > 0.96) { setTimeout(async () => say('_Say have you heard this one?_'), 3000) setTimeout(() => jokes.tellJoke(say).catch(logError), 4000) } } const idFromWord = word => { if (!word?.startsWith('<@') || !word.endsWith('>')) { return null } return word.substring(2, word.length - 1) } const getSeconds = () => new Date().getTime() / 1000 const commas = num => num.toLocaleString() const game = loadGame() const { users, nfts, squad } = game const setHighestCoins = userId => { const prevMax = users[userId].highestEver || 0 if (prevMax < users[userId].coins) { users[userId].highestEver = users[userId].coins } } const addAchievement = (user, achievementName, say) => { if (!achievements[achievementName]) { logError(`Achievement ${achievementName} does not exist!`) return } if (user.achievements[achievementName]) { return } setTimeout(async () => { user.achievements[achievementName] = true saveGame() await say(`You earned the achievement ${achievements[achievementName].name}!`) }, 500) } const getUser = userId => { if (!users[userId]) { users[userId] = { coins: 0, items: {}, upgrades: {}, achievements: {}, coinsAllTime: 0, prestige: 0 } } else { users[userId].items ??= {} users[userId].upgrades ??= {} users[userId].achievements ??= {} users[userId].coinsAllTime ??= users[userId].coins users[userId].prestige ??= 0 } return users[userId] } const getCoins = userId => { const user = getUser(userId) const currentTime = getSeconds() const lastCheck = user.lastCheck || currentTime const secondsPassed = currentTime - lastCheck const increase = getCPS(userId) * secondsPassed user.coins += increase user.coinsAllTime += increase user.coins = Math.floor(user.coins) user.lastCheck = currentTime setHighestCoins(userId) saveGame() return user.coins } const getCPS = userId => { const user = getUser(userId) const userItems = user?.items || {} return Math.round(Object.keys(userItems).reduce((total, itemName) => total + getItemCps(user, itemName), 0)) } const getItemCps = (user, itemName) => (user.items[itemName] || 0) * singleItemCps(user, itemName) const squadUpgrades = { tastyKeyboards: { name: 'Tasty Keyboards', description: 'Delicious and sticky. Boosts CPS by 20% for everyone.', effect: cps => Math.ceil(cps * 1.2), cost: 10_000_000_000_000, emoji: 'keyboard' }, copyPasteMacro: { name: 'Copy-Paste Macro.', description: 'Don\'t actually use this. Boosts CPS by 20% for everyone.', effect: cps => Math.ceil(cps * 1.2), cost: 100_000_000_000_000, emoji: 'printer' } } const squadHas = ([name]) => squad.upgrades[name] === true const squadIsMissing = name => !squadHas(name) const getCompletedSquadgrades = () => Object.entries(squadUpgrades) .filter(squadHas) .map(([, upgrade]) => upgrade) const singleItemCps = (user, itemName) => { const baseCps = buyableItems[itemName].earning const itemUpgrades = (user.upgrades[itemName] || []).map(name => upgrades[name]) const itemUpgradeCps = itemUpgrades.reduce((totalCps, upgrade) => upgrade.effect(totalCps, user), baseCps) const userGeneralUpgrades = user.upgrades.general || [] const generalUpgradeCps = Object.entries(userGeneralUpgrades).reduce((total, [, upgradeName]) => upgrades[upgradeName].effect(total, user), itemUpgradeCps) const achievementCount = Object.keys(user.achievements || {}).length const achievementMultiplier = Math.pow(1.01, achievementCount) const userQuackgrades = user.quackUpgrades?.cps || [] const quackMultiplier = userQuackgrades.reduce((total, upgrade) => quackStore[upgrade].effect(total, user), 1) const prestigeMultiplier = 1 + ((user.prestige || 0) * 0.01) return achievementMultiplier * quackMultiplier * prestigeMultiplier * getCompletedSquadgrades().reduce((cps, upgrade) => upgrade.effect(cps), generalUpgradeCps) } module.exports = { saveGame, logError, parseOr, maybeNews, idFromWord, commas, setHighestCoins, addAchievement, getCoins, getUser, singleItemCps, getCPS, getItemCps, squadUpgrades, squadIsMissing, game }