From f700c892accb713d5ff7785b62fa675f3a40d408 Mon Sep 17 00:00:00 2001 From: Maverick Date: Wed, 12 Oct 2022 19:10:23 +0200 Subject: [PATCH 1/2] Add command for simulating extended dice roll and basic shell code --- deploy-commands.js | 78 ++++++++++++++++++++++++++++++++++++++++++++++ index.js | 58 +++++++++++++++++++++++++++++----- 2 files changed, 128 insertions(+), 8 deletions(-) diff --git a/deploy-commands.js b/deploy-commands.js index 61af8d8..76cc67e 100644 --- a/deploy-commands.js +++ b/deploy-commands.js @@ -27,6 +27,84 @@ 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('difficulty') + .setDescription('The difficulty of the roll.') + .setRequired(true) + ) + .addNumberOption(option => + option + .setName('interval') + .setDescription('The interval of the roll.') + .setRequired(false) + ) + .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()); diff --git a/index.js b/index.js index 325c013..550b82c 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ require('dotenv').config(); +const { crypto } = require('crypto'); const { Client, GatewayIntentBits } = require('discord.js'); const token = process.env.TOKEN; @@ -15,15 +16,56 @@ client.on('interactionCreate', async interaction => { if (!interaction.isCommand()) 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 (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": + let dicepool = interaction.options.getNumber("dicepool"); + let difficulty = interaction.options.getNumber("difficulty"); + let interval = interaction.options.getNumber("interval") ?? 1; + let simAmount = interaction.options.getNumber("amount") ?? 10000; + let hidden = interaction.options.getBoolean("hidden") ?? false; + let edgeType = interaction.options.getString("edge") ?? "none"; + let edgeRefresh = interaction.options.getString("edgerefresh") ?? "none"; + let maxEdge = interaction.options.getNumber("edge-max") ?? 6; + let useTrueRandom = interaction.options.getBoolean("true-random") ?? false; + let edge = maxEdge; + + let warnings = []; + let calculatedResults = []; + let realResults = []; + + await interaction.reply({ content: 'Extended dice roll!', ephemeral: hidden }); + + break; + } + break; } -}) +}); + +async function fillArrayWithRandomDice(arr) { + for (let i = 0; i < arr.length; i++) { + arr[i] = await crypto.randomInt(1, 7); + } +} client.login(token); \ No newline at end of file From 42e7d69bbbca5dc04c0caeced4a8ad86276ad17e Mon Sep 17 00:00:00 2001 From: Maverick Date: Thu, 13 Oct 2022 00:18:49 +0200 Subject: [PATCH 2/2] Add Edge Mechanic and finalise rolling parts --- deploy-commands.js | 18 +++++- index.js | 155 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 153 insertions(+), 20 deletions(-) diff --git a/deploy-commands.js b/deploy-commands.js index 76cc67e..e700363 100644 --- a/deploy-commands.js +++ b/deploy-commands.js @@ -48,15 +48,29 @@ const commands = [ ) .addNumberOption(option => option - .setName('difficulty') - .setDescription('The difficulty of the roll.') + .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 diff --git a/index.js b/index.js index 550b82c..9477835 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ require('dotenv').config(); -const { crypto } = require('crypto'); +const { EOL } = require('os'); +const { randomInt } = require('node:crypto'); const { Client, GatewayIntentBits } = require('discord.js'); const token = process.env.TOKEN; @@ -13,7 +14,10 @@ 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); switch (interaction.commandName) { @@ -33,39 +37,154 @@ client.on('interactionCreate', async interaction => { case "simulate": await interaction.deferReply(); - switch (interaction.options.getSubcommand()) { + switch (await interaction.options.getSubcommand()) { case "simple": await interaction.reply({ content: 'Simple dice roll!', ephemeral: true }); break; case "extended": - let dicepool = interaction.options.getNumber("dicepool"); - let difficulty = interaction.options.getNumber("difficulty"); - let interval = interaction.options.getNumber("interval") ?? 1; - let simAmount = interaction.options.getNumber("amount") ?? 10000; - let hidden = interaction.options.getBoolean("hidden") ?? false; - let edgeType = interaction.options.getString("edge") ?? "none"; - let edgeRefresh = interaction.options.getString("edgerefresh") ?? "none"; - let maxEdge = interaction.options.getNumber("edge-max") ?? 6; - let useTrueRandom = interaction.options.getBoolean("true-random") ?? false; + // 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; - let warnings = []; - let calculatedResults = []; - let realResults = []; + 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}`); - await interaction.reply({ content: 'Extended dice roll!', ephemeral: hidden }); + 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) { +async function fillArrayWithRandomDice(arr, trueRandom = false) { for (let i = 0; i < arr.length; i++) { - arr[i] = await crypto.randomInt(1, 7); + arr[i] = await randomInt(1, 7); } + return arr; } + client.login(token); \ No newline at end of file