Fantactical #
Fantactical is a pre-alpha desktop combat tracker and hex-grid virtual tabletop
for GURPS 4e. It is a single Rust application built with Bevy and bevy_egui,
with an optional authoritative WebSocket server/client mode.
The application starts with an empty, valid encounter. Use Import Sheet to
choose a .gcs or JSON file; network clients instead wait for the server's
authoritative state.
Current status #
The codebase currently implements the following. Automated tests cover the underlying rules and state paths; rendered interaction still requires the QA called out below.
| Area | Implemented behavior |
|---|---|
| GCS import | Selected attributes, current/max HP and FP, skills, recursively nested trait/equipment weapons, equipped armor DR, portrait bytes/path, and source path for local reload |
| Turn flow | Immutable snapshots across maneuver selection, attack setup/confirmation, attack roll, defense, injury, non-combat resolution, and turn completion, including rewindable combat-prompt context |
| Maneuvers | 27 maneuver variants, status/posture/encumbrance availability filters, target and reach validation, Aim/Evaluate accumulation, and supported Extra Effort combinations |
| Combat math | 3d6 checks, authoritative random hit locations, range bands, hit-location modifiers, maneuver/modifier breakdowns, weapon-skill-based Parry/Block, damage, DR, wounding, shock, major wounds, HT follow-ups, consciousness, death, and leg crippling |
| Injury model | 27 extended humanoid hit locations plus structured GM follow-up flags for rules that are not represented as actor state |
| VTT and panels | Premium and Lite interfaces over one rules engine, hex grid, movable and six-way facing portrait/fallback tokens, HP bars, maneuver arrows/self-rings, token-to-token range ruler, character cards, event log, GM controls, and phase surfaces |
| History and persistence | Validated immutable history, rewind-by-index, non-overwriting round/checkpoint snapshots, separate event-log file, portrait-path preservation without embedded image bytes, and final save on clean application exit |
| Networking | CLI-selectable server/client roles, token authentication, first-client GM assignment, GM ownership-roster controls, authoritative intent handling, reconnect, and state/settings/event-log synchronization |
cargo test --locked --all-targets is the release-oriented verification
command. See docs/TESTING.md for the current test inventory.
Fantactical Premium is the fallback for a fresh install. A saved local edition preference takes precedence on later launches. Premium uses a board-first command shell, authoritative token motion, tactical state cues, and a compact combat surface. Fantactical Lite preserves the original low-effects layout and direct interaction style. Both editions emit the same intents and use the same game state, history, persistence, permissions, and network authority.
Important residual gaps #
Implemented does not mean rules-complete or production-ready:
- Critical-hit and critical-miss table results are selected and reported, but several non-damage effects (drop weapon, hit self/ally, lose balance, and similar results) are not applied mechanically.
- Standalone Feint/Feign declarations are recorded as non-damaging actions, but their contests and margin effects are not resolved automatically.
- Attack arc checks, hit-location sub-location rolls, the Critical Head Blow table, paralysis, infection, appearance changes, and other location-specific consequences are emitted as explicit GM follow-up flags rather than automatically resolved.
- Shock modifiers currently become active immediately instead of waiting for the injured actor's next turn, and the Change Posture maneuver only automates recovery from knockdown.
- The UI exposes GM controls for extra turns, extra attacks, Enhanced Time Sense, and network actor ownership, but not for editing PC/NPC identity.
- Lite still ships only the Mil-Sim theme; Appearance switches presentation, motion, effects, and scale rather than selecting another Lite color theme.
- There is no in-app network configuration screen.
- Network transport reconnects automatically, but the server assigns a new client identity and does not restore the prior GM role or actor ownership.
- The GUI and network flow need real-session and cross-platform QA. Linux is the primary development platform; Windows and macOS have not been verified here.
Build and run #
Prerequisites #
- Rust 1.88 or newer (declared by the crate and required by the locked image dependency)
- Bevy's platform libraries; see the official Bevy setup guide
Run an offline session:
cargo run --release
Choose Fantactical Lite for this launch, or force Reduced Motion in either edition:
cargo run --release -- --ui lite
cargo run --release -- --reduced-motion
The in-app Appearance menu switches Premium/Lite, Full/Reduced Motion, effects
quality, and interface scale. Those preferences are stored in
fantactical_ui.json in the working directory; they never enter encounter
history or network snapshots. Command-line presentation flags override the
loaded preference in memory and are not written merely by launching the app.
The default mode is off, the default port is 9002, and the default session
token is fantactical. Inspect all launch options with:
cargo run --release -- --help
Network mode #
Start a server reachable from other machines:
cargo run --release -- \
--network-mode server \
--host 0.0.0.0 \
--port 9002 \
--token replace-this-token
Connect a client:
cargo run --release -- \
--network-mode client \
--host 192.0.2.10 \
--port 9002 \
--token replace-this-token
The first authenticated client is the GM. Later clients begin with no actor ownership; the GM can assign their actors from the connected-client roster in GM Config.
Saves and resume #
Offline and server processes write files named
<session>_round<round>.json and <session>_log.json. Choose their location
and resume validated files with the following options. Missing output
directories are created as needed.
cargo run --release -- \
--session-name friday \
--save-dir saves \
--load-history saves/friday_round4.json \
--load-log saves/friday_log.json
Client processes never write session persistence files and reject
--load-history/--load-log; they receive authoritative state from the
server. Round snapshot names are create-once and are not overwritten. If a
resumed session saves a round whose canonical file already exists, Fantactical
preserves that input and writes
<session>_round<round>_checkpoint<N>.json instead.
When --load-history is supplied without --load-log, Fantactical also loads
an existing <session>_log.json from --save-dir. Pass --load-log to select
a different log explicitly.
CLI test tool #
cargo run --bin ftctl -- dice
cargo run --bin ftctl -- check-roll 12 15
cargo run --bin ftctl -- damage-roll 2 --adds 1
cargo run --bin ftctl -- test-injury \
--hp 12 --location torso --damage 5 --damage-type cut
cargo run --bin ftctl -- available-maneuvers --posture standing
cargo run --bin ftctl -- range-penalty 15
cargo run --bin ftctl -- hex-distance 0 0 3 2
cargo run --bin ftctl -- save-state /tmp/fantactical-state.json --round 1
cargo run --bin ftctl -- load-state /tmp/fantactical-state.json
save-state also uses create-once semantics and fails if its destination
already exists.
Tests #
cargo test --locked --all-targets
Documentation #
License #
MIT
If you find a bug, please report it on the canonical Tangled repository or submit a pull request there. The GitHub repository is a public mirror.