v0.3.0: rendering pass, generated props, selection model and Web build
Rendering and presentation - Shared surface-material module (src/game/materials.gd) with per-surface roughness/metallic/uv profiles; ACES tonemapping, glow, fog and phase-driven sky energy. WorldBuilder rebuilt on those profiles with facade windows that light up, door trim/handles, curb, lane dashes, bollards, bench, bin and planter foliage. - NPC silhouettes: per-role clothing palettes, caps/hoods/backpacks/sunglasses, hands and shoes, arm/leg walk swing. - Six generated low-poly GLB props (bollards, bench, bin, planter, newspaper box, hydrant) built by tools/model_pipeline.py, loaded by WorldBuilder with a procedural fallback; ADR-0012. Selection and movement - Focus ranking by view angle then distance (NpcFocus.rank_indices); Tab and the mouse wheel cycle nearby people initially, right click clears focus with a re-acquire grace, HUD shows the candidate count, Shift walks at half speed. ADR-0011. Windowed smoke extended to cover wheel cycling and clearing. QA tooling - screen_designs.py catalogue grown to 19 screens (8 scene, 11 UI) plus a `list` subcommand; draft generation keeps existing AI references unless --force. Web export - New Web preset using the non-threaded Godot template (static hosting, no COOP/COEP). tools/build_releases.sh exports build/web and packages build/step-line-web-<ver>.zip; publish_release.sh uploads it with the desktop artifacts. test_web_export.gd covers the preset and release wiring. Verification - Headless suite 177 -> 181, Python tooling 53 tests, all eight smokes pass (including the windowed focus/wheel smoke). Web bundle serves index.html, index.wasm (35 MB) and index.pck (30 MB) with HTTP 200 from a static server. - Version 0.3.0; README, PLAN, RELEASING, TODO/DONE and MEMORY updated.
This commit is contained in:
1 parent
f763cff744
commit
afa1d6beec
48 files changed
+2602
-147
No files matched your search
@@ -0,0 +1,77 @@
|
||||
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_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")
|
||||
return a and b and c and d and e and f
|
||||
|
||||
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"]:
|
||||
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")
|
||||
Reference in new issue
Block a user