added more achievments

This commit is contained in:
DeSqBlocki
2026-08-22 22:10:41 +02:00
parent ae35f61522
commit ca823726e3
12 changed files with 4977 additions and 1998 deletions
-60
View File
@@ -1,60 +0,0 @@
const {
EmbedBuilder
} = require("discord.js");
function delay(ms) {
return new Promise(
resolve =>
setTimeout(
resolve,
ms
)
);
}
module.exports = {
name:
"abort",
description:
"abort it",
async execute(
interaction
) {
const embed =
new EmbedBuilder()
.setTitle(
"Close"
)
.setDescription(
"Closing..."
);
await interaction.update({
embeds: [
embed
],
components: []
});
await delay(
2000
);
try {
await interaction.deleteReply();
}
catch {
// Ignore if already deleted/expired.
}
}
}
+91
View File
@@ -0,0 +1,91 @@
const {
EmbedBuilder
} = require(
"discord.js"
);
const {
temporaryEphemeralReply
} = require(
"../../utils/cookieEconomy"
);
const {
getCrumbSaucerSessionOwner
} = require(
"../../utils/crumbSaucer"
);
function delay(
ms
) {
return new Promise(
resolve =>
setTimeout(
resolve,
ms
)
);
}
module.exports = {
name:
"crumbSaucerClose",
async execute(
interaction
) {
const ownerId =
getCrumbSaucerSessionOwner(
interaction
);
if (
ownerId !==
interaction.user.id
) {
return temporaryEphemeralReply(
interaction,
{
content:
"This isn't your Crumb Saucer session."
}
);
}
const embed =
new EmbedBuilder()
.setTitle(
"Closed"
)
.setDescription(
"Closing Crumb Saucer..."
);
await interaction.update({
embeds: [
embed
],
components: []
});
await delay(
1500
);
try {
await interaction.deleteReply();
}
catch {}
}
};
+3 -3
View File
@@ -1,7 +1,7 @@
const {
collectCookies
collectCookiesFromSaucer
} = require(
"../../commands/applications/cookies"
"../../utils/crumbCollect"
);
@@ -13,7 +13,7 @@ module.exports = {
async execute(
interaction
) {
return collectCookies(
return collectCookiesFromSaucer(
interaction
);
}
+20
View File
@@ -0,0 +1,20 @@
const {
showCollectPage
} = require(
"../../utils/crumbCollect"
);
module.exports = {
name:
"crumbSaucerCollectMenu",
async execute(
interaction
) {
return showCollectPage(
interaction
);
}
};
File diff suppressed because it is too large Load Diff
+135 -212
View File
@@ -19,83 +19,86 @@ const {
} = require("./profileSystem");
/*
* ============================================================
* CONFIG
* ============================================================
*/
const HEIST_COOLDOWN =
60 * 60; // 1 hour
60 * 60;
/*
* All three approaches use the SAME cooldown:
*
* cooldowns_cookies.heist
*
* 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,
maxStealPercent: 0.03,
maxStealAmount: 1500,
minStealPercent:
0.01,
minPenaltyPercent: 0.01,
maxPenaltyPercent: 0.04,
maxPenaltyAmount: 750
maxStealPercent:
0.03,
maxStealAmount:
1500,
minPenaltyPercent:
0.01,
maxPenaltyPercent:
0.04,
maxPenaltyAmount:
750
},
Hack: {
baseChance: 0.45,
baseChance:
0.45,
minStealPercent: 0.03,
maxStealPercent: 0.06,
maxStealAmount: 3500,
minStealPercent:
0.03,
minPenaltyPercent: 0.03,
maxPenaltyPercent: 0.07,
maxPenaltyAmount: 2000
maxStealPercent:
0.06,
maxStealAmount:
3500,
minPenaltyPercent:
0.03,
maxPenaltyPercent:
0.07,
maxPenaltyAmount:
2000
},
Smash: {
baseChance: 0.25,
baseChance:
0.25,
minStealPercent: 0.06,
maxStealPercent: 0.12,
maxStealAmount: 7500,
minStealPercent:
0.06,
minPenaltyPercent: 0.06,
maxPenaltyPercent: 0.12,
maxPenaltyAmount: 5000
maxStealPercent:
0.12,
maxStealAmount:
7500,
minPenaltyPercent:
0.06,
maxPenaltyPercent:
0.12,
maxPenaltyAmount:
5000
}
};
/*
* ============================================================
* RESOLVE HEIST
* ============================================================
*/
async function resolveCookieHeist(
interaction,
methodName
) {
/*
* Validate ownership of this Heist menu.
*
* Footer:
*
* cookieHeist:<userId>
*/
const footer =
interaction.message
?.embeds?.[0]
@@ -114,10 +117,10 @@ async function resolveCookieHeist(
if (
type !==
"cookieHeist"
"cookieHeist"
||
ownerId !==
interaction.user.id
interaction.user.id
) {
return temporaryEphemeralReply(
interaction,
@@ -129,13 +132,9 @@ async function resolveCookieHeist(
}
/*
* Validate method.
*/
const method =
HEIST_METHODS[
methodName
methodName
];
@@ -150,12 +149,6 @@ async function resolveCookieHeist(
}
/*
* ========================================================
* SHARED HEIST COOLDOWN
* ========================================================
*/
const cooldown =
await claimCooldown(
interaction.user.id,
@@ -179,10 +172,6 @@ async function resolveCookieHeist(
}
/*
* Lock this Heist interaction immediately.
*/
await interaction.update({
embeds: [
new EmbedBuilder()
@@ -212,11 +201,6 @@ async function resolveCookieHeist(
interaction.client.user.id;
/*
* Re-fetch balances because they may have
* changed since the menu was opened.
*/
const [
playerCookies,
vaultCookies
@@ -234,10 +218,10 @@ async function resolveCookieHeist(
if (
playerCookies <
1
1
||
vaultCookies <
1
1
) {
return interaction.editReply({
embeds: [
@@ -267,21 +251,11 @@ async function resolveCookieHeist(
}
/*
* Short theatrical delay.
*/
await sleep(
1200
);
/*
* ========================================================
* DYNAMIC VAULT SECURITY
* ========================================================
*/
const securityPenalty =
getVaultSecurityPenalty(
vaultCookies
@@ -302,12 +276,6 @@ async function resolveCookieHeist(
successChance;
/*
* ========================================================
* SUCCESS
* ========================================================
*/
if (success) {
const rolledPercent =
randomFloat(
@@ -377,30 +345,6 @@ async function resolveCookieHeist(
}
const [
playerAfter,
vaultAfter
] =
await Promise.all([
getCookieBalance(
playerId
),
getCookieBalance(
vaultId
)
]);
const actualPercentage =
(
amount /
vaultCookies
)
*
100;
await trackProfileEvent(
interaction,
playerId,
@@ -412,7 +356,10 @@ async function resolveCookieHeist(
"heist",
result:
"win"
"win",
profit:
amount
}
);
@@ -441,6 +388,30 @@ async function resolveCookieHeist(
);
const [
playerAfter,
vaultAfter
] =
await Promise.all([
getCookieBalance(
playerId
),
getCookieBalance(
vaultId
)
]);
const actualPercentage =
(
amount /
vaultCookies
)
*
100;
const embed =
new EmbedBuilder()
.setTitle(
@@ -551,12 +522,6 @@ async function resolveCookieHeist(
}
/*
* ========================================================
* FAILURE
* ========================================================
*/
const penaltyPercent =
randomFloat(
method.minPenaltyPercent,
@@ -582,11 +547,6 @@ async function resolveCookieHeist(
try {
/*
* Heist fines always go into
* ShaiBot's central vault.
*/
await transferCookies(
playerId,
vaultId,
@@ -630,6 +590,38 @@ async function resolveCookieHeist(
}
await trackProfileEvent(
interaction,
playerId,
{
type:
"game_result",
game:
"heist",
result:
"loss",
profit:
-penalty
}
);
await trackProfileEvent(
interaction,
playerId,
{
type:
"cookie_loss",
amount:
penalty
}
);
const [
playerAfter,
vaultAfter
@@ -654,35 +646,6 @@ async function resolveCookieHeist(
100;
await trackProfileEvent(
interaction,
playerId,
{
type:
"game_result",
game:
"heist",
result:
"loss"
}
);
await trackProfileEvent(
interaction,
playerId,
{
type:
"cookie_loss",
amount:
penalty
}
);
const embed =
new EmbedBuilder()
.setTitle(
@@ -793,12 +756,6 @@ async function resolveCookieHeist(
}
/*
* ============================================================
* RETURN TO CRUMB SAUCER
* ============================================================
*/
function buildReturnButton() {
return [
new ActionRowBuilder()
@@ -821,25 +778,9 @@ function buildReturnButton() {
}
/*
* ============================================================
* SECURITY
* ============================================================
*/
function getVaultSecurityPenalty(
vaultCookies
) {
/*
* Approximately:
*
* 100 -> 5%
* 1,000 -> 7.5%
* 10,000 -> 10%
* 100,000 -> 12.5%
* 1,000,000 -> 15%
*/
return Math.min(
Math.log10(
vaultCookies +
@@ -853,19 +794,14 @@ function getVaultSecurityPenalty(
}
/*
* ============================================================
* PROGRESS TEXT
* ============================================================
*/
function getHeistProgressText(
methodName
) {
switch (
methodName
methodName
) {
case "Sneak":
return [
"🥷 You slip through the service entrance...",
"",
@@ -874,6 +810,7 @@ function getHeistProgressText(
case "Hack":
return [
"💻 You connect to the vault terminal...",
"",
@@ -882,6 +819,7 @@ function getHeistProgressText(
case "Smash":
return [
"💥 You charge directly at the vault...",
"",
@@ -890,17 +828,12 @@ function getHeistProgressText(
default:
return "The heist is underway...";
}
}
/*
* ============================================================
* SUCCESS TEXT
* ============================================================
*/
function getSuccessText(
methodName,
user,
@@ -911,27 +844,31 @@ function getSuccessText(
switch (
methodName
methodName
) {
case "Sneak":
return (
`🥷 ${user} slipped through security and quietly escaped with ${cookies}.`
);
case "Hack":
return (
`💻 ${user} convinced the vault terminal that ${cookies} belonged to them.`
);
case "Smash":
return (
`💥 ${user} smashed through the Cookie Vault and escaped with ${cookies}!`
);
default:
return (
`${user} escaped with ${cookies}.`
);
@@ -939,12 +876,6 @@ function getSuccessText(
}
/*
* ============================================================
* FAILURE TEXT
* ============================================================
*/
function getFailureText(
methodName,
user,
@@ -955,9 +886,10 @@ function getFailureText(
switch (
methodName
methodName
) {
case "Sneak":
return [
`🚨 ${user} stepped directly on the world's loudest Cookie wrapper.`,
"",
@@ -966,6 +898,7 @@ function getFailureText(
case "Hack":
return [
`🚨 ${user} entered the password incorrectly one too many times.`,
"",
@@ -974,6 +907,7 @@ function getFailureText(
case "Smash":
return [
`🚨 ${user} discovered that the vault door is significantly stronger than expected.`,
"",
@@ -982,6 +916,7 @@ function getFailureText(
default:
return (
`The heist failed and cost ${user} ${cookies}.`
);
@@ -989,12 +924,6 @@ function getFailureText(
}
/*
* ============================================================
* UTIL
* ============================================================
*/
function sleep(
ms
) {
@@ -1008,12 +937,6 @@ function sleep(
}
/*
* ============================================================
* EXPORTS
* ============================================================
*/
module.exports = {
resolveCookieHeist,
HEIST_COOLDOWN
+33 -283
View File
@@ -22,12 +22,6 @@ const {
} = require("./profileSystem");
/*
* ============================================================
* CONFIG
* ============================================================
*/
const GAME_COLLECTION =
"items_games";
@@ -41,12 +35,6 @@ const BLACKJACK_BETS = [
];
/*
* ============================================================
* SHOW BLACKJACK MENU
* ============================================================
*/
async function showBlackjack(
interaction
) {
@@ -70,10 +58,6 @@ async function showBlackjack(
}
/*
* Resume an unfinished game.
*/
const active =
await getActiveBlackjack(
interaction.user.id,
@@ -146,26 +130,12 @@ async function showBlackjack(
}
/*
* ============================================================
* START GAME
* ============================================================
*/
async function startBlackjack(
interaction,
bet,
allowCustomBet =
false
) {
/*
* Normal wager buttons must use one of our
* predefined values.
*
* All In and Run It Back are allowed to use
* custom wager amounts.
*/
if (
!allowCustomBet
&&
@@ -183,11 +153,6 @@ async function startBlackjack(
}
/*
* Every wager, predefined or custom, must
* still be a positive safe integer.
*/
if (
!Number.isSafeInteger(
bet
@@ -238,10 +203,6 @@ async function startBlackjack(
);
/*
* Prevent multiple active hands.
*/
const existing =
await getActiveBlackjack(
interaction.user.id,
@@ -297,18 +258,6 @@ async function startBlackjack(
}
/*
* Natural Blackjack returns:
*
* wager + 3:2 winnings
*
* = 2.5x total payout.
*
* The wager will move into the house before
* resolution, so the house currently needs
* enough to cover the additional 1.5x.
*/
if (
houseBalance <
Math.ceil(
@@ -326,10 +275,6 @@ async function startBlackjack(
}
/*
* Take wager.
*/
try {
await transferCookies(
playerId,
@@ -356,12 +301,6 @@ async function startBlackjack(
}
/*
* ========================================================
* REAL 52-CARD DECK
* ========================================================
*/
const deck =
createDeck();
@@ -426,12 +365,6 @@ async function startBlackjack(
);
/*
* ========================================================
* NATURAL BLACKJACK CHECK
* ========================================================
*/
const playerBlackjack =
isBlackjack(
playerCards
@@ -448,10 +381,6 @@ async function startBlackjack(
playerBlackjack ||
dealerBlackjack
) {
/*
* Both natural Blackjack = Push.
*/
if (
playerBlackjack &&
dealerBlackjack
@@ -496,12 +425,6 @@ async function startBlackjack(
}
/*
* ============================================================
* ALL IN
* ============================================================
*/
async function startBlackjackAllIn(
interaction
) {
@@ -525,10 +448,6 @@ async function startBlackjackAllIn(
}
/*
* Resolve balance at click time.
*/
const balance =
await getCookieBalance(
interaction.user.id
@@ -549,11 +468,6 @@ async function startBlackjackAllIn(
}
/*
* true allows the balance to be used even though
* it likely isn't one of BLACKJACK_BETS.
*/
return startBlackjack(
interaction,
balance,
@@ -562,16 +476,6 @@ async function startBlackjackAllIn(
}
/*
* ============================================================
* RUN IT BACK
* ============================================================
*
* Reuses only the previous BET.
*
* A completely new shuffled deck and hand are created.
*/
async function runBlackjackBack(
interaction
) {
@@ -588,12 +492,6 @@ async function runBlackjackBack(
[];
/*
* Expected:
*
* crumbBlackjackResult:<userId>:<bet>
*/
if (
parts[0] !==
"crumbBlackjackResult"
@@ -650,13 +548,6 @@ async function runBlackjackBack(
}
/*
* Reuse the exact previous wager.
*
* true is required because a previous All In bet
* may not be part of BLACKJACK_BETS.
*/
return startBlackjack(
interaction,
bet,
@@ -665,12 +556,6 @@ async function runBlackjackBack(
}
/*
* ============================================================
* HIT
* ============================================================
*/
async function hitBlackjack(
interaction
) {
@@ -748,27 +633,25 @@ async function hitBlackjack(
getCollections();
const games =
db.collection(
await db
.collection(
GAME_COLLECTION
);
)
.updateOne(
{
gameId:
game.gameId,
await games.updateOne(
{
gameId:
game.gameId,
state:
"Playing"
},
{
$set: {
deck,
playerCards
state:
"Playing"
},
{
$set: {
deck,
playerCards
}
}
}
);
);
const updated = {
@@ -778,10 +661,6 @@ async function hitBlackjack(
};
/*
* Bust.
*/
if (
score >
21
@@ -794,10 +673,6 @@ async function hitBlackjack(
}
/*
* Automatically stand on exactly 21.
*/
if (
score ===
21
@@ -824,12 +699,6 @@ async function hitBlackjack(
}
/*
* ============================================================
* STAND
* ============================================================
*/
async function standBlackjack(
interaction,
suppliedGame =
@@ -884,10 +753,6 @@ async function standBlackjack(
];
/*
* Dealer stands on 17.
*/
while (
getHandValue(
dealerCards
@@ -950,10 +815,6 @@ async function standBlackjack(
}
/*
* Same value = Push.
*/
else {
result =
"Push";
@@ -968,12 +829,6 @@ async function standBlackjack(
}
/*
* ============================================================
* DOUBLE
* ============================================================
*/
async function doubleBlackjack(
interaction
) {
@@ -1008,10 +863,6 @@ async function doubleBlackjack(
}
/*
* Double only on initial two cards.
*/
if (
game.playerCards.length !==
2
@@ -1086,10 +937,6 @@ async function doubleBlackjack(
];
/*
* Double receives exactly one additional card.
*/
playerCards.push(
deck.pop()
);
@@ -1158,10 +1005,6 @@ async function doubleBlackjack(
}
/*
* Double automatically stands.
*/
return standBlackjack(
interaction,
updated
@@ -1169,12 +1012,6 @@ async function doubleBlackjack(
}
/*
* ============================================================
* FINISH GAME
* ============================================================
*/
async function finishBlackjack(
interaction,
game,
@@ -1188,11 +1025,9 @@ async function finishBlackjack(
0;
switch (result) {
/*
* Normal win.
*/
switch (
result
) {
case "Win":
case "DealerBust":
@@ -1203,12 +1038,6 @@ async function finishBlackjack(
break;
/*
* Push:
*
* Return exactly the wager.
*/
case "Push":
payout =
@@ -1217,12 +1046,6 @@ async function finishBlackjack(
break;
/*
* Natural Blackjack:
*
* stake + 3:2 winnings.
*/
case "Blackjack":
payout =
@@ -1234,10 +1057,6 @@ async function finishBlackjack(
break;
/*
* Bust / Loss / Dealer Blackjack.
*/
default:
payout =
@@ -1245,10 +1064,6 @@ async function finishBlackjack(
}
/*
* Pay player.
*/
if (
payout >
0
@@ -1281,7 +1096,7 @@ async function finishBlackjack(
.setFooter({
text:
`crumbBlackjackResult:${game.userId}:${game.originalBet ?? game.bet}`
`crumbBlackjackResult:${game.userId}:${game.originalBet}`
})
],
@@ -1299,10 +1114,6 @@ async function finishBlackjack(
}
/*
* Mark game finished.
*/
const {
db
} =
@@ -1356,6 +1167,7 @@ async function finishBlackjack(
let profileResult;
if (
result ===
"Push"
@@ -1364,6 +1176,7 @@ async function finishBlackjack(
"push";
}
else if (
[
"Win",
@@ -1377,6 +1190,7 @@ async function finishBlackjack(
"win";
}
else {
profileResult =
"loss";
@@ -1394,7 +1208,9 @@ async function finishBlackjack(
"blackjack",
result:
profileResult
profileResult,
profit
}
);
@@ -1416,6 +1232,7 @@ async function finishBlackjack(
);
}
else if (
profit <
0
@@ -1520,12 +1337,15 @@ async function finishBlackjack(
)
/*
* Owner + previous exact wager.
* Important:
*
* Run It Back uses the original wager,
* not the doubled total.
*/
.setFooter({
text:
`crumbBlackjackResult:${game.userId}:${game.originalBet ?? game.bet}`
`crumbBlackjackResult:${game.userId}:${game.originalBet}`
});
@@ -1540,12 +1360,6 @@ async function finishBlackjack(
}
/*
* ============================================================
* GAME EMBED
* ============================================================
*/
function buildGameEmbed(
game
) {
@@ -1608,12 +1422,6 @@ function buildGameEmbed(
}
/*
* ============================================================
* BET BUTTONS
* ============================================================
*/
function buildBetButtons() {
return [
new ActionRowBuilder()
@@ -1624,18 +1432,14 @@ function buildBetButtons() {
.setCustomId(
`crumbBlackjackBet${bet}`
)
.setLabel(
bet.toLocaleString()
)
.setEmoji(
"🍪"
)
.setStyle(
bet >=
500
bet >= 500
? ButtonStyle.Primary
: ButtonStyle.Secondary
)
@@ -1676,12 +1480,6 @@ function buildBetButtons() {
}
/*
* ============================================================
* ACTIVE GAME BUTTONS
* ============================================================
*/
function buildGameButtons(
game
) {
@@ -1744,12 +1542,6 @@ function buildGameButtons(
}
/*
* ============================================================
* RESULT BUTTONS
* ============================================================
*/
function buildResultButtons() {
return [
new ActionRowBuilder()
@@ -1800,12 +1592,6 @@ function buildResultButtons() {
}
/*
* ============================================================
* CARDS
* ============================================================
*/
function createDeck() {
const suits = [
"♠️",
@@ -1988,12 +1774,6 @@ function formatCards(
}
/*
* ============================================================
* DATABASE LOOKUPS
* ============================================================
*/
async function getActiveBlackjack(
userId,
guildId
@@ -2072,12 +1852,6 @@ async function getGameFromInteraction(
}
/*
* ============================================================
* SESSION OWNER
* ============================================================
*/
function getBlackjackSessionOwner(
interaction
) {
@@ -2110,12 +1884,6 @@ function getBlackjackSessionOwner(
}
/*
* Active hand:
*
* crumbBlackjack:<gameId>:<userId>
*/
if (
parts[0] ===
"crumbBlackjack"
@@ -2124,12 +1892,6 @@ function getBlackjackSessionOwner(
}
/*
* Finished result:
*
* crumbBlackjackResult:<userId>:<bet>
*/
if (
parts[0] ===
"crumbBlackjackResult"
@@ -2142,12 +1904,6 @@ function getBlackjackSessionOwner(
}
/*
* ============================================================
* RESULT TITLES
* ============================================================
*/
function getResultTitle(
result
) {
@@ -2191,12 +1947,6 @@ function getResultTitle(
}
/*
* ============================================================
* EXPORTS
* ============================================================
*/
module.exports = {
showBlackjack,
+400
View File
@@ -0,0 +1,400 @@
const {
EmbedBuilder,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle
} = require("discord.js");
const {
getCookieBalance,
changeCookies,
claimCooldown,
temporaryEphemeralReply
} = require("./cookieEconomy");
const {
trackProfileEvent
} = require("./profileSystem");
/*
* ============================================================
* CONFIG
* ============================================================
*/
const COLLECT_COOLDOWN =
60 * 60;
/*
* IMPORTANT:
*
* Always use exactly the same cooldown key.
*/
const COLLECT_COOLDOWN_KEY =
"collect";
/*
* ============================================================
* SHOW COLLECT PAGE
* ============================================================
*/
async function showCollectPage(
interaction
) {
const ownerId =
getCollectOwner(
interaction
);
if (
ownerId !==
interaction.user.id
) {
return temporaryEphemeralReply(
interaction,
{
content:
"This isn't your Crumb Saucer session."
}
);
}
const balance =
await getCookieBalance(
interaction.user.id
);
const embed =
new EmbedBuilder()
.setTitle(
"🍪 Crumb Saucer Cookie Bakery"
)
.setDescription(
[
"Fresh Cookies are prepared regularly.",
"",
"Press **Collect Cookies** to claim your batch.",
"",
"Your collection cooldown is shared with `/cookies collect`."
].join("\n")
)
.addFields({
name:
"Your Vault",
value:
`**${balance.toLocaleString()} 🍪**`
})
.setFooter({
text:
`crumbSaucerCollect:${interaction.user.id}`
});
return interaction.update({
embeds: [
embed
],
components:
buildCollectButtons()
});
}
/*
* ============================================================
* COLLECT
* ============================================================
*/
async function collectCookiesFromSaucer(
interaction
) {
const ownerId =
getCollectOwner(
interaction
);
if (
ownerId !==
interaction.user.id
) {
return temporaryEphemeralReply(
interaction,
{
content:
"This isn't your Crumb Saucer session."
}
);
}
/*
* Mongo-backed cooldown.
*
* Same pattern as Cookie Heist:
*
* userId
* guildId
* cooldown key
* duration
*/
const cooldown =
await claimCooldown(
interaction.user.id,
interaction.guildId,
COLLECT_COOLDOWN_KEY,
COLLECT_COOLDOWN
);
/*
* Already collected.
*/
if (
!cooldown.claimed
) {
const balance =
await getCookieBalance(
interaction.user.id
);
const embed =
new EmbedBuilder()
.setTitle(
"🍪 Cookie Bakery"
)
.setDescription(
[
"Your next batch isn't ready yet.",
"",
`Come back <t:${cooldown.next}:R>.`
].join("\n")
)
.addFields({
name:
"Your Vault",
value:
`**${balance.toLocaleString()} 🍪**`
})
.setFooter({
text:
`crumbSaucerCollect:${interaction.user.id}`
});
return interaction.update({
embeds: [
embed
],
components:
buildCollectButtons()
});
}
/*
* ========================================================
* GENERATE BATCH
* ========================================================
*/
const amount =
Math.floor(
Math.random() *
1000
)
+
1;
await changeCookies(
interaction.user.id,
amount
);
/*
* Profile / achievements.
*/
await trackProfileEvent(
interaction,
interaction.user.id,
{
type:
"cookie_gain",
amount
}
);
const balance =
await getCookieBalance(
interaction.user.id
);
const embed =
new EmbedBuilder()
.setTitle(
"🍪 Fresh Batch!"
)
.setDescription(
[
`You collected **${amount.toLocaleString()} Cookies**! 🍪`,
"",
`Your next batch will be ready <t:${cooldown.next}:R>.`
].join("\n")
)
.addFields({
name:
"Your Vault",
value:
`**${balance.toLocaleString()} 🍪**`
})
.setFooter({
text:
`crumbSaucerCollect:${interaction.user.id}`
});
return interaction.update({
embeds: [
embed
],
components:
buildCollectButtons()
});
}
/*
* ============================================================
* BUTTONS
* ============================================================
*/
function buildCollectButtons() {
return [
new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(
"crumbSaucerCollect"
)
.setLabel(
"Collect Cookies"
)
.setEmoji(
"🍪"
)
.setStyle(
ButtonStyle.Success
),
new ButtonBuilder()
.setCustomId(
"crumbSaucerBack"
)
.setLabel(
"Crumb Saucer"
)
.setEmoji(
"◀️"
)
.setStyle(
ButtonStyle.Secondary
)
)
];
}
/*
* ============================================================
* OWNER
* ============================================================
*/
function getCollectOwner(
interaction
) {
const footer =
interaction.message
?.embeds?.[0]
?.footer
?.text;
const parts =
footer?.split(":")
??
[];
/*
* Main menu -> Cookie Bakery.
*/
if (
parts[0] ===
"crumbSaucer"
) {
return parts[1];
}
/*
* Bakery page/result.
*/
if (
parts[0] ===
"crumbSaucerCollect"
) {
return parts[1];
}
return null;
}
/*
* ============================================================
* EXPORTS
* ============================================================
*/
module.exports = {
showCollectPage,
collectCookiesFromSaucer
};
+8 -224
View File
@@ -16,12 +16,6 @@ const {
} = require("./profileSystem");
/*
* ============================================================
* CONFIG
* ============================================================
*/
const ROULETTE_BETS = [
10,
50,
@@ -64,10 +58,6 @@ const ROULETTE_CHOICES = {
};
/*
* European Roulette red numbers.
*/
const RED_NUMBERS =
new Set([
1,
@@ -91,12 +81,6 @@ const RED_NUMBERS =
]);
/*
* ============================================================
* SHOW ROULETTE
* ============================================================
*/
async function showRoulette(
interaction
) {
@@ -171,12 +155,6 @@ async function showRoulette(
}
/*
* ============================================================
* CHOOSE NORMAL WAGER
* ============================================================
*/
async function chooseRouletteBet(
interaction,
bet
@@ -223,12 +201,6 @@ async function chooseRouletteBet(
}
/*
* ============================================================
* ALL IN
* ============================================================
*/
async function startRouletteAllIn(
interaction
) {
@@ -252,10 +224,6 @@ async function startRouletteAllIn(
}
/*
* Resolve the balance at click time.
*/
const balance =
await getCookieBalance(
interaction.user.id
@@ -276,13 +244,6 @@ async function startRouletteAllIn(
}
/*
* All In only selects the wager.
*
* Nothing is transferred until the player
* chooses Red / Black / Odd / Even / Zero.
*/
return showRouletteChoices(
interaction,
balance
@@ -290,18 +251,6 @@ async function startRouletteAllIn(
}
/*
* ============================================================
* SHOW CHOICE MENU
* ============================================================
*
* Shared by:
*
* - normal wager buttons
* - All In
* - Run It Back
*/
async function showRouletteChoices(
interaction,
bet
@@ -389,16 +338,6 @@ async function showRouletteChoices(
}
/*
* ============================================================
* RUN IT BACK
* ============================================================
*
* Remembers ONLY the previous wager.
*
* It does NOT remember Red / Black / Odd / Even / Zero.
*/
async function runRouletteBack(
interaction
) {
@@ -415,12 +354,6 @@ async function runRouletteBack(
[];
/*
* Expected:
*
* crumbRouletteResult:<userId>:<bet>
*/
if (
parts[0] !==
"crumbRouletteResult"
@@ -477,10 +410,6 @@ async function runRouletteBack(
}
/*
* Same wager, fresh choice.
*/
return showRouletteChoices(
interaction,
bet
@@ -488,12 +417,6 @@ async function runRouletteBack(
}
/*
* ============================================================
* PLAY ROULETTE
* ============================================================
*/
async function playRoulette(
interaction,
choiceKey
@@ -566,15 +489,6 @@ async function playRoulette(
}
/*
* IMPORTANT:
*
* Do NOT require ROULETTE_BETS.includes(bet) here.
*
* All In and Run It Back may legitimately use
* amounts outside the predefined wager buttons.
*/
if (
!Number.isSafeInteger(
bet
@@ -616,11 +530,6 @@ async function playRoulette(
]);
/*
* Balance may have changed while the player
* was choosing their bet type.
*/
if (
playerBalance <
bet
@@ -635,20 +544,6 @@ async function playRoulette(
}
/*
* Worst payout depends on the selected wager.
*
* Red / Black / Odd / Even:
* x2
*
* Zero:
* x36
*
* The player's wager is transferred to the house
* before payout, so houseBalance + bet is available
* after accepting the wager.
*/
const maximumPayout =
bet *
choice.multiplier;
@@ -669,12 +564,6 @@ async function playRoulette(
}
/*
* ========================================================
* TAKE WAGER
* ========================================================
*/
try {
await transferCookies(
playerId,
@@ -701,12 +590,6 @@ async function playRoulette(
}
/*
* ========================================================
* PSEUDO ANIMATION
* ========================================================
*/
await interaction.update({
embeds: [
buildSpinEmbed(
@@ -764,12 +647,6 @@ async function playRoulette(
);
/*
* ========================================================
* ACTUAL RESULT
* ========================================================
*/
const result =
randomRouletteNumber();
@@ -788,12 +665,6 @@ async function playRoulette(
: 0;
/*
* ========================================================
* PAYOUT
* ========================================================
*/
if (
payout >
0
@@ -871,7 +742,9 @@ async function playRoulette(
? "win"
: profit < 0
? "loss"
: "push"
: "push",
profit
}
);
@@ -893,6 +766,7 @@ async function playRoulette(
);
}
else if (
profit <
0
@@ -992,13 +866,6 @@ async function playRoulette(
}
)
/*
* Remember only:
*
* owner
* wager
*/
.setFooter({
text:
`crumbRouletteResult:${playerId}:${bet}`
@@ -1016,17 +883,13 @@ async function playRoulette(
}
/*
* ============================================================
* RESULT LOGIC
* ============================================================
*/
function didRouletteBetWin(
choice,
number
) {
switch (choice) {
switch (
choice
) {
case "Red":
return (
@@ -1090,12 +953,6 @@ function didRouletteBetWin(
}
/*
* ============================================================
* DISPLAY
* ============================================================
*/
function buildSpinEmbed(
choice,
bet,
@@ -1167,12 +1024,6 @@ function formatRouletteNumber(
}
/*
* ============================================================
* WAGER BUTTONS
* ============================================================
*/
function buildBetButtons() {
return [
new ActionRowBuilder()
@@ -1183,18 +1034,14 @@ function buildBetButtons() {
.setCustomId(
`crumbRouletteBet${bet}`
)
.setLabel(
bet.toLocaleString()
)
.setEmoji(
"🍪"
)
.setStyle(
bet >=
500
bet >= 500
? ButtonStyle.Primary
: ButtonStyle.Secondary
)
@@ -1235,12 +1082,6 @@ function buildBetButtons() {
}
/*
* ============================================================
* CHOICE BUTTONS
* ============================================================
*/
function buildChoiceButtons() {
return [
new ActionRowBuilder()
@@ -1330,22 +1171,10 @@ function buildChoiceButtons() {
}
/*
* ============================================================
* RESULT BUTTONS
* ============================================================
*/
function buildResultButtons() {
return [
new ActionRowBuilder()
.addComponents(
/*
* Same wager.
*
* Player chooses Red/Black/etc again.
*/
new ButtonBuilder()
.setCustomId(
"crumbRouletteRunItBack"
@@ -1360,10 +1189,6 @@ function buildResultButtons() {
ButtonStyle.Success
),
/*
* Return to wager selection.
*/
new ButtonBuilder()
.setCustomId(
"crumbSaucerRoulette"
@@ -1378,10 +1203,6 @@ function buildResultButtons() {
ButtonStyle.Primary
),
/*
* Main Crumb Saucer.
*/
new ButtonBuilder()
.setCustomId(
"crumbSaucerBack"
@@ -1400,12 +1221,6 @@ function buildResultButtons() {
}
/*
* ============================================================
* SESSION OWNER
* ============================================================
*/
function getRouletteSessionOwner(
interaction
) {
@@ -1422,10 +1237,6 @@ function getRouletteSessionOwner(
[];
/*
* Main Saucer
*/
if (
parts[0] ===
"crumbSaucer"
@@ -1434,10 +1245,6 @@ function getRouletteSessionOwner(
}
/*
* Roulette wager menu
*/
if (
parts[0] ===
"crumbRouletteMenu"
@@ -1446,10 +1253,6 @@ function getRouletteSessionOwner(
}
/*
* Roulette choice menu.
*/
if (
parts[0] ===
"crumbRouletteBet"
@@ -1458,10 +1261,6 @@ function getRouletteSessionOwner(
}
/*
* Roulette result.
*/
if (
parts[0] ===
"crumbRouletteResult"
@@ -1474,12 +1273,6 @@ function getRouletteSessionOwner(
}
/*
* ============================================================
* RANDOM
* ============================================================
*/
function randomRouletteNumber() {
return Math.floor(
Math.random() *
@@ -1501,20 +1294,11 @@ function sleep(
}
/*
* ============================================================
* EXPORTS
* ============================================================
*/
module.exports = {
showRoulette,
chooseRouletteBet,
startRouletteAllIn,
showRouletteChoices,
playRoulette,
runRouletteBack
};
+254 -79
View File
@@ -10,115 +10,271 @@ const {
temporaryEphemeralReply
} = require("./cookieEconomy");
async function showCrumbSaucer(interaction) {
if (interaction.isButton()) {
const ownerId = getCrumbSaucerSessionOwner(interaction);
if (ownerId !== interaction.user.id) {
return temporaryEphemeralReply(interaction, {
content: "This isn't your Crumb Saucer session."
});
/*
* ============================================================
* MAIN MENU
* ============================================================
*/
async function showCrumbSaucer(
interaction
) {
/*
* Button navigation must preserve ownership.
*/
if (
interaction.isButton()
) {
const ownerId =
getCrumbSaucerSessionOwner(
interaction
);
if (
ownerId !==
interaction.user.id
) {
return temporaryEphemeralReply(
interaction,
{
content:
"This isn't your Crumb Saucer session."
}
);
}
}
const balance = await getCookieBalance(interaction.user.id);
const embed = buildMainMenuEmbed(interaction.user, balance);
const components = buildMainMenuButtons();
if (interaction.isButton()) {
const balance =
await getCookieBalance(
interaction.user.id
);
const embed =
buildMainMenuEmbed(
interaction.user,
balance
);
if (
interaction.isButton()
) {
return interaction.update({
embeds: [embed],
components
embeds: [
embed
],
components:
buildMainMenuButtons()
});
}
return interaction.reply({
embeds: [embed],
components
embeds: [
embed
],
components:
buildMainMenuButtons()
});
}
function buildMainMenuEmbed(user, balance) {
/*
* ============================================================
* MAIN MENU EMBED
* ============================================================
*/
function buildMainMenuEmbed(
user,
balance
) {
return new EmbedBuilder()
.setTitle("🎰 The Crumb Saucer")
.setDescription([
`Welcome, ${user}!`,
"",
"Where Cookies are won, lost,",
"and occasionally confiscated by ShaiBot.",
"",
"### 🎰 Slots",
"Spin the reels and chase the Cookie jackpot.",
"",
"### 🏦 Vault Heist",
"Attempt to breach ShaiBot's Cookie Vault.",
"",
"### 🃏 Blackjack",
"Beat the dealer without going over 21.",
"",
"### 🎡 Roulette",
"Bet on Red, Black, Odd, Even, or Zero.",
"",
"*More attractions coming soon... make a lala offering or buy me a white monster*"
].join("\n"))
.setTitle(
"🎰 The Crumb Saucer"
)
.setDescription(
[
`Welcome, ${user}!`,
"",
"Where Cookies are won, lost,",
"and occasionally confiscated by ShaiBot.",
"",
"### 🍪 Cookie Bakery",
"Collect your regular batch of Cookies.",
"",
"### 🎰 Slots",
"Spin the reels and chase the Cookie jackpot.",
"",
"### 🃏 Blackjack",
"Beat the dealer without going over 21.",
"",
"### 🎡 Roulette",
"Bet on Red, Black, Odd, Even, or Zero.",
"",
"### 🏦 Vault Heist",
"Attempt to breach ShaiBot's Cookie Vault."
].join("\n")
)
.addFields({
name: "Your Vault",
value: `**${balance.toLocaleString()} 🍪**`
name:
"Your Vault",
value:
`**${balance.toLocaleString()} 🍪**`
})
.setFooter({
text: `crumbSaucer:${user.id}`
text:
`crumbSaucer:${user.id}`
});
}
/*
* ============================================================
* MAIN MENU BUTTONS
* ============================================================
*/
function buildMainMenuButtons() {
return [
new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setCustomId("crumbSaucerSlots")
.setLabel("Slots")
.setEmoji("🎰")
.setStyle(ButtonStyle.Primary),
new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(
"crumbSaucerSlots"
)
.setLabel(
"Slots"
)
.setEmoji(
"🎰"
)
.setStyle(
ButtonStyle.Primary
),
new ButtonBuilder()
.setCustomId("crumbSaucerBlackjack")
.setLabel("Blackjack")
.setEmoji("🃏")
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId(
"crumbSaucerBlackjack"
)
.setLabel(
"Blackjack"
)
.setEmoji(
"🃏"
)
.setStyle(
ButtonStyle.Success
),
new ButtonBuilder()
.setCustomId("crumbSaucerRoulette")
.setLabel("Roulette")
.setEmoji("🎡")
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId(
"crumbSaucerRoulette"
)
.setLabel(
"Roulette"
)
.setEmoji(
"🎡"
)
.setStyle(
ButtonStyle.Primary
),
new ButtonBuilder()
.setCustomId("crumbSaucerHeist")
.setLabel("Vault Heist")
.setEmoji("🏦")
.setStyle(ButtonStyle.Danger)
),
new ButtonBuilder()
.setCustomId(
"crumbSaucerHeist"
)
.setLabel(
"Vault Heist"
)
.setEmoji(
"🏦"
)
.setStyle(
ButtonStyle.Danger
)
),
new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setCustomId("abort")
.setLabel("Close")
.setEmoji("✖️")
.setStyle(ButtonStyle.Secondary),
new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(
"crumbSaucerCollectMenu"
)
.setLabel(
"Cookie Bakery"
)
.setEmoji(
"🍪"
)
.setStyle(
ButtonStyle.Success
),
new ButtonBuilder()
.setCustomId("crumbSaucerCollect")
.setLabel("Collect")
.setEmoji("🍪")
.setStyle(ButtonStyle.Success)
)
/*
* Do NOT use global "abort" here.
*
* The dedicated handler validates
* Crumb Saucer ownership first.
*/
new ButtonBuilder()
.setCustomId(
"crumbSaucerClose"
)
.setLabel(
"Close"
)
.setEmoji(
"✖️"
)
.setStyle(
ButtonStyle.Secondary
)
)
];
}
function getCrumbSaucerSessionOwner(interaction) {
const footer = interaction.message?.embeds?.[0]?.footer?.text;
const parts = footer?.split(":") ?? [];
switch (parts[0]) {
/*
* ============================================================
* SESSION OWNER
* ============================================================
*/
function getCrumbSaucerSessionOwner(
interaction
) {
const footer =
interaction.message
?.embeds?.[0]
?.footer
?.text;
const parts =
footer?.split(":")
??
[];
switch (
parts[0]
) {
case "crumbSaucer":
case "crumbSaucerCollect":
case "crumbSaucerSlots":
case "crumbSaucerSlotsResult":
case "crumbSaucerBlackjack":
@@ -127,16 +283,35 @@ function getCrumbSaucerSessionOwner(interaction) {
case "crumbRouletteBet":
case "crumbRouletteResult":
case "cookieHeist":
return parts[1];
/*
* Active Blackjack:
*
* crumbBlackjack:<gameId>:<userId>
*/
case "crumbBlackjack":
return parts[2];
default:
return null;
}
}
/*
* ============================================================
* EXPORTS
* ============================================================
*/
module.exports = {
showCrumbSaucer
showCrumbSaucer,
getCrumbSaucerSessionOwner
};
+902 -206
View File
File diff suppressed because it is too large Load Diff
+2276 -443
View File
File diff suppressed because it is too large Load Diff