This commit is contained in:
Nicholas Butzke
2026-08-02 02:20:02 -04:00
parent 61669627db
commit 922983429e
226 changed files with 34032 additions and 18521 deletions
+169
View File
@@ -0,0 +1,169 @@
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