1387 lines
31 KiB
JavaScript
1387 lines
31 KiB
JavaScript
const {
|
|
SlashCommandBuilder,
|
|
EmbedBuilder,
|
|
MessageFlags
|
|
} = require("discord.js");
|
|
|
|
const {
|
|
getCollections,
|
|
getCookieBalance,
|
|
transferCookies,
|
|
claimCooldown,
|
|
randomFloat,
|
|
randomItem,
|
|
formatPercentage
|
|
} = require("../../utils/cookieEconomy");
|
|
|
|
const {
|
|
trackProfileEvent
|
|
} = require("../../utils/profileSystem");
|
|
|
|
const {
|
|
collectCookies
|
|
} = require("../../utils/crumbCollect");
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* CONFIG
|
|
* ============================================================
|
|
*/
|
|
|
|
const STEAL_COOLDOWN = 15 * 60; // 15 minutes
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* COMMAND
|
|
* ============================================================
|
|
*/
|
|
|
|
module.exports = {
|
|
data:
|
|
new SlashCommandBuilder()
|
|
.setName("cookies")
|
|
.setDescription("The ShaiBot cookie economy.")
|
|
|
|
.addSubcommand(
|
|
subcommand =>
|
|
subcommand
|
|
.setName("collect")
|
|
.setDescription(
|
|
"Collect a fresh batch of cookies."
|
|
)
|
|
)
|
|
|
|
.addSubcommand(
|
|
subcommand =>
|
|
subcommand
|
|
.setName("steal")
|
|
.setDescription(
|
|
"Attempt to steal another user's cookies."
|
|
)
|
|
.addUserOption(
|
|
option =>
|
|
option
|
|
.setName("target")
|
|
.setDescription(
|
|
"The unfortunate victim."
|
|
)
|
|
.setRequired(true)
|
|
)
|
|
)
|
|
|
|
.addSubcommand(
|
|
subcommand =>
|
|
subcommand
|
|
.setName("check")
|
|
.setDescription(
|
|
"Check a cookie balance."
|
|
)
|
|
.addUserOption(
|
|
option =>
|
|
option
|
|
.setName("target")
|
|
.setDescription(
|
|
"The user whose cookies you want to check."
|
|
)
|
|
)
|
|
)
|
|
|
|
.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)
|
|
)
|
|
)
|
|
|
|
.addSubcommand(
|
|
subcommand =>
|
|
subcommand
|
|
.setName("leaderboard")
|
|
.setDescription(
|
|
"Show the richest cookie collectors."
|
|
)
|
|
),
|
|
|
|
async execute(interaction) {
|
|
switch (
|
|
interaction.options.getSubcommand()
|
|
) {
|
|
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 subcommand.",
|
|
flags:
|
|
MessageFlags.Ephemeral
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* /cookies collect
|
|
* ============================================================
|
|
*/
|
|
|
|
async function collect(interaction) {
|
|
const userId =
|
|
interaction.user.id;
|
|
|
|
/*
|
|
* Shared collection logic from crumbCollect.js.
|
|
*
|
|
* This now owns:
|
|
*
|
|
* - Mongo cooldown
|
|
* - collect amount
|
|
* - balance mutation
|
|
* - profile tracking
|
|
*/
|
|
|
|
const result =
|
|
await collectCookies(
|
|
interaction,
|
|
userId
|
|
);
|
|
|
|
|
|
/*
|
|
* ========================================================
|
|
* COOLDOWN
|
|
* ========================================================
|
|
*
|
|
* Keep the existing /cookies behavior:
|
|
* cooldown errors are private/ephemeral.
|
|
*/
|
|
|
|
if (!result.collected) {
|
|
return interaction.reply({
|
|
content:
|
|
`The batch is still cooking. Come back <t:${result.cooldown.next}:R> 🍪`,
|
|
flags:
|
|
MessageFlags.Ephemeral
|
|
});
|
|
}
|
|
|
|
|
|
/*
|
|
* ========================================================
|
|
* SUCCESS EMBED
|
|
* ========================================================
|
|
*
|
|
* Preserve the existing cookies.js embed and random
|
|
* response style.
|
|
*/
|
|
|
|
const embed =
|
|
new EmbedBuilder()
|
|
.setTitle(
|
|
"🍪 Fresh Cookies!"
|
|
)
|
|
.setDescription(
|
|
randomCollectResponse(
|
|
result.amount,
|
|
interaction.user
|
|
)
|
|
)
|
|
.addFields(
|
|
{
|
|
name:
|
|
"Collected",
|
|
value:
|
|
`**+${result.amount.toLocaleString()} 🍪**`,
|
|
inline:
|
|
true
|
|
},
|
|
{
|
|
name:
|
|
"Your Vault",
|
|
value:
|
|
`**${result.balance.toLocaleString()} 🍪**`,
|
|
inline:
|
|
true
|
|
},
|
|
{
|
|
name:
|
|
"Next Batch",
|
|
value:
|
|
`<t:${result.cooldown.next}:R>`,
|
|
inline:
|
|
false
|
|
}
|
|
)
|
|
.setThumbnail(
|
|
interaction.user.displayAvatarURL({
|
|
size: 256
|
|
})
|
|
)
|
|
.setTimestamp();
|
|
|
|
|
|
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:
|
|
"C'mon, don't embarrass yourself. You can't steal from yourself.",
|
|
flags:
|
|
MessageFlags.Ephemeral
|
|
});
|
|
}
|
|
|
|
|
|
if (
|
|
target.bot
|
|
) {
|
|
return interaction.reply({
|
|
content:
|
|
"Bots have highly sophisticated Cookie security systems.",
|
|
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:
|
|
`Shame on you! ${target} doesn't have any Cookies. D:`,
|
|
flags:
|
|
MessageFlags.Ephemeral
|
|
});
|
|
}
|
|
|
|
|
|
/*
|
|
* ========================================================
|
|
* COOLDOWN
|
|
* ========================================================
|
|
*/
|
|
|
|
const cooldown =
|
|
await claimCooldown(
|
|
user.id,
|
|
interaction.guildId,
|
|
"steal",
|
|
STEAL_COOLDOWN
|
|
);
|
|
|
|
|
|
if (
|
|
!cooldown.claimed
|
|
) {
|
|
return interaction.reply({
|
|
content:
|
|
`Lay low for a while. You can steal again <t:${cooldown.next}:R>.`,
|
|
flags:
|
|
MessageFlags.Ephemeral
|
|
});
|
|
}
|
|
|
|
|
|
/*
|
|
* ========================================================
|
|
* DYNAMIC SUCCESS CHANCE
|
|
* ========================================================
|
|
*/
|
|
|
|
const successChance =
|
|
getStealChance(
|
|
yourCookies,
|
|
theirCookies
|
|
);
|
|
|
|
|
|
const success =
|
|
Math.random() <
|
|
successChance;
|
|
|
|
|
|
/*
|
|
* ========================================================
|
|
* 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* Thief stats.
|
|
*/
|
|
|
|
await trackProfileEvent(
|
|
interaction,
|
|
user.id,
|
|
{
|
|
type: "steal_success",
|
|
source: "steal",
|
|
amount,
|
|
targetUserId:
|
|
target.id
|
|
}
|
|
);
|
|
|
|
|
|
await trackProfileEvent(
|
|
interaction,
|
|
user.id,
|
|
{
|
|
type: "cookie_gain",
|
|
source: "steal",
|
|
amount,
|
|
targetUserId:
|
|
target.id
|
|
}
|
|
);
|
|
|
|
|
|
/*
|
|
* Victim stats.
|
|
*/
|
|
|
|
await trackProfileEvent(
|
|
interaction,
|
|
target.id,
|
|
{
|
|
type: "stolen_from_me",
|
|
source: "steal",
|
|
amount,
|
|
thiefUserId:
|
|
user.id
|
|
}
|
|
);
|
|
|
|
await trackProfileEvent(
|
|
interaction,
|
|
target.id,
|
|
{
|
|
type: "cookie_loss",
|
|
source: "steal",
|
|
amount,
|
|
thiefUserId:
|
|
user.id
|
|
}
|
|
);
|
|
|
|
|
|
const [
|
|
yourBalanceAfter,
|
|
targetBalanceAfter
|
|
] =
|
|
await Promise.all([
|
|
getCookieBalance(
|
|
user.id
|
|
),
|
|
getCookieBalance(
|
|
target.id
|
|
)
|
|
]);
|
|
|
|
|
|
const actualPercentage =
|
|
(
|
|
amount /
|
|
theirCookies
|
|
)
|
|
*
|
|
100;
|
|
|
|
|
|
const embed =
|
|
new EmbedBuilder()
|
|
.setTitle(
|
|
"🥷 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.username}'s Vault`,
|
|
value:
|
|
`**${targetBalanceAfter.toLocaleString()} 🍪**`,
|
|
inline:
|
|
true
|
|
},
|
|
{
|
|
name:
|
|
"Next Robbery",
|
|
value:
|
|
`<t:${cooldown.next}:R>`,
|
|
inline:
|
|
false
|
|
}
|
|
)
|
|
.setThumbnail(
|
|
target.displayAvatarURL({
|
|
size: 256
|
|
})
|
|
)
|
|
.setTimestamp();
|
|
|
|
|
|
return interaction.reply({
|
|
embeds: [embed]
|
|
});
|
|
}
|
|
|
|
|
|
/*
|
|
* ========================================================
|
|
* FAILURE
|
|
* ========================================================
|
|
*/
|
|
|
|
const penaltyPercentage =
|
|
randomFloat(
|
|
0.01,
|
|
0.08
|
|
);
|
|
|
|
|
|
const penalty =
|
|
Math.max(
|
|
1,
|
|
Math.min(
|
|
yourCookies,
|
|
Math.floor(
|
|
yourCookies *
|
|
penaltyPercentage
|
|
)
|
|
)
|
|
);
|
|
|
|
|
|
try {
|
|
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;
|
|
}
|
|
|
|
|
|
/*
|
|
* Profile tracking.
|
|
*/
|
|
|
|
await trackProfileEvent(
|
|
interaction,
|
|
user.id,
|
|
{
|
|
type: "cookie_loss",
|
|
source: "steal_penalty",
|
|
amount:
|
|
penalty,
|
|
targetUserId:
|
|
target.id
|
|
}
|
|
);
|
|
|
|
|
|
await trackProfileEvent(
|
|
interaction,
|
|
target.id,
|
|
{
|
|
type: "cookie_gain",
|
|
source: "steal_penalty",
|
|
amount:
|
|
penalty,
|
|
thiefUserId:
|
|
user.id
|
|
}
|
|
);
|
|
|
|
|
|
const [
|
|
yourBalanceAfter,
|
|
targetBalanceAfter
|
|
] =
|
|
await Promise.all([
|
|
getCookieBalance(
|
|
user.id
|
|
),
|
|
getCookieBalance(
|
|
target.id
|
|
)
|
|
]);
|
|
|
|
|
|
const actualPenaltyPercentage =
|
|
(
|
|
penalty /
|
|
yourCookies
|
|
)
|
|
*
|
|
100;
|
|
|
|
|
|
const embed =
|
|
new EmbedBuilder()
|
|
.setTitle(
|
|
"🚨 Robbery Failed!"
|
|
)
|
|
.setDescription(
|
|
randomStealFailResponse(
|
|
penalty,
|
|
target
|
|
)
|
|
)
|
|
.addFields(
|
|
{
|
|
name:
|
|
"Fine",
|
|
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.username}'s Vault`,
|
|
value:
|
|
`**${targetBalanceAfter.toLocaleString()} 🍪**`,
|
|
inline:
|
|
true
|
|
},
|
|
{
|
|
name:
|
|
"Next Robbery",
|
|
value:
|
|
`<t:${cooldown.next}:R>`,
|
|
inline:
|
|
false
|
|
}
|
|
)
|
|
.setThumbnail(
|
|
target.displayAvatarURL({
|
|
size: 256
|
|
})
|
|
)
|
|
.setTimestamp();
|
|
|
|
|
|
return interaction.reply({
|
|
embeds: [embed]
|
|
});
|
|
}
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* /cookies check
|
|
* ============================================================
|
|
*/
|
|
|
|
async function check(interaction) {
|
|
const target =
|
|
interaction.options.getUser(
|
|
"target"
|
|
)
|
|
??
|
|
interaction.user;
|
|
|
|
|
|
const balance =
|
|
await getCookieBalance(
|
|
target.id
|
|
);
|
|
|
|
|
|
const embed =
|
|
new EmbedBuilder()
|
|
.setTitle(
|
|
"🍪 Cookie Vault"
|
|
)
|
|
.setDescription(
|
|
`${target} has **${balance.toLocaleString()} Cookie${balance === 1 ? "" : "s"}**.`
|
|
)
|
|
.addFields({
|
|
name:
|
|
"Vault Balance",
|
|
value:
|
|
`**${balance.toLocaleString()} 🍪**`
|
|
})
|
|
.setThumbnail(
|
|
target.displayAvatarURL({
|
|
size: 256
|
|
})
|
|
)
|
|
.setTimestamp();
|
|
|
|
|
|
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
|
|
});
|
|
}
|
|
|
|
|
|
if (
|
|
target.bot
|
|
) {
|
|
return interaction.reply({
|
|
content:
|
|
"Bots don't need Cookies. Allegedly.",
|
|
flags:
|
|
MessageFlags.Ephemeral
|
|
});
|
|
}
|
|
|
|
|
|
try {
|
|
await transferCookies(
|
|
user.id,
|
|
target.id,
|
|
amount
|
|
);
|
|
}
|
|
|
|
catch (error) {
|
|
if (
|
|
error.message ===
|
|
"INSUFFICIENT_COOKIES"
|
|
) {
|
|
return interaction.reply({
|
|
content:
|
|
"We don't run a charity here. You don't have enough Cookies!",
|
|
flags:
|
|
MessageFlags.Ephemeral
|
|
});
|
|
}
|
|
|
|
|
|
throw error;
|
|
}
|
|
|
|
|
|
/*
|
|
* Sender lifetime giving.
|
|
*/
|
|
|
|
await trackProfileEvent(
|
|
interaction,
|
|
user.id,
|
|
{
|
|
type: "cookies_given",
|
|
source: "give",
|
|
amount,
|
|
targetUserId:
|
|
target.id
|
|
}
|
|
);
|
|
|
|
|
|
/*
|
|
* Recipient lifetime gain.
|
|
*/
|
|
|
|
await trackProfileEvent(
|
|
interaction,
|
|
target.id,
|
|
{
|
|
type: "cookie_gain",
|
|
source: "give",
|
|
amount,
|
|
senderUserId:
|
|
user.id
|
|
}
|
|
);
|
|
|
|
|
|
const [
|
|
senderBalance,
|
|
recipientBalance
|
|
] =
|
|
await Promise.all([
|
|
getCookieBalance(
|
|
user.id
|
|
),
|
|
getCookieBalance(
|
|
target.id
|
|
)
|
|
]);
|
|
|
|
|
|
const embed =
|
|
new EmbedBuilder()
|
|
.setTitle(
|
|
"🍪 Cookies Shared!"
|
|
)
|
|
.setDescription(
|
|
`${user} shared **${amount.toLocaleString()} Cookie${amount === 1 ? "" : "s"}** with ${target}.`
|
|
)
|
|
.addFields(
|
|
{
|
|
name:
|
|
"Transferred",
|
|
value:
|
|
`**${amount.toLocaleString()} 🍪**`,
|
|
inline:
|
|
true
|
|
},
|
|
{
|
|
name:
|
|
"Your Vault",
|
|
value:
|
|
`**${senderBalance.toLocaleString()} 🍪**`,
|
|
inline:
|
|
true
|
|
},
|
|
{
|
|
name:
|
|
`${target.username}'s Vault`,
|
|
value:
|
|
`**${recipientBalance.toLocaleString()} 🍪**`,
|
|
inline:
|
|
true
|
|
}
|
|
)
|
|
.setThumbnail(
|
|
target.displayAvatarURL({
|
|
size: 256
|
|
})
|
|
)
|
|
.setTimestamp();
|
|
|
|
|
|
return interaction.reply({
|
|
embeds: [embed]
|
|
});
|
|
}
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* /cookies leaderboard
|
|
* ============================================================
|
|
*/
|
|
|
|
async function leaderboard(interaction) {
|
|
const {
|
|
cookies
|
|
} =
|
|
getCollections();
|
|
|
|
|
|
const results =
|
|
await cookies
|
|
.find({
|
|
cookies: {
|
|
$gt: 0
|
|
},
|
|
|
|
userId: {
|
|
$ne:
|
|
interaction.client.user.id
|
|
}
|
|
})
|
|
.sort({
|
|
cookies: -1
|
|
})
|
|
.limit(10)
|
|
.toArray();
|
|
|
|
|
|
const description =
|
|
results.length
|
|
? results
|
|
.map(
|
|
(
|
|
record,
|
|
index
|
|
) => {
|
|
const medal =
|
|
getLeaderboardMedal(
|
|
index
|
|
);
|
|
|
|
return (
|
|
`${medal} <@${record.userId}> — ` +
|
|
`**${record.cookies.toLocaleString()}** 🍪`
|
|
);
|
|
}
|
|
)
|
|
.join("\n")
|
|
: "Nobody has collected any Cookies yet.";
|
|
|
|
|
|
const embed =
|
|
new EmbedBuilder()
|
|
.setTitle(
|
|
"🍪 Cookie Leaderboard"
|
|
)
|
|
.setDescription(
|
|
description
|
|
)
|
|
.setTimestamp();
|
|
|
|
|
|
if (
|
|
interaction.guild
|
|
?.iconURL()
|
|
) {
|
|
embed.setThumbnail(
|
|
interaction.guild.iconURL({
|
|
size: 256
|
|
})
|
|
);
|
|
}
|
|
|
|
|
|
return interaction.reply({
|
|
embeds: [embed]
|
|
});
|
|
}
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* 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
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* STEAL CHANCE
|
|
* ============================================================
|
|
*/
|
|
|
|
function getStealChance(
|
|
attackerCookies,
|
|
targetCookies
|
|
) {
|
|
const MIN_CHANCE = 0.15;
|
|
const MAX_CHANCE = 0.70;
|
|
const BASE_CHANCE = 0.50;
|
|
|
|
|
|
const targetPenalty =
|
|
Math.log10(
|
|
targetCookies +
|
|
1
|
|
)
|
|
*
|
|
0.06;
|
|
|
|
|
|
const wealthRatio =
|
|
attackerCookies /
|
|
Math.max(
|
|
targetCookies,
|
|
1
|
|
);
|
|
|
|
|
|
const povertyBonus =
|
|
wealthRatio <
|
|
1
|
|
? (
|
|
1 -
|
|
wealthRatio
|
|
)
|
|
*
|
|
0.18
|
|
: 0;
|
|
|
|
|
|
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
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* STEAL AMOUNT
|
|
* ============================================================
|
|
*/
|
|
|
|
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
|
|
};
|
|
}
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* STEAL PERCENTAGE
|
|
* ============================================================
|
|
*/
|
|
|
|
function getStealPercentage() {
|
|
const roll =
|
|
Math.random();
|
|
|
|
|
|
if (
|
|
roll <
|
|
0.50
|
|
) {
|
|
return randomFloat(
|
|
0.01,
|
|
0.03
|
|
);
|
|
}
|
|
|
|
|
|
if (
|
|
roll <
|
|
0.80
|
|
) {
|
|
return randomFloat(
|
|
0.03,
|
|
0.05
|
|
);
|
|
}
|
|
|
|
|
|
if (
|
|
roll <
|
|
0.95
|
|
) {
|
|
return randomFloat(
|
|
0.05,
|
|
0.08
|
|
);
|
|
}
|
|
|
|
|
|
return randomFloat(
|
|
0.08,
|
|
0.10
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* ============================================================
|
|
* LEADERBOARD MEDALS
|
|
* ============================================================
|
|
*/
|
|
|
|
function getLeaderboardMedal(
|
|
index
|
|
) {
|
|
switch (index) {
|
|
case 0:
|
|
return "🥇";
|
|
|
|
case 1:
|
|
return "🥈";
|
|
|
|
case 2:
|
|
return "🥉";
|
|
|
|
default:
|
|
return `**${index + 1}.**`;
|
|
}
|
|
} |