extends SceneTree ## Is any piece of street furniture standing somewhere it should not be? ## ## godot --headless --path . -s res://debug/prop_placement_audit.gd ## ## Every "there is a bus stop in the middle of the road" bug in this map came ## from a hand-written coordinate that was right when it was written and wrong ## after something moved. Screenshots catch them one at a time, from whichever ## angle happens to be pointed at them; this checks all of them at once. ## ## It re-derives the corridors from the same constants the builder uses rather ## than reading them out of it, so a prop that has drifted onto a carriageway ## fails here even if the builder thinks it placed it correctly. const MAP := "res://scenes/maps/sakura_crossing/sakura_crossing.tscn" # Mirrors of the builder's layout constants. const ROAD_HALF := 4.5 const PAVE_W := 2.6 const RAIL_HALF := 7.0 const SHOP_ST_Z := -30.0 const SHOP_ST_HALF := 4.5 const SHOP_ST_X1 := 52.0 const SOUTH_ST_Z := 20.0 const SOUTH_ST_HALF := 4.5 const EAST_ST_X := 64.0 const EAST_ST_Z := 54.0 const EAST_ST_HALF := 4.5 const HALF_X := 118.0 const CURVE_START_X := 62.0 const CURVE_RADIUS := 60.0 const CURVE_SWEEP := 55.0 ## Bodies whose placement is hand-authored and therefore worth checking. const WATCHED := ["Van", "Truck", "KeiVan", "KeiCar", "Hedge", "BlockWall", "LinesideHut", "Hoarding", "Vending", "Cherry", "Mound"] var _fails := 0 var _checked := 0 var _n := 0 var _lv: Node func _initialize() -> void: _lv = (load(MAP) as PackedScene).instantiate() root.add_child(_lv) ## The running line, as a polyline, recomputed here. func _rail_points() -> Array: var pts: Array = [] var x := -HALF_X - 40.0 while x < CURVE_START_X: pts.append(Vector2(x, 0.0)) x += 2.0 var c := Vector2(CURVE_START_X, -CURVE_RADIUS) var sweep := deg_to_rad(CURVE_SWEEP) for i in range(40): var th := sweep * float(i) / 39.0 pts.append(c + Vector2(sin(th), cos(th)) * CURVE_RADIUS) var ep: Vector2 = pts[pts.size() - 1] var et := Vector2(cos(sweep), -sin(sweep)) for i in range(60): pts.append(ep + et * (float(i) * 2.0)) return pts ## Roads a prop must never stand in. ## ## Every one is BOUNDED along its own length. Treating the main street as an ## infinite strip at |x| < 4.5 flagged a hillside three hundred and fifty metres ## out, where the road has not existed for two hundred of them. const ROAD_OUT := 170.0 # how far a road runs past the wall const HALF_Z := 88.0 func _road_hit(p: Vector2) -> String: if absf(p.x) < ROAD_HALF and absf(p.y) < HALF_Z + ROAD_OUT: return "main street" if absf(p.y - SOUTH_ST_Z) < SOUTH_ST_HALF and absf(p.x) < HALF_X + ROAD_OUT: return "south street" if p.x < SHOP_ST_X1 and p.x > -HALF_X - ROAD_OUT \ and absf(p.y - SHOP_ST_Z) < SHOP_ST_HALF: return "shopping street" if p.y > SOUTH_ST_Z and p.y < 80.0 and absf(p.x - EAST_ST_X) < EAST_ST_HALF: return "east district n-s street" if p.x > 10.0 and p.x < 108.0 and absf(p.y - EAST_ST_Z) < EAST_ST_HALF: return "east district e-w street" return "" ## Distance from a point on a carriageway to its nearest kerb. func _kerb_gap(p: Vector2) -> float: var best := 999.0 if absf(p.x) < ROAD_HALF: best = minf(best, ROAD_HALF - absf(p.x)) if absf(p.y - SOUTH_ST_Z) < SOUTH_ST_HALF: best = minf(best, SOUTH_ST_HALF - absf(p.y - SOUTH_ST_Z)) if p.x < SHOP_ST_X1 and absf(p.y - SHOP_ST_Z) < SHOP_ST_HALF: best = minf(best, SHOP_ST_HALF - absf(p.y - SHOP_ST_Z)) if absf(p.x - EAST_ST_X) < EAST_ST_HALF: best = minf(best, EAST_ST_HALF - absf(p.x - EAST_ST_X)) if absf(p.y - EAST_ST_Z) < EAST_ST_HALF: best = minf(best, EAST_ST_HALF - absf(p.y - EAST_ST_Z)) return best func _rail_hit(p: Vector2, margin: float) -> bool: for q in _rail_points(): if absf(q.x - p.x) > margin or absf(q.y - p.y) > margin: continue if q.distance_to(p) < margin: return true return false func _process(_d: float) -> bool: _n += 1 if _n < 120: return false var seen := {} for body in _lv.find_children("*", "StaticBody3D", true, false): # Godot suffixes duplicate node names with digits, so an unstripped # "Cherry2" matched nothing and most of the map went unchecked. var base := String(body.name) while base.length() > 0 and base[base.length() - 1].is_valid_int(): base = base.substr(0, base.length() - 1) if not WATCHED.has(base): continue var g: Vector3 = body.global_position var p := Vector2(g.x, g.z) # One report per object, not per collision box. var key := "%s|%.1f|%.1f" % [base, g.x, g.z] if seen.has(key): continue seen[key] = true _checked += 1 var road := _road_hit(p) if road != "": # Vehicles are SUPPOSED to be on the road — parked against the # kerb. What is wrong is one stranded out in the running lane. if base == "Van" or base == "Truck" or base == "KeiVan" or base == "KeiCar": if _kerb_gap(p) > 2.5: print("AUDIT: FAIL %-12s at (%.1f, %.1f) is parked %.1f m " % [base, g.x, g.z, _kerb_gap(p)] + "from the kerb of the %s" % road) _fails += 1 else: print("AUDIT: FAIL %-12s at (%.1f, %.1f) is in the %s carriageway" % [base, g.x, g.z, road]) _fails += 1 continue # Nothing but the railway's own furniture belongs within the four-foot. # 3.9 m clears the sleeper ends (3.75) without flagging lineside kit. if base != "LinesideHut" and _rail_hit(p, 3.9): print("AUDIT: FAIL %-12s at (%.1f, %.1f) is on the running line" % [base, g.x, g.z]) _fails += 1 elif base == "LinesideHut" and _rail_hit(p, 3.9): print("AUDIT: FAIL %-12s at (%.1f, %.1f) fouls the running line" % [base, g.x, g.z]) _fails += 1 print("AUDIT: checked %d placed props" % _checked) print("=== PROP PLACEMENT: %s ===" % ("ALL CLEAR" if _fails == 0 else "%d MISPLACED" % _fails)) return true