big
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user