Compare commits

...
Sign in to create a new pull request.

2 commits
master ... dev

2 changed files with 262 additions and 9 deletions

View file

@ -27,6 +27,98 @@ const commands = [
{ name: 'Framework', value: 'framework' },
)
)
),
new SlashCommandBuilder()
.setName('simulate')
.setDescription('Simulate different type of dice rolls.')
.addSubcommand(subcommand =>
subcommand
.setName('simple')
.setDescription('Simulate a simple dice roll.')
)
.addSubcommand(subcommand =>
subcommand
.setName('extended')
.setDescription('Simulate an extended dice roll.')
.addNumberOption(option =>
option
.setName('dicepool')
.setDescription('The dicepool of the roll.')
.setRequired(true)
)
.addNumberOption(option =>
option
.setName('threshold')
.setDescription('The threshold of the roll.')
.setRequired(true)
)
.addNumberOption(option =>
option
.setName('interval')
.setDescription('The interval of the roll.')
.setRequired(true)
)
.addStringOption(option =>
option
.setName('interval-unit')
.setDescription('The unit of the interval. Defaults to Hours.')
.setRequired(false)
.addChoices(
{ name: 'Seconds', value: 'seconds' },
{ name: 'Minutes', value: 'minutes' },
{ name: 'Hours', value: 'hours' },
{ name: 'Days', value: 'days' },
{ name: 'Weeks', value: 'weeks' },
{ name: 'Months', value: 'months' },
)
)
.addNumberOption(option =>
option
.setName('max-edge')
.setDescription('The maximum number of edge to use.')
.setRequired(false)
)
.addStringOption(option =>
option
.setName('edgetype')
.setDescription('What type of edgeing should be used ?')
.addChoices(
{ name: 'None', value: 'none' },
{ name: 'Turn Up', value: 'up' },
{ name: 'Reroll', value: 'reroll' },
{ name: 'Smart', value: 'smart' },
{ name: 'All', value: 'all' },
)
)
.addStringOption(option =>
option
.setName('edgerefresh')
.setDescription('How often should the edge refresh ?')
.addChoices(
{ name: 'Never', value: 'none' },
{ name: 'Every Roll', value: 'fill' },
{ name: '1 Edge per Roll', value: 'once' },
{ name: '2 Edge per Roll', value: 'twice' },
)
)
.addNumberOption(option =>
option
.setName('amount')
.setDescription('How many times should the roll be simulated ? WARNING this service is pricey !')
.setRequired(false)
)
.addBooleanOption(option =>
option
.setName('hidden')
.setDescription('Should the result be hidden ?')
.setRequired(false)
)
.addBooleanOption(option =>
option
.setName('true-random')
.setDescription('Should the dice be truly random ?')
.setRequired(false)
)
)
]
.map(command => command.toJSON());

179
index.js
View file

