docs: add build spec, TODO, and encounter table sources

- vegvisir-spec.md: full build spec (core flow, chargen subsystem, GM
  layer, world model, journey flow, data compilation pipeline)
- TODO.md: spec broken into 8 sequential build phases
- docs/: Norse-flavoured and general random encounter tables (authoring
  source for the compile step)
This commit is contained in:
2026-07-04 20:06:03 +10:00
parent 6ff519760c
commit c0ef70d7fe
4 changed files with 3462 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
# Vegvísir — TODO
Derived from [vegvisir-spec.md](vegvisir-spec.md). Phases are roughly sequential; content digitization (Phase 0) blocks coding on anything that consumes those tables.
## Phase 0 — Content Digitization (blocks build)
Markdown tables authored in `docs/`, used once at compile time, then deleted. Reflavored to the Norse setting where content is Glorantha-specific; mechanical structure stays RQG-derived.
- [ ] Define markdown table structure/conventions (headers, column format) for all seed files — spec Open Question 1
- [x] Flavour/encounter tables (`norse_encounter_tables.md`, `random-encounter-tables.md` exist — confirm format matches conventions once defined)
- [ ] Ability Results Table
- [ ] Resistance Table
- [ ] Movement rates / travel rules tables
- [ ] NPC generation tables (six rolled fields, per existing narrative-app spec)
- [ ] Spirit Magic spell list (Stef digitizes Magic book before coding starts)
- [ ] Kinship/pantheon content (replaces Gloranthan cults) + per-Kinship Rune Magic spell lists
- [ ] Homeland regions (Stef's regions replacing Dragon Pass homelands, with characteristic/skill modifiers)
- [ ] Family History event tables (Stef's calendar/history, RQG year-by-year roll structure)
- [ ] Occupation table (reflavored names, RQG-derived modifiers/structure)
- [ ] Personality Trait pair list — confirm final opposed pairs and starting-score method (rolled vs. assigned) — spec Open Question 7
## Phase 1 — Project Scaffolding & Data Compilation
- [ ] Project scaffolding (TypeScript app; decide runtime/UI framework, storage approach)
- [ ] One-time compile script: markdown → typed TS data files (`*.generated.ts`, marked do-not-edit)
- [ ] Compile all table categories: flavour tables, movement rates, NPC tables, Spirit Magic, Rune Magic, Occupation, Personality Trait pairs, Ability Results Table, Resistance Table
- [ ] Hand-written lookup/resolution modules importing generated data (table roll functions, movement rate lookup, Resistance Table lookup, Ability Results lookup) — kept separate from generated files
- [ ] Delete source markdown after compile; document re-compile workflow for future table changes
## Phase 2 — Character Generator + Editable Sheet
RQG chargen sequence with setting substitutions. Distinct subsystem; GM layer reads from the sheet it produces.
- [ ] Chargen engine following RQG sequence:
- [ ] 1. Homeland (Stef's regions)
- [ ] 2. Family History (year-by-year event rolls, Stef's calendar)
- [ ] 3. Rune Affinities (tied into characteristic modifiers per RQG)
- [ ] 4. Characteristics (STR, CON, SIZ, INT, POW, DEX, CHA) + derived stats: MP, HP + hit locations, Healing Rate, Damage Bonus, Max ENC, Strike Rank, Skill Category Modifiers
- [ ] 5. Occupation (reflavored table)
- [ ] 6. Kinship (grants Common Rune Magic access + Rune point pool)
- [ ] 7. Personal Skill Bonuses
- [ ] 8. Other Information (gender, name, age, handedness, scars, family, tribe/settlement/clan, heirloom, reputation, movement)
- [ ] 9. Passions (directed %, e.g. Loyalty/Hate/Love)
- [ ] 10. Personality Traits (Pendragon-style opposed pairs, each pair scored to 20)
- [ ] Persistent, mutable character sheet — every roll user-editable/re-rollable after generation (not one-shot)
- [ ] Character persistence across sessions
## Phase 3 — GM Layer (encounter resolution)
Deterministic RQG mechanics, reading from the active character sheet(s).
- [ ] Skill checks via Ability Results Table (pass/fail with degree of success, brief outcome)
- [ ] Opposed checks vs NPCs/environment via full Resistance Table
- [ ] Full combat resolution: strike ranks, hit locations, damage, round-by-round to an outcome
- [ ] Augmentation from Passions (boost/hinder per RQG rules)
- [ ] Personality Trait checks for ambiguous character behavior mid-resolution
- [ ] Spirit Magic casting during resolution (MP cost per RQG)
- [ ] Rune Magic casting (Rune points from Kinship pool)
- [ ] Reaction rolls
- [ ] (Out of scope v1, flag as future: Sorcery, Divine Intervention)
## Phase 4 — Flavour Roller, Scene Types & Favourites
- [ ] Roll engine: 34 mixed-category options per roll (encounter / cultural detail / NPC quirk), tagged by scene type + danger level
- [ ] Manual-pick scene types: Combat/Danger, Social/NPC (Travel and Tavern/Settlement are driven by Phases 56)
- [ ] Pick-one-or-none flow → hand picked option to GM layer for resolution
- [ ] Favourites: save resolved results, persistent across sessions, grouped by scene type, danger tag carried through for filtering
- [ ] Favourites browser (viewable list, grouped by scene type)
- [ ] Export: downloadable markdown dump of favourites, grouped by scene type
## Phase 5 — World Model
- [ ] Locations: name, type (Settlement / Wilderness Point / Dungeon / Landmark → maps to scene types), freeform features/tags
- [ ] Districts (settlement sub-locations): name, danger level (none/low/med/high), district-scoped NPC pool
- [ ] District encounter checks: manually triggered, chance weighted by danger level (defaults none 0% / low 15% / med 35% / high 60%, tunable in config)
- [ ] Paths: from/to, distance (km), mode of travel, danger level, terrain notes
- [ ] Travel time computation: distance ÷ RQG movement rate for mode (from compiled data, not hardcoded)
- [ ] Multi-leg routing through intermediate locations when no direct path exists (sum time across legs)
- [ ] NPCs scoped to location or district; both random draw and browsable list presentations
## Phase 6 — Journey Flow
- [ ] Origin/destination/mode selection UI (mode changeable per leg)
- [ ] Route resolution + total travel time display
- [ ] Open-road legs: fixed cadence, one encounter roll per day of travel
- [ ] In-settlement: manual per-district checks (danger-weighted, from Phase 5)
- [ ] Triggered rolls surface mixed flavour options tagged by scene type (derived from location/path type) + danger level
- [ ] Resolution + save flow wired through GM layer and favourites as usual
## Phase 7 — Narration Layer (optional, last)
- [ ] Ollama integration: mechanical result → prose paragraph via OpenAI-compatible endpoint (`localhost:11434`), model name configurable
- [ ] Plain templated-text fallback when Ollama unreachable or output unusable
- [ ] Keep all mechanical resolution deterministic — LLM narrates only
## Open Questions (resolve before/during build)
- [ ] Markdown table structure for seed files, incl. new RQG-derived tables (Rune Magic, Occupation, Resistance Table, Ability Results Table)
- [ ] Homeland/Kinship/Occupation replacement content from Stef (alongside digitization)
- [ ] Personality Trait pair final set + starting-score generation method
## Explicitly Out of Scope (v1)
- Fresh OCR/extraction from the physical book
- Chat-style generation / conversational interface
- Category selection separate from scene type
- Sorcery, Divine Intervention
- Glorantha as setting (RQG is mechanics source only)
+494
View File
@@ -0,0 +1,494 @@
# Norse Journey Encounter Tables
Roll on **Layer 1** first. If you want more detail, roll on the relevant **Layer 2** table.
---
## Layer 1: What Happens? (d20)
| Roll | Event |
|------|-------|
| 13 | Nothing — uneventful travel |
| 45 | Weather turns |
| 68 | Encounter a person |
| 910 | Encounter a group |
| 1112 | Find something |
| 1314 | Natural hazard |
| 1516 | Signs of recent violence |
| 1718 | Pursuit or being followed |
| 19 | Physical hardship |
| 20 | Something uncanny |
---
## Layer 2A: Person Encountered (d20)
| Roll | Who |
|------|-----|
| 1 | Wounded traveller, won't say how |
| 2 | Thrall fleeing their master |
| 3 | Merchant with something hidden under the cart |
| 4 | Grieving widow, heading nowhere in particular |
| 5 | Lost child, won't say where they came from |
| 6 | Outlaw watching from the treeline |
| 7 | Skald collecting stories, too curious by half |
| 8 | Old woman who knows your name somehow |
| 9 | Disgraced jarl's son, armed and bitter |
| 10 | A healer with blood on their hands they can't explain |
| 11 | A fisherman far from any water |
| 12 | A young woman disguised as a man |
| 13 | A former thrall now free, with nowhere to go |
| 14 | A blind man navigating perfectly |
| 15 | A priest of the old ways, wary and watchful |
| 16 | A very old warrior with nothing left to prove |
| 17 | Someone who claims to know the protagonist's kin |
| 18 | A deserter from a nearby lord's warband |
| 19 | A young man running an errand he refuses to describe |
| 20 | A stranger in foreign dress, lost and afraid |
---
## Layer 2B: Group Encountered (d20)
| Roll | Who |
|------|-----|
| 1 | Raiders looking for easy prey |
| 2 | Funeral procession, silent and watchful |
| 3 | Displaced villagers with everything they own |
| 4 | Hunting party — purpose unclear |
| 5 | Mercenaries between contracts, drinking heavily |
| 6 | Settlers arguing loudly among themselves |
| 7 | A jarl's tax collectors with an armed escort |
| 8 | Pilgrims travelling to a sacred site |
| 9 | A warband returning home, fewer than they left |
| 10 | Slavers with a coffle of thralls |
| 11 | Fishermen sheltering from weather inland |
| 12 | A family fleeing a blood feud |
| 13 | Young warriors seeking their first raid |
| 14 | Traders from the east, nervously off-route |
| 15 | A lord's patrol, suspicious of lone travellers |
| 16 | Refugees from a burned settlement |
| 17 | Drunken men from a wedding, armed but unfocused |
| 18 | A group of thralls transporting goods without supervision |
| 19 | Outlaws who've mistaken the protagonist for someone else |
| 20 | A band of women — armed, organised, and saying little |
---
## Layer 2C: Weather Turns (d20)
| Roll | Weather |
|------|---------|
| 1 | Sudden blizzard — visibility drops to nothing |
| 2 | Ice storm — exposed skin at risk within the hour |
| 3 | Fog so thick landmarks vanish entirely |
| 4 | Flash flood from snowmelt upstream |
| 5 | Freezing rain turns every surface treacherous |
| 6 | Unseasonable warmth — ice gives way underfoot without warning |
| 7 | Wind strong enough to knock a person sideways |
| 8 | Sleet drives horizontally, stings like needles |
| 9 | Temperature drops sharply at nightfall — no shelter near |
| 10 | Heavy snow buries the trail ahead |
| 11 | Lightning storm with nowhere to take cover |
| 12 | Hail strips leaves, spooks animals, bruises skin |
| 13 | Mist rising off ice — beautiful and disorienting |
| 14 | Frost so hard the ground rings like iron underfoot |
| 15 | A thaw that turns solid ground to mud overnight |
| 16 | Darkness falls two hours early — unnatural-feeling |
| 17 | Wind that carries sound strangely — voices from nowhere |
| 18 | Snow so dry it shifts like sand, fills tracks instantly |
| 19 | A brief clearing — then worse than before |
| 20 | Sky turns green before the storm hits |
---
## Layer 2D: Find Something (d100)
| Roll | What |
|------|------|
| 0102 | Abandoned camp, fire still warm |
| 0304 | A corpse stripped of everything but its boots |
| 0506 | A child's toy in the middle of nowhere |
| 0708 | A boat run aground with no crew |
| 0910 | A cache of stolen goods, poorly hidden |
| 1112 | A runestone defaced recently |
| 1314 | A hanged man — still breathing |
| 1516 | A sword driven into the earth, no grave beneath it |
| 1718 | A bag of silver with no name on it |
| 1920 | A dead horse with all four legs broken |
| 2122 | A campfire surrounded by nine untouched seats |
| 2324 | A letter sealed and addressed, never delivered |
| 2526 | A pit dug and abandoned mid-task |
| 2728 | Fresh tracks leading off the path and then stopping |
| 2930 | A crude grave with a weapon laid across it |
| 3132 | Signs of a large camp, abandoned days ago |
| 3334 | A locked chest with no key and no owner |
| 3536 | A cart overturned, goods intact, horse gone |
| 3738 | A bundle of clothes belonging to a child |
| 3940 | A pile of ash where something large was burned |
| 4142 | A trap set on the trail — recently, and well |
| 4344 | A carcass half-butchered and left |
| 4546 | A carved idol left at a crossroads |
| 4748 | A shield split cleanly in two |
| 4950 | A door standing alone in a field, hinged to nothing |
| 5152 | Bloodstained snow leading in two directions |
| 5354 | A drowned man face-down in a shallow stream |
| 5556 | A fire laid but never lit |
| 5758 | An empty cage sized for a person |
| 5960 | A broken cart axle and scattered grain |
| 6162 | A wound dressing discarded on the trail |
| 6364 | A ring of stones too large to be a cooking fire |
| 6566 | A well with something tied to the rope that isn't a bucket |
| 6768 | A fresh cairn with no name |
| 6970 | Burnt offerings at a tree, still smouldering |
| 7172 | A pair of boots, side by side, perfectly placed |
| 7374 | A journal in a language the protagonist doesn't know |
| 7576 | A broken spear shaft — a fine one, from somewhere important |
| 7778 | A midden heap with something that doesn't belong |
| 7980 | A ford marked with warning signs ignored by someone recently |
| 8182 | A farmstead intact but completely empty |
| 8384 | A bell hanging from a tree with no rope to ring it |
| 8586 | Animal bones arranged in a deliberate pattern |
| 8788 | A cloak pinned to the ground with a knife |
| 8990 | A child's hiding spot — recently used |
| 9192 | A rope bridge cut from one side |
| 9394 | A message carved into living wood |
| 9596 | A body buried face-down — someone afraid of what it might do |
| 9798 | A ship's figurehead far from any coast |
| 99100 | A perfectly preserved meal laid out with no one to eat it |
---
## Layer 2E: Natural Hazard (d20)
| Roll | Hazard |
|------|--------|
| 1 | River crossing — current deceptively strong |
| 2 | Thin ice over deep water |
| 3 | Rockslide blocking the pass |
| 4 | Wolves — hungry, patient, following |
| 5 | Marsh that doesn't appear on any mental map |
| 6 | Trail disappears under fresh snowfall |
| 7 | A tree falls across the path in the night |
| 8 | Loose scree on a slope that looked solid |
| 9 | A frozen waterfall — must be climbed or bypassed at great cost |
| 10 | Bog hidden under a dusting of snow |
| 11 | A river swollen and impassable where it wasn't before |
| 12 | Ice fog — cold enough to freeze breath on a scarf |
| 13 | Bear newly woken, confused and aggressive |
| 14 | A cliff path with recent falls along its edge |
| 15 | Night catches the protagonist without shelter in an exposed place |
| 16 | A ford turned to deep water by rain upstream |
| 17 | Deadfall — a tangle of fallen timber blocks the wood path |
| 18 | A crack in the ice the protagonist nearly doesn't see |
| 19 | Ravens massing overhead — drawing attention |
| 20 | An avalanche track still moving |
---
## Layer 2F: Signs of Recent Violence (d20)
| Roll | What You Find |
|------|---------------|
| 1 | Burned farmstead, still smoking |
| 2 | Bodies left unburied — deliberate dishonour |
| 3 | Blood trail leading off the path |
| 4 | A survivor too shocked to speak |
| 5 | Livestock scattered, no people anywhere |
| 6 | A child hiding, refusing to move |
| 7 | A settlement intact but everyone inside is dead |
| 8 | A battlefield — small scale, very recent |
| 9 | Weapons abandoned mid-fight and not retrieved |
| 10 | A man dying slowly, won't say who did it |
| 11 | Tracks of many boots leaving in a hurry |
| 12 | A house torn apart from the inside |
| 13 | Drag marks leading to the water |
| 14 | A warning left by the attackers for anyone who follows |
| 15 | Looted grave goods scattered on the road |
| 16 | A woman alive among the dead, armed with what she could find |
| 17 | A child carrying something that belonged to someone else |
| 18 | Signs of a struggle with no bodies — just blood |
| 19 | A man hanged from a tree with a carved accusation around his neck |
| 20 | Heads displayed on stakes at the settlement boundary |
---
## Layer 2G: Pursuit / Being Followed (d20)
| Roll | Who or What |
|------|-------------|
| 1 | Debt collector with hired muscle |
| 2 | Someone who blames the protagonist for a death |
| 3 | A thrall-catcher — wrong target |
| 4 | Outlaw seeking to silence a witness |
| 5 | Unknown — never shows themselves |
| 6 | A dog that won't stop following |
| 7 | A boy who wants to come along |
| 8 | A woman who says she's been hired to watch |
| 9 | A wounded man trailing blood and asking for help |
| 10 | Two riders keeping distance but matching pace |
| 11 | Someone who knows the protagonist's destination |
| 12 | A man who claims to be following the same person |
| 13 | A local lord's men, claiming lawful business |
| 14 | A sworn enemy of someone the protagonist helped |
| 15 | A crow — too consistent to be coincidence |
| 16 | A group that disappears whenever checked but reappears at dusk |
| 17 | A young woman escaping a marriage arrangement |
| 18 | A former comrade who shouldn't be alive |
| 19 | Someone who says they're there for the protagonist's protection |
| 20 | A figure glimpsed only at the edge of firelight |
---
## Layer 2H: Physical Hardship (d20)
| Roll | Hardship |
|------|----------|
| 1 | Food runs out a day earlier than expected |
| 2 | Shelter falls through — night in the open |
| 3 | Injury from a stumble — nothing fatal, everything inconvenient |
| 4 | Equipment fails at the worst moment |
| 5 | Illness sets in slowly over the day |
| 6 | Exhaustion forces a stop in exposed terrain |
| 7 | Water source is frozen or fouled |
| 8 | Boots give out — blisters or worse |
| 9 | A fire won't start in wet conditions |
| 10 | Pack comes loose and gear is scattered in the dark |
| 11 | An old wound reopens |
| 12 | Hands too cold to grip properly |
| 13 | Stomach sickness from something eaten |
| 14 | Sleep impossible due to cold, noise, or dread |
| 15 | Frostbite threatening fingers or toes |
| 16 | Navigation error adds half a day to the journey |
| 17 | Horse or pack animal goes lame |
| 18 | Weight carried proves too much — something must be abandoned |
| 19 | A tool or weapon lost in a river crossing |
| 20 | Dehydration from sweating in cold gear |
---
## Layer 2I: Something Uncanny (d20)
| Roll | What |
|------|------|
| 1 | Tracks in snow that begin from nowhere |
| 2 | A voice calling a name — no source found |
| 3 | An animal behaving with deliberate intelligence |
| 4 | The same landmark passed twice on a straight road |
| 5 | A dream so vivid it bleeds into waking |
| 6 | A stranger who vanishes between one breath and the next |
| 7 | A fire that burns green for a moment |
| 8 | The protagonist's own reflection does something different |
| 9 | A sound like weeping from deep in the ice |
| 10 | Birds all flee at once, in silence |
| 11 | A path that wasn't there yesterday and isn't on any map |
| 12 | A figure standing very still on the horizon — gone when approached |
| 13 | Stars in the wrong positions for an hour |
| 14 | The smell of the sea miles from any coast |
| 15 | An old man who answers questions the protagonist didn't ask aloud |
| 16 | A handprint in frost on the inside of a sealed shelter |
| 17 | A sound of battle faint and distant — no source ever found |
| 18 | The protagonist wakes with soil under their fingernails |
| 19 | A raven speaks one word, clearly, and flies away |
| 20 | Time passes differently — a night feels like minutes, or hours |
---
---
## Layer 3A: What the Person Needs (d8)
*Roll after Layer 2A. What drives this scene.*
| Roll | They need... |
|------|-------------|
| 1 | To be heard — they haven't spoken to another person in days |
| 2 | Safe passage past something specific ahead |
| 3 | News of someone, and they will pay for it |
| 4 | A reason not to do something they have already decided to do |
| 5 | To hand something off — weight they have been carrying too long |
| 6 | Help they cannot ask for directly |
| 7 | To be followed, without being told |
| 8 | Nothing they will admit to |
---
## Layer 3B: How the Group Presents (d8)
*Roll after Layer 2B. The group's attitude when the protagonist arrives.*
| Roll | They are... |
|------|------------|
| 1 | Tense — something happened just before you arrived |
| 2 | Watching, not approaching |
| 3 | Openly suspicious, hands near weapons |
| 4 | Performatively calm |
| 5 | Arguing among themselves, briefly interrupted |
| 6 | Too welcoming, too quickly |
| 7 | Clearly afraid of something that is not the protagonist |
| 8 | Divided — half want help, half do not |
---
## Layer 3C: Weather Duration (d6)
*Roll after Layer 2C. How long it lasts and what it costs.*
| Roll | Duration |
|------|----------|
| 1 | An hour — bad and fast |
| 2 | Until dark |
| 3 | Through the night |
| 4 | Two days |
| 5 | Long enough to change the route entirely |
| 6 | Indefinitely — this is the new normal for now |
---
## Layer 3D: Who Left This? (d12)
*Roll after Layer 2D. The story behind the find.*
| Roll | It was left by... |
|------|------------------|
| 1 | Someone running from something they have not escaped yet |
| 2 | A group that did not all survive what came next |
| 3 | One person, alone, who made a decision here |
| 4 | A child, deliberately |
| 5 | Whoever was here last night |
| 6 | The same person the protagonist is looking for |
| 7 | Someone who knew this place would be found |
| 8 | A person now working against the protagonist |
| 9 | Nobody — it has been here longer than it looks |
| 10 | Two people who disagreed about leaving it |
| 11 | Someone who came back to retrieve it and could not |
| 12 | Nobody living can say |
---
## Layer 3E: How Dangerous? (d6)
*Roll after Layer 2E. The true stakes of the natural hazard.*
| Roll | The stakes |
|------|-----------|
| 1 | Minor — delay and discomfort only |
| 2 | Real — something could be lost or broken |
| 3 | A skill or smart decision will see it through |
| 4 | Someone in the group is at immediate risk |
| 5 | No safe path — all options have a cost |
| 6 | This could kill. And it is patient. |
---
## Layer 3F: Who Did the Violence? (d12)
*Roll after Layer 2F. The perpetrators.*
| Roll | Responsible... |
|------|---------------|
| 1 | Raiders — long gone, but not far |
| 2 | Men on someone's orders, not their choice |
| 3 | One individual, systematic |
| 4 | The victims themselves — it went wrong |
| 5 | A local family, decades of grievance finally spent |
| 6 | Soldiers returning from somewhere worse |
| 7 | Someone the protagonist has already met |
| 8 | Nobody who can be named — only described |
| 9 | The wrong people. They mistook their target. |
| 10 | Professionals. This was clean. |
| 11 | A single act of desperation, not a plan |
| 12 | Unknown. The survivors will not say. |
---
## Layer 3G: What the Pursuer Wants (d8)
*Roll after Layer 2G. The intent behind the pursuit.*
| Roll | They want... |
|------|-------------|
| 1 | To recover something that does not belong to the protagonist |
| 2 | Information — who the protagonist knows or spoke to |
| 3 | The protagonist dead, but not yet |
| 4 | To warn, not harm |
| 5 | To deliver a message from someone who could not send it openly |
| 6 | To follow, not intercept — someone else is waiting ahead |
| 7 | Nothing yet. They are measuring. |
| 8 | To make the protagonist afraid enough to stop |
---
## Layer 3H: How Bad Is It? (d6)
*Roll after Layer 2H. Severity of the physical hardship.*
| Roll | Severity |
|------|----------|
| 1 | Uncomfortable — morale hit, nothing more |
| 2 | Forces a rest and costs half a day |
| 3 | Something is damaged, lost, or consumed early |
| 4 | Someone is impaired until properly tended to |
| 5 | Progress halted until fixed — may take a full day |
| 6 | This will matter later, even after it is resolved |
---
## Layer 3I: What the Uncanny Thing Does (d8)
*Roll after Layer 2I. The nature of the event.*
| Roll | It... |
|------|-------|
| 1 | Communicates something — though not in any language |
| 2 | Leaves evidence that cannot be explained away |
| 3 | Draws the protagonist somewhere specific |
| 4 | Disappears if approached directly |
| 5 | Repeats exactly, later |
| 6 | Is recognised by someone nearby who will not discuss it |
| 7 | Means something that will not be clear until too late |
| 8 | Was waiting for the protagonist specifically |
---
## Layer 4A: Complication (d12)
*Roll after any Layer 3 result. A twist that deepens the scene.*
| Roll | Added complication |
|------|-------------------|
| 1 | Someone is watching this exchange |
| 2 | There is less time than it first appeared |
| 3 | Another party has an interest in the outcome |
| 4 | The simplest reading is wrong |
| 5 | Someone here is lying, but not about what matters most |
| 6 | This has already happened once before, nearby |
| 7 | A child is involved and should not be |
| 8 | Trust is the cost of progress, and it will not be offered freely |
| 9 | The right answer creates a new problem |
| 10 | Someone else made this decision already — recently |
| 11 | What is needed is something the protagonist has |
| 12 | It is connected to the last thing they found |
---
## Layer 4B: The Hidden Truth (d10)
*Roll after Layer 4A rows 4, 5, 9, or 12 — or whenever the scene demands it.*
| Roll | The truth... |
|------|-------------|
| 1 | Changes who is innocent |
| 2 | Implicates someone trusted |
| 3 | Is sadder than the worst interpretation |
| 4 | Was known by someone who said nothing |
| 5 | Was an accident that became a cover-up |
| 6 | Has a second thread — pull it and something else unravels |
| 7 | Was constructed to be found this way |
| 8 | Was meant to stay buried |
| 9 | Is exactly what it looks like. That is what makes it terrible. |
| 10 | Will cost more to know than not to |
---
*These tables are designed to layer. Roll Layer 1 to frame what kind of event occurs, then roll Layer 2 for specifics, Layer 3 for context, and Layer 4 for the complication beneath. Stop at any depth that fits the moment — not every scene needs all four layers.*
File diff suppressed because it is too large Load Diff
+128
View File
@@ -0,0 +1,128 @@
# Vegvísir — Build Spec
## Purpose
Standalone tool acting as a rules-aware GM to generate and resolve flavour encounters between major story beats in a fantasy manuscript. Ruleset is RQG (RuneQuest: Roleplaying in Glorantha) mechanics — **not the Glorantha setting**. Setting-specific RQG content (homelands, cults, family-history years, geography) is reflavored to Stef's own Norse-flavoured world during digitization; only the mechanical framework (procedures, dice math, tables' *structure*) is carried over as-is.
## Core Flow
1. User selects a scene type — **or**, for travel, selects a journey (origin → destination); see Journey Flow below.
2. Tool rolls across relevant RQG-derived tables for that scene type and returns a few options at once (mixed: encounter / cultural detail / NPC quirk).
3. User picks one (or none).
4. GM layer resolves the picked encounter as a full mini-encounter: makes RQG skill/combat rolls against character stats, narrates outcomes.
5. User saves the resolved result to a favourites list.
6. Favourites persist across sessions, organized by scene type.
## GM Layer
- Rules-aware, not just narration — uses actual RQG mechanics (skill checks via the Ability Results Table, combat resolution, opposed rolls via the Resistance Table, Augmentation, reaction rolls) to adjudicate outcomes.
- Needs access to character stats (characteristics, skills, Runes, Passions, Personality Traits) to make rolls against.
- Scope: full mini-encounter resolution (e.g. a combat encounter plays out roll-by-roll to an outcome, a skill challenge resolves with success/failure/degree).
### Character source (subsystem — scope note)
Full RQG-derived character generator, reflavored to Stef's setting. Procedure follows RQG's chargen sequence, with setting-specific steps substituted:
1. **Homeland** — Stef's own regions in place of Dragon Pass homelands.
2. **Family History** — Stef's own calendar/history in place of Gloranthan years; same year-by-year event-roll structure.
3. **Rune Affinities** — Runes/Elements kept as a mechanical system (not Glorantha-specific in structure); tied into characteristic modifiers as per RQG.
4. **Characteristics** — seven stats: STR, CON, SIZ, INT, POW, DEX, CHA (RQG drops APP in favour of CHA vs RQ3). Derived: Magic Points, Hit Points (+ hit locations), Healing Rate, Damage Bonus, Max ENC, Strike Rank, Skill Category Modifiers.
5. **Occupation** — reflavored occupation table (Warrior, Farmer, Healer, Merchant, etc. — renamed/adjusted to fit Stef's cultures).
6. **Kinship** (RQG's "Cult") — Stef's own pantheon/Kinships in place of Gloranthan cults. Grants Common Rune Magic access (see Magic below).
7. **Personal Skill Bonuses**.
8. **Other Information** — gender, name, age, handedness, scars, family, tribe/settlement/clan, heirloom, starting reputation, movement.
9. **Passions** (RQG) — directed emotional intensities (e.g. Loyalty [X], Hate [Y], Love [Z]), scored by %. Used for Augmentation on relevant skill rolls.
10. **Personality Traits** (Pendragon-style, new addition) — classic opposed trait pairs (e.g. Merciful/Cruel, Honest/Deceitful, Generous/Selfish, Energetic/Lazy, Forgiving/Vengeful, Just/Arbitrary, Modest/Proud, Pious/Worldly, Prudent/Reckless, Temperate/Indulgent, Trusting/Suspicious, Valorous/Cowardly), each pair scored to 20. Not directed at anything specific — used by the GM layer to resolve ambiguous character behavior during encounter resolution (trait roll settles "would this character do X" instead of the player/GM deciding manually). Distinct from and complementary to Passions, not a replacement.
All rolls (chargen, Passions, Traits) must be user-editable/re-rollable after generation, not just at creation — implies a persistent, mutable character sheet, not a one-shot roll script.
This is a distinct subsystem from the flavour roller and GM layer. Treat as its own build phase: (1) chargen engine + editable sheet, (2) GM layer reads from that sheet.
### Subsystem detail (v1 scope)
- **Combat**: full detail — strike ranks, hit locations, damage, resolved round-by-round until the encounter ends.
- **Non-combat skill checks**: simple — single roll against the Ability Results Table, pass/fail with degree of success, brief outcome (no strike ranks/rounds).
- **Spirit Magic**: characters can cast during resolution (combat or skill), consuming MP per RQG rules. Spell list to be digitized by Stef before coding starts (same pipeline as other tables).
- **Rune Magic** (now in scope): tied to Kinship membership (RQG's Common Rune Magic model). Characters spend Rune points from their Kinship's pool. Kinship-specific Rune spell lists to be digitized alongside the Kinship/pantheon content.
- **Opposed checks** (vs NPCs/environment): full Resistance Table where applicable, not flat skill %.
- **Augmentation**: relevant Passions can boost/hinder a skill roll per RQG's augmentation rules.
- **Personality Trait checks**: used mid-resolution when the encounter hinges on ambiguous character behavior rather than a mechanical roll.
- **Out of scope for v1**: Sorcery, Divine Intervention — flag as future extension if needed.
## Scene Types (v1)
- Travel / Wilderness — now driven by Journey Flow (open-road legs), not manual pick.
- Tavern / Settlement — now driven by World Model (district checks), not manual pick.
- Combat / Danger — manual pick, standalone.
- Social / NPC — manual pick, standalone.
## Data Compilation (build step)
- Markdown tables (RQG-derived rules, flavour tables, movement rates, NPC tables, Spirit Magic list, Kinship/Rune Magic lists, Occupation table) are the **authoring source only** — used once at compile time, then deleted. Compiled code becomes the source of truth; no markdown parsing at runtime.
- Personality Trait pair list is separately authored (not sourced from RQG digitization — it's a Pendragon-style addition) but compiled the same way.
- One-time compile script converts markdown → **typed TS data files** (e.g. `movementRates.generated.ts`, `flavourTables.generated.ts`, `runeMagic.generated.ts`), clearly marked as generated/do-not-edit.
- Hand-written TS files hold lookup/resolution logic (e.g. movement rate lookup, table roll functions, Resistance Table lookup) and import the generated data — generated data and hand-written logic stay in separate files.
- Applies to all tables: flavour/encounter tables, movement rates, NPC generation tables, Spirit Magic spell list, Rune Magic spell lists, Occupation table, Personality Trait pairs.
- If source markdown changes later, re-run the compile script to regenerate; no live re-parsing.
## Requirements
- **Source content**: markdown tables in `docs/` are compiled once into typed TS data (see Data Compilation) — app runs entirely off compiled code, no runtime markdown dependency.
- **Roll output**: 34 options per roll, mixed category, tagged by scene type.
- **Save/favourites**: persistent storage across sessions, grouped by scene type, viewable/browsable list.
- **Export**: downloadable file (markdown dump of favourites, grouped by scene type).
- **No chat interface** — this is a quick roll/generate tool, not a conversational assistant.
## World Model
### Locations
- Name, type (Settlement / Wilderness Point / Dungeon / Landmark — maps to scene types), distinguishing features (freeform notes/tags).
- Settlements decompose into **Districts** (see below); other location types are atomic (no sub-zones).
### Districts (settlement sub-locations)
- Name (e.g. "Market Quarter", "Docks", "Noble Ward").
- Danger level: none / low / med / high.
- Own NPC pool, scoped to the district (not the whole settlement).
- Encounter checks are **manually triggered** — user clicks "check" while in a district, tool rolls a chance weighted by that district's danger level (no timer, no fixed cadence).
### Paths
- From location, to location.
- Distance — kilometres (stored value; not travel time directly).
- Mode of travel — walking / running / horseback / etc., per RQG movement/travel rules (already digitized — pull rates from that source rather than hardcoding).
- Travel time computed: distance ÷ movement rate for selected mode, per RQG rules.
- Danger level: none / low / med / high.
- Terrain/notes (freeform).
- If no direct path exists between two locations, the tool routes through intermediate locations and sums travel time across legs (multi-leg journeys in scope for v1).
### NPCs
- Scoped to a location or a district.
- Reuses existing NPC generation fields (six rolled fields; filler NPCs use first three, per existing narrative-app spec).
- Presentation: both a random draw (like flavour rolls) and a browsable list are available — user's choice per interaction.
## Journey Flow (replaces scene-type picking for travel)
1. User selects origin, destination, and mode of travel (walking / running / horseback / etc.).
2. Tool resolves the route (direct path, or multi-leg via intermediate locations) and computes total travel time from summed distance and the RQG movement rate for the selected mode (mode can change per leg if needed).
3. Encounter rolls during travel:
- **Open road legs**: fixed cadence, one roll per day of travel.
- **Within a settlement (district-to-district)**: no fixed cadence — user manually triggers a check per district, chance weighted by that district's danger level.
4. Each triggered roll surfaces the usual mixed flavour options (encounter / cultural detail / NPC quirk), tagged by scene type (derived from location/path type) and danger level (from the path or district).
5. GM layer resolves the picked option as before (full combat, simple skill check, etc.), reading from the active character sheet(s), including Augmentation from Passions and Personality Trait checks where relevant.
6. Resolved results save to favourites as before, still grouped by scene type; danger level tag carries through for filtering.
### Danger check probabilities
- Defaults, tunable in config: none 0%, low 15%, med 35%, high 60%.
## Narration Layer (local LLM, optional)
- Mechanical resolution (rolls, checks, combat) stays deterministic code — no LLM involvement.
- Narration only: mechanical result → prose paragraph, via local Ollama instance (OpenAI-compatible endpoint, `localhost:11434`).
- Model name configurable (whatever's pulled locally).
- Fallback: plain templated text if Ollama unreachable or output unusable. Local 7B14B models are inconsistent — template fallback keeps tool usable regardless.
- Keeps the tool fully local/offline-capable, consistent with self-hosted stack.
## Open Questions (resolve before/during build)
1. Seed source: markdown table files will live in `docs/` at project root once set up. **← structure (headers, column format) still needed once files exist, including new RQG-derived tables (Rune Magic, Occupation, Resistance Table, Ability Results Table)**
2. ~~Export format~~ — resolved: downloadable file.
3. ~~Tagging~~ — resolved: scene type + danger level (low/med/high). Tone dropped — subjective, redundant with scene type.
4. ~~Character stats supply~~ — resolved: full chargen, editable/re-rollable rolls (see Character source note above).
5. ~~Spirit Magic list~~ — resolved: Stef digitizes the Magic book before coding starts. No fallback/task queuing needed here.
6. Homeland/Kinship/Occupation reflavoring — Stef supplies replacement content (own regions, own pantheon/Kinships, adjusted occupation names) alongside digitization; mechanical structure (modifiers, table shape) stays RQG-derived.
7. Personality Trait pair list — confirm final set of opposed pairs and starting-score generation method (rolled vs. assigned) before build.
## Explicitly Out of Scope (v1)
- Fresh OCR/extraction from the physical book
- Chat-style generation
- Category selection separate from scene type
- Sorcery, Divine Intervention (magic systems)
- Using Glorantha as the actual setting — RQG is a mechanics source only