The scenes were never destroyed. Pressing Play with only Bootstrap open means GameBootstrap loads Systems and the level at runtime, and Unity discards runtime-loaded scenes when Play ends, restoring whatever the Editor had open — Bootstrap alone. The Hierarchy comes back nearly empty. Every scene file was byte-identical to HEAD throughout, still text. Tools > Nightclub Arcadia > Open Game Scenes opens all three with the level active, which is the authoring setup. With them already open, GameBootstrap's LoadIfNeeded skips them and they survive Play. GameSceneWorkflow also captures the scene setup before Play and restores it afterwards as a safety net, skipping the restore when Unity already got it right and ignoring scenes deleted meanwhile — an exception in that callback would leave the Editor in a worse state than the problem it fixes. Toggleable under the same menu. Also fixes `make lock`, which had a self-matching bug since Phase 1: `pgrep -f` on the full binary path matched the very shell running the check, because the pattern appears in its own command line. It reported the Editor as running when nothing was, and would have blocked every make test/build once anything else matched. Now `pgrep -x Unity`, which matches the process name and cannot match the shell. EditMode 42/42, PlayMode 6/6, YarnCheck 9 files / 35 nodes. Co-Authored-By: Claude Opus 5 <[email protected]>
530 lines
26 KiB
Markdown
530 lines
26 KiB
Markdown
# Nightclub Arcadia — agent reference
|
|
|
|
Reference for coding agents (Claude Code and others) working in this repository.
|
|
Human-facing design docs live in `docs/`. This file is the operational one: where
|
|
things are, what is generated, and how to build/test/validate from the terminal.
|
|
|
|
Companion: `AGENTS.md` (pointer file, same content applies).
|
|
|
|
---
|
|
|
|
## 1. Repository layout
|
|
|
|
Repo root is `nightlcub-arcadia` (the typo is in the directory name, not a mistake
|
|
to fix — it is the checkout path). The Unity project is a **subdirectory**:
|
|
|
|
```
|
|
/Users/lennart/Dev/nightlcub-arcadia/ # git root
|
|
├── CLAUDE.md, AGENTS.md # this reference
|
|
├── docs/ # design + plan docs (markdown)
|
|
├── writing/ # writer-facing context — see §4
|
|
├── tools/YarnCheck/ # .NET 9 Yarn compiler/player, no Unity needed
|
|
├── tools/writing/ # voice-sheet sync (md <-> skill_bible.json)
|
|
├── .gitignore # macOS / IDE / toolchain rules
|
|
└── NightclubArcadia/ # ← the Unity project (-projectPath)
|
|
├── .gitignore # Unity rules (anchored, must stay here)
|
|
├── Assets/
|
|
├── Packages/ # incl. embedded dev.yarnspinner.unity
|
|
└── ProjectSettings/
|
|
```
|
|
|
|
**Two `.gitignore` files, deliberately.** The Unity one anchors every rule with a
|
|
leading `/`, so it only works at the root of the Unity project folder. Do not merge them.
|
|
|
|
Unity **6000.5.8f1**, URP 17.5.0, Cinemachine 3.1.7, Input System 1.20.0,
|
|
AI Navigation 2.0.14, ProBuilder 6.1.2, Test Framework 1.7.0. Yarn Spinner is an
|
|
**embedded package** at `NightclubArcadia/Packages/dev.yarnspinner.unity` (plus the
|
|
`textanimator` addon) — it is committed, not resolved from a registry.
|
|
|
|
---
|
|
|
|
## 2. Ground rules
|
|
|
|
1. **Never hand-edit generated `.asset` files.** `Assets/Skills/Definitions/*.asset`,
|
|
`Assets/Skills/SkillDatabase.asset`, `Assets/Candidates/Definitions/*.asset` and
|
|
`Assets/Candidates/CandidateDatabase.asset` are *build output*. Their sources are
|
|
`Assets/Skills/skill_bible.json` and `Assets/Candidates/candidates.json`. Edits are
|
|
overwritten by the Editor setup scripts. Edit the JSON.
|
|
2. **Never hand-edit scene or prefab YAML unless you have read the surrounding block.**
|
|
GUID/fileID surgery in `.unity` / `.prefab` files silently detaches components.
|
|
5. **One Unity instance per project path.** Unity refuses to open a project in batch
|
|
mode while the Editor has it open — a single instance at a time. Always check before
|
|
any `-batchmode` invocation (§3.1).
|
|
6. **Prefer YarnCheck over Unity for dialogue validation.** It is seconds, not minutes,
|
|
and needs no Editor lock (§5.5).
|
|
7. **Do not commit on the user's behalf** unless asked. Unity writes to tracked files
|
|
(`ProjectSettings/*`, `.meta`, scenes) as a side effect of simply being open, so
|
|
`git status` noise is expected and is not always yours.
|
|
|
|
---
|
|
|
|
## 2b. Everyday commands
|
|
|
|
A `Makefile` at the repo root wraps everything in this document. Prefer it — the exact
|
|
invocations live there instead of being retyped.
|
|
|
|
```bash
|
|
make # list targets
|
|
make check # yarn + voice sheets + EditMode — run this before committing
|
|
make test # EditMode suite, with a readable summary
|
|
make yarn # compile every Yarn script (seconds, no Unity)
|
|
make build # macOS player into build/
|
|
make lock # fail if the Editor has the project open
|
|
```
|
|
|
|
`make test` and `make build` refuse to run while the Editor is open, which removes the most
|
|
common way these commands fail confusingly.
|
|
|
|
### Merge driver — one-time per machine
|
|
|
|
`.gitattributes` marks Unity YAML for `unityyamlmerge`. Git needs to be told what that is, or
|
|
it falls back to a line-oriented text merge that can produce a scene Unity still loads and that
|
|
is quietly wrong:
|
|
|
|
```bash
|
|
make merge-driver
|
|
```
|
|
|
|
That points git at `UnityYAMLMerge` inside the Editor bundle (`Contents/Helpers/`, not
|
|
`Contents/Tools/` — the path in most online guides is wrong for Unity 6 on macOS).
|
|
|
|
## 3. Unity from the command line
|
|
|
|
### 3.0 Two different tools, both present
|
|
|
|
| | What it is | Status |
|
|
|---|---|---|
|
|
| `/Users/lennart/.unity/bin/unity` | The **Unity CLI** (v1.0.0-beta.6). Wraps the Editor for CI-shaped work: `build`, `test`, `run`, `open`, plus editor/license/cloud management. | Installed. **Prefer this.** |
|
|
| `/Applications/Unity/Hub/Editor/6000.5.8f1/Unity.app/Contents/MacOS/Unity` | The **Editor executable**. Raw `-batchmode` flags. | Installed. Fallback, for flags the CLI does not expose. |
|
|
|
|
The CLI is **not on `PATH` for non-interactive shells** — `which unity` fails even though the
|
|
binary exists, and `unity doctor` reports `check.binary-on-path warn`. Always invoke it by
|
|
absolute path, or export it:
|
|
|
|
```bash
|
|
export UNITY_CLI="/Users/lennart/.unity/bin/unity"
|
|
export UNITY="/Applications/Unity/Hub/Editor/6000.5.8f1/Unity.app/Contents/MacOS/Unity"
|
|
export UNITY_PROJECT="/Users/lennart/Dev/nightlcub-arcadia/NightclubArcadia"
|
|
```
|
|
|
|
`unity doctor --no-banner` confirms the environment: signed in via OAuth keyring, editor
|
|
`6000.5.8f1` arm64 resolved from `ProjectVersion.txt`. Add `--json` to any command for
|
|
machine-readable output and `--no-banner` to keep logs clean.
|
|
|
|
### 3.1 Always check the lock first
|
|
|
|
Only one Unity instance per project path. Check before any headless run:
|
|
|
|
```bash
|
|
pgrep -fl "Unity.app/Contents/MacOS/Unity" | grep -v AssetImportWorker
|
|
```
|
|
|
|
`AssetImportWorker*` processes are children of the main Editor — filter them out or you will
|
|
misread the check. `Temp/UnityLockfile` and `unity status` are the other two signals.
|
|
|
|
### 3.2 Run tests
|
|
|
|
```bash
|
|
"$UNITY_CLI" test "$UNITY_PROJECT" --no-banner --mode EditMode \
|
|
--output /tmp/editmode-results.xml \
|
|
--report-format both --junit-output /tmp/editmode-junit.xml
|
|
```
|
|
|
|
Useful flags: `--filter <pattern>`, `--retries <0-10>` (reports tests that pass on retry as
|
|
flaky), `--rerun-failed`, `--shard N/M` for parallel CI, `--coverage`, `--timeout <seconds>`.
|
|
|
|
`--mode PlayMode` runs `Assets/Tests/PlayMode` — currently `BootstrapTests`, which loads
|
|
`Bootstrap.unity` and asserts the whole game comes up: both scenes loaded, the player present
|
|
and tagged, the dialogue runner found, both NPCs having resolved their runner across the scene
|
|
boundary, and the reveal camera bound to the player. **If that suite fails, the game does not
|
|
start.**
|
|
|
|
Raw-Editor equivalent, if you need a flag the CLI does not expose:
|
|
|
|
```bash
|
|
"$UNITY" -batchmode -nographics -projectPath "$UNITY_PROJECT" \
|
|
-runTests -testPlatform EditMode \
|
|
-testResults /tmp/editmode-results.xml -logFile /tmp/unity-editmode.log
|
|
```
|
|
|
|
**Do not add `-quit` to a `-runTests` run** — the runner terminates the Editor itself, and
|
|
`-quit` on top of async work can hang it.
|
|
|
|
**Parse the results XML; do not trust the exit code.** Unity's own docs state there is no common
|
|
exit-code definition across the components under test. Read `total`/`passed`/`failed` off the
|
|
root element and list any `test-case` whose `result` is not `Passed`.
|
|
|
|
### 3.3 Build a player
|
|
|
|
```bash
|
|
"$UNITY_CLI" build "$UNITY_PROJECT" --no-banner \
|
|
--target StandaloneOSX -o /tmp/NightclubArcadia.app
|
|
```
|
|
|
|
Without `--execute-method` the CLI uses Unity's built-in build — a Build Profile on Unity 6+, or
|
|
the legacy desktop player flags — and builds whatever scenes `EditorBuildSettings` enables.
|
|
`--profile <name>` selects a profile from `Assets/Settings/Build Profiles` (the profile defines
|
|
the target). `-l/--log-file` overrides the log path; the log streams to console unless `--no-tail`.
|
|
|
|
`make build` uses the entry point below, which is the form to prefer:
|
|
|
|
```bash
|
|
"$UNITY_CLI" build "$UNITY_PROJECT" --no-banner --target StandaloneOSX \
|
|
--execute-method NightclubArcadia.EditorTools.PlayerBuild.BuildMacOS \
|
|
-o "$PWD/build/NightclubArcadia.app"
|
|
```
|
|
|
|
`Assets/Editor/PlayerBuild.cs` reads the enabled scenes from the build settings, asserts
|
|
`Bootstrap.unity` is scene 0 (the player opens scene 0 on launch), honours the `-buildOutput`
|
|
the CLI forwards from `-o`, inspects the `BuildReport`, and calls `EditorApplication.Exit(1)` on
|
|
anything short of `Succeeded`. Without that last part Unity exits 0 on a build that produced
|
|
nothing and CI reports green. `BuildWindows` and `BuildLinux` are there too.
|
|
|
|
Verified: a full macOS build takes about two minutes and produces a ~172MB `.app`.
|
|
|
|
### 3.4 Run an arbitrary Editor method
|
|
|
|
```bash
|
|
"$UNITY_CLI" run "$UNITY_PROJECT" --no-banner --timeout 900 -- \
|
|
-executeMethod SomeClass.SomeStaticMethod
|
|
```
|
|
|
|
**`unity run` owns `-batchmode`, `-nographics`, `-quit` and `-logFile`.** Passing any of them
|
|
after `--` fails with a reserved-flag error. Pass only your own arguments.
|
|
|
|
Output does **not** stream to stdout. The log lands at `$UNITY_PROJECT/Logs/Editor.log` (the
|
|
previous run rotates to `Editor-prev.log`), so tag your `Debug.Log` calls and grep for them:
|
|
|
|
```bash
|
|
grep -n "\[YourTag\]" "$UNITY_PROJECT/Logs/Editor.log" | tail -20
|
|
```
|
|
|
|
This is the mechanism for the JSON → ScriptableObject regeneration in
|
|
`Assets/Editor/SkillSystemSetup.cs` and `CandidateSystemSetup.cs`. Read the actual class and
|
|
method names out of those files before invoking — do not guess them.
|
|
|
|
## 4. The dialogue content pipeline
|
|
|
|
### 4.1 Where content lives
|
|
|
|
```
|
|
NightclubArcadia/Assets/Dialogue/
|
|
├── NightclubArcadia.yarnproject # globs **/*.yarn — new files need no registration
|
|
├── Common.yarn # every <<declare>>, project-wide. Never played.
|
|
├── Commentary.yarn # passive skill barks, one node per (context, skill)
|
|
├── Scenes/ SC101, SC102, Debug
|
|
├── Characters/ Bartender, Bradford Kane, Chevalier Cassian Thal
|
|
└── Objects/ Chair
|
|
```
|
|
|
|
The writer-facing context sits at the repo root, outside `Assets/` so it gets no `.meta` files:
|
|
|
|
The scenes are split three ways, loaded additively from `Bootstrap`:
|
|
|
|
```
|
|
Assets/Scenes/
|
|
├── Bootstrap.unity entry point; GameBootstrap loads the rest
|
|
├── Systems/Systems.unity dialogue, skills, UI, camera rig, and the PLAYER
|
|
└── Levels/
|
|
├── SC101_ConferenceHall.unity geometry, light, navmesh, reveal cameras
|
|
└── SC101_ConferenceHall/ baked data for that scene (NavMesh asset)
|
|
```
|
|
|
|
`Bootstrap` must stay index 0 in the build settings. The level is made the active scene after
|
|
load, so new objects and lighting land there rather than in Bootstrap.
|
|
|
|
**Working in the Editor.** `Tools ▸ Nightclub Arcadia ▸ Open Game Scenes` opens all three at
|
|
once, with the level active. Do that before pressing Play.
|
|
|
|
If you press Play with only `Bootstrap` open, `GameBootstrap` loads the other two *at runtime*,
|
|
and Unity discards runtime-loaded scenes when Play ends — the Hierarchy comes back with Bootstrap
|
|
alone and it looks as though the scenes were destroyed. **Nothing is written to disk**; it is the
|
|
Hierarchy, not the files. With all three already open, `GameBootstrap` skips loading them
|
|
(`LoadIfNeeded`) and they are still there afterwards. `GameSceneWorkflow` also captures and
|
|
restores the scene setup around Play as a safety net; toggle it under the same menu.
|
|
|
|
```
|
|
writing/
|
|
├── README.md session entry point
|
|
├── STYLE.md register, tone moves, ban list, branching rules ← read first
|
|
├── YARN-PRIMER.md the one page of syntax a writer needs
|
|
├── GLOSSARY.md in-world and project vocabulary
|
|
├── voices/ the eleven skill voices, one sheet each (generated — see §4.7)
|
|
├── characters/ named NPCs, dialogue-facing
|
|
├── scenes/ per-scene briefs: intent, beats, checks, constraints
|
|
└── lore/ candidates.md — the three identity readings and their hard rules
|
|
```
|
|
|
|
Design material deeper than this lives in Obsidian, not the repo:
|
|
`~/Obsidian-Vaults/obsidian-vault/Projects/Active/NightclubArcadia/`. **`Ground Truth.md` there
|
|
is the solution document — never copy, quote, or summarise it into this repo**, including into
|
|
commit messages. The game's premise is that the protagonist's identity never resolves.
|
|
|
|
**Dialogue is already plain text in the repo.** It is authored in `.yarn` files, not inside
|
|
the Unity Editor, and any new `.yarn` file dropped anywhere under `Assets/Dialogue/` is picked
|
|
up automatically by the `**/*.yarn` glob in the `.yarnproject`. Adding a scene or a character
|
|
requires **no C#, no Unity, and no `.yarnproject` edit**.
|
|
|
|
The one caveat: the `.yarnproject` also stores `editorOptions.yarnScriptEditor.projectGraphPositions`
|
|
— per-file x/y coordinates for the Yarn graph editor. These churn whenever anyone opens the
|
|
graph view. Treat that block as editor state; do not fight over it in review.
|
|
|
|
### 4.2 The variable contract
|
|
|
|
Every variable used anywhere must be `<<declare>>`d in `Common.yarn`. This is the whole point
|
|
of that file: the compiler then catches `$reputaton` instead of silently creating a second
|
|
variable at runtime. Adding a scene flag means adding a `<<declare>>` line — that is a writer
|
|
action, not a code action.
|
|
|
|
Naming families that are load-bearing (enforced or lint-checked):
|
|
|
|
| Family | Meaning | Checked by |
|
|
|---|---|---|
|
|
| `$self_lean_<candidate_id>` | identity-lean counter | `CandidateYarnLintTests` — id must exist in `candidates.json` |
|
|
| `$found_<clue_id>` | clue coverage | `CandidateYarnLintTests` — id must exist in `candidates.json` |
|
|
| `$visited_<location_id>` | first arrival | convention; `unlockFlag` in `candidates.json` may only name these |
|
|
| `$errand_<errand_id>` | errand state | same |
|
|
| `$seen_<scene_id>` | scene witnessed | convention |
|
|
| `$check_*` | written by `<<check>>` | **never `<<set>>` by hand** |
|
|
|
|
### 4.3 Writer-facing Yarn vocabulary
|
|
|
|
Commands and functions registered by game code, i.e. the full set a writer may use:
|
|
|
|
| Syntax | Defined in | Notes |
|
|
|---|---|---|
|
|
| `<<check <skill> <Band>>>` | `Skills/Yarn/YarnSkillCommands.cs` | writes `$check_result`, `$check_roll`, `$check_total`, `$check_dc`, `$check_modifier`, `$check_degree`, `$check_skill` |
|
|
| `<<skill_mod <source> <skill> <n>>>` | same | `source` = bucket key, e.g. `scene`, `drunk` |
|
|
| `<<clear_skill_mods <source>>>` | same | |
|
|
| `skill_rank(<skill>)` | same | function |
|
|
| `top_skill(a, b, …)` | `Dialogue/DialougueSkillComparison.cs` | **returns its FIRST argument on a tie** — argument order is a design decision, not incidental |
|
|
| `alias_name(<name>)` | `Dialogue/AliasNameGenerator.cs` | deterministic; `$player_alias` is a smart variable derived from it |
|
|
| `<<enter_environment>>` | `Dialogue/DialogueTransitionController.cs` | hands control back to the world |
|
|
|
|
(The filename `DialougueSkillComparison.cs` is misspelled in the repo. Left alone deliberately —
|
|
renaming it changes a `.meta` GUID. Fold it into a rename batch, not a drive-by fix.)
|
|
|
|
### 4.4 Flow control discipline
|
|
|
|
`<<detour>>` returns to the caller; `<<jump>>` does not and clears the return stack.
|
|
The rule the existing files follow, and which new content must follow:
|
|
|
|
- A node started directly by `DialogueInteractable` has nothing waiting on it → its branches
|
|
`<<jump>>`, and each destination calls `<<enter_environment>>` itself.
|
|
- A sub-conversation that must resume its caller → `<<detour>>`, and **never** `<<jump>>` inside it.
|
|
|
|
Nodes sharing a `title:` form a **node group**; Yarn runs the most specific variation whose
|
|
`when:` conditions pass. Prefer adding a variation over growing an `<<if>>` chain.
|
|
|
|
### 4.5 How content reaches Unity
|
|
|
|
There is **no manual sync step** for dialogue. The chain is:
|
|
|
|
```
|
|
.yarn file saved → Unity's YarnProjectImporter recompiles the .yarnproject on focus
|
|
→ DialogueRunner plays the new nodes on next Play
|
|
```
|
|
|
|
The only things that need a human/Editor step are the *data* files, not the dialogue:
|
|
|
|
```
|
|
skill_bible.json → Tools ▸ Nightclub Arcadia ▸ (SkillSystemSetup) → Skills/Definitions/*.asset + SkillDatabase.asset
|
|
candidates.json → Tools ▸ Nightclub Arcadia ▸ (CandidateSystemSetup) → Candidates/Definitions/*.asset + CandidateDatabase.asset
|
|
```
|
|
|
|
Those setup scripts also register a `[DidReloadScripts]` callback, so in practice the assets
|
|
regenerate on script reload too. The JSON is the source of truth either way.
|
|
|
|
### 4.6 The voice-sheet pipeline
|
|
|
|
The Skill Bible prose used to live as a `\n`-escaped string inside `skill_bible.json`, which no
|
|
writer could comfortably read or revise. It now lives in `writing/voices/*.md`, and truth flows
|
|
in opposite directions for the two halves of each sheet:
|
|
|
|
```
|
|
PROSE writing/voices/<id>.md -> skill_bible.json -> Skills/Definitions/*.asset
|
|
MACHINE skill_bible.json -> the "Machine fields" table in the .md
|
|
```
|
|
|
|
```bash
|
|
python3 tools/writing/sync_voices.py --check # verify; exit 1 on drift
|
|
python3 tools/writing/sync_voices.py --to-json # fold edited prose into the bible
|
|
python3 tools/writing/sync_voices.py --to-md # refresh the machine table from the bible
|
|
```
|
|
|
|
`tools/writing/voicelib.py` does the parsing and guarantees `notes -> dict -> notes` is identity
|
|
for all eleven skills. `--to-json` on unchanged sheets is byte-identical, so it is safe to run
|
|
at any time.
|
|
|
|
`VoiceSheetSyncTests` (EditMode) enforces the same invariant, so drift fails the build instead
|
|
of silently shipping a voice that no longer matches the sheet the next scene is written against.
|
|
It deliberately does not reimplement the markdown parser in C# — it asserts the shipped prose
|
|
appears verbatim in the sheet, which catches drift in either direction with no second parser to
|
|
keep in step.
|
|
|
|
### 4.7 Code-owned vs writer-owned
|
|
|
|
| Writer-owned (edit freely, no Unity) | Code-owned (needs an engineer) |
|
|
|---|---|
|
|
| `writing/**` | `Assets/Scripts/**/*.cs` |
|
|
| `Assets/Dialogue/**/*.yarn` | `Assets/Editor/**/*.cs` |
|
|
| `<<declare>>` lines in `Common.yarn` | Yarn command/function *implementations* |
|
|
| `writing/voices/*.md` (prose → the bible, §4.6) | `Assets/Skills/skill_bible.json` (machine fields) |
|
|
| `Assets/Candidates/candidates.json` (`status`, `sceneId`, `notes`, `unlockFlag`) | `.asset`, `.prefab`, `.unity`, `.meta` |
|
|
| `docs/` writing docs | `ProjectSettings/**` |
|
|
|
|
**A writer must never need to open Unity or touch C# to write or revise a scene, a character,
|
|
or a branch.** If a writing task requires either, that is a pipeline bug — file it.
|
|
|
|
---
|
|
|
|
## 5. Validation
|
|
|
|
### 5.1 EditMode test suite
|
|
|
|
`NightclubArcadia/Assets/Tests/EditMode/` — 7 files, ~870 LOC, two assemblies:
|
|
|
|
- `NightclubArcadia.Skills.Tests` — `SkillRosterTests`, `SkillCheckSystemTests`, `DcBandTests`,
|
|
`CommentaryPickerTests`, `CandidateRegistryTests`, `CandidateYarnLintTests`
|
|
- `NightclubArcadia.Locomotion.Math.Tests` — `MotionInputMathTests`
|
|
|
|
`CandidateYarnLintTests` is a **regex lint over Yarn source**: it asserts every
|
|
`$self_lean_*` / `$found_*` in any `.yarn` file resolves to a real id in `CandidateDatabase`.
|
|
It is a tripwire, not a proof.
|
|
|
|
### 5.2 PlayMode suite
|
|
|
|
`Assets/Tests/PlayMode/BootstrapTests.cs`. Verifies the scene split at runtime — the thing no
|
|
EditMode test can see, because none of the runtime resolution has happened yet.
|
|
|
|
### 5.3 Voice-sheet sync
|
|
|
|
```bash
|
|
python3 tools/writing/sync_voices.py --check
|
|
```
|
|
|
|
Seconds, no Unity. Run it after touching `writing/voices/*.md` or `skill_bible.json` (§4.6).
|
|
|
|
### 5.4 One-click in-Editor run
|
|
|
|
`Tools ▸ Nightclub Arcadia ▸ Run Skill EditMode Tests` (`Assets/Editor/SkillSystemTestRunner.cs`).
|
|
|
|
### 5.5 YarnCheck — validate dialogue without Unity ⭐
|
|
|
|
`tools/YarnCheck` is a .NET 9 console app that compiles **and plays** the Yarn scripts using
|
|
the exact Yarn Spinner DLLs shipped in `Packages/dev.yarnspinner.unity/Runtime/DLLs`, so it
|
|
can never drift from the compiler the game uses. This is the fastest correctness signal in the
|
|
repo and it needs no Editor lock. Use it on every dialogue change.
|
|
|
|
```bash
|
|
cd /Users/lennart/Dev/nightlcub-arcadia/tools/YarnCheck
|
|
|
|
# compile everything — exit 1 + diagnostics on failure
|
|
dotnet run -- ../../NightclubArcadia/Assets/Dialogue
|
|
|
|
# compile, then play a node, choosing option 2, 2, 0, 0
|
|
dotnet run -- ../../NightclubArcadia/Assets/Dialogue Bartender_Talk 2 2 0 0
|
|
```
|
|
|
|
Leftover picks restart the node — that deliberately models the player walking away and coming
|
|
back, which is how these scenes are actually played.
|
|
|
|
**What it does not model** (read traces with this in mind): Unity commands are printed, not
|
|
executed, so `<<check>>` / `<<enter_environment>>` / `<<skill_mod>>` are stepped over and
|
|
`$check_result` stays `false` — every check-gated branch shows its *failure* side. Yarn
|
|
functions are stubbed: `top_skill` mirrors the real first-argument-wins tie behaviour,
|
|
`alias_name` returns a placeholder, `skill_rank` returns 0. Variables reset to their
|
|
`<<declare>>` defaults each run; there is no save state.
|
|
|
|
Requires the .NET 9 SDK. `tools/YarnCheck/bin/` and `obj/` are currently **committed** and
|
|
should not be — see `docs/restructure-plan.md`.
|
|
|
|
---
|
|
|
|
## 6. Code conventions
|
|
|
|
- Namespaces: `NightclubArcadia.<Area>` — `Skills`, `Dialogue`, `UI`, `UI.Dialogue`,
|
|
`UI.HUD`, `UI.Menu`, `Cinematics`, `Interaction`, `Player`, `Player.Math`.
|
|
**Exception:** `NPCStandIn.cs` sits in the global namespace. That is a scaffold tell, not a
|
|
convention to copy — new runtime types always get a namespace.
|
|
- **Assemblies.** No project code is in `Assembly-CSharp` any more:
|
|
|
|
| Assembly | Covers | Depends on |
|
|
|---|---|---|
|
|
| `NightclubArcadia.Core` | `Scripts/Core` | nothing |
|
|
| `NightclubArcadia.Locomotion.Math` | `Scripts/Player/Math` | nothing |
|
|
| `NightclubArcadia.Skills` | `Scripts/Skills` | Yarn, TMP, uGUI |
|
|
| `NightclubArcadia.Game` | the rest of `Scripts` | Core, Skills, Math, StarterAssets, packages |
|
|
| `StarterAssets` | vendored template code | Input System |
|
|
|
|
`Game` is one assembly rather than one per area because the areas are genuinely cyclic:
|
|
UI ↔ Dialogue, UI ↔ Player and UI ↔ Camera, via `UILayerBootstrap`, `PlayerControlLock` and
|
|
`CharacterPanelView`. Splitting further means moving those three, not just adding asmdefs.
|
|
The `.asmdef` at `Scripts/` covers everything beneath it except folders with their own.
|
|
- Comments in this codebase carry *decision history* — why a flag is written at the bottom of a
|
|
node rather than the top, why four voices in SC-101 breaks the three-voice cap on purpose.
|
|
Match that density. Do not strip such comments as "noise"; they are the design record.
|
|
- Editor-only code lives under `Assets/Editor/`. Anything there is stripped from builds and may
|
|
reference `UnityEditor`.
|
|
|
|
---
|
|
|
|
## 7. Traps
|
|
|
|
1. **A scene silently turns binary if it holds an object that prefers binary serialization.**
|
|
This one cost real time. `DialogueTest.unity` was binary for weeks despite the project being
|
|
set to Force Text, because the `NavMeshSurface` held its baked `NavMeshData` *embedded in the
|
|
scene* rather than as an asset. One such object forces the whole file to binary, and no amount
|
|
of re-saving or toggling the setting fixes it — Force Text only converts at the moment the
|
|
setting changes. The fix is to extract the data to its own asset, which is what baking from
|
|
the NavMeshSurface inspector produces anyway. If a scene goes binary again, bisect: move the
|
|
roots one at a time into a fresh scene and see which one yields a binary file.
|
|
|
|
2. **Serialized references cannot cross a scene boundary.** Unity nulls them. The split cost
|
|
exactly seven: both NPCs' `dialogueRunner`, `dialogueUI` and `player`, plus the reveal
|
|
camera's tracking target. All are resolved at runtime now — `SceneServices.Resolve` for the
|
|
first, `CinemachineFollowsPlayer` for the last. Anything new that a level object needs from
|
|
Systems must follow that pattern, and `BootstrapTests` is where you prove it works.
|
|
|
|
3. **The player's NavMeshAgent outlives the NavMesh.** The player is in Systems, the NavMesh is
|
|
baked into the level, so there is a window during load where the agent exists and is not on a
|
|
mesh. `ResetPath`, `isStopped` and `SetDestination` all log errors in that state. Guard on
|
|
`agent.isOnNavMesh`, not just a null check.
|
|
|
|
4. **An asmdef assembly cannot reference `Assembly-CSharp`.** Only the reverse. This is why
|
|
giving the runtime code assemblies had to include `StarterAssets`, which had none and which
|
|
four runtime files use — without that, everything referencing it would have stopped compiling.
|
|
Keep it in mind before adding loose scripts outside an asmdef folder.
|
|
|
|
5. **The one-shot scene builders under `Assets/Editor` are disabled, on purpose.**
|
|
`Set Up UI Layer`, `Set Up Player Control & Camera` and `Set Up Yarn Sandbox Scene` were
|
|
written for the single-scene layout. After the split they do damage rather than work: pointed
|
|
at a level they rebuild systems content that already exists in `Systems.unity`, and pointed at
|
|
Systems, `PlayerControlSetup` drags navigation in and silently turns that scene binary (trap 1
|
|
again). Both were observed. They now return early via `LegacySceneSetup.Blocked` and are kept
|
|
only as a record of how the wiring works until prefabs replace them (Phase 5).
|
|
`Set Up Skill System` and `Set Up Candidate System` still work — they regenerate assets from
|
|
JSON and touch no scene. **To run the game, open `Bootstrap.unity` and press Play.**
|
|
|
|
6. **`unity run` reserves `-batchmode`, `-nographics`, `-quit`, `-logFile`.** Passing any of them
|
|
after `--` is a hard error, and its output does not reach stdout — read `Logs/Editor.log` (§3.4).
|
|
|
|
7. **`-quit` with `-runTests`** hangs or truncates. Omit it (§3.2).
|
|
|
|
8. **Exit code alone is not a test verdict.** Parse the results XML (§3.2), which is what
|
|
`make test` does.
|
|
|
|
9. **`which unity` fails** in non-interactive shells even though the CLI is installed. Use the
|
|
absolute path `/Users/lennart/.unity/bin/unity` (§3.0).
|
|
|
|
10. **Editor lock** — check before every headless run, filtering out `AssetImportWorker` children.
|
|
`make test` and `make build` do this for you (§2b).
|
|
|
|
11. **Regenerated assets** — hand edits to `Skills/Definitions/*`, `Candidates/Definitions/*`
|
|
and the two `*Database.asset` files are silently discarded (§2.1).
|
|
|
|
12. **`top_skill` ties** resolve to the first argument. Reordering arguments changes the game.
|
|
|
|
13. **`Bootstrap` must stay index 0** in the build settings, and all three scenes must be listed,
|
|
or `LoadSceneAsync` by name fails at runtime with nothing but a console error.
|