Files
RpgRoller/TASKS.md

15 KiB

Rolemaster Support Plan

Goal

Extend the app with a new rolemaster ruleset that supports three Rolemaster-oriented skill roll types:

  1. Initiative: 2d10 + x
  2. Standard percentile: non-open-ended 1d100 + x
  3. Open-ended percentile: 1d100 + x with Rolemaster low-end and high-end behavior

The initial scope is only roll definition, validation, execution, logging, and UI support for these three roll types. This does not include Rolemaster attack tables, critical tables, resistance rolls, maneuver tables, or character sheet math beyond the explicit roll modifier entered for a skill.

Scope Assumptions

  • A Rolemaster campaign chooses the new rolemaster ruleset at campaign creation time.
  • Rolemaster skills are ruleset-specific and should not reuse D6-only options such as WildDice and AllowFumble.
  • Open-ended percentile behavior is defined as:
    • Roll the first d100.
    • If the first roll is 96 or higher, roll another high-ended d100 recursively and add it.
    • If the first roll is less than or equal to the skill's configured fumble range, roll another high-ended d100 recursively and subtract it.
    • Recursive follow-up rolls are high-ended only.
  • Standard percentile rolls do not open-end and do not use a fumble range.
  • Initiative rolls are simple 2d10 + x rolls with no open-ended behavior.
  • The modifier x should be an integer and may be negative if we want Rolemaster penalties to be expressible. Current generic dice validation only allows non-negative modifiers, so this must be revisited.
  • Existing D6 and D&D 5e behavior must remain unchanged.

Open Questions To Resolve Before Implementation

  • Should the Rolemaster modifier allow negative values everywhere, or only for Rolemaster skills?
  • What is the allowed fumble range for open-ended percentile skills:
    • 0-5
    • 0-10
    • unrestricted 0-100
  • Should a low-ended first roll that is also 96+ ever be possible through configuration overlap:
    • recommended answer: no, validation should prevent impossible overlap by constraining fumble range below 96
  • How should roll breakdown text read for low-ended subtraction:
    • recommended format: 12 - (97 + 44) + 15 = -114
  • Should initiative be represented as a dedicated Rolemaster roll type, or as a generic fixed expression with Rolemaster labeling:
    • recommended answer: dedicated type, because UI copy and validation differ
  • Do skill groups need to support Rolemaster defaults for all of these fields:
    • recommended answer: yes, to stay consistent with current prototype behavior

Design Direction

1. Add a new ruleset

Introduce RulesetKind.Rolemaster and expose it through DiceRules and campaign ruleset APIs.

Tasks:

  • Add Rolemaster to RulesetKind.
  • Update DiceRules.TryParseRulesetId.
  • Update DiceRules.ToRulesetId.
  • Add rolemaster to DiceRules.SupportedRulesets.
  • Update tests that enumerate supported rulesets and campaign creation behavior.

Affected areas:

  • RpgRoller/Domain/GameModels.cs
  • RpgRoller/Services/DiceRules.cs
  • RpgRoller/Contracts/ApiContracts.cs
  • related service and API tests

2. Refactor skill definition from "expression + D6 options" to "ruleset-aware roll definition"

The current model is D6-shaped:

  • DiceRollDefinition
  • WildDice
  • AllowFumble

That shape is not a clean fit for Rolemaster. The Rolemaster extension should move toward a ruleset-aware skill definition that can express roll behavior directly instead of encoding everything into a free-form expression string.

Recommended model additions:

  • SkillRollKind enum
    • StandardExpression
    • RolemasterInitiative
    • RolemasterPercentile
    • RolemasterOpenEndedPercentile
  • RollModifier integer
  • RolemasterFumbleRange nullable integer
  • keep DiceRollDefinition only if needed for legacy display or migration compatibility

Recommended direction:

  • Keep existing fields working for D6 and D&D 5e.
  • Add new fields for Rolemaster rather than overloading WildDice / AllowFumble.
  • Centralize skill validation into a single ruleset-aware validator that returns a canonical internal representation.

Tasks:

  • Define a ruleset-aware skill definition model in domain/contracts.
  • Decide whether to preserve DiceRollDefinition as persisted state or derive display text from structured fields.
  • Add mapping helpers so UI and API get a stable, ruleset-aware DTO.
  • Ensure skill groups can store the same Rolemaster defaults as skills.

Affected areas:

  • RpgRoller/Domain/GameModels.cs
  • RpgRoller/Contracts/ApiContracts.cs
  • RpgRoller/Services/GameService.cs
  • RpgRoller/Components/Pages/Home.Models.cs

3. Add Rolemaster-specific validation

Current validation is split between generic expression parsing and D6 option validation. Rolemaster needs its own validation layer.

Validation rules to add:

  • RolemasterInitiative
    • fixed base roll 2d10
    • integer modifier required
    • no fumble range
  • RolemasterPercentile
    • fixed base roll 1d100
    • integer modifier required
    • no fumble range
  • RolemasterOpenEndedPercentile
    • fixed base roll 1d100
    • integer modifier required
    • fumble range required and validated
    • fumble range must be below 96
  • reject D6-only options for Rolemaster skills
  • reject Rolemaster-only fields for D6 / D&D 5e skills

Tasks:

  • Replace or extend ValidateSkillDefinition.
  • Add a Rolemaster-focused parser/validator path.
  • Decide whether DiceRules.ParseExpression should remain only for free-form rulesets or become a lower-level helper.
  • Add canonical display formatting for Rolemaster skills, for example:
    • Initiative: 2d10+15
    • Percentile: 1d100+48
    • Open-ended percentile: OE 1d100+48, fumble <= 5

Affected areas:

  • RpgRoller/Services/DiceRules.cs
  • RpgRoller/Services/GameService.cs
  • RpgRoller.Tests/Services/DiceRulesTests.cs
  • RpgRoller.Tests/Services/ServiceSkillGroupAndOwnershipTests.cs
  • RpgRoller.Tests/Services/ServiceSkillRollTests.cs

4. Implement Rolemaster roll execution

Add a new Rolemaster execution path alongside the existing D6 and standard roll logic.

Recommended internal shape:

  • ComputeRoll dispatches by ruleset and roll kind.
  • Add dedicated methods:
    • ComputeRolemasterInitiativeRoll
    • ComputeRolemasterPercentileRoll
    • ComputeRolemasterOpenEndedRoll
    • helper RollRolemasterHighOpenEndedChain

Open-ended algorithm:

  • Roll first d100.
  • Start total with first roll.
  • If first roll >= 96:
    • roll one or more high-ended follow-up d100s
    • add all follow-up rolls
  • Else if first roll <= fumble range:
    • roll one or more high-ended follow-up d100s
    • subtract the follow-up total
  • Apply modifier after base/follow-up math.
  • Produce a full breakdown string.
  • Persist enough die detail to reconstruct the event in log detail and UI.

Tasks:

  • Extend ComputeRoll dispatch.
  • Add Rolemaster-specific roll methods.
  • Decide whether the current RollDieResult DTO is sufficient.
    • likely not sufficient, because it only has Crit, Fumble, Wild, Removed, Added
  • Recommended DTO extension:
    • Sequence or Order
    • Phase / Kind such as initial, open-ended-high, open-ended-low-subtract
    • SignedContribution or equivalent metadata for subtraction
  • Update log summary generation so Rolemaster rolls remain understandable in compact log entries.

Affected areas:

  • RpgRoller/Services/GameService.cs
  • RpgRoller/Contracts/ApiContracts.cs
  • RpgRoller/Contracts/RpgRollerJsonSerializerContext.cs
  • RpgRoller/Components/Pages/HomeControls/RollDiceStrip.razor.cs

5. Update persistence and migrations

