added runtiback and allin
This commit is contained in:
+54
-23
@@ -91,7 +91,7 @@ async function resolveCookieHeist(
|
||||
|
||||
const footer =
|
||||
interaction.message
|
||||
.embeds[0]
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
@@ -101,12 +101,16 @@ async function resolveCookieHeist(
|
||||
ownerId
|
||||
] =
|
||||
footer?.split(":")
|
||||
?? [];
|
||||
??
|
||||
[];
|
||||
|
||||
|
||||
if (
|
||||
type !== "cookieHeist" ||
|
||||
ownerId !== interaction.user.id
|
||||
type !==
|
||||
"cookieHeist"
|
||||
||
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
@@ -143,16 +147,6 @@ async function resolveCookieHeist(
|
||||
* ========================================================
|
||||
* SHARED HEIST COOLDOWN
|
||||
* ========================================================
|
||||
*
|
||||
* IMPORTANT:
|
||||
*
|
||||
* This is intentionally always "heist".
|
||||
*
|
||||
* It does NOT use:
|
||||
*
|
||||
* heistSneak
|
||||
* heistHack
|
||||
* heistSmash
|
||||
*/
|
||||
|
||||
const cooldown =
|
||||
@@ -164,7 +158,9 @@ async function resolveCookieHeist(
|
||||
);
|
||||
|
||||
|
||||
if (!cooldown.claimed) {
|
||||
if (
|
||||
!cooldown.claimed
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
@@ -178,9 +174,6 @@ async function resolveCookieHeist(
|
||||
|
||||
/*
|
||||
* Lock this Heist interaction immediately.
|
||||
*
|
||||
* Prevents selecting another approach from
|
||||
* the same message.
|
||||
*/
|
||||
|
||||
await interaction.update({
|
||||
@@ -194,6 +187,10 @@ async function resolveCookieHeist(
|
||||
methodName
|
||||
)
|
||||
)
|
||||
.setFooter({
|
||||
text:
|
||||
`cookieHeist:${interaction.user.id}`
|
||||
})
|
||||
],
|
||||
|
||||
components: []
|
||||
@@ -203,6 +200,7 @@ async function resolveCookieHeist(
|
||||
const playerId =
|
||||
interaction.user.id;
|
||||
|
||||
|
||||
const vaultId =
|
||||
interaction.client.user.id;
|
||||
|
||||
@@ -228,8 +226,11 @@ async function resolveCookieHeist(
|
||||
|
||||
|
||||
if (
|
||||
playerCookies < 1 ||
|
||||
vaultCookies < 1
|
||||
playerCookies <
|
||||
1
|
||||
||
|
||||
vaultCookies <
|
||||
1
|
||||
) {
|
||||
return interaction.editReply({
|
||||
embeds: [
|
||||
@@ -247,6 +248,10 @@ async function resolveCookieHeist(
|
||||
value:
|
||||
`<t:${cooldown.next}:R>`
|
||||
})
|
||||
.setFooter({
|
||||
text:
|
||||
`cookieHeist:${playerId}`
|
||||
})
|
||||
],
|
||||
|
||||
components:
|
||||
@@ -349,6 +354,10 @@ async function resolveCookieHeist(
|
||||
value:
|
||||
`<t:${cooldown.next}:R>`
|
||||
})
|
||||
.setFooter({
|
||||
text:
|
||||
`cookieHeist:${playerId}`
|
||||
})
|
||||
],
|
||||
|
||||
components:
|
||||
@@ -476,7 +485,12 @@ async function resolveCookieHeist(
|
||||
inline:
|
||||
false
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`cookieHeist:${playerId}`
|
||||
});
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
@@ -551,6 +565,10 @@ async function resolveCookieHeist(
|
||||
value:
|
||||
`<t:${cooldown.next}:R>`
|
||||
})
|
||||
.setFooter({
|
||||
text:
|
||||
`cookieHeist:${playerId}`
|
||||
})
|
||||
],
|
||||
|
||||
components:
|
||||
@@ -678,7 +696,12 @@ async function resolveCookieHeist(
|
||||
inline:
|
||||
false
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`cookieHeist:${playerId}`
|
||||
});
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
@@ -894,7 +917,9 @@ function getFailureText(
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function sleep(ms) {
|
||||
function sleep(
|
||||
ms
|
||||
) {
|
||||
return new Promise(
|
||||
resolve =>
|
||||
setTimeout(
|
||||
@@ -905,6 +930,12 @@ function sleep(ms) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* EXPORTS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
resolveCookieHeist,
|
||||
HEIST_COOLDOWN
|
||||
|
||||
+570
-225
File diff suppressed because it is too large
Load Diff
+401
-70
@@ -29,62 +29,33 @@ const ROULETTE_BETS = [
|
||||
|
||||
const ROULETTE_CHOICES = {
|
||||
Red: {
|
||||
name:
|
||||
"Red",
|
||||
|
||||
emoji:
|
||||
"🔴",
|
||||
|
||||
multiplier:
|
||||
2
|
||||
name: "Red",
|
||||
emoji: "🔴",
|
||||
multiplier: 2
|
||||
},
|
||||
|
||||
|
||||
Black: {
|
||||
name:
|
||||
"Black",
|
||||
|
||||
emoji:
|
||||
"⚫",
|
||||
|
||||
multiplier:
|
||||
2
|
||||
name: "Black",
|
||||
emoji: "⚫",
|
||||
multiplier: 2
|
||||
},
|
||||
|
||||
|
||||
Odd: {
|
||||
name:
|
||||
"Odd",
|
||||
|
||||
emoji:
|
||||
"🔢",
|
||||
|
||||
multiplier:
|
||||
2
|
||||
name: "Odd",
|
||||
emoji: "🔢",
|
||||
multiplier: 2
|
||||
},
|
||||
|
||||
|
||||
Even: {
|
||||
name:
|
||||
"Even",
|
||||
|
||||
emoji:
|
||||
"2️⃣",
|
||||
|
||||
multiplier:
|
||||
2
|
||||
name: "Even",
|
||||
emoji: "2️⃣",
|
||||
multiplier: 2
|
||||
},
|
||||
|
||||
|
||||
Zero: {
|
||||
name:
|
||||
"Zero",
|
||||
|
||||
emoji:
|
||||
"🟢",
|
||||
|
||||
multiplier:
|
||||
36
|
||||
name: "Zero",
|
||||
emoji: "🟢",
|
||||
multiplier: 36
|
||||
}
|
||||
};
|
||||
|
||||
@@ -156,19 +127,21 @@ async function showRoulette(
|
||||
.setTitle(
|
||||
"🎡 Crumb Saucer Roulette"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
"European Roulette — **0 to 36**.",
|
||||
"",
|
||||
"Choose your wager.",
|
||||
"",
|
||||
"🔴 **Red** — ×2",
|
||||
"⚫ **Black** — ×2",
|
||||
"🔢 **Odd** — ×2",
|
||||
"2️⃣ **Even** — ×2",
|
||||
"🟢 **Zero** — ×36"
|
||||
"🔴 **Red** — x2",
|
||||
"⚫ **Black** — x2",
|
||||
"🔢 **Odd** — x2",
|
||||
"2️⃣ **Even** — x2",
|
||||
"🟢 **Zero** — x36"
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Your Vault",
|
||||
@@ -176,6 +149,7 @@ async function showRoulette(
|
||||
value:
|
||||
`**${balance.toLocaleString()} 🍪**`
|
||||
})
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbRouletteMenu:${interaction.user.id}`
|
||||
@@ -195,7 +169,7 @@ async function showRoulette(
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* CHOOSE WAGER
|
||||
* CHOOSE NORMAL WAGER
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
@@ -238,6 +212,114 @@ async function chooseRouletteBet(
|
||||
}
|
||||
|
||||
|
||||
return showRouletteChoices(
|
||||
interaction,
|
||||
bet
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* ALL IN
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function startRouletteAllIn(
|
||||
interaction
|
||||
) {
|
||||
const ownerId =
|
||||
getRouletteSessionOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"This isn't your Roulette table."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Resolve the balance at click time.
|
||||
*/
|
||||
|
||||
const balance =
|
||||
await getCookieBalance(
|
||||
interaction.user.id
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
balance <=
|
||||
0
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"You don't have any Cookies to go All In with."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* All In only selects the wager.
|
||||
*
|
||||
* Nothing is transferred until the player
|
||||
* chooses Red / Black / Odd / Even / Zero.
|
||||
*/
|
||||
|
||||
return showRouletteChoices(
|
||||
interaction,
|
||||
balance
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* SHOW CHOICE MENU
|
||||
* ============================================================
|
||||
*
|
||||
* Shared by:
|
||||
*
|
||||
* - normal wager buttons
|
||||
* - All In
|
||||
* - Run It Back
|
||||
*/
|
||||
|
||||
async function showRouletteChoices(
|
||||
interaction,
|
||||
bet
|
||||
) {
|
||||
if (
|
||||
!Number.isSafeInteger(
|
||||
bet
|
||||
)
|
||||
||
|
||||
bet <=
|
||||
0
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"That Roulette wager isn't valid."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const balance =
|
||||
await getCookieBalance(
|
||||
interaction.user.id
|
||||
@@ -263,19 +345,29 @@ async function chooseRouletteBet(
|
||||
.setTitle(
|
||||
"🎡 Crumb Saucer Roulette"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
`Wager: **${bet.toLocaleString()} 🍪**`,
|
||||
"",
|
||||
"Where do you want to place your Cookies?",
|
||||
"",
|
||||
"🔴 **Red** — ×2",
|
||||
"⚫ **Black** — ×2",
|
||||
"🔢 **Odd** — ×2",
|
||||
"2️⃣ **Even** — ×2",
|
||||
"🟢 **Zero** — ×36"
|
||||
"🔴 **Red** — x2",
|
||||
"⚫ **Black** — x2",
|
||||
"🔢 **Odd** — x2",
|
||||
"2️⃣ **Even** — x2",
|
||||
"🟢 **Zero** — x36"
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Your Vault",
|
||||
|
||||
value:
|
||||
`**${balance.toLocaleString()} 🍪**`
|
||||
})
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbRouletteBet:${interaction.user.id}:${bet}`
|
||||
@@ -293,6 +385,105 @@ async function chooseRouletteBet(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* RUN IT BACK
|
||||
* ============================================================
|
||||
*
|
||||
* Remembers ONLY the previous wager.
|
||||
*
|
||||
* It does NOT remember Red / Black / Odd / Even / Zero.
|
||||
*/
|
||||
|
||||
async function runRouletteBack(
|
||||
interaction
|
||||
) {
|
||||
const footer =
|
||||
interaction.message
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
|
||||
const parts =
|
||||
footer?.split(":")
|
||||
??
|
||||
[];
|
||||
|
||||
|
||||
/*
|
||||
* Expected:
|
||||
*
|
||||
* crumbRouletteResult:<userId>:<bet>
|
||||
*/
|
||||
|
||||
if (
|
||||
parts[0] !==
|
||||
"crumbRouletteResult"
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"This Roulette result can no longer be replayed."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const ownerId =
|
||||
parts[1];
|
||||
|
||||
|
||||
const bet =
|
||||
Number(
|
||||
parts[2]
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"This isn't your Roulette session."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
!Number.isSafeInteger(
|
||||
bet
|
||||
)
|
||||
||
|
||||
bet <=
|
||||
0
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"The previous wager is no longer valid."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Same wager, fresh choice.
|
||||
*/
|
||||
|
||||
return showRouletteChoices(
|
||||
interaction,
|
||||
bet
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* PLAY ROULETTE
|
||||
@@ -322,7 +513,7 @@ async function playRoulette(
|
||||
|
||||
const footer =
|
||||
interaction.message
|
||||
.embeds[0]
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
@@ -371,10 +562,22 @@ 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 (
|
||||
!ROULETTE_BETS.includes(
|
||||
!Number.isSafeInteger(
|
||||
bet
|
||||
)
|
||||
||
|
||||
bet <=
|
||||
0
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
@@ -409,6 +612,11 @@ async function playRoulette(
|
||||
]);
|
||||
|
||||
|
||||
/*
|
||||
* Balance may have changed while the player
|
||||
* was choosing their bet type.
|
||||
*/
|
||||
|
||||
if (
|
||||
playerBalance <
|
||||
bet
|
||||
@@ -424,11 +632,17 @@ async function playRoulette(
|
||||
|
||||
|
||||
/*
|
||||
* Worst payout is Zero at x36.
|
||||
* Worst payout depends on the selected wager.
|
||||
*
|
||||
* Once the player's wager is transferred to the house,
|
||||
* the house must still be able to cover the remaining
|
||||
* potential payout.
|
||||
* 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 =
|
||||
@@ -452,7 +666,9 @@ async function playRoulette(
|
||||
|
||||
|
||||
/*
|
||||
* Take wager.
|
||||
* ========================================================
|
||||
* TAKE WAGER
|
||||
* ========================================================
|
||||
*/
|
||||
|
||||
try {
|
||||
@@ -545,8 +761,9 @@ async function playRoulette(
|
||||
|
||||
|
||||
/*
|
||||
* The real result is only generated after
|
||||
* the animation completes.
|
||||
* ========================================================
|
||||
* ACTUAL RESULT
|
||||
* ========================================================
|
||||
*/
|
||||
|
||||
const result =
|
||||
@@ -567,6 +784,12 @@ async function playRoulette(
|
||||
: 0;
|
||||
|
||||
|
||||
/*
|
||||
* ========================================================
|
||||
* PAYOUT
|
||||
* ========================================================
|
||||
*/
|
||||
|
||||
if (
|
||||
payout >
|
||||
0
|
||||
@@ -591,9 +814,15 @@ async function playRoulette(
|
||||
.setTitle(
|
||||
"🎡 Roulette Error"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
"The wheel resolved, but the payout failed. Please contact an administrator."
|
||||
)
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbRouletteResult:${playerId}:${bet}`
|
||||
})
|
||||
],
|
||||
|
||||
components:
|
||||
@@ -630,6 +859,7 @@ async function playRoulette(
|
||||
? "🎉 Roulette Winner!"
|
||||
: "🎡 Roulette"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
"## The ball lands on...",
|
||||
@@ -643,6 +873,7 @@ async function playRoulette(
|
||||
: "The house takes this one."
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields(
|
||||
{
|
||||
name:
|
||||
@@ -699,9 +930,17 @@ async function playRoulette(
|
||||
true
|
||||
}
|
||||
)
|
||||
|
||||
/*
|
||||
* Remember only:
|
||||
*
|
||||
* owner
|
||||
* wager
|
||||
*/
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbRouletteResult:${interaction.user.id}`
|
||||
`crumbRouletteResult:${playerId}:${bet}`
|
||||
});
|
||||
|
||||
|
||||
@@ -728,6 +967,7 @@ function didRouletteBetWin(
|
||||
) {
|
||||
switch (choice) {
|
||||
case "Red":
|
||||
|
||||
return (
|
||||
number !==
|
||||
0
|
||||
@@ -739,6 +979,7 @@ function didRouletteBetWin(
|
||||
|
||||
|
||||
case "Black":
|
||||
|
||||
return (
|
||||
number !==
|
||||
0
|
||||
@@ -750,6 +991,7 @@ function didRouletteBetWin(
|
||||
|
||||
|
||||
case "Odd":
|
||||
|
||||
return (
|
||||
number !==
|
||||
0
|
||||
@@ -761,6 +1003,7 @@ function didRouletteBetWin(
|
||||
|
||||
|
||||
case "Even":
|
||||
|
||||
return (
|
||||
number !==
|
||||
0
|
||||
@@ -772,6 +1015,7 @@ function didRouletteBetWin(
|
||||
|
||||
|
||||
case "Zero":
|
||||
|
||||
return (
|
||||
number ===
|
||||
0
|
||||
@@ -779,6 +1023,7 @@ function didRouletteBetWin(
|
||||
|
||||
|
||||
default:
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -800,6 +1045,7 @@ function buildSpinEmbed(
|
||||
.setTitle(
|
||||
"🎡 Crumb Saucer Roulette"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
"## 🎡 Spinning...",
|
||||
@@ -809,6 +1055,7 @@ function buildSpinEmbed(
|
||||
`**${status}**`
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields(
|
||||
{
|
||||
name:
|
||||
@@ -861,7 +1108,7 @@ function formatRouletteNumber(
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* BUTTON BUILDERS
|
||||
* WAGER BUTTONS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
@@ -875,12 +1122,15 @@ function buildBetButtons() {
|
||||
.setCustomId(
|
||||
`crumbRouletteBet${bet}`
|
||||
)
|
||||
|
||||
.setLabel(
|
||||
bet.toLocaleString()
|
||||
)
|
||||
|
||||
.setEmoji(
|
||||
"🍪"
|
||||
)
|
||||
|
||||
.setStyle(
|
||||
bet >=
|
||||
500
|
||||
@@ -892,6 +1142,20 @@ function buildBetButtons() {
|
||||
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"crumbRouletteAllIn"
|
||||
)
|
||||
.setLabel(
|
||||
"All In"
|
||||
)
|
||||
.setEmoji(
|
||||
"🔥"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Danger
|
||||
),
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"crumbSaucerBack"
|
||||
@@ -910,6 +1174,12 @@ function buildBetButtons() {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* CHOICE BUTTONS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function buildChoiceButtons() {
|
||||
return [
|
||||
new ActionRowBuilder()
|
||||
@@ -999,16 +1269,46 @@ function buildChoiceButtons() {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* RESULT BUTTONS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function buildResultButtons() {
|
||||
return [
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
/*
|
||||
* Same wager.
|
||||
*
|
||||
* Player chooses Red/Black/etc again.
|
||||
*/
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"crumbRouletteRunItBack"
|
||||
)
|
||||
.setLabel(
|
||||
"Run It Back"
|
||||
)
|
||||
.setEmoji(
|
||||
"🔁"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Success
|
||||
),
|
||||
|
||||
/*
|
||||
* Return to wager selection.
|
||||
*/
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"crumbSaucerRoulette"
|
||||
)
|
||||
.setLabel(
|
||||
"Play Again"
|
||||
"Change Bet"
|
||||
)
|
||||
.setEmoji(
|
||||
"🎡"
|
||||
@@ -1017,6 +1317,10 @@ function buildResultButtons() {
|
||||
ButtonStyle.Primary
|
||||
),
|
||||
|
||||
/*
|
||||
* Main Crumb Saucer.
|
||||
*/
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"crumbSaucerBack"
|
||||
@@ -1046,7 +1350,7 @@ function getRouletteSessionOwner(
|
||||
) {
|
||||
const footer =
|
||||
interaction.message
|
||||
.embeds[0]
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
@@ -1060,6 +1364,7 @@ function getRouletteSessionOwner(
|
||||
/*
|
||||
* Main Saucer
|
||||
*/
|
||||
|
||||
if (
|
||||
parts[0] ===
|
||||
"crumbSaucer"
|
||||
@@ -1071,6 +1376,7 @@ function getRouletteSessionOwner(
|
||||
/*
|
||||
* Roulette wager menu
|
||||
*/
|
||||
|
||||
if (
|
||||
parts[0] ===
|
||||
"crumbRouletteMenu"
|
||||
@@ -1080,8 +1386,21 @@ function getRouletteSessionOwner(
|
||||
|
||||
|
||||
/*
|
||||
* Roulette result
|
||||
* Roulette choice menu.
|
||||
*/
|
||||
|
||||
if (
|
||||
parts[0] ===
|
||||
"crumbRouletteBet"
|
||||
) {
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Roulette result.
|
||||
*/
|
||||
|
||||
if (
|
||||
parts[0] ===
|
||||
"crumbRouletteResult"
|
||||
@@ -1121,8 +1440,20 @@ function sleep(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* EXPORTS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
showRoulette,
|
||||
|
||||
chooseRouletteBet,
|
||||
playRoulette
|
||||
startRouletteAllIn,
|
||||
showRouletteChoices,
|
||||
|
||||
playRoulette,
|
||||
|
||||
runRouletteBack
|
||||
};
|
||||
+349
-121
@@ -14,7 +14,9 @@ const {
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* SLOT CONFIG
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
const SLOT_BETS = [
|
||||
@@ -98,6 +100,40 @@ const SLOT_OUTCOMES = [
|
||||
async function showCrumbSaucer(
|
||||
interaction
|
||||
) {
|
||||
/*
|
||||
* If this is being called from a button,
|
||||
* verify that the player owns the current
|
||||
* Crumb Saucer / game session.
|
||||
*
|
||||
* This prevents another user from pressing
|
||||
* "Crumb Saucer" / "Back" on somebody else's
|
||||
* game message and taking over their session.
|
||||
*/
|
||||
|
||||
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
|
||||
@@ -116,11 +152,8 @@ async function showCrumbSaucer(
|
||||
|
||||
|
||||
/*
|
||||
* Slash command:
|
||||
* reply normally.
|
||||
*
|
||||
* Button:
|
||||
* update existing menu message.
|
||||
* update existing menu/game message.
|
||||
*/
|
||||
|
||||
if (
|
||||
@@ -136,6 +169,11 @@ async function showCrumbSaucer(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Slash command:
|
||||
* create a new Crumb Saucer.
|
||||
*/
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
embed
|
||||
@@ -145,51 +183,13 @@ async function showCrumbSaucer(
|
||||
});
|
||||
}
|
||||
|
||||
function getSlotsSessionOwner(
|
||||
interaction
|
||||
) {
|
||||
const footer =
|
||||
interaction.message
|
||||
.embeds[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
|
||||
const parts =
|
||||
footer?.split(":")
|
||||
??
|
||||
[];
|
||||
|
||||
|
||||
/*
|
||||
* Main Crumb Saucer
|
||||
*/
|
||||
if (
|
||||
parts[0] ===
|
||||
"crumbSaucer"
|
||||
) {
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Slots menu/result
|
||||
*/
|
||||
if (
|
||||
parts[0] ===
|
||||
"crumbSaucerSlots"
|
||||
) {
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
/*
|
||||
* ============================================================
|
||||
* SLOT MENU
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function showSlots(
|
||||
interaction
|
||||
) {
|
||||
@@ -224,19 +224,21 @@ async function showSlots(
|
||||
.setTitle(
|
||||
"🎰 Crumb Saucer Slots"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
"Welcome to the Cookie reels.",
|
||||
"",
|
||||
"Choose your wager below.",
|
||||
"",
|
||||
"🍋 **Triple Lemon** — ×1.5",
|
||||
"🍒 **Triple Cherry** — ×2",
|
||||
"🍪 **Triple Cookie** — ×3",
|
||||
"⭐ **Triple Star** — ×5",
|
||||
"💎 **Jackpot** — ×10"
|
||||
"🍋 **Triple Lemon** — x1.5",
|
||||
"🍒 **Triple Cherry** — x2",
|
||||
"🍪 **Triple Cookie** — x3",
|
||||
"⭐ **Triple Star** — x5",
|
||||
"💎 **Jackpot** — x10"
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Your Vault",
|
||||
@@ -244,6 +246,7 @@ async function showSlots(
|
||||
value:
|
||||
`**${balance.toLocaleString()} 🍪**`
|
||||
})
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbSaucerSlots:${interaction.user.id}`
|
||||
@@ -260,6 +263,7 @@ async function showSlots(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* PLAY SLOTS
|
||||
@@ -271,7 +275,7 @@ async function playSlots(
|
||||
bet
|
||||
) {
|
||||
const ownerId =
|
||||
getSlotsOwner(
|
||||
getSlotsSessionOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
@@ -308,6 +312,7 @@ async function playSlots(
|
||||
const playerId =
|
||||
interaction.user.id;
|
||||
|
||||
|
||||
const vaultId =
|
||||
interaction.client.user.id;
|
||||
|
||||
@@ -388,6 +393,7 @@ async function playSlots(
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -409,11 +415,6 @@ async function playSlots(
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* A few slow edits give a slot-machine feel
|
||||
* without hammering the Discord API.
|
||||
*/
|
||||
|
||||
await sleep(
|
||||
700
|
||||
);
|
||||
@@ -478,13 +479,11 @@ async function playSlots(
|
||||
|
||||
/*
|
||||
* House pays winnings.
|
||||
*
|
||||
* The original stake was already transferred
|
||||
* to the house, so x2 means a total payout of 2x.
|
||||
*/
|
||||
|
||||
if (
|
||||
payout > 0
|
||||
payout >
|
||||
0
|
||||
) {
|
||||
try {
|
||||
await transferCookies(
|
||||
@@ -509,6 +508,10 @@ async function playSlots(
|
||||
.setDescription(
|
||||
"The reels stopped, but the payout system had a problem. Please contact an administrator."
|
||||
)
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbSaucerSlotsResult:${playerId}:${bet}`
|
||||
})
|
||||
],
|
||||
|
||||
components:
|
||||
@@ -543,6 +546,7 @@ async function playSlots(
|
||||
.setTitle(
|
||||
"🎰 Crumb Saucer Slots"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
buildSlotDisplay(
|
||||
@@ -552,6 +556,7 @@ async function playSlots(
|
||||
`### ${getSlotResultName(outcome)}`
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields(
|
||||
{
|
||||
name:
|
||||
@@ -569,7 +574,7 @@ async function playSlots(
|
||||
"Multiplier",
|
||||
|
||||
value:
|
||||
`×${outcome.multiplier}`,
|
||||
`x${outcome.multiplier}`,
|
||||
|
||||
inline:
|
||||
true
|
||||
@@ -619,9 +624,17 @@ async function playSlots(
|
||||
true
|
||||
}
|
||||
)
|
||||
|
||||
/*
|
||||
* Store owner + previous wager.
|
||||
*
|
||||
* Run It Back can now replay the exact
|
||||
* same wager without opening the bet menu.
|
||||
*/
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbSaucerSlots:${interaction.user.id}`
|
||||
`crumbSaucerSlotsResult:${playerId}:${bet}`
|
||||
});
|
||||
|
||||
|
||||
@@ -636,6 +649,88 @@ async function playSlots(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* RUN IT BACK
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function runSlotsBack(
|
||||
interaction
|
||||
) {
|
||||
const footer =
|
||||
interaction.message
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
|
||||
const parts =
|
||||
footer?.split(":")
|
||||
??
|
||||
[];
|
||||
|
||||
|
||||
if (
|
||||
parts[0] !==
|
||||
"crumbSaucerSlotsResult"
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"This Slots result can no longer be replayed."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const ownerId =
|
||||
parts[1];
|
||||
|
||||
|
||||
const bet =
|
||||
Number(
|
||||
parts[2]
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"These aren't your slots."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
!SLOT_BETS.includes(
|
||||
bet
|
||||
)
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"The previous wager is no longer available."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return playSlots(
|
||||
interaction,
|
||||
bet
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* MAIN MENU EMBED
|
||||
@@ -650,6 +745,7 @@ function buildMainMenuEmbed(
|
||||
.setTitle(
|
||||
"🎰 The Crumb Saucer"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
`Welcome, ${user}!`,
|
||||
@@ -672,6 +768,7 @@ function buildMainMenuEmbed(
|
||||
"*More attractions coming soon... make a lala offering or buy me a white monster*"
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Your Vault",
|
||||
@@ -679,6 +776,7 @@ function buildMainMenuEmbed(
|
||||
value:
|
||||
`**${balance.toLocaleString()} 🍪**`
|
||||
})
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`crumbSaucer:${user.id}`
|
||||
@@ -688,7 +786,7 @@ function buildMainMenuEmbed(
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* BUTTONS
|
||||
* MAIN MENU BUTTONS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
@@ -755,6 +853,13 @@ function buildMainMenuButtons() {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* SLOT BET BUTTONS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function buildSlotBetButtons() {
|
||||
return [
|
||||
new ActionRowBuilder()
|
||||
@@ -850,16 +955,44 @@ function buildSlotBetButtons() {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* SLOT RESULT BUTTONS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function buildResultButtons() {
|
||||
return [
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
/*
|
||||
* Exact same wager immediately.
|
||||
*/
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"crumbSaucerSlotsRunItBack"
|
||||
)
|
||||
.setLabel(
|
||||
"Run It Back"
|
||||
)
|
||||
.setEmoji(
|
||||
"🔁"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Success
|
||||
),
|
||||
|
||||
/*
|
||||
* Return to wager selection.
|
||||
*/
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"crumbSaucerSlots"
|
||||
)
|
||||
.setLabel(
|
||||
"Play Again"
|
||||
"Change Bet"
|
||||
)
|
||||
.setEmoji(
|
||||
"🎰"
|
||||
@@ -868,6 +1001,10 @@ function buildResultButtons() {
|
||||
ButtonStyle.Primary
|
||||
),
|
||||
|
||||
/*
|
||||
* Main Saucer.
|
||||
*/
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"crumbSaucerBack"
|
||||
@@ -920,7 +1057,8 @@ function rollSlotOutcome() {
|
||||
|
||||
|
||||
if (
|
||||
roll <= 0
|
||||
roll <=
|
||||
0
|
||||
) {
|
||||
return outcome;
|
||||
}
|
||||
@@ -959,10 +1097,6 @@ function generateOutcomeGrid(
|
||||
generateRandomGrid();
|
||||
|
||||
|
||||
/*
|
||||
* Winning middle row.
|
||||
*/
|
||||
|
||||
if (
|
||||
outcome.type ===
|
||||
"triple"
|
||||
@@ -978,10 +1112,6 @@ function generateOutcomeGrid(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pair.
|
||||
*/
|
||||
|
||||
if (
|
||||
outcome.type ===
|
||||
"pair"
|
||||
@@ -1002,14 +1132,11 @@ function generateOutcomeGrid(
|
||||
);
|
||||
|
||||
} while (
|
||||
other === symbol
|
||||
other ===
|
||||
symbol
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Randomize the location of the odd symbol.
|
||||
*/
|
||||
|
||||
const oddPosition =
|
||||
Math.floor(
|
||||
Math.random() *
|
||||
@@ -1034,11 +1161,6 @@ function generateOutcomeGrid(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Loss:
|
||||
* ensure all three center symbols differ.
|
||||
*/
|
||||
|
||||
let line;
|
||||
|
||||
|
||||
@@ -1095,6 +1217,7 @@ function buildSpinningEmbed(
|
||||
.setTitle(
|
||||
"🎰 Crumb Saucer Slots"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
buildSlotDisplay(
|
||||
@@ -1104,6 +1227,7 @@ function buildSpinningEmbed(
|
||||
`**${status}**`
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Bet",
|
||||
@@ -1134,23 +1258,28 @@ function getSlotResultName(
|
||||
|
||||
|
||||
switch (
|
||||
outcome.symbol
|
||||
outcome.symbol
|
||||
) {
|
||||
case "🍋":
|
||||
return "🍋 Triple Lemon!";
|
||||
|
||||
|
||||
case "🍒":
|
||||
return "🍒 Triple Cherry!";
|
||||
|
||||
|
||||
case "🍪":
|
||||
return "🍪 Triple Cookie!";
|
||||
|
||||
|
||||
case "⭐":
|
||||
return "⭐ Triple Star!";
|
||||
|
||||
|
||||
case "💎":
|
||||
return "💎 JACKPOT! 💎";
|
||||
|
||||
|
||||
default:
|
||||
return "Winner!";
|
||||
}
|
||||
@@ -1159,75 +1288,166 @@ function getSlotResultName(
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* OWNER RESOLUTION
|
||||
* SESSION OWNER RESOLUTION
|
||||
* ============================================================
|
||||
*
|
||||
* This intentionally understands every Crumb Saucer
|
||||
* screen that can navigate back to the main menu.
|
||||
*
|
||||
* That prevents another player from pressing the
|
||||
* Back / Crumb Saucer button on somebody else's
|
||||
* public interaction.
|
||||
*/
|
||||
|
||||
function getCrumbSaucerSessionOwner(
|
||||
interaction
|
||||
) {
|
||||
const footer =
|
||||
interaction.message
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
|
||||
const parts =
|
||||
footer?.split(":")
|
||||
??
|
||||
[];
|
||||
|
||||
|
||||
switch (
|
||||
parts[0]
|
||||
) {
|
||||
/*
|
||||
* Main menu
|
||||
*/
|
||||
case "crumbSaucer":
|
||||
|
||||
/*
|
||||
* Slots wager menu
|
||||
*/
|
||||
case "crumbSaucerSlots":
|
||||
|
||||
/*
|
||||
* Slots completed result
|
||||
*/
|
||||
case "crumbSaucerSlotsResult":
|
||||
|
||||
/*
|
||||
* Blackjack wager/result menu
|
||||
*/
|
||||
case "crumbSaucerBlackjack":
|
||||
|
||||
case "crumbSaucerBlackjackResult":
|
||||
|
||||
/*
|
||||
* Roulette
|
||||
*/
|
||||
case "crumbRouletteMenu":
|
||||
|
||||
case "crumbRouletteBet":
|
||||
|
||||
case "crumbRouletteResult":
|
||||
|
||||
/*
|
||||
* Heist
|
||||
*/
|
||||
case "crumbSaucerHeist":
|
||||
|
||||
case "cookieHeist":
|
||||
|
||||
return parts[1];
|
||||
|
||||
|
||||
/*
|
||||
* Active Blackjack footer:
|
||||
*
|
||||
* crumbBlackjack:<gameId>:<userId>
|
||||
*/
|
||||
|
||||
case "crumbBlackjack":
|
||||
|
||||
return parts[2];
|
||||
|
||||
|
||||
default:
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* SLOT OWNER RESOLUTION
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function getMenuOwner(
|
||||
function getSlotsSessionOwner(
|
||||
interaction
|
||||
) {
|
||||
const footer =
|
||||
interaction.message
|
||||
.embeds[0]
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
|
||||
const [
|
||||
type,
|
||||
userId
|
||||
] =
|
||||
footer
|
||||
?.split(":")
|
||||
const parts =
|
||||
footer?.split(":")
|
||||
??
|
||||
[];
|
||||
|
||||
|
||||
/*
|
||||
* Main Crumb Saucer.
|
||||
*/
|
||||
|
||||
if (
|
||||
type !==
|
||||
parts[0] ===
|
||||
"crumbSaucer"
|
||||
) {
|
||||
return null;
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
return userId;
|
||||
}
|
||||
|
||||
|
||||
function getSlotsOwner(
|
||||
interaction
|
||||
) {
|
||||
const footer =
|
||||
interaction.message
|
||||
.embeds[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
|
||||
const [
|
||||
type,
|
||||
userId
|
||||
] =
|
||||
footer
|
||||
?.split(":")
|
||||
??
|
||||
[];
|
||||
|
||||
/*
|
||||
* Slots wager menu.
|
||||
*/
|
||||
|
||||
if (
|
||||
type !==
|
||||
parts[0] ===
|
||||
"crumbSaucerSlots"
|
||||
) {
|
||||
return null;
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
return userId;
|
||||
/*
|
||||
* Completed Slots result.
|
||||
*/
|
||||
|
||||
if (
|
||||
parts[0] ===
|
||||
"crumbSaucerSlotsResult"
|
||||
) {
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
function sleep(ms) {
|
||||
/*
|
||||
* ============================================================
|
||||
* SLEEP
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function sleep(
|
||||
ms
|
||||
) {
|
||||
return new Promise(
|
||||
resolve =>
|
||||
setTimeout(
|
||||
@@ -1238,8 +1458,16 @@ function sleep(ms) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* EXPORTS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
showCrumbSaucer,
|
||||
|
||||
showSlots,
|
||||
playSlots
|
||||
playSlots,
|
||||
runSlotsBack
|
||||
};
|
||||
Reference in New Issue
Block a user