refactored and moved

This commit is contained in:
DeSqBlocki
2026-08-18 20:07:41 +02:00
parent b9a887259a
commit 0f48f09bd9
34 changed files with 3218 additions and 2943 deletions
-1693
View File
File diff suppressed because it is too large Load Diff
+267 -192
View File
@@ -1,5 +1,8 @@
const {
EmbedBuilder
EmbedBuilder,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle
} = require("discord.js");
const {
@@ -23,86 +26,47 @@ const HEIST_COOLDOWN =
/*
* Each approach has a different risk/reward profile.
* All three approaches use the SAME cooldown:
*
* Sneak:
* High success chance
* Small reward
* Small failure penalty
* cooldowns_cookies.heist
*
* Hack:
* Medium success chance
* Medium reward
* Medium failure penalty
*
* Smash:
* Low success chance
* Large reward
* Large failure penalty
* Sneak does NOT have its own cooldown.
* Hack does NOT have its own cooldown.
* Smash does NOT have its own cooldown.
*/
const HEIST_METHODS = {
Sneak: {
baseChance:
0.65,
baseChance: 0.65,
minStealPercent:
0.01,
minStealPercent: 0.01,
maxStealPercent: 0.03,
maxStealAmount: 1500,
maxStealPercent:
0.03,
maxStealAmount:
1500,
minPenaltyPercent:
0.01,
maxPenaltyPercent:
0.04
minPenaltyPercent: 0.01,
maxPenaltyPercent: 0.04
},
Hack: {
baseChance:
0.45,
baseChance: 0.45,
minStealPercent:
0.03,
minStealPercent: 0.03,
maxStealPercent: 0.06,
maxStealAmount: 3500,
maxStealPercent:
0.06,
maxStealAmount:
3500,
minPenaltyPercent:
0.03,
maxPenaltyPercent:
0.07
minPenaltyPercent: 0.03,
maxPenaltyPercent: 0.07
},
Smash: {
baseChance:
0.25,
baseChance: 0.25,
minStealPercent:
0.06,
minStealPercent: 0.06,
maxStealPercent: 0.12,
maxStealAmount: 7500,
maxStealPercent:
0.12,
maxStealAmount:
7500,
minPenaltyPercent:
0.06,
maxPenaltyPercent:
0.12
minPenaltyPercent: 0.06,
maxPenaltyPercent: 0.12
}
};
@@ -117,15 +81,12 @@ async function resolveCookieHeist(
interaction,
methodName
) {
/*
* The original /cookies heist command stores
* the player ID inside the embed footer:
* Validate ownership of this Heist menu.
*
* cookieHeist:123456789
* Footer:
*
* This prevents somebody else from pressing
* another player's heist buttons.
* cookieHeist:<userId>
*/
const footer =
@@ -139,10 +100,8 @@ async function resolveCookieHeist(
type,
ownerId
] =
footer
?.split(":")
??
[];
footer?.split(":")
?? [];
if (
@@ -153,14 +112,14 @@ async function resolveCookieHeist(
interaction,
{
content:
"This isn't your heist. Start your own with `/cookies heist`."
"This isn't your heist. Open your own from `/crumbsaucer`."
}
);
}
/*
* Validate selected method.
* Validate method.
*/
const method =
@@ -182,11 +141,18 @@ async function resolveCookieHeist(
/*
* ========================================================
* CLAIM COOLDOWN
* SHARED HEIST COOLDOWN
* ========================================================
*
* This is atomic so rapid double-clicks can't
* resolve the same heist multiple times.
* IMPORTANT:
*
* This is intentionally always "heist".
*
* It does NOT use:
*
* heistSneak
* heistHack
* heistSmash
*/
const cooldown =
@@ -203,18 +169,18 @@ async function resolveCookieHeist(
interaction,
{
content:
`You can attempt another vault heist <t:${cooldown.next}:R>.`
}
`🏦 The vault is still on high alert.\nYou can attempt another heist <t:${cooldown.next}:R>.`
},
6000
);
}
/*
* ========================================================
* LOCK THE HEIST MESSAGE
* ========================================================
* Lock this Heist interaction immediately.
*
* Remove all buttons immediately.
* Prevents selecting another approach from
* the same message.
*/
await interaction.update({
@@ -234,12 +200,6 @@ async function resolveCookieHeist(
});
/*
* ========================================================
* GET BALANCES
* ========================================================
*/
const playerId =
interaction.user.id;
@@ -247,12 +207,16 @@ async function resolveCookieHeist(
interaction.client.user.id;
/*
* Re-fetch balances because they may have
* changed since the menu was opened.
*/
const [
playerCookies,
vaultCookies
] =
await Promise.all([
getCookieBalance(
playerId
),
@@ -260,15 +224,9 @@ async function resolveCookieHeist(
getCookieBalance(
vaultId
)
]);
/*
* Something may have changed between opening
* the heist menu and pressing the button.
*/
if (
playerCookies < 1 ||
vaultCookies < 1
@@ -282,18 +240,23 @@ async function resolveCookieHeist(
.setDescription(
"There weren't enough Cookies available to resolve the heist."
)
.addFields({
name:
"Next Heist",
value:
`<t:${cooldown.next}:R>`
})
],
components: []
components:
buildReturnButton()
});
}
/*
* Small dramatic pause.
*
* Only one edit happens after this, so this is
* comfortably within Discord rate limits.
* Short theatrical delay.
*/
await sleep(
@@ -303,13 +266,8 @@ async function resolveCookieHeist(
/*
* ========================================================
* VAULT DIFFICULTY
* DYNAMIC VAULT SECURITY
* ========================================================
*
* The richer ShaiBot's vault becomes,
* the harder it is to rob.
*
* Penalty is capped at 15 percentage points.
*/
const securityPenalty =
@@ -339,38 +297,21 @@ async function resolveCookieHeist(
*/
if (success) {
/*
* Roll percentage of the current vault.
*/
const percent =
const rolledPercent =
randomFloat(
method.minStealPercent,
method.maxStealPercent
);
/*
* Apply both:
*
* percentage roll
* AND
* hard maximum
*
* This prevents a massive vault from producing
* absurd payouts.
*/
const amount =
Math.max(
1,
Math.min(
Math.floor(
vaultCookies *
percent
rolledPercent
),
method.maxStealAmount,
@@ -380,28 +321,67 @@ async function resolveCookieHeist(
);
/*
* ShaiBot pays the robber.
*/
await transferCookies(
vaultId,
playerId,
amount
);
const remaining =
await getCookieBalance(
vaultId
try {
await transferCookies(
vaultId,
playerId,
amount
);
} catch (error) {
if (
error.message ===
"INSUFFICIENT_COOKIES"
) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle(
"🏦 Heist Interrupted"
)
.setDescription(
"The ShaiBot Vault changed before you could complete the robbery."
)
.addFields({
name:
"Next Heist",
value:
`<t:${cooldown.next}:R>`
})
],
components:
buildReturnButton()
});
}
throw error;
}
const [
playerAfter,
vaultAfter
] =
await Promise.all([
getCookieBalance(
playerId
),
getCookieBalance(
vaultId
)
]);
const actualPercentage =
(
amount /
vaultCookies
) *
)
*
100;
@@ -410,6 +390,7 @@ async function resolveCookieHeist(
.setTitle(
"💥 Vault Breached!"
)
.setDescription(
getSuccessText(
methodName,
@@ -417,8 +398,8 @@ async function resolveCookieHeist(
amount
)
)
.addFields(
.addFields(
{
name:
"Approach",
@@ -465,10 +446,21 @@ async function resolveCookieHeist(
{
name:
"Vault Remaining",
"Your Vault",
value:
`**${remaining.toLocaleString()} 🍪**`,
`**${playerAfter.toLocaleString()} 🍪**`,
inline:
true
},
{
name:
"House Vault",
value:
`**${vaultAfter.toLocaleString()} 🍪**`,
inline:
true
@@ -482,9 +474,8 @@ async function resolveCookieHeist(
`<t:${cooldown.next}:R>`,
inline:
true
false
}
);
@@ -493,7 +484,8 @@ async function resolveCookieHeist(
embed
],
components: []
components:
buildReturnButton()
});
}
@@ -502,11 +494,6 @@ async function resolveCookieHeist(
* ========================================================
* FAILURE
* ========================================================
*
* Failure fines the player.
*
* Unlike normal player stealing, the fine goes
* directly into ShaiBot's vault.
*/
const penaltyPercent =
@@ -521,7 +508,6 @@ async function resolveCookieHeist(
1,
Math.min(
Math.floor(
playerCookies *
penaltyPercent
@@ -532,11 +518,49 @@ async function resolveCookieHeist(
);
await transferCookies(
playerId,
vaultId,
penalty
);
try {
/*
* Heist fines always go into
* ShaiBot's central vault.
*/
await transferCookies(
playerId,
vaultId,
penalty
);
} catch (error) {
if (
error.message ===
"INSUFFICIENT_COOKIES"
) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle(
"🚨 Heist Failed"
)
.setDescription(
"Your Cookie balance changed before the fine could be collected."
)
.addFields({
name:
"Next Heist",
value:
`<t:${cooldown.next}:R>`
})
],
components:
buildReturnButton()
});
}
throw error;
}
const [
@@ -544,7 +568,6 @@ async function resolveCookieHeist(
vaultAfter
] =
await Promise.all([
getCookieBalance(
playerId
),
@@ -552,15 +575,24 @@ async function resolveCookieHeist(
getCookieBalance(
vaultId
)
]);
const actualPenaltyPercentage =
(
penalty /
playerCookies
)
*
100;
const embed =
new EmbedBuilder()
.setTitle(
"🚨 HEIST FAILED"
)
.setDescription(
getFailureText(
methodName,
@@ -568,8 +600,8 @@ async function resolveCookieHeist(
penalty
)
)
.addFields(
.addFields(
{
name:
"Approach",
@@ -603,6 +635,17 @@ async function resolveCookieHeist(
true
},
{
name:
"Vault Lost",
value:
`**${actualPenaltyPercentage.toFixed(1)}%**`,
inline:
true
},
{
name:
"Your Vault",
@@ -633,9 +676,8 @@ async function resolveCookieHeist(
`<t:${cooldown.next}:R>`,
inline:
true
false
}
);
@@ -644,26 +686,51 @@ async function resolveCookieHeist(
embed
],
components: []
components:
buildReturnButton()
});
}
/*
* ============================================================
* VAULT SECURITY
* RETURN TO CRUMB SAUCER
* ============================================================
*/
function buildReturnButton() {
return [
new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(
"crumbSaucerBack"
)
.setLabel(
"Back to Crumb Saucer"
)
.setEmoji(
"◀️"
)
.setStyle(
ButtonStyle.Secondary
)
)
];
}
/*
* ============================================================
* SECURITY
* ============================================================
*/
function getVaultSecurityPenalty(
vaultCookies
) {
/*
* Logarithmic scaling means the vault gets harder
* as it becomes richer without becoming impossible.
*
* Examples approximately:
* Approximately:
*
* 100 -> 5%
* 1,000 -> 7.5%
@@ -673,10 +740,11 @@ function getVaultSecurityPenalty(
*/
return Math.min(
Math.log10(
vaultCookies + 1
) *
vaultCookies +
1
)
*
0.025,
0.15
@@ -686,18 +754,16 @@ function getVaultSecurityPenalty(
/*
* ============================================================
* FLAVOR TEXT
* PROGRESS TEXT
* ============================================================
*/
function getHeistProgressText(
methodName
) {
switch (
methodName
) {
case "Sneak":
return [
"🥷 You slip through the service entrance...",
@@ -728,65 +794,73 @@ function getHeistProgressText(
}
/*
* ============================================================
* SUCCESS TEXT
* ============================================================
*/
function getSuccessText(
methodName,
user,
amount
) {
const amountText =
const cookies =
`**${amount.toLocaleString()} Cookies**`;
switch (
methodName
) {
case "Sneak":
return (
`🥷 ${user} slipped through security and quietly escaped with ${amountText}.`
`🥷 ${user} slipped through security and quietly escaped with ${cookies}.`
);
case "Hack":
return (
`💻 ${user} convinced the vault terminal that ${amountText} belonged to them.`
`💻 ${user} convinced the vault terminal that ${cookies} belonged to them.`
);
case "Smash":
return (
`💥 ${user} smashed through the Cookie Vault and escaped with ${amountText}!`
`💥 ${user} smashed through the Cookie Vault and escaped with ${cookies}!`
);
default:
return (
`${user} escaped with ${amountText}.`
`${user} escaped with ${cookies}.`
);
}
}
/*
* ============================================================
* FAILURE TEXT
* ============================================================
*/
function getFailureText(
methodName,
user,
penalty
) {
const penaltyText =
const cookies =
`**${penalty.toLocaleString()} Cookies**`;
switch (
methodName
) {
case "Sneak":
return [
`🚨 ${user} stepped directly on the world's loudest Cookie wrapper.`,
"",
`The ShaiBot Vault collected a ${penaltyText} fine.`
`The ShaiBot Vault collected a ${cookies} fine.`
].join("\n");
@@ -794,7 +868,7 @@ function getFailureText(
return [
`🚨 ${user} entered the password incorrectly one too many times.`,
"",
`The ShaiBot Vault collected a ${penaltyText} security fee.`
`The ShaiBot Vault collected a ${cookies} security fee.`
].join("\n");
@@ -802,13 +876,13 @@ function getFailureText(
return [
`🚨 ${user} discovered that the vault door is significantly stronger than expected.`,
"",
`Repairs cost ${penaltyText}. Naturally, you're paying.`
`Repairs cost ${cookies}. Naturally, you're paying.`
].join("\n");
default:
return (
`The heist failed and cost ${user} ${penaltyText}.`
`The heist failed and cost ${user} ${cookies}.`
);
}
}
@@ -832,5 +906,6 @@ function sleep(ms) {
module.exports = {
resolveCookieHeist
resolveCookieHeist,
HEIST_COOLDOWN
};
File diff suppressed because it is too large Load Diff