diff --git a/client/scripts/constants.gd b/client/scripts/constants.gd index f286ad59b..309ab9311 100644 --- a/client/scripts/constants.gd +++ b/client/scripts/constants.gd @@ -94,7 +94,7 @@ const FACING_INDICATOR_OFFSET: float = 14.0 # 640px = 20 × TILE_SIZE (32px) — grid-aligned, ~33% of 1920px viewport. # Tyre architecture review 2026-02-19: readability over max-width; fits # two columns of text comfortably, leaves world game visible alongside. -const DIALOGUE_MAX_WIDTH: int = 1200 +const DIALOGUE_MAX_WIDTH: int = 640 # Default camera zoom — used as fallback when get_camera_2d() returns null const CAMERA_DEFAULT_ZOOM: Vector2 = Vector2(2.0, 2.0) diff --git a/client/scripts/main.gd b/client/scripts/main.gd index cd7d6febc..d1b3373f9 100644 --- a/client/scripts/main.gd +++ b/client/scripts/main.gd @@ -425,10 +425,8 @@ func _detect_teleport(old_pos: Vector2, new_pos: Vector2) -> bool: # Clears dialogue/monologue/interaction state (server clears its side too). # Scoped to Gauntlet testing only — production fast-travel uses diegetic gates. func _teleport_transition() -> void: - # Snap camera immediately to new position. _teleport_in_progress causes - # the lerp block in _process() to snap again on the same frame (in case - # player_position updates after this call) and skip lerp next frame. - camera.global_position = GameState.player_position * Constants.TILE_SIZE + # Set teleport flag — the camera tracking block in _process() will snap + # to the player's new position this frame (no lerp). Flag clears after snap. _camera_anchored = true _teleport_in_progress = true diff --git a/client/scripts/rendering/entity_renderer.gd b/client/scripts/rendering/entity_renderer.gd index 1bbdb7529..63e596a64 100644 --- a/client/scripts/rendering/entity_renderer.gd +++ b/client/scripts/rendering/entity_renderer.gd @@ -17,7 +17,7 @@ const TILE_SIZE: int = Constants.TILE_SIZE const ENTITY_WIDTH: int = 24 const ENTITY_HEIGHT: int = 32 const ENTITY_OFFSET_X: float = (TILE_SIZE - ENTITY_WIDTH) / 2.0 # center horizontally -const ENTITY_OFFSET_Y: float = (TILE_SIZE - ENTITY_HEIGHT) / 2.0 # center vertically (bottom-aligned for y-sort would use TILE_SIZE - ENTITY_HEIGHT, but center is correct for placeholder) +const ENTITY_OFFSET_Y: float = (TILE_SIZE - ENTITY_HEIGHT) / 2.0 # center vertically for placeholder. Migration: when real sprites land, switch to bottom-anchor (TILE_SIZE - ENTITY_HEIGHT) for correct y-sort ordering. # Lerp speed — framerate-independent exponential smoothing. # At 12.0: ~70% there after 0.1s, ~95% after 0.25s. diff --git a/client/scripts/rendering/tile_renderer.gd b/client/scripts/rendering/tile_renderer.gd index 88cfb336a..5f5ede422 100644 --- a/client/scripts/rendering/tile_renderer.gd +++ b/client/scripts/rendering/tile_renderer.gd @@ -64,8 +64,10 @@ func _setup_tileset() -> void: # Update tiles from snapshot data # tiles: Array of {x: int, y: int, z: int, type: String} -# Only renders z=0 tiles (ground floor). z=1 (FloorObjects) and z>1 (upper floors) -# are handled by separate nodes — skipped here until those layers are implemented. +# z here is the server-side FLOOR LEVEL (0 = ground, 1 = first floor, etc.), +# NOT the Godot scene z_index (which controls render order within a floor). +# This node only renders floor-level 0. Higher floor levels will be handled +# by separate TileMapLayer nodes when multi-floor rendering is implemented. func update_tiles(tiles: Array) -> void: if not _initialized: return @@ -76,8 +78,8 @@ func update_tiles(tiles: Array) -> void: if not tile_data.has("x") or not tile_data.has("y") or not tile_data.has("type"): continue - # Multi-layer support: FloorTiles only renders z=0 (ground floor). - # z=1 → FloorObjects node, z=2 → YSortGroup furniture (future layers). + # Floor-level filter: only render tiles at floor level 0 (ground). + # Floor level 1+ tiles are for upper floors (future multi-floor nodes). var tile_z: int = tile_data.get("z", 0) if tile_z != 0: continue diff --git a/client/tests/test_smooth_camera_sprint15.gd b/client/tests/test_smooth_camera_sprint15.gd index 864ec02bc..86a73b08d 100644 --- a/client/tests/test_smooth_camera_sprint15.gd +++ b/client/tests/test_smooth_camera_sprint15.gd @@ -100,8 +100,8 @@ func test_camera_converges_to_player_over_multiple_frames() -> void: var target := GameState.player_position * Constants.TILE_SIZE - # Run 40 frames (~0.67s at 60fps) — well past convergence for any speed ≥ 2.0 - for i in range(40): + # Run 120 frames (~2s at 60fps) — converges within 1px for any speed ≥ 2.0 + for i in range(120): _instance._process(0.016) var camera: Camera2D = _instance.get_node("Camera2D") @@ -123,8 +123,9 @@ func test_camera_stationary_player_no_drift() -> void: for i in range(10): _instance._process(0.016) - # Camera should still be at anchored position (target = same point) - assert_that(camera.global_position).is_equal(initial_pos) + # Camera should still be at anchored position (target = same point). + # Use distance check — lerp toward same point may introduce float rounding. + assert_that(camera.global_position.distance_to(initial_pos) < 0.01).is_true() # --- Teleport snap --- diff --git a/client/tests/test_ui_framework_sprint15.gd b/client/tests/test_ui_framework_sprint15.gd index ac572294a..d7bea54c1 100644 --- a/client/tests/test_ui_framework_sprint15.gd +++ b/client/tests/test_ui_framework_sprint15.gd @@ -270,3 +270,34 @@ func test_entities_in_ysort_group() -> void: add_child(_instance) assert_that(_instance.get_node_or_null("World/FogGroup/YSortGroup/Entities")).is_not_null() + + +# ------------------------------------------------------------------------- +# #71: Tilemap z-filter — FloorTiles only renders floor level 0 +# ------------------------------------------------------------------------- + +func test_tile_renderer_skips_nonzero_z() -> void: + # #71: Tiles with z != 0 must be filtered out by update_tiles(). + var scene := load("res://scenes/main.tscn") + _instance = scene.instantiate() + auto_free(_instance) + add_child(_instance) + + var tile_renderer: TileMapLayer = _instance.get_node("World/FogGroup/FloorTiles") + assert_that(tile_renderer).is_not_null() + + # Feed tiles at z=0 and z=1 + var tiles: Array = [ + {"x": 0, "y": 0, "z": 0, "type": "floor"}, + {"x": 1, "y": 0, "z": 1, "type": "floor"}, + {"x": 2, "y": 0, "z": 0, "type": "wall"}, + {"x": 3, "y": 0, "z": 2, "type": "door"}, + ] + tile_renderer.update_tiles(tiles) + + # z=0 tiles should be present + assert_that(tile_renderer.get_cell_source_id(Vector2i(0, 0))).is_not_equal(-1) + assert_that(tile_renderer.get_cell_source_id(Vector2i(2, 0))).is_not_equal(-1) + # z=1 and z=2 tiles should NOT be present (-1 = no cell) + assert_that(tile_renderer.get_cell_source_id(Vector2i(1, 0))).is_equal(-1) + assert_that(tile_renderer.get_cell_source_id(Vector2i(3, 0))).is_equal(-1)