@ -1,5 +1,7 @@
require('dotenv').config();
const { EOL } = require('os');
const { randomInt } = require('node:crypto');
const { Client, GatewayIntentBits } = require('discord.js');
const token = process.env.TOKEN;
@ -12,18 +14,177 @@ client.once('ready', () => {
});
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (!interaction.isCommand()) {
console.log('Not a command');
return;
}
console.log(interaction.commandName);
let commandName = interaction.commandName;
switch (interaction.commandName) {
case "generate":
switch (interaction.options.getSubcommand()) {
case "host":
let rating = interaction.options.getNumber("rating");
let type = interaction.options.getString("type") ?? "foundation";
await interaction.reply({files: ["./france-in-pictures-beautiful-places-to-photograph-eiffel-tower.jpg"]});
break;
default:
await interaction.reply({ content: 'Invalid subcommand!', ephemeral: true });
break;
}
break;
case "simulate":
await interaction.deferReply();
switch (await interaction.options.getSubcommand()) {
case "simple":
await interaction.reply({ content: 'Simple dice roll!', ephemeral: true });
break;
if (commandName === "generate") {
if (interaction.options.getSubcommand() === "host") {
let rating = interaction.options.getNumber("rating");
let type = interaction.options.getString("type") ?? "foundation";
await interaction.reply({files: ["./france-in-pictures-beautiful-places-to-photograph-eiffel-tower.jpg"]});
}
case "extended":
// Variables from Discord command
const dicepool = interaction.options.getNumber("dicepool");
const threshold = interaction.options.getNumber("threshold");
const interval = interaction.options.getNumber("interval") ?? 1;
const intervalUnit = interaction.options.getString("interval-unit") ?? "Hours";
const simAmount = interaction.options.getNumber("amount") ?? 10000;
const hidden = interaction.options.getBoolean("hidden") ?? false;
const edgeType = interaction.options.getString("edgetype") ?? "none";
const edgeRefresh = interaction.options.getString("edgerefresh") ?? "none";
const maxEdge = interaction.options.getNumber("max-edge") ?? 6;
const useTrueRandom = interaction.options.getBoolean("true-random") ?? false;
// Check if dicepool is valid
if (dicepool < 1 || dicepool > 100) {
await interaction.editReply({ content: 'Dicepool must be between 1 and 100!', ephemeral: true });
return;
}
// Arrays to build the final message
const warnings = [];
const parameters = [];
const calculatedResults = [];
const realResults = [];
let message = "";
// Variables for the simulation
let hits = 0;
let glitches = 0;
let criticalGlitches = 0;
let iterations = 0;
let edge = maxEdge;
let failed = false;
parameters.push(`Dicepool: ${dicepool}`);
parameters.push(`Threshold: ${threshold}`);
parameters.push(`Interval: ${interval} ${intervalUnit}`);
parameters.push(`Amount of simulations: ${simAmount}`);
parameters.push(`Edge: ${edgeType}`);
parameters.push(`Edge refresh: ${edgeRefresh}`);
parameters.push(`Max edge: ${maxEdge}`);
parameters.push(`Use true random: ${useTrueRandom}`);
message = `Parameters:${EOL}\t${parameters.join(EOL + '\t')}${EOL}${EOL}`;
calculatedResults.push("Calculated Results (Can't Use Edge): " + EOL);
calculatedResults.push(`Calc Rolls : `);
calculatedResults.push(`Hits Bought: `);
realResults.push("Real Results: " + EOL);
// Roll dice in dicepool using the fillArrayWithRandomDice Function till we have reached the threshold or above
while (hits < threshold) {
if (dicepool - iterations === 0) {
failed = true;
break;
}
let rolls = await fillArrayWithRandomDice(new Array(dicepool), useTrueRandom);
// TODO: encapsulate into functions
// TODO: Add smart edgetype or warning
// Edge Section:
switch (edgeType) {
case 'up':
console.log(`Pre Edge Up: ${rolls}`);
rolls.forEach((roll, index) => {
if (roll === 4 && edge >= 2) {
rolls[index] = 5;
edge -= 2;
}
});
console.log(`Post Edge Up: ${rolls}`);
break;
case 'reroll':
console.log(`Pre Reroll: ${rolls.sort().reverse()}`);
if (edge >= 4) {
let rerolls = await fillArrayWithRandomDice(rolls.filter(roll => roll < 5), useTrueRandom);
rolls = rolls.filter(roll => roll >= 5).concat(rerolls);
}
console.log(`Post Reroll: ${rolls.sort().reverse()}`);
break;
}
let tempHits = rolls.reduce((a, b) => b >= 5 ? a + 1 : a, 0);
let tempGlitches = rolls.reduce((a, b) => b === 1 ? a + 1 : a, 0) > rolls.length / 2 ? 1 : 0;
if (tempHits === 0 && tempGlitches === 1) {
criticalGlitches++;
tempGlitches--;
}
hits += tempHits;
glitches += tempGlitches;
iterations += 1;
switch (edgeRefresh) {
case 'fill':
edge = maxEdge;
break;
case 'once':
if (edge < maxEdge) {
edge++;
}
break;
case 'twice':
if (edge < maxEdge - 1) {
edge += 2;
}
break;
default:
break;
}
}
if (failed) {
await interaction.editReply({ content: 'Failed to reach threshold!', ephemeral: hidden });
} else {
await interaction.editReply({ content: message, ephemeral: hidden });
}
break;
}
break;
}
})
});
async function fillArrayWithRandomDice(arr, trueRandom = false) {
for (let i = 0; i < arr.length; i++) {
arr[i] = await randomInt(1, 7);
}
return arr;
}
client.login(token);