diff --git a/README.md b/README.md index da7d7e3..e41244c 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,24 @@ Use `` `dice: XdF` `` to roll a fudge/fate dice. See [here]( Note: If any abbreviation exclusive letters are used (A,C,D), `P` will preference Proficiency. + ## Dice Modifiers The parser supports several modifiers. If a die has been modified, it will display _how_ it has been modified in the tooltip. diff --git a/src/api/api.ts b/src/api/api.ts index 22f34a4..000506d 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -16,6 +16,7 @@ import { TableRoller } from "src/rollers/table/table"; import { SectionRoller } from "src/rollers/section/section"; import { DataViewRoller, TagRoller } from "src/rollers/tag/tag"; import { LineRoller } from "src/rollers/line/line"; +import { NarrativeStackRoller } from "src/rollers/dice/narrative"; export * from "../types/api"; @@ -26,7 +27,8 @@ export { type DataViewRoller, type TagRoller, type LineRoller, - type ArrayRoller + type ArrayRoller, + type NarrativeStackRoller }; export interface RollerOptions { @@ -98,6 +100,9 @@ class APIInstance { if (lexemes.some(({ type }) => type === "line")) { return "line"; } + if (lexemes.some(({ type }) => type === "narrative")) { + return "narrative"; + } return "dice"; } getParametersForRoller( @@ -235,6 +240,15 @@ class APIInstance { const type = this.#getTypeFromLexemes(lexemes); switch (type) { + case "narrative": { + return new NarrativeStackRoller( + this.data, + content, + lexemes, + this.app, + position + ); + } case "dice": { const roller = new StackRoller( this.data, diff --git a/src/lexer/lexer.ts b/src/lexer/lexer.ts index 3de6514..1d87dd5 100644 --- a/src/lexer/lexer.ts +++ b/src/lexer/lexer.ts @@ -164,6 +164,27 @@ class LexerClass { value: this.clampInfinite }, u: /u/u, + narrative: { + match: /^(?:\d*(?:[GgYyBbRrPpSsWw]|[AaPpDdCcBbSsFf]|pro|boo|blk|k|sb|diff))(?: ?\d*(?:[GgYyBbRrPpSsWw]|[AaPpDdCcBbSsFf]|pro|boo|blk|k|sb|diff))+$/u, + value: (match) => { + const isAbbr = /[AaCcDd]/.test(match); + return match + .toLowerCase() + .replace(/pro/g, "y") + .replace(/diff/g, "p") + .replace(/(blk|k|sb)/g, "s") + .replace(/boo/g, "b") + .replace(/p/g, isAbbr ? "y" : "p") + .replace(/a/g, "g") + .replace(/d/g, "p") + .replace(/c/g, "r") + .replace(/f/g, "w") + .replace(/ /g, "") + .replace(/(\d+)(\w)/g, (_, num: string, char: string) => + char.repeat(Number(num)) + ) + } + }, stunt: /1[Dd]S/u, "%": /\d+[Dd]\d+%/u, fudge: { diff --git a/src/main.ts b/src/main.ts index 92fc249..94581e1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -30,6 +30,7 @@ export default class DiceRollerPlugin extends Plugin { return { diceColor: this.data.diceColor, textColor: this.data.textColor, + narrativeSymbolSet: this.data.narrativeSymbolSet, colorfulDice: this.data.colorfulDice, scaler: this.data.scaler, renderTime: this.data.renderTime, @@ -68,7 +69,7 @@ export default class DiceRollerPlugin extends Plugin { return; } await roller.roll(); - if (!roller.dice.length) { + if (!roller.children.length) { new Notice("Invalid formula."); return; } diff --git a/src/renderer/font.css b/src/renderer/font.css index df9a601..6347c88 100644 --- a/src/renderer/font.css +++ b/src/renderer/font.css @@ -1,4 +1,9 @@ @font-face { - font-family: "DICE_ROLLER_GENESYS_FONT"; - src: url("src/assets/genesysglyphsanddice.woff"); + font-family: "SWRPG"; + src: url(data:font/ttf;base64,T1RUTwAKAIAAAwAgQ0ZGIG0r1Q8AAAT4AAAKEEdTVUIAAQAAAAAP4AAAAApPUy8yaB1opwAAARAAAABgY21hcARGBvEAAAOcAAABOmhlYWT9s5gkAAAArAAAADZoaGVhCHcEHwAAAOQAAAAkaG10eLAsA5UAAA8IAAAA2G1heHAANlAAAAABCAAAAAZuYW1lNobiaQAAAXAAAAIscG9zdP+4ADIAAATYAAAAIAABAAAAAQBBEs8raV8PPPUAAwPoAAAAAM0HqmQAAAAAzQeqZAAA/sIEjQN5AAAAAwACAAAAAAAAAAEAAAPo/zgAAASwAAAAAASNAAEAAAAAAAAAAAAAAAAAAAA2AABQAAA2AAAAAwNDAZAABQAIAooCWAAAAEsCigJYAAABXgAyASwAAAAABQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAVUtXTgBAACAAegMg/zgAyAPoAMgAAAABAAAAAAH0ArwAAAAgAAAAAAANAKIAAQAAAAAAAQALAAAAAQAAAAAAAgAHAAsAAQAAAAAAAwAdABIAAQAAAAAABAALAAAAAQAAAAAABQA8AC8AAQAAAAAABgASAGsAAQAAAAAAEgATAH0AAwABBAkAAQAWAJAAAwABBAkAAgAOAKYAAwABBAkAAwA6ALQAAwABBAkABAAWAJAAAwABBAkABQB4AO4AAwABBAkABgAkAWZFb3RFIFN5bWJvbFJlZ3VsYXIxLjAwMTtVS1dOO0VvdEVTeW1ib2wtUmVndWxhclZlcnNpb24gMS4wMDE7UFMgMDAxLjAwMTtob3Rjb252IDEuMC43MDttYWtlb3RmLmxpYjIuNS41ODMyOUVvdEVTeW1ib2wtUmVndWxhckVvdEUgU3ltYm9sIFJlZ3VsYXIARQBvAHQARQAgAFMAeQBtAGIAbwBsAFIAZQBnAHUAbABhAHIAMQAuADAAMAAxADsAVQBLAFcATgA7AEUAbwB0AEUAUwB5AG0AYgBvAGwALQBSAGUAZwB1AGwAYQByAFYAZQByAHMAaQBvAG4AIAAxAC4AMAAwADEAOwBQAFMAIAAwADAAMQAuADAAMAAxADsAaABvAHQAYwBvAG4AdgAgADEALgAwAC4ANwAwADsAbQBhAGsAZQBvAHQAZgAuAGwAaQBiADIALgA1AC4ANQA4ADMAMgA5AEUAbwB0AEUAUwB5AG0AYgBvAGwALQBSAGUAZwB1AGwAYQByAAAAAwAAAAMAAAEKAAEAAAAAABwAAwABAAABCgAGAO4AAAAJAHIANQAAAAAAAAA1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAgADAAQABQAGAAcACAAJAAoACwAMAA0ADgAPABAAEQASABMAFAAVABYAFwAYABkAGgAAAAAAAAAAAAAAAAAbABwAHQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwADEAMgAzADQABAAwAAAACAAIAAIAAAAgAFoAev//AAAAIABBAGH//wAV/8D/ugABAAAAAAAAAAAAAAADAAAAAAAA/7UAMgAAAAAAAAAAAAAAAAAAAAAAAAAAAQAEAgABAQETRW90RVN5bWJvbC1SZWd1bGFyAAEBASP4EAD4GwH4HAL4HQP4GASm+9IcBI36DQX0D/cHEZwcCfASAAMBAQEUH0VvdEUgU3ltYm9sLVJlZ3VsYXJFb3RFIFN5bWJvbAAAAQAiGQBCGQABAAA2AgABAE4AUAB2ALYA4wDkAOYA5wDpAOoA6wDsAO0A7gDvAPAA8gDzAPQA9gD4APkA+gD8AP4BAAHgBI0EogTGBOQE5QU3BTgFOgU7BTwFPQU/BUAFQQVCBUQFRQVGBbMGUgZTBlQGVgetCJkJDAkN/POLvfjsvQGLvfgkvQOLBPiI+VD8iAb3jvvFFfs+95MF9+gG+yD7wBX3PveTBfySB/wGXhX3PveT9z77kwX8Bvi/Ffc++5P7PvuTBQ7oDiD7I8f5r8cBtsf5rscD+lH5mBX+Jv4n+iYGT/nrFf2v/a75rwcO6PtKx/nuxwGu+v4DHASN98cV+674fQX8yQb7r/x996/8fQX4yQb3afh9FfuM/EEF/IMG+4z4QfeM+EEF+IMGDiD70hwEtwG6+h8D+k73sxX8Wvju/Fn87vhZ/PEF7wT8DviN+A74ivgO/IoFDg6EDg6EDg4ODg4ODg6EDg4OIA6EDg4OhA4gDoQOIPs7x/nOxwGmx/nOxwP6PPh/FXLEab1htmC2Wa1RowikUk2XShtKTX9yUh9Rc1lpYGBhYGlZclIIc1F+TUoaS5hNo1Eeo1KtWbZgtmC9acVzCHLEyX7MG8zJmKTEH8Wjva22tra2rb2jxAijxZjJyxrMfslzxR5U++4VdVluX2VmZmVfbll2CHVZVYBSG1JWlqFYH1mgX6hmsWWwbrd1vQh2voDAxBrElsGgvR6hvai3sbGwsLeovaEIoL7AlsQbxMGAdr0fvXW3brBmsWWoX6FZCKBZllVSGlKAVnZYHg7o+zS5+Mj3YhL/AH3uFP8AqhHs//+lgAD/AI2AAPct91z3LPci//+jgAD/AKuMzRMAE9z6h/d4FXuTiZ4FE9qRlpGWk5aSlpOWk5eepZetj7KQsoezfrKCqH6jeqB6oHeddZt1mnOZcpYIcpZylXKUCIIGp3qgeZp3mneVeJB3kHeMd4h3iHiHeIN5CBPce2FuYGJhqFqcXo9hj2GHZX9nf2h6a3RudG9zcnN3cnZ1e3V+dn99g4OHCIAGg499k3aXdph1m3Kgc59zpHSndKh6q3+ufq+HsY+2j7WcuKi7CBPqYrVutnu1g52HnoieiJ+Mn5CfkJ+Vnpqfmp+gnaecCIMGcoJxgXKAcoBzfXV8dXt3eXp2enZ+c4JufmOHY5BkCBPcj2SXap9xk3+TgJKAkoCRgJGAingYe4N7kIKPgJB+lBl9lHyXe5t7nHuge6aJehhoB49tlHCYc5h0mneceZt5nXydf51/nIGag5uEl4WViJuFGJSEjn6AgnGCgIl/in6MGX5+jpB9H5KElYKXgJeAmX+bf5x+nn+gfp9+on+jgKSApIKmg6WDpIWhhgiHoZ6ImhuaipONjY6MjRiPiQaNiJOJmowImp6Oj6EfoZCkkaWTppOklKSWpJail5+YoJiel5uYm5eZl5eWl5aVlJKSCIZ9foh+G36KgIyAjXGUGICUjZiVkpuRlY6XkZuSGZqTnJWdl52XnZqbnZydmp+Yo5iilKaPqQiuB4mce3B7dnt6GXt7fH99gn2CgIaChwj8FPdtFZiRl5OVlZWVk5eQmQiRmI6ai5qLmoiamAwlhpmDloGWgZV/k36RCH6QfY58fH2IfgwkfoV/g4GBgYCDgIZ9CIV+iHyLfIt8jnx+DCWQfZN/lYGVgZeDmIUImIWZiZqamY2YDCQOIDP5rwHy+a4D8vlXFf2v+a75rwcO6PsP+e8B8/p0A/nk+w8V/IQG+4z4QfeM+EIF+IQG94z8QgUOIPtu+oMB9w75iAP4iPtuFfgO+Iz8DviL/A78iwUODoQ2+d8ByfpkA/m3+T0VIAqdOh85nT2lQKzOW8hUw03CTrxItkS1Q60/pjqmOp04lDWU4Z3eptym3K3XtdIIttO8zcLIw8nIws68P2k9cjp5CA4OhA4ODg4gDg4ODoQODg4g+zb6UQGz+iwD+Jv5rxU6++H8IvdS97D7y/uU+xv3mmBW+9n3hveN93z7elr3yfeWtPt49x73Ofd7+6RBBSH7zhX7FfsXpvc4+xqg9yrb+wX3EPc6PLf3Rbr7Fvcjsi/7FfcUPvsgdaX7MwUOhPsP9xHw3feH3PH3EAHg+jYD+TD49xXR9xAF/A0G0vsQBTVYFUL3EPtQ+9oF9yEG+akW+1D32kL7EPcK+14F/Ez3KxX7CPtf9wj7XwX3gAb3B/df+wf3XwVb+9gV+yEGRvcN0PcOBfchBtD7DgX88FgV91D72tL3EPsK914F+KT7XhXU+xD3UPfaBfsjBvxM+5EVRPsRBfgNBkX3EQUODg6EDiD7ILv5fbsSwbv33uM5/wBLgAD//8GAAL3387oT5vpH9x8VRpYFmbOStrsaw4G/drwed71vtmexaLFhqVujW6JXmVSOhfdIGF8GhftIVIhXfVx0GVtzYW1oZWdlb2B3WQh2WoFXUxpUlVeeWx6fW6ZgrWWtZrNsuXMIE/K5c7x8wIUINAfjpAXJB6mPqZGnlKeUppekmsJiGGS/sqOsqKiuGaiuobOatggT6vxN5xX7qdX3ePsuKPsiBRPy9yXVkHUF+yYHXZFgmWOgY6Bnpm6sba10sXq2CHq1grm7GruUup22Hp23o7Grraqsr6e0nwgT6rWguJe7jwj3E/xZFfck92D7WPskfPgdvIe4f7V2GbV3sHCqaapqo2WdXwidYJRcWhpghGJ8ZB77mVgV946ffGl3bXJvGXNvb3NsdwgT8vsw3BX3Eip4gXaCdoUZdoR1h3WICA6E+SO7AfcUuvmCugP6ovjCFWB3X3tefQj3CUD7F9n7Khv7KfsYPfsJQB9emV+cYJ6xb7BtrWsIb1J8TEca+3X3RPtM93J/HpZbk1uQWpC8k7uWuwj3cpf3RPdM93UaznzLb8MerauwqrGnCPx87BX3FPcFSyrOH3mGeYZ4hwggCp46H3iPeY95kAjsz/cEy/cUG/wL/AsVwpe+n7oesmPCTbxJtkMZtUOtP6Y7kniQd5F4CPtSnfsp9zT3Vxr4L/wJFZGekJ+Snqbbrde10rbTvM3CyZiZmJiYmAifXZdXVRr7V/sp+zT7UnkeDiAg+c4B4/nNA/oF+GcVdr1tt2awZbFgqFihCKBZVpZSG1JVgHZZH1l1X25lZWZmbV92WQh2WIBWUhpSllWgWR6gWalfsGaxZbduvXUIdr3BgMQbxMCWoL0fvqG2qLGxsLCpt6C9CKG9lcHEGsSBwHW+Hg4OHqA3/wwJiwwL+OwU+ucVnBMAAQEBC3k6N4IzGzM3lAsB9AAABLAAAAPoACsEsAAjA+gALwJYAAAETAAAAlgAAARMAAACWAAAAlgAAAJYAAACWAAAAlgAAAJYAAACWAAABEwAAAJYAAACWAAAA+gAAARMAAACWAAAAlgAAARMAAAD6AAABEwAAAPoABsEsAAtA+gAZwSwAGgD6AB6AlgAAARMAD4CWAAABEwAAAJYAAACWAAAAlgAAAPoAAACWAAAAlgAAAJYAAAETAAAAlgAAAJYAAAD6AAoBEwAVQJYAAACWAAABEwAAAPoADYETAA+A+gAWAJYAAAAAQAAAAAAAAAAAAA=); +} + +@font-face { + font-family: "Genesys"; + src: url(data:font/ttf;base64,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); } diff --git a/src/renderer/geometries.ts b/src/renderer/geometries.ts index 1acefc2..5a80976 100644 --- a/src/renderer/geometries.ts +++ b/src/renderer/geometries.ts @@ -27,13 +27,15 @@ const MATERIAL_OPTIONS = { const DEFAULT_DICE_OPTIONS: DiceOptions = { diceColor: "#202020", textColor: "#ffffff", - textFont: "Arial" + textFont: "Arial", + narrativeSymbolSet: "Genesys" }; interface DiceOptions { diceColor: string; textColor: string; textFont: string; + narrativeSymbolSet: string; } export default abstract class DiceGeometry { @@ -88,7 +90,8 @@ export default abstract class DiceGeometry { public h: number, public options: Partial = { diceColor: "#202020", - textColor: "#aaaaaa" + textColor: "#aaaaaa", + narrativeSymbolSet: "Genesys" }, public scaler: number ) { @@ -101,10 +104,12 @@ export default abstract class DiceGeometry { } setColor({ diceColor, - textColor + textColor, + narrativeSymbolSet }: { diceColor?: string; textColor?: string; + narrativeSymbolSet?: string; }) { if (diceColor) { this.options.diceColor = diceColor; @@ -112,6 +117,9 @@ export default abstract class DiceGeometry { if (textColor) { this.options.textColor = textColor; } + if (narrativeSymbolSet) { + this.options.narrativeSymbolSet = narrativeSymbolSet; + } } get radius() { return this.scale * this.scaleFactor * (this.scaler ?? 1); @@ -122,6 +130,9 @@ export default abstract class DiceGeometry { get textColor() { return this.options.textColor; } + get narrativeSymbolSet() { + return this.options.narrativeSymbolSet; + } get buffer() { return this.geometry.geometry; } @@ -839,22 +850,8 @@ class D4DiceGeometry extends DiceGeometry { } abstract class GenesysDice extends DiceGeometry { - fontFace: string = "DICE_ROLLER_GENESYS_FONT"; - - /* create() { - if (!document.fonts.check(`12px '${this.fontFace}'`)) { - const font = new FontFace( - this.fontFace, - `url(data:font/ttf;base64,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)` - ); - //@ts-ignore - document.fonts.add(font); - console.log(document.fonts.check(`12px '${this.fontFace}'`)); - } - return super.create(); - } */ + fontFace = this.narrativeSymbolSet } - abstract class GenesysD12DiceGeometry extends GenesysDice { mass = 350; sides = 12; @@ -914,22 +911,49 @@ abstract class GenesysD12DiceGeometry extends GenesysDice { } } +export class GenesysForceDiceGeometry extends GenesysD12DiceGeometry { + labels = [ + "", + "", + "z", //Moved Blank to top as a 1 roll. + "z", //Success + "z", //Success + "z", //Success Success + "z", //Success Success + "z", //Advantage + "z\nz", //Success Advantage + "Z", //Success Advantage + "Z", //Success Advantage + "Z\nZ", //Advantage Advantage + "Z\nZ", //Advantage Advantage + "Z\nZ", //Advantage Advantage + ]; + constructor( + w: number, + h: number, + options: Partial = DEFAULT_DICE_OPTIONS, + scaler: number + ) { + super(w, h, options, scaler); + this.setColor({ diceColor: "white", textColor: "#000000" }); + } +} export class GenesysProficiencyDiceGeometry extends GenesysD12DiceGeometry { labels = [ "", "", - "a\na", - "a", - "a\na", - "x", - "s", - "s\na", - "s", - "s\na", - "s\ns", - "s\na", - "s\ns", - "" + "", //Moved Blank to top as a 1 roll. + "s", //Success + "s", //Success + "s\ns", //Success Success + "s\ns", //Success Success + "a", //Advantage + "s\na", //Success Advantage + "s\na", //Success Advantage + "s\na", //Success Advantage + "a\na", //Advantage Advantage + "a\na", //Advantage Advantage + "x", //Triumph ]; constructor( w: number, @@ -946,18 +970,18 @@ export class GenesysChallengeDiceGeometry extends GenesysD12DiceGeometry { labels = [ "", "", - "t\nt", - "t", - "t\nt", - "t", - "t\nf", - "f", - "t\nf", - "f", - "f\nf", - "y", - "f\nf", - "" + "", //Moved blank to roll of 1. + "f", //Fail + "f", //Fail + "f\nf", //Fail Fail + "f\nf", //Fail Fail + "t", //Threat + "t", //Threat + "t\nf", //Threat Fail + "t\nf", //Threat Fail + "t\nt", //Threat Threat + "t\nt", //Threat Threat + "y", //Despair ]; constructor( w: number, @@ -1000,7 +1024,18 @@ abstract class GenesysD8DiceGeometry extends GenesysDice { } export class GenesysAbilityDiceGeometry extends GenesysD8DiceGeometry { - labels = ["", "", "s", "a", "s\na", "s\ns", "a", "s", "a\na", ""]; + labels = [ + "", + "", + "", //Blank on 1 + "s", //Success + "s", //Success + "s\ns", //Success Success + "a", //Adv + "a", //Adv + "s\na", //Success Advantage + "a\na", //Adv Adv + ]; constructor( w: number, h: number, @@ -1012,7 +1047,18 @@ export class GenesysAbilityDiceGeometry extends GenesysD8DiceGeometry { } } export class GenesysDifficultyDiceGeometry extends GenesysD8DiceGeometry { - labels = ["", "", "t", "f", "f\nt", "t", "", "t\nt", "f\nf", "t", ""]; + labels = [ + "", + "", + "", //Blank on 1 + "f", //Fail + "f\nf", //Fail Fail + "t", //Threat + "t", //Threat + "t", //Threat + "t\nt", //Threat Threat + "f\nt", //Fail Threat + ]; constructor( w: number, h: number, @@ -1054,7 +1100,16 @@ class GenesysD6DiceGeometry extends GenesysDice { } export class GenesysBoostDiceGeometry extends GenesysD6DiceGeometry { - labels = ["", "", "", "", "s", "s \n a", "a \n a", "a", "", ""]; + labels = [ + "", + "", + "", //Blank on 1 + "", //Blank on 2 + "a\na", //Adv Adv + "a", //Adv + "s\na", //Success Adv + "s", //Success + ]; constructor( w: number, h: number, @@ -1066,7 +1121,16 @@ export class GenesysBoostDiceGeometry extends GenesysD6DiceGeometry { } } export class GenesysSetbackDiceGeometry extends GenesysD6DiceGeometry { - labels = ["", "", "", "t", "f", "", ""]; + labels = [ + "", + "", + "", //Blank on 1 + "", //Blank on 2 + "f", //Fail + "f", //Fail + "t", //Threat + "t", //Threat + ]; constructor( w: number, h: number, diff --git a/src/renderer/renderer.ts b/src/renderer/renderer.ts index ce44912..0244761 100644 --- a/src/renderer/renderer.ts +++ b/src/renderer/renderer.ts @@ -15,6 +15,7 @@ import DiceGeometry, { GenesysBoostDiceGeometry, GenesysChallengeDiceGeometry, GenesysDifficultyDiceGeometry, + GenesysForceDiceGeometry, GenesysProficiencyDiceGeometry, GenesysSetbackDiceGeometry, StuntDiceGeometry @@ -27,7 +28,15 @@ import { D6Dice, D8Dice, D12Dice, - D20Dice + D20Dice, + D100Dice, + BoostDice, + ChallengeDice, + ProficiencyDice, + DifficultyDice, + AbilityDice, + SetbackDice, + ForceDice } from "./shapes"; import { @@ -65,6 +74,7 @@ export type RendererData = { scaler: number; renderTime: number; textFont: string; + narrativeSymbolSet: string; }; class DiceRendererClass extends Component { @@ -120,7 +130,8 @@ class DiceRendererClass extends Component { textColor: this.data.textColor, colorfulDice: this.data.colorfulDice, scaler: this.data.scaler, - textFont: this.data.textFont + textFont: this.data.textFont, + narrativeSymbolSet: this.data.narrativeSymbolSet }); } else { this.factory.width = this.WIDTH; @@ -186,7 +197,6 @@ class DiceRendererClass extends Component { } onunload() { - this.stop(); this.loaded = false; cancelAnimationFrame(this.animation); @@ -210,7 +220,7 @@ class DiceRendererClass extends Component { start() { if (this.#animating) { - this.unload(); + this.stop(); } if (!this.loaded) { this.load(); @@ -449,7 +459,7 @@ class DiceRendererClass extends Component { if (!this.data.renderTime) { const renderer = this; function unrender() { - renderer.unload(); + renderer.stop(); document.body.removeEventListener( "click", unrender @@ -623,6 +633,7 @@ class DiceFactory extends Component { const diceColor = this.options.diceColor; const textColor = this.options.textColor; const textFont = this.options.textFont; + const narrativeSymbolSet = this.options.narrativeSymbolSet; // If we want colorful dice then just use the default colors in the geometry if (this.options.colorfulDice) { @@ -631,7 +642,8 @@ class DiceFactory extends Component { return { diceColor, - textFont + textFont, + narrativeSymbolSet }; } constructor( @@ -756,19 +768,94 @@ class DiceFactory extends Component { } case RenderTypes.D100: { dice.push( - new D10Dice( + new D100Dice( this.width, this.height, this.clone("d100"), - vector, - true + vector ), new D10Dice( this.width, this.height, this.clone("d10"), - vector, - true + vector + ) + ); + break; + } + case RenderTypes.BOOST: { + dice.push( + new BoostDice( + this.width, + this.height, + this.clone("boost"), + vector + ) + ); + break; + } + case RenderTypes.SETBACK: { + dice.push( + new SetbackDice( + this.width, + this.height, + this.clone("setback"), + vector + ) + ); + break; + } + case RenderTypes.ABILITY: { + dice.push( + new AbilityDice( + this.width, + this.height, + this.clone("ability"), + vector + ) + ); + break; + } + case RenderTypes.DIFFICULTY: { + dice.push( + new DifficultyDice( + this.width, + this.height, + this.clone("difficulty"), + vector + ) + ); + break; + } + case RenderTypes.FORCE: { + dice.push( + new ForceDice( + this.width, + this.height, + this.clone("force"), + vector + ) + ); + break; + } + case RenderTypes.PROFICIENCY: { + dice.push( + new ProficiencyDice( + this.width, + this.height, + this.clone("proficiency"), + vector + ) + ); + break; + } + case RenderTypes.CHALLENGE: { + dice.push( + new ChallengeDice( + this.width, + this.height, + this.clone("challenge"), + vector ) ); break; @@ -880,6 +967,12 @@ class DiceFactory extends Component { this.colors, this.options.scaler ).create(); + this.dice.force = new GenesysForceDiceGeometry( + this.width, + this.height, + this.colors, + this.options.scaler + ).create(); this.dice.setback = new GenesysSetbackDiceGeometry( this.width, this.height, diff --git a/src/renderer/shapes.ts b/src/renderer/shapes.ts index 2f99c9a..298df4e 100644 --- a/src/renderer/shapes.ts +++ b/src/renderer/shapes.ts @@ -6,6 +6,7 @@ import { type Material, type Quaternion as ThreeQuaternion } from "three"; +import { RenderTypes } from "src/rollers/dice/renderable"; interface DiceVector { pos: { x: number; y: number; z: number }; @@ -44,6 +45,7 @@ export abstract class DiceShape { abstract inertia: number; body: Body; geometry: Mesh; + values: number[] = []; stopped: boolean | number = false; iterations: number = 0; @@ -153,6 +155,7 @@ export abstract class DiceShape { } } let matindex = closest_face.materialIndex - 1; + if (this.sides == 10 && matindex == 0) matindex = 10; return this.data.values?.[matindex] ?? matindex; } @@ -290,6 +293,54 @@ export class D12Dice extends DiceShape { this.create(); } } +export class ProficiencyDice extends DiceShape { + sides = 12; + inertia = 8; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} +export class ForceDice extends DiceShape { + sides = 12; + inertia = 8; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} +export class ChallengeDice extends DiceShape { + sides = 12; + inertia = 8; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} export class D10Dice extends DiceShape { sides = 10; @@ -298,8 +349,23 @@ export class D10Dice extends DiceShape { public w: number, public h: number, public data: { geometry: Mesh; body: Body }, - vector?: { x: number; y: number }, - public isPercentile: boolean = false + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} +export class D100Dice extends DiceShape { + sides = 10; + inertia = 9; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } ) { super(w, h, data); if (vector) { @@ -325,6 +391,54 @@ export class D8Dice extends DiceShape { this.create(); } } +export class AbilityDice extends DiceShape { + sides = 8; + inertia = 10; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} +export class DifficultyDice extends DiceShape { + sides = 8; + inertia = 10; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} +export class Dice extends DiceShape { + sides = 8; + inertia = 10; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} export class D6Dice extends DiceShape { sides = 6; @@ -332,11 +446,71 @@ export class D6Dice extends DiceShape { constructor( public w: number, public h: number, - public data: { - geometry: Mesh; - body: Body; - values?: number[]; - }, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} +export class BoostDice extends DiceShape { + sides = 6; + inertia = 13; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} +export class SetbackDice extends DiceShape { + sides = 6; + inertia = 13; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} +export class StuntDice extends DiceShape { + sides = 6; + inertia = 13; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, + vector?: { x: number; y: number } + ) { + super(w, h, data); + if (vector) { + this.vector = this.generateVector(vector); + } + this.create(); + } +} +export class FudgeDice extends DiceShape { + sides = 6; + inertia = 13; + constructor( + public w: number, + public h: number, + public data: { geometry: Mesh; body: Body }, vector?: { x: number; y: number } ) { super(w, h, data); diff --git a/src/rollers/dice/dice.ts b/src/rollers/dice/dice.ts index 2bb6307..326fbc9 100644 --- a/src/rollers/dice/dice.ts +++ b/src/rollers/dice/dice.ts @@ -32,7 +32,7 @@ type ModifierType = "sort" | "kh" | "kl" | "!" | "!!" | "r" | "u"; export class DiceRoller implements RenderableDice { getType() { - return `${this.faces.max}` as RenderTypes; + return `D${this.faces.max}` as RenderTypes; } constructor( @@ -484,7 +484,13 @@ export class DiceRoller implements RenderableDice { const promises = []; for (let index = 0; index < this.rolls; index++) { promises.push( - new Promise(async (resolve) => { + new Promise(async (resolve, reject) => { + this.#controller?.signal.addEventListener( + "abort", + () => { + reject(); + } + ); const value = await this.getValue( this.getShapes(index) ); @@ -493,11 +499,14 @@ export class DiceRoller implements RenderableDice { }) ); } - await Promise.all(promises); + try { + await Promise.all(promises); + } catch (e) {} } return results; } + applyConditions() { for (const result of this.results.values()) { const negate = this.conditions.find( @@ -670,8 +679,10 @@ export class DiceRoller implements RenderableDice { getGeometries() { return [...this.shapes.values()].flat(); } - async render(): Promise { + #controller: AbortController; + async render(abortController: AbortController): Promise { this.shouldRender = true; + this.#controller = abortController; await this.roll(); this.shouldRender = false; } diff --git a/src/rollers/dice/narrative.ts b/src/rollers/dice/narrative.ts new file mode 100644 index 0000000..cc48e08 --- /dev/null +++ b/src/rollers/dice/narrative.ts @@ -0,0 +1,549 @@ +import { RenderableRoller } from "../roller"; +import { DiceRoller } from "./dice"; +import { RenderTypes } from "./renderable"; +import type { App } from "obsidian"; +import type { LexicalToken } from "src/lexer/lexer"; +import type { DiceRollerSettings } from "src/settings/settings.types"; + +interface NarrativeResult { + success: number; //negative => failure + advantage: number; //negative => threat + triumph: number; //triumphs & despairs do not cancel, but count additonally for success/failure for the purpose of overall success. + despair: number; + light: number; //Light & Dark Side points are rolled independently and do not cancel. + dark: number; +} + +abstract class NarrativeRoller extends DiceRoller { + override canRender(): boolean { + return true; + } + abstract toNarrativeResult(): NarrativeResult; + abstract override getType(): RenderTypes; +} + +class BoostRoller extends NarrativeRoller { + toNarrativeResult(): NarrativeResult { + const narrativeResult: NarrativeResult = { + success: 0, + advantage: 0, + triumph: 0, + despair: 0, + light: 0, + dark: 0 + }; + for (const result of this.resultArray) { + if (result === 6) narrativeResult.success += 1; //Success + if (result === 5) { + //1 success 1 adv + narrativeResult.advantage += 1; + narrativeResult.success += 1; + } + if (result === 4) narrativeResult.advantage += 1; //1 Adv + if (result === 3) narrativeResult.advantage += 2; //2 Adv + if (result === 2) continue; //Blank + if (result === 1) continue; //Blank + if (result === 0) continue; + } + + return narrativeResult; + } + override getType(): RenderTypes { + return RenderTypes.BOOST; + } + override canRender(): boolean { + return true; + } + constructor(amount: number) { + super(`${amount}d6`); + } +} +class SetbackRoller extends NarrativeRoller { + toNarrativeResult(): NarrativeResult { + const narrativeResult: NarrativeResult = { + success: 0, + advantage: 0, + triumph: 0, + despair: 0, + light: 0, + dark: 0 + }; + for (const result of this.resultArray) { + if (result === 6) narrativeResult.advantage -= 1; //1 Threat + if (result === 5) narrativeResult.advantage -= 1; //1 Threat + if (result === 4) narrativeResult.success -= 1; //1 Fail + if (result === 3) narrativeResult.success -= 1; //1 Fail + if (result === 2) continue; //Blank + if (result === 1) continue; //Blank + if (result === 0) continue; + } + + return narrativeResult; + } + override getType(): RenderTypes { + return RenderTypes.SETBACK; + } + override canRender(): boolean { + return true; + } + constructor(amount: number) { + super(`${amount}d6`); + } +} +class AbilityRoller extends NarrativeRoller { + toNarrativeResult(): NarrativeResult { + const narrativeResult: NarrativeResult = { + success: 0, + advantage: 0, + triumph: 0, + despair: 0, + light: 0, + dark: 0 + }; + for (const result of this.resultArray) { + if (result === 8) { + //2 Adv + narrativeResult.advantage += 2; + } + if (result === 7) { + //1 success 1 adv + narrativeResult.advantage += 1; + narrativeResult.success += 1; + } + if (result === 6) narrativeResult.advantage += 1; //1 Adv + if (result === 5) narrativeResult.advantage += 1; //1 Adv + if (result === 4) narrativeResult.success += 2; //2 Success + if (result === 3) narrativeResult.success += 1; //1 Success + if (result === 2) narrativeResult.success += 1; //1 Success + if (result === 1) continue; //Blank + if (result === 0) continue; + } + + return narrativeResult; + } + override getType(): RenderTypes { + return RenderTypes.ABILITY; + } + override canRender(): boolean { + return true; + } + constructor(amount: number) { + super(`${amount}d8`); + } +} +class DifficultyRoller extends NarrativeRoller { + toNarrativeResult(): NarrativeResult { + const narrativeResult: NarrativeResult = { + success: 0, + advantage: 0, + triumph: 0, + despair: 0, + light: 0, + dark: 0 + }; + for (const result of this.resultArray) { + if (result === 8) { + //1 Threat 1 Fail + narrativeResult.advantage -= 1; + narrativeResult.success -= 1; + } + if (result === 7) narrativeResult.advantage -= 2; //2 Threat + if (result === 6) narrativeResult.advantage -= 1; //1 Threat + if (result === 5) narrativeResult.advantage -= 1; //1 Threat + if (result === 4) narrativeResult.advantage -= 1; //1 Threat + if (result === 3) narrativeResult.success -= 2; //2 Fail + if (result === 2) narrativeResult.success -= 1; //1 Fail + if (result === 1) continue; //Blank + if (result === 0) continue; + } + + return narrativeResult; + } + override getType(): RenderTypes { + return RenderTypes.DIFFICULTY; + } + override canRender(): boolean { + return true; + } + constructor(amount: number) { + super(`${amount}d8`); + } +} +class ProficiencyRoller extends NarrativeRoller { + toNarrativeResult(): NarrativeResult { + const narrativeResult: NarrativeResult = { + success: 0, + advantage: 0, + triumph: 0, + despair: 0, + light: 0, + dark: 0 + }; + for (const result of this.resultArray) { + //@Javalent are 0 results possible? + if (result === 12) { + //1 Triumph + narrativeResult.triumph += 1; + narrativeResult.success += 1; //Triumph counts as success, but Triumph cannot be cancelled. + } + if (result === 11) narrativeResult.advantage += 2; //2 Adv + if (result === 10) narrativeResult.advantage += 2; //2 Adv + if (result === 9) { + //1 Adv 1 Success + narrativeResult.advantage += 1; + narrativeResult.success += 1; + } + if (result === 8) { + //1 Adv 1 Suc + narrativeResult.advantage += 1; + narrativeResult.success += 1; + } + if (result === 7) { + //1 Adv 1 Suc + narrativeResult.advantage += 1; + narrativeResult.success += 1; + } + if (result === 6) { + //1 Adv + narrativeResult.advantage += 1; + } + if (result === 5) { + //2 Success + narrativeResult.success += 2; + } + if (result === 4) narrativeResult.success += 2; //2 Success + if (result === 3) narrativeResult.success += 1; //1 Success + if (result === 2) narrativeResult.success += 1; //1 Success + if (result === 1) continue; //Blank + if (result === 0) continue; + } + + return narrativeResult; + } + override getType(): RenderTypes { + return RenderTypes.PROFICIENCY; + } + override canRender(): boolean { + return true; + } + constructor(amount: number) { + super(`${amount}d12`); + } +} +class ChallengeRoller extends NarrativeRoller { + toNarrativeResult(): NarrativeResult { + const narrativeResult: NarrativeResult = { + success: 0, + advantage: 0, + triumph: 0, + despair: 0, + light: 0, + dark: 0 + }; + for (const result of this.resultArray) { + //@Javalent are 0 results possible? + if (result === 12) { + //1 Despair + narrativeResult.despair += 1; + narrativeResult.success -= 1; //Despair counts as fail, but does not cancel Despair. + } + if (result === 11) narrativeResult.advantage -= 2; //2 Threat + if (result === 10) narrativeResult.advantage -= 2; //2 Threat + if (result === 9) { + //1 Threat 1 Fail + narrativeResult.advantage -= 1; + narrativeResult.success -= 1; + } + if (result === 8) { + //1 Threat 1 Fail + narrativeResult.advantage -= 1; + narrativeResult.success -= 1; + } + if (result === 7) { + //1 Threat + narrativeResult.advantage -= 1; + } + if (result === 6) { + //1 Threat + narrativeResult.advantage -= 1; + } + if (result === 5) { + //2 Fail + narrativeResult.success -= 2; + } + if (result === 4) narrativeResult.success -= 2; //2 Fail + if (result === 3) narrativeResult.success -= 1; //1 Fail + if (result === 2) narrativeResult.success -= 1; //1 Fail + if (result === 1) continue; //Blank + if (result === 0) continue; + } + + return narrativeResult; + } + override getType(): RenderTypes { + return RenderTypes.CHALLENGE; + } + override canRender(): boolean { + return true; + } + constructor(amount: number) { + super(`${amount}d12`); + } +} +class ForceRoller extends NarrativeRoller { + toNarrativeResult(): NarrativeResult { + const narrativeResult: NarrativeResult = { + success: 0, + advantage: 0, + triumph: 0, + despair: 0, + light: 0, + dark: 0 + }; + for (const result of this.resultArray) { + //@Javalent are 0 results possible? + if (result === 12) narrativeResult.light += 2; //2 Light + if (result === 11) narrativeResult.light += 2; //2 Light + if (result === 10) narrativeResult.light += 2; //2 Light + if (result === 9) narrativeResult.light += 1; //1 Light + if (result === 8) narrativeResult.light += 1; //1 Light + if (result === 7) narrativeResult.dark += 2; //2 Dark + if (result === 6) narrativeResult.dark += 1; //1 Dark + if (result === 5) narrativeResult.dark += 1; //1 Dark + if (result === 4) narrativeResult.dark += 1; //1 Dark + if (result === 3) narrativeResult.dark += 1; //1 Dark + if (result === 2) narrativeResult.dark += 1; //1 Dark + if (result === 1) narrativeResult.dark += 1; //1 Dark + if (result === 0) continue; + } + + return narrativeResult; + } + override getType(): RenderTypes { + return RenderTypes.FORCE; + } + override canRender(): boolean { + return true; + } + constructor(amount: number) { + super(`${amount}d12`); + } +} +const NARRATIVE_FACES = ["g", "y", "b", "r", "p", "s", "w"] as const; +type NarrativeFace = (typeof NARRATIVE_FACES)[number]; +export class NarrativeStackRoller extends RenderableRoller { + constructor( + public data: DiceRollerSettings, + public original: string, + public lexemes: LexicalToken[], + public app: App, + position = data.position + ) { + super(data, original, lexemes, position); + } + children: NarrativeRoller[] = []; + getTooltip() { + let map = { + success: 0, + failure: 0, + advantage: 0, + threat: 0, + triumph: 0, + despair: 0, + light: 0, + dark: 0 + }; + + for (const child of this.children) { + const die = child.toNarrativeResult(); + + if (die.success > 0) { + map.success += die.success; + } else { + map.failure += die.success; + } + if (die.advantage > 0) { + map.advantage += die.advantage; + } else { + map.threat += die.advantage; + } + map.triumph += die.triumph; + map.despair += die.despair; + map.light += die.light; + map.dark += die.dark + } + return `**Totals** +Successes: ${map.success} +Failures: ${map.failure} +Advantages: ${map.advantage} +Threats: ${map.threat} +Triumphs: ${map.triumph} +Despairs: ${map.despair} +${map.light > 0 ? `Light Side: ${map.light}` : ''} +${map.dark > 0 ? `Dark Side: ${map.dark}` : ''}`; + } + private formatSymbol(text: string, fontFamily: string): string { + return `${text}`; + } + getResultText(): string { + const display = []; this.data.narrativeSymbolSet + if (this.data.displayAsSymbols) { + if (this.result.success === 0) { + display.push(`Wash`); + } else if (this.result.success > 0) { + display.push(`${this.result.success} `+ this.formatSymbol('s', this.data.narrativeSymbolSet)); + } else if (this.result.success < 0) { + display.push(`${this.result.success} ` + this.formatSymbol('f', this.data.narrativeSymbolSet)); + } + if (this.result.advantage > 0) { + display.push(`${this.result.advantage} ` + this.formatSymbol('a', this.data.narrativeSymbolSet)); + } else if (this.result.advantage < 0) { + display.push(`${Math.abs(this.result.advantage)} ` + this.formatSymbol('t', this.data.narrativeSymbolSet)); + } + if (this.result.triumph > 0) { + display.push(`${this.result.triumph} ` + this.formatSymbol('x', this.data.narrativeSymbolSet)); + } else if (this.result.despair > 0) { + display.push(`${Math.abs(this.result.despair)} ` + this.formatSymbol('y', this.data.narrativeSymbolSet)); + } + if (this.result.light > 0) { + display.push(`${this.result.light} ` + this.formatSymbol('z', this.data.narrativeSymbolSet)); + } + if (this.result.dark > 0) { + display.push(`${this.result.dark} ` + this.formatSymbol('Z', this.data.narrativeSymbolSet)); + } + return display.join(", "); + } else { + if (this.result.success === 0) { + display.push(`Wash`); + } else if (this.result.success > 0) { + display.push(`${this.result.success} success`); + } else if (this.result.success < 0) { + display.push(`${Math.abs(this.result.success)} failure`); + } + if (this.result.advantage > 0) { + display.push(`${this.result.advantage} advantage`); + } else if (this.result.advantage < 0) { + display.push(`${Math.abs(this.result.advantage)} threat`); + } + if (this.result.triumph > 0) { + display.push(`${this.result.triumph} triumph`); + } else if (this.result.despair > 0) { + display.push(`${Math.abs(this.result.despair)} despair`); + } + if (this.result.light > 0) { + display.push(`${this.result.light} light side`); + } + if (this.result.dark > 0) { + display.push(`${this.result.dark} dark side`); + } + return display.join(", "); + } + } + onload(): void { + const map: Map = new Map(); + + for (const lexeme of this.lexemes) { + for (const maybe of lexeme.value) { + let char = maybe as NarrativeFace; + if (!NARRATIVE_FACES.includes(char)) continue; + map.set(char, (map.get(char) ?? 0) + 1); + } + } + + for (const [type, amount] of map) { + let roller: NarrativeRoller; + switch (type) { + case "g": { + roller = new AbilityRoller(amount); + break; + } + case "y": { + roller = new ProficiencyRoller(amount); + break; + } + case "b": { + roller = new BoostRoller(amount); + break; + } + case "r": { + roller = new ChallengeRoller(amount); + break; + } + case "p": { + roller = new DifficultyRoller(amount); + break; + } + case "s": { + roller = new SetbackRoller(amount); + break; + } + case "w": { + roller = new ForceRoller(amount); + break; + } + } + this.children.push(roller); + } + super.onload(); + } + getReplacer(): Promise { + throw new Error("Method not implemented."); + } + result: NarrativeResult; + async roll(render?: boolean): Promise { + if (render || (this.shouldRender && this.hasRunOnce)) { + await this.renderChildren(); + } else { + return this.rollSync(); + } + this.hasRunOnce = true; + this.calculate(); + + this.trigger("new-result"); + this.app.workspace.trigger("dice-roller:new-result", this); + + this.render(); + return this.result; + } + rollSync() { + for (const dice of this.children) { + dice.rollSync(); + } + this.hasRunOnce = true; + this.calculate(); + this.trigger("new-result"); + this.app.workspace.trigger("dice-roller:new-result", this); + + this.render(); + return this.result; + } + + calculate() { + this.result = this.children.reduce( + (a, b) => { + a.success += b.toNarrativeResult().success; + a.advantage += b.toNarrativeResult().advantage; + a.triumph += b.toNarrativeResult().triumph; + a.despair += b.toNarrativeResult().despair; + a.light += b.toNarrativeResult().light; + a.dark += b.toNarrativeResult().dark; + return a; + }, + { + success: 0, + advantage: 0, + triumph: 0, + despair: 0, + light: 0, + dark: 0 + } + ); + } + + async build(): Promise { + this.resultEl.empty(); + + this.resultEl.addClass("dice-roller-genesys"); + this.resultEl.innerHTML = this.getResultText(); + } +} diff --git a/src/rollers/dice/renderable.ts b/src/rollers/dice/renderable.ts index 87e4e48..ae332e4 100644 --- a/src/rollers/dice/renderable.ts +++ b/src/rollers/dice/renderable.ts @@ -8,7 +8,7 @@ export interface RenderableDice { roll(): Promise; rollSync(): void; - render(): Promise; + render(abortController: AbortController): Promise; shouldRender: boolean; getValue(shapes?: DiceShape[]): Promise; @@ -16,17 +16,26 @@ export interface RenderableDice { } export const RenderTypes = { + NONE: "none", /** Polyhedral */ - D4: "4", - D6: "6", - D8: "8", - D10: "10", - D12: "12", - D20: "20", - D100: "100", + D4: "D4", + D6: "D6", + D8: "D8", + D10: "D10", + D12: "D12", + D20: "D20", + D100: "D100", /** Special */ FUDGE: "fudge", STUNT: "stunt", - NONE: "none" + + /** Genesys */ + BOOST: "boost", + SETBACK: "setback", + ABILITY: "ability", + DIFFICULTY: "difficulty", + PROFICIENCY: "proficiency", + CHALLENGE: "challenge", + FORCE: "force" } as const; export type RenderTypes = (typeof RenderTypes)[keyof typeof RenderTypes]; diff --git a/src/rollers/dice/stack.ts b/src/rollers/dice/stack.ts index f02031f..5af044a 100644 --- a/src/rollers/dice/stack.ts +++ b/src/rollers/dice/stack.ts @@ -1,4 +1,4 @@ -import { App, setIcon, Notice } from "obsidian"; +import { App, Notice } from "obsidian"; import type { LexicalToken } from "src/lexer/lexer"; import { DiceRenderer } from "src/renderer/renderer"; import { @@ -6,8 +6,7 @@ import { type DiceRollerSettings } from "src/settings/settings.types"; import { ExpectedValue, Round } from "src/types/api"; -import { Icons } from "src/utils/icons"; -import { Roller, BasicRoller } from "../roller"; +import { Roller, RenderableRoller } from "../roller"; import { DiceRoller } from "./dice"; import { PercentRoller } from "./percentage"; import { StuntRoller } from "./stunt"; @@ -248,13 +247,7 @@ export class BasicStackRoller extends Roller { } } -export class StackRoller extends BasicRoller { - onunload(): void { - if (this.isRendering) { - DiceRenderer.unrender(); - } - super.onunload(); - } +export class StackRoller extends RenderableRoller { result: number; fixedText: string; displayFixedText: boolean = false; @@ -271,10 +264,10 @@ export class StackRoller extends BasicRoller { shouldRender: boolean = false; isRendering: boolean = false; showFormula: boolean = false; - get resultText() { + getDisplayText() { let text: string[] = []; let index = 0; - this.dice.forEach((dice) => { + this.children.forEach((dice) => { const slice = this.original.slice(index); text.push( slice.slice(0, slice.indexOf(dice.lexeme.text)), @@ -289,22 +282,26 @@ export class StackRoller extends BasicRoller { text.push(this.original.slice(index)); return text.join(""); } - get tooltip() { + getResultText() { + return `${this.result}`; + } + getTooltip() { if (this.isRendering) { return this.original; } if (this._tooltip) return this._tooltip; + const display = this.getDisplayText(); if (this.expectedValue === ExpectedValue.Roll || this.shouldRender) { if (this.displayFixedText) { - return `${this.original}\n${this.result} = ${this.resultText}`; + return `${this.original}\n${this.result} = ${display}`; } - return `${this.original}\n${this.resultText}`; + return `${this.original}\n${display}`; } if (this.expectedValue === ExpectedValue.Average) { if (this.displayFixedText) { - return `${this.original}\n${this.result} = average: ${this.resultText}`; + return `${this.original}\n${this.result} = average: ${display}`; } - return `${this.original}\naverage: ${this.resultText}`; + return `${this.original}\naverage: ${display}`; } return `${this.original}\nempty`; @@ -395,29 +392,22 @@ export class StackRoller extends BasicRoller { } } async onClick(evt: MouseEvent) { - evt.stopPropagation(); - evt.stopImmediatePropagation(); if (evt.getModifierState("Alt")) { this.expectedValue = ExpectedValue.Average; } else if (evt.getModifierState("Control")) { this.expectedValue = ExpectedValue.None; } - if (evt.getModifierState("Shift")) { - await this.roll(true); - this.hasRunOnce = true; - } else if (window.getSelection()?.isCollapsed) { - await this.roll(); - } + super.onClick(evt); } get dynamic() { - return this.dice.filter((d) => !d.static); + return this.children.filter((d) => !d.static); } get static() { - return this.dice.filter((d) => d.static); + return this.children.filter((d) => d.static); } get isStatic() { - return this.dice.every((d) => d.static); + return this.children.every((d) => d.static); } constructor( @@ -446,8 +436,6 @@ export class StackRoller extends BasicRoller { this.displayFixedText = this.fixedText !== ""; this.round = round; this.signed = signed; - this.loaded = true; - this.trigger("loaded"); } operators: Record number> = { "+": (a: number, b: number): number => a + b, @@ -462,12 +450,12 @@ export class StackRoller extends BasicRoller { maxStack: number[] = []; minStack: number[] = []; stackCopy: Array = []; - dice: DiceRoller[] = []; + children: DiceRoller[] = []; hasRunOnce = false; rollSync() { this.stunted = ""; this.buildDiceTree(); - for (const dice of this.dice) { + for (const dice of this.children) { dice.rollSync(); } this.calculate(); @@ -478,28 +466,6 @@ export class StackRoller extends BasicRoller { this.hasRunOnce = true; return this.result; } - setSpinner() { - this.resultEl.empty(); - setIcon(this.resultEl.createDiv("should-spin"), Icons.LOADING); - } - async renderDice() { - this.isRendering = true; - this.setTooltip(); - this.setSpinner(); - const promises = []; - for (const die of this.dice) { - promises.push( - new Promise(async (resolve) => { - await die.render(); - resolve(); - }) - ); - } - await Promise.all(promises); - - this.isRendering = false; - this.setTooltip(); - } buildDiceTree() { let index = 0; for (const dice of this.lexemes) { @@ -513,7 +479,7 @@ export class StackRoller extends BasicRoller { continue; } case "u": { - let diceInstance = this.dice[index - 1]; + let diceInstance = this.children[index - 1]; let data = dice.value ? Number(dice.value) : 1; diceInstance.modifiers.set("u", { @@ -524,7 +490,7 @@ export class StackRoller extends BasicRoller { break; } case "kh": { - let diceInstance = this.dice[index - 1]; + let diceInstance = this.children[index - 1]; let data = dice.value ? Number(dice.value) : 1; diceInstance.modifiers.set("kh", { @@ -535,7 +501,7 @@ export class StackRoller extends BasicRoller { break; } case "dl": { - let diceInstance = this.dice[index - 1]; + let diceInstance = this.children[index - 1]; let data = dice.value ? Number(dice.value) : 1; data = diceInstance.rolls - data; @@ -548,7 +514,7 @@ export class StackRoller extends BasicRoller { break; } case "kl": { - let diceInstance = this.dice[index - 1]; + let diceInstance = this.children[index - 1]; let data = dice.value ? Number(dice.value) : 1; diceInstance.modifiers.set("kl", { @@ -559,7 +525,7 @@ export class StackRoller extends BasicRoller { break; } case "dh": { - let diceInstance = this.dice[index - 1]; + let diceInstance = this.children[index - 1]; let data = dice.value ? Number(dice.value) : 1; data = diceInstance.rolls - data; @@ -572,7 +538,7 @@ export class StackRoller extends BasicRoller { break; } case "!": { - let diceInstance = this.dice[index - 1]; + let diceInstance = this.children[index - 1]; let data = Number(dice.value) || 1; diceInstance.modifiers.set("!", { @@ -584,7 +550,7 @@ export class StackRoller extends BasicRoller { break; } case "!!": { - let diceInstance = this.dice[index - 1]; + let diceInstance = this.children[index - 1]; let data = Number(dice.value) || 1; diceInstance.modifiers.set("!!", { @@ -596,7 +562,7 @@ export class StackRoller extends BasicRoller { break; } case "r": { - let diceInstance = this.dice[index - 1]; + let diceInstance = this.children[index - 1]; let data = Number(dice.value) || 1; diceInstance.modifiers.set("r", { @@ -607,7 +573,7 @@ export class StackRoller extends BasicRoller { break; } case "sort": { - let diceInstance = this.dice[index - 1]; + let diceInstance = this.children[index - 1]; let data = Number(dice.value); diceInstance.modifiers.set("sort", { @@ -625,10 +591,10 @@ export class StackRoller extends BasicRoller { ) { const previous = this.stack.pop(); dice.value = `${previous.result}${dice.value}`; - this.dice[index] = new DiceRoller(dice.value, dice); + this.children[index] = new DiceRoller(dice.value, dice); } - if (!this.dice[index]) { - this.dice[index] = new DiceRoller(dice.value, dice); + if (!this.children[index]) { + this.children[index] = new DiceRoller(dice.value, dice); } index++; @@ -636,23 +602,32 @@ export class StackRoller extends BasicRoller { } case "fudge": { - if (!this.dice[index]) { - this.dice[index] = new FudgeRoller(dice.value, dice); + if (!this.children[index]) { + this.children[index] = new FudgeRoller( + dice.value, + dice + ); } index++; break; } case "stunt": { - if (!this.dice[index]) { - this.dice[index] = new StuntRoller(dice.value, dice); + if (!this.children[index]) { + this.children[index] = new StuntRoller( + dice.value, + dice + ); } index++; break; } case "%": { - if (!this.dice[index]) { - this.dice[index] = new PercentRoller(dice.value, dice); + if (!this.children[index]) { + this.children[index] = new PercentRoller( + dice.value, + dice + ); } index++; break; @@ -663,15 +638,15 @@ export class StackRoller extends BasicRoller { async roll(render?: boolean) { this.stunted = ""; this.stackCopy = []; - if (!this.dice.length) { + if (!this.children.length) { this.buildDiceTree(); } DiceRenderer.stop(); - this.dice.forEach((dice) => (dice.shouldRender = false)); + this.children.forEach((dice) => (dice.shouldRender = false)); if (render || (this.shouldRender && this.hasRunOnce)) { - await this.renderDice(); + await this.renderChildren(); } else { - for (const dice of this.dice) { + for (const dice of this.children) { await dice.roll(); } } @@ -682,7 +657,7 @@ export class StackRoller extends BasicRoller { this.showRenderNotice && (render || (this.shouldRender && this.hasRunOnce)) ) { - new Notice(`${this.tooltip}\n\nResult: ${this.result}`); + new Notice(`${this.getTooltip()}\n\nResult: ${this.result}`); } this.trigger("new-result"); @@ -743,10 +718,10 @@ export class StackRoller extends BasicRoller { case "fudge": case "%": case "dice": { - this.stack.push(this.dice[index]); - this.stackCopy.push(this.dice[index]); - this.minStack.push(this.dice[index].getMinPossible()); - this.maxStack.push(this.dice[index].getMaxPossible()); + this.stack.push(this.children[index]); + this.stackCopy.push(this.children[index]); + this.minStack.push(this.children[index].getMinPossible()); + this.maxStack.push(this.children[index].getMaxPossible()); index++; } default: { diff --git a/src/rollers/line/line.ts b/src/rollers/line/line.ts index 199e8ec..76e36f5 100644 --- a/src/rollers/line/line.ts +++ b/src/rollers/line/line.ts @@ -17,7 +17,7 @@ export class LineRoller extends GenericEmbeddedRoller { types: string[]; content: string; - get tooltip() { + getTooltip() { return `${this.original}\n${this.path}`; } async build() { diff --git a/src/rollers/roller.ts b/src/rollers/roller.ts index c2e0aa4..142be5d 100644 --- a/src/rollers/roller.ts +++ b/src/rollers/roller.ts @@ -11,11 +11,13 @@ import { } from "obsidian"; import type { LexicalToken } from "src/lexer/lexer"; +import { DiceRenderer } from "src/renderer/renderer"; import type { ButtonPosition, DiceRollerSettings } from "src/settings/settings.types"; import { Icons } from "src/utils/icons"; +import type { RenderableDice } from "./dice/renderable"; export interface ComponentLike { addChild(child: Component): void; @@ -46,7 +48,11 @@ export abstract class Roller extends Component implements Events { abstract roll(): Promise | T; components: ComponentLike[] = []; - + loaded: boolean = false; + onload(): void { + this.loaded = true; + this.trigger("loaded"); + } onunload(): void { this.components = []; } @@ -71,7 +77,8 @@ interface BareRoller { trigger(name: "loaded"): void; on(name: "new-result", callback: () => void): EventRef; trigger(name: "new-result"): void; -}abstract class BareRoller extends Roller { +} +abstract class BareRoller extends Roller { constructor( public data: DiceRollerSettings, public original = "", @@ -125,14 +132,14 @@ interface BareRoller { this.containerEl.append(pre); } abstract build(): Promise; - abstract get tooltip(): string; + abstract getTooltip(): string; containerEl: HTMLSpanElement; resultEl: HTMLSpanElement; iconEl: HTMLSpanElement; setTooltip() { if (this.data.displayResultsInline) return; this.containerEl.setAttrs({ - "aria-label": this.tooltip + "aria-label": this.getTooltip() }); } getRandomBetween(min: number, max: number): number { @@ -159,6 +166,12 @@ export abstract class BasicRoller extends BareRoller { source: string; abstract getReplacer(): Promise; save: boolean = false; + + setSpinner() { + this.resultEl.empty(); + setIcon(this.resultEl.createDiv("should-spin"), Icons.LOADING); + } + abstract result: T; abstract roll(): Promise; @@ -170,8 +183,9 @@ export abstract class BasicRoller extends BareRoller { } get inlineText() { - return `${this.tooltip.split("\n").join(" -> ")} -> `; + return `${this.getTooltip().split("\n").join(" -> ")} -> `; } + constructor( public data: DiceRollerSettings, public original: string, @@ -182,6 +196,66 @@ export abstract class BasicRoller extends BareRoller { } } +export abstract class RenderableRoller extends BasicRoller { + shouldRender: boolean = false; + isRendering: boolean = false; + hasRunOnce: boolean = false; + override onunload(): void { + if (this.isRendering) { + DiceRenderer.stop(); + } + super.onunload(); + } + override async onClick(evt: MouseEvent) { + evt.stopPropagation(); + evt.stopImmediatePropagation(); + + if (this.isRendering) { + DiceRenderer.stop(); + } + this.#controller?.abort(); + if (evt.getModifierState("Shift")) { + await this.roll(true); + this.hasRunOnce = true; + } else if (window.getSelection()?.isCollapsed) { + await this.roll(); + } + } + + abstract rollSync(): T; + abstract roll(render?: boolean): Promise; + abstract getResultText(): string; + + children: RenderableDice[]; + #controller: AbortController; + async renderChildren(): Promise { + this.isRendering = true; + this.setTooltip(); + this.setSpinner(); + const promises = []; + this.#controller = new AbortController(); + for (const die of this.children) { + promises.push( + new Promise(async (resolve, reject) => { + this.#controller.signal.addEventListener("abort", () => { + reject(); + }); + await die.render(this.#controller); + resolve(); + }) + ); + } + try { + await Promise.all(promises); + } catch (e) { + return; + } + + this.isRendering = false; + + this.setTooltip(); + } +} export abstract class GenericFileRoller extends BasicRoller { path: string; file: TFile; @@ -308,7 +382,7 @@ export abstract class GenericEmbeddedRoller extends GenericFileRoller { export class ArrayRoller extends BareRoller { declare result: any; results: any[]; - get tooltip() { + getTooltip(): string { return `${this.options.toString()}\n\n${this.results.toString()}`; } async roll() { diff --git a/src/rollers/section/section.ts b/src/rollers/section/section.ts index 370358f..9fdef86 100644 --- a/src/rollers/section/section.ts +++ b/src/rollers/section/section.ts @@ -44,7 +44,7 @@ export class SectionRoller extends GenericEmbeddedRoller { content: string; levels: string[]; - get tooltip() { + getTooltip() { return `${this.original}\n${this.path}`; } async build() { diff --git a/src/rollers/table/table.ts b/src/rollers/table/table.ts index 6faf16b..b903e44 100644 --- a/src/rollers/table/table.ts +++ b/src/rollers/table/table.ts @@ -68,13 +68,20 @@ export class TableRoller extends GenericFileRoller { .toLowerCase(); this.header = header; } - get tooltip() { + getTooltip() { return this.prettyTooltip; } async getReplacer() { return this.result; } result: string; + getResultText(): string { + const result = [this.result]; + if (this.data.displayResultsInline) { + result.unshift(this.inlineText); + } + return result.join(""); + } async build() { this.resultEl.empty(); const result = [this.result]; @@ -84,7 +91,7 @@ export class TableRoller extends GenericFileRoller { const div = createSpan(); MarkdownRenderer.render( this.app, - result.join(""), + this.getResultText(), div, this.source, new Component() @@ -179,7 +186,7 @@ export class TableRoller extends GenericFileRoller { if (subRoller instanceof TableRoller) { subTooltips.push(subRoller.combinedTooltip); } else { - const [top, bottom] = subRoller.tooltip.split("\n"); + const [top, bottom] = subRoller.getTooltip().split("\n"); subTooltips.push(top + " --> " + bottom); } } @@ -215,9 +222,7 @@ export class TableRoller extends GenericFileRoller { if (!rollsRoller.isStatic) { formula = formula.replace( this.rollsFormula, - `${this.rollsFormula.trim()} --> ${ - rollsRoller.resultText - } > ` + `${this.rollsFormula.trim()} --> ${rollsRoller.getDisplayText()} > ` ); } } @@ -244,7 +249,7 @@ export class TableRoller extends GenericFileRoller { subTooltip = this.lookupRoller.original.trim() + " --> " + - `${this.lookupRoller.resultText}${ + `${this.lookupRoller.getDisplayText()}${ this.header ? " | " + this.header : "" }`.trim(); selectedOption = option[1]; diff --git a/src/rollers/tag/tag.ts b/src/rollers/tag/tag.ts index a612f34..efe5b0b 100644 --- a/src/rollers/tag/tag.ts +++ b/src/rollers/tag/tag.ts @@ -201,7 +201,7 @@ abstract class DataViewEnabledRoller extends BasicRoller { } }); } - get tooltip() { + getTooltip() { return this.original; } } diff --git a/src/settings/settings.const.ts b/src/settings/settings.const.ts index b48ec24..691c722 100644 --- a/src/settings/settings.const.ts +++ b/src/settings/settings.const.ts @@ -28,6 +28,8 @@ export const DEFAULT_SETTINGS: DiceRollerSettings = { textColor: "#ffffff", textFont: "Arial", showLeafOnStartup: true, + narrativeSymbolSet: "Genesys", + displayAsSymbols: false, displayAsEmbed: true, round: Round.None, diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 5a99048..3709e21 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -125,6 +125,11 @@ export default class SettingTab extends PluginSettingTab { cls: "dice-roller-nested-settings" }) ); + this.buildNarrative( + this.contentEl.createEl("details", { + cls: "dice-roller-nested-settings" + }) + ); this.buildDiceModTemplateFoldersSettings( this.contentEl.createEl("details", { cls: "dice-roller-nested-settings" @@ -359,6 +364,38 @@ export default class SettingTab extends PluginSettingTab { }); }); } + buildNarrative(containerEl: HTMLDetailsElement) { + containerEl.empty(); + this.#buildSummary(containerEl, "Narrative Rollers"); + + // Dropdown for selecting the symbol set (Genesys or SWRPG) + new Setting(containerEl) + .setName("Symbol Set") + .setDesc("Select between Genesys or SWRPG symbols.") + .addDropdown((dropdown) => { + dropdown.addOption("Genesys", "Genesys"); + dropdown.addOption("SWRPG", "SWRPG"); + dropdown.setValue(this.plugin.data.narrativeSymbolSet); + dropdown.onChange(async (value) => { + this.plugin.data.narrativeSymbolSet = value; + DiceRenderer.setData(this.plugin.getRendererData()); + await this.plugin.saveSettings(); + }); + }); + + // Toggle for displaying as text or symbols + new Setting(containerEl) + .setName("Display Results as Symbols") + .setDesc("Toggle between text and symbol results.") + .addToggle((toggle) => { + toggle.setValue(this.plugin.data.displayAsSymbols); + toggle.onChange(async (value) => { + this.plugin.data.displayAsSymbols = value; + await this.plugin.saveSettings(); + }); + }); + } + buildView(containerEl: HTMLDetailsElement) { containerEl.empty(); this.#buildSummary(containerEl, "Dice Tray"); diff --git a/src/settings/settings.types.ts b/src/settings/settings.types.ts index 33000de..49a11d3 100644 --- a/src/settings/settings.types.ts +++ b/src/settings/settings.types.ts @@ -33,6 +33,8 @@ export interface DiceRollerSettings { textColor: string; textFont: string; showLeafOnStartup: boolean; + narrativeSymbolSet: string; + displayAsSymbols: boolean; customFormulas: string[]; displayAsEmbed: boolean; diff --git a/src/styles.css b/src/styles.css index 8f2ad7f..bf6291c 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,3 +1,4 @@ @import "./assets/main.css"; @import "./view/view.css"; @import "./settings/settings.css"; +@import "./renderer/font.css"; diff --git a/src/types/obsidian-ext.d.ts b/src/types/obsidian-ext.d.ts index 827464c..c72973d 100644 --- a/src/types/obsidian-ext.d.ts +++ b/src/types/obsidian-ext.d.ts @@ -1,10 +1,11 @@ import type { StackRoller } from "src/rollers"; +import type { RenderableRoller } from "src/rollers/roller"; import type { DiceRollerSettings } from "src/settings/settings.types"; //expose dataview plugin for tags declare module "obsidian" { interface Workspace { - trigger(name: "dice-roller:new-result", data: StackRoller): void; + trigger(name: "dice-roller:new-result", data: RenderableRoller): void; trigger(name: "dice-roller:rendered-result", data: number): void; trigger(name: "dice-roller:loaded"): void; trigger(name: "dice-roller:unloaded"): void; diff --git a/src/view/view.ts b/src/view/view.ts index 394b560..b1885a1 100644 --- a/src/view/view.ts +++ b/src/view/view.ts @@ -15,6 +15,7 @@ import { type DiceIcon, IconManager } from "./view.icons"; import { Icons } from "src/utils/icons"; import { nanoid } from "nanoid"; import DiceTray from "./ui/DiceTray.svelte"; +import type { RenderableRoller } from "src/rollers/roller"; /* import { Details } from "@javalent/utilities"; */ @@ -60,15 +61,15 @@ export default class DiceView extends ItemView { this.registerEvent( this.plugin.app.workspace.on( "dice-roller:new-result", - async (roller: StackRoller) => { + async (roller: RenderableRoller) => { if ( this.plugin.data.addToView || roller.getSource() == VIEW_TYPE ) { await this.addResult({ - result: roller.result, + result: roller.getResultText(), original: roller.original, - resultText: roller.resultText, + resultText: roller.getTooltip(), timestamp: new Date().valueOf(), id: nanoid(12) }); @@ -265,7 +266,7 @@ export default class DiceView extends ItemView { roller.iconEl.detach(); roller.containerEl.onclick = null; roller.buildDiceTree(); - if (!roller.dice.length) { + if (!roller.children.length) { throw new Error("No dice."); } await roller.roll(this.plugin.data.renderer).catch((e) => { @@ -337,7 +338,7 @@ export default class DiceView extends ItemView { .setIcon(Icons.COPY) .setTooltip("Copy Result") .onClick(async () => { - await navigator.clipboard.writeText(`${result.result}`); + await navigator.clipboard.writeText(`${result.resultText}`); }); copy.extraSettingsEl.addClass("dice-content-copy"); if (Platform.isMobile) { diff --git a/test/parser/lexer.test.ts b/test/parser/lexer.test.ts index d8859c7..4d92bbd 100644 --- a/test/parser/lexer.test.ts +++ b/test/parser/lexer.test.ts @@ -1,6 +1,7 @@ import { vi, test, expect } from "vitest"; import { Lexer } from "../../src/lexer/lexer"; import { toLexicalToken } from "../util"; +import { Ok } from "@sniptt/monads"; /** * possible formats: @@ -50,7 +51,7 @@ Lexer.setDefaultFace(100); Lexer.setDefaultRoll(1); test('Lexer should parse "d"', () => { - let actual = Lexer.parse("d").map(toLexicalToken); + let actual = Lexer.parse("d").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -62,7 +63,7 @@ test('Lexer should parse "d"', () => { }); test('Lexer should parse "5d17"', () => { - let actual = Lexer.parse("5d17").map(toLexicalToken); + let actual = Lexer.parse("5d17").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -74,7 +75,7 @@ test('Lexer should parse "5d17"', () => { }); test('Lexer should parse "5d%"', () => { - let actual = Lexer.parse("5d%").map(toLexicalToken); + let actual = Lexer.parse("5d%").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -86,7 +87,7 @@ test('Lexer should parse "5d%"', () => { }); test('Lexer should parse "2d66%"', () => { - let actual = Lexer.parse("2d66%").map(toLexicalToken); + let actual = Lexer.parse("2d66%").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -98,7 +99,7 @@ test('Lexer should parse "2d66%"', () => { }); test('Lexer should parse "1d6dh', () => { - let actual = Lexer.parse("1d6dh").map(toLexicalToken); + let actual = Lexer.parse("1d6dh").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -116,7 +117,7 @@ test('Lexer should parse "1d6dh', () => { }); test('Lexer should parse "1d6dh3', () => { - let actual = Lexer.parse("1d6dh3").map(toLexicalToken); + let actual = Lexer.parse("1d6dh3").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -134,7 +135,7 @@ test('Lexer should parse "1d6dh3', () => { }); test('Lexer should parse "1d6dl', () => { - let actual = Lexer.parse("1d6dl").map(toLexicalToken); + let actual = Lexer.parse("1d6dl").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -152,7 +153,7 @@ test('Lexer should parse "1d6dl', () => { }); test('Lexer should parse "1d6dl3', () => { - let actual = Lexer.parse("1d6dl3").map(toLexicalToken); + let actual = Lexer.parse("1d6dl3").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -170,7 +171,7 @@ test('Lexer should parse "1d6dl3', () => { }); test('Lexer should parse "1d6kl', () => { - let actual = Lexer.parse("1d6kl").map(toLexicalToken); + let actual = Lexer.parse("1d6kl").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -188,7 +189,7 @@ test('Lexer should parse "1d6kl', () => { }); test('Lexer should parse "1d6kl3', () => { - let actual = Lexer.parse("1d6kl3").map(toLexicalToken); + let actual = Lexer.parse("1d6kl3").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -206,7 +207,7 @@ test('Lexer should parse "1d6kl3', () => { }); test('Lexer should parse "1d6k', () => { - let actual = Lexer.parse("1d6k").map(toLexicalToken); + let actual = Lexer.parse("1d6k").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -224,7 +225,7 @@ test('Lexer should parse "1d6k', () => { }); test('Lexer should parse "1d6k3', () => { - let actual = Lexer.parse("1d6k3").map(toLexicalToken); + let actual = Lexer.parse("1d6k3").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -242,7 +243,7 @@ test('Lexer should parse "1d6k3', () => { }); test('Lexer should parse "1d6kh', () => { - let actual = Lexer.parse("1d6kh").map(toLexicalToken); + let actual = Lexer.parse("1d6kh").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -260,7 +261,7 @@ test('Lexer should parse "1d6kh', () => { }); test('Lexer should parse "1d6kh3', () => { - let actual = Lexer.parse("1d6kh3").map(toLexicalToken); + let actual = Lexer.parse("1d6kh3").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -278,7 +279,7 @@ test('Lexer should parse "1d6kh3', () => { }); test('Lexer should parse "5d6!!', () => { - let actual = Lexer.parse("5d6!!").map(toLexicalToken); + let actual = Lexer.parse("5d6!!").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -296,7 +297,7 @@ test('Lexer should parse "5d6!!', () => { }); test('Lexer should parse "5d6!!3', () => { - let actual = Lexer.parse("5d6!!3").map(toLexicalToken); + let actual = Lexer.parse("5d6!!3").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -314,7 +315,7 @@ test('Lexer should parse "5d6!!3', () => { }); test('Lexer should parse "5d6!!i', () => { - let actual = Lexer.parse("5d6!!i").map(toLexicalToken); + let actual = Lexer.parse("5d6!!i").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -332,7 +333,7 @@ test('Lexer should parse "5d6!!i', () => { }); test('Lexer should parse "5d6!', () => { - let actual = Lexer.parse("5d6!").map(toLexicalToken); + let actual = Lexer.parse("5d6!").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -350,7 +351,7 @@ test('Lexer should parse "5d6!', () => { }); test('Lexer should parse "5d6!3', () => { - let actual = Lexer.parse("5d6!3").map(toLexicalToken); + let actual = Lexer.parse("5d6!3").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -368,7 +369,7 @@ test('Lexer should parse "5d6!3', () => { }); test('Lexer should parse "5d6!i', () => { - let actual = Lexer.parse("5d6!i").map(toLexicalToken); + let actual = Lexer.parse("5d6!i").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -386,7 +387,7 @@ test('Lexer should parse "5d6!i', () => { }); test('Lexer should parse "5d6u', () => { - let actual = Lexer.parse("5d6u").map(toLexicalToken); + let actual = Lexer.parse("5d6u").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -404,7 +405,7 @@ test('Lexer should parse "5d6u', () => { }); test('Lexer should parse "5d[3,7]', () => { - let actual = Lexer.parse("5d[3,7]").map(toLexicalToken); + let actual = Lexer.parse("5d[3,7]").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -416,7 +417,7 @@ test('Lexer should parse "5d[3,7]', () => { }); test('Lexer should parse "5d[7,3]', () => { - let actual = Lexer.parse("5d[7,3]").map(toLexicalToken); + let actual = Lexer.parse("5d[7,3]").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -428,7 +429,7 @@ test('Lexer should parse "5d[7,3]', () => { }); test('Lexer should parse "5d7r', () => { - let actual = Lexer.parse("5d7r").map(toLexicalToken); + let actual = Lexer.parse("5d7r").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -446,7 +447,7 @@ test('Lexer should parse "5d7r', () => { }); test('Lexer should parse "5d7r4', () => { - let actual = Lexer.parse("5d7r4").map(toLexicalToken); + let actual = Lexer.parse("5d7r4").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -464,7 +465,7 @@ test('Lexer should parse "5d7r4', () => { }); test('Lexer should parse "5d7ri', () => { - let actual = Lexer.parse("5d7ri").map(toLexicalToken); + let actual = Lexer.parse("5d7ri").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -482,7 +483,7 @@ test('Lexer should parse "5d7ri', () => { }); test('Lexer should parse "5d7s', () => { - let actual = Lexer.parse("5d7s").map(toLexicalToken); + let actual = Lexer.parse("5d7s").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -500,7 +501,7 @@ test('Lexer should parse "5d7s', () => { }); test('Lexer should parse "5d7sa', () => { - let actual = Lexer.parse("5d7sa").map(toLexicalToken); + let actual = Lexer.parse("5d7sa").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -518,7 +519,7 @@ test('Lexer should parse "5d7sa', () => { }); test('Lexer should parse "5d7sd', () => { - let actual = Lexer.parse("5d7sd").map(toLexicalToken); + let actual = Lexer.parse("5d7sd").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -536,12 +537,12 @@ test('Lexer should parse "5d7sd', () => { }); test('Lexer should parse "6dF', () => { - let actual = Lexer.parse("6dF").map(toLexicalToken); + let actual = Lexer.parse("6dF").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, parenedDice: undefined, - type: "dice", + type: "fudge", value: "6dF" } ]); @@ -550,7 +551,7 @@ test('Lexer should parse "6dF', () => { // SPECIAL DICE =============================================================== test('Lexer should parse "1dS', () => { - let actual = Lexer.parse("1dS").map(toLexicalToken); + let actual = Lexer.parse("1dS").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -562,13 +563,13 @@ test('Lexer should parse "1dS', () => { }); test('Lexer should parse "5dS', () => { - expect(() => Lexer.parse("5dS")).toThrow(); + expect(Lexer.parse("5dS").unwrapErr()).toBe("Could not parse"); }); // BLOCKS ===================================================================== test('Lexer should parse "[[Note]]', () => { - let actual = Lexer.parse("[[Note]]").map(toLexicalToken); + let actual = Lexer.parse("[[Note]]").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -580,7 +581,7 @@ test('Lexer should parse "[[Note]]', () => { }); test('Lexer should parse "4d[[Note]]', () => { - let actual = Lexer.parse("4d[[Note]]").map(toLexicalToken); + let actual = Lexer.parse("4d[[Note]]").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -592,7 +593,7 @@ test('Lexer should parse "4d[[Note]]', () => { }); test('Lexer should parse "[[Note]]|line', () => { - let actual = Lexer.parse("[[Note]]|line").map(toLexicalToken); + let actual = Lexer.parse("[[Note]]|line").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -604,7 +605,7 @@ test('Lexer should parse "[[Note]]|line', () => { }); test('Lexer should parse "[[Note]]|heading-2', () => { - let actual = Lexer.parse("[[Note]]|heading-2").map(toLexicalToken); + let actual = Lexer.parse("[[Note]]|heading-2").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -616,7 +617,7 @@ test('Lexer should parse "[[Note]]|heading-2', () => { }); test('Lexer should parse "[[Note^block-id]]', () => { - let actual = Lexer.parse("[[Note^block-id]]").map(toLexicalToken); + let actual = Lexer.parse("[[Note^block-id]]").unwrap().map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -628,7 +629,9 @@ test('Lexer should parse "[[Note^block-id]]', () => { }); test('Lexer should parse "1d4+1[[Note^block-id]]', () => { - let actual = Lexer.parse("1d4+1[[Note^block-id]]").map(toLexicalToken); + let actual = Lexer.parse("1d4+1[[Note^block-id]]") + .unwrap() + .map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -640,7 +643,9 @@ test('Lexer should parse "1d4+1[[Note^block-id]]', () => { }); test('Lexer should parse "[[Note^block-id]]|Header 2', () => { - let actual = Lexer.parse("[[Note^block-id]]|Header 2").map(toLexicalToken); + let actual = Lexer.parse("[[Note^block-id]]|Header 2") + .unwrap() + .map(toLexicalToken); expect(actual).toEqual([ { conditions: undefined, @@ -654,25 +659,25 @@ test('Lexer should parse "[[Note^block-id]]|Header 2', () => { // FORMULAS =================================================================== test('Lexer should parse "1d20 + -2" like "1d20 - 2"', () => { - let a = Lexer.parse("1d20 + -2").map(toLexicalToken); - let b = Lexer.parse("1d20 - 2").map(toLexicalToken); + let a = Lexer.parse("1d20 + -2").unwrap().map(toLexicalToken); + let b = Lexer.parse("1d20 - 2").unwrap().map(toLexicalToken); expect(a).toEqual(b); }); test('Lexer should parse "1d20 - -2" like "1d20 + 2"', () => { - let a = Lexer.parse("1d20 - -2").map(toLexicalToken); - let b = Lexer.parse("1d20 + 2").map(toLexicalToken); + let a = Lexer.parse("1d20 - -2").unwrap().map(toLexicalToken); + let b = Lexer.parse("1d20 + 2").unwrap().map(toLexicalToken); expect(a).toEqual(b); }); test('Lexer should parse "1d20 ----2" like "1d20 + 2"', () => { - let a = Lexer.parse("1d20 ----2").map(toLexicalToken); - let b = Lexer.parse("1d20 + 2").map(toLexicalToken); + let a = Lexer.parse("1d20 ----2").unwrap().map(toLexicalToken); + let b = Lexer.parse("1d20 + 2").unwrap().map(toLexicalToken); expect(a).toEqual(b); }); test('Lexer should parse "1d20 -++---+-+--++17" like "1d20 - 17"', () => { - let a = Lexer.parse("1d20 -++---+-+--++17").map(toLexicalToken); - let b = Lexer.parse("1d20 - 17").map(toLexicalToken); + let a = Lexer.parse("1d20 -++---+-+--++17").unwrap().map(toLexicalToken); + let b = Lexer.parse("1d20 - 17").unwrap().map(toLexicalToken); expect(a).toEqual(b); }); diff --git a/test/util.ts b/test/util.ts index 3989a73..f1a139c 100644 --- a/test/util.ts +++ b/test/util.ts @@ -1,3 +1,5 @@ +import { LexicalToken } from "../src/lexer/lexer"; + export function toLexicalToken(e: LexicalToken): LexicalToken { let { conditions, parenedDice, type, value } = e; return { conditions, parenedDice, type, value };