added more steal logic
This commit is contained in:
+452
-107
@@ -1,6 +1,7 @@
|
|||||||
const {
|
const {
|
||||||
SlashCommandBuilder,
|
SlashCommandBuilder,
|
||||||
EmbedBuilder
|
EmbedBuilder,
|
||||||
|
MessageFlags
|
||||||
} = require("discord.js");
|
} = require("discord.js");
|
||||||
|
|
||||||
const { mClient } = require("../..");
|
const { mClient } = require("../..");
|
||||||
@@ -143,8 +144,13 @@ async function getCookieBalance(userId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function changeCookies(userId, amount, session = undefined) {
|
async function changeCookies(
|
||||||
const { cookies } = getCollections();
|
userId,
|
||||||
|
amount
|
||||||
|
) {
|
||||||
|
const { cookies } =
|
||||||
|
getCollections();
|
||||||
|
|
||||||
|
|
||||||
return cookies.updateOne(
|
return cookies.updateOne(
|
||||||
{
|
{
|
||||||
@@ -152,15 +158,17 @@ async function changeCookies(userId, amount, session = undefined) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
$inc: {
|
$inc: {
|
||||||
cookies: amount
|
cookies:
|
||||||
|
amount
|
||||||
},
|
},
|
||||||
|
|
||||||
$setOnInsert: {
|
$setOnInsert: {
|
||||||
userId
|
userId
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
upsert: true,
|
upsert:
|
||||||
session
|
true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -168,70 +176,108 @@ async function changeCookies(userId, amount, session = undefined) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* COOKIE TRANSFER
|
* COOKIE TRANSFER
|
||||||
*
|
|
||||||
* This uses a transaction so both balances update together.
|
|
||||||
*
|
|
||||||
* MongoDB transactions require Atlas or a replica set.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function transferCookies(fromUserId, toUserId, amount) {
|
async function transferCookies(
|
||||||
const { cookies } = getCollections();
|
fromUserId,
|
||||||
|
toUserId,
|
||||||
|
amount
|
||||||
|
) {
|
||||||
|
const { cookies } =
|
||||||
|
getCollections();
|
||||||
|
|
||||||
const session = mClient.startSession();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await session.withTransaction(async () => {
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only debit if the sender still has enough cookies.
|
* Atomically debit the sender only if
|
||||||
*
|
* they still have enough cookies.
|
||||||
* This protects against two commands being executed
|
|
||||||
* at almost the same time.
|
|
||||||
*/
|
*/
|
||||||
const debit = await cookies.updateOne(
|
const debit =
|
||||||
{
|
|
||||||
userId: fromUserId,
|
|
||||||
cookies: {
|
|
||||||
$gte: amount
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$inc: {
|
|
||||||
cookies: -amount
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
session
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
if (debit.modifiedCount !== 1) {
|
|
||||||
throw new Error("INSUFFICIENT_COOKIES");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
await cookies.updateOne(
|
await cookies.updateOne(
|
||||||
{
|
{
|
||||||
userId: toUserId
|
userId:
|
||||||
|
fromUserId,
|
||||||
|
|
||||||
|
cookies: {
|
||||||
|
$gte:
|
||||||
|
amount
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$inc: {
|
$inc: {
|
||||||
cookies: amount
|
cookies:
|
||||||
|
-amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
debit.modifiedCount !== 1
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"INSUFFICIENT_COOKIES"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Credit the recipient.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
await cookies.updateOne(
|
||||||
|
{
|
||||||
|
userId:
|
||||||
|
toUserId
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
$inc: {
|
||||||
|
cookies:
|
||||||
|
amount
|
||||||
|
},
|
||||||
|
|
||||||
$setOnInsert: {
|
$setOnInsert: {
|
||||||
userId: toUserId
|
userId:
|
||||||
|
toUserId
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
upsert: true,
|
upsert:
|
||||||
session
|
true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
} finally {
|
} catch (error) {
|
||||||
await session.endSession();
|
|
||||||
|
/*
|
||||||
|
* Credit failed.
|
||||||
|
*
|
||||||
|
* Attempt to restore the sender's
|
||||||
|
* cookies so the transfer doesn't
|
||||||
|
* destroy currency.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
await cookies.updateOne(
|
||||||
|
{
|
||||||
|
userId:
|
||||||
|
fromUserId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$inc: {
|
||||||
|
cookies:
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (rollbackError) {
|
||||||
|
console.error(
|
||||||
|
"CRITICAL: Cookie transfer rollback failed:",
|
||||||
|
rollbackError
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,45 +411,50 @@ async function collect(interaction) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
async function steal(interaction) {
|
async function steal(interaction) {
|
||||||
const user = interaction.user;
|
const user =
|
||||||
|
interaction.user;
|
||||||
|
|
||||||
const target = interaction.options.getUser(
|
const target =
|
||||||
|
interaction.options.getUser(
|
||||||
"target",
|
"target",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Can't rob yourself.
|
* VALIDATION
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (target.id === user.id) {
|
if (target.id === user.id) {
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content:
|
||||||
"C'mon, don't embarrass yourself. You can't steal from yourself.",
|
"C'mon, don't embarrass yourself. You can't steal from yourself.",
|
||||||
ephemeral: true
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Don't let people farm bot accounts.
|
|
||||||
*/
|
|
||||||
if (target.bot) {
|
if (target.bot) {
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content:
|
||||||
"Bots have highly sophisticated cookie security systems.",
|
"Bots have highly sophisticated Cookie security systems.",
|
||||||
ephemeral: true
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const now = getUnixTime();
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check steal cooldown before doing anything expensive.
|
* COOLDOWN
|
||||||
*/
|
*/
|
||||||
const cooldown = await getCooldown(
|
|
||||||
|
const now =
|
||||||
|
getUnixTime();
|
||||||
|
|
||||||
|
|
||||||
|
const cooldown =
|
||||||
|
await getCooldown(
|
||||||
user.id,
|
user.id,
|
||||||
interaction.guildId,
|
interaction.guildId,
|
||||||
"steal"
|
"steal"
|
||||||
@@ -414,20 +465,28 @@ async function steal(interaction) {
|
|||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content:
|
||||||
`Lay low for a while. You can steal again <t:${cooldown}:R>.`,
|
`Lay low for a while. You can steal again <t:${cooldown}:R>.`,
|
||||||
ephemeral: true
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch both balances concurrently.
|
* BALANCES
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const [
|
const [
|
||||||
yourCookies,
|
yourCookies,
|
||||||
theirCookies
|
theirCookies
|
||||||
] = await Promise.all([
|
] =
|
||||||
getCookieBalance(user.id),
|
await Promise.all([
|
||||||
getCookieBalance(target.id)
|
getCookieBalance(
|
||||||
|
user.id
|
||||||
|
),
|
||||||
|
|
||||||
|
getCookieBalance(
|
||||||
|
target.id
|
||||||
|
)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
@@ -435,7 +494,8 @@ async function steal(interaction) {
|
|||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content:
|
||||||
"You need at least **1 Cookie** to attempt a robbery.",
|
"You need at least **1 Cookie** to attempt a robbery.",
|
||||||
ephemeral: true
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,34 +504,46 @@ async function steal(interaction) {
|
|||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content:
|
||||||
`Shame on you! ${target} doesn't have any Cookies. D:`,
|
`Shame on you! ${target} doesn't have any Cookies. D:`,
|
||||||
ephemeral: true
|
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Chaos mode:
|
* DYNAMIC SUCCESS CHANCE
|
||||||
* fixed 50% steal chance.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const successChance =
|
||||||
|
getStealChance(
|
||||||
|
yourCookies,
|
||||||
|
theirCookies
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
const success =
|
const success =
|
||||||
Math.random() < STEAL_SUCCESS_CHANCE;
|
Math.random() <
|
||||||
|
successChance;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Successful robbery.
|
* SUCCESS
|
||||||
*/
|
*/
|
||||||
if (success) {
|
|
||||||
const maxSteal = Math.max(
|
|
||||||
1,
|
|
||||||
Math.ceil(theirCookies / 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
const amount = Math.min(
|
if (success) {
|
||||||
theirCookies,
|
|
||||||
randomInteger(
|
/*
|
||||||
1,
|
* Roll a weighted percentage
|
||||||
maxSteal
|
* of the target's entire vault.
|
||||||
)
|
*/
|
||||||
|
|
||||||
|
const {
|
||||||
|
amount,
|
||||||
|
percentage
|
||||||
|
} =
|
||||||
|
getStealAmount(
|
||||||
|
theirCookies
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@@ -481,20 +553,39 @@ async function steal(interaction) {
|
|||||||
user.id,
|
user.id,
|
||||||
amount
|
amount
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message === "INSUFFICIENT_COOKIES") {
|
|
||||||
|
/*
|
||||||
|
* Target may have spent/given/stolen
|
||||||
|
* cookies between our balance check
|
||||||
|
* and the actual transfer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (
|
||||||
|
error.message ===
|
||||||
|
"INSUFFICIENT_COOKIES"
|
||||||
|
) {
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content:
|
||||||
`${target}'s Cookie balance changed before you could finish the robbery.`,
|
`${target}'s Cookie balance changed before you could finish the robbery.`,
|
||||||
ephemeral: true
|
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const nextSteal = await setCooldown(
|
/*
|
||||||
|
* Apply cooldown after robbery.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const nextSteal =
|
||||||
|
await setCooldown(
|
||||||
user.id,
|
user.id,
|
||||||
interaction.guildId,
|
interaction.guildId,
|
||||||
"steal",
|
"steal",
|
||||||
@@ -502,30 +593,51 @@ async function steal(interaction) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const actualPercentage =
|
||||||
|
(
|
||||||
|
amount /
|
||||||
|
theirCookies *
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content:
|
||||||
`${randomStealSuccessResponse(amount, target)}\n` +
|
`${randomStealSuccessResponse(amount, target)}\n` +
|
||||||
`**Success Chance:** ${formatPercentage(STEAL_SUCCESS_CHANCE)} • ` +
|
`🍪 You made off with **${actualPercentage.toFixed(1)}%** of their vault!\n` +
|
||||||
`Next robbery <t:${nextSteal}:R>`
|
`🎲 **Success Chance:** ${formatPercentage(successChance)}\n` +
|
||||||
|
`⏱️ Next robbery <t:${nextSteal}:R>`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Failed robbery.
|
* FAILURE
|
||||||
*
|
*
|
||||||
* Lose between 1 and roughly 1/6 of your balance.
|
* Penalty is also percentage based.
|
||||||
|
*
|
||||||
|
* Roll between roughly 1–8% of the
|
||||||
|
* attacker's current vault.
|
||||||
*/
|
*/
|
||||||
const maxPenalty = Math.max(
|
|
||||||
1,
|
const penaltyPercentage =
|
||||||
Math.ceil(yourCookies / 6)
|
randomFloat(
|
||||||
|
0.01,
|
||||||
|
0.08
|
||||||
);
|
);
|
||||||
|
|
||||||
const penalty = Math.min(
|
|
||||||
yourCookies,
|
const penalty =
|
||||||
randomInteger(
|
Math.max(
|
||||||
1,
|
1,
|
||||||
maxPenalty
|
|
||||||
|
Math.min(
|
||||||
|
yourCookies,
|
||||||
|
|
||||||
|
Math.floor(
|
||||||
|
yourCookies *
|
||||||
|
penaltyPercentage
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -536,20 +648,33 @@ async function steal(interaction) {
|
|||||||
target.id,
|
target.id,
|
||||||
penalty
|
penalty
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message === "INSUFFICIENT_COOKIES") {
|
|
||||||
|
if (
|
||||||
|
error.message ===
|
||||||
|
"INSUFFICIENT_COOKIES"
|
||||||
|
) {
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content:
|
||||||
"Your Cookie balance changed before the robbery could finish.",
|
"Your Cookie balance changed before the robbery could finish.",
|
||||||
ephemeral: true
|
|
||||||
|
flags:
|
||||||
|
MessageFlags.Ephemeral
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const nextSteal = await setCooldown(
|
/*
|
||||||
|
* Apply cooldown.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const nextSteal =
|
||||||
|
await setCooldown(
|
||||||
user.id,
|
user.id,
|
||||||
interaction.guildId,
|
interaction.guildId,
|
||||||
"steal",
|
"steal",
|
||||||
@@ -557,15 +682,23 @@ async function steal(interaction) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const actualPenaltyPercentage =
|
||||||
|
(
|
||||||
|
penalty /
|
||||||
|
yourCookies *
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content:
|
||||||
`${randomStealFailResponse(penalty, target)}\n` +
|
`${randomStealFailResponse(penalty, target)}\n` +
|
||||||
`**Success Chance:** ${formatPercentage(STEAL_SUCCESS_CHANCE)} • ` +
|
`💸 You lost **${actualPenaltyPercentage.toFixed(1)}%** of your vault.\n` +
|
||||||
`Next robbery <t:${nextSteal}:R>`
|
`🎲 **Success Chance:** ${formatPercentage(successChance)}\n` +
|
||||||
|
`⏱️ Next robbery <t:${nextSteal}:R>`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* /cookies check
|
* /cookies check
|
||||||
*/
|
*/
|
||||||
@@ -793,7 +926,219 @@ function randomStealFailResponse(amount, target) {
|
|||||||
|
|
||||||
return randomItem(responses);
|
return randomItem(responses);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* STEAL HELPERS
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getStealChance(
|
||||||
|
attackerCookies,
|
||||||
|
targetCookies
|
||||||
|
) {
|
||||||
|
const MIN_CHANCE =
|
||||||
|
0.15;
|
||||||
|
|
||||||
|
const MAX_CHANCE =
|
||||||
|
0.70;
|
||||||
|
|
||||||
|
const BASE_CHANCE =
|
||||||
|
0.50;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wealthy targets are harder to rob.
|
||||||
|
*
|
||||||
|
* Logarithmic scaling prevents massive
|
||||||
|
* vaults from becoming completely immune.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const targetPenalty =
|
||||||
|
Math.log10(
|
||||||
|
targetCookies + 1
|
||||||
|
)
|
||||||
|
*
|
||||||
|
0.06;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare attacker wealth to victim.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const wealthRatio =
|
||||||
|
attackerCookies /
|
||||||
|
Math.max(
|
||||||
|
targetCookies,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Catch-up mechanic.
|
||||||
|
*
|
||||||
|
* If the attacker has less than the target,
|
||||||
|
* they gain up to +18 percentage points.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const povertyBonus =
|
||||||
|
wealthRatio < 1
|
||||||
|
? (
|
||||||
|
1 -
|
||||||
|
wealthRatio
|
||||||
|
)
|
||||||
|
*
|
||||||
|
0.18
|
||||||
|
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Anti-bullying penalty.
|
||||||
|
*
|
||||||
|
* Rich attackers targeting poorer users
|
||||||
|
* become progressively less successful.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const bullyPenalty =
|
||||||
|
wealthRatio > 1
|
||||||
|
? Math.min(
|
||||||
|
Math.log10(
|
||||||
|
wealthRatio + 1
|
||||||
|
)
|
||||||
|
*
|
||||||
|
0.12,
|
||||||
|
|
||||||
|
0.20
|
||||||
|
)
|
||||||
|
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
|
||||||
|
const chance =
|
||||||
|
BASE_CHANCE
|
||||||
|
-
|
||||||
|
targetPenalty
|
||||||
|
+
|
||||||
|
povertyBonus
|
||||||
|
-
|
||||||
|
bullyPenalty;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always keep robbery possible,
|
||||||
|
* but never make it nearly guaranteed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return Math.max(
|
||||||
|
MIN_CHANCE,
|
||||||
|
|
||||||
|
Math.min(
|
||||||
|
MAX_CHANCE,
|
||||||
|
chance
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStealAmount(
|
||||||
|
targetCookies
|
||||||
|
) {
|
||||||
|
const MAX_STEAL_AMOUNT =
|
||||||
|
5000;
|
||||||
|
|
||||||
|
|
||||||
|
const percentage =
|
||||||
|
getStealPercentage();
|
||||||
|
|
||||||
|
|
||||||
|
const calculatedAmount =
|
||||||
|
Math.floor(
|
||||||
|
targetCookies *
|
||||||
|
percentage
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const amount =
|
||||||
|
Math.max(
|
||||||
|
1,
|
||||||
|
|
||||||
|
Math.min(
|
||||||
|
calculatedAmount,
|
||||||
|
targetCookies,
|
||||||
|
MAX_STEAL_AMOUNT
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
amount,
|
||||||
|
percentage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStealPercentage() {
|
||||||
|
const roll =
|
||||||
|
Math.random();
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 50% chance:
|
||||||
|
* steal 1–3%
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (roll < 0.50) {
|
||||||
|
return randomFloat(
|
||||||
|
0.01,
|
||||||
|
0.03
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 30% chance:
|
||||||
|
* steal 3–5%
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (roll < 0.80) {
|
||||||
|
return randomFloat(
|
||||||
|
0.03,
|
||||||
|
0.05
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 15% chance:
|
||||||
|
* steal 5–8%
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (roll < 0.95) {
|
||||||
|
return randomFloat(
|
||||||
|
0.05,
|
||||||
|
0.08
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 5% jackpot:
|
||||||
|
* steal 8–10%
|
||||||
|
*/
|
||||||
|
|
||||||
|
return randomFloat(
|
||||||
|
0.08,
|
||||||
|
0.10
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomFloat(
|
||||||
|
min,
|
||||||
|
max
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
Math.random() *
|
||||||
|
(max - min)
|
||||||
|
+
|
||||||
|
min
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GENERIC HELPERS
|
* GENERIC HELPERS
|
||||||
|
|||||||
Reference in New Issue
Block a user