converted ShaiBot casino to Nut Economy
This commit is contained in:
@@ -0,0 +1,795 @@
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle
|
||||
} = require("discord.js");
|
||||
|
||||
const {
|
||||
getNutBalance,
|
||||
transferNuts,
|
||||
temporaryEphemeralReply,
|
||||
randomItem
|
||||
} = require("./nutEconomy");
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* CONFIG
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
const SLOT_BETS = [
|
||||
10,
|
||||
50,
|
||||
100,
|
||||
500,
|
||||
1000
|
||||
];
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* NUT SYMBOLS
|
||||
* ============================================================
|
||||
*
|
||||
* All symbols are nut-themed.
|
||||
*
|
||||
* 🌰 Chestnut
|
||||
* 🥜 Peanuts
|
||||
* 🥥 Coconut
|
||||
* 🍩 Doughnut
|
||||
*/
|
||||
|
||||
const SYMBOLS = [
|
||||
"🌰",
|
||||
"🥜",
|
||||
"🥥",
|
||||
"🍩"
|
||||
];
|
||||
|
||||
|
||||
/*
|
||||
* Weighted reel.
|
||||
*
|
||||
* Chestnuts are the common symbol.
|
||||
* Peanuts are less common.
|
||||
* Coconuts are rare.
|
||||
* Doughnuts are the jackpot symbol.
|
||||
*/
|
||||
|
||||
const REEL = [
|
||||
"🌰",
|
||||
"🌰",
|
||||
"🌰",
|
||||
"🌰",
|
||||
"🌰",
|
||||
|
||||
"🥜",
|
||||
"🥜",
|
||||
"🥜",
|
||||
"🥜",
|
||||
|
||||
"🥥",
|
||||
"🥥",
|
||||
|
||||
"🍩"
|
||||
];
|
||||
|
||||
|
||||
/*
|
||||
* Triple-symbol payouts.
|
||||
*
|
||||
* Payout includes the original wager.
|
||||
*/
|
||||
|
||||
const PAYOUTS = {
|
||||
"🌰":
|
||||
1.5,
|
||||
|
||||
"🥜":
|
||||
2,
|
||||
|
||||
"🥥":
|
||||
4,
|
||||
|
||||
"🍩":
|
||||
10
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* SHOW SLOTS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function showNutSlots(
|
||||
interaction
|
||||
) {
|
||||
|
||||
const ownerId =
|
||||
getNutSlotsOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"Das ist nicht deine Nut-Hub-Session."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Acknowledge the button immediately.
|
||||
*/
|
||||
await interaction.deferUpdate();
|
||||
|
||||
|
||||
const balance =
|
||||
await getNutBalance(
|
||||
interaction.user.id
|
||||
);
|
||||
|
||||
|
||||
const embed =
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🎰 Nut Slots"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
"Dreh die Walzen und jag den **Nut Jackpot**.",
|
||||
"",
|
||||
"### 🥜 Payouts",
|
||||
"🌰 🌰 🌰 — **×1.5**",
|
||||
"🥜 🥜 🥜 — **×2**",
|
||||
"🥥 🥥 🥥 — **×4**",
|
||||
"🍩 🍩 🍩 — **×10**",
|
||||
"",
|
||||
"Choose your wager."
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Deine Nüsse",
|
||||
|
||||
value:
|
||||
`**${balance.toLocaleString()} 🥜**`
|
||||
})
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`nutSlots:${interaction.user.id}`
|
||||
});
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
components:
|
||||
buildSlotBetButtons()
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* PLAY
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function playNutSlots(
|
||||
interaction,
|
||||
bet
|
||||
) {
|
||||
if (
|
||||
!SLOT_BETS.includes(
|
||||
bet
|
||||
)
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"Dieser Einsatz ist nicht verfügbar."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const ownerId =
|
||||
getNutSlotsOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"Das ist nicht deine Slots-Session."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const playerId =
|
||||
interaction.user.id;
|
||||
|
||||
|
||||
const houseId =
|
||||
interaction.client.user.id;
|
||||
|
||||
|
||||
const [
|
||||
playerBalance,
|
||||
houseBalance
|
||||
] =
|
||||
await Promise.all([
|
||||
getNutBalance(
|
||||
playerId
|
||||
),
|
||||
|
||||
getNutBalance(
|
||||
houseId
|
||||
)
|
||||
]);
|
||||
|
||||
|
||||
if (
|
||||
playerBalance <
|
||||
bet
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
`Du brauchst **${bet.toLocaleString()} Nüsse** für diesen Einsatz.`
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Maximum possible return = x10.
|
||||
*
|
||||
* After receiving the wager, the house must
|
||||
* still be able to cover the total payout.
|
||||
*/
|
||||
|
||||
const maxPayout =
|
||||
bet *
|
||||
10;
|
||||
|
||||
|
||||
if (
|
||||
houseBalance +
|
||||
bet <
|
||||
maxPayout
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"Der Nut Vault kann diesen Einsatz aktuell nicht absichern."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await transferNuts(
|
||||
playerId,
|
||||
houseId,
|
||||
bet
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
if (
|
||||
error.message ===
|
||||
"INSUFFICIENT_NUTS"
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"Dein Nut-Bestand hat sich verändert."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initial animation state.
|
||||
*/
|
||||
|
||||
await interaction.update({
|
||||
embeds: [
|
||||
buildSpinEmbed(
|
||||
bet,
|
||||
[
|
||||
"❔",
|
||||
"❔",
|
||||
"❔"
|
||||
],
|
||||
"Die Walzen drehen..."
|
||||
)
|
||||
],
|
||||
|
||||
components: []
|
||||
});
|
||||
|
||||
|
||||
await delay(
|
||||
500
|
||||
);
|
||||
|
||||
|
||||
await interaction.editReply({
|
||||
embeds: [
|
||||
buildSpinEmbed(
|
||||
bet,
|
||||
[
|
||||
randomReelSymbol(),
|
||||
"❔",
|
||||
"❔"
|
||||
],
|
||||
"Die erste Walze stoppt..."
|
||||
)
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
await delay(
|
||||
500
|
||||
);
|
||||
|
||||
|
||||
const first =
|
||||
randomReelSymbol();
|
||||
|
||||
|
||||
const second =
|
||||
randomReelSymbol();
|
||||
|
||||
|
||||
await interaction.editReply({
|
||||
embeds: [
|
||||
buildSpinEmbed(
|
||||
bet,
|
||||
[
|
||||
first,
|
||||
second,
|
||||
"❔"
|
||||
],
|
||||
"Noch eine..."
|
||||
)
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
await delay(
|
||||
600
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Final actual result.
|
||||
*
|
||||
* Generate all three here so animation values
|
||||
* don't alter game odds.
|
||||
*/
|
||||
|
||||
const result = [
|
||||
randomReelSymbol(),
|
||||
randomReelSymbol(),
|
||||
randomReelSymbol()
|
||||
];
|
||||
|
||||
|
||||
const multiplier =
|
||||
getSlotMultiplier(
|
||||
result
|
||||
);
|
||||
|
||||
|
||||
const payout =
|
||||
Math.floor(
|
||||
bet *
|
||||
multiplier
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
payout >
|
||||
0
|
||||
) {
|
||||
await transferNuts(
|
||||
houseId,
|
||||
playerId,
|
||||
payout
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const balanceAfter =
|
||||
await getNutBalance(
|
||||
playerId
|
||||
);
|
||||
|
||||
|
||||
const profit =
|
||||
payout -
|
||||
bet;
|
||||
|
||||
|
||||
const embed =
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
multiplier >
|
||||
0
|
||||
? "🎰 Nut Slots — WIN!"
|
||||
: "🎰 Nut Slots"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
"## " +
|
||||
result.join(
|
||||
" "
|
||||
),
|
||||
"",
|
||||
multiplier >
|
||||
0
|
||||
? `🎉 **x${multiplier}**`
|
||||
: "Die Nüsse bleiben beim Haus."
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields(
|
||||
{
|
||||
name:
|
||||
"Einsatz",
|
||||
|
||||
value:
|
||||
`${bet.toLocaleString()} 🥜`,
|
||||
|
||||
inline:
|
||||
true
|
||||
},
|
||||
|
||||
{
|
||||
name:
|
||||
"Payout",
|
||||
|
||||
value:
|
||||
`${payout.toLocaleString()} 🥜`,
|
||||
|
||||
inline:
|
||||
true
|
||||
},
|
||||
|
||||
{
|
||||
name:
|
||||
"Profit",
|
||||
|
||||
value:
|
||||
`${profit >= 0 ? "+" : ""}${profit.toLocaleString()} 🥜`,
|
||||
|
||||
inline:
|
||||
true
|
||||
},
|
||||
|
||||
{
|
||||
name:
|
||||
"Deine Nüsse",
|
||||
|
||||
value:
|
||||
`${balanceAfter.toLocaleString()} 🥜`
|
||||
}
|
||||
)
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`nutSlots:${interaction.user.id}`
|
||||
});
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
components:
|
||||
buildSlotResultButtons()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* PAYOUT
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function getSlotMultiplier(
|
||||
result
|
||||
) {
|
||||
const [
|
||||
first,
|
||||
second,
|
||||
third
|
||||
] = result;
|
||||
|
||||
|
||||
/*
|
||||
* Triple.
|
||||
*/
|
||||
|
||||
if (
|
||||
first === second &&
|
||||
second === third
|
||||
) {
|
||||
return PAYOUTS[first] ?? 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Two matching symbols.
|
||||
*
|
||||
* Half the wager is returned.
|
||||
*/
|
||||
|
||||
if (
|
||||
first === second ||
|
||||
first === third ||
|
||||
second === third
|
||||
) {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Nut Salad:
|
||||
*
|
||||
* Chestnut + Peanut + Coconut
|
||||
* in any order.
|
||||
*
|
||||
* Returns the wager.
|
||||
*/
|
||||
|
||||
const nutSalad =
|
||||
new Set(
|
||||
result
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
nutSalad.size === 3 &&
|
||||
nutSalad.has("🌰") &&
|
||||
nutSalad.has("🥜") &&
|
||||
nutSalad.has("🥥")
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* DISPLAY
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function buildSpinEmbed(
|
||||
bet,
|
||||
symbols,
|
||||
status
|
||||
) {
|
||||
return new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🎰 Nut Slots"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
"## " +
|
||||
symbols.join(
|
||||
" "
|
||||
),
|
||||
"",
|
||||
`**${status}**`
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Einsatz",
|
||||
|
||||
value:
|
||||
`${bet.toLocaleString()} 🥜`
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* BUTTONS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function buildSlotBetButtons() {
|
||||
return [
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
...SLOT_BETS.map(
|
||||
bet =>
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
`nutSlotsBet${bet}`
|
||||
)
|
||||
.setLabel(
|
||||
bet.toLocaleString()
|
||||
)
|
||||
.setEmoji(
|
||||
"🥜"
|
||||
)
|
||||
.setStyle(
|
||||
bet >=
|
||||
500
|
||||
? ButtonStyle.Primary
|
||||
: ButtonStyle.Secondary
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"nutHubBack"
|
||||
)
|
||||
.setLabel(
|
||||
"Nut Hub"
|
||||
)
|
||||
.setEmoji(
|
||||
"◀️"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Secondary
|
||||
)
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function buildSlotResultButtons() {
|
||||
return [
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"nutHubSlots"
|
||||
)
|
||||
.setLabel(
|
||||
"Play Again"
|
||||
)
|
||||
.setEmoji(
|
||||
"🎰"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Primary
|
||||
),
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"nutHubBack"
|
||||
)
|
||||
.setLabel(
|
||||
"Nut Hub"
|
||||
)
|
||||
.setEmoji(
|
||||
"◀️"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Secondary
|
||||
)
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* SESSION OWNER
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function getNutSlotsOwner(
|
||||
interaction
|
||||
) {
|
||||
const footer =
|
||||
interaction.message
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
|
||||
const parts =
|
||||
footer?.split(":")
|
||||
??
|
||||
[];
|
||||
|
||||
|
||||
if (
|
||||
parts[0] ===
|
||||
"nutHub"
|
||||
) {
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
parts[0] ===
|
||||
"nutSlots"
|
||||
) {
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* RANDOM
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function randomReelSymbol() {
|
||||
return randomItem(
|
||||
REEL
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function delay(
|
||||
ms
|
||||
) {
|
||||
return new Promise(
|
||||
resolve =>
|
||||
setTimeout(
|
||||
resolve,
|
||||
ms
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
showNutSlots,
|
||||
playNutSlots
|
||||
};
|
||||
Reference in New Issue
Block a user