feat(client): Sprint 6 Touch — z-layer pipeline, cursor, fog, interactions, inventory, stance, radial

Three-scope z-layer rendering pipeline (D-049): world z:0-900 inside
CanvasGroup, insert overlay CanvasLayer 10, UI CanvasLayer 20, modal
CanvasLayer 30. Y-sort contract enforced (entities z_index=0). Reserved
ranges for VFX, airborne, lower floors documented in constants.gd.

Sprint 6 client tickets:
- #429: Cursor state machine — 4 states, 150ms transitions (D-056)
- #430: Fog shader rebuild — 5-layer fragment shader, animated noise (D-059)
- #432: Entity interaction list — vertical multi-verb, insert-styled (D-057)
- #433: World radial menu — 2 spokes, drag-release + click-click (D-058)
- #438: Inventory UI — 3x3 grid, 40x40px, 1-9 hotkeys (D-065)
- #439: Stance indicator — color-coded HUD, C/X keybinds (D-053)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 23:06:03 +01:00
co-authored by Claude Opus 4.6
parent 24cce41379
commit 1fcf08d21f
24 changed files with 2370 additions and 151 deletions
+1 -37
View File
@@ -4,7 +4,6 @@ class_name TestRendering
extends GdUnitTestSuite
var EntityRendererScript = load("res://scripts/rendering/entity_renderer.gd")
var FogRendererScript = load("res://scripts/rendering/fog_renderer.gd")
var TileRendererScript = load("res://scripts/rendering/tile_renderer.gd")
# -- Test data matching Protocol decoded format --
@@ -175,7 +174,7 @@ func test_sim_bridge_test_snapshot_has_v2_fields() -> void:
SimBridge.reset_test_state()
var snap = SimBridge._test_snapshot()
assert_that(snap.has("version")).is_true()
assert_that(snap.version).is_equal(4)
assert_that(snap.version).is_equal(Protocol.PROTOCOL_VERSION)
assert_that(snap.has("game_time")).is_true()
assert_that(snap.has("player_facing")).is_true()
assert_that(snap.has("visible_tiles")).is_true()
@@ -330,41 +329,6 @@ func test_entity_renderer_npc_has_no_facing_indicator() -> void:
renderer.queue_free()
# -- FogRenderer: position registration --
func _make_fog_renderer() -> TileMapLayer:
var fog = TileMapLayer.new()
fog.set_script(FogRendererScript)
return fog
func test_fog_renderer_registers_positions() -> void:
var fog := _make_fog_renderer()
fog.register_tile_positions(_test_tiles)
assert_that(fog._all_tile_positions.size()).is_equal(5)
assert_that(fog._all_tile_positions.has(Vector2i(0, 0))).is_true()
assert_that(fog._all_tile_positions.has(Vector2i(3, 0))).is_true()
assert_that(fog._all_tile_positions.has(Vector2i(0, 1))).is_true()
fog.free()
func test_fog_renderer_clears_on_re_register() -> void:
var fog := _make_fog_renderer()
fog.register_tile_positions(_test_tiles)
assert_that(fog._all_tile_positions.size()).is_equal(5)
fog.register_tile_positions([{"x": 10, "y": 10, "z": 0, "type": "floor"}])
assert_that(fog._all_tile_positions.size()).is_equal(1)
assert_that(fog._all_tile_positions.has(Vector2i(0, 0))).is_false()
fog.free()
func test_fog_renderer_handles_empty_data() -> void:
var fog := _make_fog_renderer()
fog._initialized = true
fog.update_fog({}, Vector2.ZERO)
fog.register_tile_positions([])
assert_that(fog._all_tile_positions.size()).is_equal(0)
fog.free()
# -- TileRenderer: tile type constants --
func test_tile_type_map_covers_required_types() -> void: