Implement the 026-roll-initiative feature that adds d20 roll buttons for bestiary combatants' initiative using a click-to-edit pattern (d20 icon when empty, plain text when set), plus a Roll All button in the top bar that batch-rolls for all unrolled bestiary combatants, with randomness confined to the adapter layer and the domain receiving pre-resolved dice values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-10 16:29:09 +01:00
parent 5b0bac880d
commit d5f7b6ee36
20 changed files with 926 additions and 27 deletions

View File

@@ -0,0 +1,84 @@
# Feature Specification: Roll Initiative
**Feature Branch**: `026-roll-initiative`
**Created**: 2026-03-10
**Status**: Draft
**Input**: User description: "Roll initiative feature — d20 button per combatant and a roll-all button in the top bar"
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Roll Initiative for a Single Combatant (Priority: P1)
As a DM, I want to click a d20 icon button next to a combatant's initiative field to randomly roll initiative (1d20 + initiative modifier from its stat block) so that the result is immediately placed into the initiative field and the tracker re-sorts.
**Why this priority**: This is the core interaction — rolling initiative for individual creatures is the most frequent use case and the atomic building block for the roll-all feature.
**Independent Test**: Can be fully tested by adding a bestiary creature, clicking the d20 button, and verifying a valid initiative value appears and the list re-sorts.
**Acceptance Scenarios**:
1. **Given** a combatant linked to a bestiary creature (e.g., Aboleth with initiative modifier +7) with no initiative value, **When** I click the d20 icon in the initiative slot, **Then** a random value between 8 and 27 (1-20 + 7) is placed into the initiative field, shown as plain text, and the encounter list re-sorts by initiative descending.
2. **Given** a combatant that is NOT linked to a bestiary creature (manually added), **When** I look at its combatant row, **Then** the initiative slot shows "--" (clickable to type a value manually) instead of a d20 button.
3. **Given** a combatant with a bestiary creature whose initiative modifier is negative (e.g., 2), **When** I click the d20 button, **Then** the result can range from 1 to 18 (1-20 + (2)).
4. **Given** a combatant that already has an initiative value, **When** I look at its row, **Then** the initiative slot shows the value as plain text (no d20 button). Clicking the value opens an inline editor to change or clear it.
---
### User Story 2 - Roll Initiative for All Bestiary Combatants (Priority: P2)
As a DM, I want a button in the top bar that rolls initiative for all combatants that have a linked stat block, so I can quickly set up initiative order at the start of combat without clicking each one individually.
**Why this priority**: Speeds up the common workflow of rolling initiative for all monsters at once. Depends on the single-roll mechanism from P1.
**Independent Test**: Can be tested by adding multiple bestiary creatures and one manual combatant, clicking the roll-all button, and verifying all bestiary combatants get initiative values while manual combatants remain unchanged.
**Acceptance Scenarios**:
1. **Given** an encounter with 3 bestiary combatants (no initiative yet) and 1 manually-added combatant, **When** I click the "Roll All Initiative" button in the top bar, **Then** all 3 bestiary combatants receive randomly rolled initiative values (1d20 + their respective modifiers) and the manual combatant's initiative field remains unchanged.
2. **Given** an encounter with bestiary combatants that already have initiative values, **When** I click the roll-all button, **Then** those combatants are skipped (their existing values are preserved). Only bestiary combatants without initiative are rolled.
3. **Given** an encounter with no bestiary combatants (all manually added), **When** I look at the top bar, **Then** the roll-all button is still visible but rolling produces no changes (no combatants are eligible).
4. **Given** an empty encounter, **When** I look at the top bar, **Then** the roll-all button is present but has no effect when clicked.
---
### Edge Cases
- What happens when a combatant's initiative modifier produces a result of 0 or negative? The value is stored as-is — negative initiative is valid in D&D.
- What happens if multiple combatants roll the same initiative? Existing auto-sort behavior handles ties by preserving relative insertion order among tied values.
- What happens if the active combatant's position changes after rolling? Existing turn-tracking-by-ID mechanism preserves the active combatant's turn through re-sorts.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: System MUST display a d20 icon button in the initiative slot for every combatant that has a linked bestiary creature and does not yet have an initiative value.
- **FR-002**: System MUST NOT display the d20 icon button for combatants without a linked bestiary creature (they see a "--" placeholder that is clickable to type a value).
- **FR-003**: When the d20 button is clicked for a combatant, the system MUST generate a random integer between 1 and 20 (inclusive, uniform distribution), add the creature's initiative modifier, and set the result as the combatant's initiative value.
- **FR-004**: The initiative modifier MUST be calculated from the creature's bestiary data using the existing initiative calculation logic (DEX modifier + initiative proficiency multiplier × proficiency bonus).
- **FR-005**: The d20 icon button MUST use the d20.svg asset from the project root.
- **FR-006**: System MUST provide a "Roll All Initiative" button in the top bar area of the encounter tracker.
- **FR-007**: When the roll-all button is clicked, the system MUST roll initiative (1d20 + modifier) for every combatant that has a linked bestiary creature and does not already have an initiative value. Combatants without a linked creature or with an existing initiative value MUST NOT be modified.
- **FR-008**: After any initiative roll (single or batch), the encounter list MUST re-sort by initiative in descending order per existing behavior.
- **FR-009**: Once a combatant has an initiative value, the d20 button is replaced by the value displayed as plain text. Clicking the value opens an inline editor to manually change or clear it. The initiative column uses a click-to-edit pattern consistent with other fields (name, AC, HP).
### Key Entities
- **Initiative Modifier**: A signed integer derived from a creature's bestiary data (DEX modifier + proficiency contribution). Calculated on demand from creature stats, not stored separately.
- **Initiative Roll Result**: The final integer value (1d20 + modifier) stored as the combatant's initiative value.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: A single combatant's initiative can be rolled with one click (the d20 button).
- **SC-002**: All eligible combatants' initiative can be rolled with one click (the roll-all button).
- **SC-003**: After rolling, the encounter list is correctly sorted by initiative descending.
- **SC-004**: Manual combatants (no stat block) are never affected by roll actions.
- **SC-005**: Rolled values fall within the valid range (1 + modifier to 20 + modifier) for each combatant's initiative modifier.
## Assumptions
- The passive initiative value shown in parentheses in the stat block is not used for rolling — only the active modifier (e.g., +7) is used.
- The d20.svg file in the project root is suitable for use as an icon at small sizes (inline with the initiative input field).
- The "top bar" refers to the turn navigation area pinned at the top of the encounter tracker (feature 022).
- Random number generation uses standard browser randomness — cryptographic randomness is not required for dice rolls.