Files
ShaiBot/commands/applications/cookies.js
T
2026-08-18 19:45:43 +02:00

1903 lines
39 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const {
SlashCommandBuilder,
EmbedBuilder,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
MessageFlags
} = require("discord.js");
const { randomUUID } = require("node:crypto");
const {
getCollections,
getCookieBalance,
changeCookies,
transferCookies,
getCooldown,
setCooldown,
getUnixTime,
randomInteger,
randomFloat,
randomItem,
formatPercentage
} = require("../../utils/cookieEconomy");
/*
* CONFIG
*/
const COLLECT_COOLDOWN =
60 * 60; // 1 hour
const STEAL_COOLDOWN =
15 * 60; // 15 minutes
const BOSS_SPAWN_COST =
500;
const BOSS_JOIN_DURATION =
30 * 60; // 30 minutes
const BOSS_MINIMUM_PLAYERS =
3;
/*
* Legacy Abra bonus
*/
const ABRA_ID =
"378135175059603457";
const ABRA_MULTIPLIER =
1.5;
/*
* COMMAND
*/
module.exports = {
data: new SlashCommandBuilder()
.setName("cookies")
.setDescription(
"The ShaiBot cookie economy."
)
/*
* /cookies collect
*/
.addSubcommand(subcommand =>
subcommand
.setName("collect")
.setDescription(
"Collect a fresh batch of cookies."
)
)
/*
* /cookies steal
*/
.addSubcommand(subcommand =>
subcommand
.setName("steal")
.setDescription(
"Attempt to steal another user's cookies."
)
.addUserOption(option =>
option
.setName("target")
.setDescription(
"The unfortunate victim."
)
.setRequired(true)
)
)
/*
* /cookies boss spawn
*/
.addSubcommandGroup(group =>
group
.setName("boss")
.setDescription(
"Cookie Boss commands."
)
.addSubcommand(subcommand =>
subcommand
.setName("spawn")
.setDescription(
`Spawn a Cookie Boss for ${BOSS_SPAWN_COST} Cookies.`
)
)
)
/*
* /cookies check
*/
.addSubcommand(subcommand =>
subcommand
.setName("check")
.setDescription(
"Check a cookie balance."
)
.addUserOption(option =>
option
.setName("target")
.setDescription(
"The user whose cookies you want to check."
)
)
)
/*
* /cookies give
*/
.addSubcommand(subcommand =>
subcommand
.setName("give")
.setDescription(
"Give some of your cookies to another user."
)
.addUserOption(option =>
option
.setName("target")
.setDescription(
"The recipient."
)
.setRequired(true)
)
.addIntegerOption(option =>
option
.setName("amount")
.setDescription(
"How many cookies to give."
)
.setRequired(true)
.setMinValue(1)
)
)
/*
* /cookies leaderboard
*/
.addSubcommand(subcommand =>
subcommand
.setName("leaderboard")
.setDescription(
"Show the richest cookie collectors."
)
),
async execute(interaction) {
const group =
interaction.options.getSubcommandGroup(
false
);
const subcommand =
interaction.options.getSubcommand();
/*
* Subcommand groups
*/
if (
group === "boss" &&
subcommand === "spawn"
) {
return spawnBoss(
interaction
);
}
/*
* Normal subcommands
*/
switch (subcommand) {
case "collect":
return collect(
interaction
);
case "steal":
return steal(
interaction
);
case "check":
return check(
interaction
);
case "give":
return give(
interaction
);
case "leaderboard":
return leaderboard(
interaction
);
default:
return interaction.reply({
content:
"Unknown cookies command.",
flags:
MessageFlags.Ephemeral
});
}
}
};
/*
* ============================================================
* /cookies collect
* ============================================================
*/
async function collect(interaction) {
const userId =
interaction.user.id;
const guildId =
interaction.guildId;
const now =
getUnixTime();
const cooldown =
await getCooldown(
userId,
guildId,
"collect"
);
if (
cooldown >
now
) {
return interaction.reply({
content:
`The batch is still cooking. Come back <t:${cooldown}:R> 🍪`,
flags:
MessageFlags.Ephemeral
});
}
let amount =
randomInteger(
1,
20
);
/*
* Legacy Abra bonus
*/
if (
userId ===
ABRA_ID
) {
amount =
Math.ceil(
amount *
ABRA_MULTIPLIER
);
}
await changeCookies(
userId,
amount
);
const nextCollect =
await setCooldown(
userId,
guildId,
"collect",
COLLECT_COOLDOWN
);
const balance =
await getCookieBalance(
userId
);
const embed =
new EmbedBuilder()
.setTitle(
"🍪 Fresh Batch"
)
.setDescription(
randomCollectResponse(
amount,
interaction.user
)
)
.addFields(
{
name:
"Collected",
value:
`**${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}**`,
inline:
true
},
{
name:
"Your Vault",
value:
`**${balance.toLocaleString()} 🍪**`,
inline:
true
},
{
name:
"Next Batch",
value:
`<t:${nextCollect}:R>`,
inline:
false
}
)
.setThumbnail(
interaction.user.displayAvatarURL({
size: 256
})
);
return interaction.reply({
embeds: [
embed
]
});
}
/*
* ============================================================
* /cookies steal
* ============================================================
*/
async function steal(interaction) {
const user =
interaction.user;
const target =
interaction.options.getUser(
"target",
true
);
/*
* Validation
*/
if (
target.id ===
user.id
) {
return interaction.reply({
content:
"You can't steal from yourself.",
flags:
MessageFlags.Ephemeral
});
}
/*
* ShaiBot itself is reserved for
* The Crumb Saucer Vault Heist.
*/
if (
target.id ===
interaction.client.user.id
) {
return interaction.reply({
content:
"ShaiBot's vault requires a proper Vault Heist. Visit `/crumbsaucer`.",
flags:
MessageFlags.Ephemeral
});
}
if (target.bot) {
return interaction.reply({
content:
"Other bots are protected by Cookie Law.",
flags:
MessageFlags.Ephemeral
});
}
/*
* Cooldown
*/
const now =
getUnixTime();
const cooldown =
await getCooldown(
user.id,
interaction.guildId,
"steal"
);
if (
cooldown >
now
) {
return interaction.reply({
content:
`Lay low for a while. You can steal again <t:${cooldown}:R>.`,
flags:
MessageFlags.Ephemeral
});
}
/*
* Balances
*/
const [
yourCookies,
theirCookies
] =
await Promise.all([
getCookieBalance(
user.id
),
getCookieBalance(
target.id
)
]);
if (
yourCookies <
1
) {
return interaction.reply({
content:
"You need at least **1 Cookie** to attempt a robbery.",
flags:
MessageFlags.Ephemeral
});
}
if (
theirCookies <
1
) {
return interaction.reply({
content:
`${target} doesn't have any Cookies.`,
flags:
MessageFlags.Ephemeral
});
}
/*
* Dynamic success chance
*/
const successChance =
getStealChance(
yourCookies,
theirCookies
);
const success =
Math.random() <
successChance;
let embed;
/*
* ========================================================
* SUCCESS
* ========================================================
*/
if (success) {
const {
amount
} =
getStealAmount(
theirCookies
);
try {
await transferCookies(
target.id,
user.id,
amount
);
} catch (error) {
if (
error.message ===
"INSUFFICIENT_COOKIES"
) {
return interaction.reply({
content:
`${target}'s Cookie balance changed before you could finish the robbery.`,
flags:
MessageFlags.Ephemeral
});
}
throw error;
}
const [
yourBalanceAfter,
targetBalanceAfter
] =
await Promise.all([
getCookieBalance(
user.id
),
getCookieBalance(
target.id
)
]);
const actualPercentage =
(
amount /
theirCookies
) *
100;
embed =
new EmbedBuilder()
.setTitle(
"🥷 Cookie Robbery Successful"
)
.setDescription(
randomStealSuccessResponse(
amount,
target
)
)
.addFields(
{
name:
"Cookies Stolen",
value:
`**${amount.toLocaleString()} 🍪**`,
inline:
true
},
{
name:
"Vault Taken",
value:
`**${actualPercentage.toFixed(1)}%**`,
inline:
true
},
{
name:
"Success Chance",
value:
`**${formatPercentage(successChance)}**`,
inline:
true
},
{
name:
"Your Vault",
value:
`**${yourBalanceAfter.toLocaleString()} 🍪**`,
inline:
true
},
{
name:
`${target.displayName}'s Vault`,
value:
`**${targetBalanceAfter.toLocaleString()} 🍪**`,
inline:
true
}
)
.setThumbnail(
target.displayAvatarURL({
size: 256
})
);
}
/*
* ========================================================
* FAILURE
* ========================================================
*/
else {
const penaltyPercentage =
randomFloat(
0.01,
0.08
);
const penalty =
Math.max(
1,
Math.min(
yourCookies,
Math.floor(
yourCookies *
penaltyPercentage
)
)
);
try {
/*
* Normal steal failures reward
* the victim.
*/
await transferCookies(
user.id,
target.id,
penalty
);
} catch (error) {
if (
error.message ===
"INSUFFICIENT_COOKIES"
) {
return interaction.reply({
content:
"Your Cookie balance changed before the robbery could finish.",
flags:
MessageFlags.Ephemeral
});
}
throw error;
}
const [
yourBalanceAfter,
targetBalanceAfter
] =
await Promise.all([
getCookieBalance(
user.id
),
getCookieBalance(
target.id
)
]);
const actualPenaltyPercentage =
(
penalty /
yourCookies
) *
100;
embed =
new EmbedBuilder()
.setTitle(
"🚨 Cookie Robbery Failed"
)
.setDescription(
randomStealFailResponse(
penalty,
target
)
)
.addFields(
{
name:
"Penalty",
value:
`**${penalty.toLocaleString()} 🍪**`,
inline:
true
},
{
name:
"Vault Lost",
value:
`**${actualPenaltyPercentage.toFixed(1)}%**`,
inline:
true
},
{
name:
"Success Chance",
value:
`**${formatPercentage(successChance)}**`,
inline:
true
},
{
name:
"Your Vault",
value:
`**${yourBalanceAfter.toLocaleString()} 🍪**`,
inline:
true
},
{
name:
`${target.displayName}'s Vault`,
value:
`**${targetBalanceAfter.toLocaleString()} 🍪**`,
inline:
true
}
)
.setThumbnail(
target.displayAvatarURL({
size: 256
})
);
}
/*
* Apply cooldown
*/
const nextSteal =
await setCooldown(
user.id,
interaction.guildId,
"steal",
STEAL_COOLDOWN
);
embed.addFields({
name:
"Next Robbery",
value:
`<t:${nextSteal}:R>`,
inline:
false
});
return interaction.reply({
embeds: [
embed
]
});
}
/*
* ============================================================
* /cookies boss spawn
* ============================================================
*/
async function spawnBoss(interaction) {
if (!interaction.guild) {
return interaction.reply({
content:
"Cookie Bosses can only be summoned inside a server.",
flags:
MessageFlags.Ephemeral
});
}
const {
db
} =
getCollections();
const events =
db.collection(
"items_events"
);
/*
* Only one unresolved Cookie Boss per guild.
*/
const existing =
await events.findOne({
eventType:
"CookieBoss",
guildId:
interaction.guildId,
state: {
$in: [
"Joining",
"Starting",
"Active",
"Resolving"
]
}
});
if (existing) {
return interaction.reply({
content:
"There is already an active Cookie Boss in this server.",
flags:
MessageFlags.Ephemeral
});
}
const balance =
await getCookieBalance(
interaction.user.id
);
if (
balance <
BOSS_SPAWN_COST
) {
return interaction.reply({
content:
`You need **${BOSS_SPAWN_COST.toLocaleString()} Cookies** to summon the boss.`,
flags:
MessageFlags.Ephemeral
});
}
/*
* Unique boss encounter.
*/
const eventId =
randomUUID();
/*
* This virtual account holds all committed
* Cookie Boss currency until the event resolves.
*/
const escrowId =
`boss:${eventId}`;
const now =
getUnixTime();
const joinUntil =
now +
BOSS_JOIN_DURATION;
/*
* Spawner cost seeds the reward pool.
*/
try {
await transferCookies(
interaction.user.id,
escrowId,
BOSS_SPAWN_COST
);
} catch (error) {
if (
error.message ===
"INSUFFICIENT_COOKIES"
) {
return interaction.reply({
content:
"Your Cookie balance changed before the boss could be summoned.",
flags:
MessageFlags.Ephemeral
});
}
throw error;
}
/*
* Initial recruitment embed
*/
const embed =
new EmbedBuilder()
.setTitle(
"🍪 The Crumbbound Behemoth Appears!"
)
.setDescription(
[
"*Drawn by the scent of an irresponsibly large Cookie Vault,*",
"*the Crumbbound Behemoth has lumbered into Shaiwase.*",
"",
`**Recruitment ends:** <t:${joinUntil}:R>`,
`**Minimum adventurers:** ${BOSS_MINIMUM_PLAYERS}`,
`**Participants:** 0/${BOSS_MINIMUM_PLAYERS}`,
`**Reward Pool:** ${BOSS_SPAWN_COST.toLocaleString()} 🍪`,
"",
"Choose how many Cookies you want to commit to the fight.",
"Your tier determines your temporary HP and Power.",
"",
"If enough players join, the summoner may start the battle early."
].join("\n")
)
.setFooter({
text:
`cookieBoss:${eventId}`
});
/*
* Join tier buttons
*/
const joinRow =
new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(
"cookieBossJoinScout"
)
.setLabel(
"Scout — 100"
)
.setEmoji(
"🍪"
)
.setStyle(
ButtonStyle.Secondary
),
new ButtonBuilder()
.setCustomId(
"cookieBossJoinFighter"
)
.setLabel(
"Fighter — 500"
)
.setEmoji(
"⚔️"
)
.setStyle(
ButtonStyle.Primary
),
new ButtonBuilder()
.setCustomId(
"cookieBossJoinChampion"
)
.setLabel(
"Champion — 1,000"
)
.setEmoji(
"🔥"
)
.setStyle(
ButtonStyle.Danger
)
);
/*
* Summoner early-start button.
*/
const startRow =
new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(
"cookieBossStart"
)
.setLabel(
"Start Battle"
)
.setEmoji(
"⚔️"
)
.setStyle(
ButtonStyle.Success
)
);
let message;
try {
message =
await interaction.reply({
embeds: [
embed
],
components: [
joinRow,
startRow
],
fetchReply:
true
});
} catch (error) {
/*
* Refund spawn cost if Discord message creation fails.
*/
try {
await transferCookies(
escrowId,
interaction.user.id,
BOSS_SPAWN_COST
);
} catch (refundError) {
console.error(
"CRITICAL: Failed to refund boss spawn after message failure:",
refundError
);
}
throw error;
}
/*
* Persist encounter.
*/
try {
await events.insertOne({
eventType:
"CookieBoss",
eventId,
escrowId,
guildId:
interaction.guildId,
channelId:
interaction.channelId,
messageId:
message.id,
creatorId:
interaction.user.id,
state:
"Joining",
createdAt:
now,
joinUntil,
battleStartedAt:
null,
battleEndsAt:
null,
minimumParticipants:
BOSS_MINIMUM_PLAYERS,
rewardPool:
BOSS_SPAWN_COST,
boss: {
bossId:
"crumbbound-behemoth",
name:
"The Crumbbound Behemoth",
health:
15000,
maxHealth:
15000,
rage:
0
},
participants:
{}
});
} catch (error) {
/*
* If Mongo fails after the message was posted,
* remove the message and refund the creator.
*/
try {
await message.delete();
} catch {}
try {
await transferCookies(
escrowId,
interaction.user.id,
BOSS_SPAWN_COST
);
} catch (refundError) {
console.error(
"CRITICAL: Failed to refund boss spawn after database failure:",
refundError
);
}
throw error;
}
}
/*
* ============================================================
* /cookies check
* ============================================================
*/
async function check(interaction) {
const target =
interaction.options.getUser(
"target"
)
??
interaction.user;
const balance =
await getCookieBalance(
target.id
);
const isHouse =
target.id ===
interaction.client.user.id;
const embed =
new EmbedBuilder()
.setTitle(
isHouse
? "🏦 ShaiBot Cookie Vault"
: "🍪 Cookie Vault"
)
.setDescription(
isHouse
? `The house currently holds **${balance.toLocaleString()} Cookies**.`
: `${target} currently has **${balance.toLocaleString()} Cookie${balance === 1 ? "" : "s"}**.`
)
.setThumbnail(
target.displayAvatarURL({
size: 256
})
);
return interaction.reply({
embeds: [
embed
]
});
}
/*
* ============================================================
* /cookies give
* ============================================================
*/
async function give(interaction) {
const user =
interaction.user;
const target =
interaction.options.getUser(
"target",
true
);
const amount =
interaction.options.getInteger(
"amount",
true
);
if (
target.id ===
user.id
) {
return interaction.reply({
content:
"You cannot give Cookies to yourself.",
flags:
MessageFlags.Ephemeral
});
}
/*
* Keep bot deposits tied to the game systems.
*/
if (target.bot) {
return interaction.reply({
content:
"Bots don't accept direct Cookie transfers. Visit `/crumbsaucer` if you'd like to donate to the house involuntarily.",
flags:
MessageFlags.Ephemeral
});
}
try {
await transferCookies(
user.id,
target.id,
amount
);
} catch (error) {
if (
error.message ===
"INSUFFICIENT_COOKIES"
) {
return interaction.reply({
content:
"You don't have enough Cookies.",
flags:
MessageFlags.Ephemeral
});
}
throw error;
}
const [
yourBalance,
theirBalance
] =
await Promise.all([
getCookieBalance(
user.id
),
getCookieBalance(
target.id
)
]);
const embed =
new EmbedBuilder()
.setTitle(
"🍪 Cookies Shared"
)
.setDescription(
`${user} gave ${target} **${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}**.`
)
.addFields(
{
name:
"Your Vault",
value:
`**${yourBalance.toLocaleString()} 🍪**`,
inline:
true
},
{
name:
`${target.displayName}'s Vault`,
value:
`**${theirBalance.toLocaleString()} 🍪**`,
inline:
true
}
)
.setThumbnail(
target.displayAvatarURL({
size: 256
})
);
return interaction.reply({
embeds: [
embed
]
});
}
/*
* ============================================================
* /cookies leaderboard
* ============================================================
*/
async function leaderboard(interaction) {
const {
cookies
} =
getCollections();
/*
* Restrict to Discord snowflake-style user IDs.
*
* This prevents temporary boss escrow accounts
* such as:
*
* boss:<uuid>
*
* from appearing on the leaderboard.
*/
const results =
await cookies
.find({
userId: {
$regex:
/^\d+$/
},
cookies: {
$gt:
0
}
})
.sort({
cookies:
-1
})
.limit(15)
.toArray();
/*
* Exclude ShaiBot itself from the player
* leaderboard.
*/
const playerResults =
results
.filter(
record =>
record.userId !==
interaction.client.user.id
)
.slice(
0,
10
);
const description =
playerResults.length
? playerResults
.map(
(
record,
index
) => {
return (
`${getLeaderboardMedal(index)} ` +
`<@${record.userId}> — ` +
`**${record.cookies.toLocaleString()}** 🍪`
);
}
)
.join("\n")
: "Nobody has collected any Cookies yet.";
const houseBalance =
await getCookieBalance(
interaction.client.user.id
);
const embed =
new EmbedBuilder()
.setTitle(
"🍪 Cookie Leaderboard"
)
.setDescription(
description
)
.addFields({
name:
"🏦 ShaiBot House Vault",
value:
`**${houseBalance.toLocaleString()} 🍪**`
})
.setTimestamp();
if (
interaction.guild?.iconURL()
) {
embed.setThumbnail(
interaction.guild.iconURL({
size: 256
})
);
}
return interaction.reply({
embeds: [
embed
]
});
}
/*
* ============================================================
* STEAL HELPERS
* ============================================================
*/
function getStealChance(
attackerCookies,
targetCookies
) {
const MIN_CHANCE =
0.15;
const MAX_CHANCE =
0.70;
const BASE_CHANCE =
0.50;
/*
* Wealthier targets become harder to rob.
*/
const targetPenalty =
Math.log10(
targetCookies +
1
)
*
0.06;
/*
* Compare attacker wealth to target wealth.
*/
const wealthRatio =
attackerCookies /
Math.max(
targetCookies,
1
);
/*
* Poorer attackers receive a modest
* catch-up bonus.
*/
const povertyBonus =
wealthRatio <
1
? (
1 -
wealthRatio
)
*
0.18
: 0;
/*
* Rich attackers targeting poorer users
* receive an anti-bullying penalty.
*/
const bullyPenalty =
wealthRatio >
1
? Math.min(
Math.log10(
wealthRatio +
1
)
*
0.12,
0.20
)
: 0;
const chance =
BASE_CHANCE
-
targetPenalty
+
povertyBonus
-
bullyPenalty;
return Math.max(
MIN_CHANCE,
Math.min(
MAX_CHANCE,
chance
)
);
}
function getStealAmount(
targetCookies
) {
const MAX_STEAL_AMOUNT =
5000;
const percentage =
getStealPercentage();
const calculatedAmount =
Math.floor(
targetCookies *
percentage
);
const amount =
Math.max(
1,
Math.min(
calculatedAmount,
targetCookies,
MAX_STEAL_AMOUNT
)
);
return {
amount,
percentage
};
}
function getStealPercentage() {
const roll =
Math.random();
/*
* 50%:
* 1–3%
*/
if (
roll <
0.50
) {
return randomFloat(
0.01,
0.03
);
}
/*
* 30%:
* 3–5%
*/
if (
roll <
0.80
) {
return randomFloat(
0.03,
0.05
);
}
/*
* 15%:
* 5–8%
*/
if (
roll <
0.95
) {
return randomFloat(
0.05,
0.08
);
}
/*
* 5% jackpot:
* 8–10%
*/
return randomFloat(
0.08,
0.10
);
}
/*
* ============================================================
* COLLECT RESPONSES
* ============================================================
*/
function randomCollectResponse(
amount,
user
) {
const cookies =
`**${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}**`;
const responses = [
`${user}, I made you ${cookies} 🍪`,
`${user}, here's your ${cookies} 🍪 I ate the rest, sorry >.<`,
`Only ${cookies} 🍪 for you today ${user}... Abra stole the rest :c`,
`${user}, would you like a glass of milk with your ${cookies}? 🥛`,
`${cookies} 🍪 Careful, ${user}! They're still warm :3`,
`${cookies} 🍪 Best batch yet!`,
`${user}, these ${cookies} I bequeath unto thine own self upon this day.`,
`${user}, your ${cookies} are fresh out of the oven. Try not to burn your tongue!`,
`${user}, I bribed the Cookie Monster to spare these ${cookies} just for you.`,
`${user}, Santa came early! You got ${cookies}. Milk not included.`,
`${user}, your ${cookies} are artisanal, gluten-free, and slightly cursed.`,
`A wild baker appeared! They hand ${user} ${cookies} 🍪`,
`${user}, Abra said these ${cookies} are "totally not stolen"... 🤔`,
`${user}, here's your ${cookies} — watch out, they may be booby-trapped.`
];
return randomItem(
responses
);
}
/*
* ============================================================
* STEAL SUCCESS RESPONSES
* ============================================================
*/
function randomStealSuccessResponse(
amount,
target
) {
const cookies =
`**${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}**`;
const responses = [
`You've successfully stolen ${cookies} from ${target}!`,
`Ninja mode activated. You snatched ${cookies} from ${target} without leaving a crumb behind.`,
`Abra taught you well — ${cookies} from ${target} are now yours!`,
`Like a phantom in the night, you took ${cookies} from ${target}.`,
`You just pulled the sweetest heist: ${cookies} from ${target}!`,
`Mission complete. ${target} is ${cookies} poorer.`,
`You distracted ${target} with a dance and swiped ${cookies}.`,
`Clean getaway! ${target} won't notice the missing ${cookies} until it's too late.`,
`The Cookie Vault has been breached. ${cookies} taken from ${target}.`,
`You emerged from the shadows carrying ${cookies} from ${target}.`
];
return randomItem(
responses
);
}
/*
* ============================================================
* STEAL FAILURE RESPONSES
* ============================================================
*/
function randomStealFailResponse(
amount,
target
) {
const cookies =
`**${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}**`;
const responses = [
`You were caught red-handed by ${target} and lost ${cookies} as a penalty!`,
`Fail! ${target} caught you mid-bite — you dropped ${cookies}.`,
`Not your best moment... You lost ${cookies} to ${target} in the struggle.`,
`Stealth level: 0. You handed ${cookies} to ${target} as an apology.`,
`The Cookie jar was booby-trapped! You gave ${cookies} to ${target} while running away.`,
`Ouch. The guard dog barked, and now ${target} has your ${cookies}.`,
`You tripped over your own shoelace — ${target} picked up ${cookies}.`,
`Caught by the Cookie Police! ${cookies} confiscated and given to ${target}.`,
`Your pockets had holes... ${target} found the ${cookies} you dropped.`
];
return randomItem(
responses
);
}
/*
* ============================================================
* LEADERBOARD
* ============================================================
*/
function getLeaderboardMedal(
index
) {
switch (index) {
case 0:
return "🥇";
case 1:
return "🥈";
case 2:
return "🥉";
default:
return `**${index + 1}.**`;
}
}