diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d0e5e..66bcf95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ Semantic Versioning. ## [Unreleased] +### Changed + +- Removed every hat and hood from the NPC roster: the firefighter helmet is the only head gear + left, so no character hides its face behind a cap. The street performer no longer wears the + cone hat and carries the baton and board in the hands instead. +- Fixed the modular Worker characters: the guard, delivery, firefighter and crowd roles had holes + in the torso and an open crown, a continuous unibrow and no visible eyes. They now have a closed + torso, hair, two eyebrows and two eyes. + ## [0.9.9] — 2026-11-06 ### Changed diff --git a/TODO.md b/TODO.md index 857a15a..f1cd212 100644 --- a/TODO.md +++ b/TODO.md @@ -890,8 +890,11 @@ gear) and 5 (head gear on the skull). - [x] Worker roles (vest, firefighter, guard, crowd_d) had **no visible eyes**: the Worker's own quads sit inside the head. The hoodie's eye pair is now transplanted with a 4 mm forward offset, so all four roles show two eyes and two brows (owner request). -- [x] Owner: every hat/head item moves up. `GEAR_OFFSETS` now lifts the cap, hood and beanie by - 0.07 m on top of the shared head-gear lift; the firefighter helmet is deliberately unchanged. +- [x] Owner: remove **all** hats except the firefighter helmet, and remove the performer cone. + Caps and hoods are gone from `ACCESSORIES`, the per-character gear list and the primitive + fallbacks; the performer keeps only the baton and board in hand. The head-gear lift/scale + compensation those hats needed is deleted with them, so `firefighter_helmet` is the only + head item and no role can hide its face. - [x] Worker faces: the dark "unibrow" band turned out to be the **cap brim** sitting on the brow, not head geometry. `HEAD_GEAR_LIFT` went 0.07 → 0.103 (the gear is authored around the head *centre* at 1.62, the Quaternius head centre is 1.69) and `GEAR_SCALE` grows the cap/hood so @@ -900,8 +903,9 @@ gear) and 5 (head gear on the skull). helmet dome covers the hair, and the cap reads as a cap rather than a visor. - [ ] The eyes are single-colour quads on every base; a light sclera + pupil texture on the `Eye` slot would finish the face at portrait distance. -- [ ] The raised caps now hover slightly above the hair shell with a visible gap; either shrink - the hair or fit the cap crown to it. +- [x] Owner review page re-created for the second pass: + [`docs/npc-visual-review.md`](docs/npc-visual-review.md), with the hat/head-item table and a + refreshed eight-row checklist. - [-] Arm/torso intersection on `npc_dress`/`npc_jacket`/`npc_crowd_a`/`npc_crowd_d`: the baked rest now aims the upper arm 7° out from vertical. Verified clear on the `casual` base; a light clip remains on `worker`, so this needs either a wider angle or a per-base shoulder diff --git a/docs/blender_previews/sheet_npc_roster_0.9.9.jpg b/docs/blender_previews/sheet_npc_roster_0.9.9.jpg index 27408ae..163c739 100644 Binary files a/docs/blender_previews/sheet_npc_roster_0.9.9.jpg and b/docs/blender_previews/sheet_npc_roster_0.9.9.jpg differ diff --git a/docs/blender_previews/sheet_npc_roster_crowd_0.9.9.jpg b/docs/blender_previews/sheet_npc_roster_crowd_0.9.9.jpg index b9376e5..cf9a765 100644 Binary files a/docs/blender_previews/sheet_npc_roster_crowd_0.9.9.jpg and b/docs/blender_previews/sheet_npc_roster_crowd_0.9.9.jpg differ diff --git a/docs/blender_previews/sheet_npc_roster_poses_0.9.9.jpg b/docs/blender_previews/sheet_npc_roster_poses_0.9.9.jpg index 5016d59..b6ccca4 100644 Binary files a/docs/blender_previews/sheet_npc_roster_poses_0.9.9.jpg and b/docs/blender_previews/sheet_npc_roster_poses_0.9.9.jpg differ diff --git a/docs/blender_previews/sheet_npc_roster_views_0.9.9.jpg b/docs/blender_previews/sheet_npc_roster_views_0.9.9.jpg index 06b86e0..a2f1562 100644 Binary files a/docs/blender_previews/sheet_npc_roster_views_0.9.9.jpg and b/docs/blender_previews/sheet_npc_roster_views_0.9.9.jpg differ diff --git a/docs/npc-visual-review.md b/docs/npc-visual-review.md index 6a2daab..25ffb13 100644 --- a/docs/npc-visual-review.md +++ b/docs/npc-visual-review.md @@ -1,14 +1,44 @@ -# NPC roster visual review — 0.9.9 signoff +# NPC roster visual review — 0.9.9 -**Status: awaiting owner visual signoff.** This page is the single review point for the -Quaternius roster swap that shipped in 0.9.9 -([ADR-0017](adr/0017-quaternius-npc-base.md), [ADR-0018](adr/0018-quaternius-stage2-runtime-rig.md)). -Every character name the game ships is captured here at gameplay distance, in three studio views, -and in the three animation poses the runtime drives. +**Status: revised after owner feedback; awaiting a second signoff.** This page is the single +review point for the Quaternius roster in +[ADR-0017](adr/0017-quaternius-npc-base.md) and +[ADR-0018](adr/0018-quaternius-stage2-runtime-rig.md). Every character name the game ships is +captured here at gameplay distance, in two studio views, and in the three animation poses the +runtime drives. -Until this page is signed off, the roster swap is reversible: the procedural library and the -MakeHuman head are still in the tree and still pass `audit`, `inspect`, `npc_proportions` and the -tests (`build --source procedural`). +## What changed since the first review + +The first pass was signed off **with follow-ups** and rejected three checklist rows: faces at +portrait distance, hands and hand-held gear, and head gear on the skull. The body findings were +all build defects, not art. This revision covers what was fixed and what is still open. + +| Owner finding | Cause | Status | +| --- | --- | --- | +| `npc_guard` holes in the upper body | the build **deleted** the `Worker_Vest` material, and on the modular Worker the vest *is* the torso | fixed — the vest is the `cloth` slot, nothing is deleted | +| `npc_firefighter`, `npc_crowd_d` broken shoulders | same, plus the deleted `Worker_Yellow` hat was the skull crown | fixed — the crown stays and takes the `hair` tint | +| one unibrow on `npc_guard`, `npc_vest`, `npc_crowd_d` | the Worker's `Eyebrows` is one bar across the face and its eye quads are oversized | fixed — the bar is cut, the hoodie's brows are transplanted 20 mm above the eye line | +| Worker roles had **no visible eyes** | the Worker's own eye quads sit inside the head | fixed — the hoodie's eye pair is transplanted with a 4 mm forward offset | +| arms intersecting the body on `npc_dress`, `npc_jacket`, `npc_crowd_a`, `npc_crowd_d` | the baked rest aimed the upper arm straight down, which fails on the narrow bases | reduced — the arm rests 7° out from vertical (mesh width 0.180 → 0.220 on `casual`, 0.228 → 0.262 on `hoodie`) | +| head gear did not sit on the skull | gear is authored around the procedural head centre (1.62); the Quaternius head centre is 1.69 | **resolved by removal** — see below | +| performer cone swallowed the head | a 0.44 m cone authored for the old body | removed — the performer carries a baton and board in the hands | + +## Hats and head items + +**The firefighter helmet is the only head gear in the roster.** Caps, hoods and the performer cone +were removed from the role table, the per-character gear list and the performer props, so no +character can hide its face behind a hat. That also retired the head-gear lift/scale compensation +the caps needed on the Quaternius head. + +| Role | Head gear | Body / hand items | +| --- | --- | --- | +| `npc_firefighter` | firefighter helmet | SCBA tank, flashlight | +| `npc_influencer` | none | sunglasses, shoulder bag, phone | +| `npc_tourist` | none | backpack, camera | +| `npc_authorized_guest` | none | lanyard | +| `npc_performer` | none | baton and board **in the hands** | +| `npc_protester` | none | pole and sign **in the hands** | +| every other role | none | role tint only | ## 1. Full-body roster (12 roles) @@ -18,133 +48,87 @@ One tile per shipped character, neutral studio, camera at 4.2 m. ## 2. Front and profile per role -Front row set, then profile. Use this to judge the silhouette, the head size and the gear placement -without perspective hiding anything. +Front set, then profile. Use this for silhouette, head size and gear placement without perspective +hiding anything. ![NPC roster front and profile](blender_previews/sheet_npc_roster_views_0.9.9.jpg) -## 3. Animation poses (idle / sit / walk contact) - -Six representative roles × three poses, driven by the real `_animate()` path with the FSM frozen. -Check: arms hang instead of holding an A-pose, the seated pose puts the forearms on the thighs and -keeps the shins down, the walk contact keeps limbs in contralateral opposition, and no limb -pinches or detaches at the shoulder, elbow, hip or knee. - -![NPC roster poses](blender_previews/sheet_npc_roster_poses_0.9.9.jpg) - -## 4. Crowd distance (8 m) +## 3. Crowd distance (8 m) The distance the player actually sees a passer-by from the sidewalk. ![NPC roster at crowd distance](blender_previews/sheet_npc_roster_crowd_0.9.9.jpg) +## 4. Animation poses (idle / sit / walk contact) + +Six roles × three poses, driven by the real `_animate()` path with the FSM frozen. Check that arms +hang instead of holding an A-pose, the seated pose puts the forearms on the thighs with the shins +down, walk contact keeps contralateral opposition, and nothing pinches or detaches at the shoulder, +elbow, hip or knee. + +![NPC roster poses](blender_previews/sheet_npc_roster_poses_0.9.9.jpg) + ## Signoff checklist -Tick each row. Anything you reject is a concrete task rather than an opinion, so it can go -straight into `TODO.md`. +Tick a verdict per row. A rejected row becomes a concrete `TODO.md` task, not an opinion. | # | Question | Verdict | | --- | --- | --- | -| 1 | Do the twelve characters read as distinct people at 8 m? | [x] yes [ ] no | -| 2 | Is the head size right for the body (no bobblehead, no pinhead)? | [x] yes [ ] no | -| 3 | Do the faces hold up at portrait distance? | [ ] yes [x] no | -| 4 | Do the hands and the hand-held gear (camera, cup, clipboard, sign) read correctly? | [ ] yes [x] no | -| 5 | Does the head gear (cap, hood, sunglasses, firefighter helmet) sit on the skull? | [ ] yes [x] no | -| 6 | Are the role outfits believable for a Hollywood-boulevard shift (tourist, delivery, guard, responder)? | [x] yes [ ] no | -| 7 | Are the seeded tint palettes acceptable, or too pastel/saturated? | [x] yes [ ] no | -| 8 | Do the seated and walking poses hold up? | [x] yes [ ] no | +| 1 | Do the twelve characters read as distinct people at 8 m? | ☐ yes ☐ no | +| 2 | Is the head size right for the body (no bobblehead, no pinhead)? | ☐ yes ☐ no | +| 3 | Do the faces hold up at portrait distance — brows, eyes, mouth, hair? | ☐ yes ☐ no | +| 4 | Do the hands and the hand-held items (camera, clipboard, board, baton) read correctly? | ☐ yes ☐ no | +| 5 | Is the firefighter helmet the only head gear, and does it sit correctly? | ☐ yes ☐ no | +| 6 | Are the role outfits believable for a Hollywood-boulevard shift? | ☐ yes ☐ no | +| 7 | Are the seeded tint palettes acceptable, or too pastel/saturated? | ☐ yes ☐ no | +| 8 | Do the seated and walking poses hold up? | ☐ yes ☐ no | -**Signoff:** [ ] approved as shipped [x] approved with the follow-ups below [ ] rejected +**Signoff:** ☐ approved as shipped ☐ approved with the follow-ups below ☐ rejected -## Issues visible in the captures +## Still open -The first review pass found three body defects. All three came from the build pipeline, -not the art, and all three are addressed in the second pass (2026-11-06): +Recorded as tasks in `TODO.md` under **Owner review findings**: -| Finding | Cause | Status | -| --- | --- | --- | -| `npc_guard` has holes in the upper body | the build **deleted** the `Worker_Vest` material, and on the modular Worker the vest *is* the torso | **fixed** — the vest is now the `cloth` slot, tinted per role; nothing is deleted | -| `npc_firefighter`, `npc_crowd_d` have a broken shoulder/upper-body area | same cause, plus the deleted `Worker_Yellow` hat was the crown of the skull, leaving the head open | **fixed** — the hat/crown stays in the mesh and only the part above z 1.65 takes the `hair` tint | -| `npc_guard`, `npc_vest`, `npc_crowd_d` have one big unibrow | the Worker's `Eyebrows` material is one continuous bar, and no other upstream head has brows that fit this mesh | **replaced** — the bar is cut out and the hoodie's two brows are transplanted onto the Worker head, lifted above the eye quads | -| `npc_dress`, `npc_jacket`, `npc_crowd_a`, `npc_crowd_d` arms intersect the body | the baked rest aimed the upper arm straight down, which works on the wide bases but not the narrow ones | **reduced** — the arm now rests 7° out from vertical (mesh width 0.180 → 0.220 on `casual`, 0.228 → 0.262 on `hoodie`); a light clip remains on the `worker` bases | +1. **Eyes have no whites.** Every base uses single-colour quads, so eyes read as dark rectangles at + portrait distance. A light sclera plus pupil on the `Eye` slot would finish the face. +2. **Arms still graze the torso on the `worker` base** — the 7° rest tilt cleared the `casual` base + but not this one. +3. **The `npc_vest` clipboard is oversized** on the shorter Quaternius arm. +4. **Roster variety is three meshes for twelve roles.** Tint and gear carry the difference; the + modular part library (hair, outfits) is the fix. +5. `npc_dress` and `npc_hoodie` have no coat or dress geometry — they are the `casual`/`hoodie` + bases with a matching tint. -Follow-up pass (same day) went further on the Worker head, which still read as a hard hat under -the bare tint: - -| Finding | Fix | -| --- | --- | -| crown still shaped like a hard hat | the hoodie's **hair** is transplanted onto the Worker head and the hat faces above the collar are cut away, so guard/responder/crowd_d have hair | -| the dark bar was the Worker's *eye* quads, not only the brow bar | the eye quads are removed too; the face now reads brows + moustache | -| performer cone swallowed the head, and the performer/protester props floated beside the body | `_add_props()` now parents carried props to the hand proxies and the cone shrank from a 0.44 m to a 0.25 m base | - -Third pass traced the remaining "black bar across the eyes" to the **cap brim**, not to head -geometry: head gear is authored around the procedural head centre (1.62) but the Quaternius head -centre sits at 1.69, so every cap sat 7 cm low with its brim across the brows. `HEAD_GEAR_LIFT` -is now 0.103 and `GEAR_SCALE` grows the cap/hood to clear the 0.228 m hair shell. The vision judge -confirms on `npc_guard`: no bar or visor, eyes and brows separately visible, the cap sits on the -head, no holes. - -Re-verified after the fix with the sheets below and the vision judge: no holes or missing geometry -on any of the three Worker-based roles. - -**Still open** — checkpoint row 4 (hand-held gear) and the list below: the eye quads have no -whites, the `npc_vest` clipboard is oversized on the shorter arm, and the arms still graze the -torso on the `worker` base. - -Found by user: - -1. npc_firefighter, npc_guard and npc_crowd_d have errors in the body: - - npc_guard has "holes" in upper body - - npc_firefighter and npc_crowd_d have buggy shoulder and upper body area -2. npc_guard, npc_vest and npc_crowd_d have one large unibrow: use other character's eyebrow geometry instead. -3. npc_dress, npc_jacket, npc_crowd_a and npc_crowd_d arms are too close and intersecting the body - -Found while building this page; none blocks the swap, all are queued in `TODO.md`: - -1. **`npc_dress` wears the performer cone hat plus the protest sign** because the performer props - are procedural and scaled for the old body. The dress itself has no geometry anymore — the role - is the `casual` base with a dress tint. -2. **`npc_coat` and `npc_hoodie`** are the hoodie base with a coat/hood tint; the raised hood and - the long coat silhouette are gone. -3. **The `npc_vest` clipboard** reads oversized at 0.23 × 0.31 m on the shorter Quaternius arm. -4. **The firefighter helmet brim** is 1.55× the head width — realistic for the helmet, but it reads - as a wide-brim hat next to the small head. -5. **Skin and cloth read pale** on the `hoodie` and `worker` bases; the seeded palette was tuned - against the procedural library's darker cloth. -6. **Roster variety is three meshes for twelve roles.** Tint and gear carry the difference; the - modular part library is the fix. - -## Measured facts for this build +## Measured facts | Metric | Value | | --- | --- | | Characters | 12, all on the Quaternius 62-joint rig | -| Triangles per character | 4,458 (`worker` minus vest) … 6,424 (`casual`) | +| Triangles per character | 4,458 (worker, before the head transplant) … 6,424 (`casual`) | | glTF vertices per character | 9,370 … 13,440 | | Budget | 24,000 tris / 24,000 verts per character | | Tint slots | `skin`, `cloth`, `pants`, `hair`, `accessory`, plus fixed detail | | Texel density | 73–74 px/m on the 256² maps | -| Height | 1.817–1.881 m | -| 24-NPC frame budget | 160,608 tris, 0.84 ms average frame (budget 50 ms) | +| Height | 1.82–1.88 m | +| 24-NPC frame budget | 174,976 tris, 0.85 ms average frame (budget 50 ms) | | Gates | `inspect` clean, `audit` 12/12, `fit` OK, Godot 278/278, Python 167/167 | ## Reproducing the captures ```sh godot --path . --script res://src/test/capture_npc_baseline.gd -- --out=res://tmp/npc-signoff -godot --path . --script res://src/test/capture_npc_pose.gd -- --out=res://tmp/npc-signoff-pose \ - --pose=idle # repeat for sit and walk_contact +godot --path . --script res://src/test/capture_npc_pose.gd \ + --out=res://tmp/npc-signoff-pose --pose=idle # repeat for sit and walk_contact ``` -## Superseded +## History -This page previously carried the **0.9.4 procedural library** baseline (eight hand-built -characters, the face/hand/neck fixes, the 13-core proportion audit). That mesh no longer ships, so -the text is retired; the comparison sheets stay in `blender_previews/` as history: +The page previously carried the 0.9.4 **procedural library** baseline (eight hand-built characters, +the face/hand/neck fixes, the 13-core proportion audit). That mesh no longer ships; its comparison +sheets stay in `blender_previews/` as records: `sheet_npc_baseline_compare-p1.jpg`, `-body.jpg`, `-torso.jpg`, `-face-detail.jpg`, `sheet_npc_hands_compare.jpg`, `sheet_npc_hands_grip_compare.jpg`. -The current source evaluation and per-character build table live in -[`npc-model-sourcing.md#shipped-roster-099-owner-chose-the-full-swap`](npc-model-sourcing.md#shipped-roster-099-owner-chose-the-full-swap); -the face options that led to the full swap are in +Per-character sources and build steps are in +[`npc-model-sourcing.md`](npc-model-sourcing.md); the face options that led to the full swap are in [`screens/npc-face-gate/`](screens/npc-face-gate/README.md). diff --git a/src/game/npc_avatar.gd b/src/game/npc_avatar.gd index 9c8b26d..23085e2 100644 --- a/src/game/npc_avatar.gd +++ b/src/game/npc_avatar.gd @@ -154,15 +154,17 @@ const HAIR_COLORS := [Color(0.12, 0.1, 0.08), Color(0.35, 0.22, 0.12), Color(0.5 ## Which small accessory a role wears. This is look-only: the player must still ## read intent from behaviour, never from the archetype name (ADR-0010). +## Only the responder wears head gear: the firefighter helmet. Everything else +## is a body or hand item, so no role can grow a hat that hides the face. const ACCESSORIES := { - Rules.NpcType.AUTHORIZED_STAFF: "cap", - Rules.NpcType.RESPONDER: "cap", - Rules.NpcType.DELIVERY: "cap", + Rules.NpcType.AUTHORIZED_STAFF: "none", + Rules.NpcType.RESPONDER: "none", + Rules.NpcType.DELIVERY: "none", Rules.NpcType.TOURIST: "backpack", Rules.NpcType.INFLUENCER: "sunglasses", - Rules.NpcType.TRESPASSER: "hood", - Rules.NpcType.VIOLENT_THREAT: "hood", - Rules.NpcType.MENTAL_CRISIS: "hood", + Rules.NpcType.TRESPASSER: "none", + Rules.NpcType.VIOLENT_THREAT: "none", + Rules.NpcType.MENTAL_CRISIS: "none", Rules.NpcType.AUTHORIZED_GUEST: "lanyard", Rules.NpcType.PERFORMER: "none", Rules.NpcType.PROTESTER: "none", @@ -178,22 +180,6 @@ const BLENDER_GEAR_DIR := "res://assets/models/blender/npc_gear/" const GEAR_OFFSETS := { "lanyard": Vector3(0, 0, 0.04), "firefighter_helmet": Vector3(0, 0.03, 0), - # The Quaternius head wears a 0.24 m hair shell that stands ~3.5 cm above the - # cap crown, so the cap sinks into the hair and only its brim shows. Lift the - # cap and hood onto the hair; sunglasses stay at the eye line. - # Owner: every hat and head item moves up; the firefighter helmet keeps its fit. - "cap": Vector3(0, 0.07, 0), - "hood": Vector3(0, 0.07, 0), - "beanie": Vector3(0, 0.07, 0), -} -## The hat geometry is authored around the 0.315 m procedural head and scaled to -## 0.64 in the Blender build to match the 0.20 m Quaternius skull. That fits the -## skull but not the 0.228 m hair shell on top of it, so a cap sinks into the hair -## and only its brim shows. These scale the head-gear node instead of the mesh. -const GEAR_SCALE := { - "cap": 1.22, - "hood": 1.12, - "beanie": 1.22, } ## Hand gear is authored around the wrist proxy. The palm sits ~4 cm in front ## of that origin, so without this offset the first phone/flashlight versions @@ -824,8 +810,6 @@ func _attach_gear(gear_name: String) -> bool: node.position += GEAR_OFFSETS[gear_name] if str(GEAR_ATTACH[gear_name]) == "hand": node.position += HAND_ITEM_FORWARD - if GEAR_SCALE.has(gear_name): - node.scale = Vector3.ONE * float(GEAR_SCALE[gear_name]) _detail_nodes.append(node) var ao := Materials.load_ao(BLENDER_GEAR_DIR + gear_name + "_ao.png") for child in node.find_children("*", "MeshInstance3D", true, false): @@ -837,30 +821,20 @@ func _attach_gear(gear_name: String) -> bool: func _add_character_gear() -> void: match character_model: "npc_firefighter": + # The only head gear in the roster. _attach_gear("firefighter_helmet") _attach_gear("scba_tank") - "npc_guard": - _attach_gear("cap") - "npc_vest": - _attach_gear("cap") "npc_dress": _attach_gear("shoulder_bag") -## Small role accessory: cap, hood, backpack, sunglasses or a staff lanyard. +## Small role accessory: backpack, sunglasses or a staff lanyard. No hats: the +## firefighter helmet is the only head gear in the roster. func _add_accessory(kind: String, shirt: Color) -> void: if character_model == "npc_firefighter": return - if character_model == "npc_hoodie" and kind == "hood": - # The hoodie variant already models the hood up. - return if _attach_gear(kind): return match kind: - "cap": - _detail(_add_sphere(_head, 0.175, Vector3(0, 0.12, 0), shirt.darkened(0.15), "cloth")) - _detail(_add_box_mesh(_head, Vector3(0.30, 0.03, 0.16), Vector3(0, 0.10, 0.16), shirt.darkened(0.22), "cloth")) - "hood": - _detail(_add_sphere(_head, 0.205, Vector3(0, 0.03, -0.03), Color(0.16, 0.16, 0.18), "cloth")) "backpack": _detail(_add_box_mesh(_rig, Vector3(0.34, 0.42, 0.16), Vector3(0, 1.06, -0.24), Color(0.24, 0.28, 0.36), "cloth")) _detail(_add_box_mesh(_rig, Vector3(0.30, 0.06, 0.02), Vector3(0, 1.24, -0.33), Color(0.42, 0.44, 0.5), "cloth")) @@ -980,19 +954,9 @@ func _add_props(npc_type: int) -> void: Rules.NpcType.DELIVERY: _add_box_prop(_rig, Vector3(0.34, 0.26, 0.28), Vector3(0.0, 1.02, 0.3), Color(0.55, 0.4, 0.25)) Rules.NpcType.PERFORMER: - # The cone is sized to the shipped head (0.20 m wide) and sits on the - # crown; at the old 0.44 m base it swallowed the whole head. - var hat := _material(Color(0.85, 0.2, 0.25)) - var cone := MeshInstance3D.new() - var cm := CylinderMesh.new() - cm.top_radius = 0.012 - cm.bottom_radius = 0.125 - cm.height = 0.24 - cone.mesh = cm - cone.material_override = hat - cone.position = Vector3(0, 0.15, 0) - _head.add_child(cone) - # Hat in one hand and a small board in the other: the performer plays + # No cone on the head: the performer plays the sidewalk with a board in + # one hand and a baton in the other, so the face stays readable. + # Baton in one hand and a small board in the other: the performer plays # to the sidewalk once at the waypoint. Both ride the hand proxies, so # they follow the arm instead of floating beside the body at a fixed # rig offset (which is what the old -0.46 m placement did). diff --git a/src/test/tests/test_npc_character.gd b/src/test/tests/test_npc_character.gd index 545f4a7..348cf88 100644 --- a/src/test/tests/test_npc_character.gd +++ b/src/test/tests/test_npc_character.gd @@ -326,7 +326,7 @@ func _gear_bounds(node: Node3D) -> AABB: return box func test_head_and_back_gear_clear_the_body_silhouette() -> bool: - const HEAD_WIDTH := 0.315 + const HEAD_WIDTH := 0.201 const WIDEST_BODY_RADIUS := 0.26 var problems: Array[String] = [] var seen := {} @@ -362,7 +362,9 @@ func test_head_and_back_gear_clear_the_body_silhouette() -> bool: problems.append("%s only reaches %.3f m (body %.3f)" % [ name, maxf(outer_x, outer_z), WIDEST_BODY_RADIUS]) avatar.free() - for required in ["head:sunglasses", "head:firefighter_helmet", "head:cap", + # The firefighter helmet is the only head gear in the roster; caps and hoods + # were removed so no role can hide its face. + for required in ["head:sunglasses", "head:firefighter_helmet", "back:shoulder_bag", "back:backpack", "back:scba_tank"]: if not seen.has(required): problems.append("missing %s" % required) diff --git a/src/test/tests/test_target_cycle.gd b/src/test/tests/test_target_cycle.gd index 0a1e7cb..bb19484 100644 --- a/src/test/tests/test_target_cycle.gd +++ b/src/test/tests/test_target_cycle.gd @@ -65,12 +65,14 @@ func test_outfit_varies_across_seeds() -> bool: return a and b func test_accessories_follow_the_role() -> bool: + # No hats: the firefighter helmet is the only head gear, so responders and + # trespassers carry body accessories instead of a cap or hood. var a := TestUtils.check_eq( - NpcAvatar.outfit_for(Rules.NpcType.RESPONDER, 5)["accessory"], "cap", - "responders wear a cap") + NpcAvatar.outfit_for(Rules.NpcType.RESPONDER, 5)["accessory"], "none", + "responders wear no hat") var b := TestUtils.check_eq( - NpcAvatar.outfit_for(Rules.NpcType.TRESPASSER, 5)["accessory"], "hood", - "trespassers wear a hood") + NpcAvatar.outfit_for(Rules.NpcType.TRESPASSER, 5)["accessory"], "none", + "trespassers wear no hood") var c := TestUtils.check_eq( NpcAvatar.outfit_for(Rules.NpcType.AUTHORIZED_GUEST, 5)["accessory"], "lanyard", "guests wear a lanyard")