Add comprehensive design and specification documentation (#1)
* Write the Unreal documentation set: design, steps, ideas, decisions and specs Rewrites the design and engineering docs of the two earlier Unity projects (Adventurer Guild, Project Malleable) for an Unreal Engine 5 prototype that builds and proves three things in order: a movement controller, fighting and crafting. Neither earlier core loop is carried; their ideas are catalogued as features with what each needs. - Docs/Design.md: the human summary (pillars, fixed decisions, glossary) - Docs/Steps.md: a ladder of fifteen proofs, the first six in full - Docs/Ideas.md: salvaged ideas from both projects, none scheduled - Docs/Decisions.md: D-01..D-36, open decisions OD-01..OD-08, deferrals - Docs/Spec/: notation, Architecture, Movement, Interaction, Combat, Crafting, Networking, Telemetry, UI, written as Unreal C++ skeletons - CLAUDE.md and README.md for the repository * Add the stat block: one attribute set on every body, every influence an effect Every body (player, enemy, NPC) carries the same UStatBlockAttributeSet, and every outside influence on it is a gameplay effect: base values, buffs, debuffs, ground surfaces, carried weight, gear, being downed. Counters are tags and attributes on the receiver: immunity blocks by tag, resistance scales by attribute, cleanse removes by tag. Same status from several sources: strongest wins; different statuses multiply. - Docs/Spec/Stats.md: the block, effects and their five sources (abilities, areas, surfaces, items, the world), stacking, counters, readers, tests - Movement: the movement component's tagged speed-multiplier map is removed; speed is tuning times the MoveSpeed attribute; the ground surface trace turns mud into an effect; the gym gets mud and ice patches - Combat: the attribute set moves to Stats; enemies and kits carry FStatBlockDefaults; the funnel reads MagicResist and Immune.Damage tags; taunt and mark are statuses - Interaction: carried weight is GE_Encumbered against CarryCapacity - Crafting: class bonuses are the WorkSpeed and WorkQuality attributes; enchantments may grant a holder effect - Steps: step 5 builds the block with mud, a volume and an immunity - Decisions D-37 and D-38; design summary, CLAUDE.md, indexes, worklog
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# Specifications
|
||||
|
||||
These documents are written for an implementer that reads carefully and lifts code, which today means Claude and
|
||||
whoever reviews its pull requests. They are not the human summary; that is [`../Design.md`](../Design.md). Read the
|
||||
summary first, then [`Architecture.md`](Architecture.md), then the document for the system you are touching.
|
||||
|
||||
Each spec is pseudocode plus the reasoning behind it. The pseudocode is C++-shaped Unreal code and it is meant to be
|
||||
implemented, not admired: a class skeleton here is the class we expect to find in `Source/`, with the same name, the
|
||||
same members and the same comments about why. Where a spec says `// pure`, that is a promise about testability the
|
||||
real code is expected to keep.
|
||||
|
||||
## Documents
|
||||
|
||||
| Doc | Owns | Read when |
|
||||
| --- | --- | --- |
|
||||
| [Architecture.md](Architecture.md) | Modules, lifetimes, authority, data assets, gameplay tags, the C++/Blueprint boundary, testing, conventions | Before writing any code |
|
||||
| [Stats.md](Stats.md) | The one stat block every body carries, effects and their sources (abilities, areas, surfaces, items, the world), stacking, counters | Step 5, and anything that changes a number on a body |
|
||||
| [Movement.md](Movement.md) | The character, the movement component, the look model, camera modes, input, the gym level | Steps 3 to 5 |
|
||||
| [Interaction.md](Interaction.md) | The one interaction system every prop uses, carrying, throwing | Step 6, and any prop |
|
||||
| [Combat.md](Combat.md) | Attributes, the one damage funnel, melee hit detection, enemies, abilities, kits, downed and revive | Steps 7 to 11 |
|
||||
| [Crafting.md](Crafting.md) | Families, parts, pieces, traits, substances, the pure rules, stations, activities, the link back to combat | Steps 12 to 15 |
|
||||
| [Networking.md](Networking.md) | The authority model, the responsiveness tiers, prediction posture, persistence and identity seams | Any replicated feature |
|
||||
| [Telemetry.md](Telemetry.md) | The sink, the envelope, the event catalogue | Any feature (every feature emits) |
|
||||
| [UI.md](UI.md) | HUD, prompts, the theme asset, world-space text, localisation | Any screen or prop text |
|
||||
|
||||
## Notation
|
||||
|
||||
```
|
||||
Server*(...) runs on the authority only. Validates the instigator and re-checks every precondition,
|
||||
then mutates. A UFUNCTION(Server, Reliable) RPC or a plain method guarded by HasAuthority().
|
||||
Get* / Preview* side-effect-free query. Safe to call from UI every frame, on any peer.
|
||||
// pure deterministic, no world access, unit-tested, reused by the client preview and the server verdict.
|
||||
On* (delegate) a multicast delegate a service raises. UI subscribes; UI never polls.
|
||||
*_Cosmetic a BlueprintAssignable hook a designer may wire. Never load-bearing.
|
||||
UFooDefinition a UPrimaryDataAsset, authored content.
|
||||
FFooInstance runtime state with identity (an FGuid).
|
||||
FFooRecord the persisted snapshot of an instance. Separate type from the instance on purpose.
|
||||
TAG_Foo_Bar a native gameplay tag, "Foo.Bar", declared once in code. Never a string literal at a call site.
|
||||
```
|
||||
|
||||
Status markers inside a spec:
|
||||
|
||||
```
|
||||
[DECIDED] settled, with the reason. Logged in ../Decisions.md.
|
||||
[PROPOSED] the recommended shape, not yet built on. Becomes [DECIDED] when the step that builds it closes.
|
||||
[SALVAGED] an idea carried over from the two earlier projects, reshaped for this one. See ../Ideas.md.
|
||||
Qn an open question, collected at the end of each document.
|
||||
```
|
||||
|
||||
## Cross-cutting rules
|
||||
|
||||
Every spec obeys these. They are stated once here so no spec has to restate them, and any spec that appears to
|
||||
break one is wrong.
|
||||
|
||||
1. **The server decides. The client asks, and predicts only its own body.** Every mutation has an explicit
|
||||
instigator and is re-validated on the authority, even when the game is being played alone. Standalone and
|
||||
listen-server play are development conveniences; the dedicated server is the real target and nothing may assume
|
||||
it is absent. See [Networking.md](Networking.md).
|
||||
2. **One formula, many consumers.** A preview and the real thing call the same pure function. Damage, coherence,
|
||||
naming, assembly validation, spec matching and interaction prompts all follow this. If a UI-side "fast copy" of a
|
||||
rule ever appears, it is a bug.
|
||||
3. **One funnel per kind of consequence.** All damage goes through one execution. All interactions go through one
|
||||
service. All activity quality goes through one result type. A second path is where rules quietly diverge.
|
||||
4. **Rules are plain C++ in the core module; actors and components adapt them to the world.** The core module knows
|
||||
no `AActor`. This is what makes rules testable without a level and runnable on a headless server.
|
||||
5. **Content is data, addressed by gameplay tag or primary asset id, never by string.** A new sword, enemy or
|
||||
substance is an asset, not a code change. If adding a variant needs code, the model is wrong.
|
||||
6. **Every rejection carries a reason a player can read.** A refused interaction, an illegal assembly, a blocked
|
||||
ability: each returns a reason tag that the prompt or panel shows. Silent refusal is a failed step.
|
||||
7. **Nothing is destroyed silently.** Dropped things persist on the floor, full containers refuse with a reason,
|
||||
thrown things land where they land. The rare deliberate deletion is announced before it happens.
|
||||
8. **Bonuses, never locks.** A class, a level or a piece of gear changes speed, quality or numbers. It never changes
|
||||
what a station accepts or what an assembly permits, and it never grants another class's signature ability.
|
||||
9. **Solo viability is a gate, not a mode.** Every rule is checked against one player. Where it costs a solo player
|
||||
something, the spec says so and names the tuning knob.
|
||||
10. **Derived state is recomputed on load, never trusted from storage.** A rebalance re-scores old items everywhere.
|
||||
11. **Telemetry from the first line.** Every feature emits through the injected sink into a catalogue that exists
|
||||
before there is anyone to measure. Retrofitting emit calls is the tax this avoids.
|
||||
12. **Determinism where it is promised.** Anything generated from a seed uses its own `FRandomStream`, integer
|
||||
coordinates and ordered collections, and never reads physics, time or frame counts. Nothing today is generated
|
||||
from a seed; the rule exists so the first thing that is inherits it.
|
||||
13. **One stat block; every outside influence is an effect on it.** Every body carries the same attribute set.
|
||||
A buff, a debuff, ground, weight, gear and base values are all gameplay effects applied to it; counters are
|
||||
tags and attributes on the receiver. No system keeps its own multiplier. See [Stats.md](Stats.md).
|
||||
|
||||
## How a spec relates to the steps
|
||||
|
||||
[`../Steps.md`](../Steps.md) schedules; these documents specify. A step links to the sections it builds and states
|
||||
what proves it done. If a step and its spec disagree, the spec wins and the step is corrected. A spec that turns out
|
||||
wrong in the building is corrected in the same pull request as the code, under a **What was built, and where it
|
||||
differs** heading at the end of the affected document, so the next reader is not misled by the sketch.
|
||||
Reference in New Issue
Block a user