diff --git a/src/games/hvacoins/index.js b/src/games/hvacoins/index.js index 4d6a5cc..216f217 100644 --- a/src/games/hvacoins/index.js +++ b/src/games/hvacoins/index.js @@ -15,7 +15,7 @@ const { getRandomFromArray, chaosFilter, addReactions, - game: { nfts, squad, users, horrors }, setHighestCoins, definitelyShuffle + game: { nfts, squad, users, horrors }, setHighestCoins, definitelyShuffle, getCompletedSquadgradeNames } = require('./utils') const slack = require('../../slack') const buyableItems = require('./buyableItems') @@ -24,7 +24,7 @@ const achievements = require('./achievements') const webapi = require('./webapi') const prestige = require('./prestige') const lore = require('./lore') -const { getChaos } = require('./quackstore') +const { getChaos, quackStore } = require('./quackstore') const settings = require('./settings') // const readline = require('readline').createInterface({ @@ -44,7 +44,7 @@ const settings = require('./settings') const getUpgradeEmoji = upgrade => upgrade.emoji || buyableItems[upgrade.type].emoji const upgradeText = (user, showOwned = false) => { const userDoesNotHave = ([upgradeName, upgrade]) => hasUpgrade(user, upgrade, upgradeName) === showOwned - const userMeetsCondition = ([, upgrade]) => upgrade.condition(user) + const userMeetsCondition = ([, upgrade]) => upgrade.condition(user, getCompletedSquadgradeNames()) const format = ([key, value]) => `:${getUpgradeEmoji(value)}: *${key}* - ${commas(value.cost)}\n_${value.description}_` return '\n\n' + Object.entries(upgrades) @@ -86,7 +86,7 @@ const dmsOnly = { const prestigeOnly = { hidden: false, condition: async ({ event, say, words, user }) => { - if (user.prestige) { + if (!user.prestige) { await say('Sorry, you must have prestiged to access this menu.') } return user.prestige && await dmsOnly.condition({ event, say, words, user }) @@ -287,7 +287,13 @@ const messageHandler = async ({ event, say, isRecycle = false }) => { await c.action({ event, say, trueSay, words, user, userId: event.user }) if (!isRecycle) { - setTimeout(() => lightning({ event, say, trueSay, words, user }), 10000) + const userQuackgrades = (user.quackUpgrades?.lightning || []).map(name => quackStore[name]) + const defaultOdds = 0.01 + const odds = userQuackgrades.reduce((total, upgrade) => upgrade.effect(total), defaultOdds) + console.log(odds) + if (Math.random() < odds) { + setTimeout(() => lightning({ event, say, trueSay, words, user }), 10000) + } } } @@ -327,13 +333,8 @@ slack.onReaction(async ({ event }) => { }) const strikes = {} -const lightning = async ({ event }) => { - if (Math.random() > 0.01) { - return - } - if (event.user !== slack.users.Sage) { - await slack.messageSage(`Sent a zap to ${slack.users[event.user]}.`) - } +const lightning = async ({ event, user }) => { + const msToBottle = chaosFilter(5000, 1, user, Infinity, 2500) const message = await slack.app.client.chat.postMessage({ channel: event.channel, text: ':zap: Lightning strike!', @@ -370,7 +371,7 @@ const lightning = async ({ event }) => { blocks: [] }) await slack.messageSage(`They didn't bottle it!`) - }, 5000) + }, msToBottle) } slack.app.action('lightningStrike', async ({ body, ack }) => { @@ -380,20 +381,20 @@ slack.app.action('lightningStrike', async ({ body, ack }) => { } delete strikes[body.message.ts] const c = getCoins(body.user.id) - const secondsOfCps = seconds => Math.floor(getCPS(getUser(body.user.id)) * seconds) - const diff = 500 + Math.floor(c * 0.10) + secondsOfCps(60 * 30) const user = getUser(body.user.id) - user.coins += diff + const secondsOfCps = seconds => Math.floor(getCPS(user) * seconds) + let payout = Math.floor(c * 0.10) + secondsOfCps(60 * 30) + payout = 500 + chaosFilter(payout, 1, user) + user.coins = c + payout saveGame() await slack.app.client.chat.update({ channel: body.channel.id, ts: body.message.ts, - text: `Lightning successfully bottled! :sake::zap: You got ${commas(diff)} HVAC!`, + text: `Lightning successfully bottled! :sake::zap: You got ${commas(payout)} HVAC!`, blocks: [] }) await ack() - await slack.messageSage(`They bottled it!`) }) slack.onMessage(async msg => { @@ -486,15 +487,6 @@ command( timestamp: sent.ts, reactions: ['one', 'two', 'three'] }) - // const addReaction = async emojiName => - // slack.app.client.reactions.add({ - // channel: event.channel, - // timestamp: sent.ts, - // name: emojiName - // }) - // await addReaction('one') - // await addReaction('two') - // await addReaction('three') const ts = sent.message.ts const lastOne = {} @@ -588,7 +580,7 @@ command( if (words[1] !== 'hvacker' && words[1] !== `<@${slack.users.Hvacker}>`) { return } - + if (!settings.horrorEnabled) { return say(`You know? I actually feel okay, thanks`) } @@ -686,10 +678,9 @@ command(['!cps'], say(`You are currently earning \`${commas(getCPS(user))}\` HVAC Coin per second.`)) // What's that one game? Split or Steal? - -command(['!split'], - `Play a game of split or steal.` - , {hidden: true}) +// command(['!split'], +// `Play a game of split or steal.` +// , {hidden: true}) // const maxTemp = 30 @@ -749,16 +740,16 @@ const doMine = async ({ user, userId, say }) => { const secondsOfCps = seconds => Math.floor(getCPS(user) * seconds) let diff let prefix - if (random > 0.9967) { + if (random > 0.9937) { diff = 500 + Math.floor(c * 0.10) + secondsOfCps(60 * 30) prefix = `:gem: You found a lucky gem worth ${commas(diff)} HVAC!\n` addAchievement(user, 'luckyGem', say) await slack.messageSage(`${slack.users[userId]} FOUND A LUCKY GEM COIN WORTH ${commas(diff)} HVAC!`) - } else if (random > 0.986) { + } else if (random > 0.976) { diff = 50 + Math.floor(c * 0.025) + secondsOfCps(60) prefix = `:goldbrick: You found a lucky gold coin worth ${commas(diff)} HVAC!\n` addAchievement(user, 'goldBrick', say) - } else if (random > 0.96) { + } else if (random > 0.95) { diff = 10 + Math.floor(c * 0.01) + secondsOfCps(10) prefix = `:money_with_wings: You found a lucky green coin worth ${commas(diff)} HVAC!\n` addAchievement(user, 'greenCoin', say) @@ -882,7 +873,7 @@ command( if (hasUpgrade(user, upgrade, upgradeName)) { return say('You already have that upgrade!') } - if (!upgrade.condition(user)) { + if (!upgrade.condition(user, getCompletedSquadgradeNames())) { return say('That item does not exist!') } const c = user.coins @@ -941,7 +932,8 @@ command( return say('No squadgrades are currently available') } const currentCoins = user.coins - let amount = parseAll(words[1], currentCoins) + let [, ...amountWords] = words + let amount = parseAll(amountWords.join(' '), currentCoins) if (amount > currentCoins) { return say(`You don't have that much HVAC! You have ${currentCoins}.`) } @@ -953,11 +945,17 @@ command( } squad.upgrades[current.name] -= amount user.coins -= amount - if (squad.upgrades[current.name] <= 0) { + user.squadGradeContributions ??= 0 + user.squadGradeContributions += amount + let status + if (squad.upgrades[current.name] < 1) { squad.upgrades[current.name] = true + status = `\n\nYou now have ${current.name}!` + } else { + status = ` Current status:\n\n${squadText()}` } saveGame() - await say(`Thank you for your contribution of ${commas(amount)} HVAC! Current status:\n\n${squadText()}`) + await say(`Thank you for your contribution of ${commas(amount)} HVAC!${status}`) } ) @@ -970,6 +968,22 @@ command( buyRoute ) +command( + ['!changelog', '!changes'], + `View my current git log`, + async ({ event, }) => { + const command = `git log > ./gitlog` + const child = exec(command) + child.on('close', async () => { + await slack.app.client.files.upload({ + channels: event.channel, + initial_comment: 'Here\'s my current `git log`', + file: createReadStream('./gitlog') + }) + }) + } +) + command( ['!check', '!ch'], 'Check how many coins another player has', @@ -994,7 +1008,8 @@ command( 'Donate coins to a fellow player\n' + ' Send coins by saying \'!gift @player coin_amount\'', async ({ words, say, user }) => { - const [, target, amountText] = words + let [, target, ...amountText] = words + amountText = amountText.join(' ') const amount = parseAll(amountText, user.coins) const targetId = idFromWord(target) if (!amount || amount < 0) { @@ -1006,6 +1021,10 @@ command( if (user.coins < amount) { return say(`You don't have that many coins! You have ${commas(user.coins)} HVAC.`) } + const date = new Date() + if (targetId === slack.users.Nik && date.getFullYear() === 2022 && date.getMonth() === 4) { + return say(`Your generosity is appreciated, but let the guy play for a bit!`) + } if (amountText === 'all' && slack.users.Tyler === targetId) { addAchievement(user, 'walmartGiftCard', say) } @@ -1091,7 +1110,7 @@ command( let index = 1 await say( Object.entries(users) - .filter(([id, user]) => Object.entries(user.items).length > 0) + .filter(([id, user]) => Object.entries(user.items).length > 0 || user.prestige) .sort(([id, user1], [id2, user2]) => { const leftPrestige = getUser(id).prestige const rightPrestige = getUser(id2).prestige @@ -1213,6 +1232,7 @@ command( const exec = require('child_process').exec const fs = require('fs') +const { createReadStream } = require('fs') command( ['!pl'], '!pl `code`\n\n' + diff --git a/src/games/hvacoins/quackstore.js b/src/games/hvacoins/quackstore.js index 2a33e9d..07a7488 100644 --- a/src/games/hvacoins/quackstore.js +++ b/src/games/hvacoins/quackstore.js @@ -37,6 +37,17 @@ const quackStore = { return cps * getChaos(Math.round(user.interactions / 50)) }, cost: 10 + }, + + dryerSheet: { + name: 'Dryer Sheet', + type: 'lightning', + emoji: 'rose', + description: 'Smells nice. Makes lightning twice as likely to strike.', + //+ '_\n_Averages a 26% CPS boost.', + effect: lightningOdds => lightningOdds * 2, + preReqs: ['nuclearFuel'], + cost: 10 } } diff --git a/src/games/hvacoins/upgrades.js b/src/games/hvacoins/upgrades.js index 67d6d66..7530a7b 100644 --- a/src/games/hvacoins/upgrades.js +++ b/src/games/hvacoins/upgrades.js @@ -1,9 +1,49 @@ -const basic = ({ type, description, count, cost }) => ({ +const basic = ({ type, description, count, cost, extraCondition = () => true, effect = cps => cps * 2 }) => ({ type, description, - condition: user => user.items[type] >= count, + condition: (user, squadGrades) => user.items[type] >= count && extraCondition(user, squadGrades), cost, - effect: itemCps => itemCps * 2 + effect +}) + +const evil = ({ type, description, cost }) => basic({ + type, + description, + count: 40, + cost, + extraCondition: (user, squadGrades) => squadGrades?.includes('discardHumanMorals'), +}) + +const heavenly = ({ type, description, cost }) => basic({ + type, + description, + count: 60, + cost, + extraCondition: (user, squadGrades) => squadGrades?.includes('redemption'), +}) + +const baby = ({ type, description, cost }) => basic({ + type, + description, + count: 80, + cost, + extraCondition: (user, squadGrades) => squadGrades?.includes('redemption'), +}) + +const geometry = ({ type, description, cost }) => basic({ + type, + description, + count: 100, + cost, + extraCondition: (user, squadGrades) => squadGrades?.includes('redemption'), +}) + +const universitality = ({ type, description, cost }) => basic({ + type, + description, + count: 100, + cost, + extraCondition: (user, squadGrades) => squadGrades?.includes('redemption'), }) module.exports = { @@ -25,24 +65,44 @@ module.exports = { count: 25, cost: 50_000 }), + rats: evil({ + type: 'mouse', + description: 'Consume the rotten remains of your foes', + cost: 150_000, + }), + hoodedMice: heavenly({ + type: 'mouse', + description: 'These monks have nearly reached enlightenment.', + cost: 1_000_000, + }), fasterComputers: basic({ type: 'accountant', description: 'Accountants can ~steal~ optimize twice as much HVAC!', count: 1, - cost: 11_000 + cost: 11_000, }), lackOfMorality: basic({ type: 'accountant', description: 'Accountants are taking a hint from nearby CEOs.', count: 10, - cost: 200_000 + cost: 200_000, }), widerBrains: basic({ type: 'accountant', description: 'For accountant do double of thinking.', count: 25, - cost: 550_000 + cost: 550_000, + }), + vastLayoffs: evil({ + type: 'accountant', + description: 'The weak are not part of our future.', + cost: 2_450_000, + }), + charityFund: heavenly({ + type: 'accountant', + description: 'THIS one is more than just a tax break.', + cost: 16_333_333, }), biggerBlowhole: basic({ @@ -63,6 +123,16 @@ module.exports = { count: 25, cost: 6_000_000 }), + blightWhales: evil({ + type: 'whale', + description: `Infectious with evil, they swim the ocean spreading their spores.`, + cost: 24_000_000 + }), + whaleChoir: heavenly({ + type: 'whale', + description: `Their cleansing songs reverberate through the sea.`, + cost: 144_000_000 + }), greasyTracks: basic({ type: 'train', @@ -82,6 +152,16 @@ module.exports = { count: 25, cost: 65_000_000 }), + hellTrain: evil({ + type: 'train', + description: 'Shipping blood needed for the ritual.', + cost: 370_000_000 + }), + toyTrain: heavenly({ + type: 'train', + description: 'Something simple. Toot toot!', + cost: 2_220_000_000 + }), gasolineFire: basic({ type: 'fire', @@ -101,6 +181,16 @@ module.exports = { count: 25, cost: 700_000_000 }), + lava: evil({ + type: 'fire', + description: `Hopefully no usurpers have any "accidents".`, + cost: 4_200_000_000 + }), + blueFire: heavenly({ + type: 'fire', + description: `You can hear it singing with delight.`, + cost: 25_200_000_000 + }), spoonerang: basic({ type: 'boomerang', @@ -120,6 +210,16 @@ module.exports = { count: 25, cost: 10_000_000_000 }), + loyalRang: evil({ + type: 'boomerang', + description: `Frequently reports back to your throne on the state of your empire.`, + cost: 60_000_000_000 + }), + youRang: heavenly({ + type: 'boomerang', + description: 'Your arms and legs recede into your body. You bend at the middle. You fly. And for a moment, you are free.', + cost: 360_000_000_000 + }), lunarPower: basic({ type: 'moon', @@ -139,6 +239,16 @@ module.exports = { count: 25, cost: 165_000_000_000 }), + tidalUpheaval: evil({ + type: 'moon', + description: `The hell with the ocean. That's valuable_ *the abstract concept of more power* _we're losing.`, + cost: 865_000_000_000 + }), + newMoon: heavenly({ + type: 'moon', + description: `Build a second moon to provide space for affordable housing.`, + cost: 5_190_000_000_000 + }), glassButterfly: basic({ type: 'butterfly', @@ -158,6 +268,16 @@ module.exports = { count: 25, cost: 2_550_000_000_000 }), + venomousMoths: evil({ + type: 'butterfly', + description: 'Specifically manufactured for their horrifying brain-melt toxins.', + cost: 12_550_000_000_000 + }), + quietingNectar: heavenly({ + type: 'butterfly', + description: 'Calming and extra sweet. Soothes even human ails.', + cost: 75_300_000_000_000 + }), silverMirror: basic({ type: 'mirror', @@ -177,6 +297,16 @@ module.exports = { count: 25, cost: 37_500_000_000_000 }), + crackedMirror: evil({ + type: 'mirror', + description: `YOU SMILE. DO NOT FEAR, THIS IS THE FACE OF A FRIEND.`, + cost: 222_000_000_000_000 + }), + funHouseMirror: heavenly({ + type: 'mirror', + description: `yoU LOok so siLLY IN thesE THINgs`, + cost: 1_330_000_000_000_000 + }), fzero: basic({ type: 'quade', @@ -196,6 +326,16 @@ module.exports = { count: 25, cost: 500_000_000_000_000 }), + thatsNotQuade: evil({ + type: 'quade', + description: `The skinless face lacks even a moustache. Nevertheless, it pledges its allegiance.`, + cost: 3_000_000_000_000_000 + }), + hannahMontanaLinux: heavenly({ + type: 'quade', + description: `The patrician's choice.`, + cost: 18_000_000_000_000_000 + }), latestNode: basic({ type: 'hvacker', @@ -215,6 +355,103 @@ module.exports = { count: 25, cost: 7_000_000_000_000_000 }), + undefinedBehavior: evil({ + type: 'hvacker', + description: `skREEEFDS☐☐☐☐☐it's☐jwtoo☐laate☐☐☐☐☐`, + cost: 42_000_000_000_000_000 + }), + mutualUnderstanding: heavenly({ + type: 'hvacker', + description: `lol fat chance, dummy. Points for trying, though`, + cost: 250_000_000_000_000_000 + }), + + coffee: basic({ + type: 'creator', + description: `Didn't you know? It makes you smarter. No consequencAAAAAA`, + count: 1, + cost: 1_960_000_000_000_000 + }), + bribery: basic({ + type: 'creator', + description: `How much could he be making that a couple bucks won't get me more HVAC?`, + count: 10, + cost: 32_300_000_000_000_000 + }), + vim: basic({ + type: 'creator', + description: `*teleports behind you*`, + count: 25, + cost: 100_000_000_000_000_000 + }), + regrets: evil({ + type: 'creator', + description: `HE HAS NONE. HE LAUGHS.`, + cost: 600_000_000_000_000_000 + }), + goVegan: heavenly({ + type: 'creator', + description: `Unlock your vegan powers.`, + cost: 3_600_000_000_000_000_000 + }), + + angelInvestors: basic({ + type: 'smallBusiness', + description: 'Not so small NOW are we?', + count: 1, + cost: 3_140_000_000_000_000 + }), + officeManager: basic({ + type: 'smallBusiness', + description: 'Sate your laborers with snacks.', + count: 10, + cost: 80_000_000_000_000_000 + }), + undyingLoyalty: basic({ + type: 'smallBusiness', + description: 'Your foolish employees bow to your every whim, regardless of salary.', + count: 25, + cost: 138_000_000_000_000_000 + }), + deathSquad: evil({ + type: 'smallBusiness', + description: `pwease don't unionize uwu :pleading_face:`, + cost: 858_000_000_000_000_000 + }), + coop: heavenly({ + type: 'smallBusiness', + description: `By the people, for the people.`, + cost: 5_140_000_000_000_000_000 + }), + + corporateBuyouts: basic({ + type: 'bigBusiness', + description: 'The cornerstone of any family-run business.', + count: 1, + cost: 28_140_000_000_000_000 + }), + politicalSway: basic({ + type: 'bigBusiness', + description: `What's a bit of lobbying between friends?`, + count: 10, + cost: 560_000_000_000_000_000 + }), + humanDiscontent: basic({ + type: 'bigBusiness', + description: 'A sad populace is a spendy populace!', + count: 25, + cost: 1_372_000_000_000_000_000 + }), + weJustKillPeopleNow: evil({ + type: 'bigBusiness', + description: 'It is extremely difficult to get more evil than we already were. Nevertheless,', + cost: 7_072_000_000_000_000_000 + }), + makePublic: heavenly({ + type: 'bigBusiness', + description: `Downplay immediate profit for more long-term benefits.`, + cost: 42_000_000_000_000_000_000 + }), homage: { type: 'general', @@ -232,13 +469,4 @@ module.exports = { cost: 100_000_000_000_000, effect: (itemCps, user) => itemCps * 1.1 } - - // moreUpgrades: { - // type: 'general', - // description: 'Adds additional upgrades', - // condition: user => Object.entries(user.items).reduce((total, [, countOwned]) => countOwned + total, 0) >= 400, - // emoji: 'cookie', - // cost: 10_000_000_000_000, - // effect: nothing - // }, } diff --git a/src/games/hvacoins/utils.js b/src/games/hvacoins/utils.js index 893f9fc..aba01fb 100644 --- a/src/games/hvacoins/utils.js +++ b/src/games/hvacoins/utils.js @@ -21,16 +21,19 @@ const loadGame = () => { return game } -const chaosFilter = (num, odds, user, max = Infinity) => { +const chaosFilter = (num, odds, user, max = Infinity, min = -Infinity) => { const userQuackgrades = user.quackUpgrades?.cps || [] const hasChaos = userQuackgrades.includes('chaos') - if (!hasChaos || Math.random() < odds) { + if (!hasChaos || Math.random() < odds || !num) { return num } const chaosed = num * getChaos(user) if (chaosed > max) { return max } + if (chaosed < min) { + return min + } return chaosed } @@ -112,11 +115,13 @@ const parseAll = (str, allNum) => { str = str.toLowerCase()?.replace(/,/g, '') - if (str === 'all') { - return allNum - } - switch (str) { + case 'all': + case 'sugma': + case 'ligma': + case 'pulma': + case 'deez': + case 'max_int': case 'my soul': return allNum case 'sex': @@ -238,6 +243,20 @@ const squadUpgrades = { effect: cps => cps * 1.2, cost: 100_000_000_000_000, emoji: 'printer' + }, + discardHumanMorals: { + name: 'Neglect human decency', + description: `Unlocks a new tier of upgrades, but at what cost?`, + effect: cps => cps * 1.1, + cost: 100_000_000_000_000_000, + emoji: 'hole' + }, + redemption: { + name: 'Redemption', + description: 'Can you return from the depths of depravity and save your soul?', + effect: cps => cps * 1.1, + cost: 1_000_000_000_000_000_000, + emoji: 'people_hugging' } } @@ -249,6 +268,11 @@ const getCompletedSquadgrades = () => .filter(squadHas) .map(([, upgrade]) => upgrade) +const getCompletedSquadgradeNames = () => + Object.entries(squadUpgrades) + .filter(squadHas) + .map(([name]) => name) + const prestigeMultiplier = user => 1 + ((user.prestige || 0) * 0.01) const quackGradeMultiplier = user => { @@ -383,5 +407,6 @@ module.exports = { getRandomFromArray, chaosFilter, addReactions, + getCompletedSquadgradeNames, game } diff --git a/src/slack/index.js b/src/slack/index.js index f282510..ec19cf8 100644 --- a/src/slack/index.js +++ b/src/slack/index.js @@ -69,6 +69,7 @@ const users = { U0344TFA7HQ: 'Hvacker', U0X0ZQCN6: 'Caleb', U03BBTD4CQZ: 'Fernando', + U03DF152WUV: 'Nik', Sage: 'U028BMEBWBV', Adam: 'U02U15RFK4Y', @@ -80,6 +81,7 @@ const users = { Caleb: 'U0X0ZQCN6', Hvacker: 'U0344TFA7HQ', Fernando: 'U03BBTD4CQZ', + Nik: 'U03DF152WUV' } const activePolls = {} @@ -157,10 +159,12 @@ app.event('message', async ({ event, context, client, say }) => { const reactCounts = {} Object.entries(reactPosters).forEach(([id, votes]) => { - votes = votes.filter(v => [goodEmoji, hotterEmoji, colderEmoji].includes(v)) + console.log(`VOTES FROM ${id}:`, votes) + votes = votes.filter(v => [goodEmoji, hotterEmoji, colderEmoji].find(emoji => v.startsWith(emoji))) if (votes.length === 1) { - reactCounts[votes[0]] ??= 0 - reactCounts[votes[0]] += 1 + const name = votes[0].replace(/:.*/g, '') + reactCounts[name] ??= 0 + reactCounts[name] += 1 } })