Skip to content

Latest commit

 

History

History
126 lines (113 loc) · 5.19 KB

File metadata and controls

126 lines (113 loc) · 5.19 KB

Shadowdark Encounter Tracker — Implementation Plan

Overview

macOS SwiftUI app (macOS 14+, Swift 6, @Observable) for running Shadowdark RPG combat encounters. 3-pane UI with one-click dice rolling for fast combat.

Key Features

  • 3-pane UI: Initiative list (left), combatant detail (center), roll log (right)
  • Combat tracking: Initiative, rounds, HP, AC, conditions, allegiance
  • One-click rolling: Attack/spell rolls with advantage/disadvantage, auto-damage on hit
  • Save/load encounters to disk as .sdenc JSON files
  • Party persistence: Save party members separately, load them into any new encounter instantly
  • Monster/NPC templates: Pre-built stat blocks saved as reusable templates, drag into encounters fast

Project Structure

ShadowdarkTracker/
├── ShadowdarkTrackerApp.swift
├── Models/
│   ├── Allegiance.swift            # enum: ally, enemy, neutral
│   ├── Condition.swift             # enum: blinded, paralyzed, poisoned, etc.
│   ├── AbilityScores.swift         # STR-CHA + modifier table
│   ├── DiceExpression.swift        # Parsed "2d10+3" struct
│   ├── Attack.swift                # name, bonus, damage, quantity
│   ├── Spell.swift                 # name, dc, damage, range, description
│   ├── SpecialAbility.swift        # name, description
│   ├── Combatant.swift             # Unified PC/Monster/NPC model
│   ├── RollLogEntry.swift          # Single roll result record
│   ├── Encounter.swift             # Combatants + round state + roll log
│   ├── Party.swift                 # Named group of PCs for reuse
│   └── CombatantTemplate.swift     # Reusable monster/NPC stat block
├── Dice/
│   ├── DiceParser.swift            # Regex parser for dice notation
│   └── DiceRoller.swift            # Roll engine with adv/disadv
├── Services/
│   ├── FileService.swift           # Encounter save/load (.sdenc JSON)
│   ├── PartyStore.swift            # Party save/load (.sdparty JSON)
│   └── TemplateStore.swift         # Monster/NPC template library (.sdtemplate JSON)
├── State/
│   └── EncounterManager.swift      # @Observable source of truth
├── Views/
│   ├── ContentView.swift           # HSplitView 3-pane root
│   ├── Initiative/
│   │   ├── InitiativeListView.swift
│   │   └── InitiativeRowView.swift
│   ├── Detail/
│   │   ├── CombatantDetailView.swift   # Router: PC vs Monster/NPC
│   │   ├── PCDetailView.swift
│   │   ├── MonsterDetailView.swift
│   │   └── AttackActionView.swift      # Roll button row per attack
│   ├── RollLog/
│   │   ├── RollLogView.swift
│   │   └── RollLogEntryView.swift
│   ├── Toolbar/
│   │   └── EncounterToolbar.swift      # Round controls, save/load, add combatant
│   ├── Shared/
│   │   ├── HPBar.swift
│   │   ├── ConditionBadges.swift
│   │   └── DiceExpressionField.swift
│   └── Library/
│       ├── PartyPickerView.swift       # Load saved party into encounter
│       ├── TemplateLibraryView.swift   # Browse/search monster & NPC templates
│       └── TemplateEditorView.swift    # Create/edit a template
└── Resources/
    └── Assets.xcassets

Data Models

Combatant (single unified struct)

struct Combatant: Codable, Identifiable, Equatable {
    let id: UUID
    var name: String
    var kind: CombatantKind          // .pc, .monster, .npc
    var allegiance: Allegiance       // .ally, .enemy, .neutral

    var ac: Int
    var maxHP: Int
    var currentHP: Int
    var initiative: Int?
    var conditions: Set<Condition>
    var dexModifier: Int

    var abilityScores: AbilityScores?
    var attacks: [Attack]
    var spells: [Spell]
    var specialAbilities: [SpecialAbility]
    var movement: String?
    var level: Int?
    var alignment: String?
}

Encounter

struct Encounter: Codable, Identifiable {
    let id: UUID
    var name: String
    var combatants: [Combatant]
    var currentRound: Int
    var activeCombatantID: UUID?
    var rollLog: [RollLogEntry]
    var createdAt: Date
    var modifiedAt: Date
}

Implementation Order

Phase Files What
1 Leaf models Condition, Allegiance, AbilityScores, DiceExpression
2 Composite models Attack, Spell, SpecialAbility
3 Core models Combatant, RollLogEntry, Encounter, Party, CombatantTemplate
4 Dice engine DiceParser, DiceRoller
5 File services FileService, PartyStore, TemplateStore
6 State EncounterManager
7 Shared views HPBar, ConditionBadges, DiceExpressionField
8 Left pane InitiativeRowView, InitiativeListView
9 Center pane AttackActionView, PCDetailView, MonsterDetailView, CombatantDetailView
10 Right pane RollLogEntryView, RollLogView
11 Library views PartyPickerView, TemplateLibraryView, TemplateEditorView
12 Toolbar + root EncounterToolbar, ContentView, ShadowdarkTrackerApp