Compare commits
No commits in common. "dev" and "main" have entirely different histories.
210
distribution.js
210
distribution.js
@ -1,210 +0,0 @@
|
|||||||
export class DenominatorTooLargeError extends Error {}
|
|
||||||
|
|
||||||
export function Distribution(floor, ceil, denominator, fill) {
|
|
||||||
if(denominator > Number.MAX_SAFE_INTEGER) {
|
|
||||||
throw DenominatorTooLargeError("Distribution denominator unsafe")
|
|
||||||
}
|
|
||||||
|
|
||||||
let length = (ceil + 1) - floor
|
|
||||||
let buffer = new ArrayBuffer(length * 4)
|
|
||||||
let data = new Uint32Array(buffer)
|
|
||||||
|
|
||||||
if(Number.isInteger(fill)) {
|
|
||||||
data.fill(fill)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
floor,
|
|
||||||
ceil,
|
|
||||||
denominator,
|
|
||||||
data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Die(size) {
|
|
||||||
return Distribution(1, size, size, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Index(dist, result) {
|
|
||||||
return result - dist.floor
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Set(dist, result, prob) {
|
|
||||||
return dist.data.set([ prob ], Index(dist, result))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Get(dist, result) {
|
|
||||||
return dist.data.at(Index(dist, result))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Inc(dist, result, amount = 1) {
|
|
||||||
let i = Index(dist, result)
|
|
||||||
return dist.data.set([ dist.data.at(i) + amount ], i)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Includes(dist, result) {
|
|
||||||
return dist.floor <= result && result <= dist.ceil
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Convolve(d1, d2) {
|
|
||||||
let ax1 = d1.floor + d2.floor
|
|
||||||
let ax2 = d1.ceil + d2.ceil
|
|
||||||
let Pc = Distribution(ax1, ax2, d1.denominator * d2.denominator, 0)
|
|
||||||
|
|
||||||
for(let S = ax1; S <= ax2; S++) {
|
|
||||||
for(let r1 = d1.floor; r1 <= d1.ceil; r1++) {
|
|
||||||
let r2 = S - r1
|
|
||||||
|
|
||||||
if(Includes(d2, r2)) {
|
|
||||||
Inc(Pc, S, Get(d1, r1) * Get(d2, r2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pc
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Highest(d1, d2) {
|
|
||||||
let hd, ld
|
|
||||||
if(d1.ceil >= d2.ceil) {
|
|
||||||
hd = d1, ld = d2
|
|
||||||
} else {
|
|
||||||
hd = d2, ld = d1
|
|
||||||
}
|
|
||||||
|
|
||||||
let Ph = Distribution(Math.max(d1.floor, d2.floor), hd.ceil, d1.denominator * d2.denominator, 0)
|
|
||||||
|
|
||||||
for(let r1 = hd.floor; r1 <= hd.ceil; r1++) {
|
|
||||||
for(let r2 = ld.floor; r2 <= ld.ceil; r2++) {
|
|
||||||
let S = Math.max(r2, r1)
|
|
||||||
|
|
||||||
Inc(Ph, S, Get(hd, r1) * Get(ld, r2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ph
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Lowest(d1, d2) {
|
|
||||||
let ld, hd
|
|
||||||
if(d1.floor <= d2.floor) {
|
|
||||||
ld = d1, hd = d2
|
|
||||||
} else {
|
|
||||||
ld = d2, hd = d1
|
|
||||||
}
|
|
||||||
|
|
||||||
let Ph = Distribution(ld.floor, Math.min(d1.ceil, d2.ceil), d1.denominator * d2.denominator, 0)
|
|
||||||
|
|
||||||
for(let r1 = ld.floor; r1 <= ld.ceil; r1++) {
|
|
||||||
for(let r2 = hd.floor; r2 <= hd.ceil; r2++) {
|
|
||||||
let S = Math.min(r2, r1)
|
|
||||||
|
|
||||||
Inc(Ph, S, Get(ld, r1) * Get(hd, r2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ph
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ForEach(dist, func) {
|
|
||||||
for(let i = 0; i < dist.data.length; i++) {
|
|
||||||
func(i + dist.floor, dist.data.at(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Floats(dist) {
|
|
||||||
let buf = new ArrayBuffer(dist.data.length * 4)
|
|
||||||
let view = new Float32Array(buf)
|
|
||||||
|
|
||||||
ForEach(dist, (i) => {
|
|
||||||
view[Index(dist, i)] = Get(dist, i) / dist.denominator
|
|
||||||
})
|
|
||||||
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Count(dist, amount) {
|
|
||||||
let counter = amount
|
|
||||||
|
|
||||||
for(let i = 0; i < dist.data.length; i++) {
|
|
||||||
counter -= dist.data.at(i)
|
|
||||||
if(counter <= 0) {
|
|
||||||
return i + dist.floor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAX_32_BIT_INT = 4294967295
|
|
||||||
export function Roll(dist) {
|
|
||||||
let i = crypto.getRandomValues(new Uint32Array(1))[0]
|
|
||||||
i = Math.ceil((i / MAX_32_BIT_INT) * dist.denominator)
|
|
||||||
|
|
||||||
return Count(dist, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
2 11.11% ████████████
|
|
||||||
3 22.22% ████████████
|
|
||||||
# 4 33.33% ████████████
|
|
||||||
5 22.22% ████████████
|
|
||||||
6 1.11% ████████████
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function Visualize(dist, highlight = 13, tableWidth = 13) {
|
|
||||||
let largestProb = 0
|
|
||||||
let items = []
|
|
||||||
ForEach(dist, (i, v) => {
|
|
||||||
if(v > largestProb) largestProb = v
|
|
||||||
items.push([ i, v ])
|
|
||||||
})
|
|
||||||
|
|
||||||
let v = ''
|
|
||||||
let probUnits = tableWidth / largestProb
|
|
||||||
|
|
||||||
for(let [ res, prob ] of items) {
|
|
||||||
let percentage = prob / dist.denominator
|
|
||||||
percentage *= 100
|
|
||||||
|
|
||||||
v += res === highlight ? '#' : ' '
|
|
||||||
v += res.toString().padStart(3, ' ')
|
|
||||||
v += ' '
|
|
||||||
v += percentage.toFixed(2).padStart(5) + '%'
|
|
||||||
v += ' '
|
|
||||||
let u = prob * probUnits
|
|
||||||
let fb = Math.floor(u)
|
|
||||||
let r = u - fb
|
|
||||||
v += ('\u2588'.repeat(fb) + fractionalBlockCharacter(r)).padEnd(tableWidth, '\u2500')
|
|
||||||
v += '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fractionalBlockCharacter(float) {
|
|
||||||
if(float >= 7/8) return '\u2589'
|
|
||||||
if(float >= 3/4) return '\u258A'
|
|
||||||
if(float >= 5/8) return '\u258B'
|
|
||||||
if(float >= 1/2) return '\u258C'
|
|
||||||
if(float >= 3/8) return '\u258D'
|
|
||||||
if(float >= 1/4) return '\u258E'
|
|
||||||
if(float >= 1/8) return '\u258F'
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// )
|
|
||||||
|
|
||||||
let exD = Die(6)
|
|
||||||
exD.floor += 3
|
|
||||||
exD.ceil += 3
|
|
||||||
// TODO: Change ceil back to a function so this is less weird
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
// Die(6).data.length
|
|
||||||
Visualize(
|
|
||||||
// Convolve(
|
|
||||||
// Die(4),
|
|
||||||
Highest(Die(8), exD)
|
|
||||||
// )
|
|
||||||
)
|
|
||||||
)
|
|
204
experiment.js
204
experiment.js
@ -1,204 +0,0 @@
|
|||||||
// export function Weights(size) {
|
|
||||||
// let set = new Set()
|
|
||||||
|
|
||||||
// for(let i = 0; i < size; i++) {
|
|
||||||
// set.add(i + 1)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return new Map([
|
|
||||||
// [ 1, set ]
|
|
||||||
// ])
|
|
||||||
// }
|
|
||||||
|
|
||||||
export function Weights(size) {
|
|
||||||
let out = []
|
|
||||||
|
|
||||||
for(let i = 0; i < size; i++) {
|
|
||||||
out.push(1/size)
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Add(left, right, out = new Map()) {
|
|
||||||
for(let i = 0; i < left.size * right.size; i++) {
|
|
||||||
let li = parseInt(i / left.size) + 1
|
|
||||||
let ri = (i % left.size) + 1
|
|
||||||
|
|
||||||
let key = li + ri
|
|
||||||
let base = out.get(key) ?? 0
|
|
||||||
out.set(key, base + left.get(li) + right.get(ri))
|
|
||||||
}
|
|
||||||
// for(let li = 0; li < left.size; li++) {
|
|
||||||
// for(let ri = 0; ri < right.size; ri++) {
|
|
||||||
// let i = li + ri
|
|
||||||
// let b = out.get(i) ?? 0
|
|
||||||
// out.set(i, b + left.get(i) + right.get(i))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Mul(left, to) {
|
|
||||||
let out = left
|
|
||||||
|
|
||||||
for(let i = 1; i < to; i++) {
|
|
||||||
out = Add(out, left)
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(Weights(6))
|
|
||||||
// console.log(Add(Weights(6), Weights(6)))
|
|
||||||
// console.log(Mul(Weights(6), 3))
|
|
||||||
|
|
||||||
export function WeightOf(size) {
|
|
||||||
return 1 / size
|
|
||||||
}
|
|
||||||
|
|
||||||
function probSum2dN(N, S) {
|
|
||||||
let P = 0
|
|
||||||
for(let r1 = 1; r1 < N + 1; r1++) {
|
|
||||||
let r2 = S - r1
|
|
||||||
if(1 <= r2 && r2 <= N) {
|
|
||||||
P += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [ P, Math.pow(N, 2) ]
|
|
||||||
}
|
|
||||||
|
|
||||||
// function conv(a, b) {
|
|
||||||
// let bf = b.reverse()
|
|
||||||
// let len = (a.length) + (b.length)
|
|
||||||
// let P = new Map()
|
|
||||||
|
|
||||||
// for(let i = 1; i < len; i++) {
|
|
||||||
// let topSliceRange = [
|
|
||||||
// Math.max(i - a.length, 0),
|
|
||||||
// Math.min(i, b.length)
|
|
||||||
// ]
|
|
||||||
// let bottomSliceRange = [
|
|
||||||
// Math.max(b.length - i, 0),
|
|
||||||
// Math.min(b.length, len - i)
|
|
||||||
// ]
|
|
||||||
// let topSlice = a.slice(...topSliceRange)
|
|
||||||
// let bottomSlice = bf.slice(...bottomSliceRange)
|
|
||||||
|
|
||||||
// P.set(
|
|
||||||
// i + 1,
|
|
||||||
// topSlice.reduce(
|
|
||||||
// (a, v, i) => a + (v * bottomSlice[i]),
|
|
||||||
// 0
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return P
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const conv = (vec1, vec2) => {
|
|
||||||
// if (vec1.length === 0 || vec2.length === 0) {
|
|
||||||
// throw new Error('Vectors can not be empty!');
|
|
||||||
// }
|
|
||||||
// const volume = vec1;
|
|
||||||
// const kernel = vec2;
|
|
||||||
// /* Initialized to zero by default */
|
|
||||||
// const convVec = new Float32Array(volume.length + kernel.length);
|
|
||||||
|
|
||||||
// let i = 0;
|
|
||||||
// for (let j = 0; j < kernel.length; ++j) {
|
|
||||||
// convVec[j] = volume[0] * kernel[j];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for (i = 1; i < volume.length; ++i) {
|
|
||||||
// for (let j = 0; j < kernel.length; ++j) {
|
|
||||||
// convVec[i + j] += volume[i] * kernel[j];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return convVec;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// conv(
|
|
||||||
// Weights(6),
|
|
||||||
// Weights(6)
|
|
||||||
// // conv(Weights(6), Weights(6))
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
|
|
||||||
class Distribution extends Float32Array {
|
|
||||||
static Die(size) {
|
|
||||||
return new Distribution(1, size, 1 / size)
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(first, last, fill) {
|
|
||||||
|
|
||||||
let len = (last + 1) - first
|
|
||||||
let b = new ArrayBuffer(len * 4)
|
|
||||||
super(b)
|
|
||||||
|
|
||||||
this.first = first
|
|
||||||
this.last = last
|
|
||||||
if(fill) {
|
|
||||||
this.fill(fill)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
forEach(func) {
|
|
||||||
for(let i = this.first; i <= this.last; i++) {
|
|
||||||
func(this.get(i), i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set(i, value) {
|
|
||||||
super.set([ value ], i - this.first)
|
|
||||||
}
|
|
||||||
|
|
||||||
increase(i, value) {
|
|
||||||
let index = i - this.first
|
|
||||||
super.set([ this.at(index) + value ], index)
|
|
||||||
}
|
|
||||||
|
|
||||||
get(i) {
|
|
||||||
return this.at(i - this.first)
|
|
||||||
}
|
|
||||||
|
|
||||||
contains(indice) {
|
|
||||||
return indice >= this.first && indice <= this.last
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function conv(d1, d2) {
|
|
||||||
let ax1 = d1.first + d2.first
|
|
||||||
let ax2 = d1.last + d2.last
|
|
||||||
console.log(ax1, ax2, ax2 - ax1)
|
|
||||||
let Pc = new Distribution(ax1, ax2, 0)
|
|
||||||
|
|
||||||
for(let S = ax1; S <= ax2; S++) {
|
|
||||||
for(let r1 = d1.first; r1 <= d1.last; r1++) {
|
|
||||||
let r2 = S - r1
|
|
||||||
|
|
||||||
if(d2.contains(r2)) {
|
|
||||||
Pc.increase(S, d1.get(r1) * d2.get(r2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pc
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(Distribution.Die(6))
|
|
||||||
// let a = Distribution.Die(12)
|
|
||||||
// a.set(2, 0.000069)
|
|
||||||
// a.forEach(console.log)
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
conv(
|
|
||||||
Distribution.Die(6),
|
|
||||||
conv(Distribution.Die(6), Distribution.Die(6))
|
|
||||||
)
|
|
||||||
)
|
|
@ -1,5 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
rollRegex: /^(?<count>\d+)?(?<mode>[dhlfb])(?<size>\d+)(?<operation>\s*([+\-*x\/])\s*(?<modifier>\d+))?(\s*\<(?<table>[\w\-\s]+)?(\s*:(?<exclusions>.*?))?\>)?/,
|
rollRegex: /^(\d+)?([dhlfb])(\d+)(\s*([+\-*x\/])\s*(\d+))?/,
|
||||||
optionRollRegex: /^(\d+)?(([dhlfb])(\d+))?(\s*([+\-*x\/])\s*(\d+))?/,
|
optionRollRegex: /^(\d+)?(([dhlfb])(\d+))?(\s*([+\-*x\/])\s*(\d+))?/,
|
||||||
descriptionRegex: /\s*((\d*-\d*)|(\d+))?([^;\n]+)/g,
|
descriptionRegex: /\s*((\d*-\d*)|(\d+))?([^;\n]+)/g,
|
||||||
macroNameRegex: /^[a-z0-9]+$/,
|
macroNameRegex: /^[a-z0-9]+$/,
|
||||||
|
169
src/index.js
169
src/index.js
@ -50,43 +50,25 @@ const ParseOptionRoll = expression => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ParseExclusions = (exclusions) => {
|
|
||||||
let numbers = []
|
|
||||||
|
|
||||||
for(let part of exclusions.split(/\s*,\s*/)) {
|
|
||||||
let int = parseInt(part)
|
|
||||||
if(int !== NaN) {
|
|
||||||
numbers.push(int)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return numbers
|
|
||||||
}
|
|
||||||
|
|
||||||
const ParseRoll = expression => {
|
const ParseRoll = expression => {
|
||||||
let match = constants.rollRegex.exec(expression.trim())
|
let match = constants.rollRegex.exec(expression.trim())
|
||||||
|
|
||||||
if(match == null)
|
if(match == null)
|
||||||
return
|
return
|
||||||
|
|
||||||
let {
|
let [
|
||||||
count,
|
count,
|
||||||
mode,
|
mode,
|
||||||
size,
|
size,
|
||||||
table,
|
modifierString,
|
||||||
exclusions,
|
|
||||||
operation,
|
operation,
|
||||||
modifier
|
modifier
|
||||||
} = match.groups
|
] = match.slice(1)
|
||||||
|
|
||||||
console.log(count)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
count: ParseRollInt(count, 1),
|
count: ParseRollInt(count, 1),
|
||||||
mode,
|
mode,
|
||||||
size: ParseRollInt(size),
|
size: ParseRollInt(size),
|
||||||
table,
|
|
||||||
exclusions: exclusions == null ? [] : ParseExclusions(exclusions),
|
|
||||||
operation,
|
operation,
|
||||||
modifier: ParseRollInt(modifier),
|
modifier: ParseRollInt(modifier),
|
||||||
descriptionConditions: PullDescription(expression, match)
|
descriptionConditions: PullDescription(expression, match)
|
||||||
@ -141,13 +123,28 @@ const OnMessage = (message, respond) => {
|
|||||||
if(dice == undefined)
|
if(dice == undefined)
|
||||||
return // No dice
|
return // No dice
|
||||||
|
|
||||||
Roll(dice, respond)
|
RollDice(dice, respond)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Result = (dice) => {
|
const RollDice = (dice, respond) => {
|
||||||
|
if(dice.size > 255) {
|
||||||
|
respond('That die is way too big... .-.')
|
||||||
|
return
|
||||||
|
} else if(dice.size < 2) {
|
||||||
|
respond('I cannot even fathom a die with that geometry ;-;')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dice.count > 100) {
|
||||||
|
respond('I don\'t have that many dice O_O')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let rolls = [ ...crypto.getRandomValues(new Uint8Array(dice.count) ) ]
|
let rolls = [ ...crypto.getRandomValues(new Uint8Array(dice.count) ) ]
|
||||||
.map(n => Math.ceil((n / 256) * dice.size))
|
.map(n => Math.ceil((n / 256) * dice.size))
|
||||||
let result = 0
|
let result = 0
|
||||||
|
let operationSymbol = dice.operation
|
||||||
|
let response = ''
|
||||||
|
|
||||||
switch(dice.mode.toLowerCase()) {
|
switch(dice.mode.toLowerCase()) {
|
||||||
case 'd':
|
case 'd':
|
||||||
@ -197,48 +194,6 @@ const Result = (dice) => {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
|
||||||
result,
|
|
||||||
rolls
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Roll = (dice, respond) => {
|
|
||||||
if(dice.size > 255) {
|
|
||||||
respond('That die is way too big... .-.')
|
|
||||||
return
|
|
||||||
} else if(dice.size < 2) {
|
|
||||||
respond('I cannot even fathom a die with that geometry ;-;')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dice.count > 100) {
|
|
||||||
respond('I don\'t have that many dice O_O')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let result, rolls
|
|
||||||
let tries = 0
|
|
||||||
const maxTries = dice.size * dice.count * 256
|
|
||||||
|
|
||||||
const roll = () => {
|
|
||||||
({ result, rolls } = Result(dice))
|
|
||||||
tries++
|
|
||||||
}
|
|
||||||
|
|
||||||
roll()
|
|
||||||
while(dice.exclusions.includes(result)) {
|
|
||||||
roll()
|
|
||||||
|
|
||||||
if(tries >= maxTries) {
|
|
||||||
// TODO: Buttons to rerun the command and reset table, if applicable
|
|
||||||
respond(`I've rolled ${tries} times, but no unique results are left in this table.`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let response = ''
|
|
||||||
|
|
||||||
if(dice.descriptionConditions) {
|
if(dice.descriptionConditions) {
|
||||||
for(let { range, content } of dice.descriptionConditions) {
|
for(let { range, content } of dice.descriptionConditions) {
|
||||||
if(!range || result >= range.lower && result <= range.upper)
|
if(!range || result >= range.lower && result <= range.upper)
|
||||||
@ -249,7 +204,6 @@ const Roll = (dice, respond) => {
|
|||||||
response += `\` ${result} \` \u27F5 [${rolls.join(', ')}] ${dice.count + dice.mode + dice.size}`
|
response += `\` ${result} \` \u27F5 [${rolls.join(', ')}] ${dice.count + dice.mode + dice.size}`
|
||||||
|
|
||||||
if(dice.operation) {
|
if(dice.operation) {
|
||||||
let operationSymbol = dice.operation
|
|
||||||
response += ' ' + operationSymbol + ' ' + dice.modifier
|
response += ' ' + operationSymbol + ' ' + dice.modifier
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +265,39 @@ const Subcommands = (data, subcommandCallbacks) =>
|
|||||||
return subcommandCallbacks[interaction.options.getSubcommand()](interaction)
|
return subcommandCallbacks[interaction.options.getSubcommand()](interaction)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const OpenMacros = guildId =>
|
||||||
|
db.sublevel(guildId).sublevel('macros')
|
||||||
|
|
||||||
|
const ReloadMacros = async guildId => {
|
||||||
|
let commands = []
|
||||||
|
let macros = OpenMacros(guildId)
|
||||||
|
let cacheEntry = {}
|
||||||
|
|
||||||
|
for await (let [ name, dice ] of macros.iterator() ) {
|
||||||
|
cacheEntry[name] = dice
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
name,
|
||||||
|
description: Elipsify("Roll " + dice.replaceAll('\n', ';'), 100),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: "options",
|
||||||
|
description: "Dice, modifiers, or descriptions to apply over the macro",
|
||||||
|
type: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.macroCache.set(guildId, cacheEntry)
|
||||||
|
|
||||||
|
await rest.put(
|
||||||
|
Routes.applicationGuildCommands(process.env.DISCORD_ID, guildId),
|
||||||
|
{ body: globalThis.commands }
|
||||||
|
)
|
||||||
|
.catch(err => console.error('Failed to reload macros:', err) )
|
||||||
|
}
|
||||||
|
|
||||||
const Elipsify = (string, maxLength) =>
|
const Elipsify = (string, maxLength) =>
|
||||||
string.length > maxLength ? string.slice(0, maxLength - 3) + '...' : string
|
string.length > maxLength ? string.slice(0, maxLength - 3) + '...' : string
|
||||||
|
|
||||||
@ -366,7 +353,7 @@ const HandleCommand = async interaction => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Roll(dice, content => interaction.followUp(content) )
|
RollDice(dice, content => interaction.followUp(content) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,50 +422,6 @@ const About = async (interaction) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const TableLevel = guildId =>
|
|
||||||
db.sublevel(guildId).sublevel('tables')
|
|
||||||
|
|
||||||
const Table = (guildId, name, excludedNumbers = new Set()) => {
|
|
||||||
TableLevel(guildId).put(name, excludedNumbers)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const MacroLevel = guildId =>
|
|
||||||
db.sublevel(guildId).sublevel('macros')
|
|
||||||
|
|
||||||
const ReloadMacros = async guildId => {
|
|
||||||
let commands = []
|
|
||||||
let macros = MacroLevel(guildId)
|
|
||||||
let cacheEntry = {}
|
|
||||||
|
|
||||||
for await (let [ name, dice ] of macros.iterator() ) {
|
|
||||||
cacheEntry[name] = dice
|
|
||||||
|
|
||||||
commands.push({
|
|
||||||
name,
|
|
||||||
description: Elipsify("Roll " + dice.replaceAll('\n', ';'), 100),
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: "options",
|
|
||||||
description: "Dice, modifiers, or descriptions to apply over the macro",
|
|
||||||
type: 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
globalThis.macroCache.set(guildId, cacheEntry)
|
|
||||||
|
|
||||||
await rest.put(
|
|
||||||
Routes.applicationGuildCommands(process.env.DISCORD_ID, guildId),
|
|
||||||
{ body: globalThis.commands }
|
|
||||||
)
|
|
||||||
.catch(err => console.error('Failed to reload macros:', err) )
|
|
||||||
}
|
|
||||||
|
|
||||||
const AddMacro = async (interaction) => {
|
const AddMacro = async (interaction) => {
|
||||||
let respond = Responses(interaction, true)
|
let respond = Responses(interaction, true)
|
||||||
let name = interaction.options.get('name').value.toLowerCase()
|
let name = interaction.options.get('name').value.toLowerCase()
|
||||||
@ -502,7 +445,7 @@ const AddMacro = async (interaction) => {
|
|||||||
await interaction.deferReply({ ephemeral: true })
|
await interaction.deferReply({ ephemeral: true })
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
MacroLevel(interaction.guild.id).put(name, dice),
|
OpenMacros(interaction.guild.id).put(name, dice),
|
||||||
ReloadMacros(interaction.guild.id)
|
ReloadMacros(interaction.guild.id)
|
||||||
])
|
])
|
||||||
interaction.followUp(`Macro added! Try \`/${name}\`! You might need to switch to a different server and back or reopen Discord in order for it to recognize the new command.`)
|
interaction.followUp(`Macro added! Try \`/${name}\`! You might need to switch to a different server and back or reopen Discord in order for it to recognize the new command.`)
|
||||||
@ -530,8 +473,6 @@ const RemoveMacro = async (interaction) => {
|
|||||||
await interaction.followUp(`Removed \`${name}\`, its dice expression was: \`\`\`${dice}\`\`\``)
|
await interaction.followUp(`Removed \`${name}\`, its dice expression was: \`\`\`${dice}\`\`\``)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Start = async () => {
|
const Start = async () => {
|
||||||
Command(
|
Command(
|
||||||
constants.commands.about,
|
constants.commands.about,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user