Gutter priced and wrecked: GUTTER_COLLATERAL=90 baked with reasoning; torn-gutter house wreck, carport pattern
The fascia anchors have said collateral:"gutter" since Sprint 6 and nobody
priced one — collateralFor('gutter') returns null, backyard_01's house is a
free failure. Proposal rides in the asset like the carport's did: 90, between
the gnome (25) and the carport (180), with the band reasoning next to the
constant. A rules the number.
house_yardside_wrecked_v1: same origin/footprint/facade (shared _house_facade
helper — intact house BIN chunk byte-identical to the committed GLB, only the
three new price extras differ), fascia torn through the anchored span, left
run hanging by its last bracket, right run kinked ON the grass, downpipe out
of plumb, offcuts flat on the lawn. No fascia_anchor_* empties survive — you
cannot re-tie to a ripped eave.
collateral_key is new and explicit: unlike the carport, the structure and the
thing you take have different names, so the key names which collateral string
the value prices.
All 33 GLBs byte-identical across two full factory runs; the house pair also
byte-identical between --only and full runs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8decac0626
commit
4d1a2d7aa9
@ -133,6 +133,31 @@
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "house_yardside_wrecked",
|
||||
"dims": [
|
||||
9.4552,
|
||||
2.1021,
|
||||
2.9172
|
||||
],
|
||||
"tris": 340,
|
||||
"nodes": [
|
||||
"debris_fascia",
|
||||
"door",
|
||||
"downpipe_loose",
|
||||
"fascia_torn",
|
||||
"gutter_down",
|
||||
"gutter_torn",
|
||||
"house_yardside_wrecked",
|
||||
"roof",
|
||||
"wall",
|
||||
"window",
|
||||
"window_glow",
|
||||
"window_light_anchor"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "carport_01",
|
||||
"dims": [
|
||||
@ -468,42 +493,5 @@
|
||||
"problems": []
|
||||
}
|
||||
],
|
||||
"debris": [
|
||||
{
|
||||
"file": "BlueCrate_v2.glb",
|
||||
"dims": [
|
||||
0.36,
|
||||
0.36,
|
||||
0.29
|
||||
],
|
||||
"sane": true
|
||||
},
|
||||
{
|
||||
"file": "BlackTub_v2.glb",
|
||||
"dims": [
|
||||
0.36,
|
||||
0.54,
|
||||
0.2
|
||||
],
|
||||
"sane": true
|
||||
},
|
||||
{
|
||||
"file": "WhiteTub_v2.glb",
|
||||
"dims": [
|
||||
0.36,
|
||||
0.54,
|
||||
0.2
|
||||
],
|
||||
"sane": true
|
||||
},
|
||||
{
|
||||
"file": "WoodenBin_v2.glb",
|
||||
"dims": [
|
||||
0.35,
|
||||
0.36,
|
||||
0.31
|
||||
],
|
||||
"sane": true
|
||||
}
|
||||
]
|
||||
"debris": []
|
||||
}
|
||||
@ -684,10 +684,16 @@ def build_gate(name):
|
||||
return root
|
||||
|
||||
|
||||
def build_house_yardside(name):
|
||||
"""Rear façade only — no interior. The fascia is the point: DESIGN.md says
|
||||
it holds until the first real gust, then leaves with the gutter."""
|
||||
root = add_empty(name)
|
||||
def _house_facade(name, root):
|
||||
"""The rear façade shared VERBATIM by the intact house and its torn-gutter
|
||||
wreck — wall ring, door, window, night glow, roof. Split out in Sprint 12
|
||||
because a house does not fall down when a sail rips its eave line off (the
|
||||
carport folds; brick veneer shrugs), so the two variants have to be the SAME
|
||||
building or the aftermath swap reads as the house moving. One set of numbers,
|
||||
two eave lines.
|
||||
|
||||
Returns the measurements and materials the eave line is built from, because
|
||||
the eave is exactly the part that differs between the two callers."""
|
||||
wall_m = get_material("Mat_Render", PAL["render_wall"], 0.9)
|
||||
brick_m = get_material("Mat_Brick", PAL["brick"], 0.9)
|
||||
trim = get_material("Mat_Timber", PAL["timber_dark"], 0.8)
|
||||
@ -754,12 +760,47 @@ def build_house_yardside(name):
|
||||
root, size=0.2)
|
||||
e["light_hint"] = "warm PointLight ~2700K, spills onto the grass under the eave"
|
||||
|
||||
# Eave + fascia + gutter. The eave overhangs 0.55 into the yard (-Y).
|
||||
# The roof and its eave. The eave overhangs 0.55 into the yard (-Y).
|
||||
eave_y = -0.55
|
||||
fascia_z = H + 0.10
|
||||
join_group([add_box(f"{name}_roof", (W + 0.2, D + 0.75, 0.10),
|
||||
(0, (eave_y + D / 2) / 2, H + 0.05), roof_m,
|
||||
rot=(math.radians(-6), 0, 0))], "roof", root)
|
||||
return dict(W=W, H=H, D=D, eave_y=eave_y, fascia_z=fascia_z,
|
||||
trim=trim, gutter_m=gutter_m)
|
||||
|
||||
|
||||
# What it costs to take the client's gutter — and the fascia it hangs off —
|
||||
# with you. Same deal as CARPORT_COLLATERAL below, second time around: Lane A
|
||||
# owns this number, it's a design call, not an art one. But the fascia anchors
|
||||
# have said collateral:"gutter" since Sprint 6 and NOBODY has ever priced one —
|
||||
# collateralFor('gutter') returns null, so backyard_01's house is a free
|
||||
# failure: tie 25 m² to a 0.35 fascia, rip it off, pay for a shackle. My
|
||||
# proposal and the reasoning, to argue with rather than adopt:
|
||||
# · the band is established now: gnome 25 at the floor, carport 180, my
|
||||
# stated ceiling ~250. The gutter goes between the ornament and the
|
||||
# structure, because that is what it is;
|
||||
# · a night's shop budget is 80 and the wild night's fee is ~76 (feeFor at a
|
||||
# 32 m/s peak). At 90 the gutter costs slightly more than EITHER — the
|
||||
# night it fires on is wiped, kit and fee together. That is the fascia's
|
||||
# lesson in one number: the "free" anchors were the dearest kit in the yard;
|
||||
# · under ~50 it's a shrug — less than the fee alone, so the trap couldn't
|
||||
# even wipe its own night;
|
||||
# · over ~130 the backyard outbids half a carport for a smaller lie: the
|
||||
# fascia (0.35) is honestly better steel than the beam (0.22), it's three
|
||||
# anchors not four, and it's the tutorial yard — the first trap a player
|
||||
# can hit should cost them a night, not the week.
|
||||
GUTTER_COLLATERAL = 90
|
||||
|
||||
|
||||
def build_house_yardside(name):
|
||||
"""Rear façade only — no interior. The fascia is the point: DESIGN.md says
|
||||
it holds until the first real gust, then leaves with the gutter."""
|
||||
root = add_empty(name)
|
||||
f = _house_facade(name, root)
|
||||
W, H, eave_y, fascia_z = f["W"], f["H"], f["eave_y"], f["fascia_z"]
|
||||
trim, gutter_m = f["trim"], f["gutter_m"]
|
||||
|
||||
fascia = add_box("fascia", (W + 0.2, 0.035, 0.20), (0, eave_y, fascia_z),
|
||||
trim, parent=root)
|
||||
gutter = join_group([
|
||||
@ -780,6 +821,109 @@ def build_house_yardside(name):
|
||||
e["collateral"] = "gutter"
|
||||
stamp(root, name, "structure")
|
||||
root["facade_width"] = W
|
||||
# The proposal rides in the asset, exactly like the carport's did (and A
|
||||
# adopted that one into site JSON, where the canonical number now lives —
|
||||
# sites are data; this is the fallback and the argument). collateral_key is
|
||||
# NEW and explicit because the carport never needed it: there, the priced
|
||||
# thing and the structure share a name. Here the structure is "house" and
|
||||
# the thing you take is the GUTTER — the key names which collateral string
|
||||
# this value prices, so Lane A's wiring never has to guess.
|
||||
root["collateral_key"] = "gutter"
|
||||
root["collateral_value"] = GUTTER_COLLATERAL
|
||||
root["collateral_label"] = "the gutter"
|
||||
return root
|
||||
|
||||
|
||||
def build_house_yardside_wrecked(name):
|
||||
"""The house after the fascia let go (SPRINT12 §gate 3.3) — DESIGN.md's line
|
||||
made geometry: "holds until the first real gust, then rips off taking the
|
||||
gutter with it".
|
||||
|
||||
Same origin, same footprint, same façade — _house_facade, shared verbatim —
|
||||
because a house does not fall down when a sail takes its eave line; only
|
||||
what the anchors betrayed changes. The fascia is torn through the span the
|
||||
fixings held, and the gutter it carried is in two states at once: the left
|
||||
run still hangs off the surviving board by its last bracket, torn end sagged
|
||||
toward the grass, and the right run is ON the grass out in the yard, kinked
|
||||
where it landed, with the downpipe leaning out of plumb after it. A gutter
|
||||
doesn't shatter — it unzips, springs, and lands where the wind was going.
|
||||
|
||||
NO fascia_anchor_* empties survive into this file. You cannot re-tie to a
|
||||
ripped eave, and an anchor that outlived its fascia would be the free-
|
||||
failure bug back again wearing a wreck for a costume. e.test.js pins that,
|
||||
and that the gutter is DOWN here while its intact twin's is UP at the eave.
|
||||
"""
|
||||
rng = rng_for(name)
|
||||
root = add_empty(name)
|
||||
f = _house_facade(name, root)
|
||||
W, eave_y, fascia_z = f["W"], f["eave_y"], f["fascia_z"]
|
||||
trim, gutter_m = f["trim"], f["gutter_m"]
|
||||
|
||||
# The fascia, minus what the sail took: a 3.4 m board survives on the left,
|
||||
# a stub clings to the far right corner, and both torn ends splinter past
|
||||
# the break. The gap in between is where fascia_anchor_02 used to be.
|
||||
fascia = [
|
||||
add_box(f"{name}_fascia_left", (3.4, 0.035, 0.20),
|
||||
(-2.9, eave_y, fascia_z), trim),
|
||||
add_box(f"{name}_fascia_stub", (0.6, 0.035, 0.20),
|
||||
(4.3, eave_y, fascia_z), trim),
|
||||
]
|
||||
for i, (bx, ang) in enumerate(((-1.05, 14), (3.85, -18))):
|
||||
fascia.append(add_box(f"{name}_fascia_splinter_{i}", (0.38, 0.028, 0.09),
|
||||
(bx, eave_y, fascia_z + 0.02), trim,
|
||||
rot=(0, math.radians(ang + rng.uniform(-3, 3)), 0)))
|
||||
join_group(fascia, "fascia_torn", root)
|
||||
|
||||
# The left run: still bracketed at the healthy end, torn free at the break,
|
||||
# hanging diagonally with the freed end sprung out over the grass.
|
||||
join_group([add_tube_between(
|
||||
f"{name}_gutter_hang",
|
||||
(-4.4, eave_y - 0.05, fascia_z - 0.12),
|
||||
(-0.55, eave_y - 0.34, 1.28), 0.055, gutter_m, verts=8)],
|
||||
"gutter_torn", root)
|
||||
|
||||
# The right run went where the wind was going: out over the yard, down on
|
||||
# the grass, kinked where it hit. Flat ON the grass — the carport wreck's
|
||||
# first-pass lesson (a corner sunk to z=-0.41) is why nothing here sinks.
|
||||
kx = 2.4 + rng.uniform(-0.3, 0.3)
|
||||
join_group([
|
||||
add_tube_between(f"{name}_gutter_ground_a",
|
||||
(0.35, -1.35, 0.055), (kx, -1.72, 0.055),
|
||||
0.055, gutter_m, verts=8),
|
||||
add_tube_between(f"{name}_gutter_ground_b",
|
||||
(kx, -1.72, 0.055), (4.85, -1.48, 0.055),
|
||||
0.055, gutter_m, verts=8),
|
||||
], "gutter_down", root)
|
||||
|
||||
# The downpipe keeps its bottom strap and loses the top one with the run —
|
||||
# so it leans out of plumb into the yard. Cheapest possible "this eave is
|
||||
# finished" tell: it reads from across the lawn at night.
|
||||
join_group([add_tube_between(
|
||||
f"{name}_downpipe_loose",
|
||||
(W / 2 - 0.25, eave_y - 0.05, 0.02),
|
||||
(W / 2 - 0.12, eave_y - 0.62, 2.58), 0.04, gutter_m, verts=8)],
|
||||
"downpipe_loose", root)
|
||||
|
||||
# What a ripped fascia leaves on the lawn: offcuts scattered near where the
|
||||
# run came down, every board flat on the grass.
|
||||
debris = []
|
||||
for i, (dx, dy, ln, ang) in enumerate(((1.35, -1.15, 0.85, 22),
|
||||
(3.25, -1.58, 0.55, -34),
|
||||
(2.15, -0.92, 0.34, 63))):
|
||||
debris.append(add_box(f"{name}_fascia_bit_{i}", (ln, 0.20, 0.035),
|
||||
(dx + rng.uniform(-0.08, 0.08),
|
||||
dy + rng.uniform(-0.08, 0.08), 0.0175), trim,
|
||||
rot=(0, 0, math.radians(ang + rng.uniform(-6, 6)))))
|
||||
join_group(debris, "debris_fascia", root)
|
||||
|
||||
stamp(root, name, "structure")
|
||||
root["broken_variant_of"] = "house_yardside"
|
||||
root["facade_width"] = W
|
||||
# Same price as the state it used to be, so scoring can read either —
|
||||
# the carport wreck's rule, verbatim.
|
||||
root["collateral_key"] = "gutter"
|
||||
root["collateral_value"] = GUTTER_COLLATERAL
|
||||
root["collateral_label"] = "the gutter"
|
||||
return root
|
||||
|
||||
|
||||
@ -2277,6 +2421,15 @@ ASSETS = [
|
||||
nodes=["wall", "door", "window", "roof", "fascia", "gutter",
|
||||
"window_glow", "window_light_anchor",
|
||||
"fascia_anchor_01", "fascia_anchor_02", "fascia_anchor_03"]),
|
||||
# The torn-gutter aftermath (SPRINT12 §gate 3.3). Same height as the intact
|
||||
# house — a house doesn't fall, only its eave line does — but DEEPER (y),
|
||||
# because the right gutter run and the fascia offcuts are on the grass out
|
||||
# in the yard. If y ever measures ~1.2 again someone un-tore it.
|
||||
dict(name="house_yardside_wrecked", fn=build_house_yardside_wrecked,
|
||||
dims=((9.0, 9.9), (1.7, 2.6), (2.8, 3.3)),
|
||||
nodes=["wall", "door", "window", "roof", "window_glow",
|
||||
"fascia_torn", "gutter_torn", "gutter_down",
|
||||
"downpipe_loose", "debris_fascia"]),
|
||||
# site_02 (corner block): the anchor trap, not just dressing.
|
||||
dict(name="carport_01", fn=build_carport_01,
|
||||
dims=((3.0, 3.5), (5.2, 5.8), (2.4, 2.8)),
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 3.5 MiB |
Binary file not shown.
BIN
web/world/models/house_yardside_wrecked_v1.glb
Normal file
BIN
web/world/models/house_yardside_wrecked_v1.glb
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user