diff --git a/app/.gitkeep b/app/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/assets/javascripts/discourse/components/composer-valid-roll.gjs b/assets/javascripts/discourse/components/composer-valid-roll.gjs index 3c0aaf1..4acc4f3 100644 --- a/assets/javascripts/discourse/components/composer-valid-roll.gjs +++ b/assets/javascripts/discourse/components/composer-valid-roll.gjs @@ -94,9 +94,13 @@ export default class ComposerValidRoll extends Component { .filter(Boolean); rolls.forEach((roll) => { try { + // TODO: Handle descriptions separately. parser won't be able to handle 2d20+4 // test + // see https://github.com/dice-roller/rpg-dice-roller/issues/287 rpgDiceRoller.Parser.parse(roll); } catch (err) { this.errors.push(err); + // eslint-disable-next-line no-console + console.warn("Rollmaster: Error parsing notation", roll, err); } }); }); diff --git a/lib/rollmaster/dice_engine.rb b/lib/rollmaster/dice_engine.rb index 882497e..dcb9d11 100644 --- a/lib/rollmaster/dice_engine.rb +++ b/lib/rollmaster/dice_engine.rb @@ -29,13 +29,49 @@ def self.roll(*diceRolls) result end + # Formats the notation of the dice rolls. + # Note: this does not actually reorder the dice rolls, it just formats them by removing + # whitespace and ensuring the notation is valid. + def self.format_notation(*diceRolls) + result = nil + protect do + context = v8 + result = context.call("formatNotation", *diceRolls) + end + if result.is_a?(Hash) && result["type"] == "error" + raise Rollmaster::DiceEngine::RollError.new(result["msg"]) + end + result + end + def self.attach_function(ctx) ctx.eval <<~JS function roll(...diceRolls) { const roller = new rpgDiceRoller.DiceRoller; try { roller.roll(...diceRolls); - return JSON.parse(JSON.stringify(roller.log)) + return roller.log.map((r) => { + const output = r.output; + const start = output.lastIndexOf(': '); + return output.substring(start + 2); + }); + } catch (e) { + return { + type: "error", + name: e.name, + msg: e.message, + }; + } + } + + function formatNotation(...diceRolls) { + const parse = rpgDiceRoller.Parser.parse; + const formatted = []; + try { + diceRolls.forEach((notation) => { + const parsed = parse(notation); + formatted.push(format(parsed)); + }); } catch (e) { return { type: "error", @@ -43,6 +79,31 @@ def self.attach_function(ctx) msg: e.message, }; } + return formatted; + } + + function format(expressions) { + removeDescription(expressions); + return expressions + .map((e) => { + if (typeof e === "string" || typeof e === "number") { + return e; + } + return e.notation; + }) + .join(""); + } + + function removeDescription(exp) { + exp.forEach((e) => { + if (typeof e === "object" && e.description) { + e.description = null; + if (e.expressions) { + e.expressions.forEach((subExp) => removeDescription(subExp)); + } + } + }); + return exp; } JS end diff --git a/spec/lib/rollmaster/dice_engine_spec.rb b/spec/lib/rollmaster/dice_engine_spec.rb index 2308e82..b3eb703 100644 --- a/spec/lib/rollmaster/dice_engine_spec.rb +++ b/spec/lib/rollmaster/dice_engine_spec.rb @@ -11,7 +11,7 @@ expect(result).not_to be_nil expect(result).to be_a(Array) expect(result.size).to eq(dice_rolls.size) - expect(result.all? { |r| r.is_a?(Hash) }).to be(true) + expect(result.all? { |r| r.is_a?(String) }).to be(true) end it "raises a RollError for invalid dice rolls" do @@ -33,6 +33,25 @@ end end + describe ".format_notation" do + it "formats the notation of the dice rolls" do + dice_rolls = ["{3d8 * 2, 20 / 2d10, 2d10 - d4} // testing"] + formatted_result = described_class.format_notation(*dice_rolls) + + expect(formatted_result).not_to be_nil + expect(formatted_result).to be_a(Array) + expect(formatted_result.first).to eq("{3d8*2, 20/2d10, 2d10-1d4}") # has stripped whitespace and comments + end + + it "raises a RollError for invalid notation" do + dice_rolls = ["invalid_notation"] + + expect { described_class.format_notation(*dice_rolls) }.to raise_error( + Rollmaster::DiceEngine::RollError, + ) + end + end + describe ".reset_context" do it "resets the V8 context" do initial_context = described_class.v8