Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
3029165dac |
210
distribution.js
Normal file
210
distribution.js
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
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
Normal file
204
experiment.js
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
// 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: /^(\d+)?([dhlfb])(\d+)(\s*([+\-*x\/])\s*(\d+))?/,
|
rollRegex: /^(?<count>\d+)?(?<mode>[dhlfb])(?<size>\d+)(?<operation>\s*([+\-*x\/])\s*(?<modifier>\d+))?(\s*\<(?<table>[\w\-\s]+)?(\s*:(?<exclusions>.*?))?\>)?/,
|
||||||
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,25 +50,43 @@ 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,
|
||||||
modifierString,
|
table,
|
||||||
|
exclusions,
|
||||||
operation,
|
operation,
|
||||||
modifier
|
modifier
|
||||||
] = match.slice(1)
|
} = match.groups
|
||||||
|
|
||||||
|
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)
|
||||||
@ -123,28 +141,13 @@ const OnMessage = (message, respond) => {
|
|||||||
if(dice == undefined)
|
if(dice == undefined)
|
||||||
return // No dice
|
return // No dice
|
||||||
|
|
||||||
RollDice(dice, respond)
|
Roll(dice, respond)
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Result = (dice) => {
|
||||||
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':
|
||||||
@ -194,6 +197,48 @@ const RollDice = (dice, respond) => {
|
|||||||
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)
|
||||||
@ -204,6 +249,7 @@ const RollDice = (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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,39 +311,6 @@ 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
|
||||||
|
|
||||||
@ -353,7 +366,7 @@ const HandleCommand = async interaction => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RollDice(dice, content => interaction.followUp(content) )
|
Roll(dice, content => interaction.followUp(content) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,6 +435,50 @@ 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()
|
||||||
@ -445,7 +502,7 @@ const AddMacro = async (interaction) => {
|
|||||||
await interaction.deferReply({ ephemeral: true })
|
await interaction.deferReply({ ephemeral: true })
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
OpenMacros(interaction.guild.id).put(name, dice),
|
MacroLevel(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.`)
|
||||||
@ -473,6 +530,8 @@ 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