- Replace the alley with the 27 m Comedy Store-inspired hall, signature canopy, circular blade sign, red curtain bays, west entrance, low-rise neighbours, dry hillside and opposite frontage.
- Add day/night/across-street concept frames and real-site mapping notes.
- Expose the Joke Shop day stage in the realism captures and refresh the gallery shots.
- Render multiline HUD/wand strings correctly and list carried items in the wand panel.
- Add the Joke Shop layout/budget tests and update docs, roadmap and changelog.
Replace the generic boulevard facade with a stylized 6840 Hollywood Blvd block: six Ionic columns, eight stone steps, Masonic frieze/medallions, event banners, side doors, El Capitan Theatre and retail neighbours, Ovation Hollywood arcade, Walk of Fame and four-lane street markings.
Add the Day and Night Hollywood venue-menu rows. Night uses a phase-time offset so a full-length shift stays in LATE while the clock starts at zero; late routine traffic keeps the street populated and the offset round-trips through checkpoint restore.
Retune local-light falloff to linear attenuation, add a cool north sky fill, and use the smooth limestone facade texture. Refresh the staged gallery and map references; add geometry-budget, menu, night-offset and late-traffic tests.
Regenerate the committed screenshot gallery on the current build, run compare
(13 good/0 bad) and the automatic-model judge (13/13 match, 0 diff), and record
the result. Update the 0.9.2 visual checklist items.
- add the v2 runtime picks: per-phase skyline panoramas, brick, sidewalk
pavers, dry/wet asphalt, awning canvas, and their normal maps
- add the realism tooling and evidence: tools/scene_concepts.py, capture_realism.gd,
concept frames, reports/sheets, and tests
- retune phase lighting/fog/exposure, add sun yaw, brighten the night glow,
add the flashlight beam mesh, and rebuild the planter/tree assets
- regenerate all eleven staged screenshots from the current build; compare is
11 good / 0 bad
- finish the visual-alignment closeout in DONE/TODO, update architecture,
blender-assets, PLAN, and ignore rules (editable Blender sources untracked)
- keep the judge retry and document the verified default judge model
Verification: headless 227/227; Python tooling 86/86; check_screenshots 11/11.
Phase 4 of the v0.6.0 graphics plan:
- tools/bake_ao.py bakes 256² AO maps for every Blender character/gear/prop
(Cycles, existing UVs) next to each GLB; Materials.apply_ao() wires the red
channel into character slots, gear, and props. Compatibility has no SSAO.
- Regenerated brick/concrete/asphalt at 1024² with OpenRouter, derived tangent
normal maps via tools/make_normal_maps.py, and wired them through
SurfaceMaterials.textured() without changing UV scales.
- Evaluated Forward+: boots on desktop Vulkan, but the renderer is project-wide
and Web must stay GL Compatibility; v0.6.0 keeps Compatibility (decision in
MEMORY/plan). A desktop-only override is deferred.
- Recaptured screenshots; compare 11 good / 0 bad. Fixed marquee text alignment.
- Tests: baked AO maps exist for all 46 assets; prop/character AO material
checks; headless 222/222, Python 69/69, all smokes pass.
- Version 0.6.0; ADR-0013 accepted; README/architecture/blender-assets/plan and
TODO/DONE updated.
NpcAvatar
- select one of 8 library character variants by role/seed (with npc_base and the
procedural rig as fallbacks), keep Rig/Head and the existing bone animation
- tint only skin/cloth/pants/accessory/hair; fixed detail materials keep their
colour, npc_vest keeps its hi-vis vest, firefighter/guard use dark uniform tints
- attach caps, hoods, helmets, backpacks, SCBA tanks, lanyards and hand items at
the documented head/back/chest/hand points (primitive fallbacks retained)
WorldBuilder
- prefer assets/models/blender/props/ for the six replacements and the new
street dressing (street lamp/tree, palms, planters, A-frame, cone, barrier,
stanchion, podium, handrail, wall lantern, double door, marquee, sedan)
- fit the same colliders/navigation obstacles and keep the placement validator;
collect library emissives (bulbs, marquee face, wall lanterns, door LED, sedan
lights) for set_night_mode()
- add a library triangle counter + 60k budget check (current ~27,986); toggle
prop gallery to the library models; fix the marquee label placement
Tests/smoke
- character selection/gear attach/fixed-material tint tests; library prop load,
emissive and triangle-budget tests; smoke_props routes a guest around a
stanchion in open sidewalk (the old bench route is now crowded by trees)
- headless 222/222, Python 68/68, all smokes pass, compare 11 good / 0 bad
Docs: TODO Phase 3.5 moved to DONE, README/architecture/blender-assets/plan
describe the integrated runtime, MEMORY records the integration rules.
Wire the skinned model from ADR-0013 into NpcAvatar:
- _build_skinned_body() instantiates the imported npc_base.glb, binds the 14
armature bones, and keeps the procedural _build_body() as the offline
fallback (use_skinned_model can force it in tests)
- outfit_for()/layer_for() tint the five material slots through the new
SurfaceMaterials.npc_slot(); accessories and hand items attach under Rig/Head
- the existing limb pivots (Hip/Knee/Shoulder/Elbow/Hand + Head marker) keep the
old animation math; _sync_skeleton() copies their rotations to bones. Bone
pose rotation is absolute local orientation, so it is composed with the rest
basis (otherwise the whole body snaps into a broken pose)
- skinned rigs scale uniformly (non-uniform parent scale shears the skin) and
keep Rig/Head for the focus cone, nametag, and reaction tag
- commit the editable npc_base.blend plus a .gdignore: Godot's editor otherwise
tries to import the blend and hangs during --import
- npc_pipeline audit tests (contract, textures, clean GLB, committed source)
Verification: headless 212/212, Python tooling 60/60, NPC budget 16 avatars =
144 meshes at ~6.9 ms, movement/focus/props smokes pass, compare 11 good / 0 bad.
TODO Phases 0-3 checked; environment lift and release remain.
Update the game name in project.godot, export presets, UI/marquee/title strings, packaging (AppImage/NSIS/desktop/appdata), tools, docs, and the generated prop GLBs. Packaging assets and README screenshots are regenerated. Headless suite 209/209 and Python tooling tests pass.
Ten of the eleven README captures are refreshed (the prop gallery is unchanged). README gains an NPC character-model subsection and a 'Confirmed game assets' section with linked, audited textures/models; architecture Level 2 lists npc_pipeline.py and asset_pipeline.py.
NPC characters
- Articulated rig (hips/knees, shoulders/elbows/hands) replaces rigid capsules;
the walk cycle bends knees and elbows and flees with a longer stride.
- Layered outfits (jacket/vest/coat/dress), extended accessories and hand items
(bag, umbrella, phone, camera, clipboard, cup), face details and five hair
silhouettes, all deterministic per profile seed.
- Idle behaviour layers (phone, clipboard, crossed arms, look-around), breathing
and weight shift; escalation raises hands or points, and noticed NPCs turn
toward the guard.
- Reaction symbols above the head (!!, ?!, !, ?), door-queue offsets assigned by
GameManager, three LOD tiers with hysteresis and a billboard impostor.
- smoke_npc_budget.gd: 16 avatars = 534 meshes, ~6.9 ms average (50 ms budget).
Player movement
- Separate accel/decel, step-up assist for kerbs and the entrance steps, and a
forward-axis fix: W was inverted (get_axis(forward, back) is negative).
- Head bob, landing dip and precision-walk FOV, gated by persisted head-bob and
invert-Y settings; precision chip and tick; surface-aware footsteps and a
landing cue forwarded to AudioFeedback.
- Gamepad support (sticks, deadzone, face buttons, bumpers), a control legend
that follows the active device, and named actions for the remaining keys.
- Key/button remapping in Settings, persisted as input_bindings and applied by
InputBindings (mouse and gamepad axes are never removed).
- smoke_movement.gd (windowed): climbs the steps, checks precision speed, chip,
footsteps and landing; new 11-precision-walk capture and catalogue entry.
Verification: headless 208/208, Python tooling 56/56, all 11 smokes pass,
compare 11 good / 0 bad, movement judge match. Version 0.5.0. Only optional
Future ideas remain in TODO.
Props were decoration: the guard and guests walked straight through benches,
bins, bollards and planters. They now block movement and steer guests around.
Collision
- _add_prop_model() fits a StaticBody3D from the model's mesh bounds in
holder-local space: a cylinder for bollard/bin/hydrant, a box otherwise, so
placement rotation and variant scale carry over.
- Fallback primitives go through the same fit, so a missing .glb cannot silently
remove collision.
- Each prop also publishes a NavigationObstacle3D; NpcAvatar enables agent
avoidance and moves with the safe velocity from velocity_computed. The navmesh
stays a flat walkable plane and avoidance does the steering.
Placement
- PROP_PLACEMENTS groups furniture by entrance, mid-block and curb zones, with a
static placement_problems() validator for bounds, overlap/min-spacing,
duplicates, and the door/steps/guest-lane exclusions.
- variant_for(index) gives deterministic yaw jitter, subtle scale, and optional
clutter (bag by the bin, crate by the newspaper boxes, cone by the hydrant).
Verification
- New windowed smoke_props.gd: guard walks into a bench and stops; a guest
headed beyond the bench routes around it and never enters the collider.
- Headless 182 -> 187: placement validation and its failure modes, variant
determinism/range, collider + obstacle on every placed prop, fallback collider.
- Other smokes (PLAN verbs, crowd, emergency, timeline) still pass; Python
tooling 53/53; screenshots recaptured; compare 9 good / 0 bad.
- Version 0.4.0. TODO: collision/placement section moved to DONE; prop pipeline
checks remain.
The three newest catalogue screens had AI mockups but no staged capture, so
compare and judge skipped them. capture_screenshots.gd now continues past the
night set:
- switches to The Joke Shop at 620 s (EVENING still has routine spawns), frames a
guest, and retries the wand until it alerts; if nobody carries an alerting item
it stages the documented keys false-alarm case.
- applies a formal warning (arms the body camera) then blocking, jumps to 760 s
for night lighting, and captures the HUD at L3.
- closes the open incident and opens the body-camera timeline, which now shows
the warning / body-camera / blocking rows.
Wired the refs for wand_screening, escalation_hud and body_camera_timeline, and
regenerated the escalation mockup as a third-person over-the-shoulder night shot
so the reference matches the game camera. Added Hud.clear_log() to drop
target-cycling noise from staged shots. README gallery lists the three captures.
Verification: headless 182/182, Python tooling 53/53, compare 9 good / 0 bad,
judge: wand match 0.90, timeline match 1.00, escalation review, 0 diff.
Version 0.3.3.