Package a Forward+ preset with volumetric fog, SDFGI and a volumetric flashlight for a separate Linux/Windows Desktop HQ zip. Gate the effects on a real RenderingDevice, tune LATE fog, and adapt local light falloff for Godot 4.7. Record the Compatibility/Forward+ A/B captures and metrics, add the artifact to the build/publish scripts, and keep the default Compatibility/Web path unchanged. Headless 231/231, Python 87/87; exported HQ binary boots with Vulkan.
68 lines
3.1 KiB
GDScript
68 lines
3.1 KiB
GDScript
extends RefCounted
|
|
## Headless tests for the door lock mechanic and flashlight perception bonus.
|
|
|
|
const Rules := preload("res://src/core/rules.gd")
|
|
const DoorState := preload("res://src/core/door_state.gd")
|
|
const NpcProfile := preload("res://src/core/npc_profile.gd")
|
|
const NPCController := preload("res://src/core/npc_controller.gd")
|
|
|
|
func test_door_toggle_transitions() -> bool:
|
|
var door := DoorState.new()
|
|
var a := TestUtils.check_eq(door.locked, false, "starts unlocked")
|
|
var b := TestUtils.check_eq(door.toggle(), true, "toggle locks")
|
|
var c := TestUtils.check_eq(door.toggle(), false, "toggle unlocks")
|
|
return a and b and c
|
|
|
|
func test_locked_door_thwarts_force() -> bool:
|
|
var door := DoorState.new()
|
|
door.set_locked(true)
|
|
var result := door.attempt_force()
|
|
var a := TestUtils.check_eq(result["succeeded"], false, "locked door blocks forced entry")
|
|
var b := TestUtils.check_eq(result["outcome"], "thwarted", "thwarted outcome")
|
|
return a and b
|
|
|
|
func test_unlocked_door_admits_force() -> bool:
|
|
var door := DoorState.new()
|
|
var result := door.attempt_force()
|
|
var a := TestUtils.check_eq(result["succeeded"], true, "open door lets forced entry through")
|
|
var b := TestUtils.check_eq(result["outcome"], "entered", "entered outcome")
|
|
return a and b
|
|
|
|
func test_flashlight_aims_a_tight_forward_spot() -> bool:
|
|
## The lamp sits on the guard's head and aims straight down the view axis with
|
|
## a narrow cone: a wide cone washed the whole entrance instead of lighting the
|
|
## door being checked (realism pass, 2026-11-01).
|
|
var root := Node3D.new()
|
|
var gm: Node = load("res://src/game/game_manager.gd").new()
|
|
root.add_child(gm)
|
|
var guard: Node3D = gm.call("_build_guard")
|
|
var lamp: SpotLight3D = guard.get_node_or_null("Head/Flashlight") as SpotLight3D
|
|
var a := TestUtils.check(lamp != null, "flashlight lives on the guard's head")
|
|
var b := TestUtils.check(lamp != null and lamp.rotation_degrees.is_equal_approx(Vector3.ZERO),
|
|
"flashlight aims straight ahead")
|
|
var c := TestUtils.check(lamp != null and lamp.spot_angle <= 20.0,
|
|
"flashlight cone is narrow enough to read as a torch")
|
|
var d := TestUtils.check(lamp != null and not lamp.visible, "flashlight starts off")
|
|
var e := TestUtils.check(lamp != null
|
|
and is_zero_approx(lamp.light_volumetric_fog_energy),
|
|
"headless/compatibility builds leave the flashlight non-volumetric")
|
|
root.free()
|
|
return a and b and c and d and e
|
|
|
|
func test_flashlight_widens_perception() -> bool:
|
|
## Without the flashlight, an NPC at AWARE_RADIUS + 3 m is unaware.
|
|
## With the flashlight bonus (4 m), the same distance registers as aware.
|
|
var p := NpcProfile.new()
|
|
p.id = "nightwalker"
|
|
p.npc_type = Rules.NpcType.TOURIST
|
|
var dim := NPCController.new()
|
|
dim.setup("d1", p, "wander", 1)
|
|
var unlit: Dictionary = dim.perceive_guard(NPCController.AWARE_RADIUS + 3.0, 0.0)
|
|
var a := TestUtils.check_eq(unlit["band"], "unaware", "unlit: too far to notice")
|
|
|
|
var lit := NPCController.new()
|
|
lit.setup("l1", p, "wander", 2)
|
|
var lit_result: Dictionary = lit.perceive_guard(NPCController.AWARE_RADIUS + 3.0, 4.0)
|
|
var b := TestUtils.check_eq(lit_result["band"], "aware", "flashlight bonus makes NPC aware")
|
|
return a and b
|