Pass the pre-build NPC review gate
Full build / Import + headless tests (push) Successful in 34s
Full build / Full desktop build + optional release (push) Skipped

- Add a review harness that stages all eight library characters at portrait, conversation and crowd distance on both maps in day and night.
- Fix the final-context lanyard badge and firefighter helmet fit with runtime attach offsets; the asset-space gear contract is unchanged.
- Record the gate sign-off, refresh the gallery, and move the completed review to DONE.
This commit is contained in:
Gig Line Dev committed 2026-11-02 14:32:10 +01:00
1 parent 37f925c7ad
commit f71a36faa1
18 files changed
+335 -45

No files matched your search

+15 -1
View File
@@ -133,6 +133,10 @@ var _height_scale := 1.0
var loaded_model := false
var fallback_avatar := true
var character_model := ""
## QA/review override for the visual model. Empty keeps role/seed selection;
## `capture_npc_review.gd` uses it to stage the one library model (guard) that
## has no spawned NPC role.
var character_override := ""
## Tests can force the offline rig; the game always tries the committed model.
var use_skinned_model := true
var _skeleton: Skeleton3D = null
@@ -167,6 +171,13 @@ const ACCESSORIES := {
const BLENDER_NPC_DIR := "res://assets/models/blender/npc/"
const BLENDER_GEAR_DIR := "res://assets/models/blender/npc_gear/"
## Final-context adjustments applied inside the attach node. The lanyard was
## authored under the jacket's front surface; a small forward offset keeps the
## ribbon and badge readable without changing the attach-point contract.
const GEAR_OFFSETS := {
"lanyard": Vector3(0, 0, 0.04),
"firefighter_helmet": Vector3(0, 0.03, 0),
}
const GEAR_ATTACH := {
"cap": "head", "beanie": "head", "hood": "head", "sunglasses": "head",
"firefighter_helmet": "head",
@@ -335,7 +346,8 @@ static func reaction_label(mode: String, escalation: int) -> String:
## Instantiate the Blender-authored skinned base mesh. Returns false when the
## GLB is missing/not imported, so setup falls back to the procedural rig.
func _build_skinned_body(npc_type: int) -> bool:
character_model = character_model_for(npc_type, _visual_seed)
character_model = character_override if character_override != "" \
else character_model_for(npc_type, _visual_seed)
var path := BLENDER_NPC_DIR + character_model + ".glb"
if not ResourceLoader.exists(path):
character_model = "npc_base"
@@ -731,6 +743,8 @@ func _attach_gear(gear_name: String) -> bool:
_:
node.free()
return false
if GEAR_OFFSETS.has(gear_name):
node.position += GEAR_OFFSETS[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):