Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
236fbf537f | ||
|
|
588722fe7a | ||
|
|
7f2f06bf42 | ||
|
|
6db3cc0873 | ||
|
|
8938dd918c | ||
|
|
742bfd3898 | ||
|
|
87069a30e8 | ||
|
|
3f5c5ce442 | ||
|
|
28fcd96b59 | ||
|
|
e984129597 | ||
|
|
75dea2e7c2 | ||
|
|
09d07452af | ||
|
|
f5ee2b7408 | ||
|
|
5980d329e8 | ||
|
|
6052ea391b | ||
|
|
4e8665e25b | ||
|
|
b945eb6e8f | ||
|
|
03c13c716a | ||
|
|
0a9c78cb30 | ||
|
|
abe5c3f933 | ||
|
|
140ab42536 | ||
|
|
8bdfd0176e | ||
|
|
4e23cc3ad5 | ||
|
|
8d32a8cac0 | ||
|
|
2445ddab34 | ||
|
|
8cacd510bf | ||
|
|
bf383aa0fa | ||
|
|
4bbc576b7f | ||
|
|
542b1264da | ||
|
|
56f4833c8e | ||
|
|
1b31dfdbe4 | ||
|
|
5158efc467 | ||
|
|
ac0cf35cfb | ||
|
|
6655edb5c6 | ||
|
|
60f389c16b | ||
|
|
3084145031 | ||
|
|
34bca89885 | ||
|
|
b53e8a67f9 | ||
|
|
cdfd57fb65 | ||
|
|
5bab6754c0 | ||
|
|
7dd8251a0c | ||
|
|
8e113a4110 | ||
|
|
32c9c45605 | ||
|
|
e4065c2068 | ||
|
|
e7d733bc93 | ||
|
|
93da916f90 | ||
|
|
e3ec4a356a | ||
|
|
55339f23c1 | ||
|
|
a51dae2c33 | ||
|
|
6824dd76a1 | ||
|
|
fde4aa4d0a | ||
|
|
ebb36cf0cb | ||
|
|
724e8b183f | ||
|
|
e4e1566e55 | ||
|
|
0aacee6518 | ||
|
|
215b843b67 | ||
|
|
53d9dc3f78 | ||
|
|
98f2f95f07 | ||
|
|
a677a95d13 | ||
|
|
1afd97c72d | ||
|
|
6ca8bcf034 | ||
|
|
13eb4e0c87 | ||
|
|
8fac1b47cf | ||
|
|
b79e30e9df | ||
|
|
1e761e5a76 | ||
|
|
3eb2ae5e3c | ||
|
|
888897cac9 | ||
|
|
988d431da8 | ||
|
|
87e1c9e7a5 | ||
|
|
480a1ba05f | ||
|
|
43c17ce3c0 | ||
|
|
03f4d4bc49 | ||
|
|
15f9117517 | ||
|
|
78267918bf | ||
|
|
ea3860a60c | ||
|
|
b721f189c5 | ||
|
|
634b1f75a3 | ||
|
|
36583921cf | ||
|
|
3866b0ae01 | ||
|
|
8c2761427f | ||
|
|
2b07913481 | ||
|
|
768dd94bfc | ||
|
|
f516d3e796 | ||
|
|
f95a685934 | ||
|
|
05cb6a43cb | ||
|
|
343b3348c2 | ||
|
|
ad91e71eeb | ||
|
|
6e04c59589 | ||
|
|
e21ff30783 | ||
|
|
5b306b1c52 | ||
|
|
f09a5e7937 | ||
|
|
383f601bc9 | ||
|
|
c46db76d85 | ||
|
|
1a5f2863b0 | ||
|
|
a6fd77068a | ||
|
|
4ed73fcc7b | ||
|
|
c0ddce79e8 | ||
|
|
0c7c9a2b23 | ||
|
|
b94b87d096 | ||
|
|
5dec59355a | ||
|
|
fe3ba3054e | ||
|
|
88a0793bba | ||
|
|
1d4f718376 | ||
|
|
7a5b902797 | ||
|
|
2d67e14e52 | ||
|
|
2443343f2b | ||
|
|
3e41cda591 | ||
|
|
84bc4137cb | ||
|
|
5dc3ef0f4c | ||
|
|
6f3facff43 | ||
|
|
51f537720f | ||
|
|
08e9044593 | ||
|
|
ad14410337 | ||
|
|
876fb59882 | ||
|
|
979c6ad789 | ||
|
|
1921bfae91 | ||
|
|
78485259b7 | ||
|
|
fef3af44c0 | ||
|
|
bfe3c5a701 | ||
|
|
46f1e41874 | ||
|
|
f48d87c84f | ||
|
|
a9658ecd81 | ||
|
|
b8507df346 | ||
|
|
60bf9a8ef6 | ||
|
|
b51bdc8efe | ||
|
|
a7b2852253 | ||
|
|
4bb2896819 | ||
|
|
f5b8ef2e70 | ||
|
|
0044f5f065 | ||
|
|
4bc6390e9f | ||
|
|
fb3da52438 | ||
|
|
d7220c1a0a | ||
|
|
31574b8d82 | ||
|
|
b60f67548d | ||
|
|
a57b86cd48 | ||
|
|
dfe5056303 | ||
|
|
58cf12fd1d | ||
|
|
7150bd7673 | ||
|
|
12e70357c5 | ||
|
|
7dc3d3c873 | ||
|
|
cd5e0e0ff7 | ||
|
|
8ce62e7556 | ||
|
|
24c939d388 | ||
|
|
48082c3dfa | ||
|
|
03c355e7c1 | ||
|
|
15ab8c686d | ||
|
|
ab9c67d266 | ||
|
|
7c1a4bfd69 | ||
|
|
e7ee177c31 | ||
|
|
c1cd4898e8 | ||
|
|
e805a91ad3 | ||
|
|
7315166361 | ||
|
|
f6aa26e6ca | ||
|
|
26e6dd6413 | ||
|
|
8f65751975 | ||
|
|
9692f262f1 | ||
|
|
98c31fe44c | ||
|
|
daf0926d91 | ||
|
|
f71a36faa1 | ||
|
|
37f925c7ad | ||
|
|
74dc1fdc1d | ||
|
|
3f335481d6 | ||
|
|
af923b1483 | ||
|
|
3a74cedee0 | ||
|
|
844e39246a | ||
|
|
7c48183410 | ||
|
|
64590fe797 | ||
|
|
49bfccc808 | ||
|
|
7401d4bca0 | ||
|
|
e79a37beba | ||
|
|
fbd2ab2cb3 | ||
|
|
9b8f513f9f | ||
|
|
ad12fdad7e | ||
|
|
87612f2057 | ||
|
|
ffd98daf08 | ||
|
|
f811e6054d | ||
|
|
9d37e0a1c5 | ||
|
|
883626a067 | ||
|
|
b5d4d5fb87 | ||
|
|
fac9b002c3 | ||
|
|
aa51ee331e | ||
|
|
86332cd401 | ||
|
|
76eb5e3a79 | ||
|
|
f4a53e2d4f | ||
|
|
806fe78688 | ||
|
|
6a3066d763 | ||
|
|
f4cce28454 | ||
|
|
7d39523e49 | ||
|
|
7e084192ce | ||
|
|
6c367e8c7e | ||
|
|
659b0e35b2 | ||
|
|
28f2a48811 | ||
|
|
64f0c6a198 | ||
|
|
8b0845bcce | ||
|
|
4cca2af2dd | ||
|
|
99f390078d | ||
|
|
a9fe6b9944 | ||
|
|
d4201caa62 | ||
|
|
6100d19a25 | ||
|
|
a7ca698f73 | ||
|
|
26d64baa01 | ||
|
|
151630e957 | ||
|
|
1b319c48b7 | ||
|
|
a478bfbf35 | ||
|
|
702f54ae53 | ||
|
|
bbf4693475 | ||
|
|
d7b0203502 | ||
|
|
da876e3545 | ||
|
|
1ee32215b0 | ||
|
|
2624b54b23 | ||
|
|
85d67ff8b1 | ||
|
|
47c57ddc76 | ||
|
|
3c07525351 | ||
|
|
4a672025a2 | ||
|
|
6acbc2b3a4 | ||
|
|
d7e523f63c | ||
|
|
26ecb93fd4 | ||
|
|
8997f59819 | ||
|
|
79270795a8 | ||
|
|
dc4bc6c511 | ||
|
|
c8b98fdb30 | ||
|
|
d96f53c285 | ||
|
|
b39fee6176 | ||
|
|
c2c93c28a5 | ||
|
|
3030c60ae1 | ||
|
|
cb65a5a601 | ||
|
|
06a4f5479f | ||
|
|
8032999a4d | ||
|
|
8582e496ff | ||
|
|
ae326ce970 | ||
|
|
00faf45e83 | ||
|
|
41d3511fc7 | ||
|
|
1efa5bc7d9 | ||
|
|
4745adeb13 | ||
|
|
15deb58b73 | ||
|
|
6a0bf70fdf | ||
|
|
f458551594 | ||
|
|
8f228b0c56 | ||
|
|
d307746351 | ||
|
|
d872034388 | ||
|
|
d8cfddbf89 | ||
|
|
d513e0b82c | ||
|
|
7efb5024ec | ||
|
|
a231f7588a | ||
|
|
3989d7b2e5 | ||
|
|
0eb17e3bb4 | ||
|
|
a7daae8a14 | ||
|
|
3f4a8e5f9f | ||
|
|
91b10e0f94 | ||
|
|
4943dbff16 | ||
|
|
b24d0aeb61 | ||
|
|
a27e8cb74c | ||
|
|
2a4e0d1998 |
No files matched your search
@@ -0,0 +1,210 @@
|
|||||||
|
---
|
||||||
|
name: gig-line-blender-asset-contract
|
||||||
|
description: Use when adding, rebuilding, auditing or fixing any Gig Line Blender-authored character, NPC base, gear, prop or venue geometry. Covers the 13-bone npc_base contract, five tint slots, triangle/vertex budgets, gear attach space, 256² texture/AO rules, headless flatpak Blender commands, and the inspect/audit/fit/proportion/capture gates. Do not use for pure GDScript/UI work or release publishing.
|
||||||
|
compatibility: Gig Line repository checkout. Godot 4.7.2 on PATH; Blender 5.2 LTS via the org.blender.Blender flatpak for build/bake/render. Audit, fit, inspect and proportion checks are stdlib-only.
|
||||||
|
metadata:
|
||||||
|
project: Gig Line
|
||||||
|
domain: 3d-assets
|
||||||
|
version: "1.0.0"
|
||||||
|
triggers: Blender, NPC, character, gear, prop, npc_base, GLB, AO, texel, asset rebuild, fit, audit
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gig Line Blender Asset Contract
|
||||||
|
|
||||||
|
Use this skill for any change that touches:
|
||||||
|
|
||||||
|
- `assets/models/blender/**`
|
||||||
|
- `assets/models/generated/npc_base.glb`
|
||||||
|
- `assets/textures/npc/**`
|
||||||
|
- `tools/blender_assets.py`, `tools/npc_pipeline.py`, `tools/bake_ao.py`,
|
||||||
|
`tools/npc_proportions.py` or `tools/npc_baseline.py`
|
||||||
|
- Blender-authored venue/prop geometry
|
||||||
|
|
||||||
|
The project contract and measured gates override any generic Blender advice.
|
||||||
|
There is **no Blender MCP** in this project. Blender runs headless through the
|
||||||
|
flatpak CLI; the `blender-skills` pack is optional, local guidance only, and
|
||||||
|
gitignored.
|
||||||
|
|
||||||
|
## Read first
|
||||||
|
|
||||||
|
From the repository root, read the relevant sources rather than relying on this
|
||||||
|
skill alone:
|
||||||
|
|
||||||
|
1. `AGENTS.md` — required workflow and verification.
|
||||||
|
2. `TODO.md` and `MEMORY.md` — current track, claimed tasks, known gotchas.
|
||||||
|
3. `docs/blender-assets.md` — library contract and command reference.
|
||||||
|
4. `docs/asset-tooling.md` — exact rebuild/bake/capture commands.
|
||||||
|
5. `docs/npc-visual-review.md` and `docs/3d-model-review.md` — accepted limits,
|
||||||
|
scorecards and known blockers.
|
||||||
|
6. `docs/known-limitations.md` — limits that must not be silently “fixed” into
|
||||||
|
contract changes.
|
||||||
|
|
||||||
|
The contract lives in code and docs, not in this skill. If they disagree, stop
|
||||||
|
and resolve the source of truth before editing assets.
|
||||||
|
|
||||||
|
## Non-negotiable contract
|
||||||
|
|
||||||
|
| Area | Rule |
|
||||||
|
| --- | --- |
|
||||||
|
| Mesh root | `npc_base` mesh node, skinned under `Rig`; preserve the limb node names. |
|
||||||
|
| Bones | 13 contract bones: `Head`, `HipL/R`, `KneeL/R`, `ShoulderL/R`, `ElbowL/R`, `HandL/R`, `Spine`, `Neck` (parent/helper bones must not change the contract). |
|
||||||
|
| Tint slots | `skin`, `cloth`, `pants`, `accessory`, `hair`. |
|
||||||
|
| Orientation | Front is Blender −Y / Godot +Z; feet grounded near y = 0; metres. |
|
||||||
|
| Library budget | **8,000 triangles / 8,000 glTF vertices** per character (`CHAR_MAX_TRIS`/`CHAR_MAX_VERTS`). |
|
||||||
|
| Frozen fallback | `npc_base` keeps **6,000 tris / 4,000 verts**. Do not silently raise or lower it. |
|
||||||
|
| Gear budget | **1,500 triangles** per gear piece; authored in attach-point space. |
|
||||||
|
| Prop budget | **4,000 triangles** per prop. |
|
||||||
|
| Gear attach | Use the catalogued attach points; verify with `fit`. Runtime `GEAR_OFFSETS` (lanyard forward, firefighter helmet up) and `HAND_ITEM_FORWARD` are intentional, not asset-contract changes. |
|
||||||
|
| Textures | 256² albedo/normal/ORM maps. Cloth and pants use `NPC_FABRIC_UV_SCALE = (4,4,1)` and **skip** baked per-character AO; skin/hair/gear keep baked AO. |
|
||||||
|
| CI | Offline and skill-free. Committed GLBs/AO/maps must make CI pass without Blender or MCP. |
|
||||||
|
| Licensing | No new asset source without ADR-0015: CC0, or CC-BY with attribution in `CREDITS.md`; record provenance. |
|
||||||
|
|
||||||
|
Do not change the renderer, engine baseline, runtime dependency graph or the
|
||||||
|
`npc_base` contract as part of an asset pass.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### 1. Preflight and claim
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status --short
|
||||||
|
git log --oneline -5
|
||||||
|
godot --version
|
||||||
|
flatpak run org.blender.Blender --version | head -3
|
||||||
|
```
|
||||||
|
|
||||||
|
Inspect the current asset and the relevant docs. Claim the TODO item with `[-]`
|
||||||
|
before starting. Work on one asset family per commit.
|
||||||
|
|
||||||
|
### 2. Establish a baseline
|
||||||
|
|
||||||
|
Run the checks that do not need Blender first:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/blender_assets.py audit
|
||||||
|
python3 tools/blender_assets.py fit
|
||||||
|
python3 tools/blender_assets.py texels
|
||||||
|
python3 tools/npc_proportions.py report
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
For a specific incoming or existing GLB:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- \
|
||||||
|
inspect --file "$PWD/path/to/asset.glb" --category npc_gear --json
|
||||||
|
```
|
||||||
|
|
||||||
|
Record the before measurements (tris/verts, head/shoulder/hip/leg/arm/foot,
|
||||||
|
fabric texel density, gear clearance). Do not “improve” an asset without a
|
||||||
|
measurable target.
|
||||||
|
|
||||||
|
### 3. Optional Blender-skill routing
|
||||||
|
|
||||||
|
If the local `blender-skills` pack is installed, load `blender-director`, then
|
||||||
|
only the specialists the task needs:
|
||||||
|
|
||||||
|
- character mesh/face/hands: `character-artist`, `retopology`
|
||||||
|
- rig/weights/pose: `rigging`
|
||||||
|
- UVs/texel density: `uv-workflow`
|
||||||
|
- maps/materials: `texture-workflow`, `materials`, `lookdev`
|
||||||
|
- gear/props: `prop-artist`, `hard-surface`
|
||||||
|
- budget/LOD/collision: `asset-optimization`, `lod-pipeline`, `collision-proxy`
|
||||||
|
- final review: `qa-review`
|
||||||
|
|
||||||
|
Translate any MCP instruction into a change to `tools/*.py` or a headless CLI
|
||||||
|
run. Never add an MCP dependency to the build, CI or runtime.
|
||||||
|
|
||||||
|
### 4. Build or bake
|
||||||
|
|
||||||
|
Characters, gear and library props:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- build --only <name>
|
||||||
|
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/bake_ao.py" -- --only <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
NPC base and its material maps:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/npc_pipeline.py prepare # only when fabric tiles change
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/npc_pipeline.py" -- build
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not overwrite a review image or a promoted texture pick. Use a new `-vN`
|
||||||
|
path and document the promotion.
|
||||||
|
|
||||||
|
### 5. Import and capture
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --import
|
||||||
|
godot --headless --path . --script res://src/test/test_runner.gd
|
||||||
|
|
||||||
|
# neutral baseline: 8 variants x 6 views, raw frames stay in tmp/
|
||||||
|
godot --path . --script res://src/test/capture_npc_baseline.gd \
|
||||||
|
-- --out=res://tmp/npc-baseline
|
||||||
|
|
||||||
|
# final-context: 96 frames across both venues and day/night
|
||||||
|
godot --path . --script res://src/test/capture_npc_review.gd \
|
||||||
|
-- --out=res://tmp/npc-review
|
||||||
|
```
|
||||||
|
|
||||||
|
Compare before/after with `tools/npc_baseline.py compare`; do not replace the
|
||||||
|
committed baseline sheet until a fix actually lands. See
|
||||||
|
`references/verification.md` for the full gate matrix.
|
||||||
|
|
||||||
|
### 6. Verify and hand off
|
||||||
|
|
||||||
|
At minimum:
|
||||||
|
|
||||||
|
- `blender_assets.py audit` and `fit` exit 0.
|
||||||
|
- `npc_proportions.py` passes the stylized guide.
|
||||||
|
- `npc_pipeline.py audit` passes if the base/textures changed.
|
||||||
|
- texel density is inside the authored/fabric bands.
|
||||||
|
- No character exceeds 8,000/8,000; gear/prop caps hold.
|
||||||
|
- 24-NPC budget and Web/Compatibility path remain green.
|
||||||
|
- Contract tests, headless suite, capture gates and docs check pass.
|
||||||
|
- The diff contains only the intended assets, tools, tests and docs.
|
||||||
|
|
||||||
|
State in the handoff: files changed, measurements before/after, skills used,
|
||||||
|
gates run, and any accepted limit.
|
||||||
|
|
||||||
|
## Failure playbook
|
||||||
|
|
||||||
|
- **`Class "..." hides a global script class`** — a stray `.gd` backup is in the
|
||||||
|
project. Delete `tmp/*.gd` backups, `rm -rf .godot`, re-import; never keep
|
||||||
|
`class_name` copies in `tmp/`.
|
||||||
|
- **Blender cannot see the path** — the flatpak cwd is `/app/blender`; use
|
||||||
|
absolute paths (`"$PWD/..."`).
|
||||||
|
- **Empty or stale captures** — close modals/pause, check the capture script's
|
||||||
|
settle frames, and remove stale output directories before re-running.
|
||||||
|
- **Pose harness mismatch** — the current `capture_npc_pose.gd` is blocked per
|
||||||
|
`docs/3d-model-review.md`; do not use its frames as pose evidence until the
|
||||||
|
recipe is fixed.
|
||||||
|
- **Asset test fails after import** — re-run audit/fit first, then import; if the
|
||||||
|
manifest or AO path changed, rebuild the affected asset rather than editing
|
||||||
|
the manifest by hand.
|
||||||
|
- **Uncertain about a visual change** — produce a labelled before/after sheet and
|
||||||
|
ask for review; do not hide a regression behind a budget claim.
|
||||||
|
|
||||||
|
## Bundled references
|
||||||
|
|
||||||
|
- [`references/asset-contract.md`](references/asset-contract.md) — full contract,
|
||||||
|
inventory, file locations and provenance rules.
|
||||||
|
- [`references/verification.md`](references/verification.md) — command matrix,
|
||||||
|
capture gates, expected results and acceptance criteria.
|
||||||
|
|
||||||
|
## Do not
|
||||||
|
|
||||||
|
- Add an MCP server, Blender dependency or API call to the build/runtime.
|
||||||
|
- Change the bone/tint/contract to make a generic asset fit.
|
||||||
|
- Rewrite committed baselines or overwrite existing images.
|
||||||
|
- Commit `.blend` sources, raw captures or generated AI inputs.
|
||||||
|
- Treat an asset audit as a substitute for visual review at portrait and crowd
|
||||||
|
distance.
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
# Asset Contract Reference
|
||||||
|
|
||||||
|
This is a working summary. The authoritative sources remain
|
||||||
|
`docs/blender-assets.md`, `docs/asset-tooling.md`, ADR-0013, the tests in
|
||||||
|
`src/test/tests/` and the constants in `tools/blender_assets.py` /
|
||||||
|
`tools/npc_pipeline.py`.
|
||||||
|
|
||||||
|
## Library inventory
|
||||||
|
|
||||||
|
- **Character variants (8):** `npc_casual`, `npc_jacket`, `npc_coat`,
|
||||||
|
`npc_vest`, `npc_dress`, `npc_hoodie`, `npc_firefighter`, `npc_guard`.
|
||||||
|
- **Frozen fallback:** `npc_base` / `assets/models/generated/npc_base.glb`.
|
||||||
|
- **Gear:** 18 attach-space pieces covering head, back, chest and hand items.
|
||||||
|
- **Library props:** 20 Blender-authored props plus 6 generated props.
|
||||||
|
- **Venues:** procedural venues in `src/game/world_builder.gd`; Blender venue
|
||||||
|
geometry follows the same budgets and collision/provenance rules.
|
||||||
|
|
||||||
|
## Skeleton and geometry
|
||||||
|
|
||||||
|
The contract skeleton has 13 bones:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Head, Spine, Neck,
|
||||||
|
HipL, HipR,
|
||||||
|
KneeL, KneeR,
|
||||||
|
ShoulderL, ShoulderR,
|
||||||
|
ElbowL, ElbowR,
|
||||||
|
HandL, HandR
|
||||||
|
```
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- `npc_base` mesh node exists and is skinned under `Rig`.
|
||||||
|
- Limb node names stay stable for `NpcAvatar` and tests.
|
||||||
|
- Front is Blender −Y / Godot +Z.
|
||||||
|
- Feet are grounded at approximately y = 0; no floating origin.
|
||||||
|
- Units are metres; character height is approximately 1.8 m.
|
||||||
|
- No zero-area/duplicate degenerate faces in the exported mesh.
|
||||||
|
- Every character has UVs in [0,1] unless a planned UDIM/atlas change is
|
||||||
|
explicitly accepted.
|
||||||
|
|
||||||
|
## Material slots
|
||||||
|
|
||||||
|
Exactly these five tint slots:
|
||||||
|
|
||||||
|
```text
|
||||||
|
skin, cloth, pants, accessory, hair
|
||||||
|
```
|
||||||
|
|
||||||
|
- Neutral albedo is authored in Blender; runtime tinting is authoritative.
|
||||||
|
- Slot names are contract values; do not rename or add a sixth slot casually.
|
||||||
|
- The procedural fallback (`NpcAvatar._build_body`) must keep working when the
|
||||||
|
GLB is missing.
|
||||||
|
|
||||||
|
## Budgets
|
||||||
|
|
||||||
|
| Asset | Limit |
|
||||||
|
| --- | --- |
|
||||||
|
| Library character | 8,000 triangles / 8,000 glTF vertices |
|
||||||
|
| `npc_base` fallback | 6,000 triangles / 4,000 vertices |
|
||||||
|
| Gear piece | 1,500 triangles |
|
||||||
|
| Prop | 4,000 triangles |
|
||||||
|
| Library prop/venue total | must stay inside the venue tests and library budget |
|
||||||
|
|
||||||
|
`CHAR_MAX_TRIS`, `CHAR_MAX_VERTS`, `GEAR_MAX_TRIS`, `PROP_MAX_TRIS` and related
|
||||||
|
constants live in `tools/blender_assets.py`. The budget is per character, not an
|
||||||
|
average. Measure after every mesh change. Do not lower the cap to make a diff
|
||||||
|
pass.
|
||||||
|
|
||||||
|
## Gear and hand items
|
||||||
|
|
||||||
|
- Gear is authored in the attach-point space documented by `GEAR` /
|
||||||
|
`GEAR_ATTACH` / `ATTACH_BLENDER`.
|
||||||
|
- `blender_assets.py fit` measures gear boxes against the character's head band,
|
||||||
|
clothing radius and back clearance.
|
||||||
|
- Runtime offsets are intentional:
|
||||||
|
- `NpcAvatar.GEAR_OFFSETS = {"lanyard": Vector3(0,0,0.04),
|
||||||
|
"firefighter_helmet": Vector3(0,0.03,0)}`
|
||||||
|
- `NpcAvatar.HAND_ITEM_FORWARD = Vector3(0,0,0.04)`
|
||||||
|
- An offset is **not** an authored-asset change; do not bake it into the GLB
|
||||||
|
without a separate contract decision.
|
||||||
|
- Open-palm grip (no finger-curl rig) is the accepted stylized limit.
|
||||||
|
|
||||||
|
## Textures and AO
|
||||||
|
|
||||||
|
- NPC maps are 256² albedo + normal + packed ORM.
|
||||||
|
- `TEX_SIZE = 256` in `tools/npc_pipeline.py`.
|
||||||
|
- Fabric UV scale is `Materials.NPC_FABRIC_UV_SCALE = Vector3(4,4,1)`.
|
||||||
|
- Cloth/pants skip the per-character baked AO; skin/hair/gear keep it. This
|
||||||
|
avoids the large blotchy magnification that appeared at 256².
|
||||||
|
- Gear/props may have their own AO maps; `tools/bake_ao.py` writes
|
||||||
|
`<name>_ao.png` next to the GLB and Godot wires it into the AO channel.
|
||||||
|
- Never overwrite an existing committed image; use a new `-vN` candidate and
|
||||||
|
promote only after review.
|
||||||
|
- Run `python3 tools/asset_pipeline.py audit <path>` and the relevant
|
||||||
|
`npc_pipeline.py audit` before committing map changes.
|
||||||
|
|
||||||
|
## File map
|
||||||
|
|
||||||
|
| Path | Role |
|
||||||
|
| --- | --- |
|
||||||
|
| `tools/blender_assets.py` | Catalogue, builders, audit/fit/inspect/texels/sheet. |
|
||||||
|
| `tools/npc_pipeline.py` | `npc_base` builder, texture preparation and audit. |
|
||||||
|
| `tools/bake_ao.py` | Blender AO bake into `<name>_ao.png`. |
|
||||||
|
| `tools/npc_proportions.py` | Exported-GLB proportion audit (no Blender). |
|
||||||
|
| `tools/npc_baseline.py` | Contact sheets/comparisons (no Blender). |
|
||||||
|
| `assets/models/blender/manifest.json` | Generated asset manifest. |
|
||||||
|
| `assets/models/blender/**/*.glb` | Committed library assets. |
|
||||||
|
| `assets/models/generated/npc_base.glb` | Frozen NPC fallback. |
|
||||||
|
| `assets/textures/npc/*_{albedo,normal,orm}.png` | Committed NPC maps. |
|
||||||
|
| `src/test/capture_npc_baseline.gd` | 48-frame neutral baseline. |
|
||||||
|
| `src/test/capture_npc_review.gd` | 96-frame final-context review. |
|
||||||
|
|
||||||
|
## Provenance and source rules
|
||||||
|
|
||||||
|
- Procedural Blender assets are project-owned.
|
||||||
|
- External/CC assets need ADR-0015 clearance: CC0 preferred, CC-BY only with
|
||||||
|
attribution in `CREDITS.md`; no service assets with redistribution limits.
|
||||||
|
- Editable `.blend` files under `assets/models/blender_src/` are gitignored;
|
||||||
|
only exported `.glb`, AO maps, manifests and committed preview sheets ship.
|
||||||
|
- AI-generated source images are development inputs, not shipped assets. Keep
|
||||||
|
raw generations under gitignored paths until picked, checked and credited.
|
||||||
|
- Do not copy a downloaded/Street View/AI reference into a committed asset
|
||||||
|
without the licence and provenance review.
|
||||||
|
|
||||||
|
## Verification invariants
|
||||||
|
|
||||||
|
The asset pass is not complete unless:
|
||||||
|
|
||||||
|
1. `python3 tools/blender_assets.py audit` exits 0.
|
||||||
|
2. `python3 tools/blender_assets.py fit` exits 0 and the report is reviewed.
|
||||||
|
3. `python3 tools/npc_proportions.py report` passes the stylized guide.
|
||||||
|
4. `python3 tools/npc_pipeline.py audit` passes when the base/textures changed.
|
||||||
|
5. The 48-frame baseline and 96-frame final-context review are captured, with no
|
||||||
|
blank frames.
|
||||||
|
6. The 24-NPC budget smoke is inside its frame budget.
|
||||||
|
7. The full Godot suite and Python tooling tests pass.
|
||||||
|
8. No runtime contract, renderer or dependency changed except the rebuilt
|
||||||
|
assets and their generated metadata.
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
# Verification Matrix
|
||||||
|
|
||||||
|
Run from the repository root. Stop and fix the first failure before continuing.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --version
|
||||||
|
flatpak run org.blender.Blender --version | head -3
|
||||||
|
git status --short
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: Godot 4.7.2; Blender 5.2 LTS flatpak; clean or intentionally scoped
|
||||||
|
working tree. Blender's flatpak cwd is `/app/blender`; all paths passed to
|
||||||
|
Blender must be absolute.
|
||||||
|
|
||||||
|
## Asset checks
|
||||||
|
|
||||||
|
| Check | Command | Expected |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Contract audit | `python3 tools/blender_assets.py audit` | exit 0, no `BAD` assets |
|
||||||
|
| Gear fit | `python3 tools/blender_assets.py fit` | exit 0; review head band/clothing/back clearances |
|
||||||
|
| Texture/texel density | `python3 tools/blender_assets.py texels` | authored 40–400 px/m; fabric ≥250 px/m |
|
||||||
|
| NPC base | `python3 tools/npc_pipeline.py audit` | exit 0, contract bones/slots/maps |
|
||||||
|
| Proportions | `python3 tools/npc_proportions.py report` | all eight inside the stylized guide |
|
||||||
|
| Single incoming GLB | `flatpak run org.blender.Blender --background --python "$PWD/tools/blender_assets.py" -- inspect --file "$PWD/path.glb" --category <cat> --json` | contract result and measurements |
|
||||||
|
| Prop audit | `python3 tools/model_pipeline.py audit` | exit 0 when generated props changed |
|
||||||
|
| Texture audit | `python3 tools/asset_pipeline.py audit assets/textures/ai/game_ready` | exit 0 when textures changed |
|
||||||
|
|
||||||
|
## Build/bake
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# one library asset
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/blender_assets.py" -- build --only npc_guard
|
||||||
|
|
||||||
|
# bake AO for the same asset
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/bake_ao.py" -- --only npc_guard
|
||||||
|
|
||||||
|
# NPC base material maps/base
|
||||||
|
python3 tools/npc_pipeline.py prepare
|
||||||
|
flatpak run org.blender.Blender --background \
|
||||||
|
--python "$PWD/tools/npc_pipeline.py" -- build
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
Only run `prepare` when AI fabric inputs changed. Do not run a full library
|
||||||
|
rebuild just to hide a small diff; build the affected asset and its AO map.
|
||||||
|
|
||||||
|
## Import and tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --import
|
||||||
|
godot --headless --path . --script res://src/test/test_runner.gd
|
||||||
|
python3 -m unittest discover -s tools/tests -t tools -p 'test_*.py'
|
||||||
|
python3 tools/check_docs.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: Godot suite passes; Python tooling suite passes; docs check OK. The
|
||||||
|
project uses its own headless runner, not GUT.
|
||||||
|
|
||||||
|
## Capture gates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# neutral studio baseline: 48 frames (8 characters x 6 views)
|
||||||
|
godot --path . --script res://src/test/capture_npc_baseline.gd \
|
||||||
|
-- --out=res://tmp/npc-baseline-after
|
||||||
|
|
||||||
|
# final-context: 96 frames (8 models x 3 distances x 2 venues x day/night)
|
||||||
|
godot --path . --script res://src/test/capture_npc_review.gd \
|
||||||
|
-- --out=res://tmp/npc-review-after
|
||||||
|
|
||||||
|
# optional hand close-up: 8 frames
|
||||||
|
godot --path . --script res://src/test/capture_npc_baseline.gd \
|
||||||
|
-- --out=res://tmp/npc-hands --view=hand
|
||||||
|
```
|
||||||
|
|
||||||
|
Raw frames stay under `tmp/` and are never committed. Compare with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/npc_baseline.py compare \
|
||||||
|
--before tmp/npc-baseline --after tmp/npc-baseline-after \
|
||||||
|
--out docs/blender_previews/sheet_npc_baseline_compare-<topic>.jpg
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not replace the committed baseline sheet until the change is accepted.
|
||||||
|
|
||||||
|
## Budget/performance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --script res://src/test/smoke_npc_budget.gd
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected report: 24 avatars / 168 meshes / 131,616 triangles (current baseline)
|
||||||
|
inside the 50 ms budget. Record the actual frame time; do not copy an old number
|
||||||
|
without measuring.
|
||||||
|
|
||||||
|
## Visual review criteria
|
||||||
|
|
||||||
|
For each changed variant, inspect at least:
|
||||||
|
|
||||||
|
- 2 m portrait: eyes, mouth, ears, neck/shoulder, hair/headgear clearance.
|
||||||
|
- 4.2 m conversation: hands, gear contact, clothing silhouette.
|
||||||
|
- 8 m crowd: readable silhouette and LOD/impostor swap.
|
||||||
|
- Day and night/LATE final-context frames.
|
||||||
|
- Front, three-quarter and profile for a shape change.
|
||||||
|
|
||||||
|
Accepted stylized limits are in `docs/npc-visual-review.md` and
|
||||||
|
`docs/known-limitations.md`. Do not turn an accepted limit into an unreviewed
|
||||||
|
contract change.
|
||||||
|
|
||||||
|
## Final diff review
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff --check
|
||||||
|
git status --short
|
||||||
|
git diff --stat
|
||||||
|
```
|
||||||
|
|
||||||
|
Check that only intended `.glb`, AO/maps, manifest, tests and docs changed. If a
|
||||||
|
`.blend`, raw capture, generated AI input or temporary sheet is staged, stop and
|
||||||
|
remove it. Commit one coherent asset family per commit.
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
---
|
||||||
|
name: gig-line-release-qa
|
||||||
|
description: Use when preparing, validating, tagging or publishing a Gig Line release or release candidate, or when running the full screenshot, screen-design and realism QA gates. Covers version/changelog discipline, smoke and tooling matrices, clean-worktree builds, checksums, Web/HQ verification, publish rollback and post-release records. Do not use for ordinary asset edits or feature work.
|
||||||
|
compatibility: Gig Line repository checkout with Godot 4.7.2 on PATH and Python 3 + Pillow. Release export needs installed Godot export templates; publishing needs Gitea credentials and is intentionally CI/tag-driven.
|
||||||
|
metadata:
|
||||||
|
project: Gig Line
|
||||||
|
domain: release-qa
|
||||||
|
version: "1.0.0"
|
||||||
|
triggers: release, tag, version bump, changelog, build releases, publish, screenshots, screen design, realism judge, release candidate, deployment
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gig Line Release and Visual QA
|
||||||
|
|
||||||
|
Use this skill for version bumps, release candidates, tags, artifact builds,
|
||||||
|
publishing, and the full visual/QA gate. For a Blender/asset rebuild, use
|
||||||
|
`gig-line-blender-asset-contract` first.
|
||||||
|
|
||||||
|
## Read first
|
||||||
|
|
||||||
|
1. `AGENTS.md` — release procedure and verification rules.
|
||||||
|
2. `TODO.md` — current milestone, blockers and release exit criteria.
|
||||||
|
3. `docs/RELEASING.md` — build/publish commands, artifact list, macOS policy.
|
||||||
|
4. `docs/smoke-checklist.md` — manual QA matrix.
|
||||||
|
5. `docs/development-status.md`, `CHANGELOG.md`, `README.md` — current claims.
|
||||||
|
6. `MEMORY.md` — release gotchas, especially the clean-worktree rule.
|
||||||
|
|
||||||
|
A release is not just a tag. It includes a version bump, changelog, clean build,
|
||||||
|
artifact/checksum verification, CI publication, and post-release records.
|
||||||
|
|
||||||
|
## Hard rules
|
||||||
|
|
||||||
|
- **Do not tag while the TODO still lists unresolved release blockers.** Check
|
||||||
|
external playtest, legal/AI provenance, signing and target-hardware items.
|
||||||
|
- **Never reuse a tag.** If a published version needs a fix, bump the patch and
|
||||||
|
create a new tag.
|
||||||
|
- **Build from a clean checkout or detached worktree.** A normal working tree can
|
||||||
|
pack ignored `tmp/` and `build/` content into exports, inflating a ~160 MB
|
||||||
|
package to 1.6–3.1 GB.
|
||||||
|
- A tag push triggers CI checks/build/publish. Do not upload manually unless CI is
|
||||||
|
unavailable and you have followed the same preflight.
|
||||||
|
- Do not commit generated realism runs, raw captures, API keys, or build artifacts.
|
||||||
|
- The vision judge is an optional development aid with external API cost, never a
|
||||||
|
hard CI gate.
|
||||||
|
- Keep `project.godot`, changelog, tag and release asset version equal.
|
||||||
|
|
||||||
|
## Release workflow
|
||||||
|
|
||||||
|
### 1. Scope and version
|
||||||
|
|
||||||
|
Choose the semantic increment from `AGENTS.md`:
|
||||||
|
|
||||||
|
- MAJOR: incompatible/removed behavior, save/config contract or core-pillar
|
||||||
|
replacement.
|
||||||
|
- MINOR: new user-facing feature/venue.
|
||||||
|
- PATCH: fixes and polish.
|
||||||
|
|
||||||
|
Update:
|
||||||
|
|
||||||
|
1. `project.godot` `config/version`.
|
||||||
|
2. `CHANGELOG.md` with a dated entry.
|
||||||
|
3. `README.md`, `PLAN.md`, `docs/architecture.md`,
|
||||||
|
`docs/development-status.md`, `TODO.md`/`DONE.md` and `MEMORY.md` as needed.
|
||||||
|
|
||||||
|
For a release candidate, use a prerelease version only if the project policy
|
||||||
|
allows it and never present it as the final release.
|
||||||
|
|
||||||
|
### 2. Preflight
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status --short
|
||||||
|
git log --oneline -5
|
||||||
|
git tag -l "v<version>"
|
||||||
|
rg -n 'config/version' project.godot
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: only intended release changes; tag does not exist; version matches the
|
||||||
|
changelog and planned artifacts.
|
||||||
|
|
||||||
|
### 3. Automated checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --script res://src/test/test_runner.gd
|
||||||
|
python3 -m unittest discover -s tools/tests -t tools -p 'test_*.py'
|
||||||
|
python3 tools/check_docs.py
|
||||||
|
python3 tools/blender_assets.py audit
|
||||||
|
python3 tools/blender_assets.py fit
|
||||||
|
python3 tools/npc_proportions.py report
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run the smoke matrix, using isolated user-data directories for
|
||||||
|
tutorial/campaign:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for s in smoke_audio smoke_crowd smoke_emergency smoke_npc_budget \
|
||||||
|
smoke_plan_verbs smoke_scenario_content smoke_session_shell \
|
||||||
|
smoke_timeline smoke_ui_flows; do
|
||||||
|
godot --headless --path . --script res://src/test/$s.gd || exit 1
|
||||||
|
done
|
||||||
|
|
||||||
|
XDG_DATA_HOME=/tmp/gig-line-campaign godot --path . --fixed-fps 60 \
|
||||||
|
--script res://src/test/smoke_campaign.gd
|
||||||
|
XDG_DATA_HOME=/tmp/gig-line-tutorial godot --path . --fixed-fps 60 \
|
||||||
|
--script res://src/test/smoke_tutorial.gd -- --tutorial
|
||||||
|
|
||||||
|
godot --path . --script res://src/test/smoke_movement.gd
|
||||||
|
godot --path . --script res://src/test/smoke_props.gd
|
||||||
|
godot --path . --script res://src/test/smoke_focus_radial.gd
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a fresh `$XDG_DATA_HOME` for campaign/tutorial and check the exit code, not
|
||||||
|
just the log.
|
||||||
|
|
||||||
|
### 4. Visual and judge gates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./tools/check_screenshots.sh
|
||||||
|
python3 tools/screen_designs.py compare
|
||||||
|
python3 tools/screen_designs.py judge --model auto # optional, record model/verdicts
|
||||||
|
```
|
||||||
|
|
||||||
|
For realism/venue work:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --path . --script res://src/test/capture_realism.gd \
|
||||||
|
-- --out=res://docs/screens/realism/<run>/after
|
||||||
|
python3 tools/scene_concepts.py realism --run docs/screens/realism/<run> \
|
||||||
|
--judge --model <pinned-model>
|
||||||
|
```
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- Gallery: 21 good / 0 bad; screen judge has no unexplained `diff`.
|
||||||
|
- Realism runs stay under `docs/screens/realism/` (gitignored); only decisions and
|
||||||
|
committed sheets/docs are tracked.
|
||||||
|
- Record judge model, run count/attempts, spend if exposed, and verdicts.
|
||||||
|
- Review every `review` verdict; turn layout regressions into fixes or documented
|
||||||
|
known limitations.
|
||||||
|
|
||||||
|
### 5. Clean build
|
||||||
|
|
||||||
|
Commit the release preparation first, then build the committed tree in a clean
|
||||||
|
worktree:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git worktree add --detach /tmp/gig-line-release HEAD
|
||||||
|
cd /tmp/gig-line-release
|
||||||
|
./tools/build_releases.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify artifact names/sizes and checksums:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -lh build/gig-line-*<version>*
|
||||||
|
PUBLISH_DRY_RUN=1 ./tools/publish_release.sh <version>
|
||||||
|
sha256sum -c build/SHA256SUMS-<version>.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Check package contents:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
unzip -l build/gig-line-web-<version>.zip | head
|
||||||
|
unzip -l build/gig-line-desktop-hq-<version>.zip | head
|
||||||
|
unzip -p build/gig-line-desktop-hq-<version>.zip override.cfg
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the runnable Linux and HQ packages:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./build/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
unzip -q build/gig-line-desktop-hq-<version>.zip -d /tmp/gig-line-hq
|
||||||
|
/tmp/gig-line-hq/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
```
|
||||||
|
|
||||||
|
Check Web static serving with a local HTTP server and `curl` for `index.html`,
|
||||||
|
`index.wasm`, `index.pck` and the player docs. Windows/macOS/AppImage/installer
|
||||||
|
checks require the target matrix; if unavailable, record that explicitly.
|
||||||
|
|
||||||
|
Remove the worktree after verification:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git worktree remove --force /tmp/gig-line-release
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Tag and publish
|
||||||
|
|
||||||
|
Only after every gate is green:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "Release <version> <topic>"
|
||||||
|
git tag -a "v<version>" -m "Gig Line <version>"
|
||||||
|
git push origin main "v<version>"
|
||||||
|
```
|
||||||
|
|
||||||
|
CI builds and publishes on the tag. Verify:
|
||||||
|
|
||||||
|
- Tag commit equals the release commit.
|
||||||
|
- CI checks/build job succeeded.
|
||||||
|
- Release page has the expected 7 assets (Linux, Windows, macOS, NSIS setup,
|
||||||
|
AppImage, Web, Desktop HQ).
|
||||||
|
- Asset names and download links point at the released version.
|
||||||
|
|
||||||
|
`tools/publish_release.sh` preflights artifact size/free space, writes SHA-256
|
||||||
|
checksums, rolls back newly uploaded assets/release on failure, and supports
|
||||||
|
opt-in retention:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RETAIN_RELEASES=7 ./tools/publish_release.sh <version>
|
||||||
|
```
|
||||||
|
|
||||||
|
The default `RETAIN_RELEASES=0` never prunes.
|
||||||
|
|
||||||
|
### 7. Post-release record
|
||||||
|
|
||||||
|
Update `README.md`, `docs/development-status.md`, `MEMORY.md`, `CHANGELOG.md`,
|
||||||
|
`TODO.md`/`DONE.md` and the release page with the built/CI result. Commit the
|
||||||
|
release record and push. Record any skipped check and the reason; never leave a
|
||||||
|
tag with unreported missing verification.
|
||||||
|
|
||||||
|
## Known gotchas
|
||||||
|
|
||||||
|
- **Dirty build inflation:** always use the clean worktree above.
|
||||||
|
- **`Class ... hides a global script class`:** remove stray `.gd` backups in
|
||||||
|
`tmp/`, delete `.godot`, re-import.
|
||||||
|
- **Screenshot staging:** `capture_ui_states.gd` needs its settle frames and
|
||||||
|
staged state; do not capture a paused frame or a stale modal.
|
||||||
|
- **Judge keys:** OpenRouter calls need `.env`/`OPENROUTER_API_KEY`; never commit
|
||||||
|
the key or generated review folders.
|
||||||
|
- **Tag builds publish automatically:** do not run a second manual publish that
|
||||||
|
duplicates assets; use the existing release if re-running.
|
||||||
|
- **macOS is unsigned unless policy changes:** record quarantine instructions and
|
||||||
|
do not claim notarization.
|
||||||
|
- **Release blockers are real:** external playtest, legal/storefront approval,
|
||||||
|
AI-provider rights and target-hardware checks cannot be replaced by green unit
|
||||||
|
tests.
|
||||||
|
|
||||||
|
## Bundled references
|
||||||
|
|
||||||
|
- [`references/release-runbook.md`](references/release-runbook.md) — version,
|
||||||
|
build, tag, CI, publish and rollback sequence.
|
||||||
|
- [`references/qa-gates.md`](references/qa-gates.md) — full command matrix,
|
||||||
|
expected results and evidence to record.
|
||||||
|
|
||||||
|
## Completion checklist
|
||||||
|
|
||||||
|
- [ ] Version/changelog/docs/tag match.
|
||||||
|
- [ ] No unresolved release blockers for a final tag.
|
||||||
|
- [ ] Godot, Python, smokes, asset and docs gates pass.
|
||||||
|
- [ ] Gallery and optional judges reviewed with evidence.
|
||||||
|
- [ ] Clean worktree build and checksums verified.
|
||||||
|
- [ ] Linux/HQ/Web checks recorded; unavailable platforms noted.
|
||||||
|
- [ ] Tag pushed; CI/publish succeeded; seven assets verified.
|
||||||
|
- [ ] Release record and durable docs committed.
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
# QA Gate Matrix
|
||||||
|
|
||||||
|
Run from the repository root. Capture the exit code and the useful report line,
|
||||||
|
not only the final “OK”.
|
||||||
|
|
||||||
|
## 1. Source and docs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status --short
|
||||||
|
git log --oneline -5
|
||||||
|
python3 tools/check_docs.py
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: intended files only; `check_docs: OK`; no whitespace errors.
|
||||||
|
|
||||||
|
## 2. Automated suites
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --headless --path . --script res://src/test/test_runner.gd
|
||||||
|
python3 -m unittest discover -s tools/tests -t tools -p 'test_*.py'
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: the current Godot suite passes (historically 272+ tests) and the
|
||||||
|
Python tooling suite passes (currently 160 tests). Do not edit expected counts
|
||||||
|
without inspecting the suite.
|
||||||
|
|
||||||
|
## 3. Asset gates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/blender_assets.py audit
|
||||||
|
python3 tools/blender_assets.py fit
|
||||||
|
python3 tools/blender_assets.py texels
|
||||||
|
python3 tools/npc_pipeline.py audit
|
||||||
|
python3 tools/npc_proportions.py report
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: exit 0, no `BAD` assets, all eight characters inside the stylized
|
||||||
|
proportion guide, no budget/contract regression.
|
||||||
|
|
||||||
|
Optional texture/model audits when those assets changed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/asset_pipeline.py audit assets/textures/ai/game_ready
|
||||||
|
python3 tools/model_pipeline.py audit
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Smoke matrix
|
||||||
|
|
||||||
|
Headless:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for s in smoke_audio smoke_crowd smoke_emergency smoke_npc_budget \
|
||||||
|
smoke_plan_verbs smoke_scenario_content smoke_session_shell \
|
||||||
|
smoke_timeline smoke_ui_flows; do
|
||||||
|
echo "== $s"
|
||||||
|
godot --headless --path . --script res://src/test/$s.gd || exit 1
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Isolated data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf /tmp/gig-line-release-campaign /tmp/gig-line-release-tutorial
|
||||||
|
XDG_DATA_HOME=/tmp/gig-line-release-campaign godot --path . --fixed-fps 60 \
|
||||||
|
--script res://src/test/smoke_campaign.gd
|
||||||
|
XDG_DATA_HOME=/tmp/gig-line-release-tutorial godot --path . --fixed-fps 60 \
|
||||||
|
--script res://src/test/smoke_tutorial.gd -- --tutorial
|
||||||
|
```
|
||||||
|
|
||||||
|
Windowed input/UI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --path . --script res://src/test/smoke_movement.gd
|
||||||
|
godot --path . --script res://src/test/smoke_props.gd
|
||||||
|
godot --path . --script res://src/test/smoke_focus_radial.gd
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: 14 smokes green. `smoke_npc_budget` should print the current mesh,
|
||||||
|
triangle and frame-time evidence; the 50 ms budget is a safety ceiling.
|
||||||
|
|
||||||
|
## 5. Screenshot and design gates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./tools/check_screenshots.sh
|
||||||
|
python3 tools/screen_designs.py compare
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: 21 good / 0 bad; no unexplained compare output. The judge is optional:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/screen_designs.py judge --model auto
|
||||||
|
```
|
||||||
|
|
||||||
|
Record model, confidence/verdict summary, `diff` count and any manual review.
|
||||||
|
`docs/screen-design-report.md` is the durable record.
|
||||||
|
|
||||||
|
## 6. Realism judge (when visual assets/lighting changed)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
godot --path . --script res://src/test/capture_realism.gd \
|
||||||
|
-- --out=res://docs/screens/realism/<run>/after
|
||||||
|
python3 tools/scene_concepts.py realism --run docs/screens/realism/<run> \
|
||||||
|
--judge --model <pinned-model>
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: a complete set of required stage captures, no stale pauses/modals, and
|
||||||
|
a report under the gitignored run directory. Record:
|
||||||
|
|
||||||
|
- pinned model/fallbacks
|
||||||
|
- shots attempted/completed
|
||||||
|
- spend if the tool exposes it
|
||||||
|
- mean score and Pillow gap
|
||||||
|
- every `review` verdict and disposition
|
||||||
|
|
||||||
|
Do not commit the run. Commit only the decision/report summary and any durable
|
||||||
|
comparison sheet explicitly intended for review.
|
||||||
|
|
||||||
|
## 7. Package checks
|
||||||
|
|
||||||
|
Use the clean worktree from `release-runbook.md`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PUBLISH_DRY_RUN=1 ./tools/publish_release.sh <version>
|
||||||
|
sha256sum -c build/SHA256SUMS-<version>.txt
|
||||||
|
unzip -l build/gig-line-web-<version>.zip
|
||||||
|
unzip -l build/gig-line-desktop-hq-<version>.zip
|
||||||
|
unzip -p build/gig-line-desktop-hq-<version>.zip override.cfg
|
||||||
|
./build/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
|
||||||
|
- names match the version;
|
||||||
|
- Linux/Windows/macOS/Web/HQ present locally;
|
||||||
|
- Web contains HTML/wasm/pck and docs;
|
||||||
|
- HQ contains `override.cfg`, executables and docs;
|
||||||
|
- Linux/HQ start without a script error.
|
||||||
|
|
||||||
|
Windows installer/AppImage and macOS launch require CI or the target matrix. If
|
||||||
|
not available, mark them as unverified rather than passing them by analogy.
|
||||||
|
|
||||||
|
## 8. Release verification
|
||||||
|
|
||||||
|
After the tag push:
|
||||||
|
|
||||||
|
- CI checks job succeeded;
|
||||||
|
- CI build job succeeded;
|
||||||
|
- release page has seven assets;
|
||||||
|
- each artifact name includes the released version;
|
||||||
|
- the tag points at the release commit;
|
||||||
|
- README/changelog/status and release notes agree.
|
||||||
|
|
||||||
|
## Evidence summary template
|
||||||
|
|
||||||
|
```text
|
||||||
|
Release: vX.Y.Z (commit)
|
||||||
|
Godot: pass (N/N)
|
||||||
|
Python tooling: pass (N/N)
|
||||||
|
Smokes: 14/14
|
||||||
|
Assets: audit/fit/proportions/npc audit pass
|
||||||
|
Budget: 24 NPCs, N meshes, N triangles, N ms
|
||||||
|
Gallery: 21 good / 0 bad
|
||||||
|
Screen judge: N/N match, N review, N diff
|
||||||
|
Realism: model, attempts, mean, gap, review dispositions
|
||||||
|
Build: clean worktree, artifact sizes/checksums
|
||||||
|
Launch: Linux/HQ/Web checks; unverified platforms
|
||||||
|
CI/release: run id, seven assets
|
||||||
|
Skipped checks + reason:
|
||||||
|
Known limitations:
|
||||||
|
```
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
# Release Runbook
|
||||||
|
|
||||||
|
Authoritative source: `AGENTS.md` (Release Procedure), `docs/RELEASING.md`.
|
||||||
|
This runbook is the operational checklist; if it conflicts with those, they win.
|
||||||
|
|
||||||
|
## 0. Decide the release
|
||||||
|
|
||||||
|
Determine whether this is a major, minor, patch, or release-candidate checkpoint.
|
||||||
|
Do not assign a version to an unversioned development track until its scope is
|
||||||
|
accepted into a release.
|
||||||
|
|
||||||
|
Check the current release constraints in `TODO.md`:
|
||||||
|
|
||||||
|
- external playtest evidence
|
||||||
|
- legal/AI-provider/storefront review
|
||||||
|
- macOS signing/notarization policy
|
||||||
|
- target hardware matrix
|
||||||
|
- any manual QA gaps
|
||||||
|
|
||||||
|
If a final release would ship with unresolved blockers, prepare an RC or stop;
|
||||||
|
do not create a final tag.
|
||||||
|
|
||||||
|
## 1. Prepare the version
|
||||||
|
|
||||||
|
Files to update:
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
| --- | --- |
|
||||||
|
| `project.godot` | `config/version="X.Y.Z"` |
|
||||||
|
| `CHANGELOG.md` | dated `## [X.Y.Z] — YYYY-MM-DD` entry |
|
||||||
|
| `README.md` | latest/current version and summary |
|
||||||
|
| `PLAN.md` / `docs/architecture.md` | current version reference |
|
||||||
|
| `docs/development-status.md` | shipped capability/release result |
|
||||||
|
| `TODO.md` / `DONE.md` | close/move milestone when complete |
|
||||||
|
| `MEMORY.md` | release state and gotchas |
|
||||||
|
|
||||||
|
Then commit the preparation before building.
|
||||||
|
|
||||||
|
## 2. Run gates
|
||||||
|
|
||||||
|
Use `qa-gates.md` as the command matrix. The minimum release gate is:
|
||||||
|
|
||||||
|
- Godot suite
|
||||||
|
- Python tooling tests
|
||||||
|
- documentation gate
|
||||||
|
- asset audits/fit/proportions
|
||||||
|
- 24-NPC budget
|
||||||
|
- 14 smoke scripts
|
||||||
|
- screenshot compare
|
||||||
|
- optional screen/realism judge with recorded model/attempts
|
||||||
|
- clean build and artifact checks
|
||||||
|
|
||||||
|
Any skipped check must be written into `MEMORY.md` or `TODO.md` with a reason.
|
||||||
|
|
||||||
|
## 3. Build in a clean worktree
|
||||||
|
|
||||||
|
Why: Godot can pack ignored `tmp/` captures and `build/` outputs into the export.
|
||||||
|
A dirty build can produce 1.6–3.1 GB artifacts instead of ~160–240 MB.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git worktree add --detach /tmp/gig-line-release HEAD
|
||||||
|
cd /tmp/gig-line-release
|
||||||
|
./tools/build_releases.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected local artifacts:
|
||||||
|
|
||||||
|
```text
|
||||||
|
build/gig-line-linux-<version>.x86_64
|
||||||
|
build/gig-line-windows-<version>.exe
|
||||||
|
build/gig-line-macos-<version>.zip
|
||||||
|
build/gig-line-web-<version>.zip
|
||||||
|
build/gig-line-desktop-hq-<version>.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
CI additionally produces the NSIS setup and AppImage, for seven release assets.
|
||||||
|
|
||||||
|
## 4. Preflight and checksum
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PUBLISH_DRY_RUN=1 ./tools/publish_release.sh <version>
|
||||||
|
sha256sum -c build/SHA256SUMS-<version>.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
`publish_release.sh` checks non-empty artifacts and local free space before any
|
||||||
|
API call. It writes checksums under `build/` and does not upload them by default.
|
||||||
|
|
||||||
|
Inspect packages:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
unzip -l build/gig-line-web-<version>.zip | head -20
|
||||||
|
unzip -l build/gig-line-desktop-hq-<version>.zip | head -20
|
||||||
|
unzip -p build/gig-line-desktop-hq-<version>.zip override.cfg
|
||||||
|
```
|
||||||
|
|
||||||
|
Web must contain `index.html`, `index.wasm`, `index.pck`, and player docs.
|
||||||
|
Desktop HQ must contain the Linux/Windows executables, `override.cfg`,
|
||||||
|
`README.txt`, `CREDITS.md`, `PRIVACY.md`, `accessibility.md` and
|
||||||
|
`known-limitations.md`.
|
||||||
|
|
||||||
|
## 5. Runtime launch smoke
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./build/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
unzip -q build/gig-line-desktop-hq-<version>.zip -d /tmp/gig-line-hq
|
||||||
|
/tmp/gig-line-hq/gig-line-linux-<version>.x86_64 --headless --quit-after 60
|
||||||
|
```
|
||||||
|
|
||||||
|
For Web, serve `build/web` and check HTTP 200 for `index.html`, `index.wasm`,
|
||||||
|
`index.pck` and docs. Do not claim browser interaction from a curl check.
|
||||||
|
|
||||||
|
Remove the temporary worktree after checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git worktree remove --force /tmp/gig-line-release
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Tag and CI
|
||||||
|
|
||||||
|
From the real repository, on the release commit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag -a "v<version>" -m "Gig Line <version>"
|
||||||
|
git push origin main "v<version>"
|
||||||
|
```
|
||||||
|
|
||||||
|
The tag triggers `.gitea/workflows/build.yml`: checks, export, installer,
|
||||||
|
AppImage, release publish. Verify the run and assets:
|
||||||
|
|
||||||
|
- check job success
|
||||||
|
- build job success
|
||||||
|
- seven assets present
|
||||||
|
- tag/version/artifact names equal
|
||||||
|
- download links respond
|
||||||
|
|
||||||
|
Do not reuse a published tag. If a fix is needed, bump patch and repeat.
|
||||||
|
|
||||||
|
## 7. Manual publish fallback
|
||||||
|
|
||||||
|
Only if CI cannot publish:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RETAIN_RELEASES=7 ./tools/publish_release.sh <version>
|
||||||
|
```
|
||||||
|
|
||||||
|
The script reuses or creates the release, uploads present artifacts, verifies the
|
||||||
|
asset count, and rolls back newly uploaded assets/new release on failure. The
|
||||||
|
default retention is `0` (no deletion). Never delete older releases implicitly.
|
||||||
|
|
||||||
|
## 8. Post-release record
|
||||||
|
|
||||||
|
Commit a release record that includes:
|
||||||
|
|
||||||
|
- tag and commit
|
||||||
|
- CI run/result
|
||||||
|
- seven assets
|
||||||
|
- checksums or size evidence
|
||||||
|
- skipped checks
|
||||||
|
- known limitations
|
||||||
|
- release URL
|
||||||
|
|
||||||
|
Update `DONE.md`/`TODO.md` and `MEMORY.md`; keep the changelog and README
|
||||||
|
consistent. Push the record commit.
|
||||||
|
|
||||||
|
## Versioning pitfalls
|
||||||
|
|
||||||
|
- A `vX.Y.Z` tag is immutable in practice; do not move/replace it.
|
||||||
|
- A release candidate is not a final release. Do not label it as such in the
|
||||||
|
README or release page if blockers remain.
|
||||||
|
- `config/version` must match the tag and artifact filenames.
|
||||||
|
- The clean-worktree build is a release requirement, not an optimization.
|
||||||
@@ -17,12 +17,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Godot 4.3
|
- name: Install Godot 4.7.2
|
||||||
run: |
|
run: |
|
||||||
curl -sL -o /tmp/godot.zip \
|
curl -sL -o /tmp/godot.zip \
|
||||||
https://github.com/godotengine/godot/releases/download/4.3-stable/Godot_v4.3-stable_linux.x86_64.zip
|
https://github.com/godotengine/godot/releases/download/4.7.2-stable/Godot_v4.7.2-stable_linux.x86_64.zip
|
||||||
unzip -qo /tmp/godot.zip -d /tmp/godot-bin
|
unzip -qo /tmp/godot.zip -d /tmp/godot-bin
|
||||||
sudo install /tmp/godot-bin/Godot_v4.3-stable_linux.x86_64 /usr/local/bin/godot
|
sudo install /tmp/godot-bin/Godot_v4.7.2-stable_linux.x86_64 /usr/local/bin/godot
|
||||||
|
godot --version
|
||||||
|
|
||||||
- name: Import project (build class cache)
|
- name: Import project (build class cache)
|
||||||
run: godot --headless --import
|
run: godot --headless --import
|
||||||
@@ -56,18 +57,24 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Godot 4.3 and export templates
|
- name: Install Godot 4.7.2 and export templates
|
||||||
run: |
|
run: |
|
||||||
curl -sL -o /tmp/godot.zip \
|
curl -sL -o /tmp/godot.zip \
|
||||||
https://github.com/godotengine/godot/releases/download/4.3-stable/Godot_v4.3-stable_linux.x86_64.zip
|
https://github.com/godotengine/godot/releases/download/4.7.2-stable/Godot_v4.7.2-stable_linux.x86_64.zip
|
||||||
unzip -qo /tmp/godot.zip -d /tmp/godot-bin
|
unzip -qo /tmp/godot.zip -d /tmp/godot-bin
|
||||||
sudo install /tmp/godot-bin/Godot_v4.3-stable_linux.x86_64 /usr/local/bin/godot
|
sudo install /tmp/godot-bin/Godot_v4.7.2-stable_linux.x86_64 /usr/local/bin/godot
|
||||||
|
godot --version
|
||||||
|
TEMPLATE_DIR="$HOME/.local/share/godot/export_templates/4.7.2.stable"
|
||||||
|
NEEDED="version.txt icudt_godot.dat linux_release.x86_64 windows_release_x86_64.exe macos.zip web_nothreads_release.zip web_dlink_nothreads_release.zip"
|
||||||
curl -sL -o /tmp/templates.tpz \
|
curl -sL -o /tmp/templates.tpz \
|
||||||
https://github.com/godotengine/godot/releases/download/4.3-stable/Godot_v4.3-stable_export_templates.tpz
|
https://github.com/godotengine/godot/releases/download/4.7.2-stable/Godot_v4.7.2-stable_export_templates.tpz
|
||||||
mkdir -p ~/.local/share/godot/export_templates/4.3.stable
|
rm -rf /tmp/tpl && mkdir -p /tmp/tpl
|
||||||
cd /tmp && rm -rf tpl && mkdir tpl && unzip -qo templates.tpz -d tpl
|
unzip -qo /tmp/templates.tpz -d /tmp/tpl
|
||||||
cp tpl/templates/* "$HOME/.local/share/godot/export_templates/4.3.stable/" 2>/dev/null || true
|
mkdir -p "$TEMPLATE_DIR"
|
||||||
ls "$HOME/.local/share/godot/export_templates/4.3.stable" | head -5
|
for file in $NEEDED; do
|
||||||
|
cp "/tmp/tpl/templates/$file" "$TEMPLATE_DIR/$file"
|
||||||
|
done
|
||||||
|
ls "$TEMPLATE_DIR" | head -10
|
||||||
|
|
||||||
- name: Import project
|
- name: Import project
|
||||||
run: godot --headless --import
|
run: godot --headless --import
|
||||||
@@ -84,19 +91,19 @@ jobs:
|
|||||||
# NOTE: on Linux, makensis uses -D defines (the /D form is Windows-only
|
# NOTE: on Linux, makensis uses -D defines (the /D form is Windows-only
|
||||||
# and is rejected as a script path), and it resolves relative paths
|
# and is rejected as a script path), and it resolves relative paths
|
||||||
# against the script directory — pass absolute paths from the workspace.
|
# against the script directory — pass absolute paths from the workspace.
|
||||||
cp assets/textures/icon.ico packaging/windows/step-line.ico
|
cp assets/textures/icon.ico packaging/windows/gig-line.ico
|
||||||
makensis \
|
makensis \
|
||||||
-DVERSION="$VERSION" \
|
-DVERSION="$VERSION" \
|
||||||
-DARTIFACT="$PWD/build/step-line-windows-$VERSION.exe" \
|
-DARTIFACT="$PWD/build/gig-line-windows-$VERSION.exe" \
|
||||||
-DICON="$PWD/packaging/windows/step-line.ico" \
|
-DICON="$PWD/packaging/windows/gig-line.ico" \
|
||||||
-DOUT="$PWD/build/step-line-setup-$VERSION.exe" \
|
-DOUT="$PWD/build/gig-line-setup-$VERSION.exe" \
|
||||||
packaging/windows/step-line.nsi
|
packaging/windows/gig-line.nsi
|
||||||
|
|
||||||
- name: Verify Windows installer contents
|
- name: Verify Windows installer contents
|
||||||
run: |
|
run: |
|
||||||
VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)"
|
VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)"
|
||||||
ls -la build/step-line-setup-$VERSION.exe
|
ls -la build/gig-line-setup-$VERSION.exe
|
||||||
7z l build/step-line-setup-$VERSION.exe | grep -E "step-line.exe|uninstall.exe"
|
7z l build/gig-line-setup-$VERSION.exe | grep -E "gig-line.exe|uninstall.exe"
|
||||||
|
|
||||||
- name: Package Linux AppImage
|
- name: Package Linux AppImage
|
||||||
run: |
|
run: |
|
||||||
@@ -106,7 +113,7 @@ jobs:
|
|||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: step-line-build
|
name: gig-line-build
|
||||||
path: build/*
|
path: build/*
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
@@ -114,7 +121,7 @@ jobs:
|
|||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
env:
|
env:
|
||||||
# An explicit repo secret wins. Without one, publish_release.sh reuses
|
# An explicit repo secret wins. Without one, publish_release.sh reuses
|
||||||
# the local git extraheader that actions/checkout configures for the job
|
# the local git extraheader that actions/checkout configures on the job
|
||||||
# (the runner's own credentials), so tag builds still publish.
|
# (the runner's own credentials), so tag builds still publish.
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
VERSION: ${{ github.ref_name }}
|
VERSION: ${{ github.ref_name }}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ Thumbs.db
|
|||||||
*.new
|
*.new
|
||||||
|
|
||||||
# Logs and temp
|
# Logs and temp
|
||||||
|
tmp/
|
||||||
*.log
|
*.log
|
||||||
user://
|
user://
|
||||||
# CI runner workflow logs (written into the checkout by the Gitea runner)
|
# CI runner workflow logs (written into the checkout by the Gitea runner)
|
||||||
@@ -44,3 +45,50 @@ user://
|
|||||||
build/
|
build/
|
||||||
|
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
# AI / asset-development outputs (regenerated by the tools; not shipped in the game)
|
||||||
|
assets/models/concepts/
|
||||||
|
assets/models/npc/*.blend1
|
||||||
|
assets/textures/npc/ai/
|
||||||
|
assets/textures/ai/*
|
||||||
|
!assets/textures/ai/game_ready/
|
||||||
|
# Godot extracts embedded glTF textures next to the source on import
|
||||||
|
assets/models/generated/npc_base_*_albedo.png
|
||||||
|
assets/models/generated/npc_base_*_normal.png
|
||||||
|
|
||||||
|
# Local AppImage extraction
|
||||||
|
squashfs-root/
|
||||||
|
|
||||||
|
# Local agent skills. The third-party Blender pack is installed per machine and
|
||||||
|
# stays ignored; the two project-authored skills are tracked so the workflow can
|
||||||
|
# be shared and reviewed.
|
||||||
|
.agents/
|
||||||
|
!.agents/
|
||||||
|
.agents/*
|
||||||
|
!.agents/.gdignore
|
||||||
|
!.agents/skills/
|
||||||
|
.agents/skills/*
|
||||||
|
!.agents/skills/gig-line-blender-asset-contract/
|
||||||
|
!.agents/skills/gig-line-blender-asset-contract/**
|
||||||
|
!.agents/skills/gig-line-release-qa/
|
||||||
|
!.agents/skills/gig-line-release-qa/**
|
||||||
|
# Blender asset library: per-asset Cycles renders are local review output (the
|
||||||
|
# committed review artefacts are docs/blender_previews/sheet_*.jpg), and Godot
|
||||||
|
# extracts the GLB-embedded textures next to the models on import.
|
||||||
|
docs/blender_previews/*.png
|
||||||
|
assets/models/blender/**/*.png
|
||||||
|
!assets/models/blender/**/*_ao.png
|
||||||
|
assets/models/blender_src/*.blend1
|
||||||
|
# Editable Blender sources are developer-only; keep the .gdignore marker tracked.
|
||||||
|
assets/models/blender_src/*.blend
|
||||||
|
# Realism pass (tools/scene_concepts.py): candidate pools, per-run captures and
|
||||||
|
# generated reports stay local; validated picks are tracked under
|
||||||
|
# game_ready/v2/picks/ (ADR-0014). Concept references are committed in
|
||||||
|
# docs/screens/concepts/, but generated QA lives outside the repository.
|
||||||
|
assets/textures/ai/game_ready/v2/*
|
||||||
|
!assets/textures/ai/game_ready/v2/picks/
|
||||||
|
docs/screens/realism/
|
||||||
|
|
||||||
|
## Local pi agent session data
|
||||||
|
pi-session-*.html
|
||||||
|
pi-session-*.jsonl
|
||||||
@@ -14,6 +14,97 @@ The project should be developed in small, reviewable steps. Agents must keep pla
|
|||||||
- `MEMORY.md` — Persistent session memory for important decisions, discoveries, unresolved questions, constraints, and useful implementation context.
|
- `MEMORY.md` — Persistent session memory for important decisions, discoveries, unresolved questions, constraints, and useful implementation context.
|
||||||
- `README.md` — Root-level documentation for the project. It should explain what the game is, how to work on it, how to run it, and where to find project information.
|
- `README.md` — Root-level documentation for the project. It should explain what the game is, how to work on it, how to run it, and where to find project information.
|
||||||
- `AGENTS.md` — This collaboration and agent workflow guide.
|
- `AGENTS.md` — This collaboration and agent workflow guide.
|
||||||
|
- `CHANGELOG.md` — The version history of user-facing changes. Keep an entry for each patch, minor, and major version.
|
||||||
|
- `docs/development-status.md` — The implemented-features list and what is not yet implemented. Update it whenever a feature lands, is renamed, or is removed.
|
||||||
|
- `docs/architecture.md` — The arc42-style architecture documentation (goals, constraints, building blocks, runtime views, decisions, risks).
|
||||||
|
- `docs/gameplay.md` — Gameplay systems, venues, and the escalation model.
|
||||||
|
- `docs/asset-tooling.md` — Asset pipelines, model tools, and screenshot gates.
|
||||||
|
- `docs/npc-model-sourcing.md` — Character-source evaluation, licence rules and the character-development plan.
|
||||||
|
- `docs/getting-started.md` — Setup, run/test commands, and the controls table.
|
||||||
|
- `docs/smoke-checklist.md` — The manual smoke-test checklist.
|
||||||
|
- `docs/adr/` — Architecture Decision Records.
|
||||||
|
|
||||||
|
## Priority and Versioning
|
||||||
|
|
||||||
|
`TODO.md` is the priority source. The active order is:
|
||||||
|
|
||||||
|
1. unversioned development tracks;
|
||||||
|
2. versioned release milestones, in version order;
|
||||||
|
3. future/unversioned candidates and deferred work.
|
||||||
|
|
||||||
|
Do not assign a version number to an unversioned track until its scope is accepted into a
|
||||||
|
release. When a version is assigned, it must be the next semantic version that matches the
|
||||||
|
scope (major/minor/patch), not a placeholder.
|
||||||
|
|
||||||
|
## Local Blender skills (development aid)
|
||||||
|
|
||||||
|
The MIT `blender-skills` pack is installed locally at `.agents/skills/blender-skills/`
|
||||||
|
(94 skills plus a shared `references/` folder). It is **gitignored on purpose**: it is not
|
||||||
|
game content, not part of the build, tests, CI or release packages, and every contributor
|
||||||
|
may or may not have it. The agent runtime (Pi) gates project `.agents/skills` behind project
|
||||||
|
trust, so the trust prompt must be accepted before the skills load; `/skill:blender-director`
|
||||||
|
forces one.
|
||||||
|
|
||||||
|
Use them when a task touches `tools/blender_assets.py`, `tools/npc_pipeline.py`,
|
||||||
|
`tools/bake_ao.py`, `assets/models/blender/**`, or Blender-authored venue/prop geometry.
|
||||||
|
|
||||||
|
### Project-authored skills (tracked)
|
||||||
|
|
||||||
|
Two project skills are committed under `.agents/skills/` and are guidance for
|
||||||
|
any agent, not only Pi:
|
||||||
|
|
||||||
|
- `gig-line-blender-asset-contract` — the NPC/gear/prop contract, build order,
|
||||||
|
headless Blender commands, failure playbook and verification gates.
|
||||||
|
- `gig-line-release-qa` — the test/smoke matrix, visual judges, clean-worktree
|
||||||
|
build, checksum/tag/publish sequence and release records.
|
||||||
|
|
||||||
|
They are not runtime dependencies and do not move tool implementations into
|
||||||
|
`.agents/`. The third-party `blender-skills` pack stays local/gitignored.
|
||||||
|
|
||||||
|
### Routing
|
||||||
|
|
||||||
|
Start with `blender-director` for anything multi-discipline, then load only the
|
||||||
|
specialists the task needs:
|
||||||
|
|
||||||
|
| Work | Skills |
|
||||||
|
| --- | --- |
|
||||||
|
| Plan/route a Blender task, reference photo match | `blender-director` |
|
||||||
|
| Character mesh, face, hands, proportions | `character-artist`, `character-archetypes`, `retopology` |
|
||||||
|
| Skeleton, weights, pose/contract rigging | `rigging` |
|
||||||
|
| UV layout, texel density, UDIM | `uv-workflow` |
|
||||||
|
| Bakery, atlas, normal/AO/ORM maps | `texture-workflow` |
|
||||||
|
| PBR materials and shading | `materials` |
|
||||||
|
| Screenshot-driven material/light iteration | `lookdev` |
|
||||||
|
| Triangle/vertex budgets, naming, scene hygiene | `asset-optimization` |
|
||||||
|
| LOD tiers and impostors | `lod-pipeline` |
|
||||||
|
| Collision shapes and proxies | `collision-proxy` |
|
||||||
|
| GLB export for Godot | `godot-export`, `export-pipeline` |
|
||||||
|
| Gear, props, street furniture | `prop-artist`, `hard-surface` |
|
||||||
|
| Trees, hedges, foliage cards | `vegetation-artist` |
|
||||||
|
| Facades, venues, modular kits, dressing | `environment-artist`, `archviz`, `set-dressing`, `geometry-nodes` |
|
||||||
|
| Lighting, framing, final render/composite | `lighting`, `camera-cinematography`, `rendering`, `compositing` |
|
||||||
|
| Style direction (stylized / low-poly / realistic) | `stylized-style`, `lowpoly-style`, `realistic-style` |
|
||||||
|
| Ship/no-ship review | `qa-review` |
|
||||||
|
|
||||||
|
### Project rules that override a skill
|
||||||
|
|
||||||
|
1. **The project contract wins.** `npc_base` mesh node, the 13 contract bones at their rest
|
||||||
|
positions, five tint slots, front on Blender −Y / Godot +Z, grounded feet, 8,000
|
||||||
|
triangles / 8,000 glTF vertices per character, 1,500 per gear, 4,000 per prop. A skill
|
||||||
|
may recommend another topology, naming or budget scheme; do not adopt it unless the
|
||||||
|
change is explicitly planned and the contract, tests and docs are updated together.
|
||||||
|
2. **There is no Blender MCP here.** The skills assume MCP tools; this project runs Blender
|
||||||
|
as headless flatpak scripts (`tools/blender_assets.py`, `tools/npc_pipeline.py`). Translate
|
||||||
|
MCP steps into script changes or CLI runs. Never add an MCP dependency to the build, CI or
|
||||||
|
runtime.
|
||||||
|
3. **CI stays offline and skill-free.** Skills are local guidance only; every gate must still
|
||||||
|
work from a fresh checkout with committed `.glb`/`.png`/`.tres` files and Godot.
|
||||||
|
4. **No new asset source without the licence gate.** Skills may suggest reference packs or
|
||||||
|
generated meshes; ADR-0015 (CC0, or CC-BY with attribution in `CREDITS.md`) and the
|
||||||
|
provenance rules in `docs/npc-model-sourcing.md` still apply.
|
||||||
|
5. **Evidence, not vibes.** After a skill-informed change, run `blender_assets.py inspect`,
|
||||||
|
`audit` and `fit` plus the relevant capture/smoke gates, and say in the handoff which
|
||||||
|
skills informed the change and what the measurements were.
|
||||||
|
|
||||||
## Required Workflow
|
## Required Workflow
|
||||||
|
|
||||||
@@ -36,9 +127,22 @@ Do not assume that a task is unimplemented without checking the code and documen
|
|||||||
All planned implementation work must be represented in `TODO.md`.
|
All planned implementation work must be represented in `TODO.md`.
|
||||||
|
|
||||||
- Use headings to group related work into sections.
|
- Use headings to group related work into sections.
|
||||||
- Use checkbox items for each concrete task.
|
- Use level-2 headings (`##`) for milestones or stable themes. A milestone should state its goal
|
||||||
|
and an explicit exit criterion; a theme groups small related work that does not need a
|
||||||
|
milestone.
|
||||||
|
- Order level-2 sections by current priority: unversioned development tracks first, then
|
||||||
|
versioned release milestones in version order, then future/deferred work. Do not assign a
|
||||||
|
version number to an unversioned track until it is scheduled for a release.
|
||||||
|
- Use level-3 headings (`###`) for workstreams inside a milestone. Keep the hierarchy to three
|
||||||
|
levels: milestone → workstream → checkbox.
|
||||||
|
- Use checkbox items for each concrete task, normally under a level-3 workstream. Direct
|
||||||
|
checkboxes under a level-2 section are acceptable for small, single-workstream sections.
|
||||||
- Keep tasks small enough to complete and review independently.
|
- Keep tasks small enough to complete and review independently.
|
||||||
- Add new tasks when discoveries expand the scope.
|
- Add new tasks when discoveries expand the scope.
|
||||||
|
- When starting a task, claim it for the agent workflow by changing `[ ]` to `[-]`. Keep it `[-]`
|
||||||
|
while implementation or verification is in progress; only change it to `[x]` after the work and
|
||||||
|
relevant checks are finished. If work is abandoned or blocked, leave a clear note and return the
|
||||||
|
item to `[ ]` (or add a follow-up task).
|
||||||
- Mark a task complete only after the implementation and relevant verification are finished.
|
- Mark a task complete only after the implementation and relevant verification are finished.
|
||||||
- If any verification check fails, do not mark the task complete. Fix the failure, or if it cannot be fixed, add a follow-up task to `TODO.md`, record the failure in `MEMORY.md`, and report it in the handoff summary.
|
- If any verification check fails, do not mark the task complete. Fix the failure, or if it cannot be fixed, add a follow-up task to `TODO.md`, record the failure in `MEMORY.md`, and report it in the handoff summary.
|
||||||
- Do not use prose status updates as a substitute for checkboxes.
|
- Do not use prose status updates as a substitute for checkboxes.
|
||||||
@@ -49,27 +153,53 @@ Recommended format:
|
|||||||
```md
|
```md
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
## Core Gameplay
|
## Milestone X.Y.Z — goal and exit criterion
|
||||||
|
|
||||||
- [ ] Add player movement controls.
|
### Workstream A
|
||||||
- [ ] Implement NPC approach behavior.
|
|
||||||
- [x] Define escalation levels.
|
|
||||||
|
|
||||||
## Documentation
|
- [x] Completed task.
|
||||||
|
- [-] Task in progress. (claimed; implementation/verification in progress)
|
||||||
|
- [ ] Open task.
|
||||||
|
|
||||||
- [ ] Document the test procedure in `README.md`.
|
### Workstream B
|
||||||
|
|
||||||
|
- [ ] Another task.
|
||||||
|
|
||||||
|
**Exit:** a verifiable statement of what "done" means for the milestone.
|
||||||
|
|
||||||
|
## Theme
|
||||||
|
|
||||||
|
- [ ] A small housekeeping task with no milestone.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Recommended `DONE.md` format: a `# DONE` heading, followed by each moved level-2 section
|
||||||
|
unchanged, with a completion date line under each section heading.
|
||||||
|
|
||||||
|
Order the actual file as described in "Priority and Versioning": unversioned development tracks
|
||||||
|
first, then versioned milestones in version order, then future/deferred work.
|
||||||
|
|
||||||
### 3. Move Completed Sections to `DONE.md`
|
### 3. Move Completed Sections to `DONE.md`
|
||||||
|
|
||||||
When every task in a `TODO.md` section is complete:
|
`TODO.md` uses nested sections. Apply these completion rules:
|
||||||
|
|
||||||
1. Verify the work and update the checkbox items to `[x]`.
|
1. **Level-3 workstream:** verify the work, mark every checkbox `[x]`, and add a short completion
|
||||||
2. Move the entire completed section, including its heading and checklist, to `DONE.md`.
|
note. Leave the completed workstream under its parent milestone; do not move an isolated
|
||||||
3. Remove that section from `TODO.md`.
|
level-3 section to `DONE.md`.
|
||||||
4. Add a short completion note or date when useful.
|
2. **Level-2 milestone/theme:** it is complete only when its direct checkboxes and **all** of its
|
||||||
|
level-3 workstreams are complete. Verify the whole milestone, record its capabilities in
|
||||||
|
`docs/development-status.md` when it represents shipped functionality, and write the matching
|
||||||
|
`CHANGELOG.md` entry.
|
||||||
|
3. Move the **entire level-2 section**, including all level-3 headings and checklists, to
|
||||||
|
`DONE.md`. Remove it from `TODO.md` and add a completion date/note.
|
||||||
|
4. **Planning/overview sections** such as a gap analysis, roadmap overview, dependencies or
|
||||||
|
deferred ideas are not completion milestones. Do not move them merely because their checkboxes
|
||||||
|
are empty or complete; update or supersede them as the roadmap changes.
|
||||||
|
5. Do not move partially completed milestones. Leave them in `TODO.md` until every nested task is
|
||||||
|
complete, or split the unfinished work into a new smaller level-2 milestone first.
|
||||||
|
|
||||||
Do not move partially completed sections. Leave them in `TODO.md` until all of their tasks are complete, or split the section into smaller sections first.
|
When a completed milestone represents shipped functionality, also record the new capability in
|
||||||
|
`docs/development-status.md` so the implemented-features list stays accurate, and write a matching
|
||||||
|
entry in `CHANGELOG.md` (see "Maintain `CHANGELOG.md`" below).
|
||||||
|
|
||||||
### 4. Maintain `MEMORY.md`
|
### 4. Maintain `MEMORY.md`
|
||||||
|
|
||||||
@@ -78,7 +208,7 @@ Use `MEMORY.md` for information that should persist between sessions but does no
|
|||||||
Record items such as:
|
Record items such as:
|
||||||
|
|
||||||
- Important design and architecture decisions
|
- Important design and architecture decisions
|
||||||
- Current implementation state
|
- Notable implementation state and gotchas (the canonical implemented-features list lives in `docs/development-status.md`, not here)
|
||||||
- Known bugs and workarounds
|
- Known bugs and workarounds
|
||||||
- Commands needed to run, test, or build the project
|
- Commands needed to run, test, or build the project
|
||||||
- Decisions that future agents should not revisit unnecessarily
|
- Decisions that future agents should not revisit unnecessarily
|
||||||
@@ -128,9 +258,29 @@ The root-level `README.md` is the project’s public-facing technical and projec
|
|||||||
- Links to `PLAN.md`, `TODO.md`, `DONE.md`, `MEMORY.md`, and this file where appropriate
|
- Links to `PLAN.md`, `TODO.md`, `DONE.md`, `MEMORY.md`, and this file where appropriate
|
||||||
- Known limitations and contribution guidance
|
- Known limitations and contribution guidance
|
||||||
|
|
||||||
Update `README.md` whenever setup instructions, commands, project structure, or user-facing behavior changes.
|
Update `README.md` whenever setup instructions, commands, project structure, or user-facing behavior changes. The README is intentionally concise and links to the `docs/` files; keep `docs/development-status.md` (and the other docs it links to) synchronized with the implementation rather than pasting detail back into the README.
|
||||||
|
|
||||||
### 7. Implement in Small Increments
|
### 7. Maintain `CHANGELOG.md`
|
||||||
|
|
||||||
|
The root-level `CHANGELOG.md` records the user-facing changes for every release. It exists so players and contributors can see what changed between versions without reading the commit log.
|
||||||
|
|
||||||
|
- Add an entry for every patch, minor, and major version change.
|
||||||
|
- Follow Semantic Versioning (`major.minor.patch`): bump major only for changes that remove or break an existing player-facing feature, save format, or config contract, or that replace a system listed as a core pillar in PLAN.md; bump minor for new user-facing features; bump patch for fixes and small polish.
|
||||||
|
- A version increment is not complete until its `CHANGELOG.md` entry, `config/version` update and
|
||||||
|
matching local release build have all been produced and verified.
|
||||||
|
- Group each entry under `Added`, `Changed`, `Fixed`, and `Removed` headings, and date every entry.
|
||||||
|
- Describe changes in user-facing terms, not as raw commit messages.
|
||||||
|
- Add the entry when its work ships. If the version number is not yet known, record it under `Unreleased` as a staging entry; moving it into a numbered version is a version increment and is complete only when the `config/version` update and matching local release build have also been produced and verified (see "Commit, Build and Version Discipline").
|
||||||
|
|
||||||
|
When a full executable level-2 milestone/theme in `TODO.md` is implemented, completed, and moved to `DONE.md`:
|
||||||
|
|
||||||
|
1. Verify the work and mark every checkbox in the section `[x]`.
|
||||||
|
2. Use the section heading and its completed items to describe the changes and write a `CHANGELOG.md` entry under the next appropriate version.
|
||||||
|
3. Move the section to `DONE.md`.
|
||||||
|
|
||||||
|
Do not move a completed section to `DONE.md` without adding its `CHANGELOG.md` entry.
|
||||||
|
|
||||||
|
### 8. Implement in Small Increments
|
||||||
|
|
||||||
For each task:
|
For each task:
|
||||||
|
|
||||||
@@ -138,12 +288,44 @@ For each task:
|
|||||||
2. Inspect the relevant files and dependencies.
|
2. Inspect the relevant files and dependencies.
|
||||||
3. Make the smallest coherent change that completes the task.
|
3. Make the smallest coherent change that completes the task.
|
||||||
4. Run the relevant tests, checks, or manual verification.
|
4. Run the relevant tests, checks, or manual verification.
|
||||||
5. Update documentation and `MEMORY.md` if the change creates lasting context.
|
5. Update documentation if the change creates lasting context: add features to `docs/development-status.md`, adjust the README links or other `docs/` files as needed, and record decisions/gotchas in `MEMORY.md`.
|
||||||
6. Mark the task complete.
|
6. Mark the task complete.
|
||||||
7. Move its section to `DONE.md` when the entire section is complete.
|
7. Keep completed level-3 workstreams in place until the parent milestone is complete; then move the entire level-2 milestone to `DONE.md` after recording its `CHANGELOG.md` entry.
|
||||||
|
8. Commit each coherent, verified change as it is completed. Do not wait until a whole
|
||||||
|
milestone is finished; keep commits small and descriptive and push them when the branch is
|
||||||
|
shareable.
|
||||||
|
9. If the change is a release (a major, minor, or patch version increment), follow the Release
|
||||||
|
Procedure in "Commit, Build and Version Discipline".
|
||||||
|
10. Push committed changes to the shared repository if applicable.
|
||||||
|
11. Note the new version, build artifacts or tag in the relevant documentation and handoff
|
||||||
|
summary.
|
||||||
|
|
||||||
Avoid unrelated refactors unless they are required for the task or explicitly approved.
|
Avoid unrelated refactors unless they are required for the task or explicitly approved.
|
||||||
|
|
||||||
|
### 9. Commit, Build and Version Discipline
|
||||||
|
|
||||||
|
- **Commit often.** Make a commit after each coherent change or verified task, not only when a
|
||||||
|
milestone or release is finished. Keep the working tree clean between tasks where possible.
|
||||||
|
- **Push regularly.** Push shareable commits to the shared repository; do not accumulate a long
|
||||||
|
local-only history.
|
||||||
|
- **Build every version.** Every major, minor and patch increment follows the Release Procedure
|
||||||
|
below; a changelog entry alone is not a release.
|
||||||
|
- **CI is a second gate, not a substitute.** Tag builds remain automated, but the local build is
|
||||||
|
the required pre-tag check for every version.
|
||||||
|
- **Unversioned tracks stay unversioned.** A development track does not get a placeholder
|
||||||
|
version; the version is assigned when the work is scoped into a release.
|
||||||
|
|
||||||
|
**Release Procedure** — follow these steps in order for every major, minor, or patch version
|
||||||
|
increment:
|
||||||
|
|
||||||
|
1. Update `config/version`.
|
||||||
|
2. Write the `CHANGELOG.md` entry for the version.
|
||||||
|
3. Run the local release build (`tools/build_releases.sh`).
|
||||||
|
4. Verify the build artifacts and checksums.
|
||||||
|
5. Commit the changes.
|
||||||
|
6. Tag the version.
|
||||||
|
7. Push the commit and tag.
|
||||||
|
|
||||||
## Collaboration Rules
|
## Collaboration Rules
|
||||||
|
|
||||||
- Prefer clear, reversible changes over broad rewrites.
|
- Prefer clear, reversible changes over broad rewrites.
|
||||||
@@ -152,6 +334,8 @@ Avoid unrelated refactors unless they are required for the task or explicitly ap
|
|||||||
- Do not claim a task is complete without verification.
|
- Do not claim a task is complete without verification.
|
||||||
- Keep documentation synchronized with the implementation.
|
- Keep documentation synchronized with the implementation.
|
||||||
- Mention files changed and checks run in handoff summaries.
|
- Mention files changed and checks run in handoff summaries.
|
||||||
|
- Commit coherent increments as you go; do not wait until the end of a milestone.
|
||||||
|
- Follow the Release Procedure in "Commit, Build and Version Discipline" whenever a major, minor or patch version is incremented.
|
||||||
- Leave follow-up work as checkbox items in `TODO.md` rather than hiding it in prose.
|
- Leave follow-up work as checkbox items in `TODO.md` rather than hiding it in prose.
|
||||||
- If blocked, document the blocker in `MEMORY.md` and add a corresponding task to `TODO.md`.
|
- If blocked, document the blocker in `MEMORY.md` and add a corresponding task to `TODO.md`.
|
||||||
- Avoid overwriting another contributor’s work; inspect recent changes before editing shared files.
|
- Avoid overwriting another contributor’s work; inspect recent changes before editing shared files.
|
||||||
@@ -163,6 +347,10 @@ Before handing off work, an agent must run every check below that applies to the
|
|||||||
- Run automated tests.
|
- Run automated tests.
|
||||||
- Run formatting, linting, or type checks.
|
- Run formatting, linting, or type checks.
|
||||||
- Start the game or application and perform a basic smoke test.
|
- Start the game or application and perform a basic smoke test.
|
||||||
|
- For 3D/Blender changes: `blender_assets.py inspect`, `audit` and `fit`, the relevant capture
|
||||||
|
gate, and a note of which local skills informed the change.
|
||||||
|
- If the local Blender skills are not installed, state "no local skills used" in the handoff
|
||||||
|
and list the skill-free checks that were run instead.
|
||||||
- Check that documentation and task status reflect the actual state.
|
- Check that documentation and task status reflect the actual state.
|
||||||
- Review the final diff for accidental changes or unfinished markers.
|
- Review the final diff for accidental changes or unfinished markers.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,564 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable user-facing changes to Gig Line. The format follows
|
||||||
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions use
|
||||||
|
Semantic Versioning.
|
||||||
|
|
||||||
|
## [0.9.7] — 2026-11-04
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Character development: a more realistic face on all eight library characters
|
||||||
|
(higher head subdivision, brow ridge, both eyelids, fuller lower lip) and four
|
||||||
|
crowd-variety characters (`npc_crowd_a`–`d`) with deterministic per-role pools
|
||||||
|
so the routine crowd no longer clones a single look.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The NPC pose harness now captures seated, walking and idle deformation (the
|
||||||
|
harness froze the avatar's process so the FSM could no longer revert the pose).
|
||||||
|
|
||||||
|
## [0.10.0-rc.1] — 2026-11-03
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Prepared the release-candidate packages for Linux, Windows, macOS, Web and the
|
||||||
|
optional Desktop HQ zip. The Web/HQ archives now include the credits, privacy,
|
||||||
|
accessibility and known-limitations pages, and release publishing writes
|
||||||
|
SHA-256 checksums and rolls back partial uploads.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Corrupt or partial save files now fall back to safe defaults without logging a
|
||||||
|
parse error, and a failed atomic replace leaves the previous valid settings
|
||||||
|
intact. Added migration coverage for 0.8-era profiles and 0.9.0 checkpoints
|
||||||
|
across all three venues.
|
||||||
|
|
||||||
|
## [0.9.6] — 2026-11-02
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The UI, model and texture passes are now integrated into a verified checkpoint
|
||||||
|
build. The gallery, NPC portraits, realism captures, asset audits, smoke
|
||||||
|
matrix, 24-NPC budget and Web/Desktop HQ packages were regenerated and
|
||||||
|
re-checked; no gameplay or content changes were introduced.
|
||||||
|
|
||||||
|
## [0.9.5] — 2026-11-02
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- NPC albedo, normal and ORM maps are now 256² instead of 128², giving the
|
||||||
|
face, fabric and gear materials more detail at portrait distance.
|
||||||
|
- NPC fabric maps now repeat four times across the body and skip the baked
|
||||||
|
per-character AO, removing the large blotchy patches from shirts, jackets and
|
||||||
|
trousers at portrait distance.
|
||||||
|
- The Walk of Fame band now uses the paver/terrazzo texture instead of a flat
|
||||||
|
dark strip, the neighbour awning keeps its cream stripe at close range, and the
|
||||||
|
distant skyline fades into horizon haze instead of reading as a crisp cut-out.
|
||||||
|
|
||||||
|
## [0.9.4] — 2026-11-02
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Character meshes now have an 8,000-triangle / 8,000-vertex library budget
|
||||||
|
(up from 6,000 / 5,000) to support the remaining face, grip and clothing
|
||||||
|
detail; the `npc_base` fallback keeps its original 6,000 / 4,000 contract.
|
||||||
|
- Character necks are fuller and shoulders slope from the neck instead of
|
||||||
|
reading as a thin post on a hanger.
|
||||||
|
- Character eyes sit in the socket behind a thin upper lid, and the chin and
|
||||||
|
cheekbones are sculpted into the head instead of reading as separate spheres.
|
||||||
|
- Character torsos taper from the chest to the waist and flare slightly into
|
||||||
|
the hips, so shirts and jackets sit less like straight tubes.
|
||||||
|
- Character hands now have a wider palm, bent fingers with rounded tips and a
|
||||||
|
separate thumb instead of flat bars.
|
||||||
|
- Character noses gained ala wings and a smaller bridge/tip, the mouth is a
|
||||||
|
rounded slot, and the ears have a darker inner concha.
|
||||||
|
- Hand-held items now sit forward in the open palm instead of being partly
|
||||||
|
buried inside the hand.
|
||||||
|
- Shoes and responder boots now have rounded toes and heels instead of reading
|
||||||
|
as flat blocks.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Tourists no longer appear to carry two backpacks: the photo-pose camera bag is
|
||||||
|
hidden except while they are actually taking a picture.
|
||||||
|
- Backpack shoulder straps are now flattened bands that sit outside the chest
|
||||||
|
instead of thin rods partly buried in the torso.
|
||||||
|
|
||||||
|
## [0.9.3] — 2026-11-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Eight committed gallery captures now cover the start menu, pause, settings,
|
||||||
|
shift review, emergency radio, action wheel, crowd panic and responder
|
||||||
|
arrival, so every screen-design reference has a deterministic current-build
|
||||||
|
capture.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- All session, settings, radio, Help, review and timeline action buttons now
|
||||||
|
share explicit normal, hover, pressed, focus and disabled styles. Timeline
|
||||||
|
Previous/Next disable at their boundaries.
|
||||||
|
- Keyboard and gamepad focus rings now wrap through every modal, with explicit
|
||||||
|
default focus and A/B accept/cancel bindings; hidden Continue and disabled
|
||||||
|
timeline controls are skipped.
|
||||||
|
- Esc and right-click now close the top panel consistently, restore the venue
|
||||||
|
menu after menu Settings, and recapture the mouse without leaving the guard
|
||||||
|
frozen. Help no longer opens Pause behind itself.
|
||||||
|
- The HUD target chip grows and caps with the selected font scale, top labels
|
||||||
|
clip safely, and long action-button labels ellipsize instead of pushing panels
|
||||||
|
wider than the screen.
|
||||||
|
- Campaign reset and tutorial replay now share a guarded confirmation dialog;
|
||||||
|
repeated presses cannot stack dialogs or fire the action twice. Emergency
|
||||||
|
hold-to-send still submits once and ignores the release after submission.
|
||||||
|
- NPC reason answers now use role-specific dialogue contexts, so staff, guests,
|
||||||
|
delivery workers, performers and other archetypes answer instead of silently
|
||||||
|
deflecting the Reason verb.
|
||||||
|
- Hollywood and the TV Studio now have door-staff lines for their wristband,
|
||||||
|
ticket, credential and guest-list checks.
|
||||||
|
- Clarified the emergency radio cancellation message, the Ask verb in Help/HUD
|
||||||
|
hints, the start-menu key hint and the campaign-reset confirmation.
|
||||||
|
|
||||||
|
## [0.9.2] — 2026-11-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- A stylized reconstruction of the real El Capitan Entertainment Centre block at
|
||||||
|
6840 Hollywood Boulevard: six Ionic columns, eight stone steps, the Masonic
|
||||||
|
parapet motto and medallions, red event banners, central and side entrances,
|
||||||
|
the El Capitan Theatre and retail neighbours, the Ovation Hollywood arcade,
|
||||||
|
the Walk of Fame and four-lane street markings.
|
||||||
|
- Real-site reference notes and AI art-direction frames for the Hollywood block.
|
||||||
|
- The Joke Shop is now a Sunset Strip comedy-club block based on The Comedy Store at
|
||||||
|
8433 Sunset Blvd: a 27 m black hall under a signature-covered canopy, a circular
|
||||||
|
**THE JOKE SHOP** blade sign, red velvet curtain bays, a west-end entrance, cream
|
||||||
|
and white neighbours, a dry hillside of stucco houses and palms, and a low retail
|
||||||
|
and billboard frontage across Sunset.
|
||||||
|
- Real-site reference notes and AI art-direction frames for the Joke Shop block,
|
||||||
|
including day, night and across-the-street concepts.
|
||||||
|
- A separate day capture stage for the Joke Shop, so the club can be compared in
|
||||||
|
both lighting states.
|
||||||
|
- Completed the UI string migration: start menu, settings, pause, Help, HUD,
|
||||||
|
radio and shift-review labels now come from the English string table.
|
||||||
|
- Separate **Hollywood Boulevard — Day** and **Hollywood Boulevard — Night**
|
||||||
|
rows in the venue menu. Night starts directly in the late phase and keeps the
|
||||||
|
full shift after dark.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The default Hollywood venue is no longer a generic brick entrance; the venue
|
||||||
|
name and policy copy now identify the El Capitan Entertainment Centre.
|
||||||
|
- Night lighting on the landmark uses low-energy practical washes rather than
|
||||||
|
the street-lamp brightness, so the facade reads without blowing out.
|
||||||
|
- The Joke Shop lighting now uses warm sconces, canopy downlights and ground
|
||||||
|
uplights; the hillside stays a dark silhouette at night instead of a lit wall.
|
||||||
|
- The Joke Shop venue copy and gallery captions describe the Sunset Strip block,
|
||||||
|
not the old brick alley.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The Ovation Hollywood sign faces the boulevard instead of rendering mirrored.
|
||||||
|
- Local night lights use linear attenuation instead of constant falloff, so
|
||||||
|
sconces and street lamps pool on the ground and stone rather than washing the
|
||||||
|
whole facade flat and yellow.
|
||||||
|
- The captured HUD clears stale objectives and target prompts between staged
|
||||||
|
screenshots.
|
||||||
|
- The Joke Shop beauty capture closes a stale wand/inspection panel before shooting
|
||||||
|
and frames the entrance and sign instead of a nearby target.
|
||||||
|
- Multiline HUD and wand strings now render real line breaks instead of a literal
|
||||||
|
`\n`; the wand panel also lists the guest's carried items.
|
||||||
|
- Final-context NPC gear fit: authorized-guest lanyards now show their badge in
|
||||||
|
front of the jacket, and responder helmets sit above the eyes instead of across
|
||||||
|
the brow.
|
||||||
|
|
||||||
|
## [0.9.1] — 2026-11-02
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Unified the project, local tooling and CI/release environment on Godot 4.7.2,
|
||||||
|
including matching export templates and project feature metadata.
|
||||||
|
- Evaluated Gitea Actions cache for the Godot binary/templates; disabled it after
|
||||||
|
cache saves timed out or were rejected, pending a healthy host cache service.
|
||||||
|
- Updated release and setup documentation for the 4.7.2 baseline.
|
||||||
|
|
||||||
|
## [0.9.0] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Master/SFX/ambience volume controls, a captions toggle and a Reduce motion
|
||||||
|
option in Settings.
|
||||||
|
- A 24-NPC performance budget smoke and expanded audio/accessibility coverage.
|
||||||
|
- Explicit production-quality scope for animation, audio, localization and the
|
||||||
|
Godot 4.3-CI/4.7-local support matrix.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Coat and dress strides are shortened to reduce hem/leg clipping.
|
||||||
|
- Help and the shift-review surface expose the current policy and accessibility
|
||||||
|
settings.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Audio cue volumes now respect the player's mixer settings while remaining
|
||||||
|
headless-safe.
|
||||||
|
|
||||||
|
## [0.8.1] — 2026-11-01
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Normal radio reports now send on click. The button was wired only to the
|
||||||
|
emergency hold-to-confirm handlers, so a non-emergency press did nothing.
|
||||||
|
|
||||||
|
## [0.8.0] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- A first-shift tutorial with contextual prompts, a safe visitor and replay/skip.
|
||||||
|
- A three-venue campaign shell with shift progression and a Continue action.
|
||||||
|
- Resume-from-checkpoint for paused shifts, including NPC, incident and system state.
|
||||||
|
- Easy/normal/hard difficulty presets for spawn pacing, compliance tolerance and
|
||||||
|
report strictness.
|
||||||
|
- A searchable in-game Help overlay with controls and the current venue policy.
|
||||||
|
- Recovery events, contextual NPC dialogue and an eligible-event debug preview.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The shift review now includes venue policy, campaign next steps and continued
|
||||||
|
review controls.
|
||||||
|
- Event scheduling suppresses recently played scripted events and respects the
|
||||||
|
difficulty spawn multiplier.
|
||||||
|
- Save data is written atomically and split into settings, campaign, shift
|
||||||
|
history and versioned checkpoint files.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Missing/older settings migrate through defaults; a partial write cannot
|
||||||
|
replace a valid save.
|
||||||
|
- The HUD debug overlay now refreshes and shows the next eligible events.
|
||||||
|
|
||||||
|
## [0.7.3] — 2026-11-01
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Mouse look now reads relative motion at the `_input` stage, so a full-screen
|
||||||
|
HUD or focused control cannot swallow it while the cursor is captured.
|
||||||
|
- Mouse capture is restored after window/compositor focus returns, and clicking
|
||||||
|
back into a gameplay window recaptures it instead of opening the radial menu.
|
||||||
|
- The HUD is mouse-transparent; its presentation controls no longer block
|
||||||
|
gameplay mouse input.
|
||||||
|
|
||||||
|
## [0.7.2] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Automatic OpenRouter model selection for the screenshot judges: the cheapest
|
||||||
|
currently available image+JSON-capable model is used, with ordered fallbacks
|
||||||
|
and a pinned `GIG_LINE_JUDGE_MODEL`/`--model` override.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The judge reports the model that produced each verdict and fails clearly on an
|
||||||
|
invalid API key instead of retrying other models.
|
||||||
|
- The screen-design and realism reports now include the fresh judge results.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The blocked screenshot judge now runs with the valid `OPENROUTER_API_KEY`
|
||||||
|
(`13/13` screen designs match, `0` diff).
|
||||||
|
|
||||||
|
## [0.7.1] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- An optional Desktop HQ build for Windows and Linux that uses Forward+,
|
||||||
|
volumetric fog and SDFGI, packaged with an `override.cfg` beside the
|
||||||
|
executable.
|
||||||
|
- Volumetric flashlight haze in the Desktop HQ build, with a LATE-phase preset
|
||||||
|
tuned so the entrance facade stays readable.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The normal Compatibility build remains the default; the preset only affects
|
||||||
|
the extracted Desktop HQ copy.
|
||||||
|
- Night-time light falloff adapts to Godot 4.7 while keeping the 4.3 baseline
|
||||||
|
unchanged.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The flashlight no longer disappears at night on Godot 4.7 under the old
|
||||||
|
attenuation curve.
|
||||||
|
- Headless and Web builds stay non-volumetric and boot normally when Forward+
|
||||||
|
is unavailable.
|
||||||
|
|
||||||
|
## [0.7.0] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Distinct venue geometry: the Joke Shop is now a narrow brick alley with a
|
||||||
|
shuttered facade, warm wall lantern, dumpster and AC clutter; the TV Studio
|
||||||
|
has a stone facade, bulb-lit canopy, glossy black car and a stanchion/barrier
|
||||||
|
line at the door.
|
||||||
|
- The README gallery includes captured Joke Shop and TV Studio views.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Venue geometry is data-driven through `WorldBuilder.VENUE_SPECS`; switching
|
||||||
|
venues rebuilds the world while keeping the guard and UI intact.
|
||||||
|
- Each venue has its own navigation bounds and concept-gallery mapping.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- The Joke Shop no longer renders the Hollywood skyline or marquee.
|
||||||
|
- The TV Studio no longer uses the boulevard marquee or palm-lined layout.
|
||||||
|
|
||||||
|
## [0.6.1] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Per-phase AI skyline panoramas (day, dusk, night) replacing the flat box skyline.
|
||||||
|
- Higher-detail surface textures with normal maps (brick, concrete, asphalt, awning, and more).
|
||||||
|
- A runtime image root (`assets/images/`) with a UI and scene art loader; the start menu,
|
||||||
|
pause, settings, shift review, radio, wand, inspection, and radial menu are styled against
|
||||||
|
their mockups.
|
||||||
|
- NPC face and hand detail: eyes, nose, lips, ears, fingered hands, and cuffed sleeves.
|
||||||
|
- A staged NPC portrait capture for judging avatar quality up close.
|
||||||
|
- Gear-fit checks (`tools/blender_assets.py fit`) and regression tests for head and back gear.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Scene lighting, fog, sun angle, and night glow retuned to match the AI concept frames;
|
||||||
|
the Late phase uses wet asphalt.
|
||||||
|
- The flashlight is a tighter forward spot that reads in the dark.
|
||||||
|
- Sunglasses narrowed to sit inside the face silhouette; shoulder bag moved clear of the
|
||||||
|
coat and dress skirts; firefighter helmet lifted off the brow.
|
||||||
|
- The README now links to focused docs pages for gameplay, architecture, setup, asset
|
||||||
|
tooling, development status, and the manual smoke checklist; `PLAN.md` was compacted
|
||||||
|
into a design plan.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Sidewalk overexposure at the entrance reduced to match the concept target.
|
||||||
|
- The body-camera overlay no longer overlaps the shift/reputation/crowd status.
|
||||||
|
- Selecting a venue releases the menu input lock and recaptures the mouse.
|
||||||
|
- The shoulder bag no longer clips through the coat's skirt; the firefighter helmet no
|
||||||
|
longer cuts into the brow ridge; the portrait subject now faces the camera.
|
||||||
|
- `--portrait-type` is now parsed and applied instead of being ignored.
|
||||||
|
|
||||||
|
## [0.6.0] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Blender-authored character, gear and prop library with baked ambient occlusion.
|
||||||
|
- Higher-resolution environment textures with tangent normal maps.
|
||||||
|
- Phase-driven emissive details on library props, characters and street dressing.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The game was renamed from "Step Line" to "Gig Line".
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- NPCs now face their movement direction instead of walking backwards.
|
||||||
|
|
||||||
|
## [0.5.0] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Articulated NPC characters with layered outfits, hand items, face/hair variety,
|
||||||
|
idle behaviour, reaction symbols, queue offsets and three LOD tiers.
|
||||||
|
- Player acceleration/braking, step-up assist, head bob, landing dip,
|
||||||
|
precision-walk feedback and surface-aware footsteps.
|
||||||
|
- Remappable keyboard and gamepad controls.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Crowd roles now select deterministic character variants and props.
|
||||||
|
|
||||||
|
## [0.4.0] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Collision and navigation footprints for every street prop.
|
||||||
|
- Zoned prop placements with deterministic variants and small clutter.
|
||||||
|
|
||||||
|
## [0.3.3] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Staged in-game captures for the wand screening, escalation HUD, and body-camera timeline screens.
|
||||||
|
|
||||||
|
## [0.3.2] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- AI reference mockups for the seven remaining catalogue screens.
|
||||||
|
|
||||||
|
## [0.3.1] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Prop models drawn with a single texture atlas (one material per prop).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The Web build is listed in the README downloads table.
|
||||||
|
|
||||||
|
## [0.3.0] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Surface-material profiles, ACES tonemapping, glow, aerial fog and phase-based
|
||||||
|
colour grading.
|
||||||
|
- Framed facade windows, door trim, curb and lane markings.
|
||||||
|
- Generated street-furniture models and the Web export.
|
||||||
|
- Aim-free targeting, a focus cone and precision walk.
|
||||||
|
|
||||||
|
## [0.2.16] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Venues authorable as editor `.tres` resources.
|
||||||
|
|
||||||
|
## [0.2.15] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- A vision-model judge for comparing screenshots against reference designs.
|
||||||
|
|
||||||
|
## [0.2.14] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Anonymous release downloads enabled on the host.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `Strings.fmt` keeps positional placeholders stable when new keys are added.
|
||||||
|
|
||||||
|
## [0.2.13] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Missing scenario content: Joke Shop reservation/ID cases, wand refusal and late
|
||||||
|
arrival events, the studio filming-boundary repeat, and a celebrity-arrival crowd spike.
|
||||||
|
|
||||||
|
## [0.2.12] — 2026-10-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Release uploads fail loudly instead of leaving an empty release with a green job.
|
||||||
|
- Pruned releases restored and CI publishing verified end to end.
|
||||||
|
|
||||||
|
## [0.2.11] — 2026-10-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Releases publish using the runner's checkout credentials when no token or git
|
||||||
|
credentials exist.
|
||||||
|
|
||||||
|
## [0.2.10] — 2026-10-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Release publishing fixed with a CI token fallback.
|
||||||
|
|
||||||
|
## [0.2.9] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Accessibility pass: escalation feedback that reads in grayscale and a working HUD font scale.
|
||||||
|
|
||||||
|
## [0.2.8] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Procedural crowd ambience that rises with crowd mood, and a radio crackle.
|
||||||
|
|
||||||
|
## [0.2.7] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- NPC theater poses: sitting, taking photos, filming, and performing.
|
||||||
|
|
||||||
|
## [0.2.6] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- A body-camera timeline on the shift review for stepping through recorded events.
|
||||||
|
|
||||||
|
## [0.2.5] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Emergency aftermath: evacuation clears the entrance, and unjustified calls write
|
||||||
|
an investigation note with a false-report reputation hit.
|
||||||
|
|
||||||
|
## [0.2.4] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Crowd presentation: onlookers rubberneck at incidents and clear out during panic.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- NPC navigation issues resolved.
|
||||||
|
|
||||||
|
## [0.2.3] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Remaining PLAN actions: eye contact, "I recognize this person", a formal-warning
|
||||||
|
body-camera flag, blocking that stations the guard in front of the door, and
|
||||||
|
requesting a reason for entry.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- A dead incident-recording path.
|
||||||
|
|
||||||
|
## [0.2.2] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Session shell: startup menu with venue selection, pause overlay, settings
|
||||||
|
persistence, and a "run another shift" flow back to the menu.
|
||||||
|
- Control pass: focus cone, head ring, radial action menu, and named input actions.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Release publishing now uploads the installer and AppImage.
|
||||||
|
|
||||||
|
## [0.2.1] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Windows NSIS installer and Linux AppImage packaging; all exports embed the PCK.
|
||||||
|
- An AI texture generation mode in the asset pipeline.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- NSIS build fixed for Linux (absolute defines, space-free installed exe, BMP icon).
|
||||||
|
- Runtime hygiene: RefCounted cycle cleanup, full-length shifts, and a wand cooldown.
|
||||||
|
|
||||||
|
## [0.2.0] — 2026-10-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- First playable build of the security-guard simulation at the Hollywood Boulevard entrance.
|
||||||
|
- Core simulation: shift coordination, venue scenarios, event scheduling, NPC movement
|
||||||
|
and states, and escalation from observation through deterrence, warnings, and blocking.
|
||||||
|
- Access verification for wristbands, IDs, tickets, credentials, and guest lists, with
|
||||||
|
admit/redirect/hold/refuse decisions and feedback scoring.
|
||||||
|
- Escalation levels 0–5 with radio assistance and a hold-to-confirm emergency call.
|
||||||
|
- Incident recording, body-camera evidence, reputation scoring, crowd reactions, and
|
||||||
|
an end-of-shift review.
|
||||||
|
- Scenario content for Hollywood Boulevard, The Joke Shop, and the TV Studio, including
|
||||||
|
a mental-health crisis event.
|
||||||
|
- A protester NPC, the "call someone else" reaction, a lockable door, and a flashlight.
|
||||||
|
- Save/persistence, navmesh pathfinding, and JSON localization.
|
||||||
|
- Visual polish: phase lighting, street lamps, marquee, street dressing, and NPC variety.
|
||||||
|
- Detailed graphics: authored textures, a humanoid NPC rig, and HUD/inspection feedback.
|
||||||
|
- Downloadable Linux, Windows, and macOS builds published by CI.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- The dialogue tone-of-voice picker (reactions now depend on escalation level and NPC profile).
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Credits and Provenance
|
||||||
|
|
||||||
|
Gig Line is a local, single-player security-shift game built with Godot 4 and
|
||||||
|
GDScript. This file records the asset provenance needed for the release review;
|
||||||
|
it is not a legal opinion or a claim that every provider licence has already
|
||||||
|
been cleared for public distribution.
|
||||||
|
|
||||||
|
## Game, code and design
|
||||||
|
|
||||||
|
- Game design, systems, scenarios, dialogue and UI code: the Gig Line project.
|
||||||
|
- Engine: [Godot Engine 4.7.2](https://godotengine.org/), MIT licence.
|
||||||
|
- The game bundles no third-party fonts; it uses Godot's default theme font.
|
||||||
|
- No third-party music, sound effects or voice recordings are bundled. Crowd
|
||||||
|
ambience and radio cues are generated procedurally at runtime.
|
||||||
|
|
||||||
|
## 3D characters, gear and props
|
||||||
|
|
||||||
|
The character, gear and prop library is generated by project-owned scripts
|
||||||
|
(`tools/blender_assets.py`, `tools/npc_pipeline.py`, `tools/model_pipeline.py`)
|
||||||
|
using Blender. The `.blend` working sources are developer artifacts and are not
|
||||||
|
shipped. The exported `.glb` files and baked AO maps are project assets; they do
|
||||||
|
not embed third-party models or purchased asset packs.
|
||||||
|
|
||||||
|
### Development-only research previews
|
||||||
|
|
||||||
|
Two review sheets under `docs/blender_previews/` compare source candidates for
|
||||||
|
the future NPC variety work. They are development documentation and are not
|
||||||
|
bundled with the game or its release packages. The underlying source models are
|
||||||
|
kept outside version control.
|
||||||
|
|
||||||
|
- [`sheet_npc_sources_research.jpg`](docs/blender_previews/sheet_npc_sources_research.jpg)
|
||||||
|
includes renders of **Quaternius Ultimate Modular Characters** (CC0 1.0,
|
||||||
|
<https://quaternius.com/>), **Kenney Blocky Characters 2.0** (CC0 1.0,
|
||||||
|
<https://kenney.nl/assets/blocky-characters>), and **CesiumMan** and
|
||||||
|
**RiggedFigure** (CC BY 4.0, © 2017 Cesium,
|
||||||
|
<https://github.com/KhronosGroup/glTF-Sample-Assets>).
|
||||||
|
- [`sheet_npc_crowd_prototypes.jpg`](docs/blender_previews/sheet_npc_crowd_prototypes.jpg)
|
||||||
|
contains renders of project-owned generator prototypes.
|
||||||
|
|
||||||
|
The CC-BY preview images are included only as development evidence. Any future
|
||||||
|
shipped derivative of CesiumMan or RiggedFigure must keep the attribution above.
|
||||||
|
|
||||||
|
The venue geometry is authored procedurally in `src/game/world_builder.gd` as a
|
||||||
|
stylized reconstruction. The Joke Shop is a fictional stand-in for the real
|
||||||
|
Sunset Strip block; the game does not claim endorsement by, or an exact replica
|
||||||
|
of, any real venue or tenant.
|
||||||
|
|
||||||
|
### Development-only reference images
|
||||||
|
|
||||||
|
The venue reference boards under `docs/screens/refs/` are development documentation and are not
|
||||||
|
bundled with the game or its release packages. Files under CC BY are attributed here as well as in
|
||||||
|
their folder sidecar:
|
||||||
|
|
||||||
|
- `docs/screens/refs/hollywood/el-capitan-facade-day-wikimedia-2009-10-27.jpg` — "woo (Irvine, CA,
|
||||||
|
USA)", CC BY 2.0, via Wikimedia Commons.
|
||||||
|
- `docs/screens/refs/hollywood/el-capitan-theatre-blade-sign-day-wikimedia-2008-10.jpg` — Andreas
|
||||||
|
Praefcke, CC BY 3.0, via Wikimedia Commons.
|
||||||
|
- `docs/screens/refs/hollywood/premiere-staging-street-day-wikimedia-2006-02-12.jpg` — John
|
||||||
|
Barreiros, CC BY 2.0, via Wikimedia Commons.
|
||||||
|
- Public-domain files in the same folder: Charles W. Beam (1922) and Gary Minnaert (2006).
|
||||||
|
- `docs/screens/refs/hollywood/el-capitan-theatre-marquee-neighbour-day-wikimedia-2009-07-11.jpg` —
|
||||||
|
Alan Light, CC BY 2.0, via Wikimedia Commons.
|
||||||
|
- `docs/screens/refs/hollywood/hollywood-boulevard-night-context-ccbysa.jpg` — Downtowngal,
|
||||||
|
CC BY-SA 3.0, via Wikimedia Commons; used unmodified for colour/lighting study only, no derivative
|
||||||
|
asset is made from it.
|
||||||
|
- CC0 files in the Hollywood folder need no attribution: Luijtenphotos (2019) and Benoît Prieur
|
||||||
|
(2022).
|
||||||
|
|
||||||
|
## AI-assisted images
|
||||||
|
|
||||||
|
Surface textures, skyline panoramas, UI art and concept references were produced
|
||||||
|
with image-generation calls from the project's tooling. The default model slugs
|
||||||
|
recorded in the source are:
|
||||||
|
|
||||||
|
- `bytedance-seed/seedream-4.5` — `tools/generate_textures.py`
|
||||||
|
- `openai/gpt-5-image-mini` — `tools/asset_pipeline.py`,
|
||||||
|
`tools/model_pipeline.py`, `tools/scene_concepts.py` and
|
||||||
|
`tools/screen_designs.py`
|
||||||
|
- Judging/review calls use the model selected by `tools/openrouter_judge.py` and
|
||||||
|
are development-only; they do not ship in the game.
|
||||||
|
|
||||||
|
The prompts and generation workflow live in those tools. Per-file generation
|
||||||
|
metadata is not stored with every image, so the exact provider, model version and
|
||||||
|
generation date of each committed bitmap cannot be reconstructed from the
|
||||||
|
repository alone. Before a public 0.10.0 release, the provider terms and
|
||||||
|
commercial-use/redistribution rights for those generated outputs must be
|
||||||
|
reviewed and recorded. **This is a release blocker, not an assumed clearance.**
|
||||||
|
|
||||||
|
Reference photographs linked from the venue documents are hosted on Wikimedia
|
||||||
|
Commons and were used for proportion/material study only. They are not included
|
||||||
|
in the game or its release packages. If any future derivative asset copies a
|
||||||
|
reference more directly, its attribution and licence must be added here.
|
||||||
|
|
||||||
|
## Audio
|
||||||
|
|
||||||
|
All runtime audio is procedural (crowd bed, radio crackle and interface cues).
|
||||||
|
There are no external audio files, so there is no separate music/SFX licence to
|
||||||
|
clear. See `docs/accessibility.md` for caption and volume behaviour.
|
||||||
|
|
||||||
|
## Third-party names
|
||||||
|
|
||||||
|
The game is a work of fiction. Real place names and public references are used
|
||||||
|
for orientation and satire/stand-in geography; no real person, venue or brand
|
||||||
|
endorses the game. Review the storefront and trademark language before release.
|
||||||
@@ -1,427 +1,187 @@
|
|||||||
# MEMORY
|
# MEMORY
|
||||||
|
|
||||||
Session memory for durable decisions, state, and gotchas. Keep concise, remove obsolete entries.
|
Session memory for durable decisions, gotchas, and commands. Implementation history lives in
|
||||||
|
`DONE.md`, user-facing changes in `CHANGELOG.md`, the live task list in `TODO.md`, and the
|
||||||
|
canonical implemented-features list in `docs/development-status.md`.
|
||||||
|
|
||||||
## Decisions
|
## Decisions
|
||||||
|
|
||||||
- Engine: Godot 4.3 stable (official editor build, `~/.local/bin/godot`). The system's preinstalled `godot-runner` (4.7.2, Fedora) cannot load projects from arbitrary paths because it was compiled without path overrides; use the official editor binary instead.
|
- **Local-only reference override (2026-11-03, owner):** development-only photo research ignores
|
||||||
- Language: GDScript. All cross-file references use explicit `preload()` constants (not `class_name` globals) so scripts compile in headless `--script` runs without the editor's class cache.
|
the licence gate. Downloads live in `tmp/research/` (gitignored): DDG image search, street-view
|
||||||
- Test framework deviation: the project uses a custom minimal headless test runner (`src/test/test_runner.gd`) instead of GUT, because GUT needs the editor plugin. CI runs it with `--headless`.
|
frames, neighbouring buildings — never committed, never shipped. Promotion into
|
||||||
- Eventing: no autoload signal bus. `ShiftCoordinator` owns an `EventBus` instance (`src/core/event_bus.gd`) passed to subsystems; subsystems emit only when a bus is assigned. Autoloads at parse time are unreliable under `--script`.
|
`docs/screens/refs/` always passes the sidecar licence + privacy gate and `CREDITS.md`.
|
||||||
- Static signals are not available in Godot 4.3 GDScript ("Expected func or var after static") — do not use them.
|
- **Project agent skills (2026-11-03):** two tracked skills live in `.agents/skills/`:
|
||||||
- GDScript lambdas capture local variables **by value**; mutating a captured local inside a lambda does not affect the outer variable. Use member functions for accumulators in tests.
|
`gig-line-blender-asset-contract` and `gig-line-release-qa`. They are guidance only,
|
||||||
- Shift length is the scenario's `shift_duration_s` (900 s in all three venues). Tests and the screenshot tool override elapsed time; they do not change the default.
|
add no runtime dependency and are referenced from `tools/README.md`. The third-party
|
||||||
- RNG: NPC reactions and event scheduling are seedable for deterministic tests.
|
`blender-skills` pack remains gitignored.
|
||||||
|
- **TV Studio shares the Hollywood location (owner):** the Late Night TV Studio is the same El
|
||||||
|
Capitan Entertainment Centre block at 6840 Hollywood Blvd (which houses *Jimmy Kimmel Live!*),
|
||||||
|
restaged for a late-night arrival. `VENUE_SPECS["tv_studio"]` is a 30.0 m variant of the 29.6 m
|
||||||
|
Hollywood facade. One reference board serves both venues; never create a separate TV Studio board.
|
||||||
|
- **Jimmy Kimmel Live = starting point:** the Hollywood first shift and the TV Studio arrival both
|
||||||
|
begin at the El Capitan block. Stated in `docs/hollywood-boulevard-map.md` and `docs/gameplay.md`.
|
||||||
|
- **Street-view limitation:** Google Street View panoramas need a Google API key or manual browser
|
||||||
|
capture, so the local reference sets use DDG street-level photos instead.
|
||||||
|
- **Image-recitation filter:** Gemini image models reject well-known branded facades (Comedy Store
|
||||||
|
wall) as generation anchors (`IMAGE_RECITATION`). Workaround: anchor on a less-branded frame of
|
||||||
|
the same block, or generate text-only when the reference adds no geometry.
|
||||||
|
- **OpenRouter image-to-image:** `/v1/images` is text-to-image only; the working path is
|
||||||
|
`POST /v1/chat/completions` with a `content` array carrying a base64 `image_url`. Response
|
||||||
|
images arrive as `message.images[].image_url.url` (fallback: a data URI inside the message text).
|
||||||
|
Implemented in `generate_textures.generate_image(reference_path=...)` and
|
||||||
|
`scene_concepts.py generate/anchor`; provenance sidecars are `.refgen.json`.
|
||||||
|
- **Facade/venue material picks (2026-11-03):** the Masonic/El Capitan facade uses the
|
||||||
|
photo-anchored `limestone_ashlar` field (warm tint `Color(0.85, 0.81, 0.74)`) with `stone_steps`
|
||||||
|
for trim; the Joke Shop uses `dark_stucco`; the marquee canopy/sign box stay `black_metal` by
|
||||||
|
design (fictional "GIG LINE" marquee, not the red El Capitan Theatre marquee next door). The
|
||||||
|
TV Studio reuses `masonic_facade`. Committed Comedy Store refs were removed in favour of the
|
||||||
|
local-only `tmp/research/comedy-store/` set; the three-up board's photo panel is a historical
|
||||||
|
public-domain frame.
|
||||||
|
- **Godot 4.7.2 `--import` gotcha:** with a stale `.godot/` cache, new image files never import
|
||||||
|
(no `.import` created) and `preload()` of the new texture fails every dependent script with the
|
||||||
|
misleading `Could not resolve external class member "spec"`. Fix: import in a scratch copy with
|
||||||
|
`.godot/` removed, then copy back the `.png.import` and `.godot/imported/*.ctex/.md5`.
|
||||||
|
- **Blender skills pack (local aid):** MIT `blender-skills` at `.agents/skills/blender-skills/`
|
||||||
|
(94 skills + shared `references/`), gitignored, kept out of Godot's scanner by
|
||||||
|
`.agents/.gdignore`. Keep the pack flat when updating (`cp -a <pack>/.claude/skills/. ...`).
|
||||||
|
Pi gates project `.agents/skills` behind project trust.
|
||||||
|
- **NPC model sourcing (ADR-0015):** CC0 acceptable for shipped assets; CC-BY only with
|
||||||
|
attribution in `CREDITS.md`; no service assets with redistribution restrictions. The procedural
|
||||||
|
generator is the primary path. Quaternius and MakeHuman were evaluated as references only, not
|
||||||
|
shipped bases (Quaternius: 62-bone T-pose rig mismatch; MakeHuman: needs retopology + custom
|
||||||
|
rig + clothing).
|
||||||
|
- **Gaussian splatting (unversioned):** offline/development-only until an ADR proves licence,
|
||||||
|
privacy, Compatibility/Web cost and mesh collision. Accepted photo channels: team captures,
|
||||||
|
permissioned user/volunteer photos, clearly licensed downloads; every set needs a
|
||||||
|
provenance/consent sidecar. No runtime splat dependency in 0.10.0.
|
||||||
|
- **0.10.0 balance protocol:** `docs/balance-acceptance.md` defines targets/denominators for the
|
||||||
|
36 venue/difficulty/phase cells, seeds 1–100, and access/emergency error rates. Gotcha:
|
||||||
|
`_read_npc_truth()` is type-derived, not behaviour-derived; the emergency-fairness review must
|
||||||
|
account for that oracle mismatch.
|
||||||
|
- **Character mesh cap:** eight library variants use 8,000 tris / 8,000 glTF vertices
|
||||||
|
(`CHAR_MAX_TRIS`/`CHAR_MAX_VERTS` in `tools/blender_assets.py`); the frozen `npc_base` fallback
|
||||||
|
keeps 6,000/4,000. Measure after any geometry change; never silently lower the cap back to 5,000.
|
||||||
|
- **Engine baseline:** Godot 4.7.2 stable (official editor `~/.local/bin/godot`). The system's
|
||||||
|
`godot-runner` cannot load arbitrary project paths (compiled without path overrides); use the
|
||||||
|
official editor binary.
|
||||||
|
- **GDScript/architecture:** cross-file references use explicit `preload()` constants (not
|
||||||
|
`class_name`) so scripts compile in headless `--script` runs; the test runner is a custom minimal
|
||||||
|
harness (not GUT, which needs the editor plugin); no autoload signal bus — `ShiftCoordinator`
|
||||||
|
owns the `EventBus` instance passed to subsystems.
|
||||||
|
- **Skinned NPCs (v0.6.0):** `NpcAvatar._build_skinned_body()` loads `npc_base.glb`; procedural
|
||||||
|
`_build_body()` is the fallback. Bone pose rotation is the **absolute local orientation**, not an
|
||||||
|
offset — compose with `get_bone_rest(i).basis` or the body snaps to a broken pose. Five tint
|
||||||
|
slots via `SurfaceMaterials.npc_slot()`; keep `_rig.scale` uniform on the skinned path.
|
||||||
|
- **NPC facing:** the avatar's visual front is **+Z**; Godot `look_at()` aims **-Z** (made NPCs
|
||||||
|
walk backwards). Use `NpcAvatar.yaw_towards(dir)` and set `rotation.y`; never `look_at()` on
|
||||||
|
avatars. Head-turn offsets use `+basis.z` for the same reason.
|
||||||
|
- **Release blockers (still open):** macOS unsigned, AI image provider terms/commercial rights not
|
||||||
|
cleared, storefront/rating approval pending, external playtests and full hardware matrix pending.
|
||||||
|
Recorded in `CREDITS.md`, `PRIVACY.md`, `docs/accessibility.md`, `docs/known-limitations.md`.
|
||||||
|
|
||||||
## Handoff (read this first)
|
## Gotchas / Known Issues
|
||||||
|
|
||||||
- Repo: `/run/media/system/DATA/Documents/06-games/security-game` (also reachable at
|
- **Two character builders — do not confuse them.** `tools/blender_assets.py` is the real builder
|
||||||
`/var/home/zwitschi/Documents/06-games/security-game`). Godot: `~/.local/bin/godot` (4.3.stable).
|
for the **eight library characters, 18 gear pieces and props** (it has the 0.9.4 face/hand
|
||||||
Tests: `godot --headless --path . --script res://src/test/test_runner.gd` → **177/177**.
|
sculpting in `_face()`/`_hands_and_shoes()`); `tools/npc_pipeline.py` builds **only**
|
||||||
Python tooling tests: `cd tools && python3 -m unittest test_asset_pipeline test_screen_designs test_model_pipeline` → **53**.
|
`assets/models/generated/npc_base.glb`, the frozen plain-primitive fallback. Reading
|
||||||
Session shell smoke: `godot --headless --path . --script res://src/test/smoke_session_shell.gd` → exit 0.
|
`npc_pipeline.py` to judge the library's face/hand detail is wrong.
|
||||||
Windowed control smoke: `godot --path . --script res://src/test/smoke_focus_radial.gd` → exit 0
|
- **Pose capture fixed (2026-11-04):** the harness was missing `set_physics_process(false)`/
|
||||||
(now also covers wheel cycling and right-click clearing).
|
`set_process(false)` on the avatar, so the FSM (`_advance_fsm` + `_move_toward_target`) ran every
|
||||||
- `main` is in sync with origin. **v0.2.2** tags the session-shell + world-follow-up commit;
|
tick, reset `arrived`/velocity and made `theater_pose()` revert to idle/rest. Disabling both fixes
|
||||||
CI builds the portable binaries, NSIS installer, and AppImage on the tag push.
|
it (sit vs idle diff 5.63, walk vs idle 2.29). The earlier "moving the `Rig` child does not
|
||||||
- **v0.3.0 (2026-10-31, working tree):** rendering pass (`src/game/materials.gd`, phase-driven
|
render" probe was a red herring — it set the position inside `_shoot()` in the same frame as
|
||||||
environment), generated `.glb` props (`tools/model_pipeline.py`, `assets/models/generated/`),
|
`root.get_texture().get_image()`, so it never rendered a frame. Lesson: pose/portrait harnesses
|
||||||
aim-free selection with wheel cycling and Shift precision walk (ADR-0011/0012), and a **Web**
|
must freeze the avatar's process/physics before driving `_animate()`.
|
||||||
export target. `export_presets.cfg` gains a `Web` preset with `variant/thread_support=false`
|
- Capture/pose harness gotchas: scripts that read captured-mouse input must run windowed
|
||||||
(static hosting, no COOP/COEP); `tools/build_releases.sh` exports `build/web/` and zips it to
|
(`smoke_props.gd`); the NPC FSM rewrites `rotation.y` every physics tick, so pose/portrait
|
||||||
`build/step-line-web-<ver>.zip`, and `publish_release.sh` uploads it. Covered by
|
captures set the yaw after `set_physics_process(false)`; `visible=false` does not keep the crowd
|
||||||
`src/test/tests/test_web_export.gd`.
|
out of a portrait frame (park the rest of the crowd away instead).
|
||||||
- **Prop atlas (v0.3.1):** `model_pipeline.atlas_for()` builds a per-prop power-of-two PNG with
|
- Blender: `bisect_plane` and thin bevels leave zero-area triangles (Godot "non-finite normal in
|
||||||
one 8×8 cell per palette role, plus a metallic/roughness atlas (G=roughness, B=metallic). The
|
LOD generation"); `_clean()` + a weld after bisect fixes them and the audit rejects them.
|
||||||
GLB merges all parts into one primitive with constant per-vertex UVs (cell centre) and a single
|
- Image models have per-model size floors/caps (Seedream needs 1920+; `GEN_SIZE=2048`, do not
|
||||||
material (`baseColorTexture` + `metallicRoughnessTexture`, NEAREST sampling). Colours are sRGB in
|
lower; gpt-5-image-mini caps lower). `generate_image` applies model-specific sizing; concept
|
||||||
the texture, so the old `srgb_to_linear`/`_linear_rgba` helpers were removed. `audit_glb` fails
|
generation defaults to `--request-size 1536`.
|
||||||
if a prop is not exactly one material or misses TEXCOORD_0/images.
|
- Local photo research tooling: DDG rate-limits by IP (HTTP 202) — the `refcollect.py` token bucket
|
||||||
- **Mockups complete (v0.3.2):** `docs/screens/designs/` holds AI references for all 19
|
(0.5 req/s, burst 3) plus retry/backoff handles it; some DDG frames are AVIF mislabeled
|
||||||
catalogue screens. Only the six staged captures (01–06) are compared/judged; the other 13
|
`.png`/`.jpg` (convert with ffmpeg; `refcollect.py sheet` skips unreadable). Commons API needs a
|
||||||
have no `capture`, so adding staged captures for the wand panel, timeline and escalation HUD
|
descriptive User-Agent and rate-limits thumbnail downloads (fetch all metadata in one
|
||||||
would extend automated visual checks. `generate` now calls `_load_env_file()`.
|
`titles=A|B|C` query).
|
||||||
- **Staged captures (v0.3.3):** `capture_screenshots.gd` now emits nine shots. States 7–10 switch
|
- Frozen decisions: do not re-open `override_access`; do not bind wand to `W`; do not remove the
|
||||||
to The Joke Shop at elapsed 620 s (EVENING has routine spawns; LATE has none), retry the wand
|
radio easter egg.
|
||||||
until it alerts, then jump to 760 s after staging for night lighting. The timeline needs
|
- GDScript: no static signals; lambdas capture locals **by value** (mutating a captured local does
|
||||||
`recorder.close_all_open()`, because `to_dict()` only lists closed incidents. The escalation
|
not affect the outer variable — use member functions for accumulators).
|
||||||
HUD mockup is third-person now; `judge` still classes it `review` (no diff).
|
- Reference cycles: `FSM` holds its owner via `WeakRef`, `ShiftCoordinator` is `RefCounted`; if a
|
||||||
- **Prop collision (v0.4.0):** `_add_prop_model()` fits a `StaticBody3D` from the model bounds
|
new subsystem introduces a cycle, repeat the WeakRef pattern.
|
||||||
(cylinder for bollard/bin/hydrant, box otherwise) and adds a `NavigationObstacle3D`; NPC agents
|
- Headless (dummy renderer) logs `Parameter m is null` from `mesh_get_surface_count` — benign, does
|
||||||
enable avoidance and move with the safe velocity from `velocity_computed`. The navmesh stays a
|
not occur with a real GL driver.
|
||||||
flat plane, so avoidance (not a baked mesh) routes guests around props. Placements live in
|
- macOS export requires `rendering/textures/vram_compression/import_etc2_astc=true` (universal
|
||||||
`PROP_PLACEMENTS` with `placement_problems()` validation; variants come from `variant_for(index)`.
|
build demands ETC2 ASTC).
|
||||||
`smoke_props.gd` must be windowed because the guard reads captured-mouse input.
|
- Typed spawn signal: handlers must accept the 4th `event: Dictionary` arg or Godot will not call
|
||||||
- **Presentation pass remaining (TODO):** NPC characters and player movement. Grounding facts: the
|
them.
|
||||||
NPC rig is rigid capsules with a whole-limb swing, and `guard_controller.gd` uses one `accel`
|
- GDScript inference trap: a `var` typed as a base class (`radial: CanvasLayer`) exposes script
|
||||||
value. Each task should get its own windowed smoke or capture where it changes feel/looks.
|
members as `Variant`; type it as the script type (`ActionRadial`). `--check-only --script`
|
||||||
- **Backlog complete:** every planned TODO section is done and moved to `DONE.md`; `TODO.md`
|
cannot see the `Save` autoload — use a real boot (`--quit-after 180`) to verify full-scene
|
||||||
now only lists optional future ideas (live 3D replay, NPC LOD, more venues, real audio, prop
|
compile.
|
||||||
atlas, AI mockups for the new catalogue entries, image-to-mesh props). GUT was consciously not
|
- Do not snapshot right after changing a transform in the same frame — `capture_screenshots.gd`
|
||||||
adopted (custom runner covers it without an editor plugin).
|
|
||||||
- Do not re-open `override_access`. Do not bind wand to `W`. Do not remove the radio easter egg.
|
|
||||||
|
|
||||||
## Current State
|
|
||||||
|
|
||||||
- **v0.3.0 pass (2026-10-31, uncommitted):** rendering, generated prop models, screen
|
|
||||||
catalogue, aim-free selection. See ADR-0011 (selection) and ADR-0012 (materials/props).
|
|
||||||
- `src/game/materials.gd` (`SurfaceMaterials`) is now the single source of surface
|
|
||||||
recipes + `configure_environment()`. `GameManager.PHASE_LIGHTING` gained
|
|
||||||
`exposure/glow/fog_color/fog_density/fog_energy/contrast/saturation/ambient_energy`.
|
|
||||||
Renderer is **GL Compatibility**, so SSAO/SSIL are unavailable; glow+fog+ACES are used
|
|
||||||
instead and unsupported settings are ignored rather than gated in code.
|
|
||||||
- World: scaled brick (uv 22,10), framed windows with per-window night emission
|
|
||||||
(`i % 3 != 2` lit), stone base course, door trim/handles, curb, lane dashes (the old
|
|
||||||
loop had `Vector3(i * 0.0, …)` and stacked every dash at x=0), street lamps use the
|
|
||||||
metal profile. The two flat planters that clipped the steps were removed.
|
|
||||||
- `tools/model_pipeline.py` builds `assets/models/generated/*.glb` (bollard, bench,
|
|
||||||
trash_bin, planter_box, newspaper_box, fire_hydrant) from box/cylinder specs;
|
|
||||||
`WorldBuilder._add_prop_model()` loads them with a procedural fallback and records
|
|
||||||
`loaded_models` / `fallback_props`. **Gotcha:** glTF `baseColorFactor` is LINEAR —
|
|
||||||
writing sRGB made every prop pastel; `srgb_to_linear()` is applied in `build_glb`.
|
|
||||||
**Gotcha:** a new/changed `.glb` needs `godot --headless --path . --import` before
|
|
||||||
`ResourceLoader.exists()` / `load()` sees it (test_generated_props fails otherwise).
|
|
||||||
- `model_pipeline.py concept` (OpenRouter, reads repo `.env`) writes per-prop concept
|
|
||||||
sheets to `assets/models/concepts/`; `dominant_colors()` centre-crops and weights by
|
|
||||||
saturation (a grey studio backdrop otherwise wins), `assign_roles()` maps colours by
|
|
||||||
hue/value. Delete a concept file to fall back to the catalogue palette.
|
|
||||||
- NPC avatars: `outfit_for(type, seed)` (pure) gives shirt/trousers/hair/skin +
|
|
||||||
accessory (cap, hood, backpack, sunglasses, lanyard) and height scale; `_arms` and
|
|
||||||
`_legs` now swing (`limb_swing`) while walking. Keep `Rig/Head` named for focus.
|
|
||||||
- Selection: `NpcFocus.rank_indices` (angle then distance) — `pick_index` = first.
|
|
||||||
`GuardController.cycle_target(direction)` walks nearest-first `nearby_candidates()` and
|
|
||||||
sets `target_pinned`, which makes `_update_target()` skip the cone. `clear_target()`
|
|
||||||
sets `focus_suppress_s = 0.75` so the cone does not instantly re-acquire the person.
|
|
||||||
New input actions `walk_slow` (Shift), `cycle_target_next` / `cycle_target_prev`
|
|
||||||
(wheel down / up). Right click with nothing open clears focus (Esc still pauses).
|
|
||||||
- HUD: `nearby_label` + `set_nearby_hint(count)` show `hud.nearby`; `hud.controls` and
|
|
||||||
`hud.targeting` mention the wheel and Shift.
|
|
||||||
- `tools/screen_designs.py`: catalogue now 19 screens (added wand_screening,
|
|
||||||
joke_shop_queue, studio_arrival, crowd_panic, responder_arrival,
|
|
||||||
body_camera_timeline, escalation_hud), new `list` command, `generate --only/--force`
|
|
||||||
(existing designs are kept by default so a draft run cannot clobber AI mockups),
|
|
||||||
and `judge --dry-run` no longer raises `KeyError: 'class'`.
|
|
||||||
- Screenshot tool leak: `capture_screenshots.gd` intermittently prints
|
|
||||||
`ObjectDB instances leaked at exit`. Verified against a `git worktree` of HEAD~ — the
|
|
||||||
baseline leaks more often (3/3 vs 1/3), so it is a pre-existing quit-timing artifact,
|
|
||||||
not a regression. Normal boots are clean.
|
|
||||||
|
|
||||||
- Access artifacts: `src/core/access_loadout.gd` builds from event `override_access` + venue policy.
|
|
||||||
Spawn signal is now `(profile, intent, npc_id, event)`. Recognition is not auto-attached to every
|
|
||||||
guest. `AccessVerifier.verify(..., already_screened=false)` so Joke Shop scoring uses
|
|
||||||
`controller.screened`. Joke Shop/Studio gained `sidewalk`/`steps_end` waypoints. Wand venues roll
|
|
||||||
`carried_items` per spawn (seeded). Tests in `src/test/tests/test_access_loadout.gd`.
|
|
||||||
- Wand shortcut is **`C`**, not `W` (`W` is walk). `radio_emergency` input map no longer includes W.
|
|
||||||
`hud.controls` / `hud.screen_first` updated.
|
|
||||||
- Control pass (in tree, uncommitted, smoke-tested): cone focus via `src/game/npc_focus.gd`,
|
|
||||||
accel/decel, gravity only airborne, `input_locked` freeze, Esc no longer on the guard.
|
|
||||||
`NpcAvatar` in group `npc`; neck/hair/eyes/shoes; blue focus circle + `Label3D` name; `set_focus_visual`.
|
|
||||||
`src/game/ui/action_radial.gd` doughnut (LMB/Space open, A/D/arrows, Space/click confirm, Esc/RMB
|
|
||||||
cancel). Crosshair CanvasLayer. Emergency still `R` only. Number keys 1–6 when the wheel is closed.
|
|
||||||
- Named input actions now exist and are used: `action_menu`, `confirm`, `cancel` (Esc + RMB),
|
|
||||||
`inspect` (E), `wand` (C). The old unused `interact` action was removed.
|
|
||||||
- Runtime fixes from the smoke: `Head` is +1.6 above the guard root (root sits on the floor); guard
|
|
||||||
spawns in front of the door facing the street; building volume moved to z ∈ [-8,-4] so the door,
|
|
||||||
awning, and marquee are visible; step treads rise toward the door; `ShiftCoordinator` pulls the
|
|
||||||
event scheduler once per simulated second (was every physics frame → >1200 NPCs in seconds);
|
|
||||||
`GameManager.MAX_LIVE_NPCS = 16` caps live avatars.
|
|
||||||
- **Session shell (2026-10-31, uncommitted):** the game now boots into `StartMenu`
|
|
||||||
(`src/game/ui/start_menu.gd`, layer 20) instead of a live shift. `GameManager._on_venue_selected()`
|
|
||||||
builds the scenario and calls `_start_shift()`; `_shift_running` gates input and `_refresh_freeze()`.
|
|
||||||
Esc with nothing open now pauses (`PauseOverlay`, layer 18) → resume / restart / settings /
|
|
||||||
quit-to-menu. `SettingsPanel` (layer 22) writes `look_sensitivity` + `hud_font_scale` through
|
|
||||||
`Save.save_settings()` → `user://settings.json`; `Save.load_settings()` merges over
|
|
||||||
`Save.DEFAULT_SETTINGS` on boot and `GameManager._apply_settings()` pushes them into the guard/HUD.
|
|
||||||
Shift review "Run another shift" now calls `_show_start_menu()` (not a silent restart). `V` stays a
|
|
||||||
debug venue bind. Smoke: `src/test/smoke_session_shell.gd` (menu → shift → pause → settings → menu).
|
|
||||||
- **World follow-ups closed (2026-10-31):** skyline backdrop restored with `T_SKYLINE`
|
|
||||||
(`assets/textures/skyline.png`, regenerated RGBA with a transparent sky) and an
|
|
||||||
`TRANSPARENCY_ALPHA_SCISSOR` unshaded material (`_skyline_mat`); steps + landing are a
|
|
||||||
`StaticBody3D` with per-tread `BoxShape3D` colliders; marquee `font_size` 160 / outline 24; focus
|
|
||||||
circle (blue disc in front of the face) + name tag 48px; `NpcAvatar.ephemeral`/`lifetime_s` despawns routine traffic
|
|
||||||
(authored events stay). Guard spawn height verified on the landing (guard_y=0.84).
|
|
||||||
- **PLAN verbs (2026-10-31, uncommitted):** the radial now has 12 slices
|
|
||||||
(`Stop, Look, Point, No, Ask, Inspect, Wand, Warn, Block, Reason, Recognize, Radio`).
|
|
||||||
`eye_contact` reuses the Level 1 reaction table via `_apply_interaction`. Recognition is a player
|
|
||||||
decision: `NPCController.player_recognized` + `IncidentRecorder.add_access_evidence`, and
|
|
||||||
`GameManager._artifacts_for()` prepends a `RECOGNITION` artifact only after the player declares
|
|
||||||
it. The body camera is a real flag (`GameManager.body_camera_on`, `Hud.set_body_camera`), off by
|
|
||||||
default, `B` toggles it, and a formal warning arms it and writes a `body_camera` incident event.
|
|
||||||
Blocking also calls `_station_between()` to physically place the guard 0.9 m from the NPC toward
|
|
||||||
the entrance. `_request_reason()` logs the NPC's seeded `dialogue_lines` pick.
|
|
||||||
- **NPC navigation was dead (fixed 2026-10-31):** avatars never moved in the live game, for three
|
|
||||||
stacked reasons. (1) `agent.target_position` was re-assigned every physics frame, which
|
|
||||||
invalidates the path so `get_next_path_position()` keeps returning the current point — only set
|
|
||||||
it when the target changes. (2) Nav path points sit on the navmesh plane (y=0) while bodies stand
|
|
||||||
at y≈0.5, so a 3D "are we there" test always looked 0.5 m away and the horizontal direction was
|
|
||||||
zero — compare horizontally. (3) `path[0]` is the old start point, so picking any point "far
|
|
||||||
enough away" can pick the point behind you and oscillate — pick the first point that is
|
|
||||||
**closer to the goal** than the current position. `_nav_try_frames`/`_move_direct` fallback is
|
|
||||||
still there.
|
|
||||||
- **Crowd reactions listen on the EventBus:** `CrowdSystem.apply_event()` emits
|
|
||||||
`event_bus.crowd_changed`; its own `changed` signal is never emitted. `GameManager._connect_crowd()`
|
|
||||||
therefore connects to `coordinator.event_bus.crowd_changed` (reconnect after every
|
|
||||||
`coordinator.setup()`, which recreates both crowd and event bus).
|
|
||||||
- **Crowd presentation:** `NpcAvatar.start_rubberneck(at)` (idle bystanders away from the door stop
|
|
||||||
and face the incident for 12 s) and `start_flee(from)` (panic: walk away at 2.4 m/s, then free
|
|
||||||
themselves). Someone `awaiting_interaction` within 3 m of the door is "engaged" and does not
|
|
||||||
rubberneck. Use `position` (not `global_position`) for that distance check so it also works in
|
|
||||||
headless tests outside the tree.
|
|
||||||
- **Perf:** a busy 16-NPC entrance measured ~1.2 ms/frame (824 fps with vsync off, 800 samples),
|
|
||||||
so LOD/culling is not warranted at `MAX_LIVE_NPCS = 16`. Re-measure before raising the cap.
|
|
||||||
- **Incident recording was dead:** `IncidentRecorder.start_incident()` was never called, so every
|
|
||||||
`add_event`/`add_access_evidence` silently no-opped and the review always showed 0 incidents.
|
|
||||||
`_begin_incident()` now starts one per focused NPC (`has_open_incident()` guard), verbs call
|
|
||||||
`_ensure_incident()`, and `ShiftCoordinator._finish_shift()` closes leftovers as `unresolved`
|
|
||||||
before building the review.
|
|
||||||
- **Editor-ready venues (2026-10-31):** `tools/export_venues.gd` (a SceneTree script) saves the
|
|
||||||
three factory scenarios to `content/venues/<id>.tres` with `ResourceSaver`; the `.tres` files
|
|
||||||
round-trip through `ResourceLoader` (events, profiles, policy, shift phases, door lines,
|
|
||||||
locations). `test_venue_resources.gd` also fails if the exported files are missing or stale
|
|
||||||
relative to the factory, so re-run the export after changing `ScenarioFactory`.
|
|
||||||
- **Vision judge (2026-10-31):** `python3 tools/screen_designs.py judge` sends the capture plus
|
|
||||||
its concept mockup to a vision model (`DEFAULT_JUDGE_MODEL = google/gemini-2.5-flash-lite`,
|
|
||||||
override with `--model`; `openai/gpt-4o-mini` also works). Reads the repo `.env` via
|
|
||||||
`_load_env_file()` (never overrides real env). Verdicts are classed `match` / `review`
|
|
||||||
(mismatch below `--fail-confidence`, default 0.6) / `diff` (confident mismatch → exit 1) /
|
|
||||||
`unknown`. `--dry-run`, `--json`, `--only` supported. The prompt explicitly ignores art style,
|
|
||||||
because the references are stylised mockups; the live run gave 3 match, 3 review, 0 diff.
|
|
||||||
Python tests: `cd tools && python3 -m unittest test_asset_pipeline test_screen_designs`.
|
|
||||||
- **String formatting safety (2026-10-31):** `Strings.placeholder_count()` counts `%` specs
|
|
||||||
(ignoring `%%`), `fmt()` warns and returns the template when the arg count does not match
|
|
||||||
(instead of raising an engine error), and `validate()` lints the table for trailing `%`,
|
|
||||||
unsupported conversions, and mixed positional/sequential placeholders. Tests cover all three
|
|
||||||
plus a formatting pass over every placeholder key with type-correct dummy args.
|
|
||||||
- **Authored scenario content (2026-10-31):** events can now carry `hint` (a string key logged
|
|
||||||
when the avatar spawns), `extra_spawns` (celebrity arrivals pull in tourists/influencers via
|
|
||||||
`_spawn_npc` with `kind: routine`), and `refuses_screening` (copied to
|
|
||||||
`NPCController.refuses_screening`; `_toggle_wand_screening` logs `wand.refused`, records a
|
|
||||||
`wand_refused` event, applies a small prop hit, and does not open the panel). `film_boundary`
|
|
||||||
is a new intent that maps to `steps_end` so the influencer stands past the posted line.
|
|
||||||
- **Accessibility (2026-10-31):** escalation text is `"<name> [###--] L3/5"` via
|
|
||||||
`Hud.escalation_meter(level)` (pure, tested) so the cue survives grayscale. HUD font scale uses
|
|
||||||
`add_theme_font_size_override` on the core labels (`set_font_scale`), **not** `Control.scale`,
|
|
||||||
which pushed the right-anchored clock label off-screen. Settings load before `_build_ui()`, so
|
|
||||||
`_ready()` re-applies player settings after the HUD exists.
|
|
||||||
- **Audio (2026-10-31):** `AudioFeedback.start_crowd_bed()` builds a 4 s looping `AudioStreamWAV`
|
|
||||||
(one-pole-filtered noise + 68/103 Hz drone, seeded) and `set_crowd_level(mood)` maps 0..1 to
|
|
||||||
-34..-12 dB. `play_radio_open/close` push short filtered-noise bursts. All procedural; with a
|
|
||||||
dummy (headless) audio server the calls are no-ops. `_free_player()` returns null when `_ready`
|
|
||||||
has not run (tests), and callers handle null.
|
|
||||||
- **NPC theater (2026-10-31):** `NpcAvatar.theater_pose()` maps `intent` + `arrived` to
|
|
||||||
idle/sit/photo/film/perform. Pure helpers `crouch_for_pose`, `prop_lift_for_pose`,
|
|
||||||
`sway_for_pose` keep the math testable. Sit lowers the rig by 0.34 and swings the legs forward
|
|
||||||
(negative X pitch; positive looked like kneeling). `_photo_prop` is set for influencer/tourist
|
|
||||||
cameras, `_sign_prop` for protester/performer boards; their base Y values are captured at build
|
|
||||||
time so the animation offsets from rest.
|
|
||||||
- **Body-camera timeline (2026-10-31):** `src/game/ui/event_timeline.gd` flattens
|
|
||||||
`review.incidents.incidents[].events` into one time-ordered list (`flatten`), formats each row
|
|
||||||
with `describe`, and steps with Left/Right or the buttons. `ShiftCoordinator.started_at_ms`
|
|
||||||
gives the review an absolute start for mm:ss stamps. `GameManager._open_timeline` hides the
|
|
||||||
review dim (two stacked overlays made the screen nearly black) and `_timeline_from_review`
|
|
||||||
restores it on close. Smoke: `src/test/smoke_timeline.gd`.
|
|
||||||
- **Radio dispatch was dead after the first shift:** `coordinator.setup()` builds a new
|
|
||||||
`RadioSystem`, so the `responder_arrived` connection made in `_ready` was lost on every venue
|
|
||||||
switch/restart (responders never resolved anything). `_connect_radio()` now re-wires it next to
|
|
||||||
`_connect_crowd()` after every setup. Same class of bug as the crowd signal.
|
|
||||||
- **Emergency aftermath:** `ShiftCoordinator.responder_outcomes` / `investigations` feed the review.
|
|
||||||
`evacuation` (emergency call, low danger, no weapon) locks the door, makes every NPC flee, marks
|
|
||||||
them `leave_requested`, and applies a `security_failure` hit. An emergency placed while
|
|
||||||
`current_npc_truth` has no weapon and `danger_level <= 1` appends an investigation note, records
|
|
||||||
`investigation_opened`, and applies `false_report` -6.
|
|
||||||
- **Skyline backdrop (2026-10-31):** `PlaneMesh` defaults to a **horizontal** XZ plane — set
|
|
||||||
`mesh.orientation = PlaneMesh.FACE_Z` or the "backdrop" renders as a band overhead. The skyline
|
|
||||||
is now a ring of four 400x60 planes at 200 m (center y=30, so building bases meet the ground),
|
|
||||||
`TEXTURE_FILTER` default, `TRANSPARENCY_ALPHA_SCISSOR` with threshold **0.5** (0.3 left faint
|
|
||||||
mip dashes across the sky), unshaded with a lifted albedo (`Color(2.6,2.7,3.0)`) so it is not
|
|
||||||
pure black. Ground is 500x500 so no sky gap shows under the buildings. The `skyline.png` import
|
|
||||||
needs `mipmaps/generate=true`.
|
|
||||||
- **Asset gotcha:** `assets/textures/game_ready/` is *not* imported (no `.import`). Use the
|
|
||||||
imported `assets/textures/skyline.png` path. Untracked duplicate folder should not be referenced.
|
|
||||||
- Host sign-in follow-up is **done**: `REQUIRE_SIGNIN_VIEW = false` in `/etc/gitea/app.ini`
|
|
||||||
(backup `app.ini.bak-*` alongside), Gitea restarted; anonymous repo page/API return 200 and
|
|
||||||
release assets download in full. Reverse by setting it back to `true` and restarting.
|
|
||||||
- **Release publishing was broken since v0.2.2 (fixed 2026-10-31):** `publish_release.sh`
|
|
||||||
only built `AUTH` in the `GITEA_TOKEN` branch; the `~/.git-credentials` branch set `CRED`
|
|
||||||
and then used unbound `${AUTH[@]}` under `set -u`, so every API call failed and the tag
|
|
||||||
job ended "could not create or find release". The repo also has **no** `GITEA_TOKEN`
|
|
||||||
secret (`/actions/secrets` is empty) and the runner has no git credentials. Fixes: the
|
|
||||||
script sets `AUTH=(-u "$CRED")`; the workflow falls back to `github.token`
|
|
||||||
(`secrets.GITEA_TOKEN || github.token`) — but `github.token` is **not** provided by this
|
|
||||||
Gitea/act runner, so the env dump showed no token and the v0.2.10 tag build failed again.
|
|
||||||
Final fix: `publish_release.sh` falls back to the **local git extraheader** that
|
|
||||||
`actions/checkout` writes (`git config --local --get-regexp '^http\..*\.extraheader$'`),
|
|
||||||
which carries the runner's basic-auth credentials for the same server. Verified locally by
|
|
||||||
adding that extraheader and publishing v0.2.10 (all five artifacts). **v0.2.9 and v0.2.10
|
|
||||||
are published**; v0.2.4–v0.2.8 tags have no artifacts. Verify the CI path on the next tag.
|
|
||||||
- **CI publishing verified (2026-10-31):** tagging v0.2.12 produced a release with all five
|
|
||||||
artifacts automatically (no repo secret), confirming the checkout-extraheader fallback.
|
|
||||||
- **Host access (2026-10-31):** `root@192.168.88.30` now works from this workstation (key auth),
|
|
||||||
host name `gitea`, Gitea 28.2.0. Maintenance applied: `apt-get clean`, journald capped to
|
|
||||||
`SystemMaxUse=300M` (`/etc/systemd/journald.conf.d/99-size.conf`, journal ~133M), disk at
|
|
||||||
~7.6G free of 16G.
|
|
||||||
- **Host disk resolved (2026-10-31):** the Gitea VM root filesystem was expanded from 7.8G to
|
|
||||||
16G (`/dev/mapper/pve-vm--500--disk--0`, now ~7.8G free). The pruned **v0.2.0** and **v0.2.9**
|
|
||||||
releases were restored from the local `build/` artifacts and verified (4 and 5 assets).
|
|
||||||
`git.allucanget.biz` runs on this host as user `zwitschi`; root@192.168.88.30 is **not**
|
|
||||||
reachable from this workstation (publickey denied; password auth closes the banner), so host
|
|
||||||
maintenance must go through a `zwitschi` login with sudo.
|
|
||||||
- **Host disk full (2026-10-31, historical):** the v0.2.11 tag build created release id 27 and logged the
|
|
||||||
uploads, but they failed with `HTTP 500 Attachments.Save: write … no space left on device`
|
|
||||||
(`/dev/mapper/pve-vm--500--disk--0`, 7.8G, 100%, 0 free). `git push` then failed too
|
|
||||||
("unpacker error"). Unblocked by deleting the superseded **v0.2.0** and **v0.2.9** releases
|
|
||||||
via the API (~565 MB of assets), then publishing v0.2.11 locally with the fixed script
|
|
||||||
(verified 5 assets). Remaining ~150 MB is tight — the next release needs host cleanup on
|
|
||||||
192.168.88.30: `sudo journalctl --vacuum-size=100M` (journal 837 MB), `sudo apt-get clean`
|
|
||||||
(91 MB), clear `/var/lib/gitea/data/attachments/tmp`. SSH works as `zwitschi`; `sudo -n`
|
|
||||||
needs a password. **Releases now:** v0.2.11, v0.2.10, v0.2.1 (5 assets each); v0.2.0 and
|
|
||||||
v0.2.9 entries were pruned. PLAN.md + ADRs 0007–0010 still describe the intended UX.
|
|
||||||
- **Release v0.2.1 is LIVE** (2026-10-31): CI run 1110 built the NSIS installer + AppImage
|
|
||||||
and published — installers/AppImage are on the releases page. Releasing = bump version → tag → push.
|
|
||||||
- NSIS gotchas (validated in a local ubuntu:24.04 docker repro): makensis uses `-D` defines
|
|
||||||
(never `/D`), resolves relative `-DICON`/`-DARTIFACT` against the **script directory** so CI
|
|
||||||
passes `$PWD`-absolute paths; installed exe must be space-free (`step-line.exe`); ICO must be
|
|
||||||
classic BMP entries (ImageMagick `icon:auto-resize=48,32,16`; PIL emits PNG-compressed ICOs NSIS
|
|
||||||
cannot load).
|
|
||||||
- appimagetool runs system `appstreamcli` if present (strict, exit 3 on warnings) — project
|
|
||||||
metadata now passes `--pedantic`.
|
|
||||||
- Distribution facts (verified via Gitea API + host creds): repo is **public**, account is
|
|
||||||
admin; `v0.2.1` has all 5 assets; the installers were initially missing from the release
|
|
||||||
because `publish_release.sh` uploaded only portables — now fixed (uploads installer +
|
|
||||||
AppImage; falls back to `~/.git-credentials` when no `GITEA_TOKEN`).
|
|
||||||
- Host sign-in: instance `[service] REQUIRE_SIGNIN_VIEW=true` blocks anonymous browse/download
|
|
||||||
(not a repo/API setting). `~/.git-credentials` holds the Gitea creds (basic-auth usable);
|
|
||||||
`.env` only holds `OPENROUTER_API_KEY`.
|
|
||||||
- **Release v0.2.0 was LIVE**: pushing the `v0.2.0` tag made Gitea Actions run the build job
|
|
||||||
and publish automatically (host has the `GITEA_TOKEN` secret). Tag page and `/releases` are
|
|
||||||
public (HTTP 200). Caveat: anonymous **asset downloads** redirect to login on this host
|
|
||||||
(host setting — tracked in TODO). Future releases = bump `config/version`, `git tag vX.Y.Z`,
|
|
||||||
push. No local token needed.
|
|
||||||
- Core simulation, three scenarios, v0.2.1 installers/AppImage, first-person prototype (inspect/wand/radio, shift review, Tab targeting, V venue cycle) are on local `main`. Tagged v0.2.1 was 57 tests; this working tree is **74/74**.
|
|
||||||
- NPC avatars now walk to per-intent waypoints (`VenueScenario.waypoint_for_intent`): enter/trespass/deliver/arrive → entrance, photo/film/perform/wander/roam → sidewalk, sit → steps_end. `NpcAvatar.setup` takes an explicit `door_position`.
|
|
||||||
- Returning-trespasser behavior: `NpcProfile.return_probability` (trespasser 0.6, mental-crisis 0.4, others 0). `NPCController._maybe_schedule_return()` moves a directed-away NPC to `LOITERING_DISTANT`; `tick_return(delta)` fires after 20-60s and transitions back to `APPROACH`. `NpcAvatar._advance_fsm` re-targets the door on return.
|
|
||||||
- Perception pass: `NPCController.perceive_guard(distance)` returns `{noticed, band, reaction}` with bands `unaware` (>6m), `aware` (2-6m), `near` (<2m). Near + temperament ≥0.6 → ARGUE, else COMPLY. `GameManager._perception_pass()` feeds real guard distance each physics tick and logs first-time notice.
|
|
||||||
- Responder dispatch: `RadioSystem` tracks `pending_calls` and decrements `arrival_delay_s` in `tick`; emits `responder_arrived` when a call's delay elapses. `RadioSystem.responder_outcome(call)` maps danger/weapons → command/restraint/evacuation/arrest. `GameManager._on_responder_arrived` spawns a `RESPONDER` avatar at the entrance and logs the outcome. HUD shows an emergency-radio cooldown label via `_update_radio_cooldown`.
|
|
||||||
- Wand screening (Joke Shop): `WandScreener` classifies carried items as prohibited vs false-alarm vs clear. `NpcProfile.carried_items` is filled at spawn on wand venues. `GameManager` binds **`C`** to `_toggle_wand_screening` and `A`/`D` to `decide_wand` while inspect/wand is open. `decide_access` still blocks admission until `controller.screened` is true, and `verify` now takes that flag. Door-staff lines log through GameManager.
|
|
||||||
- Design change (2026-10-31): the selectable dialogue tone-of-voice system was **removed**.
|
|
||||||
`Rules.Tone`/`TONE_NAMES`, `NpcProfile.tone_modifiers`, and the tone param on
|
|
||||||
`NPCController.roll_reaction/apply_player_action` no longer exist; `NpcProfile.dialogue_lines`
|
|
||||||
is a flat `Array[String]`; `GameManager` uses `_apply_interaction(level, action_name)`;
|
|
||||||
the `T` key is gone. HUD `tone_label` removed and `hud.tone_prefix` string deleted from JSON.
|
|
||||||
- Polish/accessibility: venue cycling (`V` cycles `VENUE_ORDER` and restarts the shift), aim-free
|
|
||||||
targeting (`Tab` cycles nearby NPCs within `NEARBY_RADIUS`), color-coded escalation indicator
|
|
||||||
(`Hud.set_escalation`), procedural audio (`AudioFeedback` node generates sine tones for
|
|
||||||
confirm/radio/escalation/deny), and a centralized `Strings` table (`src/core/strings.gd`, `lookup`/`fmt`)
|
|
||||||
backed by `content/strings.en.json`. NOTE: `Strings.get` was renamed to `Strings.lookup` because
|
|
||||||
`get` collides with `Object.get`.
|
|
||||||
- Newly added: LATE shift phase (start 700s) with a reachability test; PROTESTER archetype + events + colors; CALL_SOMEONE reaction; `DoorState` core class + `L` binding + visual indicator; flashlight (`G`, SpotLight3D, perception bonus at evening/late); persistence wired (`Save.save_shift_result` on shift end, history + best score on the review screen); `NavigationRegion3D` flat navmesh + `NavigationAgent3D` NPC paths (steps now visual-only); strings moved to `res://content/strings.en.json` (41 keys, loader keeps `lookup`/`fmt`); `export_presets.cfg` (Linux/Windows/macOS) + `tools/build_releases.sh` + `docs/RELEASING.md`; README Downloads section.
|
|
||||||
- Run tests: `godot --headless --path . --script res://src/test/test_runner.gd`
|
|
||||||
- Boot check: `godot --headless --path . --quit-after 120`
|
|
||||||
- Screenshot tools: `godot --path . -s res://src/test/take_screenshot.gd` (single shot to `user://screenshot.png`); `godot --path . -s res://src/test/capture_screenshots.gd` (staged README gallery shots to `user://screenshots/`, copied to `docs/screenshots/`).
|
|
||||||
- Release build verified locally: export templates for 4.3 installed at `~/.local/share/godot/export_templates/4.3.stable/`; `./tools/build_releases.sh` produced Linux/Windows/macOS artifacts for 0.1.0 in `build/` and the Linux binary passed a headless smoke test. Publishing to Gitea still needs a token (see `tools/publish_release.sh`, `docs/RELEASING.md`).
|
|
||||||
- CI: the repo now carries a **Gitea Actions** workflow at `.gitea/workflows/build.yml` (checks on
|
|
||||||
push/PR; full export + artifact upload + optional release publish on tags and `workflow_dispatch`).
|
|
||||||
A repo secret `GITEA_TOKEN` is required for tag releases. The old `.github/workflows/ci.yml` was
|
|
||||||
removed so hosts scanning both paths do not double-run.
|
|
||||||
- Venue cycling: `V` switches Hollywood Blvd → The Joke Shop → TV Studio and restarts the shift.
|
|
||||||
- Venue door-staff dialogue: `VenueScenario.door_staff_line(context, rng)` and GameManager logging
|
|
||||||
(id/ticket/wand-check, refusal, prohibited-item) — Joke Shop flavor.
|
|
||||||
- CI cache: `.gitea/workflows/build.yml` no longer uses `actions/cache` (the runner can't reach
|
|
||||||
the GitHub cache service; import is fast). `GODOT_SILENCE_ROOT_WARNING=1` set on jobs.
|
|
||||||
Logs live in `.gitea/logs/` after each run.
|
|
||||||
- Visual polish (v0.2.0): phase lighting (GameManager.PHASE_LIGHTING + ProceduralSkyMaterial),
|
|
||||||
street lamps + night mode in WorldBuilder.set_night_mode, marquee Label3D, crosswalk/planters/
|
|
||||||
trees, NPC per-seed variation. capture_screenshots.gd shoots day + fast-forwarded LATE night
|
|
||||||
(6 shots in docs/screenshots/). Version bumped to 0.2.0; binaries rebuilt.
|
|
||||||
- Assets pipeline: `python3 tools/generate_textures.py` produces all authored PNGs under
|
|
||||||
`assets/textures/` (≤1024px, pow2); regenerate after changing the script, then run
|
|
||||||
`godot --headless --import` before tests. 57 headless tests guard the pipeline.
|
|
||||||
- General image pipeline: `python3 tools/asset_pipeline.py` defaults to
|
|
||||||
`openai/gpt-5-image-mini`, requests supported 1:1/3:2 sources, then fits each role's target.
|
|
||||||
It provides crop, resize, tile edge blending, alpha-glyph recoloring, and role-aware audits.
|
|
||||||
Screenshot auditing checks 1280x720 minimum, 16:9 aspect, and contrast; visual composition
|
|
||||||
remains a manual check. Python tools require Pillow from `requirements-tools.txt`.
|
|
||||||
- AI texture mode: `python3 tools/generate_textures.py --ai` generates textures via the
|
|
||||||
OpenRouter image API (`POST https://openrouter.ai/api/v1/images`, model
|
|
||||||
`bytedance-seed/seedream-4.5` by default, base64 PNG in `data[].b64_json`). Requires
|
|
||||||
`OPENROUTER_API_KEY` (a gitignored `.env` in the repo root holds it locally; source it
|
|
||||||
with `set -a && . ./.env && set +a`). `--prompt`/`--name`/`--model`/`--size` override the
|
|
||||||
default batch. Output is downscaled to ≤1024px. NOTE: seedream rejects sizes below
|
|
||||||
3,686,400 px (1920×1920), so the script requests `GEN_SIZE=2048` and downscales — do not
|
|
||||||
lower `GEN_SIZE` below 1920. Verified end-to-end 2026-10-31 (3 textures generated).
|
|
||||||
Legacy `--ai` remains on Seedream 4.5 and still ignores `background: transparent`; do not
|
|
||||||
use it for alpha-dependent assets. Gemini 3.1 Flash Image advertised broad ratios but the
|
|
||||||
current provider rejected authentication. The validated role-aware model is GPT-5 Image Mini.
|
|
||||||
- Runtime assets: all 15 loaded world/HUD/card textures now live in
|
|
||||||
`assets/textures/ai/game_ready/`; each passes role audit. Generated tile edges are blended,
|
|
||||||
icon alpha masks are recolored white where needed, and the vignette uses the existing
|
|
||||||
deterministic generator because the API returned an empty transparent image. The six
|
|
||||||
`docs/screenshots/` captures were regenerated after wiring and pass screenshot audit.
|
|
||||||
- Detailed graphics: textured facade/awning/skyline, humanoid NPC rigs + props + walk bob +
|
|
||||||
attention head-turn, inspection artifact cards, mode icons, escalation vignette, body-cam
|
|
||||||
overlay (REC + shift clock). Screenshot set regenerated (`docs/screenshots/`, day + night).
|
|
||||||
- Polish: wand scan cooldown (`WAND_COOLDOWN_S` + `wand.recharge` message).
|
|
||||||
- NOTE: collaborators occasionally add uncommitted work to this shared tree (e.g. the radio
|
|
||||||
easter egg); check `git status` before committing and leave foreign changes untouched unless asked.
|
|
||||||
|
|
||||||
- Screen-design references (`tools/screen_designs.py`, tests in `tools/test_screen_designs.py`):
|
|
||||||
`generate` turns the screen descriptions in `docs/` (README captions, ADRs 0007–0010,
|
|
||||||
PLAN, architecture) into reference images under `docs/screens/designs/` — `--draft`
|
|
||||||
draws offline Pillow wireframes, `--ai` generates realistic 3D scenes / in-game UI via
|
|
||||||
OpenRouter (default model `openai/gpt-5-image-mini`). Screens are tagged `scene` or
|
|
||||||
`ui`; prompts get a shared art-direction suffix, and requests use a 3:2 landscape
|
|
||||||
source cropped to 16:9 (not a square centre-crop). `compare` checks `docs/screenshots/*.png` for aspect/size/
|
|
||||||
blank-frame and coarse layout/palette/detail drift. Drift distances are warnings, not
|
|
||||||
failures: a mockup never matches a real render, so semantic composition is a follow-up
|
|
||||||
vision-model judge (tracked in TODO).
|
|
||||||
|
|
||||||
## Known Issues / Notes
|
|
||||||
|
|
||||||
- Reference cycles are fixed: `FSM` holds its owner via `WeakRef`, `ShiftCoordinator` is
|
|
||||||
`RefCounted`, and persistence tests free the `Save` node — the suite exits with no
|
|
||||||
"resources still in use" warnings (was ~13). If a future subsystem introduces a cycle,
|
|
||||||
repeat the WeakRef pattern.
|
|
||||||
- Shift length: `GameManager` uses the scenario's `shift_duration_s` (900s ≈ 10-15 min);
|
|
||||||
tests and the screenshot tool override elapsed time directly.
|
|
||||||
- Headless (dummy renderer) logs "Parameter m is null" from `mesh_get_surface_count`; it does not occur with a real GL driver and does not affect logic.
|
|
||||||
- Steps are visual-only (no collider) so NPCs can path to the entrance; the guard spawns on the landing so it never needs to climb.
|
|
||||||
- Persistence tests write into the real `user://shift_results.json` (harmless local pollution of the demo history).
|
|
||||||
- macOS export required `rendering/textures/vram_compression/import_etc2_astc=true` in `project.godot` (universal build demands ETC2 ASTC enabled).
|
|
||||||
- Joke Shop `door_staff_lines` are logged through `GameManager` (inspect / wand / refuse). They are not a separate dialogue UI.
|
|
||||||
- Access loadout is implemented in the working tree (see Current State); do not re-fix `override_access` as if it were still unread.
|
|
||||||
- `Save.SETTINGS_FILE` is declared and unused; no mouse-sensitivity or pause menu yet.
|
|
||||||
- Headless suite is **77/77**; the windowed focus/radial smoke passes. Remaining visual follow-ups are at the top of `TODO.md`.
|
|
||||||
- Typed spawn signal extra args: handlers must accept the 4th `event: Dictionary` or Godot will not call them (`test_shift_coordinator_runs_a_shift` failed until `_on_spawn` took four args).
|
|
||||||
- GDScript inference trap that broke the main scene: a `var` typed as a base class
|
|
||||||
(`radial: CanvasLayer`) exposes script members as `Variant`, so `var x := radial.is_open` is a
|
|
||||||
parse error. Type it as the script type (`ActionRadial`) or annotate the local explicitly.
|
|
||||||
`--check-only --script` cannot see the `Save` autoload; use a real boot (`--quit-after 180`) to
|
|
||||||
verify a full-scene compile.
|
|
||||||
- Do not snapshot right after changing a transform in the same frame: `capture_screenshots.gd`
|
|
||||||
needs a state/frame gap or the capture uses the old camera orientation.
|
needs a state/frame gap or the capture uses the old camera orientation.
|
||||||
- `EventScheduler` probabilities are per simulated second, not per call. Never poll it per frame.
|
- `EventScheduler` probabilities are per simulated second, not per call; never poll it per frame.
|
||||||
- Screenshot staging lives in `src/test/capture_screenshots.gd` (guard at z≈10.5 facing the door).
|
- Light falloff: `omni_attenuation = 0.0` is **constant**, not physical, falloff in Godot 4.7 —
|
||||||
`docs/screenshots/` copies are manual: copy from `user://screenshots/`.
|
all local lights use `1.0`.
|
||||||
|
- glTF `baseColorFactor` is LINEAR — writing sRGB makes props pastel; `srgb_to_linear()` is applied
|
||||||
|
in `build_glb`. A new/changed `.glb` needs `godot --headless --import` before `load()` sees it.
|
||||||
|
- **Rebuilding a character GLB also requires re-baking its AO** (`tools/bake_ao.py -- --only
|
||||||
|
<name>`). `blender_assets.py build` writes the GLB only; the old `<name>_ao.png` then maps onto
|
||||||
|
the new geometry and produces large dark cheek/jaw blotches. Bake the AO, re-import, and refresh
|
||||||
|
the stale `.godot` cache (scratch-copy import) before judging a face/body change.
|
||||||
|
- `Save.SETTINGS_FILE` is declared and unused; settings persist through
|
||||||
|
`load_settings()`/`save_settings()`.
|
||||||
|
- Crowd reactions emit `event_bus.crowd_changed`; `GameManager._connect_crowd()` must reconnect
|
||||||
|
after every `coordinator.setup()` (same class of bug as the radio-dispatch reconnect).
|
||||||
|
- Packaging: NSIS uses `-D` defines (never `/D`) and resolves relative defines against the script
|
||||||
|
directory (pass `$PWD`-absolute paths); installed exe must be space-free; ICO must be classic
|
||||||
|
BMP entries (PIL emits PNG-compressed ICOs NSIS can't load). appimagetool runs system
|
||||||
|
`appstreamcli` — pass `--pedantic`.
|
||||||
|
- Credentials: `~/.git-credentials` holds the Gitea creds; `.env` holds only `OPENROUTER_API_KEY`.
|
||||||
|
- `assets/textures/game_ready/` is not imported (no `.import`) — use the imported
|
||||||
|
`assets/textures/skyline.png` path.
|
||||||
|
- Godot 4.7 writes `*.gd.uid` files — kept out of `git status` via `.git/info/exclude`.
|
||||||
|
- Collaborators occasionally leave uncommitted work in the shared tree; check `git status` before
|
||||||
|
committing and leave foreign changes untouched.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
- Tests: `godot --headless --path . --script res://src/test/test_runner.gd`
|
||||||
|
- Boot check: `godot --headless --path . --quit-after 120`
|
||||||
|
- Single screenshot: `godot --path . -s res://src/test/take_screenshot.gd`
|
||||||
|
- Staged gallery: `tools/check_screenshots.sh` (re-run the whole script, never a partial capture)
|
||||||
|
- Blender build: `flatpak run org.blender.Blender --background --python tools/npc_pipeline.py -- build`
|
||||||
|
(flatpak cwd is `/app/blender` and cannot read `/tmp` — use absolute repo paths; `.gdignore`
|
||||||
|
markers keep Godot from importing `.blend`/`tmp`)
|
||||||
|
- Textures: `python3 tools/generate_textures.py` (then `godot --headless --import`); AI mode
|
||||||
|
`python3 tools/generate_textures.py --ai`; general pipeline `python3 tools/asset_pipeline.py`
|
||||||
|
- Character checks: `python3 tools/blender_assets.py inspect|audit|fit`,
|
||||||
|
`python3 tools/npc_proportions.py`
|
||||||
|
- Docs: `python3 tools/check_docs.py` (every tracked image must be linked from its folder README)
|
||||||
|
- Reference research: `python3 tools/refcollect.py search <q> --slug <block> --limit N`
|
||||||
|
(local-only), `tools/refboard.py` (Commons), `tools/three_up.py compose/rows`,
|
||||||
|
`tools/scene_concepts.py generate/anchor/realism`
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
- Add a vision-model (LLM) judge on top of the screen-design comparison: pixel and
|
|
||||||
perceptual metrics cannot decide whether a screenshot's composition is correct.
|
|
||||||
- Whether the responder avatar should be playable/controllable or purely scripted.
|
- Whether the responder avatar should be playable/controllable or purely scripted.
|
||||||
- Localization target languages beyond English.
|
- Localization target languages beyond English.
|
||||||
- macOS notarization approach for broad distribution.
|
- macOS notarization approach for broad distribution.
|
||||||
|
|
||||||
## Plan vs code (re-evaluated)
|
## Current State
|
||||||
|
|
||||||
On local `main` (6 commits not on origin): v0.2.1 packages, ADRs 0007–0010, asset pipeline wired to
|
The playable build covers the three venues, tutorial/campaign/checkpoints, the 0.9.3–0.9.6
|
||||||
`assets/textures/ai/game_ready/`, screenshots refreshed.
|
polish/texture/integration passes and the Blender + realism asset pipeline (see `DONE.md` and
|
||||||
|
`CHANGELOG.md`). The 12-character library (realistic face + four crowd variants) and the
|
||||||
Uncommitted: `AccessLoadout`, control pass (cone, ring, radial, NPC face, named input actions),
|
Comedy Store map rebuild are complete. **Priority (2026-11-04, owner):** 1) release the finished procedural character development as
|
||||||
and boot/spawn fixes. Headless 77/77; windowed smoke passes. Access-artifact and control-pass
|
the **0.9.7 patch**, 2) finish the MakeHuman character + the 3D model and map improvements,
|
||||||
sections moved to `DONE.md`.
|
3) re-evaluate the 3D models, 4) ship it all together in the **0.10.0 minor** — `TODO.md` is the
|
||||||
|
priority source. Test counts as of 2026-11-04: Godot 277/277,
|
||||||
Still in `TODO.md`: world follow-ups (guard float, step colliders, skyline asset, marquee size);
|
Python 161/161, `check_docs` OK.
|
||||||
session shell; leftover PLAN verbs; crowd rubberneck; emergency aftermath; body-cam replay;
|
|
||||||
sit/photo/film poses; real crowd audio; Joke Shop/Studio extra events; PLAN.md says GUT/GitHub.
|
|
||||||
|
|
||||||
<!-- b64: RWFzdGVyIGVnZzogYSByYXJlIHJhZGlvIHRyYW5zbWlzc2lvbiAiwr9Ew7NuZGUgZXN0w6EgbGEgbW90YT8iIGZpcmVzIHZpYSBHYW1lTWFuYWdlci5fbWF5YmVfcmFkaW9fZWFzdGVyX2VnZyAoMi41JSBjaGFuY2UgYWZ0ZXIgYSBzdWNjZXNzZnVsIHJhZGlvIGNhbGwpLiBTdHJpbmcga2V5IHJhZGlvLmVhc3Rlcl9lZ2cgaW4gY29udGVudC9zdHJpbmdzLmVuLmpzb24uIERvIG5vdCByZW1vdmUu -->
|
<!-- b64: RWFzdGVyIGVnZzogYSByYXJlIHJhZGlvIHRyYW5zbWlzc2lvbiAiwr9Ew7NuZGUgZXN0w6EgbGEgbW90YT8iIGZpcmVzIHZpYSBHYW1lTWFuYWdlci5fbWF5YmVfcmFkaW9fZWFzdGVyX2VnZyAoMi41JSBjaGFuY2UgYWZ0ZXIgYSBzdWNjZXNzZnVsIHJhZGlvIGNhbGwpLiBTdHJpbmcga2V5IHJhZGlvLmVhc3Rlcl9lZ2cgaW4gY29udGVudC9zdHJpbmdzLmVuLmpzb24uIERvIG5vdCByZW1vdmUu -->
|
||||||
@@ -1,612 +1,209 @@
|
|||||||
# Game Plan: Step Line
|
# Game Plan: Gig Line
|
||||||
|
|
||||||
## Premise
|
## Premise
|
||||||
|
|
||||||
You play a private security guard stationed at the top of the steps outside a fictional building on Hollywood Boulevard. Your job is to keep the entrance and steps clear while dealing with tourists, trespassers, performers, protesters, intoxicated pedestrians, and occasional determined intruders.
|
You are a private security guard working the entrance of a stylized reconstruction of the real El Capitan Entertainment Centre on Hollywood Boulevard. You keep the steps and doors clear, check who is allowed in, and deal with tourists, performers, influencers, confused visitors, intoxicated pedestrians, trespassers, people in mental-health crisis, and rare determined threats.
|
||||||
|
|
||||||
The game is about **judgment, communication, and escalation management**, not simply attacking anyone who approaches.
|
The game is about **judgment, communication, access verification, and proportionate escalation**, not about attacking everyone who approaches.
|
||||||
|
|
||||||
---
|
## Design Pillars
|
||||||
|
|
||||||
|
- **Lowest effective response wins.** Observation, a gesture, or a clear direction should solve most situations.
|
||||||
|
- **People are not labels.** Clothes, props, credentials, and behavior tell the player what they need; internal NPC types stay in the debug overlay.
|
||||||
|
- **Every decision has a record.** Incidents, evidence, radio reports, and outcomes feed the shift review.
|
||||||
|
- **Emergency help is a last resort.** Responders handle crises the guard cannot safely manage; calling them has costs.
|
||||||
|
- **Readability beats raw fidelity.** The GL Compatibility renderer, clear silhouettes, the focus ring, and the HUD stay legible.
|
||||||
|
|
||||||
|
## Development Priorities
|
||||||
|
|
||||||
|
The active planning order is set in [`TODO.md`](TODO.md) and in `AGENTS.md`:
|
||||||
|
|
||||||
|
1. **Character development (unversioned).** Expand the owned procedural character library with
|
||||||
|
crowd, age, build, hair and outfit variety. Keep the frozen bone/tint/gear contract; see
|
||||||
|
[ADR-0015](docs/adr/0015-npc-model-variety-sourcing.md) and
|
||||||
|
[`docs/npc-model-sourcing.md`](docs/npc-model-sourcing.md). Quaternius is a reference;
|
||||||
|
MakeHuman is deferred to a separate local pipeline.
|
||||||
|
2. **Local map development (unversioned).** Move beyond stylized venue reconstruction with
|
||||||
|
licensed OpenStreetMap and 3D building data, downloaded reference imagery for photoreal
|
||||||
|
facades, AI-assisted composition, and a Gaussian-splatting evaluation that stays in the
|
||||||
|
development pipeline unless it proves runtime-safe.
|
||||||
|
3. **Versioned release hardening.** The 0.10.0 release candidate and later semantic versions come
|
||||||
|
after the unversioned development tracks in planning order. Versions are assigned only when a
|
||||||
|
track's scope is accepted into a release.
|
||||||
|
|
||||||
## Core Gameplay Loop
|
## Core Gameplay Loop
|
||||||
|
|
||||||
1. Watch the stairs, entrance, and sidewalk.
|
1. Watch the entrance, steps, queue, and sidewalk.
|
||||||
2. Identify why each person is approaching.
|
2. Work out why each person is approaching.
|
||||||
3. Give verbal warnings and directions.
|
3. Use a gesture or short direction before escalating.
|
||||||
4. Escalate only when necessary.
|
4. Verify authorized access when the venue requires it.
|
||||||
5. Call for assistance in rare dangerous situations.
|
5. Escalate only when the person refuses, forces entry, or presents a genuine threat.
|
||||||
6. Maintain public safety, building security, and your employer’s reputation.
|
6. Record the incident and call for help when it is justified.
|
||||||
|
7. Review the shift: safety, judgment, accuracy, crowd reaction, and reputation.
|
||||||
|
|
||||||
People should not all behave the same way. Some are confused, some are rude, some are filming, and a very small number are deliberately attempting to enter.
|
A shift lasts roughly 10–15 minutes. Most people comply when approached calmly.
|
||||||
|
|
||||||
## Authorized Access
|
## Access and Verification
|
||||||
|
|
||||||
Some people are authorized to enter the building. The security guard must verify them before granting access.
|
Some people are authorized to enter. The guard may admit them by:
|
||||||
|
|
||||||
An authorized person can be admitted if either:
|
- recognizing a person the guard already knows; or
|
||||||
|
- verifying a venue-appropriate artifact: wristband, ID, ticket, credential, or guest list.
|
||||||
|
|
||||||
- The guard already knows and recognizes them.
|
Artifacts can be valid, expired, counterfeit, mismatched, missing, or ambiguous. The player inspects the details the venue asks for instead of judging by appearance.
|
||||||
- The person presents a valid event or staff wristband, which the guard checks before opening the entrance.
|
|
||||||
|
|
||||||
The wristband check should require the player to inspect details such as its color, pattern, date, or event marking. NPCs may have valid wristbands, expired wristbands, counterfeit wristbands, or no wristband at all.
|
Decisions are admit, redirect, hold, or refuse. Incorrectly denying an authorized person hurts reputation; admitting an unauthorized person is a security failure. Recognition is a player decision and is recorded as evidence.
|
||||||
|
|
||||||
The player should avoid relying on appearance or assumptions. A familiar person may still need verification if the building’s current policy requires it, while an unfamiliar person with a valid wristband may be admitted after a successful check. Incorrectly denying an authorized person harms the building’s reputation; admitting someone without authorization creates a security failure.
|
## Escalation Model
|
||||||
|
|
||||||
---
|
| Level | Player response | Intent |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 0 | Observation | Watch harmless behavior; intervene only when needed. |
|
||||||
|
| 1 | Simple deterrence / polite direction | Raise a hand, make eye contact, point to the sidewalk or check-in area, shake the head, give a short instruction. |
|
||||||
|
| 2 | Formal warning | State the rule and ask for compliance; optionally arm the body camera. |
|
||||||
|
| 3 | Blocking and positioning | Stand between the person and the door, secure the entrance, keep distance and an escape route, ask for their reason. |
|
||||||
|
| 4 | Radio for security assistance | Request a second guard, supervisor, building management, or other support. |
|
||||||
|
| 5 | Emergency response | Hold and confirm the emergency call only for an immediate threat such as a weapon, assault, forced entry, or serious threat to occupants. |
|
||||||
|
|
||||||
## Escalation System
|
At Level 2 an NPC may leave, argue, pretend not to understand, record the guard, call someone, or move closer. At Level 3 the goal is to deny access **without starting a fight**. Physical contact creates legal, employment, and public-relations consequences.
|
||||||
|
|
||||||
## Level 0: Observation
|
At Level 4 the report matters: location, description, behavior, weapons seen, and immediate danger. Exaggerated reports damage credibility. At Level 5 the call may evacuate the building, close the street, trigger a review, cost reputation, or open an investigation if unjustified. Responders may command, restrain, evacuate, or arrest; they are not a routine stronger attack.
|
||||||
|
|
||||||
The guard watches NPC behavior and decides whether intervention is needed.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- Someone taking a photograph from the sidewalk
|
|
||||||
- A tourist reading a map
|
|
||||||
- A person sitting briefly on the edge of the steps
|
|
||||||
- Someone approaching the entrance with a delivery package
|
|
||||||
|
|
||||||
The player should avoid interrupting harmless behavior. Unnecessary confrontations reduce the guard’s performance rating.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Level 1: Simple Deterrence and Polite Direction
|
|
||||||
|
|
||||||
The player can begin with a simple, nonverbal deterrent before speaking. These actions are quick and low-pressure, making them useful when someone is only approaching the steps casually or appears unsure.
|
|
||||||
|
|
||||||
Possible actions include:
|
|
||||||
|
|
||||||
- Raise an open hand to signal “stop” or “please wait”
|
|
||||||
- Make eye contact
|
|
||||||
- Point toward the public sidewalk
|
|
||||||
- Gesture toward the wristband-check area
|
|
||||||
- Shake the head gently when someone approaches a restricted entrance
|
|
||||||
|
|
||||||
If the person continues, the player uses a short verbal prompt.
|
|
||||||
|
|
||||||
Example commands:
|
|
||||||
|
|
||||||
- “Please keep the stairs clear.”
|
|
||||||
- “The entrance is for authorized guests only.”
|
|
||||||
- “You can’t remain here.”
|
|
||||||
- “Please move back to the sidewalk.”
|
|
||||||
|
|
||||||
At this stage, most NPCs comply immediately.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Level 2: Formal Warning
|
|
||||||
|
|
||||||
If the person remains on the steps or continues toward the entrance, the player issues a formal warning.
|
|
||||||
|
|
||||||
Possible dialogue:
|
|
||||||
|
|
||||||
> “This is private property. You have been asked to leave. Please move away from the entrance now.”
|
|
||||||
|
|
||||||
The player may activate a visible security light or body camera. This creates a record of the interaction and can discourage repeat offenders.
|
|
||||||
|
|
||||||
At this level, the NPC may:
|
|
||||||
|
|
||||||
- Leave
|
|
||||||
- Argue
|
|
||||||
- Pretend not to understand
|
|
||||||
- Start recording the guard
|
|
||||||
- Call someone else
|
|
||||||
- Move closer to the door
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Level 3: Blocking and Physical Positioning
|
|
||||||
|
|
||||||
The guard moves into a defensive position between the NPC and the building.
|
|
||||||
|
|
||||||
Gameplay actions:
|
|
||||||
|
|
||||||
- Stand in front of the entrance
|
|
||||||
- Point toward the public sidewalk
|
|
||||||
- Lock or secure the door
|
|
||||||
- Request the person’s reason for entry
|
|
||||||
- Keep distance while maintaining a clear escape route
|
|
||||||
- Use a flashlight or radio warning tone at night
|
|
||||||
|
|
||||||
The player should not automatically shove or strike people. Physical contact creates legal, employment, and public-relations consequences.
|
|
||||||
|
|
||||||
This level is about **denying access without creating a fight**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Level 4: Radio for Security Assistance
|
|
||||||
|
|
||||||
If the person refuses to leave, attempts to force the door, threatens staff, or repeatedly returns, the player uses the radio.
|
|
||||||
|
|
||||||
Possible assistance:
|
|
||||||
|
|
||||||
- A second security guard
|
|
||||||
- Building management
|
|
||||||
- Police dispatch
|
|
||||||
- Nearby security personnel
|
|
||||||
|
|
||||||
The radio call takes time, so the player must continue monitoring the person while waiting. The player can choose what information to report:
|
|
||||||
|
|
||||||
- Location
|
|
||||||
- Description
|
|
||||||
- Behavior
|
|
||||||
- Whether weapons have been seen
|
|
||||||
- Whether anyone is in immediate danger
|
|
||||||
|
|
||||||
Incorrect or exaggerated reports damage the guard’s credibility.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Level 5: Emergency Response
|
|
||||||
|
|
||||||
This is reserved for immediate threats, such as:
|
|
||||||
|
|
||||||
- A person displaying a weapon
|
|
||||||
- An attempted forced entry while people are inside
|
|
||||||
- An assault on the guard or another person
|
|
||||||
- A serious threat against occupants
|
|
||||||
- A violent struggle that cannot be safely disengaged
|
|
||||||
|
|
||||||
The player presses and holds a clearly marked **Emergency Radio** button. A confirmation prompt prevents accidental activation.
|
|
||||||
|
|
||||||
The call brings an off-duty law-enforcement officer or armed emergency responder to the location. This character has greater authority and can control a dangerous situation, but the call has serious consequences:
|
|
||||||
|
|
||||||
- The building may be evacuated
|
|
||||||
- The street may be closed
|
|
||||||
- The guard’s actions are reviewed
|
|
||||||
- The building loses reputation
|
|
||||||
- The player may face an investigation if the call was unjustified
|
|
||||||
- The officer may use force only when the game’s threat assessment supports it
|
|
||||||
|
|
||||||
The officer should not function as a convenient “stronger attack.” Their role is to handle a crisis the player cannot safely manage. In most scenarios, their arrival results in commands, restraint, evacuation, or arrest rather than automatic gunfire.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## NPC Behavior Types
|
## NPC Behavior Types
|
||||||
|
|
||||||
## Tourists
|
- **Tourists:** take photos, sit on steps, drift toward restricted doors; usually respond well to polite directions.
|
||||||
|
- **Street performers:** may block stairs or argue when moved; can be redirected to a permitted area.
|
||||||
- Take photos
|
- **Influencers and filmmakers:** film the guard, test boundaries, and create public-relations risk.
|
||||||
- Sit on steps
|
- **Confused visitors:** may have appointments or genuinely believe the building is public; reward patient explanation.
|
||||||
- Wander toward restricted doors
|
- **Intoxicated pedestrians:** move unpredictably, ignore instructions, and need extra distance or assistance.
|
||||||
- Usually respond well to polite directions
|
- **Trespassers:** test entrances, use distractions, and may return after being told to leave.
|
||||||
|
- **Violent threats:** rare, escalate quickly, and justify emergency response only when behavior shows immediate danger.
|
||||||
## Street Performers
|
- **People in mental-health crisis:** may loiter, behave unpredictably, and respond poorly to aggression; require patience and careful de-escalation. The setting may refer to a 51/50 hold, but the game should present the person respectfully, not as an automatic threat.
|
||||||
|
|
||||||
- May block stairs
|
|
||||||
- May become argumentative if moved
|
|
||||||
- Can be redirected to a permitted area
|
|
||||||
|
|
||||||
## Influencers and Filmmakers
|
|
||||||
|
|
||||||
- Film the guard
|
|
||||||
- Test boundaries for content
|
|
||||||
- May accuse the guard of overreacting
|
|
||||||
- Create public-relations risk
|
|
||||||
|
|
||||||
## Confused Visitors
|
|
||||||
|
|
||||||
- Have appointments or directions
|
|
||||||
- May genuinely believe the building is public
|
|
||||||
- Reward patient explanations
|
|
||||||
|
|
||||||
## Intoxicated Pedestrians
|
|
||||||
|
|
||||||
- Move unpredictably
|
|
||||||
- May ignore instructions
|
|
||||||
- Require additional distance and assistance
|
|
||||||
|
|
||||||
## Trespassers
|
|
||||||
|
|
||||||
- Repeatedly test entrances
|
|
||||||
- Use distractions
|
|
||||||
- May return after being told to leave
|
|
||||||
|
|
||||||
## Violent Threats
|
|
||||||
|
|
||||||
- Rare
|
|
||||||
- Escalate quickly
|
|
||||||
- Trigger the emergency response system only when their behavior indicates immediate danger
|
|
||||||
|
|
||||||
## 51/50s
|
|
||||||
|
|
||||||
Crazy Homeless Individuals may be referred to as "51/50s" in the context of the game, indicating a person who may be experiencing a mental health crisis and could pose a potential threat to themselves or others.
|
|
||||||
|
|
||||||
- May loiter near entrances
|
|
||||||
- Can be unpredictable in behavior
|
|
||||||
- Often respond poorly to aggressive confrontation
|
|
||||||
- Require careful de-escalation and patience
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Special Systems
|
## Special Systems
|
||||||
|
|
||||||
## Reputation Meter
|
### Reputation
|
||||||
|
|
||||||
The player is evaluated on:
|
The shift evaluates peaceful resolutions, response time, radio accuracy, unnecessary confrontations, public complaints, property damage, entrance security, and whether assistance was justified. Calling armed help too often scores poorly even when no one is hurt.
|
||||||
|
|
||||||
- Number of incidents resolved peacefully
|
### Evidence and Body Camera
|
||||||
- Response time
|
|
||||||
- Accuracy of radio reports
|
|
||||||
- Number of unnecessary confrontations
|
|
||||||
- Public complaints
|
|
||||||
- Property damage
|
|
||||||
- Whether the entrance remained secure
|
|
||||||
|
|
||||||
A player who calls armed assistance too often should score poorly, even if no one was injured.
|
Interactions are recorded automatically. The body camera is a real state the player can toggle; a formal warning also arms it. The shift review uses the recorded events and evidence to show whether the response was justified.
|
||||||
|
|
||||||
## Evidence and Body Camera
|
### Crowd Reaction
|
||||||
|
|
||||||
Interactions are automatically recorded. After each shift, the player reviews incidents and sees whether their escalation was justified.
|
Nearby pedestrians react to the guard. Calm professionalism attracts little attention; aggression draws a crowd; visible weapons or responders can cause panic. Good de-escalation keeps the boulevard moving.
|
||||||
|
|
||||||
This encourages players to think before acting.
|
### Radio Limits
|
||||||
|
|
||||||
## Public Crowd Reaction
|
The emergency radio has a cooldown, and the call requires a short report. Assistance takes time to arrive, so the guard must keep monitoring the situation.
|
||||||
|
|
||||||
Nearby pedestrians react to the guard’s behavior:
|
|
||||||
|
|
||||||
- Calm professionalism attracts little attention
|
|
||||||
- Aggressive behavior creates a crowd
|
|
||||||
- Visible weapons or emergency responders cause panic
|
|
||||||
- Good de-escalation keeps the boulevard moving normally
|
|
||||||
|
|
||||||
## Limited Radio Battery or Cooldown
|
|
||||||
|
|
||||||
The emergency radio should not be infinitely available. A call has a cooldown and may require the player to give a short report first.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Shift Structure
|
## Shift Structure
|
||||||
|
|
||||||
Each shift lasts 10–15 minutes.
|
- **Early:** harmless tourists, delivery workers, people resting on the steps.
|
||||||
|
- **Midday:** heavier crowds, more entry attempts, more filming.
|
||||||
|
- **Evening:** lower light, more intoxicated pedestrians and aggressive groups.
|
||||||
|
- **Late:** a rare serious incident may test whether the player recognizes a real threat without overreacting to ordinary behavior.
|
||||||
|
|
||||||
### Early Shift
|
## Outcomes
|
||||||
|
|
||||||
Mostly harmless tourists, delivery workers, and people sitting on the stairs.
|
A successful shift keeps the building secure, resolves most incidents without force, keeps public access clear, makes justified emergency calls, avoids bystander injuries, and follows policy.
|
||||||
|
|
||||||
### Midday
|
A poor shift involves excessive force, confronting innocent people, ignoring a real threat, false reports, allowing unauthorized entry, causing a crowd or panic, or behaving recklessly even if the door stayed locked.
|
||||||
|
|
||||||
Crowds become heavier. More people attempt to enter the building or film interactions.
|
## Venues
|
||||||
|
|
||||||
### Evening
|
### El Capitan Entertainment Centre (6840 Hollywood Blvd)
|
||||||
|
|
||||||
Lighting decreases. Intoxicated pedestrians and aggressive groups become more common.
|
The original scenario: the historic Masonic Temple / El Capitan Entertainment Centre, with six Ionic columns, eight stone steps, the Hollywood Walk of Fame sidewalk, neighbouring theatre and retail frontage, and the four-lane boulevard. Tourists, performers, influencers, confused visitors, intoxicated pedestrians, trespassers, people in crisis, and rare violent threats approach from the street.
|
||||||
|
|
||||||
### Late Shift
|
Authorized people are recognized or present a valid wristband. Wristbands vary by color, pattern, date, and event marking, and may be valid, expired, counterfeit, or missing.
|
||||||
|
|
||||||
A rare serious incident may occur, testing whether the player recognizes a genuine threat instead of overreacting to ordinary behavior.
|
### The Joke Shop
|
||||||
|
|
||||||
---
|
A Sunset Strip comedy club inspired by The Comedy Store at 8433 Sunset Blvd: a
|
||||||
|
low black hall under a deep, signature-covered canopy, a circular blade sign,
|
||||||
|
red velvet curtains and the dry hillside rising behind it. The door person
|
||||||
|
checks IDs, tickets, reservations, guest-list status, and screens guests with a
|
||||||
|
handheld wand under venue policy.
|
||||||
|
|
||||||
## Win and Loss Conditions
|
The wand does **not** identify a weapon automatically. Keys, belts, and phones can trigger false alarms; the player must ask the guest to identify the item and apply procedure. A guest with valid credentials can still require assistance if they threaten staff or patrons.
|
||||||
|
|
||||||
## Successful Shift
|
Typical events:
|
||||||
|
|
||||||
- Building remains secure
|
- a busy weekend show creates a long line;
|
||||||
- Most incidents are resolved without physical force
|
- a performer arrives through the guest entrance;
|
||||||
- Public access remains clear
|
- a guest has a reservation but no ID;
|
||||||
- Emergency calls are justified
|
- a fake or borrowed ID appears;
|
||||||
- No bystanders are injured
|
- a wand alert comes from keys or a belt;
|
||||||
- The player follows company policy
|
- a guest refuses screening but insists on entering;
|
||||||
|
- someone tries to follow an authorized person through the door;
|
||||||
|
- a late audience member arrives during the performance.
|
||||||
|
|
||||||
## Poor Performance
|
Joke Shop escalation follows the common ladder: direction, explanation, repeat request, refusal, manager/security assistance, emergency only for immediate danger.
|
||||||
|
|
||||||
- Excessive use of force
|
### Hollywood Boulevard Late Night TV Studio
|
||||||
- Repeatedly confronting innocent people
|
|
||||||
- Ignoring a real threat
|
|
||||||
- Making false radio reports
|
|
||||||
- Allowing repeated unauthorized entry
|
|
||||||
- Causing a crowd or panic
|
|
||||||
|
|
||||||
A shift can be technically “secure” but still receive a poor rating if the guard behaves recklessly.
|
A busy studio entrance at the same real El Capitan Entertainment Centre block as the Hollywood day shift, restaged for a late-night arrival: the same stone facade and steps with a bulb-lit canopy, a black car at the kerb and a barrier line. It has audience members, production staff, performers, guests, deliveries, celebrity arrivals, tourists, influencers, and the same range of street behavior as the building scenario.
|
||||||
|
|
||||||
---
|
Staff and guests are recognized or present a wristband, credential, ticket, guest-list confirmation, or other approved marker. The player keeps the steps clear, protects arrivals and equipment, manages filming boundaries, watches for tailgating, and calls for help only when there is immediate danger.
|
||||||
|
|
||||||
## Venues and Scenarios
|
Typical events:
|
||||||
|
|
||||||
The game should support multiple venues, each with its own access rules, crowd patterns, equipment, and escalation procedures. The core gameplay remains the same: observe, verify access, communicate clearly, and use the lowest effective level of escalation.
|
- a crowd gathers for a celebrity arrival;
|
||||||
|
- tourists block the entrance for photographs;
|
||||||
|
- a performer sets up in the audience queue;
|
||||||
|
- an influencer ignores the filming boundary;
|
||||||
|
- a confused visitor believes the studio is open;
|
||||||
|
- an intoxicated pedestrian approaches the stage door;
|
||||||
|
- a trespasser blends into an audience group;
|
||||||
|
- an authorized guest arrives without a wristband;
|
||||||
|
- someone presents a counterfeit credential;
|
||||||
|
- a violent threat appears during a crowded arrival.
|
||||||
|
|
||||||
## The Joke Shop
|
## Presentation and Controls
|
||||||
|
|
||||||
**The Joke Shop** is a comedy club scenario where the player works as a door person at the entrance. The player is responsible for checking identification, confirming that guests are attending the correct show, and screening guests for weapons with a handheld security wand.
|
The game is first-person: WASD and mouse look, a focus cone on nearby people, a billboarded focus circle, `Tab`/mouse-wheel cycling for aim-free selection, and a radial action menu opened with left click or `Space`. `Shift` is precision walk. Gamepad input is supported, and keys/buttons can be remapped in Settings.
|
||||||
|
|
||||||
### Primary Responsibilities
|
The action wheel prioritizes de-escalation: Stop, Look, Point, No, Ask, Inspect, Wand, Warn, Block, Reason, Recognize, Radio. Emergency stays on `R` and is not a wheel slice. `W` is walk, never the wand; `C` is the optional wand shortcut.
|
||||||
|
|
||||||
- Check each guest’s government-issued ID where required.
|
The visual direction is readable stylized realism:
|
||||||
- Confirm that the guest meets the venue’s age requirement.
|
|
||||||
- Verify that the ID appears valid and belongs to the person presenting it.
|
|
||||||
- Check tickets, reservations, or guest-list status.
|
|
||||||
- Use the handheld wand according to venue policy.
|
|
||||||
- Direct guests through the entrance without blocking the sidewalk.
|
|
||||||
- Refuse entry when a guest has an invalid ID, no valid admission, or a prohibited item.
|
|
||||||
|
|
||||||
### Guest Outcomes
|
- **Character direction:** 8 skinned NPC variants, 18 gear pieces and 20 street/venue props, with four crowd-variety variants prototyped. The generator-first expansion is the active character track ([ADR-0015](docs/adr/0015-npc-model-variety-sourcing.md)); Quaternius is a reference and MakeHuman is deferred to a local pipeline.
|
||||||
|
- **Realism pass:** AI-assisted per-phase skyline panoramas, v2 surface tiles with normal maps, wet asphalt at night, a warm day haze, phase-specific sun/exposure/fog, a tight flashlight spot (no beam geometry), and denser foliage.
|
||||||
|
- **Local map direction:** the venues are currently stylized reconstructions. The local-map track evaluates licensed OpenStreetMap/3D building data, licensed reference photography, AI-assisted composition and Gaussian splatting as a development tool; online data and splat rendering are not runtime dependencies.
|
||||||
|
- **References and QA:** runtime UI art lives in `assets/images/ui/`; reference/art-direction sets live in `docs/screens/designs/` and `docs/screens/concepts/`; captures live in `docs/screenshots/`; realism runs live in `docs/screens/realism/`. `screen_designs.py compare/judge` and `tools/check_screenshots.sh` guard drift.
|
||||||
|
- **Storage convention:** see [ADR-0014](docs/adr/0014-runtime-image-root.md). Never overwrite an existing image; new outputs get a new path or `-vN` suffix.
|
||||||
|
|
||||||
A guest may have:
|
The active planning order — unversioned character and local-map development first, then the versioned 0.10.0 release-candidate hardening and later releases — is tracked in [`TODO.md`](TODO.md); this plan stays at the product/design level.
|
||||||
|
|
||||||
- A valid ID and ticket
|
Primary decisions: [ADR-0007](docs/adr/0007-locomotion-and-input-layers.md), [ADR-0008](docs/adr/0008-cone-focus-and-head-ring.md), [ADR-0009](docs/adr/0009-radial-action-menu.md), [ADR-0010](docs/adr/0010-procedural-npc-presentation.md), [ADR-0011](docs/adr/0011-aim-free-selection-and-precision-walk.md), [ADR-0012](docs/adr/0012-surface-materials-and-generated-props.md), [ADR-0013](docs/adr/0013-blender-authored-npc-characters.md), [ADR-0015](docs/adr/0015-npc-model-variety-sourcing.md).
|
||||||
- A valid ID but no ticket or reservation
|
|
||||||
- An expired, damaged, or questionable ID
|
|
||||||
- An ID that does not match the guest
|
|
||||||
- A prohibited item detected by the wand
|
|
||||||
- A harmless item that causes a false alarm
|
|
||||||
- No ID when identification is required
|
|
||||||
|
|
||||||
The player must make decisions based on the venue’s rules and observable evidence, not on a guest’s appearance or attitude. A calm guest with a valid ID should be admitted efficiently, while an aggressive guest with valid credentials may still require assistance if they threaten staff or other patrons.
|
|
||||||
|
|
||||||
### Joke Shop Escalation
|
|
||||||
|
|
||||||
1. **Nonverbal direction:** Raise a hand, point to the ID-check area, or gesture for the guest to wait.
|
|
||||||
2. **Routine explanation:** Explain the ID, ticket, or wand-check requirement.
|
|
||||||
3. **Repeat request:** Clearly state what is missing or what must happen before entry.
|
|
||||||
4. **Refusal of entry:** Keep the guest outside and direct them away from the entrance.
|
|
||||||
5. **Manager or security assistance:** Call for a supervisor or another staff member when a guest argues, refuses screening, or blocks the line.
|
|
||||||
6. **Emergency response:** Call emergency services only for an immediate threat, such as a weapon, assault, or serious attempt to force entry.
|
|
||||||
|
|
||||||
The handheld wand should not automatically identify an item as a weapon. The player must interpret the alert, ask the guest to identify the item, and follow venue procedure. The goal is to create a believable screening challenge while keeping the emphasis on communication and proportionate responses.
|
|
||||||
|
|
||||||
### Scenario Events
|
|
||||||
|
|
||||||
- A busy weekend comedy show creates a long line.
|
|
||||||
- A performer arrives through the guest entrance and must be recognized or verified.
|
|
||||||
- A guest forgets their ID but appears on the reservation list.
|
|
||||||
- A fake or borrowed ID is presented.
|
|
||||||
- A wand alert comes from keys, a belt buckle, or a phone.
|
|
||||||
- A guest refuses the screening but insists on entering.
|
|
||||||
- A disruptive guest attempts to follow another person through the door.
|
|
||||||
- A late-arriving audience member tries to enter during a performance.
|
|
||||||
|
|
||||||
## Hollywood Boulevard Late Night TV Studio
|
|
||||||
|
|
||||||
This scenario takes place outside a late-night television studio on Hollywood Boulevard. The guard manages the front steps, studio entrance, guest arrivals, deliveries, audience members, and the public crowd gathered around the building.
|
|
||||||
|
|
||||||
The studio encounters all of the NPC types described in the game plan:
|
|
||||||
|
|
||||||
- **Tourists** stopping to take photos or watch arrivals
|
|
||||||
- **Street performers** occupying the steps or performing near the entrance
|
|
||||||
- **Influencers and filmmakers** recording the guard, guests, or celebrity arrivals
|
|
||||||
- **Confused visitors** looking for the wrong entrance or trying to attend a show without understanding the access rules
|
|
||||||
- **Intoxicated pedestrians** approaching the entrance or disrupting the crowd
|
|
||||||
- **Trespassers** testing the doors, trying to follow authorized guests, or returning after being directed away
|
|
||||||
- **Violent threats**, which are rare but require careful recognition and emergency escalation
|
|
||||||
|
|
||||||
The studio also has authorized guests and staff. They must either be recognized by the guard or present a valid wristband, credential, guest-list confirmation, or other approved access marker. The player must check these credentials without holding up the audience line or allowing unauthorized people to enter.
|
|
||||||
|
|
||||||
### Studio Responsibilities
|
|
||||||
|
|
||||||
- Keep the front steps and entrance clear.
|
|
||||||
- Direct tourists and performers away from restricted areas.
|
|
||||||
- Check wristbands, credentials, tickets, or guest-list status.
|
|
||||||
- Admit authorized staff, performers, guests, and audience members.
|
|
||||||
- Manage influencers and filmmakers without unnecessarily obstructing lawful filming.
|
|
||||||
- Protect celebrity arrivals and production equipment from crowding.
|
|
||||||
- Coordinate with studio staff, production assistants, and additional security guards.
|
|
||||||
- Watch for attempts to follow authorized people through the entrance.
|
|
||||||
- Call for emergency assistance only when there is an immediate threat.
|
|
||||||
|
|
||||||
### Studio Scenario Events
|
|
||||||
|
|
||||||
- A crowd gathers to watch a celebrity arrive.
|
|
||||||
- Tourists stop on the steps and block the entrance for photographs.
|
|
||||||
- A street performer sets up in front of the audience queue.
|
|
||||||
- An influencer repeatedly ignores the restricted filming boundary.
|
|
||||||
- A confused visitor believes the studio is open to the public.
|
|
||||||
- An intoxicated pedestrian wanders toward the stage door.
|
|
||||||
- A trespasser tries to blend into an audience group.
|
|
||||||
- An authorized guest arrives without their wristband and must be verified another way.
|
|
||||||
- Someone presents a counterfeit wristband or credential.
|
|
||||||
- A violent threat appears during a crowded arrival, requiring the player to protect the entrance while calling for assistance.
|
|
||||||
|
|
||||||
The scenario should test the player’s ability to distinguish ordinary Hollywood Boulevard activity from a genuine security problem. The best solution is usually a simple gesture, clear direction, or credential check rather than immediate escalation.
|
|
||||||
|
|
||||||
Future venues can reuse this structure while changing the rules, equipment, staff roles, and types of access decisions.
|
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
## Engine: Godot 4
|
- **Engine:** Godot 4.7.2, GL Compatibility renderer (Web parity); optional Forward+ Desktop HQ build.
|
||||||
|
- **Language:** GDScript.
|
||||||
|
- **Architecture:** venue-driven simulation; shared systems plus `Resource`-based scenario data.
|
||||||
|
- **Navigation:** `NavigationRegion3D` / `NavigationAgent3D`.
|
||||||
|
- **NPC behavior:** lightweight custom finite state machines driven by `NpcProfile` resources.
|
||||||
|
- **UI/audio:** Godot `Control` nodes, themes, procedural audio, and JSON-backed localized strings.
|
||||||
|
- **Persistence:** JSON in `user://` for settings and shift history.
|
||||||
|
- **Testing:** custom headless runner (`src/test/test_runner.gd`); GUT was evaluated and not adopted. Python tooling tests cover the asset/screenshot pipelines.
|
||||||
|
- **Source control/CI:** git on Gitea; `.gitea/workflows/build.yml` runs headless checks and builds/publishes releases on tags.
|
||||||
|
- **Targets:** Windows, Linux, macOS, and Web. Release artifacts include portables, an NSIS installer, an AppImage, a Web zip, and an optional Desktop HQ zip (Forward+ volumetric lighting; Vulkan required).
|
||||||
|
|
||||||
The game is built with the Godot Engine 4.x editor. Godot was chosen for its free and open-source license, built-in 3D rendering, navigation, animation, audio, and UI systems, and its low weight for fast prototyping and iteration.
|
## Versioning and Delivery
|
||||||
|
|
||||||
## Language: GDScript
|
`config/version` in `project.godot` is the single version source (currently `0.10.0-rc.1`). Releases are tagged `v<version>`; the CI pipeline builds and publishes the standard artifacts plus the optional Desktop HQ zip. Version bumps follow [Semantic Versioning](https://semver.org/): every major, minor and patch increment needs a changelog entry, a local `tools/build_releases.sh` build, and artifact/checksum verification before tagging. Unversioned development tracks do not receive a version until their scope enters a release. See [`docs/RELEASING.md`](docs/RELEASING.md) and [`README.md`](README.md) for commands.
|
||||||
|
|
||||||
Gameplay logic is written in GDScript. It is tightly integrated with the engine, fast to iterate on, and avoids an external build toolchain for the prototype phase.
|
|
||||||
|
|
||||||
## Architecture Mapping
|
|
||||||
|
|
||||||
The venue-driven architecture maps onto Godot as follows:
|
|
||||||
|
|
||||||
- **Nodes and scenes:** Each major system (`ShiftCoordinator`, `NPCController`, `InteractionSystem`, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`, `ShiftReview`) is a Godot node or scene.
|
|
||||||
- **Scenarios as resources:** `VenueScenario` and content such as `NpcProfile`, `Dialogue`, `EventPool`, and `AccessPolicy` are defined as custom Godot `Resource` types so venue content is data-driven and inspectable in the editor.
|
|
||||||
- **Navigation:** `NavigationRegion3D` and `NavigationAgent3D` drive NPC and player movement.
|
|
||||||
- **NPC behavior:** Lightweight custom finite state machines built on the `NPCController` node, with behavior traits coming from `NpcProfile` resources.
|
|
||||||
- **UI:** Godot `Control` nodes and themes for prompts, access inspection, radio reports, dialogue, timers, and the shift review screen.
|
|
||||||
- **Audio:** Godot audio buses and `AudioStreamPlayer` nodes for voices, crowd noise, radio, and environment sound.
|
|
||||||
|
|
||||||
## Content and Data
|
|
||||||
|
|
||||||
- Scenario and entity configuration uses typed Godot `Resource` files (`.tres`), created either in the editor or via scripts.
|
|
||||||
- Localized dialogue and text are stored in JSON files when they need to be editable outside the editor.
|
|
||||||
- Save data and shift results use JSON in Godot's `user://` directory.
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
- Unit and scenario tests use a lightweight custom headless runner
|
|
||||||
(`src/test/test_runner.gd`). GUT was evaluated but requires the editor plugin.
|
|
||||||
- Tests run in headless mode so rules, access verification, escalation, and scoring can be verified without rendering or user input.
|
|
||||||
|
|
||||||
## Version Control and CI
|
|
||||||
|
|
||||||
- Source is tracked with git.
|
|
||||||
- Gitea Actions (`.gitea/workflows/build.yml`) runs headless Godot tests on push/PR and exports
|
|
||||||
Linux/Windows/macOS desktop builds plus the NSIS installer and AppImage on tags.
|
|
||||||
|
|
||||||
## Planned Commands
|
|
||||||
|
|
||||||
The following commands apply once the Godot project is scaffolded:
|
|
||||||
|
|
||||||
- Open the editor: `godot --editor --path .`
|
|
||||||
- Run the game: `godot --path .`
|
|
||||||
- Run tests headlessly: `godot --headless --path . res://src/test/test_runner.tscn`
|
|
||||||
|
|
||||||
## Export Targets
|
|
||||||
|
|
||||||
Desktop is the first target: Windows, Linux, and macOS. A Web export now ships as well
|
|
||||||
(non-threaded, static hosting); it is published with each release as a zip.
|
|
||||||
|
|
||||||
## Player Control, Focus, and Action Menu (next pass)
|
|
||||||
|
|
||||||
The prototype already walks and looks in first person, but the control scheme fights itself and targeting is a hairline. This pass replaces that with three things that belong together: solid locomotion, a visible focus on a person, and a round action menu for what to do with them.
|
|
||||||
|
|
||||||
Decisions: [ADR-0007](docs/adr/0007-locomotion-and-input-layers.md) locomotion and input layers, [ADR-0008](docs/adr/0008-cone-focus-and-head-ring.md) cone focus and focus circle, [ADR-0009](docs/adr/0009-radial-action-menu.md) radial action menu, [ADR-0010](docs/adr/0010-procedural-npc-presentation.md) procedural NPC presentation.
|
|
||||||
|
|
||||||
### What is wrong today
|
|
||||||
|
|
||||||
- `W` is walk-forward in the input map and wand-screening in `GameManager._handle_key`. Walking at someone with a wand venue fires the wand.
|
|
||||||
- `A` / `D` are strafe and also allow/deny during inspection.
|
|
||||||
- `Q` is polite direction and is also bound as `cancel`.
|
|
||||||
- Focus is an 8 m physics ray from the camera. Missing a capsule by a few pixels clears the target. There is no mark in the world that you have someone.
|
|
||||||
- NPC bodies are colored capsules with props. They read as markers, not people.
|
|
||||||
- Actions live on number keys `1`–`6` plus `Q` / `E` / `W` / `R`. That is a cheat-sheet, not a verb you pick while looking at a person.
|
|
||||||
- `Esc` only toggles mouse capture. There is no "I changed my mind" for an action in progress.
|
|
||||||
|
|
||||||
### Movement and look
|
|
||||||
|
|
||||||
Keep first-person WASD + mouse look. Fix the feel and stop stealing those keys.
|
|
||||||
|
|
||||||
- Body yaw from mouse X, head pitch from mouse Y, pitch clamped (already `-80` to `80`).
|
|
||||||
- Move relative to body yaw only (already). Add short acceleration / deceleration so starts and stops are not instant.
|
|
||||||
- Gravity only when `not is_on_floor()`; `floor_snap_length` so the steps work.
|
|
||||||
- Mouse look runs only while `MOUSE_MODE_CAPTURED` and no modal UI is open (radial, inspection, wand, radio, shift review).
|
|
||||||
- Walk speed stays around 3.2 m/s. No sprint, no crouch in this pass.
|
|
||||||
- While the radial is open: freeze look and WASD so `A` / `D` can cycle the menu. Recapture the mouse when the menu closes.
|
|
||||||
- `Esc` with a modal open closes that modal and does nothing else. `Esc` with nothing open keeps the current capture/release toggle.
|
|
||||||
|
|
||||||
Equipment stays on its own keys because it is not about a person: `G` flashlight, `L` door lock, `Tab` cycle nearby people, `V` venue (debug), `F` debug overlay. `R` stays the dedicated emergency radio (hold-to-confirm on the existing panel). Those do not go on the wheel.
|
|
||||||
|
|
||||||
Wand leaves `W`. It is a slice on the wheel (greyed when the venue has no wand) and an optional extra key that is not WASD, suggested `C`.
|
|
||||||
|
|
||||||
### Focus area
|
|
||||||
|
|
||||||
Replace the hairline ray as the primary picker.
|
|
||||||
|
|
||||||
- A **focus cone** in front of the camera: people within about 10 m whose head projects inside a screen-space circle around the crosshair (about 90 px / 10 degrees).
|
|
||||||
- If several heads sit in the circle, pick the one closest to the center, then the nearer one if tied.
|
|
||||||
- The person must be in front (dot with camera forward > 0) and not fully occluded by the building. A short ray to the head is enough for occlusion.
|
|
||||||
- `Tab` still cycles people inside 12 m as the aim-free path. Cycling sets the same focused person the cone would.
|
|
||||||
- Clearing: look away until the head leaves the circle for a few frames (small grace so a bob does not drop them), or they walk out of range.
|
|
||||||
|
|
||||||
### Focus circle
|
|
||||||
|
|
||||||
When a person is in focus, draw a blue, semi-transparent circle in front of their face.
|
|
||||||
|
|
||||||
- Attach to the avatar `Head` node, just in front of the face, billboarded to the camera so it stays a circle on screen.
|
|
||||||
- Slow spin is fine; no ping-pong scale spam.
|
|
||||||
- Idle focus: blue, semi-transparent, modest size. Radial open on that person: brighter, slightly larger.
|
|
||||||
- Optional small name under the circle (`display_name` only, not the archetype cheat `Violent Threat` in the world). The existing HUD target line can keep the archetype for the prototype.
|
|
||||||
- One circle at a time. Everyone else has none.
|
|
||||||
|
|
||||||
Implementation sketch: a thin unshaded `MeshInstance3D` circle (flat disc) in front of the face on the avatar, toggled from `GuardController` / `GameManager` when focus changes. Screen-space fallback (unproject head, draw a `Control` circle) is acceptable if the 3D circle z-fights on hats.
|
|
||||||
|
|
||||||
### Round action HUD
|
|
||||||
|
|
||||||
A doughnut / pie menu centered on the screen, opened on the focused person.
|
|
||||||
|
|
||||||
**Open**
|
|
||||||
|
|
||||||
- Left mouse button, or `Space`, while someone is in focus and no other modal is open.
|
|
||||||
- If nobody is in focus, log the existing "aim first" line and do not open.
|
|
||||||
- The click that opens the menu must not also pick a slice.
|
|
||||||
|
|
||||||
**While open**
|
|
||||||
|
|
||||||
- Mouse becomes visible. Look and walk freeze.
|
|
||||||
- Center of the doughnut shows the focused person's `display_name`.
|
|
||||||
- Slices, clockwise from the top, de-escalation first:
|
|
||||||
1. Raise hand (stop / wait)
|
|
||||||
2. Point away
|
|
||||||
3. Head shake
|
|
||||||
4. Polite direction (old `Q`)
|
|
||||||
5. Inspect access (old `E`)
|
|
||||||
6. Wand (grey if the venue has no wand)
|
|
||||||
7. Formal warning
|
|
||||||
8. Block
|
|
||||||
9. Radio assist
|
|
||||||
- Emergency is **not** on the wheel (`R` only).
|
|
||||||
- Highlight follows the mouse angle when the cursor is over the ring. `Left` / `Right` or `A` / `D` step the highlight. Wrap around.
|
|
||||||
|
|
||||||
**Confirm**
|
|
||||||
|
|
||||||
- Click a slice: that action runs, menu closes, mouse recaptured.
|
|
||||||
- `Space` while a slice is highlighted: same.
|
|
||||||
- Inspect or wand: close the wheel, then open the existing inspect / wand panel. Allow / deny on that panel stay `A` / `D` (walk is frozen only while the wheel is up; once the inspect panel is open, WASD can move again, so allow/deny on `A` / `D` remains a press handler and must ignore repeats / echo, which it already does). Safer: keep walk frozen for inspect/wand too, until the panel closes. Prefer freeze until the whole interaction ends.
|
|
||||||
- Radio assist: close the wheel, open the existing radio panel.
|
|
||||||
|
|
||||||
**Cancel**
|
|
||||||
|
|
||||||
- `Esc` or right mouse button: hide the menu, no action, no recorder event, recapture mouse.
|
|
||||||
- Focus and the focus circle stay as they were.
|
|
||||||
|
|
||||||
Number keys `1`–`6` may remain as shortcuts that skip the wheel so a smoke-test script still works. They must not fire while the wheel is open (the wheel owns `A` / `D` / `Space`). Document both in the README.
|
|
||||||
|
|
||||||
### NPC display
|
|
||||||
|
|
||||||
Stay on the procedural capsule rig (no Mixamo / GLB dependency this pass). Make them read as people at conversation distance.
|
|
||||||
|
|
||||||
- Neck between torso and head; shoes; simple hair cap (color from seed); two dark eye spheres on the face so a head has a front.
|
|
||||||
- Keep archetype props (camera, box, hat, sign, bag). Keep height / brightness variation from the visual seed.
|
|
||||||
- Collision capsule matches the scaled height so the focus ray / cone hits the body you see.
|
|
||||||
- `Head` stays a named node other systems can find (`get_node("Rig/Head")`) for the ring and the cone.
|
|
||||||
- Walk bob and the "look at the guard when noticed" cue stay.
|
|
||||||
- Do not put the internal type name (`Trespasser`, `Violent Threat`) on the body. The player should judge from clothes, props, and behavior. The debug overlay (`F`) can still print the type.
|
|
||||||
|
|
||||||
AI textures in `assets/textures/ai/` stay out of this pass unless a specific map (shirt, hair) is already the right size.
|
|
||||||
|
|
||||||
### Input map (target)
|
|
||||||
|
|
||||||
| Action | Bind |
|
|
||||||
| --- | --- |
|
|
||||||
| Walk | WASD / arrows (arrows **do not** walk while the wheel is open; they step the highlight) |
|
|
||||||
| Look | Mouse, captured |
|
|
||||||
| Open / confirm action menu | LMB, `Space` |
|
|
||||||
| Cancel menu | RMB, `Esc` |
|
|
||||||
| Step highlight | `A` / `D`, left / right arrows |
|
|
||||||
| Emergency radio | `R` |
|
|
||||||
| Flashlight | `G` |
|
|
||||||
| Door lock | `L` |
|
|
||||||
| Cycle nearby people | `Tab` |
|
|
||||||
| Wand shortcut | `C` (not `W`) |
|
|
||||||
| Inspect shortcut | `E` still allowed when the wheel is closed |
|
|
||||||
|
|
||||||
### Files this pass is expected to touch
|
|
||||||
|
|
||||||
- `src/game/guard_controller.gd` — cone focus, no look while modal, accel, floor snap.
|
|
||||||
- `src/game/npc_avatar.gd` — face / hair / neck, head marker, ring toggle.
|
|
||||||
- `src/game/ui/action_radial.gd` — new Control, `_draw` doughnut, slice pick.
|
|
||||||
- `src/game/ui/hud.gd` — host the radial; stop teaching number-key verbs as the only path.
|
|
||||||
- `src/game/game_manager.gd` — input routing, freeze flags, open/confirm/cancel.
|
|
||||||
- `project.godot` — input map.
|
|
||||||
- `src/core/strings.gd` — controls line, aim-first, no new tone-of-voice.
|
|
||||||
- `src/test/tests/test_focus_and_radial.gd` — cone pick, wrap, cancel emits nothing, `W` is not wand.
|
|
||||||
- README controls table and smoke checklist.
|
|
||||||
|
|
||||||
## Rendering, Models, and Selection (v0.3.0 pass)
|
|
||||||
|
|
||||||
Decisions: [ADR-0011](docs/adr/0011-aim-free-selection-and-precision-walk.md) aim-free selection and precision walk, [ADR-0012](docs/adr/0012-surface-materials-and-generated-props.md) surface materials and generated props.
|
|
||||||
|
|
||||||
The blockout look and the aim-first selection model were the two biggest gaps left by the control pass. This pass closes them without changing the game's rules.
|
|
||||||
|
|
||||||
**Look.** Surfaces get real profiles instead of stock materials: brick is rough and finely tiled, concrete is rougher still, asphalt is smoother and damp-looking, glass is smooth and slightly metallic. The environment gains ACES tonemapping, soft glow, aerial fog and a mild grade, all driven by the same per-phase configuration that already moves the sun and sky. The facade gains framed windows that light up after dark, a stone base course, door trim and handles. Street furniture becomes real geometry: bollards, benches, bins, planters, newspaper boxes and a hydrant, built as `.glb` props by `tools/model_pipeline.py` with a procedural fallback. NPCs get per-role clothing palettes, small role accessories (cap, hood, backpack, sunglasses, lanyard), hands, shoes, and an arm/leg walk swing. The rule stays: the player judges people from clothes, props and behaviour, never from an archetype label.
|
|
||||||
|
|
||||||
**Models.** Prop meshes are deterministic primitives, so the build works offline. An image model can supply the per-prop colour palette from a concept sheet; that is the only AI influence on geometry, and it is optional. Colours are converted from sRGB to linear before they reach glTF, because glTF factors are linear.
|
|
||||||
|
|
||||||
**Selection.** The focus cone ranks heads by angle from the crosshair then distance. `Tab` and the mouse wheel cycle nearby people nearest-first and pin the choice, so aim-free play works. Right click clears focus and briefly suppresses re-acquisition. Holding `Shift` walks at half speed for close work at the door. Escape keeps its single meaning (close the top panel, otherwise pause).
|
|
||||||
|
|
||||||
## Design Direction
|
## Design Direction
|
||||||
|
|
||||||
The game should feel like a tense workplace simulation with occasional crisis moments. The most satisfying outcome is not overpowering an NPC; it is recognizing the situation early and resolving it calmly.
|
The game should feel like a tense workplace simulation with occasional crisis moments. The most satisfying outcome is not overpowering an NPC; it is recognizing the situation early and resolving it calmly.
|
||||||
|
|
||||||
A useful guiding rule:
|
|
||||||
|
|
||||||
> **The player wins by choosing the lowest level of escalation that keeps everyone safe.**
|
> **The player wins by choosing the lowest level of escalation that keeps everyone safe.**
|
||||||
|
|
||||||
|
## Future Ideas
|
||||||
|
|
||||||
|
- A rare Bob Dobbs cameo (Easter-egg appearance; placement note in `MEMORY.md`).
|
||||||
|
- Specific unusual NPC behaviors, such as slapping their own face, using hands as scissors, or interacting with props unexpectedly.
|
||||||
|
- Live 3D body-camera replay; additional venues or seasonal events; real voice/dialogue audio; an image-to-mesh prop pipeline.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Privacy Note
|
||||||
|
|
||||||
|
Gig Line runs locally. The game itself does not include telemetry, analytics,
|
||||||
|
accounts, advertising, cloud saves or network requests. It does not access the
|
||||||
|
camera, microphone, contacts or location, and the body-camera feature is a
|
||||||
|
simulated overlay, not a real recording device.
|
||||||
|
|
||||||
|
Player data is written only to Godot's local `user://` directory:
|
||||||
|
|
||||||
|
- `settings.json`
|
||||||
|
- `campaign.json`
|
||||||
|
- `shift_results.json`
|
||||||
|
- `shift_state.json`
|
||||||
|
|
||||||
|
Those files stay on the player's machine unless the player copies them. Deleting
|
||||||
|
the files (or the `Gig Line` user directory) resets the profile. See
|
||||||
|
[`docs/getting-started.md`](docs/getting-started.md#save-data-and-migration) for
|
||||||
|
locations, migration and rollback instructions.
|
||||||
|
|
||||||
|
The repository's development tools can call OpenRouter when a developer supplies
|
||||||
|
`OPENROUTER_API_KEY`; those calls generate or judge development assets and are
|
||||||
|
not part of the shipped game. The key and generated review artifacts remain
|
||||||
|
local/gitignored.
|
||||||
|
|
||||||
|
Questions about a storefront release should be checked against the final package
|
||||||
|
and the provider terms recorded in [`CREDITS.md`](CREDITS.md); external legal and
|
||||||
|
storefront approval is still pending for 0.10.0.
|
||||||
@@ -1,455 +1,214 @@
|
|||||||
# Step Line
|
# Gig Line
|
||||||
|
|
||||||
Step Line is a workplace security simulation about observation, communication, access verification, and proportionate escalation. The player works security at public-facing venues around Hollywood Boulevard, managing stairs, entrances, crowds, guests, staff, and occasional serious threats.
|
Gig Line is a workplace security simulation about observation, communication, access verification, and proportionate escalation. The player works security at public-facing venues around Hollywood Boulevard, managing stairs, entrances, crowds, guests, staff, and occasional serious threats.
|
||||||
|
|
||||||
The central design goal is simple:
|
The central design goal is simple:
|
||||||
|
|
||||||
> The player wins by choosing the lowest level of escalation that keeps everyone safe.
|
> The player wins by choosing the lowest level of escalation that keeps everyone safe.
|
||||||
|
|
||||||
## Screenshots
|
## Development Priorities
|
||||||
|
|
||||||
| | |
|
The active planning order lives in [`TODO.md`](TODO.md) and [`AGENTS.md`](AGENTS.md):
|
||||||
| --- | --- |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  |  |
|
|
||||||
|  | |
|
|
||||||
|
|
||||||
*Screenshots are generated by [`src/test/capture_screenshots.gd`](src/test/capture_screenshots.gd) from the current build — day, late-night, wand screening, escalation, and shift-review timeline.*
|
1. **3D model and local map re-evaluation** — score every shipped asset, then evaluate licensed OpenStreetMap/3D data, reference-derived photoreal facades, AI-assisted composition and a Gaussian-splatting development evaluation.
|
||||||
|
2. **Character development** — crowd, age, build, hair and outfit variety on the owned procedural library, keeping the frozen NPC contract. Source evaluation and the licence decision are in [`docs/npc-model-sourcing.md`](docs/npc-model-sourcing.md).
|
||||||
|
3. **Versioned release hardening** — the 0.10.0 release candidate and later semantic versions follow the unversioned development tracks.
|
||||||
|
|
||||||
|
Versions are assigned only when a track's scope is accepted into a release. Every major, minor and patch version requires a changelog entry, a local release build and artifact verification before tagging.
|
||||||
|
|
||||||
## ⬇️ Downloads
|
## ⬇️ Downloads
|
||||||
|
|
||||||
| Platform | Package |
|
| Platform | Package |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| **Windows — installer** | [`step-line-setup-<ver>.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · contains all game data, shortcuts + uninstaller |
|
| **Windows — installer** | [`gig-line-setup-<ver>.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · contains all game data, shortcuts + uninstaller |
|
||||||
| **Windows — portable** | [`step-line-windows-<ver>.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, no install needed |
|
| **Windows — portable** | [`gig-line-windows-<ver>.exe`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, no install needed |
|
||||||
| **Linux — AppImage** | [`step-line-<ver>-x86_64.AppImage`](https://git.allucanget.biz/zwitschi/security-game/releases) · single file, `chmod +x` and run |
|
| **Linux — AppImage** | [`gig-line-<ver>-x86_64.AppImage`](https://git.allucanget.biz/zwitschi/security-game/releases) · single file, `chmod +x` and run |
|
||||||
| **Linux — portable** | [`step-line-linux-<ver>.x86_64`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, `chmod +x` and run |
|
| **Linux — portable** | [`gig-line-linux-<ver>.x86_64`](https://git.allucanget.biz/zwitschi/security-game/releases) · 64-bit, `chmod +x` and run |
|
||||||
| **macOS** | [`step-line-macos-<ver>.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unsigned build, quarantine note below |
|
| **macOS** | [`gig-line-macos-<ver>.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unsigned build, quarantine note below |
|
||||||
| **Web (browser)** | [`step-line-web-<ver>.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unzip and serve the folder from any static host (no special headers) |
|
| **Web (browser)** | [`gig-line-web-<ver>.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · unzip and serve the folder from any static host (no special headers) |
|
||||||
|
| **Desktop HQ** | [`gig-line-desktop-hq-<ver>.zip`](https://git.allucanget.biz/zwitschi/security-game/releases) · optional Forward+ build with volumetric fog/SDFGI/flashlight; requires a Vulkan-capable GPU/driver |
|
||||||
|
|
||||||
**All releases:** <https://git.allucanget.biz/zwitschi/security-game/releases>
|
**All releases:** <https://git.allucanget.biz/zwitschi/security-game/releases>
|
||||||
|
|
||||||
> Downloads are public (no sign-in required) since the host's instance-wide
|
> Downloads are public (no sign-in required) since the host's instance-wide
|
||||||
> `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31.
|
> `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31.
|
||||||
>
|
>
|
||||||
> **Latest: v0.4.0** — props have colliders and navigation obstacles, with zoned placements and deterministic variants.
|
> **Latest published: v0.9.6** — the integration checkpoint: the 0.9.5 texture
|
||||||
|
> and material refinement (256² NPC maps, a four-times fabric weave without baked
|
||||||
|
> AO blotches, repaired Walk of Fame/awning surfaces and per-phase skyline haze)
|
||||||
|
> is packaged with the v0.9.4 NPC visual optimization, the Hollywood/Joke Shop
|
||||||
|
> maps and the v0.9.3 UI/accessibility polish, with the full gallery, smoke matrix
|
||||||
|
> and Web/Desktop HQ build re-verified.
|
||||||
|
>
|
||||||
|
> **Current source: v0.10.0-rc.1** — the release-candidate source; the final tag is pending external playtest and legal review.
|
||||||
> Releases are built automatically when a `vX.Y.Z` tag is pushed
|
> Releases are built automatically when a `vX.Y.Z` tag is pushed
|
||||||
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
|
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
|
||||||
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
|
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
|
||||||
> (NSIS + appimagetool) and uploads with the portable binaries.
|
> (NSIS + appimagetool) and uploads with the portable binaries.
|
||||||
>
|
>
|
||||||
> Note: on this host, downloading release files currently requires sign-in;
|
|
||||||
> the page and tag URLs are public.
|
|
||||||
>
|
|
||||||
> macOS builds are unsigned: right-click → Open, or clear the quarantine with
|
> macOS builds are unsigned: right-click → Open, or clear the quarantine with
|
||||||
> `xattr -dr com.apple.quarantine step-line-macos.zip`. Build instructions and
|
> `xattr -dr com.apple.quarantine gig-line-macos.zip`. Build instructions and
|
||||||
> the release pipeline live in [`docs/RELEASING.md`](docs/RELEASING.md).
|
> the release pipeline live in [`docs/RELEASING.md`](docs/RELEASING.md).
|
||||||
|
|
||||||
If you are here to develop or test locally instead of downloading, jump to
|
If you are here to develop or test locally instead of downloading, jump to
|
||||||
[Run and Test](#run-and-test) below.
|
[Setup and Run](#setup-and-run) below.
|
||||||
|
|
||||||
The project is under active development. The design and architecture are documented, and a playable prototype vertical slice exists (Godot 4.3 + GDScript): a procedural Hollywood Boulevard entrance, a first-person guard, simulated NPCs with intents and reactions, the six-level escalation model, access inspection and admission decisions, radio calls with credibility checks, reputation, crowd mood, and an end-of-shift review.
|
The project is under active development. The design and architecture are documented, and a playable prototype vertical slice exists (Godot 4 + GDScript): a stylized El Capitan Entertainment Centre block, a first-person guard, simulated NPCs with intents and reactions, the six-level escalation model, access inspection and admission decisions, radio calls with credibility checks, reputation, crowd mood, and an end-of-shift review.
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
Every capture is rendered from the current build by [`tools/check_screenshots.sh`](tools/check_screenshots.sh)
|
||||||
|
(21 captures, compared against the runtime art). The full gallery, including every UI state, is in
|
||||||
|
[`docs/screenshots/`](docs/screenshots/README.md).
|
||||||
|
|
||||||
|
### Venues
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
|  | **Hollywood Boulevard — day.** The El Capitan Entertainment Centre block: six Ionic columns, Masonic parapet, event banners, eight stone steps and the Walk of Fame terrazzo. |
|
||||||
|
|  | **Hollywood Boulevard — late.** Sconces pool on the limestone, street lamps light the boulevard and the skyline fades into night haze. |
|
||||||
|
|  | **Night patrol.** A tight flashlight spot picks out the door and the person in front of it; movement and awareness change at night. |
|
||||||
|
|  | **The Joke Shop.** A black Sunset Strip club under its signature-covered canopy, circular blade sign, red curtains and the dry hillside behind. |
|
||||||
|
|  | **TV Studio arrival.** Stone facade, bulb canopy, black car and crowd barriers for a late celebrity arrival. |
|
||||||
|
|
||||||
|
### Interaction and systems
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
|  | **Focus and targeting.** The cone focus ring, name tag and target chip; `Tab`/wheel cycles nearby people without aiming. |
|
||||||
|
|  | **Access inspection.** A venue-correct wristband check with evidence shown before the admit/refuse decision. |
|
||||||
|
|  | **Radio report.** Security assistance with location, behaviour and what was actually seen. |
|
||||||
|
|  | **Wand screening.** A false alarm on keys: ask the guest to identify the item before choosing a response. |
|
||||||
|
|  | **Escalation.** Level 3 blocking with the shape meter, body camera armed and the reaction log. |
|
||||||
|
|  | **Body-camera timeline.** Every recorded event, step by step, in the shift review. |
|
||||||
|
|  | **Precision walk.** Half-speed close work on the steps, with the HUD state chip visible. |
|
||||||
|
|
||||||
|
### Shell and UI
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
|  | **Venue menu.** Day and night rows for Hollywood, the Joke Shop and the TV Studio, campaign continue and review links. |
|
||||||
|
|  | **Settings.** Difficulty, volumes, HUD scale, reduce motion, captions and key/button remapping, all persisted. |
|
||||||
|
|  | **Shift review.** Grade, policy outcome, incidents, responder results and campaign Continue. |
|
||||||
|
|  | **Action wheel.** De-escalation first: stop, look, point, no, ask, inspect, warn, block, reason, recognize, radio. |
|
||||||
|
|
||||||
|
### 3D asset progress
|
||||||
|
|
||||||
|
The character, gear and prop libraries are authored procedurally in Blender and audited before use;
|
||||||
|
the sheets below track their visual progress.
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
|  | **Character library.** Eight skinned variants (4.5–5.5k triangles each) with five tint slots, attach-point gear and baked maps. |
|
||||||
|
|  | **Street and venue props.** Twenty library props with colliders, navigation avoidance and colour atlases. |
|
||||||
|
|  | **Neutral baseline.** 8 characters × 6 views (front, three-quarter, profile, back, full body, crowd) used for the 0.9.4 pass and the current re-evaluation. |
|
||||||
|
|  | **Texture refinement.** 128 → 256 px NPC maps and a 4× fabric weave, shown before/after on the same camera. |
|
||||||
|
|  | **Crowd variety prototypes.** Four approved looks (bun/slim, bald/broad, coat/umbrella, hood/cup) queued for the roster build. |
|
||||||
|
|
||||||
|
More sheets and the review criteria are in [`docs/blender_previews/`](docs/blender_previews/README.md)
|
||||||
|
and [`docs/3d-model-review.md`](docs/3d-model-review.md).
|
||||||
|
|
||||||
## Game Overview
|
## Game Overview
|
||||||
|
|
||||||
A typical shift lasts 10–15 minutes. The player:
|
A typical shift lasts 10–15 minutes: watch the entrance and sidewalk, judge why
|
||||||
|
each person is approaching, verify access with recognition, wristbands, IDs,
|
||||||
1. Watches the entrance, steps, queue, and surrounding sidewalk.
|
tickets or credentials, and escalate only when necessary. The game rewards calm
|
||||||
2. Determines why each person is approaching.
|
resolution over force. The venue menu splits Hollywood Boulevard into Day and
|
||||||
3. Uses simple gestures or polite directions first.
|
Night rows; the full mechanics — the three venues, the six-level
|
||||||
4. Verifies authorized access using recognition, wristbands, credentials, tickets, or guest lists.
|
escalation model, and the core systems — are in
|
||||||
5. Escalates only when a person refuses directions, attempts unauthorized entry, or presents a genuine threat.
|
[`docs/gameplay.md`](docs/gameplay.md); the design rationale lives in
|
||||||
6. Records the incident through body-camera and radio systems.
|
[`PLAN.md`](PLAN.md).
|
||||||
7. Finishes the shift with a performance review based on safety, judgment, and professionalism.
|
|
||||||
|
|
||||||
The game rewards calm resolutions rather than aggressive behavior. Emergency assistance and armed responders are rare last-resort systems with meaningful consequences.
|
|
||||||
|
|
||||||
## Planned Venues
|
|
||||||
|
|
||||||
### Hollywood Boulevard Building
|
|
||||||
|
|
||||||
The original scenario places the guard at the top of the steps outside a fictional building. Tourists, street performers, influencers, confused visitors, intoxicated pedestrians, trespassers, people experiencing a mental-health crisis, and rare violent threats may approach the entrance.
|
|
||||||
|
|
||||||
Authorized people must either be recognized by the guard or present a valid wristband. Wristbands can have different colors, patterns, dates, or event markings. They may be valid, expired, counterfeit, or missing.
|
|
||||||
|
|
||||||
### The Joke Shop
|
|
||||||
|
|
||||||
The Joke Shop is a comedy-club scenario. The door person checks IDs, tickets, reservations, and guest-list status, then uses a handheld security wand according to venue policy. The player must interpret wand alerts rather than treating every alert as proof of a weapon; keys, belts, phones, and other harmless objects can trigger false alarms.
|
|
||||||
|
|
||||||
### Hollywood Boulevard Late Night TV Studio
|
|
||||||
|
|
||||||
This scenario combines the previously defined NPC types with a busy television-studio entrance. The player manages audience members, production staff, performers, guests, deliveries, celebrity arrivals, tourists, street performers, influencers, confused visitors, intoxicated pedestrians, trespassers, people experiencing a mental-health crisis, and rare violent threats.
|
|
||||||
|
|
||||||
The studio uses wristbands, credentials, tickets, guest-list confirmation, and personal recognition to authorize entry. Crowd control, filming boundaries, following authorized guests through the door, and counterfeit credentials are key events.
|
|
||||||
|
|
||||||
## Escalation Model
|
|
||||||
|
|
||||||
The common escalation model has six levels:
|
|
||||||
|
|
||||||
0. **Observation** — Decide whether intervention is necessary.
|
|
||||||
1. **Simple deterrence and polite direction** — Raise an open hand, make eye contact, point toward the sidewalk or check-in area, shake the head, or give a short instruction.
|
|
||||||
2. **Formal warning** — Clearly explain the rule and request compliance.
|
|
||||||
3. **Blocking and physical positioning** — Secure the entrance, maintain distance, preserve an escape route, and deny access without starting a fight.
|
|
||||||
4. **Radio for security assistance** — Request a supervisor, second guard, building management, or other appropriate support.
|
|
||||||
5. **Emergency response** — Hold the emergency-radio button and confirm only when there is an immediate threat, such as a weapon, assault, forced entry, or serious threat to occupants.
|
|
||||||
|
|
||||||
An off-duty law-enforcement officer or armed emergency responder may arrive during a valid emergency. This character is not intended to be a routine stronger attack. Their role is crisis control, evacuation, restraint, arrest, or other appropriate intervention, with force governed by the situation and meaningful consequences for unjustified calls.
|
|
||||||
|
|
||||||
## Core Systems
|
|
||||||
|
|
||||||
- **NPC behavior:** NPCs have different motivations, temperaments, compliance levels, and reactions to the player’s approach.
|
|
||||||
- **Access verification:** Recognition, wristbands, IDs, tickets, credentials, and guest lists determine who may enter.
|
|
||||||
- **Nonverbal deterrence:** Small actions such as raising a hand can resolve low-risk situations without dialogue or force.
|
|
||||||
- **Dialogue:** Polite directions and warnings affect outcomes; simple gestures can resolve low-risk situations without dialogue.
|
|
||||||
- **Escalation:** The player chooses the lowest effective response.
|
|
||||||
- **Radio assistance:** Calls require useful information and may take time to arrive.
|
|
||||||
- **Evidence:** Body-camera records support shift review and accountability.
|
|
||||||
- **Crowd reaction:** Aggressive behavior, visible weapons, and emergency response can create panic or attract a crowd.
|
|
||||||
- **Reputation:** Peaceful resolutions, accurate reports, low property damage, and justified calls improve performance; unnecessary confrontations and false reports reduce it.
|
|
||||||
- **Shift review:** The player receives feedback on access decisions, escalation choices, public complaints, injuries, and security outcomes.
|
|
||||||
|
|
||||||
## Architecture Overview
|
|
||||||
|
|
||||||
The planned solution uses a venue-driven simulation architecture. Venue configuration supplies rules, access types, NPC pools, equipment, events, and escalation policies. Shared simulation systems provide movement, perception, interactions, dialogue, access verification, escalation, radio calls, crowds, reputation, evidence, and shift review.
|
|
||||||
|
|
||||||
```text
|
|
||||||
Venue Scenario / Rules
|
|
||||||
|
|
|
||||||
v
|
|
||||||
Shift Coordinator -----> Event Scheduler
|
|
||||||
| |
|
|
||||||
v v
|
|
||||||
NPC Simulation <---------- Interaction System
|
|
||||||
| |
|
|
||||||
v v
|
|
||||||
Access Verification ----> Escalation / Response
|
|
||||||
| |
|
|
||||||
+-------------> Reputation / Evidence
|
|
||||||
|
|
|
||||||
v
|
|
||||||
Shift Review
|
|
||||||
```
|
|
||||||
|
|
||||||
The architecture maps onto Godot 4 nodes, scenes, and custom resources as described in [Tech Stack](#tech-stack). See the full arc42-style outline in [`docs/architecture.md`](docs/architecture.md) and the decisions in [`docs/adr/`](docs/adr/).
|
|
||||||
|
|
||||||
## Tech Stack
|
|
||||||
|
|
||||||
- **Engine:** Godot Engine 4.x
|
|
||||||
- **Language:** GDScript
|
|
||||||
- **Architecture:** Venue-driven simulation using nodes, scenes, and custom `Resource`-based scenario data
|
|
||||||
- **Navigation:** `NavigationRegion3D` and `NavigationAgent3D`
|
|
||||||
- **NPC behavior:** Custom lightweight finite state machines driven by `NpcProfile` resources
|
|
||||||
- **UI and audio:** Godot `Control` nodes, themes, audio buses, and `AudioStreamPlayer` nodes
|
|
||||||
- **Art assets:** authored PNGs and the in-game generated set in `assets/textures/ai/game_ready/`; street-furniture models in `assets/models/generated/` build from `tools/model_pipeline.py`; structural geometry stays procedural (`WorldBuilder`). The legacy `tools/generate_textures.py --ai` batch remains available.
|
|
||||||
- **Asset tooling:** `tools/asset_pipeline.py` generates role-aware assets through OpenRouter and provides crop, resize, seamless-tile, and image-audit commands, including gameplay screenshot checks. `tools/model_pipeline.py` builds and audits the prop `.glb` models.
|
|
||||||
- **Testing:** GUT (Godot Unit Test) in headless mode
|
|
||||||
- **Source control:** git on a Gitea host with Gitea Actions (`.gitea/workflows/build.yml`) for headless tests and desktop release builds
|
|
||||||
- **Targets:** Windows, Linux, and macOS desktop first; web later
|
|
||||||
|
|
||||||
### Image Asset Tools
|
|
||||||
|
|
||||||
Install the Pillow dependency used by the existing texture generator and the asset tools:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 -m pip install -r requirements-tools.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
Generate a role-sized image (generation requires `OPENROUTER_API_KEY`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 tools/asset_pipeline.py generate --prompt "weathered red brick" --role tile --output assets/textures/brick_extra.png
|
|
||||||
```
|
|
||||||
|
|
||||||
Generated images are resized to the selected role profile. Non-square requests use
|
|
||||||
the validated `openai/gpt-5-image-mini` source ratio and crop to the final role dimensions.
|
|
||||||
Transparent glyphs can be recolored while preserving their alpha mask with
|
|
||||||
`--foreground '#ffffff'`.
|
|
||||||
|
|
||||||
Crop uses the image center by default; `--x` and `--y` select an explicit crop origin. Resize defaults to aspect-preserving `contain`; use `--fit cover` to crop or `--fit stretch` to distort to the exact dimensions.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 tools/asset_pipeline.py crop assets/textures/source.png assets/textures/crop.png --width 256 --height 128
|
|
||||||
python3 tools/asset_pipeline.py resize assets/textures/source.png assets/images/backgrounds/scene.png --width 1920 --height 1080 --fit cover
|
|
||||||
python3 tools/asset_pipeline.py seamless assets/textures/rough_tile.png assets/textures/tile.png
|
|
||||||
```
|
|
||||||
|
|
||||||
Audit accepts image files or directories. `--role auto` infers common roles from paths and filenames; explicit roles include `tile`, `texture`, `icon`, `card`, `banner`, `backdrop`, `background`, `overlay`, `app_icon`, and `screenshot`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 tools/asset_pipeline.py audit assets/textures
|
|
||||||
python3 tools/asset_pipeline.py audit assets/images/backgrounds --role background --json
|
|
||||||
python3 -m unittest discover -s tools -p 'test_*.py'
|
|
||||||
```
|
|
||||||
|
|
||||||
The audit classifies measurable fit issues as `good` or `bad` based on role-specific dimensions, aspect ratio, alpha requirements, and tile-edge continuity. It lists perspective/projection as a visual review item because image semantics cannot be judged reliably by these deterministic checks.
|
|
||||||
|
|
||||||
### Prop models
|
|
||||||
|
|
||||||
Street furniture (bollards, benches, bins, planters, newspaper boxes, a hydrant)
|
|
||||||
is real 3D geometry generated by `tools/model_pipeline.py` and written to
|
|
||||||
`assets/models/generated/*.glb`. Godot imports each file as a `PackedScene`, and
|
|
||||||
`WorldBuilder` instantiates it with a procedural fallback when the model or its
|
|
||||||
import is missing, so the world still builds on a fresh checkout.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Print the catalogue (parts, triangle count, whether a model/concept exists)
|
|
||||||
python3 tools/model_pipeline.py list
|
|
||||||
|
|
||||||
# Rebuild the models (deterministic, offline) and validate them
|
|
||||||
python3 tools/model_pipeline.py build
|
|
||||||
python3 tools/model_pipeline.py audit
|
|
||||||
|
|
||||||
# Optional: an image model supplies the colour palette per prop
|
|
||||||
# (requires OPENROUTER_API_KEY; the repo .env is read automatically)
|
|
||||||
python3 tools/model_pipeline.py concept
|
|
||||||
python3 tools/model_pipeline.py concept --only bench --force
|
|
||||||
```
|
|
||||||
|
|
||||||
Each prop is a small list of boxes and cylinders, so `build` needs no network and
|
|
||||||
the output is byte-identical between runs. `concept` asks an image model for a
|
|
||||||
studio-style reference; `dominant_colors` and `assign_roles` then map that image's
|
|
||||||
colours onto material roles (darkest → metal, warm saturated → wood, light grey →
|
|
||||||
stone, green → foliage). glTF stores colours in linear space, so the tool converts
|
|
||||||
the sRGB palettes before writing them. Re-run `build` and `godot --headless
|
|
||||||
--path . --import` after changing a prop. See
|
|
||||||
[ADR-0012](docs/adr/0012-surface-materials-and-generated-props.md).
|
|
||||||
|
|
||||||
Surface materials share one definition in `src/game/materials.gd`: named
|
|
||||||
roughness/metallic profiles per surface, plus the environment tune-up (ACES
|
|
||||||
tonemapping, glow, fog, colour grading) that the phase lighting applies.
|
|
||||||
|
|
||||||
### Screen design references
|
|
||||||
|
|
||||||
Screen mockups and screenshot comparison use `tools/screen_designs.py`. It turns the
|
|
||||||
screen descriptions in `docs/` (README captions, ADRs 0007–0010, `PLAN.md`, and
|
|
||||||
`architecture.md`) into reference images under `docs/screens/designs/`, then uses
|
|
||||||
those images as the expected state to check the game's captures.
|
|
||||||
|
|
||||||
Each screen is tagged `scene` (a realistic first-person 3D world view) or `ui`
|
|
||||||
(an in-game panel overlaid on a softened scene), and the generator appends a
|
|
||||||
shared art-direction suffix so the frames read as one coherent game. Requests use
|
|
||||||
a 3:2 landscape source and crop to the 16:9 capture dimensions.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Export the built-in scenarios to editor-authorable .tres resources
|
|
||||||
godot --headless --path . --script res://tools/export_venues.gd
|
|
||||||
|
|
||||||
# List the catalogue (name, kind, whether a reference design exists)
|
|
||||||
python3 tools/screen_designs.py list
|
|
||||||
|
|
||||||
# Deterministic wireframes (offline, no API key); existing designs are kept
|
|
||||||
# unless --force is passed, so an offline run never replaces an AI mockup
|
|
||||||
python3 tools/screen_designs.py generate --draft
|
|
||||||
python3 tools/screen_designs.py generate --draft --only wand --force
|
|
||||||
|
|
||||||
# Realistic 3D scenes and in-game screen designs via OpenRouter
|
|
||||||
# (requires OPENROUTER_API_KEY)
|
|
||||||
python3 tools/screen_designs.py generate
|
|
||||||
|
|
||||||
# Compare docs/screenshots/*.png against the designs
|
|
||||||
python3 tools/screen_designs.py compare
|
|
||||||
python3 tools/screen_designs.py compare --json
|
|
||||||
|
|
||||||
# Ask a vision model whether each capture realises its design's screen and layout
|
|
||||||
# (requires OPENROUTER_API_KEY; the repo .env is read automatically)
|
|
||||||
python3 tools/screen_designs.py judge
|
|
||||||
python3 tools/screen_designs.py judge --model openai/gpt-4o-mini --json
|
|
||||||
python3 tools/screen_designs.py judge --only 03- # one capture
|
|
||||||
python3 tools/screen_designs.py judge --dry-run # build requests, no network
|
|
||||||
```
|
|
||||||
|
|
||||||
The catalogue covers 19 screens: the nine README captures plus the start menu,
|
|
||||||
pause overlay, settings, shift review, radial menu, emergency radio, wand
|
|
||||||
screening, Joke Shop queue, TV Studio arrival, crowd panic, responder arrival,
|
|
||||||
body-camera timeline, and the escalation HUD. All 19 have an AI reference in
|
|
||||||
`docs/screens/designs/`; the nine screens with staged captures are also compared
|
|
||||||
or judged against the running game.
|
|
||||||
|
|
||||||
`compare` flags gross drift: wrong aspect ratio, undersized frame, near-blank
|
|
||||||
frame, and large layout/palette/detail distance. It is a regression gate, not a
|
|
||||||
semantic judge — pixel and perceptual metrics cannot decide whether a frame's
|
|
||||||
composition is *right*.
|
|
||||||
|
|
||||||
`judge` covers that gap: it sends each capture together with its concept mockup to
|
|
||||||
a vision model and asks whether the capture realises the same screen and layout
|
|
||||||
intent. Art style, fidelity, fonts, and placeholder text are explicitly ignored.
|
|
||||||
Verdicts are classed as `match`, `review` (a mismatch below `--fail-confidence`,
|
|
||||||
default 0.6 — expected when a concept mockup is more stylised than the game), or
|
|
||||||
`diff` (a confident mismatch, which exits non-zero). Regenerate the designs after changing screen layout, then re-run
|
|
||||||
`compare` after regenerating the game screenshots with
|
|
||||||
`src/test/capture_screenshots.gd`.
|
|
||||||
|
|
||||||
### Setup
|
|
||||||
|
|
||||||
1. Download the Godot 4.3 editor (official build) and put `godot` on your PATH.
|
|
||||||
2. Open or run the project from this directory.
|
|
||||||
|
|
||||||
### Run and Test
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Open in the editor
|
|
||||||
godot --editor --path .
|
|
||||||
|
|
||||||
# Run the game
|
|
||||||
godot --path .
|
|
||||||
|
|
||||||
# Headless smoke boot (120 frames then exit)
|
|
||||||
godot --headless --path . --quit-after 120
|
|
||||||
|
|
||||||
# Run all unit tests headlessly (exit code 0 = pass)
|
|
||||||
godot --headless --path . --script res://src/test/test_runner.gd
|
|
||||||
|
|
||||||
# Save a screenshot to user:// (windowed)
|
|
||||||
godot --path . --script res://src/test/take_screenshot.gd
|
|
||||||
|
|
||||||
# Windowed control smoke: focus circle, action wheel, wheel cycling + right-click clear; saves PNGs, exits 1 on failure
|
|
||||||
godot --path . --script res://src/test/smoke_focus_radial.gd
|
|
||||||
|
|
||||||
# Session-shell smoke: menu → shift → pause → settings → menu (headless or windowed)
|
|
||||||
godot --headless --path . --script res://src/test/smoke_session_shell.gd
|
|
||||||
|
|
||||||
# PLAN-verb smoke: recognize, request reason, body camera, warning, blocking
|
|
||||||
godot --headless --path . --script res://src/test/smoke_plan_verbs.gd
|
|
||||||
|
|
||||||
# Prop-collision smoke (windowed): guard stops at a bench, guest routes around it
|
|
||||||
godot --path . --script res://src/test/smoke_props.gd
|
|
||||||
|
|
||||||
# Crowd smoke: rubbernecking on aggression, panic clearing the entrance
|
|
||||||
godot --headless --path . --script res://src/test/smoke_crowd.gd
|
|
||||||
|
|
||||||
# Emergency smoke: unjustified call, investigation note, evacuation aftermath
|
|
||||||
godot --headless --path . --script res://src/test/smoke_emergency.gd
|
|
||||||
|
|
||||||
# Body-camera timeline smoke: review → timeline → step through → back to review
|
|
||||||
godot --headless --path . --script res://src/test/smoke_timeline.gd
|
|
||||||
|
|
||||||
# Audio smoke: crowd bed follows mood, radio open/close crackle
|
|
||||||
godot --headless --path . --script res://src/test/smoke_audio.gd
|
|
||||||
|
|
||||||
# Scenario-content smoke: wand refusal, celebrity crowd spike
|
|
||||||
godot --headless --path . --script res://src/test/smoke_scenario_content.gd
|
|
||||||
```
|
|
||||||
|
|
||||||
The unit test runner is the project's own lightweight harness (no editor plugin required) so CI can run it without the editor.
|
|
||||||
|
|
||||||
### Controls
|
|
||||||
|
|
||||||
| Input | Action |
|
|
||||||
| --- | --- |
|
|
||||||
| W/A/S/D + mouse | Move and look |
|
|
||||||
| Space / left click | Action wheel for the focused person (Stop, Look, Point, No, Ask, Inspect, Wand, Warn, Block, Reason, Recognize, Radio) |
|
|
||||||
| A / D or ←/→ | Step through the action wheel (while it is open) |
|
|
||||||
| Space / left click | Confirm the highlighted action (while the wheel is open) |
|
|
||||||
| Esc / right click | Close the top panel; Esc with nothing open pauses the shift (Esc again resumes) |
|
|
||||||
| ← / → or ↑ / ↓ | Navigate the venue menu and the pause / settings panels |
|
|
||||||
| Enter | Start the highlighted venue from the menu, or confirm in a panel |
|
|
||||||
| 1 / 2 / 3 | Quick gestures without the wheel: raise hand, point, head shake (Level 1) |
|
|
||||||
| Q | Polite direction (Level 1) |
|
|
||||||
| 4 | Formal warning (Level 2) |
|
|
||||||
| 5 | Blocking and positioning (Level 3) |
|
|
||||||
| 6 | Security assistance radio (Level 4) |
|
|
||||||
| R | Emergency radio, hold on-screen button ~1.6s (Level 5) |
|
|
||||||
| E | Inspect access artifacts / close inspection |
|
|
||||||
| C | Handheld wand screening (wand venues only) |
|
|
||||||
| A / D | Allow / deny entry during inspection or wand check |
|
|
||||||
| V | Cycle venue (debug shortcut; restart via V restarts the current shift) |
|
|
||||||
| Tab / mouse wheel ↓ / ↑ | Cycle the target among nearby NPCs, nearest first (aim-free; the target stays selected) |
|
|
||||||
| Right click (nothing open) | Clear the focused person and return to aim-based focus |
|
|
||||||
| Shift (hold) | Precision walk at roughly half speed for close work at the door |
|
|
||||||
| G | Toggle flashlight (widens NPC perception at night) |
|
|
||||||
| B | Toggle the body camera; a formal warning switches it on automatically |
|
|
||||||
| L | Lock / unlock the restricted door (thwarts forced entry) |
|
|
||||||
| F | Toggle debug overlay |
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
The full arc42-style architecture documentation lives in [`docs/architecture.md`](docs/architecture.md). It covers goals, constraints, context, solution strategy, building blocks, runtime views, deployment, cross-cutting concepts, quality requirements, risks, a glossary, the CI/CD build pipeline, semantic versioning, and release management.
|
A venue-driven simulation: shared systems (movement, perception, interaction,
|
||||||
|
access verification, escalation, radio, crowd, reputation, evidence, shift
|
||||||
|
review) driven by per-venue scenario data, on Godot 4 + GDScript. The full
|
||||||
|
arc42-style document is [`docs/architecture.md`](docs/architecture.md);
|
||||||
|
decisions are recorded as ADRs in [`docs/adr/`](docs/adr/).
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
- **Engine:** Godot 4.7.2 · **Language:** GDScript
|
||||||
|
- **NPCs:** Blender-authored skinned library (`assets/models/blender/`) with procedural fallbacks; lightweight FSMs on `NpcProfile`
|
||||||
|
- **World:** procedural `WorldBuilder` + generated prop models
|
||||||
|
- **Art:** v2 skyline/surface textures in `assets/textures/ai/game_ready/v2/picks/`; UI/scene art in `assets/images/`
|
||||||
|
- **Asset tooling:** `asset_pipeline.py`, `model_pipeline.py`, `npc_pipeline.py`, `blender_assets.py`, `scene_concepts.py`, `screen_designs.py`, `check_screenshots.sh`
|
||||||
|
- **Testing:** custom headless runner (`src/test/test_runner.gd`)
|
||||||
|
- **Targets:** Windows, Linux, macOS, Web
|
||||||
|
|
||||||
|
The asset pipelines, model tools, and screenshot gates are documented in
|
||||||
|
[`docs/asset-tooling.md`](docs/asset-tooling.md).
|
||||||
|
|
||||||
|
## Setup and Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m pip install -r requirements-tools.txt
|
||||||
|
|
||||||
|
godot --editor --path . # open in the editor
|
||||||
|
godot --path . # run the game
|
||||||
|
godot --headless --path . --script res://src/test/test_runner.gd # run all tests
|
||||||
|
```
|
||||||
|
|
||||||
|
Full setup, every run/test/smoke command, and the controls table are in
|
||||||
|
[`docs/getting-started.md`](docs/getting-started.md).
|
||||||
|
|
||||||
## Project Documentation
|
## Project Documentation
|
||||||
|
|
||||||
- [`PLAN.md`](PLAN.md) — Game design plan and scenario details.
|
- [`PLAN.md`](PLAN.md) — Game design plan, development priorities and scenario details.
|
||||||
- [`AGENTS.md`](AGENTS.md) — Development and collaboration workflow.
|
- [`TODO.md`](TODO.md) — Priority-ordered tasks: character and local-map development, release milestones, and future work.
|
||||||
- [`TODO.md`](TODO.md) — Active checkbox task list.
|
- [`docs/README.md`](docs/README.md) — Documentation index, current priorities and visual previews.
|
||||||
|
- [`docs/npc-model-sourcing.md`](docs/npc-model-sourcing.md) — Character-source evaluation, licence rules and the character-development plan.
|
||||||
|
- [`AGENTS.md`](AGENTS.md) — Development, commit, build and collaboration workflow.
|
||||||
- [`DONE.md`](DONE.md) — Completed task sections.
|
- [`DONE.md`](DONE.md) — Completed task sections.
|
||||||
- [`MEMORY.md`](MEMORY.md) — Persistent session notes.
|
- [`MEMORY.md`](MEMORY.md) — Persistent session notes.
|
||||||
|
- [`CHANGELOG.md`](CHANGELOG.md) — User-facing changes by version.
|
||||||
- [`docs/architecture.md`](docs/architecture.md) — arc42-style architecture documentation.
|
- [`docs/architecture.md`](docs/architecture.md) — arc42-style architecture documentation.
|
||||||
|
- [`docs/gameplay.md`](docs/gameplay.md) — Gameplay systems, venues, and the escalation model.
|
||||||
|
- [`docs/hollywood-boulevard-map.md`](docs/hollywood-boulevard-map.md) — Real-site references, game mapping and limitations for the Hollywood venue.
|
||||||
|
- [`docs/joke-shop-map.md`](docs/joke-shop-map.md) — Real-site references, game mapping and limitations for the Joke Shop / Sunset Strip venue.
|
||||||
|
- [`docs/tutorial.md`](docs/tutorial.md) — First-shift tutorial beats and rules.
|
||||||
|
- [`docs/asset-tooling.md`](docs/asset-tooling.md) — Asset pipelines, model tools, and screenshot gates.
|
||||||
|
- [`tools/README.md`](tools/README.md) — Tool inventory, categories, test commands and AI-skill evaluation.
|
||||||
|
- [`.agents/skills/gig-line-blender-asset-contract`](.agents/skills/gig-line-blender-asset-contract/SKILL.md) — tracked AI skill for Blender/NPC/gear asset work.
|
||||||
|
- [`.agents/skills/gig-line-release-qa`](.agents/skills/gig-line-release-qa/SKILL.md) — tracked AI skill for release and visual-QA work.
|
||||||
|
- [`docs/getting-started.md`](docs/getting-started.md) — Setup, run/test commands, and controls.
|
||||||
|
- [`docs/development-status.md`](docs/development-status.md) — Implemented features and what's next.
|
||||||
|
- [`docs/accessibility.md`](docs/accessibility.md) — Accessibility features, known gaps and statement.
|
||||||
|
- [`docs/known-limitations.md`](docs/known-limitations.md) — Release-candidate blockers and accepted limitations.
|
||||||
|
- [`docs/smoke-checklist.md`](docs/smoke-checklist.md) — Manual smoke-test checklist.
|
||||||
|
- [`CREDITS.md`](CREDITS.md) — Credits, AI/reference provenance and licensing notes.
|
||||||
|
- [`PRIVACY.md`](PRIVACY.md) — Local-only data, save files and development API use.
|
||||||
|
- [`docs/blender-assets.md`](docs/blender-assets.md) — Blender asset library: characters, gear, props, attach points.
|
||||||
|
- [`docs/npc-visual-review.md`](docs/npc-visual-review.md) — 0.9.4 NPC baseline, acceptance criteria and ranked visual gaps; 0.9.5 texture comparison is in [`docs/blender_previews/sheet_texture_refinement_0.9.5.jpg`](docs/blender_previews/sheet_texture_refinement_0.9.5.jpg).
|
||||||
|
- [`docs/npc-proportion-audit.md`](docs/npc-proportion-audit.md) — NPC proportion measurements against the shared stylized guide.
|
||||||
- [`docs/adr/`](docs/adr/) — Architecture Decision Records.
|
- [`docs/adr/`](docs/adr/) — Architecture Decision Records.
|
||||||
|
|
||||||
## Development Status
|
## Development Status
|
||||||
|
|
||||||
Implemented so far:
|
v0.10.0-rc.1 is the current release-candidate source: the 0.9.6 integration
|
||||||
|
checkpoint plus save-migration hardening and release tooling, on top of the 0.9.5
|
||||||
|
texture refinement (256² NPC maps, a 4× fabric weave with baked-AO removal, repaired
|
||||||
|
Walk of Fame/awning surfaces and per-phase skyline haze), on top of the v0.9.4
|
||||||
|
NPC visual optimization and the v0.9.3 UI/accessibility polish pass. It builds on
|
||||||
|
v0.9.2's Hollywood/Joke Shop map rebuilds, v0.9.1's Godot 4.7.2 baseline,
|
||||||
|
v0.9.0's production-quality pass, v0.8's
|
||||||
|
tutorial/campaign/resume work and the v0.7.x venue/gear/judge work. The active
|
||||||
|
planning priorities are the unversioned character and local-map development tracks;
|
||||||
|
the 0.10.0 release candidate is the next versioned release. See
|
||||||
|
[`docs/development-status.md`](docs/development-status.md) for the implemented list
|
||||||
|
and [`TODO.md`](TODO.md) for the priority-ordered roadmap. Manual verification uses
|
||||||
|
the [`docs/smoke-checklist.md`](docs/smoke-checklist.md).
|
||||||
|
|
||||||
- Godot 4.3 project scaffold, input map, procedural world builder
|
## Credits, Privacy and Accessibility
|
||||||
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
|
||||||
- Three authored scenarios (Hollywood Boulevard building, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
|
||||||
- Headless unit tests: **187 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material)
|
|
||||||
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
|
||||||
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
|
||||||
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
|
||||||
- Session shell: venue start menu (keyboard + mouse), Esc pause overlay (resume / restart / settings / quit to menu), settings panel with mouse-look sensitivity and HUD font scale persisted to `user://settings.json`, and "Run another shift" returning to the venue menu
|
|
||||||
- World presentation: skyline backdrop rendered with an alpha material, walkable step and landing colliders, larger marquee text, and larger focus circle / name tag
|
|
||||||
- Routine traffic despawns after a while at its waypoint so the crowd cap no longer fills with idle standers
|
|
||||||
- PLAN verbs: eye contact (distinct Level 1 cue), recognizing a person as a player decision recorded as access evidence, a body-camera flag that a formal warning arms (and `B` toggles), blocking that physically stations the guard between the person and the door, and a Level 3 request-reason ask answered from the NPC's `dialogue_lines`
|
|
||||||
- Incidents are now actually opened on focus and closed at shift end, so player actions, access evidence, escalation levels, and unresolved interactions reach the review (previously every recorded event was dropped)
|
|
||||||
- Crowd presentation: aggressive behavior and responder arrivals make idle bystanders stop and face the incident; panic sends people away from the door (and they leave the scene). A busy 16-NPC entrance measured ~1.2 ms/frame, so no LOD/culling was needed at the current cap
|
|
||||||
- Distant skyline ring: four alpha-scissored planes at 200 m read as a hazy city on the horizon (the baked backdrop used to show as a black slab), with a larger ground plane so the buildings meet the street
|
|
||||||
- Flashlight aims straight ahead and lights the person/door being checked instead of the ground in front of the guard
|
|
||||||
- Emergency aftermath: the `evacuation` outcome locks the door and clears the entrance, an emergency call that the situation did not justify opens an investigation and applies the false-report hit, and the shift review names the responder outcome and any investigations
|
|
||||||
- Body-camera timeline: the shift review opens a text timeline of every recorded incident event (time, person, action/outcome) that you step through with the arrow keys or buttons. No live 3D replay yet
|
|
||||||
- NPC theater: seated guests sit on the steps with bent legs, influencers and tourists raise a camera prop while photographing, performers sway their sign, and filming visitors pan slowly — all once they reach their intent's waypoint
|
|
||||||
- Audio: a looping procedural crowd bed (filtered murmur) whose volume follows `CrowdSystem.mood`, plus radio open/close crackle. All generated at runtime, no audio assets, and safe with a dummy audio server
|
|
||||||
- Accessibility: the escalation cue shows a shape meter plus the numeric level (`Escalation: Blocking [###--] L3/5`) so it reads in grayscale, and the HUD font scale from settings is applied at boot by resizing fonts (not Control.scale, which broke the right-aligned layout)
|
|
||||||
- Editor-ready venues: `tools/export_venues.gd` writes `content/venues/<id>.tres` from `ScenarioFactory`, and the `.tres` files load back with events, profiles, policy, phases, door lines, and locations intact (the factory stays the source of truth)
|
|
||||||
- Localization safety: `Strings.fmt` validates the placeholder count before formatting and fails safe (warns, returns the template) instead of raising an engine error, `Strings.placeholder_count` ignores `%%`, and `Strings.validate()` lints the table for trailing `%`, unsupported conversions, and mixed positional/sequential placeholders
|
|
||||||
- Authored scenario content: Joke Shop reservation-without-ID, borrowed/mismatched ID, a guest who refuses the wand screen, and a late arrival during a performance; Studio filming-boundary repeats and a celebrity arrival that draws a small crowd; Building events that point the player at the body camera and request-reason verbs
|
|
||||||
- Visual polish pass (v0.2.0): phase-based lighting (sun/sky/ambient per shift phase), street lamps with night-mode bulbs and omni lights, glowing marquee signage, crosswalk, planters and low-poly street trees, NPC height/brightness variety, denser routine traffic — day and late-night screenshot sets
|
|
||||||
- Assets pass: authored textures in `assets/textures/` (brick facade, concrete, asphalt, awning, skyline, vignette, HUD icons, access-card art) generated by `tools/generate_textures.py`, soft sun shadows, humanoid NPC rigs with archetype props + procedural walk bob and attention head-turn, inspection artifact cards, escalation vignette, mode icons, and a body-camera overlay (REC + shift clock)
|
|
||||||
- Rendering pass (v0.3.0 work, [ADR-0012](docs/adr/0012-surface-materials-and-generated-props.md)): one material module with roughness/metallic profiles per surface, ACES tonemapping, glow, aerial fog and colour grading driven by the shift phase, correctly scaled brick, framed facade windows that light up at night, door trim and handles, a curb and lane dashes, and the glowing marquee/lamp bulbs. NPCs gained per-role clothing palettes, caps/hoods/backpacks/sunglasses/lanyards, hands and shoes, and an arm/leg walk swing
|
|
||||||
- Prop collision and placement (v0.4.0): every prop fits a static collider from its mesh bounds and publishes a navigation obstacle, so the guard and guests cannot walk through benches, bins, or bollards; placements are grouped into zones with a validator, and deterministic variants add rotation, scale, and small clutter
|
|
||||||
- Prop atlas (v0.3.1): `tools/model_pipeline.py` packs each prop's palette into one power-of-two colour atlas plus a metallic/roughness atlas and emits a single material with per-vertex UVs, so a prop is one draw material rather than one per part; `audit` enforces the contract
|
|
||||||
- Generated prop models (v0.3.0 work): `tools/model_pipeline.py` builds bollards, benches, bins, planters, newspaper boxes and a hydrant as `.glb` props with per-part materials, loaded by `WorldBuilder` with a procedural fallback; the optional AI concept step (`model_pipeline.py concept`) supplies per-prop colour palettes
|
|
||||||
- Web build: `build/step-line-web-<version>.zip` (Godot's non-threaded web template, no COOP/COEP headers needed) built by `tools/build_releases.sh` and published with the desktop artifacts; serve the unzipped folder from any static host
|
|
||||||
- Aim-free selection pass (v0.3.0 work, [ADR-0011](docs/adr/0011-aim-free-selection-and-precision-walk.md)): the focus cone ranks candidates by angle then distance, `Tab` and the mouse wheel cycle nearby people nearest-first and pin the selection, right click clears focus with a short re-acquire grace, the HUD reports how many people are in the ring, and holding `Shift` walks at half speed for close work
|
|
||||||
|
|
||||||
Not yet implemented: live 3D body-camera replay, editor-based GUT integration, and crowd performance profiling/LOD (see `TODO.md`).
|
Gig Line is a local-only single-player game: there is no runtime telemetry or
|
||||||
|
network service, and saves stay in Godot's local `user://` folder. The
|
||||||
### Smoke Test Checklist
|
body-camera overlay is simulated. Asset provenance, AI-assisted image usage and
|
||||||
|
credits are recorded in [`CREDITS.md`](CREDITS.md); the local-data policy is in
|
||||||
1. `godot --path .` starts the game on the venue menu without script errors.
|
[`PRIVACY.md`](PRIVACY.md); accessibility features and gaps are in
|
||||||
2. Choosing a venue (click or ↑/↓ + Enter) starts that venue's shift and hides the menu.
|
[`docs/accessibility.md`](docs/accessibility.md); and release-candidate blockers
|
||||||
3. The guard moves and looks around; the building, steps, and sidewalk are visible.
|
are listed in [`docs/known-limitations.md`](docs/known-limitations.md).
|
||||||
4. The guard's camera sits at eye height and the guard stands on the landing, not inside the building.
|
|
||||||
5. Walking into the steps carries the guard up onto the landing (steps have colliders).
|
|
||||||
6. The skyline backdrop shows silhouettes against the sky, not an opaque black slab.
|
|
||||||
7. The `STEP LINE` marquee is readable from the sidewalk.
|
|
||||||
8. NPCs spawn and approach the entrance during the first minutes.
|
|
||||||
9. Looking at an NPC inside the focus cone shows a blue focus circle in front of the face, the person's name, and the HUD target line.
|
|
||||||
10. `Space` or left click opens the action wheel; `A`/`D` highlight a slice, `Space` confirms it, `Esc`/right click cancels without a log entry.
|
|
||||||
11. `E`/`C` open the inspection/wand panels from the wheel and freeze movement until they close; walking never fires the wand.
|
|
||||||
12. Pressing `Q` produces a reaction log entry and reputation feedback.
|
|
||||||
13. `E` opens access inspection showing venue-correct artifacts and policy result; `A`/`D` record a decision.
|
|
||||||
14. `6` opens the assistance form; submitting shows dispatch feedback.
|
|
||||||
15. `R` opens the emergency form; the button must be held ~1.6s or the call cancels.
|
|
||||||
16. `G` toggles the flashlight (indicator appears in the HUD); `L` toggles the door lock (indicator turns red when locked).
|
|
||||||
17. `F` toggles the debug overlay with NPC states and shift stats.
|
|
||||||
18. The wheel's `Look` slice is eye contact (Level 1) and logs as its own action.
|
|
||||||
19. `Reason` asks why the person is there and logs their `dialogue_lines` reply.
|
|
||||||
20. `Recognize` records the guard's recognition as access evidence; pressing it twice says so.
|
|
||||||
21. `Warn` turns the body camera on (REC overlay appears); `B` toggles it manually.
|
|
||||||
22. `Block` moves the guard between the person and the door.
|
|
||||||
23. The shift review reports the incident count, including interactions left unresolved.
|
|
||||||
24. Escalating on someone (or a responder arriving) makes idle bystanders stop and face the incident.
|
|
||||||
25. Repeated alarms panic the crowd and nearby people clear away from the door instead of standing around.
|
|
||||||
26. `Esc` with nothing open pauses the shift; resume / restart / settings / quit-to-menu all work, and settings changes persist across a restart.
|
|
||||||
27. NPCs route around the building using the navigation mesh (no stuck-at-wall behavior during a smoke run).
|
|
||||||
28. An unjustified emergency call (calm person, no weapon) logs an investigation and costs reputation.
|
|
||||||
29. After an evacuation outcome the door is locked and NPCs are leaving the entrance.
|
|
||||||
30. The shift review's "Body camera timeline" button steps through the recorded events (Left/Right or the buttons) and Esc returns to the review.
|
|
||||||
31. The escalation label shows a `[###--] L3/5` shape meter as well as the level name, and stays readable in grayscale.
|
|
||||||
32. Changing "HUD font scale" in Settings resizes the HUD text and persists across a restart.
|
|
||||||
33. When the shift timer reaches 0, the shift review screen shows grade, incident summary, responder outcomes, investigations, and persisted shift history; "Run another shift" returns to the venue menu.
|
|
||||||
|
|
||||||
## Development Workflow
|
## Development Workflow
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1007 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1004 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1000 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 954 KiB |
|
After Width: | Height: | Size: 910 KiB |
|
After Width: | Height: | Size: 971 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1021 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 35 KiB |