Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

combat

A terminal UI (TUI) application for D&D Dungeon Masters to manage combat encounters.

Built with ratatui for the TUI (crossterm backend), rusqlite for persistence.

Features

  • Track initiative order, hit points, and damage for all combatants
  • Apply actions: damage, healing, temporary HP, misses, buffs
  • Distinguish action types: Action, Bonus Action, Reaction, Legendary Action
  • Save prepared combats and resume them across sessions
  • Turn and round tracking with automatic reordering (finished combatants move to the bottom)

Running

cargo run -p combat

# Suppress stderr (logs go to output.log)
cargo run -p combat 2>/dev/null

Architecture

State Machine

The application is a state machine driven by the Screen enum (defined in types.rs). Every key press is routed by event_handler::handle_key_event, which matches on the current Screen variant and dispatches to the appropriate handler module. Each handler returns a new Screen, and the app loop replaces app.screen with it.

Screen::Menu
  │
  ├─[C] New combat ──────────────────► Screen::Create(CreateCombatState)
  │                                          │
  ├─[L] Load combat ─► Screen::Load          ├─[S] Start combat ──► Screen::Combat(CombatState)
  │       │                                  │                           │
  │       └─[Enter] ─────────────────────────│                           └─[A] Action ──► Screen::Action(ActionState)
  │                                          └─[P] Save ──► DB                               │
  │                                                                    ┌─[M] Miss ───────────┤
  └─[q] Quit                                                           │                     ├─[A] Attack ──► Screen::Prompt ──► Screen::Combat
                                                                       │                     ├─[H] Heal ────► Screen::Prompt ──► Screen::Combat
                                                                       │                     └─[T] THP ─────► Screen::Prompt ──► Screen::Combat
                                                                       └───────────────────────────────────────────────────────► Screen::Combat

Overlay screens can appear from any context:

  • Screen::Prompt(PromptState) — a modal text-input dialog. Carries a callback: Fn(&mut App, String) -> Screen so any screen can push a prompt and regain control when the user confirms.
  • Screen::Error(ErrorState) — a modal error display. Stores the originating screen and returns to it on dismissal.

Screens

Variant State struct Purpose
Screen::Menu Main menu; entry point on launch
Screen::Create(CreateCombatState) CreateCombatState Add/edit combatants before combat begins; save as prepared or start immediately
Screen::Load(LoadState) LoadState Browse saved combats from the database and resume one
Screen::Combat(CombatState) CombatState Active combat: HP tracking, turn order, round progression
Screen::Action(ActionState) ActionState Action flow: choose targets then apply an effect (damage / heal / THP / miss / buff)
Screen::Prompt(PromptState) PromptState Generic reusable text-input overlay; resolves via a callback
Screen::Error(ErrorState) ErrorState Error overlay; dismisses back to the originating screen

Module Structure

src/
├── main.rs            Entry point. Bootstraps logger (fern → output.log),
│                      opens SQLite DB, runs migrations, initializes terminal.
├── app.rs             App struct (database + current screen) and the main event loop.
├── types.rs           All domain types: Screen, Combatant, Combat, Round, Turn,
│                      Action, ActionType, ActionEffect, and all *State structs.
├── database.rs        SQLite persistence. Combatants and rounds are stored as
│                      MessagePack BLOBs via rmp-serde.
├── tui.rs             Terminal initialization/teardown (crossterm raw mode + alternate screen).
├── errors.rs          DatabaseError and panic/error hooks that restore the terminal.
├── event_handler.rs   Top-level key event router; dispatches to per-screen handlers.
├── event_handler/
│   ├── menu.rs
│   ├── combat_create.rs
│   ├── load.rs
│   ├── combat.rs
│   ├── action.rs
│   ├── prompt.rs
│   └── error.rs
├── ui.rs              Top-level render entry point.
└── ui/
    ├── logo.rs        Logo rendering utility.
    └── screens/       One render function per Screen variant (no state mutation).

Persistence

The combat table stores combatants and rounds as MessagePack BLOBs. Migrations run on every startup via run_database_migrations (idempotent CREATE TABLE IF NOT EXISTS). The DB file is ./db.db3 relative to the working directory at runtime.