Files
Arthonor-Neo/utils/nutSlots.js
T

795 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
};