extends SceneTree ## godot --headless --path . --script res://tests/ui/test_pixel_tower.gd ## Camera coverage uses actual Runtime construction and World occupants: a ## misplaced pointer must not select the next cell, another floor, or BACK. var _checks := 0 var _failures := 0 var _clock: Variant var _state: Variant var _runtime: Variant var _preview: WorldPreview var _last_facility: StringName = &"" var _last_construction: StringName = &"" var _last_cell: Variant = null func _initialize() -> void: call_deferred("_run") func _run() -> void: _clock = root.get_node("GameClock") _state = root.get_node("GameState") _runtime = root.get_node("SimulationRuntime") _clock.reset(1, 6, 0) _state.reset() _runtime.initialize(_clock, _state, 724242) _clock.set_paused(true) root.size = Vector2i(1280, 720) _preview = WorldPreview.new() _preview.size = Vector2(1100, 600) _preview.set_runtime(_runtime) var pixel_theme := load("res://assets/themes/japanese_fantasy/visual-theme-v1.tres") as VisualThemeProfile _expect(pixel_theme != null and pixel_theme.pixel_dungeon_tiles != null and pixel_theme.pixel_town_tiles != null, "serialized theme loads both real Kenney atlases for actors and scenery") if pixel_theme != null: _preview.set_visual_theme(pixel_theme) if pixel_theme.pixel_dungeon_tiles != null: _expect(pixel_theme.pixel_dungeon_tiles.get_size() == Vector2(192, 176), "packed actor atlas matches the 12-column source regions") root.add_child(_preview) _preview.set_process(false) _preview.facility_selected.connect(_on_facility_selected) _preview.construction_selected.connect(_on_construction_selected) _preview.cell_clicked.connect(_on_cell_clicked) await process_frame await process_frame _test_zoom_anchor_and_reset() _test_construction_and_facility_clicks() _test_expanded_world_round_trips() _test_grid_preserves_hit_testing() _test_animation_pause_and_simulation_isolation() await _test_vertical_scroll_and_connection() _preview.queue_free() await process_frame if _failures == 0: print("PASS: %d pixel tower checks" % _checks) else: push_error("FAIL: %d of %d pixel tower checks failed" % [_failures, _checks]) quit(_failures) func _test_zoom_anchor_and_reset() -> void: _preview.reset_view() var overview := _preview.get_map_rect() var anchor_cell := Vector3i(-4, 2, WorldPreview.LayerMode.FRONT) var anchor := _preview.cell_to_canvas(anchor_cell) _preview.set_zoom(2.0, anchor) _expect(_preview.canvas_to_cell(anchor) == anchor_cell, "zoom anchored at a room keeps that room under the pointer") _expect(_preview.cell_to_canvas(anchor_cell).distance_to(anchor) <= 2.0, "cursor-anchored zoom keeps the same location within the room") _expect(_preview.get_cell_rect(anchor_cell).size.y > _preview.get_cell_rect(anchor_cell).size.x, "tower floors retain the rectangular cell aspect used by hit testing") _preview.pan_offset += Vector2(137, -73) _preview.reset_view() var restored := _preview.get_map_rect() _expect(restored.position.is_equal_approx(overview.position) and restored.size.is_equal_approx(overview.size), "reset returns to the original full overview after both pan and zoom") _expect(_preview.canvas_to_cell(_preview.cell_to_canvas(anchor_cell)) == anchor_cell, "reset keeps authoritative world coordinates selectable") func _test_construction_and_facility_clicks() -> void: var origin := Vector3i(-6, 0, WorldPreview.LayerMode.FRONT) var result: Variant = _runtime.submit_command( &"build_facility", {&"definition_id": &"reception", &"origin": origin, &"rotation": 0}, &"player", &"", 50, -1, &"pixel.camera.reception" ) _expect(result != null and result.is_success(), "camera fixture builds a reception through Runtime") var jobs: Array = _runtime.construction_service.get_jobs() _expect(jobs.size() == 1, "fixture creates exactly one real construction site") if jobs.size() != 1: return var job: Variant = jobs[0] _preview.layer_mode = WorldPreview.LayerMode.FRONT _preview.set_zoom(2.0) _center_on(origin) _click(_preview.cell_to_canvas(origin)) _expect(_last_construction == StringName(job.job_id) and _last_facility.is_empty(), "zoomed construction click resolves the real job rather than a finished facility") _clock.advance_minutes(120) var facilities: Array = _runtime.construction_service.get_facilities() _expect(facilities.size() == 1, "the clicked construction completes in simulation time") if facilities.size() != 1: return var facility_id := StringName(facilities[0].get(&"instance_id", &"")) var occupied: Array[Vector3i] = [] for x: int in range(4): for y: int in range(2): occupied.append(origin + Vector3i(x, y, 0)) for zoom: float in [1.0, 2.0, 3.0]: _preview.set_zoom(zoom) _center_on(origin + Vector3i(1, 1, 0)) for cell: Vector3i in occupied: var rect := _preview.get_cell_rect(cell) # Sample near opposite corners to catch square-cell conversions that # look correct at the center but select a different floor near edges. for fraction: Vector2 in [Vector2(0.15, 0.15), Vector2(0.85, 0.85)]: var point := rect.position + rect.size * fraction _expect(_preview.canvas_to_cell(point) == cell, "zoom %.1f maps interior point to occupied cell %s" % [zoom, cell]) _expect(_preview.facility_at_canvas(point) == facility_id, "zoom %.1f resolves reception footprint at %s" % [zoom, cell]) _click(point) _expect(_last_facility == facility_id and _last_construction.is_empty(), "zoom %.1f emits facility selection for the clicked interior" % zoom) var empty_neighbor := origin + Vector3i(4, 0, 0) var empty_point := _preview.cell_to_canvas(empty_neighbor) _click(empty_point) _expect(_last_facility.is_empty() and _last_cell == empty_neighbor, "zoom %.1f does not extend the reception hit area into the adjacent cell" % zoom) var front_point := _preview.cell_to_canvas(origin) _preview.layer_mode = WorldPreview.LayerMode.BACK _click(front_point) _expect(_last_facility.is_empty() and _last_cell == Vector3i(origin.x, origin.y, WorldPreview.LayerMode.BACK), "zoom %.1f cannot select the FRONT reception through the BACK layer" % zoom) _preview.layer_mode = WorldPreview.LayerMode.FRONT _click(_preview.cell_to_canvas(origin)) _expect(_last_facility == facility_id, "zoom %.1f restores reception selection after returning to FRONT" % zoom) func _test_expanded_world_round_trips() -> void: var reputation: Variant = _runtime.submit_command( &"update_progression_metrics", {&"metrics": {&"reputation": 25}}, &"player", &"", 100, -1, &"pixel.camera.rank.reputation" ) var capital: Variant = _runtime.submit_command( &"change_resources", {&"deltas": {&"gold": 10000}}, &"player", &"", 100, -1, &"pixel.camera.rank.capital" ) var promoted: Variant = _runtime.submit_command( &"set_world_tree_rank", {&"rank": 2}, &"player", &"", 100, -1, &"pixel.camera.rank.promote" ) _expect(reputation.is_success() and capital.is_success() and promoted.is_success(), "public progression commands expand the camera fixture above and below ground") if not promoted.is_success(): return var bounds: Dictionary = _runtime.world_service.get_bounds() var cells: Array[Vector3i] = [ Vector3i.ZERO, Vector3i(0, -1, 0), Vector3i(int(bounds[&"min_x"]), int(bounds[&"min_y"]), 0), Vector3i(int(bounds[&"max_x"]), int(bounds[&"max_y"]), 0), Vector3i(-3, 4, 0), ] for layer: int in [WorldPreview.LayerMode.FRONT, WorldPreview.LayerMode.BACK]: _preview.layer_mode = layer as WorldPreview.LayerMode for zoom: float in [1.0, 2.0, 3.0]: _preview.set_zoom(zoom) for cell: Vector3i in cells: cell.z = layer _center_on(cell) _preview.pan_offset += Vector2(31, -17) var point := _preview.cell_to_canvas(cell) _expect(_preview.canvas_to_cell(point) == cell, "zoom %.1f round trip survives pan, bounds, and layer at %s" % [zoom, cell]) var rect := _preview.get_cell_rect(cell) _expect(rect.has_point(point), "cell center remains inside its rendered rectangle") _preview.reset_view() var overview := _preview.get_map_rect() _expect(_preview.canvas_to_cell(overview.position - Vector2.ONE * 2.0) == null, "outside-world pixels cannot become buildable cells after overview reset") func _test_grid_preserves_hit_testing() -> void: _preview.layer_mode = WorldPreview.LayerMode.FRONT var cell := Vector3i(-5, 1, 0) _center_on(cell) var point := _preview.cell_to_canvas(cell) var expected := _preview.facility_at_canvas(point) _expect(not expected.is_empty(), "grid fixture points at a real reception cell") for show: bool in [false, true, false]: _preview.show_grid = show _click(point) _expect(_last_facility == expected and _preview.canvas_to_cell(point) == cell, "grid visibility never changes facility or floor hit testing") func _test_animation_pause_and_simulation_isolation() -> void: var before_digest := SimulationDigest.compute(_runtime.create_snapshot()) var before_minute: int = _clock.absolute_minute var stopped_at := float(_preview.get("_animation_time")) _preview.call("_process", 0.5) _expect(is_equal_approx(float(_preview.get("_animation_time")), stopped_at), "paused simulation freezes actor animation even when rendering receives a frame") _clock.set_paused(false) _preview.call("_process", 0.25) var animated_at := float(_preview.get("_animation_time")) _expect(animated_at > stopped_at, "unpausing resumes the pixel actor animation clock") _clock.set_paused(true) _preview.call("_process", 0.5) _expect(is_equal_approx(float(_preview.get("_animation_time")), animated_at), "pausing again freezes the exact resumed animation position") var cell := Vector3i(-5, 1, 0) _center_on(cell) var anchor := _preview.cell_to_canvas(cell) var wheel := InputEventMouseButton.new() wheel.button_index = MOUSE_BUTTON_WHEEL_UP wheel.pressed = true wheel.position = anchor _preview.call("_gui_input", wheel) _expect(_preview.canvas_to_cell(anchor) == cell, "wheel input preserves the room under the cursor while paused") var press := InputEventMouseButton.new() press.button_index = MOUSE_BUTTON_MIDDLE press.pressed = true _preview.call("_gui_input", press) var drag := InputEventMouseMotion.new() drag.relative = Vector2(43, 27) drag.button_mask = MOUSE_BUTTON_MASK_MIDDLE _preview.call("_gui_input", drag) press.pressed = false _preview.call("_gui_input", press) var moved_point := _preview.cell_to_canvas(cell) _expect(moved_point.distance_to(anchor + drag.relative) <= 2.0, "middle-button drag translates the visible room by the pointer movement") _click(moved_point) _expect(not _last_facility.is_empty(), "a room remains selectable immediately after dragging") _preview.set_pan_mode(true) _click(moved_point) _expect(_last_facility.is_empty() and _last_cell == null, "left drag in pan mode does not select or issue a placement click") drag.button_mask = MOUSE_BUTTON_MASK_LEFT _preview.call("_gui_input", drag) _expect(_preview.cell_to_canvas(cell).distance_to(moved_point + drag.relative) <= 2.0, "explicit pan mode moves the room with the left mouse button") var pan_before := _preview.pan_offset drag.button_mask = 0 _preview.call("_gui_input", drag) _expect(_preview.pan_offset == pan_before and not bool(_preview.get("_dragging")), "returning after releasing outside the viewport does not leave a stuck drag") _click(_preview.cell_to_canvas(cell)) _preview.notification(Control.NOTIFICATION_APPLICATION_FOCUS_OUT) _expect(not bool(_preview.get("_dragging")), "application focus loss cancels the camera drag") _preview.set_pan_mode(false) _click(_preview.cell_to_canvas(cell)) _expect(not _last_facility.is_empty(), "leaving pan mode restores normal facility selection") _preview.reset_view() _expect(int(_clock.absolute_minute) == before_minute, "animation frames and camera input do not advance simulation minutes") _expect(SimulationDigest.compute(_runtime.create_snapshot()) == before_digest, "animation, zoom, pan, and visual selection preserve the authoritative simulation digest") func _center_on(cell: Vector3i) -> void: _preview.pan_offset += _preview.size * 0.5 - _preview.cell_to_canvas(cell) func _click(position: Vector2) -> void: _last_facility = &"" _last_construction = &"" _last_cell = null var event := InputEventMouseButton.new() event.button_index = MOUSE_BUTTON_LEFT event.pressed = true event.position = position event.global_position = _preview.global_position + position _preview.call("_gui_input", event) func _on_facility_selected(instance_id: StringName) -> void: _last_facility = instance_id func _on_construction_selected(job_id: StringName) -> void: _last_construction = job_id func _on_cell_clicked(cell: Vector3i) -> void: _last_cell = cell func _expect(condition: bool, message: String) -> void: _checks += 1 if not condition: _failures += 1 push_error(message) func _test_vertical_scroll_and_connection() -> void: var previous_mouse_filter := _preview.mouse_filter _preview.mouse_filter = Control.MOUSE_FILTER_IGNORE var evidence := OS.get_environment("THE_TREE_P05_FIXTURE_OUTPUT") var before := var_to_bytes(_runtime.create_snapshot()) _preview.reset_view() _preview.set_pan_mode(true) var anchor_cell := Vector3i(-5,0,0) var wheel := InputEventMouseButton.new() wheel.button_index = MOUSE_BUTTON_WHEEL_UP wheel.pressed = true wheel.position = _preview.cell_to_canvas(anchor_cell) wheel.factor = 1.0 var zoom_before := _preview.zoom_level var point_before := _preview.cell_to_canvas(anchor_cell) _preview._gui_input(wheel) _expect(_preview.zoom_level == zoom_before and _preview.cell_to_canvas(anchor_cell).y > point_before.y, "pan-mode wheel moves vertically without zooming") wheel.button_index = MOUSE_BUTTON_WHEEL_DOWN _preview._gui_input(wheel) _expect(_preview.cell_to_canvas(anchor_cell) == point_before, "opposite wheel returns the same cell to the same point") wheel.ctrl_pressed = true wheel.button_index = MOUSE_BUTTON_WHEEL_UP _preview._gui_input(wheel) _expect(_preview.zoom_level > zoom_before, "Ctrl-wheel retains zoom while pan mode is active") _preview.set_pan_mode(false) wheel.ctrl_pressed = false wheel.button_index = MOUSE_BUTTON_WHEEL_DOWN _preview._gui_input(wheel) _expect(_preview.zoom_level == zoom_before, "ordinary wheel retains established zoom outside pan mode") _preview.set_pan_mode(true) wheel.button_index = MOUSE_BUTTON_WHEEL_UP for i in 200: _preview._gui_input(wheel) var clamped := _preview.pan_offset _preview._gui_input(wheel) _expect(_preview.pan_offset == clamped, "repeated vertical scroll stays at the existing camera boundary") _preview.reset_view() _preview.set_pan_mode(false) _expect(before == var_to_bytes(_runtime.create_snapshot()), "new camera input leaves whole authoritative Runtime bytes unchanged") var camera_after := var_to_bytes(_runtime.create_snapshot()) if not evidence.is_empty(): var f := FileAccess.open(evidence.path_join("before-camera.var"),FileAccess.WRITE);f.store_buffer(before) f = FileAccess.open(evidence.path_join("after-camera.var"),FileAccess.WRITE);f.store_buffer(var_to_bytes(_runtime.create_snapshot())) var definition: FacilityDefinition = _runtime.definition_registry.get_facility(&"standard_room") _preview.layer_mode = 0 _preview.set_build_definition(definition) _preview.set("_hovered_cell",Vector3i(-6,2,0)) _preview.call("_update_build_preview") var before_query := var_to_bytes(_runtime.create_snapshot()) var disconnected: Dictionary = _preview.get_build_preview_report(Vector3i(-6,2,0))[&"connection"] _expect(disconnected[&"status"] == &"DISCONNECTED", "upper room warns before real stairs reach its access cells") _expect(before_query == var_to_bytes(_runtime.create_snapshot()), "connection warning query preserves every Runtime byte") var control := FacilityInstance.new() control.origin = Vector3i(-6,2,0) control.occupied_cells.assign(_runtime.world_service.cells_for_footprint(control.origin,definition.footprint)) _expect(not _runtime.call("_find_facility_access_path",_runtime.call("_operations_entrance_cell"),control)[&"reachable"], "warning agrees with ordinary facility routing before stairs") await _capture_connection("disconnected",evidence) for cell: Vector3i in [Vector3i(-7,1,0),Vector3i(-7,2,0)]: var result: CommandResult = _runtime.submit_command(&"build_facility",{&"definition_id":&"guest_stairs",&"origin":cell}) _expect(result.is_success(), "connect preview through real paid stair construction") _clock.advance_minutes(_runtime.definition_registry.get_facility(&"guest_stairs").build_minutes) _preview.set("_hovered_cell",Vector3i(-6,2,0)) _preview._process(0) before_query = var_to_bytes(_runtime.create_snapshot()) var connected: Dictionary = _preview.get_build_preview_report(Vector3i(-6,2,0))[&"connection"] _expect(connected[&"status"] == &"CONNECTED" and not connected[&"path"].is_empty(), "real stair completion changes warning into a connected path") _expect(before_query == var_to_bytes(_runtime.create_snapshot()), "connected preview preserves the full authoritative state") _expect(_runtime.call("_find_facility_access_path",_runtime.call("_operations_entrance_cell"),control)[&"reachable"], "connected preview agrees with ordinary facility routing") _expect(_preview.get("_last_preview_report")[&"connection"][&"status"] == &"CONNECTED", "topology changes refresh the visible report even while paused") await _capture_connection("connected",evidence) var route: Array = connected[&"path"] for i in range(1,route.size()): _expect(not _runtime.path_service.get_edge_report(route[i-1],route[i]).is_empty(), "drawn connection consists only of actually accessible edges") var blocked: Array = connected[&"access_points"] for cell: Vector3i in blocked: _runtime.path_service.set_cell_temporary_block(cell,true) _preview._process(0) var blocked_report: Dictionary = _preview.get_build_preview_report(Vector3i(-6,2,0))[&"connection"] _expect(blocked_report[&"status"] == &"DISCONNECTED", "temporary access block invalidates the cached connected report") for cell: Vector3i in blocked: _runtime.path_service.set_cell_temporary_block(cell,false) _preview._process(0) _expect(_preview.get_build_preview_report(Vector3i(-6,2,0))[&"connection"][&"status"] == &"CONNECTED", "removing the block restores the same connection without stale UI") var connected_snapshot: Dictionary = _runtime.create_snapshot() _runtime.path_service.set_cell_temporary_block(Vector3i(-6,0,0),true) var branch_a: Dictionary = _runtime.create_snapshot() var revisions_a: Array = _preview.call("_current_preview_topology") _expect(_runtime.restore_snapshot(connected_snapshot), "restore real connected Runtime before second save branch") _runtime.path_service.set_cell_temporary_block(Vector3i(-7,2,0),true) var branch_b: Dictionary = _runtime.create_snapshot() var revisions_b: Array = _preview.call("_current_preview_topology") _expect(revisions_a == revisions_b, "different saved block topology deliberately has identical revision tuple") _expect(_runtime.restore_snapshot(branch_a), "restore first equal-revision snapshot through actual Runtime") var branch_a_report: Dictionary = _preview.get_build_preview_report(Vector3i(-6,2,0))[&"connection"] _expect(_runtime.restore_snapshot(branch_b), "restore second equal-revision snapshot through actual Runtime") var branch_b_report: Dictionary = _preview.get_build_preview_report(Vector3i(-6,2,0))[&"connection"] _expect(branch_a_report[&"status"] == &"CONNECTED" and branch_b_report[&"status"] == &"DISCONNECTED", "snapshot restore replaces stale connection despite identical revision numbers") _expect(branch_b_report[&"status"] == &"DISCONNECTED", "blocked access remains disconnected after equal-revision save restore") _expect(_runtime.restore_snapshot(connected_snapshot), "restore connected branch after collision test") _preview.set_runtime(_runtime) _expect(_preview.get_build_preview_report(Vector3i(-6,2,0))[&"connection"][&"status"] == &"CONNECTED", "Runtime rebind clears prior connection cache") if not evidence.is_empty(): var report := {"checks":_checks,"failures":_failures,"pid":OS.get_process_id(),"camera_before_after_equal":before==camera_after,"disconnected":disconnected,"connected":connected,"blocked":blocked_report,"equal_revision_a":revisions_a,"equal_revision_b":revisions_b,"branch_a":branch_a_report,"branch_b":branch_b_report,"fixture_only":true,"normal_OS_input":false,"unit_accepted":false} var f := FileAccess.open(evidence.path_join("report.json"),FileAccess.WRITE);f.store_string(JSON.stringify(report,"\t")) _preview.set_build_definition(null) _preview.mouse_filter = previous_mouse_filter func _capture_connection(label: String, evidence: String) -> void: if evidence.is_empty() or DisplayServer.get_name() == "headless": return _preview.set_zoom(2) _center_on(Vector3i(-5,1,0)) _preview.set("_hovered_cell",Vector3i(-6,2,0)) _preview.call("_update_build_preview") _preview.queue_redraw() await process_frame await RenderingServer.frame_post_draw _expect(root.get_texture().get_image().save_png(evidence.path_join("connection-"+label+".png"))==OK, "capture actual native connection line "+label)