extends SceneTree ## Requirement-level audit for the park/shrine/intersection follow-up pass. const MAP := "res://scenes/maps/sakura_crossing/sakura_crossing.tscn" const ROAD_HALF := 4.5 const PAVE_W := 2.6 const SHOP_ST_Z := -30.0 const SOUTH_ST_Z := 20.0 const EAST_ST_X := 64.0 const EAST_ST_Z := 54.0 const STREET_HALF := 4.5 var _level: Node var _frames := 0 var _fails := 0 func _initialize() -> void: _level = (load(MAP) as PackedScene).instantiate() root.add_child(_level) func _ok(condition: bool, message: String) -> void: if condition: print("FOLLOWUP_AUDIT OK ", message) else: print("FOLLOWUP_AUDIT FAIL ", message) _fails += 1 func _all_nodes() -> Array[Node]: var result: Array[Node] = [] var queue: Array[Node] = [_level] while not queue.is_empty(): var node: Node = queue.pop_front() result.append(node) for child in node.get_children(): queue.append(child) return result func _base_name(node: Node) -> String: var value := String(node.name) while value.length() > 0 and value[value.length() - 1].is_valid_int(): value = value.substr(0, value.length() - 1) return value func _named(name: String) -> Array[Node]: var result: Array[Node] = [] for node in _all_nodes(): if _base_name(node) == name: result.append(node) return result func _box_size(node: Node) -> Vector3: var collisions := node.find_children("*", "CollisionShape3D", true, false) if collisions.is_empty(): return Vector3.ZERO var collision := collisions[0] as CollisionShape3D if collision == null or not collision.shape is BoxShape3D: return Vector3.ZERO return (collision.shape as BoxShape3D).size func _on_road_or_pavement(p: Vector2) -> bool: var paved_half := ROAD_HALF + PAVE_W + 0.30 if absf(p.x) <= paved_half: return true if absf(p.y - SOUTH_ST_Z) <= paved_half: return true if p.x <= 52.0 and absf(p.y - SHOP_ST_Z) <= paved_half: return true if p.x >= ROAD_HALF - 0.2 and p.x <= 108.0 \ and absf(p.y - EAST_ST_Z) <= paved_half: return true if p.y >= SOUTH_ST_Z and p.y <= 80.0 \ and absf(p.x - EAST_ST_X) <= paved_half: return true return false func _audit_signs(nodes: Array[Node]) -> void: var signs := 0 for node in nodes: if not node.has_meta("ground_sign"): continue signs += 1 var g := (node as Node3D).global_position _ok(not _on_road_or_pavement(Vector2(g.x, g.z)), "ground sign %s is clear of roads, crosswalks and pavements" % node.name) _ok(signs == 6, "all six freestanding sign posts are tagged and checked") func _audit_sidewalk_connectors(nodes: Array[Node]) -> void: var connectors := 0 var full_size := true for node in nodes: if not node.has_meta("sidewalk_connector"): continue connectors += 1 var collisions := node.find_children("*", "CollisionShape3D", true, false) var collision: CollisionShape3D = collisions[0] if not collisions.is_empty() else null if collision == null or not collision.shape is BoxShape3D: full_size = false continue var size := (collision.shape as BoxShape3D).size if size.x < PAVE_W + 0.59 or size.z < PAVE_W + 0.59: full_size = false _ok(connectors == 10, "eight main-junction and two shrine-side corner connectors exist") _ok(full_size, "every tactile apron reaches the adjoining pavement on both axes") func _audit_east_link(nodes: Array[Node]) -> void: var links: Array[Node] = [] for node in nodes: if node.has_meta("east_street_link"): links.append(node) _ok(links.size() == 1, "the shrine-side neighbourhood road has one continuous link slab") if links.size() == 1: var body := links[0] as StaticBody3D var collisions := body.find_children("*", "CollisionShape3D", true, false) var collision: CollisionShape3D = collisions[0] if not collisions.is_empty() else null var size := (collision.shape as BoxShape3D).size var left_edge := body.global_position.x - size.x * 0.5 _ok(left_edge <= ROAD_HALF, "the neighbourhood branch physically reaches the main road") var query := PhysicsRayQueryParameters3D.create( Vector3(6.0, 2.0, EAST_ST_Z), Vector3(6.0, -1.0, EAST_ST_Z)) var hit: Dictionary = _level.get_world_3d().direct_space_state.intersect_ray(query) var collider: Node = hit.get("collider") as Node _ok(not hit.is_empty() and collider != null and _base_name(collider) == "EastStreetLink", "the former gap is road surface, not the old sidewalk barrier") func _audit_park_and_playground() -> void: _ok(_named("PondBridge").size() == 1, "north park has a connected pond-loop bridge") _ok(_named("PicnicTable").size() >= 3, "park system has multiple social/picnic nodes") _ok(_named("ParkLamp").size() >= 8, "park paths have a consistent lighting rhythm") _ok(_named("ParkBin").size() >= 4, "park entrances and activity nodes have service assets") _ok(_named("SwingBeam").size() == 1 and _named("SwingSeat").size() == 2 \ and _named("SwingChain").size() == 4, "two swing seats align to four chains and one overhead beam") var beams := _named("SwingBeam") var seats := _named("SwingSeat") var chains := _named("SwingChain") if beams.size() == 1 and seats.size() == 2 and chains.size() == 4: var beam := beams[0] as Node3D var beam_size := _box_size(beam) var beam_left := beam.global_position.x - beam_size.x * 0.5 var beam_right := beam.global_position.x + beam_size.x * 0.5 var chains_under_beam := true for chain in chains: var cp := (chain as Node3D).global_position chains_under_beam = chains_under_beam \ and cp.x >= beam_left and cp.x <= beam_right \ and absf(cp.z - beam.global_position.z) < 0.01 \ and absf((cp.y + 1.55 * 0.5) - (beam.global_position.y - beam_size.y * 0.5)) < 0.08 _ok(chains_under_beam, "every swing chain lands under and touches the common top beam") var seats_have_pairs := true for seat in seats: var sp := (seat as Node3D).global_position var matching := 0 for chain in chains: var cp := (chain as Node3D).global_position if absf(cp.x - sp.x) <= 0.55 and absf(cp.z - sp.z) < 0.01: matching += 1 seats_have_pairs = seats_have_pairs and matching == 2 _ok(seats_have_pairs, "each swing seat is centered beneath exactly two chains") _ok(_named("SlidePlatform").size() == 1 and _named("SlideSlope").size() == 1, "slide platform and slope are both present") var platforms := _named("SlidePlatform") var slopes := _named("SlideSlope") if platforms.size() == 1 and slopes.size() == 1: var platform := platforms[0] as Node3D var slope := slopes[0] as Node3D var platform_size := _box_size(platform) var slope_size := _box_size(slope) var high_end := slope.global_transform * Vector3(0.0, 0.0, slope_size.z * 0.5) var platform_edge := Vector3(platform.global_position.x, platform.global_position.y, platform.global_position.z - platform_size.z * 0.5) _ok(Vector2(high_end.y, high_end.z).distance_to( Vector2(platform_edge.y, platform_edge.z)) < 0.16, "slide incline meets the platform edge at the same height and z datum") _ok(_named("Sandpit").size() == 1 and _named("Seesaw").size() == 1 \ and _named("ClimbingFrame").size() == 1, "playground contains distinct sand, seesaw and climbing zones") var cherries: Array[Node] = [] var cedars: Array[Node] = [] for node in _named("Cherry"): var p := (node as Node3D).global_position if p.x >= -96.0 and p.x <= 92.0 and p.z < -70.0: cherries.append(node) for node in _named("Cedar"): var p := (node as Node3D).global_position if p.x >= -96.0 and p.x <= 92.0 and p.z <= -78.0 and p.z >= -89.0: cedars.append(node) var min_gap := INF for cherry in cherries: var cp := (cherry as Node3D).global_position for cedar in cedars: var ep := (cedar as Node3D).global_position min_gap = minf(min_gap, Vector2(cp.x, cp.z).distance_to(Vector2(ep.x, ep.z))) _ok(not cherries.is_empty() and not cedars.is_empty() and min_gap > 8.0, "north-park cherry and evergreen planting bands are spatially separated") func _audit_shrine() -> void: _ok(_named("Komainu").size() >= 4, "outer and inner shrine thresholds have guardian pairs") _ok(_named("ShrineBanner").size() >= 4, "shrine approach and precinct have banner rhythm") _ok(_named("OmikujiRack").size() == 1, "precinct has a dedicated fortune-tying rack") _ok(_named("SacredTree").size() == 1 and _named("Hokora").size() == 1, "shrine lawn has a sacred tree and auxiliary shrine destination") func _process(_delta: float) -> bool: _frames += 1 if _frames < 120: return false var nodes := _all_nodes() _audit_signs(nodes) _audit_sidewalk_connectors(nodes) _audit_east_link(nodes) _audit_park_and_playground() _audit_shrine() print("=== SAKURA FOLLOW-UP AUDIT: %s ===" % ("ALL CLEAR" if _fails == 0 else "%d FAILED" % _fails)) quit(0 if _fails == 0 else 1) return true