require('dotenv').config(); const { EOL } = require('os'); const { randomInt } = require('node:crypto'); const { Client, GatewayIntentBits } = require('discord.js'); const token = process.env.TOKEN; const client = new Client({ intents: [GatewayIntentBits.Guilds] }) client.once('ready', () => { console.log('Bot is Ready!'); }); client.on('interactionCreate', async interaction => { if (!interaction.isCommand()) { console.log('Not a command'); return; } console.log(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; 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);