Rolemaster support needs persisted structured fields for skills and skill groups, and possibly richer serialized roll-die details.

Tasks:

  • Update Skill and SkillGroup EF models.
  • Update RpgRollerDbContext configuration.
  • Add EF Core migration for new Rolemaster fields.
  • Preserve existing data for D6 / D&D 5e campaigns.
  • Decide migration defaults for legacy rows.
  • Update SqliteSchemaUpgrader coverage expectations if schema assumptions change.
  • Verify serialized roll log payloads remain readable for old entries after DTO extension.

Data migration recommendations:

  • Legacy D6 / D&D 5e skills should retain current behavior with deterministic defaults.
  • New Rolemaster fields should be nullable or have safe defaults during migration.
  • Avoid destructive migration patterns; favor additive schema evolution.

Affected areas:

  • RpgRoller/Data/RpgRollerDbContext.cs
  • RpgRoller/Migrations/*
  • RpgRoller/Hosting/SqliteSchemaUpgrader.cs
  • RpgRoller.Tests/HostingCoverageTests.cs

6. Expose Rolemaster in APIs and frontend models

The UI currently assumes only IsD6 versus "not D6". That binary split will not scale to Rolemaster.

Tasks:

  • Replace IsD6 branching with a ruleset-aware UI model.
  • Extend skill and skill-group request/response contracts with Rolemaster fields.
  • Ensure campaign creation UI includes Rolemaster in the ruleset picker.
  • Update workspace models and display helpers so Rolemaster skill summaries render correctly.

Recommended frontend model changes:

  • Introduce ruleset-aware form state rather than raw D6 toggles.
  • Replace "Expression" with a Rolemaster-specific editor when the selected campaign ruleset is rolemaster.
  • Add a "Roll type" selector with options:
    • Initiative
    • Percentile
    • Open-ended percentile
  • Show modifier input for all Rolemaster roll types.
  • Show fumble range input only for open-ended percentile.

Affected areas:

  • RpgRoller/Contracts/ApiContracts.cs
  • RpgRoller/Components/Pages/Home.Models.cs
  • RpgRoller/Components/Pages/HomeControls/SkillFormModal.razor
  • RpgRoller/Components/Pages/HomeControls/SkillFormModal.razor.cs
  • RpgRoller/Components/Pages/HomeControls/CharacterPanel.razor
  • RpgRoller/Components/Pages/HomeControls/CharacterPanel.razor.cs
  • RpgRoller/Components/Pages/Workspace.razor.cs

7. Update roll visualization and log readability

Rolemaster open-ended rolls will be hard to interpret if they reuse the current D6-oriented chip styling without extra context.

Tasks:

  • Extend RollDiceStrip to distinguish:
    • initial percentile roll
    • added high-open-ended rolls
    • subtracted low-end follow-up rolls
  • Add tooltip or title text that explains each die's role in the chain.
  • Update compact log summaries so they surface open-ended behavior.
  • Ensure private/public visibility behavior remains unchanged.

Recommended display goals:

  • A normal percentile roll should read as simple and uncluttered.
  • A high-open-ended roll should clearly show chained additions.
  • A low-ended roll should clearly show subtraction, not just a list of numbers.

Affected areas:

  • RpgRoller/Components/Pages/HomeControls/RollDiceStrip.razor
  • RpgRoller/Components/Pages/HomeControls/RollDiceStrip.razor.cs
  • RpgRoller/Components/Pages/Workspace.razor.cs
  • RpgRoller/Services/GameService.cs

8. Expand tests to preserve behavior and coverage

This repo expects very high coverage and already has strong service/API regression tests. Rolemaster support should land with exhaustive tests, not just happy paths.

Service-level tests to add:

  • parse and ruleset mapping for rolemaster
  • skill creation validation for each Rolemaster roll type
  • rejection of invalid fumble ranges
  • rejection of D6-only options on Rolemaster skills
  • initiative roll math
  • standard percentile roll math
  • open-ended high roll with one extra roll
  • open-ended high roll with recursive extra rolls
  • open-ended low roll with subtraction
  • open-ended low roll where the subtract chain itself recursively high-opens
  • breakdown formatting and die metadata correctness

API tests to add:

  • create Rolemaster campaign
  • create/update skill groups with Rolemaster defaults
  • create/update Rolemaster skills
  • roll each Rolemaster skill type via API
  • verify log page and detail endpoints for Rolemaster rolls

Frontend/regression checks:

  • Rolemaster ruleset appears in campaign creation UI
  • Rolemaster skill form shows correct conditional fields
  • roll results render correctly for open-ended cases

Coverage-sensitive files likely needing tests:

  • RpgRoller.Tests/Services/DiceRulesTests.cs
  • RpgRoller.Tests/Services/ServiceSkillRollTests.cs
  • new dedicated Rolemaster service tests
  • RpgRoller.Tests/Api/CampaignApiTests.cs
  • RpgRoller.Tests/HostingCoverageTests.cs

9. Documentation updates

The repository instructions require related docs to be updated whenever behavior changes.

Tasks:

  • Update README.md to list Rolemaster as a supported ruleset.
  • Document the three supported Rolemaster roll types.
  • Document the open-ended percentile algorithm, including:
    • 96+ recursive addition
    • <= fumble range recursive subtraction
  • Document any limits on modifier and fumble range values.
  • If API contracts change materially, update openapi/RpgRoller.json.

10. Delivery plan in small iterations

Recommended implementation order:

Iteration 1: ruleset plumbing

  • Add Rolemaster ruleset enum/id/display support.
  • Add campaign creation support.
  • Add parser/validator scaffolding for Rolemaster roll types.
  • Add baseline tests for ruleset mapping and validation.

Iteration 2: structured skill model

  • Add persisted Rolemaster fields for skills and skill groups.
  • Add migration and schema tests.
  • Update API contracts and service mapping.
  • Keep existing D6 / D&D behavior green.

Iteration 3: roll engine

  • Implement initiative and standard percentile execution.
  • Implement open-ended percentile execution with recursive high-end chaining and low-end subtraction.
  • Extend die-result metadata and breakdown formatting.
  • Add focused service tests for roll math.

Iteration 4: frontend editing

  • Update campaign ruleset selection UI.
  • Replace D6-only form branching with ruleset-aware skill editing.
  • Support Rolemaster skill groups and skills in create/edit flows.
  • Verify with ephemeral Playwright.

Iteration 5: log/detail polish and docs

  • Improve roll chip rendering and compact summaries for Rolemaster.
  • Update README/OpenAPI/docs.
  • Run full local CI and coverage checks.

Risks

  • The current skill contract is tightly coupled to D6-era fields, so a shallow patch will create long-term complexity.
  • The current RollDieResult type is too generic for clear Rolemaster open-ended auditing.
  • Allowing negative Rolemaster modifiers may require carefully scoping validation changes so D6/D&D rules do not loosen unintentionally.
  • Migration design needs care to avoid breaking legacy SQLite databases.
  • UI complexity will grow if ruleset-specific fields are bolted onto the current IsD6 logic instead of replacing it with a ruleset-aware form model.
  • A campaign can be created with ruleset id rolemaster.
  • Rolemaster skill groups and skills can be created and edited through API and UI.
  • Supported Rolemaster roll types are limited to:
    • initiative 2d10 + x
    • standard percentile 1d100 + x
    • open-ended percentile 1d100 + x with fumble range
  • Open-ended percentile rolls correctly:
    • add recursively on first-roll 96+
    • subtract a recursive high-end chain on first-roll <= fumble range
  • Roll logs and detail views clearly show how a Rolemaster result was produced.
  • Existing D6 and D&D 5e flows remain unchanged.
  • Local CI, coverage checks, and frontend verification all pass.