extends RefCounted ## Headless tests for the shared surface/environment recipes (ADR-0012). const Materials := preload("res://src/game/materials.gd") const GameManager := preload("res://src/game/game_manager.gd") const Rules := preload("res://src/core/rules.gd") func test_every_profile_has_physical_values() -> bool: var ok := true for role in ["brick", "concrete", "asphalt", "awning", "stone", "metal", "painted_metal", "wood", "cloth", "skin", "glass", "rubber"]: var profile: Dictionary = Materials.profile(role) var roughness: float = float(profile.get("roughness", -1.0)) var metallic: float = float(profile.get("metallic", -1.0)) if roughness < 0.0 or roughness > 1.0: ok = false if metallic < 0.0 or metallic > 1.0: ok = false return TestUtils.check(ok, "all roles have roughness/metallic in 0..1") func test_textured_recipes_keep_their_texture_and_profile() -> bool: var brick := Materials.brick() var concrete := Materials.concrete() var asphalt := Materials.asphalt() var awning := Materials.awning() var a := TestUtils.check(brick.albedo_texture != null, "brick keeps its texture") var b := TestUtils.check(concrete.uv1_scale.x > 1.0, "concrete tiles across the sidewalk") var c := TestUtils.check(asphalt.roughness < brick.roughness, "asphalt is smoother than brick") var d := TestUtils.check(awning.albedo_texture != null, "awning keeps its texture") var e := TestUtils.check(Materials.flat(Color.RED, "metal").metallic > 0.5, "metal role is metallic") return a and b and c and d and e func test_realism_pick_tiles_and_wet_asphalt() -> bool: ## The realism pass swapped the flat first-generation tiles for the v2 picks. ## The street has a dry and a wet variant for day and late night. var brick := Materials.brick() var dry := Materials.asphalt() var wet := Materials.asphalt(Color(0.46, 0.48, 0.53), Vector2(30, 7), true) var a := TestUtils.check(brick.albedo_texture.resource_path.contains("v2/picks/"), "brick uses the promoted v2 pick") var b := TestUtils.check(brick.normal_enabled and brick.normal_texture != null, "brick carries its normal map") var c := TestUtils.check(dry.albedo_texture != wet.albedo_texture, "wet asphalt uses its own albedo") var d := TestUtils.check(wet.roughness < dry.roughness, "wet asphalt is glossier than dry asphalt") var e := TestUtils.check(wet.metallic_specular > dry.metallic_specular, "wet asphalt catches the lamps") var f := TestUtils.check(wet.albedo_texture.resource_path.contains("picks"), "the wet map is a promoted pick") return a and b and c and d and e and f func test_glass_and_emissive_recipes() -> bool: var glass := Materials.glass() var pane := Materials.window_pane() var bar := Materials.emissive(Color(1.0, 0.35, 0.75), 2.5) var a := TestUtils.check(glass.transparency == BaseMaterial3D.TRANSPARENCY_ALPHA, "glass is alpha blended") var b := TestUtils.check(glass.roughness < 0.2, "glass is smooth") var c := TestUtils.check(pane.emission_enabled, "window panes can light up") var d := TestUtils.check(is_equal_approx(pane.emission_energy_multiplier, 0.0), "window panes start dark") var e := TestUtils.check(is_equal_approx(bar.emission_energy_multiplier, 2.5), "emissive energy is applied") return a and b and c and d and e func test_environment_tuning_applies_phase_values() -> bool: var env := Environment.new() var late: Dictionary = GameManager.PHASE_LIGHTING[Rules.Phase.LATE] Materials.configure_environment(env, late) var a := TestUtils.check(env.tonemap_mode == Environment.TONE_MAPPER_ACES, "ACES tonemapping") var b := TestUtils.check(env.glow_enabled, "glow enabled for the marquee") var c := TestUtils.check(env.fog_enabled, "fog enabled for distance haze") var d := TestUtils.check(env.fog_density > 0.0, "fog has a density") var e := TestUtils.check(env.adjustment_enabled, "colour grading enabled") var f := TestUtils.check( env.fog_light_color.is_equal_approx(late["fog_color"]), "phase fog colour is used") var advanced := Materials.forward_plus_active() var g := TestUtils.check(env.volumetric_fog_enabled == advanced, "volumetric fog is gated to Forward+ (advanced=%s)" % advanced) var h := TestUtils.check(env.sdfgi_enabled == advanced, "SDFGI is gated to Forward+ (advanced=%s)" % advanced) return a and b and c and d and e and f and g and h func test_every_phase_has_environment_keys() -> bool: var ok := true for phase in [Rules.Phase.EARLY, Rules.Phase.MIDDAY, Rules.Phase.EVENING, Rules.Phase.LATE]: var cfg: Dictionary = GameManager.PHASE_LIGHTING.get(phase, {}) for key in ["exposure", "glow", "fog_color", "fog_density", "fog_energy", "contrast", "saturation", "volumetric_density", "volumetric_energy"]: if not cfg.has(key): ok = false return TestUtils.check(ok, "all phases set exposure/glow/fog/grading") func test_night_phases_are_night() -> bool: var evening: Dictionary = GameManager.PHASE_LIGHTING[Rules.Phase.EVENING] var late: Dictionary = GameManager.PHASE_LIGHTING[Rules.Phase.LATE] var early: Dictionary = GameManager.PHASE_LIGHTING[Rules.Phase.EARLY] return TestUtils.check(bool(evening["night"]) and bool(late["night"]) and not bool(early["night"]), "night flag matches the phase")