Pass the pre-build NPC review gate
- 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:
1 parent
37f925c7ad
commit
f71a36faa1
18 files changed
+335
-45
No files matched your search
@@ -6,12 +6,13 @@ const NpcAvatar := preload("res://src/game/npc_avatar.gd")
|
||||
const NpcProfile := preload("res://src/core/npc_profile.gd")
|
||||
const Rules := preload("res://src/core/rules.gd")
|
||||
|
||||
func _avatar(npc_type: int, seed_value: int) -> NpcAvatar:
|
||||
func _avatar(npc_type: int, seed_value: int, character: String = "") -> NpcAvatar:
|
||||
var profile := NpcProfile.new()
|
||||
profile.id = "char_test"
|
||||
profile.npc_type = npc_type
|
||||
profile.display_name = "Test Person"
|
||||
var avatar := NpcAvatar.new()
|
||||
avatar.character_override = character
|
||||
avatar.setup(profile, "enter", seed_value,
|
||||
Vector3(0, 0.5, 6), Vector3(0, 0.5, 1.5), Vector3(0, 0.5, 1.2), "char_test")
|
||||
return avatar
|
||||
@@ -167,7 +168,23 @@ func test_character_selection_is_deterministic_and_complete() -> bool:
|
||||
return TestUtils.check(problems.is_empty(),
|
||||
"every role selects an existing library character (%s)" % str(problems))
|
||||
|
||||
func test_review_override_can_stage_the_guard_model() -> bool:
|
||||
## The guard character has no spawned NPC role, so the final-context review
|
||||
## forces it explicitly instead of remapping a role.
|
||||
var avatar := _avatar(Rules.NpcType.CONFUSED_VISITOR, 12, "npc_guard")
|
||||
var a := TestUtils.check_eq(avatar.character_model, "npc_guard",
|
||||
"override selects the guard model")
|
||||
var b := TestUtils.check(avatar.loaded_model and not avatar.fallback_avatar,
|
||||
"the guard GLB loads through the normal skinned path")
|
||||
avatar.free()
|
||||
return a and b
|
||||
|
||||
func test_library_gear_attaches_to_the_contract_points() -> bool:
|
||||
var guest := _avatar(Rules.NpcType.AUTHORIZED_GUEST, 12)
|
||||
var lanyard := guest.find_child("Gear_lanyard", true, false) as Node3D
|
||||
var e := TestUtils.check(lanyard != null and lanyard.position.z > 0.03,
|
||||
"the lanyard badge clears the jacket front")
|
||||
guest.free()
|
||||
var tourist := _avatar(Rules.NpcType.TOURIST, 12)
|
||||
var backpack := tourist.find_child("Gear_backpack", true, false) as Node3D
|
||||
var camera := tourist.find_child("Gear_camera", true, false) as Node3D
|
||||
@@ -188,8 +205,10 @@ func test_library_gear_attaches_to_the_contract_points() -> bool:
|
||||
"firefighter helmet attaches to Rig/Head")
|
||||
var d := TestUtils.check(tank != null and flashlight != null,
|
||||
"responder gets SCBA tank and flashlight")
|
||||
var e2 := TestUtils.check(helmet != null and helmet.position.y > 0.02,
|
||||
"the firefighter helmet clears the brow")
|
||||
responder.free()
|
||||
return a and b and c and d
|
||||
return a and b and c and d and e and e2
|
||||
|
||||
## Horizontal bounds of a gear GLB in its attach-node space. The head/back
|
||||
## contract is measured against the Blender fit report (head width 0.315 m,
|
||||
|
||||
Reference in new issue
Block a user