converted ShaiBot casino to Nut Economy
This commit is contained in:
@@ -0,0 +1,777 @@
|
||||
// utils/nutHeist.js
|
||||
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
MessageFlags
|
||||
} = require("discord.js");
|
||||
|
||||
const {
|
||||
getNutBalance,
|
||||
transferNuts,
|
||||
claimCooldown
|
||||
} = require("./nutEconomy");
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* CONFIG
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
const HEIST_COOLDOWN =
|
||||
60 * 60;
|
||||
|
||||
const HEIST_ACTIONS = {
|
||||
Sneak: {
|
||||
label:
|
||||
"Sneak",
|
||||
|
||||
emoji:
|
||||
"🥷",
|
||||
|
||||
successChance:
|
||||
0.55,
|
||||
|
||||
minReward:
|
||||
10,
|
||||
|
||||
maxReward:
|
||||
100,
|
||||
|
||||
minLoss:
|
||||
5,
|
||||
|
||||
maxLoss:
|
||||
40
|
||||
},
|
||||
|
||||
Hack: {
|
||||
label:
|
||||
"Hack",
|
||||
|
||||
emoji:
|
||||
"💻",
|
||||
|
||||
successChance:
|
||||
0.40,
|
||||
|
||||
minReward:
|
||||
50,
|
||||
|
||||
maxReward:
|
||||
250,
|
||||
|
||||
minLoss:
|
||||
20,
|
||||
|
||||
maxLoss:
|
||||
100
|
||||
},
|
||||
|
||||
Smash: {
|
||||
label:
|
||||
"Smash",
|
||||
|
||||
emoji:
|
||||
"💥",
|
||||
|
||||
successChance:
|
||||
0.25,
|
||||
|
||||
minReward:
|
||||
150,
|
||||
|
||||
maxReward:
|
||||
500,
|
||||
|
||||
minLoss:
|
||||
50,
|
||||
|
||||
maxLoss:
|
||||
200
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* HELPERS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function randomInteger(
|
||||
min,
|
||||
max
|
||||
) {
|
||||
return Math.floor(
|
||||
Math.random() *
|
||||
(
|
||||
max -
|
||||
min +
|
||||
1
|
||||
)
|
||||
) + min;
|
||||
}
|
||||
|
||||
|
||||
function getHeistOwner(
|
||||
interaction
|
||||
) {
|
||||
const footer =
|
||||
interaction.message
|
||||
?.embeds?.[0]
|
||||
?.footer
|
||||
?.text;
|
||||
|
||||
const parts =
|
||||
footer?.split(":")
|
||||
??
|
||||
[];
|
||||
|
||||
|
||||
/*
|
||||
* Coming from Nut Hub.
|
||||
*
|
||||
* nutHub:<userId>
|
||||
*/
|
||||
|
||||
if (
|
||||
parts[0] ===
|
||||
"nutHub"
|
||||
) {
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Coming from Heist.
|
||||
*
|
||||
* nutHeist:<userId>
|
||||
*/
|
||||
|
||||
if (
|
||||
parts[0] ===
|
||||
"nutHeist"
|
||||
) {
|
||||
return parts[1];
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
function buildHeistButtons() {
|
||||
return [
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"nutHeistSneak"
|
||||
)
|
||||
.setLabel(
|
||||
"Sneak"
|
||||
)
|
||||
.setEmoji(
|
||||
"🥷"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Success
|
||||
),
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"nutHeistHack"
|
||||
)
|
||||
.setLabel(
|
||||
"Hack"
|
||||
)
|
||||
.setEmoji(
|
||||
"💻"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Primary
|
||||
),
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"nutHeistSmash"
|
||||
)
|
||||
.setLabel(
|
||||
"Smash"
|
||||
)
|
||||
.setEmoji(
|
||||
"💥"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Danger
|
||||
)
|
||||
),
|
||||
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"nutHubBack"
|
||||
)
|
||||
.setLabel(
|
||||
"Nut Hub"
|
||||
)
|
||||
.setEmoji(
|
||||
"◀️"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Secondary
|
||||
)
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function buildReturnButtons() {
|
||||
return [
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"nutHubHeist"
|
||||
)
|
||||
.setLabel(
|
||||
"Heist Menu"
|
||||
)
|
||||
.setEmoji(
|
||||
"🏦"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Primary
|
||||
),
|
||||
|
||||
new ButtonBuilder()
|
||||
.setCustomId(
|
||||
"nutHubBack"
|
||||
)
|
||||
.setLabel(
|
||||
"Nut Hub"
|
||||
)
|
||||
.setEmoji(
|
||||
"◀️"
|
||||
)
|
||||
.setStyle(
|
||||
ButtonStyle.Secondary
|
||||
)
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* TEMPORARY EPHEMERAL MESSAGE
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function temporaryEphemeralReply(
|
||||
interaction,
|
||||
options
|
||||
) {
|
||||
const reply =
|
||||
await interaction.reply({
|
||||
...options,
|
||||
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Ephemeral messages cannot truly be given
|
||||
* a Discord-side expiration time.
|
||||
*
|
||||
* We attempt to delete the reply after 5 seconds.
|
||||
*/
|
||||
|
||||
setTimeout(
|
||||
async () => {
|
||||
try {
|
||||
await interaction.deleteReply();
|
||||
}
|
||||
|
||||
catch {
|
||||
// Ignore expired/deleted interaction.
|
||||
}
|
||||
},
|
||||
5000
|
||||
);
|
||||
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* SHOW HEIST MENU
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function showNutHeist(
|
||||
interaction
|
||||
) {
|
||||
const ownerId =
|
||||
getHeistOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"Das ist nicht deine Nut-Hub-Session."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
await interaction.deferUpdate();
|
||||
|
||||
|
||||
const balance =
|
||||
await getNutBalance(
|
||||
interaction.user.id
|
||||
);
|
||||
|
||||
|
||||
const embed =
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🏦 Nut Vault Heist"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
"Arthonor bewacht seinen Vorrat an Nüssen.",
|
||||
"",
|
||||
"Wähle deinen Ansatz.",
|
||||
"",
|
||||
"### 🥷 Sneak",
|
||||
"**55%** Erfolgschance",
|
||||
"Kleine Beute, geringes Risiko.",
|
||||
"",
|
||||
"### 💻 Hack",
|
||||
"**40%** Erfolgschance",
|
||||
"Mittlere Beute, mittleres Risiko.",
|
||||
"",
|
||||
"### 💥 Smash",
|
||||
"**25%** Erfolgschance",
|
||||
"Große Beute, hohes Risiko.",
|
||||
"",
|
||||
`Ein Heist hat **${Math.floor(HEIST_COOLDOWN / 60)} Minuten Cooldown**.`
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Deine Nüsse",
|
||||
|
||||
value:
|
||||
`**${balance.toLocaleString()} 🥜**`
|
||||
})
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`nutHeist:${interaction.user.id}`
|
||||
});
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
components:
|
||||
buildHeistButtons()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* PERFORM HEIST
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
async function performNutHeist(
|
||||
interaction,
|
||||
actionName
|
||||
) {
|
||||
const ownerId =
|
||||
getHeistOwner(
|
||||
interaction
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Prevent another player from using
|
||||
* somebody else's Heist menu.
|
||||
*/
|
||||
|
||||
if (
|
||||
ownerId !==
|
||||
interaction.user.id
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"Das ist nicht dein Heist."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const action =
|
||||
HEIST_ACTIONS[
|
||||
actionName
|
||||
];
|
||||
|
||||
|
||||
if (
|
||||
!action
|
||||
) {
|
||||
return temporaryEphemeralReply(
|
||||
interaction,
|
||||
{
|
||||
content:
|
||||
"Unbekannte Heist-Aktion."
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Acknowledge immediately.
|
||||
*/
|
||||
|
||||
await interaction.deferUpdate();
|
||||
|
||||
|
||||
/*
|
||||
* ========================================================
|
||||
* CLAIM SHARED HEIST COOLDOWN
|
||||
* ========================================================
|
||||
*
|
||||
* Sneak / Hack / Smash all use the same field:
|
||||
*
|
||||
* items_cooldowns
|
||||
*
|
||||
* {
|
||||
* userId: "...",
|
||||
* nut: ...,
|
||||
* heist: ...
|
||||
* }
|
||||
*/
|
||||
|
||||
const cooldown =
|
||||
await claimCooldown(
|
||||
interaction.user.id,
|
||||
"heist",
|
||||
HEIST_COOLDOWN
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
!cooldown.claimed
|
||||
) {
|
||||
const embed =
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🏦 Vault Locked Down"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
"Arthonor ist noch auf der Hut.",
|
||||
"",
|
||||
`Du kannst <t:${cooldown.next}:R> wieder einen Heist versuchen.`
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`nutHeist:${interaction.user.id}`
|
||||
});
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
components:
|
||||
buildReturnButtons()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ========================================================
|
||||
* ROLL RESULT
|
||||
* ========================================================
|
||||
*/
|
||||
|
||||
const success =
|
||||
Math.random() <
|
||||
action.successChance;
|
||||
|
||||
|
||||
const playerId =
|
||||
interaction.user.id;
|
||||
|
||||
|
||||
/*
|
||||
* Arthonor himself acts as the vault.
|
||||
*/
|
||||
|
||||
const vaultId =
|
||||
interaction.client.user.id;
|
||||
|
||||
|
||||
/*
|
||||
* ========================================================
|
||||
* SUCCESS
|
||||
* ========================================================
|
||||
*/
|
||||
|
||||
if (
|
||||
success
|
||||
) {
|
||||
let reward =
|
||||
randomInteger(
|
||||
action.minReward,
|
||||
action.maxReward
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Never steal more Nuts than
|
||||
* Arthonor currently owns.
|
||||
*/
|
||||
|
||||
const vaultBalance =
|
||||
await getNutBalance(
|
||||
vaultId
|
||||
);
|
||||
|
||||
|
||||
reward =
|
||||
Math.min(
|
||||
reward,
|
||||
vaultBalance
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
reward <=
|
||||
0
|
||||
) {
|
||||
const embed =
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🏦 The Vault Is Empty"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
`${action.emoji} **${action.label}** succeeded...`,
|
||||
"",
|
||||
"but Arthonor's Nut stash is empty.",
|
||||
"",
|
||||
"You escape with nothing."
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`nutHeist:${playerId}`
|
||||
});
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
components:
|
||||
buildReturnButtons()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
await transferNuts(
|
||||
vaultId,
|
||||
playerId,
|
||||
reward
|
||||
);
|
||||
|
||||
|
||||
const balance =
|
||||
await getNutBalance(
|
||||
playerId
|
||||
);
|
||||
|
||||
|
||||
const embed =
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"💰 Heist Successful!"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
[
|
||||
`${action.emoji} **${action.label}** worked!`,
|
||||
"",
|
||||
`You stole **${reward.toLocaleString()} 🥜** from Arthonor's stash.`
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Deine Nüsse",
|
||||
|
||||
value:
|
||||
`**${balance.toLocaleString()} 🥜**`
|
||||
})
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`nutHeist:${playerId}`
|
||||
});
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
components:
|
||||
buildReturnButtons()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ========================================================
|
||||
* FAILURE
|
||||
* ========================================================
|
||||
*/
|
||||
|
||||
let loss =
|
||||
randomInteger(
|
||||
action.minLoss,
|
||||
action.maxLoss
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Don't take more than the player owns.
|
||||
*/
|
||||
|
||||
const playerBalance =
|
||||
await getNutBalance(
|
||||
playerId
|
||||
);
|
||||
|
||||
|
||||
loss =
|
||||
Math.min(
|
||||
loss,
|
||||
playerBalance
|
||||
);
|
||||
|
||||
|
||||
if (
|
||||
loss >
|
||||
0
|
||||
) {
|
||||
await transferNuts(
|
||||
playerId,
|
||||
vaultId,
|
||||
loss
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const balance =
|
||||
await getNutBalance(
|
||||
playerId
|
||||
);
|
||||
|
||||
|
||||
const embed =
|
||||
new EmbedBuilder()
|
||||
.setTitle(
|
||||
"🚨 Heist Failed!"
|
||||
)
|
||||
|
||||
.setDescription(
|
||||
loss > 0
|
||||
? [
|
||||
`${action.emoji} **${action.label}** went wrong.`,
|
||||
"",
|
||||
"Arthonor caught you.",
|
||||
"",
|
||||
`You lost **${loss.toLocaleString()} 🥜**.`
|
||||
].join("\n")
|
||||
|
||||
: [
|
||||
`${action.emoji} **${action.label}** went wrong.`,
|
||||
"",
|
||||
"Arthonor caught you...",
|
||||
"",
|
||||
"but you didn't have any Nuts for him to take."
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
.addFields({
|
||||
name:
|
||||
"Deine Nüsse",
|
||||
|
||||
value:
|
||||
`**${balance.toLocaleString()} 🥜**`
|
||||
})
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`nutHeist:${playerId}`
|
||||
});
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
components:
|
||||
buildReturnButtons()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============================================================
|
||||
* EXPORTS
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
showNutHeist,
|
||||
performNutHeist
|
||||
};
|
||||
Reference in New Issue
Block a user