nx_wasm_craft_lookup_candidate.nx source
↩ module page · 4671 lines · 262144 B
1// nx_wasm_craft.nx -- NISHI CRAFT: a GENERATED 3D VOXEL WORLD, playable first-person. The operator's
2// 2026-05-19 vision ("build minecraft equivalent on the lowest possible hardware") finally EMITTING:
3// procedural terrain (integer value-noise heightmap, biome banding, water, trees) in a 64x32x64 block
4// grid, rendered by a per-pixel integer DDA raycast (true 3D: block faces, face shading, distance fog,
5// sky), walked in first person (collision, auto-step-up, water float), and EDITED live (break/place at
6// the crosshair). Every pixel and every world byte is NishiLang; base-relative so the identical code
7// runs natively (gate, PNG proof) and as wasm (browser page); input arrives through nx_input_abstract
8// (keyboard/gamepad/touch all work on day one).
9//
10// Controls (via the input layer's semantic actions): W/S or Up/Down = walk, A/D or Left/Right = turn,
11// R/F = look up/down (bound to the MENU/PAUSE bits -- the 8-action mask is fully spent; widening it
12// to 16 actions is the filed follow-up), E = break block, B = place stone. Touch d-pad works: center
13// tap = break.
14//
15// license_tier: ORIGINAL
16import "nx_syscalls.nx"
17import "nx_itrig.nx"
18import "nx_input_abstract.nx"
19import "nx_atmosphere.nx"
20import "nx_weather_lib.nx"
21import "nx_softbind.nx"
22import "nx_skeleton.nx"
23import "nx_skin_ita.nx"
24import "nx_nxa_anim_lib.nx" // GE31/GE23: the ONE NXA clip evaluator -- the cast's joint rows are computed HERE, not in page JS
25import "_hdl_build/nx_entity_store.nx"
26import "_hdl_build/nx_gamesave_core.nx"
27import "nx_indoorgen_data.nx"
28// GE14a: the PURE staged-terrain core (six parameter fields -> spline -> relief/erosion).
29// Allocator-free and syscall-free, which is the only reason it can live in this wasm target --
30// nx_worldpipe's hydrology and erosion BAKES allocate and stay NAS-side (rung GE14b).
31import "nx_worldpipe_core.nx"
32// THE ONE GEN-TIME RECIPE ALLOWLIST (2026-09-04): init_impl_v's gok9 chain, nx_world_snap and nx_game_page_emit's
33// NXGP passes all read genp_gen_ok from this lib, so an index admitted to the engine reaches the instrument and
34// the page in the same edit. Pure arithmetic, wasm-safe. nx_wasm_craft_gate T90 pins every P_* slot to it.
35import "nx_genp_allow_lib.nx"
36// GE32: frame-budget telemetry -- the window, percentiles and spike count are computed HERE from the raw
37// rAF interval the page feeds through perf_frame, so the served HUD and the pacing referee gate read ONE
38// ruler. Allocation-free and syscall-free like everything else in this target.
39import "nx_perf_lib.nx"
40import "nx_beach_release_recipe.nx"
41const WATER_MAGIC_16777216: i64 = 16777216
42const EJRN_HASH_MUL: i64 = 40503
43const WATER_MAGIC_33554431: i64 = 33554431
44const TERRAIN_HASH_X: i64 = 374761393
45const TERRAIN_HASH_Z: i64 = 668265263
46const TERRAIN_HASH_SEED: i64 = 1442695041
47const SAVE_CK_MASK: i64 = 4611686018427387903
48const TERRAIN_HASH_MIX: i64 = 1274126177
49const RAY_HUGE: i64 = 1099511627776
50const WATER_MAGIC_65536: i64 = 65536
51const WATER_MAGIC_4096: i64 = 4096
52const WATER_MAGIC_1048576: i64 = 1048576
53const WATER_MAGIC_1536: i64 = 1536
54const WATER_MAGIC_6144: i64 = 6144
55const GENE_MASK_TEMPERAMENT: i64 = 57344
56const SAVE_CK_SEED: i64 = 1469598103
57const SAVE_CK_PRIME: i64 = 1099511
58const WATER_MAGIC_2400: i64 = 2400
59const WATER_MAGIC_2500: i64 = 2500
60const WATER_MAGIC_1400: i64 = 1400
61const CRAFT_DEFAULT_SEED: i64 = 20260728
62
63// ★4K-CLASS NATIVE, SCALED TO THE HARDWARE (operator 2026-08-14: "significantly worse ... up and
64// beyond 4k resolution ... scaling to the available hardware"). The WebGL path used to render at true
65// device resolution while this rasteriser was pinned at 480x300 and CSS-stretched to 1920 -- a 4x linear
66// upscale, which is exactly what "significantly worse" was.
67// W/H are the FRAMEBUFFER, and the renderer derives rw=W/q, rh=H/q, foc=W/(2q) -- so FOV is invariant
68// under q BY CONSTRUCTION and the adaptive controller is the thing that scales to the hardware.
69// 1920x1200 keeps the old 16:10 aspect EXACTLY, so framing is unchanged -- it is 4x the linear detail
70// and 16x the AREA of the 480x300 this replaces.
71// ★THE SAFETY PROPERTY THAT MAKES THIS NON-REGRESSIVE: at q=8 this is rw=240, rh=150, foc=120 --
72// byte-for-byte the old default. The initial q is 8, so the first frame costs what it always did; the
73// controller only refines toward q=1 (one ray per pixel) when frames measure under 11ms.
74// ★CEILING CHOSEN FROM THE MEMORY ENVELOPE, NOT TASTE: pixels are i64 because this language has no
75// 32-bit pointer type (MEASURED: 0 hits for `as *u32` across 23,072 sources, coverage complete), so
76// the framebuffer costs W*H*8 = 18.4 MB. A 3840x2400 ceiling would cost 73.7 MB to expose a q=1 that
77// is unreachable anyway -- 9.2M DDA rays per frame is seconds, not milliseconds. Real 4K needs 4-byte
78// pixels plus SIMD, not a bigger constant here; raising W alone buys memory, not detail.
79// ★THE CEILING IS BOUNDED BY THE WASM LINEAR MEMORY, AND THAT BOUND IS NOT NEGOTIABLE BY TASTE.
80// The WAT backends size linear memory FROM THIS MODULE (2026-08-18): they read the
81// `static nx_wasm_pages_req` contract row below at emit time, so the page count has ONE
82// owner (this file) instead of the three drifting copies that shipped 192-vs-364 and
83// fail-closed the lane. (The AHEAD reconciliation that gated this: the 21 "lost" strings
84// were superseded rewords of nx_parse diagnostics, proven live by fixture.) A 1920x1200 framebuffer at O_FB needs
85// 3,590,528 + 18,432,000 = 22,022,528 bytes, i.e. 9.4 MB PAST THE END, so every render trapped with
86// "index out of bounds" and /craft went black. Shipped and caught in production 2026-08-14.
87// 1200x750 keeps the 16:10 aspect and needs 3,590,528 + 7,200,000 = 10,790,528, leaving 1,792,384
88// bytes of headroom inside the same 192 pages. It is still 2.5x the linear detail and 6.25x the AREA
89// of the 480x300 this replaced.
90// ★AND IT KEEPS EXACT PARITY WITH THE OLD DEFAULT: at Q_START=5, rw = 1200/5 = 240, rh = 750/5 = 150,
91// foc = 1200/(2*5) = 120 -- byte-for-byte the pre-change frame, so no machine is worse off.
92// WASM_MEM_BYTES below mirrors the backend's page count so nx_wasm_craft_gate can REFUSE a ceiling
93// that does not fit; the real remedy is for the backend to size pages from the module's own arena
94// rather than a constant, and that is owed.
95// ★THE RENDER CEILING IS THE PAGE'S OWN DISPLAY CAP, NOT A TASTE (operator 2026-08-17: "why these
96// arbitrary resolution rates"): the emitted page has always capped the canvas at min(98vw,1920px) --
97// the ceiling below and that cap were TWO constants describing ONE thing, and at 1200 the render
98// ceiling silently bound first on every desktop display. Now the ceiling IS the cap (1920 wide at
99// the shipped 16:10 aspect); which fraction of it a device actually renders is decided PER DEVICE by
100// the adaptive controller against that device's own measured budget -- no resolution number below
101// this line is chosen by hand.
102const W: i64 = 1920
103const H: i64 = 1200
104const WASM_PAGE_BYTES: i64 = 65536
105// pages = ceil(CRAFT_TOTAL / page) + the previously PROVEN slack. ceil alone is 339 (339*65536 =
106// 22,216,704 >= CRAFT_TOTAL 22,216,352); the +25 pages carry forward the old configuration's
107// measured-working headroom class (~1.6MB) as the runtime/shim allowance -- honestly UNMEASURED
108// whether anything needs it, so it is kept, named, and gate T60 pins the fit while the wasm-vm
109// gate's PAINTS tooth proves the module actually runs inside what it declares.
110const WASM_PAGES: i64 = 364
111const WASM_MEM_BYTES: i64 = WASM_PAGES * WASM_PAGE_BYTES
112// MODULE-OWNED PAGE SIZING, BLOCKED AT THE ENCODER (2026-08-18): the WAT backends
113// (wat_mem_pages in nx_wasm/nx_wasm_scalararm) READ a module-declared
114// `static nx_wasm_pages_req: i64 = WASM_PAGES` and size linear memory from it --
115// mechanism shipped and bite-proven. But nxasm_x86 refuses ANY nonzero-init
116// writable static (G3_NONZERO_INIT_STATIC_UNSUPPORTED_BOTH_LANES: GNU .rodata
117// would fault the first write, nxasm ignores .data wholesale), so declaring it
118// here breaks every NATIVE importer of this engine (the gate). Until nxasm
119// grows .data placement (debt filed with this exact spec), the page count rides
120// the backends' WAT_MEM_PAGES_DEFAULT and the vm-gate's FITS tooth is the
121// drift catcher -- it refused the 192-era wasm and will refuse the next drift.
122// DO NOT re-add the static until `nx_sov_build_run nx_wasm_craft_gate` compiles.
123const RES: i64 = 2 // ray per RESxRES pixel block (240x150 rays)
124const RW: i64 = 240
125const RH: i64 = 150
126const FOC: i64 = 120 // ~90 degree fov at RW=240
127// ★THE ADAPTIVE RANGE, WIDENED AT BOTH ENDS so "scale to the hardware" is real in both directions.
128// q divides the framebuffer: q=1 is 1920x1200 (one ray per pixel) and q=24 is 80x50.
129// Q_START=8 reproduces the OLD default exactly (1920/8=240 x 1200/8=150 rays, foc 1920/16=120), so
130// weak hardware is never worse off than it was and strong hardware can now actually be used.
131const Q_MAX: i64 = 24
132const Q_START: i64 = 5
133
134// ★A DEPTH BUFFER THAT COSTS ZERO BYTES. The compare board's skinned-characters-in-world row names its
135// blocker as "seed a depth buffer from the voxel pass so terrain occludes limbs correctly".
136// ⚠THE CONTRACT SYMBOL IS DELIBERATELY NOT SPELLED HERE. The compare generator measures whether the
137// symbol EXISTS in source, and it does not strip comments -- so naming it in prose flipped the published
138// row to LANDED with no capability behind it (measured: coverage jumped 500 to 525 permil on a comment).
139// A completion signal that keys on a name rewards writing the name; a scanner that does not skip comments
140// measures the documentation, not the code. Ship the function, then the row flips honestly.
141// A separate buffer cannot exist here: the WAT backend emits 192 wasm pages = 12,582,912
142// bytes and the arena already uses 10,790,528, leaving 1,792,384 -- while a full-resolution depth plane
143// would need another W*H*8 = 7,200,000. Shipping one would black out /craft exactly as the 1920x1200
144// framebuffer did on 2026-08-14.
145// The room was already there: a pixel is an i64 carrying a 24-bit packed colour, so 40 bits sit unused
146// every frame. The voxel pass ALREADY computes the crossing-t it needs for shading and then throws it
147// away. Depth now rides bits 32..55 of the same word and is stripped in one pass before the frame is
148// published, so no consumer -- page blit, gate frame scans, PNG proof -- ever sees a changed format.
149const FB_COLOR_MASK: i64 = 16777215
150const FB_DEPTH_SHIFT: i64 = 32
151const FB_DEPTH_MAX: i64 = 16777215
152
153// ★THE WIDTH THE HUD PIXEL SIZES WERE AUTHORED AT. Every HUD length below is scaled by rw/HUD_DESIGN_W
154// rather than held as a raw pixel count, for the same reason mob_draw's billboard cap is a fraction: a
155// constant pixel size means a different FRACTION OF THE SCREEN at every resolution, and the canvas is
156// sized to rw x rh and upscaled by the browser, so the fraction is what the player actually sees.
157const HUD_DESIGN_W: i64 = 480
158const HUD_PIP_MAX: i64 = 24
159const HUD_HOTBAR_N: i64 = 7
160
161const WX: i64 = 128 // world blocks (v2: 4x the area, taller)
162const WY: i64 = 48
163const WZ: i64 = 128
164const WATER_H: i64 = 9
165
166// FARMING-LIFE blocks (roadmap B1/B2): wetness lives IN the id and crops are separate stage ids,
167// so the v6 save format is UNTOUCHED -- voxels + the edit journal already persist every farm cell.
168const B_FARM: i64 = 12 // tilled farmland, dry
169const B_CROP0: i64 = 13 // crop sprout (placed by SEEDS onto farmland)
170const B_CROP1: i64 = 14 // crop growing
171const B_CROP2: i64 = 15 // crop MATURE -- breaking it is the HARVEST
172const B_FARMW: i64 = 16 // farmland, wet (rain or water within the 5x5 ring)
173
174// ★THE FRAMEBUFFER MOVED TO THE END OF THE ARENA so it can GROW without shifting a single other
175// offset. Every block below (O_ST .. O_GENP) keeps its absolute address, which is what makes a 16x
176// resolution change a 4-constant edit instead of a 12-offset relayout nobody could verify.
177// The 1,152,000 bytes at offset 0 are now unused and reclaimable -- stated, not hidden.
178const O_FB: i64 = 3592832 // W*H i64 packed R|G<<8|B<<16 (at the arena tail; see CRAFT_TOTAL)
179const O_ST: i64 = 1152000 // 64 state slots
180const O_INPUT: i64 = 1152512 // nx_input_abstract arena (64 words)
181const O_VOX: i64 = 1153024 // WX*WY*WZ u8 blocks
182const O_MOBS: i64 = 1939456 // nx_entity_store arena (certified part): en_bytes(16,7)=1632
183const MOB_CAP: i64 = 16
184const MOB_NCOMP: i64 = 7
185const MOB_BYTES: i64 = 1632
186const O_SPEC: i64 = 1941120 // GAME SPEC block (64 words) -- AFTER the arena's 1632B end
187const O_EJRN: i64 = 1941760 // EDIT JOURNAL: 8192 packed i64 hash slots at ABSOLUTE world
188 // coords (v6 streaming). Reuses the pre-v6 save staging spot.
189const EJRN_SLOTS: i64 = 8192
190const EJRN_BYTES: i64 = 65536
191const O_SAVE: i64 = 2730336 // v6 save image (relocated past MOBAUX): 128B header + voxels
192 // + mob arena + edit journal. The page reads save_off() so the
193 // relocation is invisible to every surface.
194const SAVE_HDR: i64 = 128
195const SAVE_BODY: i64 = 853600 // WX*WY*WZ + MOB_BYTES + EJRN_BYTES
196const SAVE_BODY_V5: i64 = 788064 // pre-streaming body (load-compat: v5 saves still open)
197const SAVE_LEN: i64 = 853728 // SAVE_HDR + SAVE_BODY
198const CRAFT_BYTES: i64 = 3584064 // O_SAVE + SAVE_LEN
199const O_MOBAUX: i64 = 2729952 // 16 mobs x (dx,dz,dy) last-applied step, i64 triples
200 // (384B, fits the 544B rounding slack past the save image;
201 // save format untouched). NON-PERSISTED display truth.
202 // The page's heading/speed used to be a JS derivative of
203 // positions -- a workaround; the ORGAN owns motion, so it
204 // exports the applied step incl the vertical component
205 // (climb cadence needs dy).
206// GAIT-1 -- 16 mobs x 1 word FACING (it-units, IT_PI = a half turn; forward = (sin,cos) in x,z,
207// the SAME convention the camera basis at mob_draw and the wander drive already use, so this
208// engine has ONE yaw convention and not two). 128B in the same free band as O_MOBVY:
209// 2729696 + 128 = O_MOBVY, and O_EJRN ends at 2007296, so 2007296..2729696 is unallocated --
210// the region is carved exactly the way O_MOBVY was and the save format is untouched for the
211// same reason. NON-PERSISTED: a zero arena faces +z, and every write is a deterministic
212// function of arena state, so the T4/T17 two-arena checksums hold by construction.
213// ⚠WHY A STATE WORD AND NOT A DERIVATIVE OF THE LAST STEP: a heading recomputed from the
214// current step every tick IS the wobble. The steering below is an 8-direction bang-bang signal
215// whose per-axis sign can flip in a single tick, so any facing derived from it inherits every
216// one of those flips. The low-pass has to live somewhere; this is that somewhere, one word.
217const O_MOBHDG: i64 = 2729696
218const O_MOBVY: i64 = 2729824 // GE12: 16 mobs x 1 word VERTICAL VELOCITY Q8/tick (128B, the slack
219 // directly BEFORE O_MOBAUX: 2729824 + 128 = O_MOBAUX; free since
220 // O_EJRN ends at 2007296). NON-PERSISTED: a zero arena is AT REST
221 // by construction and every write derives from deterministic
222 // state, so the T4/T17 two-arena checksums hold.
223const O_SOFT: i64 = 3584064 // A4 soft-body springs: 16 mobs x SB_PTS(6, R7 regions) x SD_STRIDE words.
224 // NON-PERSISTED display truth (pure function of root motion;
225 // re-seated on init/load/shift/summon -- the MOBAUX pattern).
226const O_NEED: i64 = 3588672 // C1 needs: 16 mobs x 4 words (hunger, energy, social, spare).
227 // NON-PERSISTED; re-seeded deterministically on init/load.
228const O_MIND: i64 = 3589184 // C3/C5 THE INNER LIFE: 16 girls x 24 words -- 8 THOUGHT
229 // slots (id<<8 | mag+128, expiry-tick) then 4 GOSSIP slots
230 // (subj<<16 | pred<<8 | conf, born-tick). NON-PERSISTED;
231 // every write derives from deterministic state, so the
232 // T4/T17 two-arena checksums hold by construction.
233const O_PASS: i64 = 3592256 // THE TRAVELER'S PASSPORT (the isekai carry): the
234 // CHARACTER crosses worlds while each world keeps its own
235 // v6 save. Wire = 12 words (magic ver charseed broken
236 // placed harv plant found lost ticks worldmask ck);
237 // words 16-24 = the in-arena delta SNAPSHOT so a world
238 // reload never double-counts a deed into the ledger.
239const PASS_MAGIC: i64 = 20260802001
240const PASS_LEN: i64 = 192 // 24 wire words: [0]magic [1]ver [2]charseed [3]broken
241 // [4]placed [5]harv [6]plant [7]found [8]lost [9]ticks
242 // [10]worldmask [11]party-count [12..20]=3x(kind,disg,
243 // gene) -- COMPANIONS CROSS WORLDS with their bond and
244 // bred genome intact -- [21]ck. Snapshot at words 32+.
245const O_GENP: i64 = 3592640 // W1c GEN-TIME RECIPE OVERRIDES (192B): [0] magic, [1] n,
246 // then idx,val pairs x11. Written by the page (NXGP block)
247 // or a native harness BEFORE init_v; init applies them
248 // AFTER wc_spec identity defaults and BEFORE genworld, so
249 // terrain-shape rows (0..6, 59, 60) become world DATA.
250 // Zero region (fresh arena) = no overrides by construction.
251const GENP_MAGIC: i64 = 20260813001
252const GENP_MAXP: i64 = 11
253// ★WRITTEN AS THE ARITHMETIC, NOT AS A TYPED-OUT TOTAL. The framebuffer TAILS the arena, so the
254// total is the pre-framebuffer arena plus the frame itself. Typing the sum by hand is what let the
255// ceiling silently outgrow the wasm linear memory: a hand-computed 22,022,528 looks exactly as
256// authoritative as a correct one. As arithmetic it re-derives itself whenever W, H or O_FB move,
257// and nx_wasm_craft_gate asserts it against WASM_MEM_BYTES so it can never again exceed the module's
258// own memory without the gate going RED first.
259// ★R21 SKINNED-CHARACTER RIG REGION (wc_mob_mesh). ONE rig per arena, shared by every girl: geometry is
260// species data, pose+genome are per-girl -- the same split the body canon uses (species = parameters,
261// individuals = seeded variation). The rig TAILS the framebuffer so every existing offset is untouched.
262const O_RIG: i64 = O_FB + W*H*8
263// == sk_bytes() (256 + 32*256 + 2048*64 + 2048*24 + 64). A const cannot call a func, so the value is
264// written out ONCE here and the gate asserts RIG_BYTES == sk_bytes() -- the derived-constant discipline:
265// the duplicate exists, and it CANNOT drift without a RED.
266const RIG_BYTES: i64 = 188736
267const O_PROJ: i64 = O_RIG + RIG_BYTES
268const PROJ_MAXV: i64 = 211 // projected-vertex slots == RIG_VERT_TOTAL (declared beside the
269 // rig tables; a const cannot forward-reference, so the value is
270 // written out ONCE here and gate T67 asserts the two are EQUAL
271 // and that the built rig fits -- the same derived-constant
272 // discipline as RIG_BYTES/T62. A silent overflow here is the
273 // pmboard zero-byte class: the projection loop would write past
274 // O_PROJ into the skin scratch with no error at all.
275const PROJ_BYTES: i64 = PROJ_MAXV * 24
276const O_SKIN3: i64 = O_PROJ + PROJ_BYTES // 3-word sRGB scratch for nx_skin_ita's out3 (wasm has no
277 // mmap; every buffer is an arena offset)
278// =====================================================================================================
279// H1 -- STRAND GUIDES IN THE ENGINE (2026-08-31). Operator standing order: "i dont want js powering any of
280// this it should be emitting all the way from the first byte up via nishi lang and nishi ecosystem". The
281// per-girl hair guide simulation -- rest curves from the asset's own HSTR section, colliders FITTED from the
282// asset's own rig (VERT/SKEL/DYNA), a per-tick solver (spring-to-rest + damping + gravity + the world's wind
283// field + the girl's own motion as an inertial drag), follow-the-leader length projection alternating with
284// capsule push-out, DFTL velocity correction, and the 2-nearest-guide render weights -- lives HERE as arena
285// state. The page may do exactly two things with it: copy the asset's raw section BYTES into the scratch
286// below once at load (never a computed value), and copy the export block OUT to a GL texture each frame.
287// Every number a visitor sees on a strand is computed in this file.
288// UNITS: the NXA's bind (mesh) space, z up, integers. Blocks -> bind units through the page's own pairing
289// (HG_UNITS_PER_178CM per 1.78 m of stature; GPE_UNITS_PER_178CM in the emitter is the same pairing).
290// TICK: HG_TPS per second -- the fixed sim cadence the page's accumulator steps (16.6667 ms) and
291// nx_hairdyn_lib's hd_ticks_per_s() derives (sqrt(SB_MHZ_TICK_SQ)/1000 = 60).
292// PLANT: SB_K_HAIR / SB_C_HAIR over SD_G -- the gate-pinned hair plant of nx_softbind, unchanged, so every
293// guide answers with the stiffness and damping the retired one-chain page solver had.
294// FRAME: each guide is solved in HER bind space with the root pinned to its rest root; her world step
295// enters as the viscous drag a world-frame damper exerts (the incumbent lag at walking speed), her heading
296// rotates the world wind and step into bind space (the page's own R(-hdg).(m.x,m.z,-m.y) composition).
297// Colliders are the BIND-pose body (the same v1 limit the dress overlay declares: an extreme swing can clip).
298// =====================================================================================================
299const WC_CAST_N: i64 = 12 // the cast genmobs places (placed < WC_CAST_N); hair state is sized to it
300const HG_MOBS: i64 = WC_CAST_N
301const HG_NP: i64 = 8 // points per HSTR strand: the section contract (GR_PTS in nx_nxa_groom)
302const HG_W: i64 = HG_NP*3 // words per strand (GR_WORDS)
303const HG_NSEG: i64 = HG_NP - 1
304const HG_REND_MAX: i64 = 1200 // render strands: the page's shipped NXH_GUIDES budget, PRESERVED and moved here
305const HG_SIM: i64 = 128 // simulated guides per girl. PICKED, named as picked: 1200/128 = 9.4 render
306 // strands per guide sits inside the guide/follow band real-time hair ships
307 // (TressFX: 4..16 follow hairs per simulated guide); bounded above by the
308 // arena fit (T60) and the WebGL2 texture floor (HG_MOBS*HG_SIM/HG_TEX_SR rows)
309const HG_TEX_SR: i64 = 4 // strands per guide-texture row: rows = strands/4 keep every hair texture under
310 // the WebGL2 MAX_TEXTURE_SIZE minimum (2048) at HG_REND_MAX and HG_MOBS*HG_SIM
311const HG_TAB_W: i64 = 8 // render-strand table row: [rest-idx nb1 nb2 w1 w2 lockw spanz 0]
312const HG_CAP_MAX: i64 = 32 // collider slots (mask bits): head neck 2 shoulders 2 arms torso + DYNA anchors
313const HG_CAP_W: i64 = 10 // ax ay az bx by bz r kind sx sy
314const HG_KIND_CAP: i64 = 0
315const HG_KIND_ELL: i64 = 1
316const HG_Q12: i64 = WATER_MAGIC_4096 // ellipse-scale / segment-parameter fixed point: the engine's fx4096
317const HG_SKEL_W: i64 = 8 // words per SKEL joint: parent x y z + 4 (the 64-byte joint record the page reads)
318const HG_DYNA_MINW: i64 = 8 // a DYNA row carries side x y z k c max infl before an anchor is readable
319const HG_DYNA_MAXW: i64 = 64 // a row wider than this is not this format (nx_nxa_dyna writes 12); bounds the count check
320const HG_DYNA_INFL: i64 = 7 // DYNA word: influence radius (nx_nxa_dyna payload layout)
321const HG_HDR_BYTES: i64 = 512
322const HG_H_LOADED: i64 = 0
323const HG_H_NSTR: i64 = 1
324const HG_H_NREND: i64 = 2
325const HG_H_NSIM: i64 = 3
326const HG_H_SKIN: i64 = 4
327const HG_H_LOCKW: i64 = 5
328const HG_H_NCAP: i64 = 6
329const HG_H_SEGLEN: i64 = 7
330const HG_H_SPAN: i64 = 8
331const HG_H_TICKS: i64 = 9
332const HG_H_PUSHES: i64 = 10
333const HG_H_INSIDE: i64 = 11
334const HG_H_MAXDISP: i64 = 12
335const HG_H_H: i64 = 13
336const HG_H_ZMN: i64 = 14
337const HG_H_HJ: i64 = 15
338const HG_H_RC: i64 = 16
339const HG_H_NOCOLL: i64 = 17 // gate lever: 1 skips the push-out (the neg-control that proves it fires)
340const HG_H_RESTIN: i64 = 18 // rest nodes inside a collider BODY at load: the clipping the operator sees at rest
341const HG_H_WINDOFF: i64 = 19 // gate lever: 1 ignores the wind field
342const HG_H_GUST: i64 = 20
343const HG_H_TICKED: i64 = 21 // girls ticked on the last hairtick
344const HG_RC_NOSTR: i64 = 0 - 1 // load refusals, each naming its conjunct
345const HG_RC_HSTRCAP: i64 = 0 - 2
346const HG_RC_NOVERT: i64 = 0 - 3
347const HG_RC_VERTCAP: i64 = 0 - 4
348const HG_RC_NOSKEL: i64 = 0 - 5
349const HG_RC_SKELCAP: i64 = 0 - 6
350// arena regions: tail the skin scratch, every byte accounted, CRAFT_TOTAL re-derived below
351const O_HGHDR: i64 = O_SKIN3 + 24
352const O_HGREST: i64 = O_HGHDR + HG_HDR_BYTES
353const O_HGRL: i64 = O_HGREST + HG_REND_MAX*HG_W*8
354const O_HGCAP: i64 = O_HGRL + HG_SIM*HG_NSEG*8
355const O_HGMASK: i64 = O_HGCAP + HG_CAP_MAX*HG_CAP_W*8
356const O_HGSIMIDX: i64 = O_HGMASK + HG_SIM*8
357const O_HGTAB: i64 = O_HGSIMIDX + HG_SIM*8
358const HG_BLK_BYTES: i64 = HG_MOBS*HG_SIM*HG_W*8
359const O_HGX: i64 = O_HGTAB + HG_REND_MAX*HG_TAB_W*8 // positions
360const O_HGP: i64 = O_HGX + HG_BLK_BYTES // previous positions (velocity = X - P)
361const O_HGD: i64 = O_HGP + HG_BLK_BYTES // THE EXPORT: displacement from rest, the buffer the page copies per frame
362const O_HGSCR: i64 = O_HGD + HG_BLK_BYTES // solver scratch: correction (HG_W) + collider probe (6) + point (3)
363const HG_SCR_WORDS: i64 = HG_W + 9
364// GE32 (2026-09-02): the frame-budget telemetry window -- PF_WORDS i64 owned by nx_perf_lib, fed by the page's
365// perf_frame door once per frame, read back through the perf_* doors. Tails the hair scratch; CRAFT_TOTAL below.
366const O_PERF: i64 = O_HGSCR + HG_SCR_WORDS*8
367// GE30 PER-WORKER TILE SCRATCH. The ray pass used ONE scratch (O_ST slots 46-50) for its five wray
368// outputs, which is correct for one thread and a DATA RACE the instant a second worker renders a band
369// of the same frame: both would write the same five words and each would read the other's crossing-t.
370// Every worker therefore gets its own TILE_W-word slot -- camera basis at +0 (8 words, written by
371// wc_cam so the band pass and the overlay pass share ONE camera ruler) and the ray outs at +TILE_HOUT.
372// APPENDED at the arena tail, so no existing offset moves and the v6 save image is untouched.
373const O_TILE: i64 = O_PERF + PF_WORDS*8
374const TILE_W: i64 = 32 // i64 words per worker slot
375const TILE_HOUT: i64 = 8 // word offset of the 5 wray outs inside a slot
376const TILE_MAX: i64 = 32 // the most workers a band pass may be cut into; the thread ladder
377 // (knowledge/tier_ladder.conf) picks the count from measured
378 // hardware and the page clamps to this, so it can never ask for a
379 // scratch slot that does not exist.
380// ---- GE55 RAW UNIFORM BLOCK (2026-09-05, operator: no JS, no Python, WebGPU as a pure SUBMISSION PIPE) ----
381// The sim packs its OWN state for the GPU. 40 little-endian 32-bit words, 160 B, APPENDED at the arena tail like
382// every region before it (no existing offset moves). The words are RAW: fixed-point ints, Q12 angles, packed RGB,
383// flags. This lane carries no f32 lowering (measured 2026-09-05: nx_compile_wat has no f32 ops), so every float the
384// shader needs (cam/256, sin and cos of yaw and pitch, the palette light dl2 = .22+.78*pow(df,.6), the sky unpack,
385// the sun direction) is DERIVED ON THE GPU from the one IR (nx_world_shader_src), never in JS and never here.
386// Word index = byte offset / 4, chosen so the WGSL struct {20 scalars, pal as 3 vec4u, palf as 2 vec4u} lays out
387// IDENTICALLY under the uniform address-space rules (nx_wgsl layout is the ONE ruler; a gate pins both sides).
388// GE53 THE SURFACE SOURCE: one Q8 height per column (WX x WZ i64 slots), filled by gencol from the SAME
389// eroded, ocean-shaped field the voxel column is cut from, BEFORE the one line that rounds it to whole
390// blocks. Under P_SURF the raycaster marches this field (bilinear between columns = sub-block precision)
391// instead of the cell grid, and the page uploads it as a texture so every tier reads ONE surface. Placed
392// BEFORE the raw block so CRAFT_TOTAL == O_URAW + URAW_BYTES (T91) still holds; 128 KB inside T60's headroom.
393const O_HQ: i64 = O_TILE + TILE_MAX*TILE_W*8
394const HQ_BYTES: i64 = WX*WZ*8
395const O_URAW: i64 = O_HQ + HQ_BYTES
396const URAW_WORDS: i64 = 44 // GE53: 41 words (r_surf at 40) padded to the vec4u boundary
397const URAW_BYTES: i64 = 176 // URAW_WORDS*4: a vec4u-aligned struct, so the 3-word tail pad is real (was 160 beside WORDS=44 -- pack_uniforms zeroed 176 B into a 160 B block; corrected 2026-09-06 when the GE53 shader shipped its 41-word struct)
398const UR_CAMX: i64 = 0 // Q8 world units (the page used camx()/256)
399const UR_CAMY: i64 = 1
400const UR_CAMZ: i64 = 2
401const UR_TNOW: i64 = 3
402const UR_YAW: i64 = 4 // Q12 radians (the page used yaw()/4096)
403const UR_PITCH: i64 = 5
404const UR_SUNEL: i64 = 6 // Q12 sine of the sun elevation (cal_sunel)
405const UR_DAYT: i64 = 7
406const UR_DLEN: i64 = 8
407const UR_RAIN: i64 = 9
408const UR_SKT: i64 = 10 // packed RGB (spec word 7)
409const UR_SKH: i64 = 11 // packed RGB (spec word 8)
410const UR_SUN: i64 = 12 // spec word 9 (the GPU applies the df>.04 daylight guard)
411const UR_CLD: i64 = 13 // spec word 10
412const UR_WMAX: i64 = 14
413const UR_SELB: i64 = 15 // sel_block(S_SEL)
414const UR_HOK: i64 = 16
415const UR_NPCT: i64 = 17 // npc target t, or -1
416const UR_BROKEN: i64 = 18 // edit counters (broken + placed): the bridge refreshes the voxel texture when their sum moves
417const UR_PLACED: i64 = 19
418const UR_PAL: i64 = 20 // 12 packed RGB words (spec 16..27), vec4u-aligned at byte 80
419const UR_PALF: i64 = 32 // 5 packed RGB words (spec 34..38) at byte 128
420const UR_RESW: i64 = 37 // canvas width in px -- the door tells the sim through set_res (the sim has no window)
421const UR_RESH: i64 = 38
422const UR_SURF: i64 = 40 // GE53: the ground-is-the-field word (sp[P_SURF]); words 41..43 are the vec4u tail pad
423const UR_YFLIP: i64 = 39 // 1 on the WebGPU door (top-left fragment origin), 0 on WebGL2 -- set_yflip
424const CRAFT_TOTAL: i64 = O_URAW + URAW_BYTES
425// ---- GE31/GE23 THE CAST CLIP EXTENSION (2026-09-06) --------------------------------------------
426// The ENGINE evaluates the cast's animation clip (nx_nxa_anim_lib) from each girl's own path-length
427// stride phase and exports the finished dual-quaternion joint rows; the page's animEval became a copy.
428// WHY AN EXTENSION BEYOND CRAFT_TOTAL RATHER THAN A REGION INSIDE IT: (1) T91 pins O_URAW as the arena
429// tail and T60 pins CRAFT_TOTAL inside the backend's DECLARED pages; (2) the vm-gate runs init and
430// render inside those declared pages, and nothing here is touched until the page has grown memory
431// and called nxa_ingest; (3) the clip does not fit anyway -- ref9e's ANIM section is 113,465 words
432// (nx_asset_floor_gate, 2026-09-06), 887 KB against a 282 KB headroom, so a region inside the base
433// arena would have been a lie about the asset. The backend declares WASM_PAGES initial pages and cannot
434// read a module static (G3, see WASM_PAGES above), so the size gets ONE owner the honest way: the
435// engine exports wasm_pages_req() DERIVED from CRAFT_EXT_TOTAL and the page grows memory to it before
436// init -- the MT twin already declares max = 2x, so growth is inside its ceiling by construction.
437// Every cap below is named for its purpose, derived from the SHIPPED asset, and refused BY NAME in
438// wc_nxa_ingest when exceeded; an asset over a cap is the next ladder rung, never a silent truncation.
439const NXA_NJ_CAP: i64 = 128 // ref9e 104 joints; the corpus tops out at 370 (nx_nxa_play) = the next rung
440const NXA_ANIM_CAP_W: i64 = 131072 // 1 MiB of key words; ref9e ANIM measured 113,465 words
441const NXA_POSE_CAP_W: i64 = 2048 // ref9e POSE measured 835 words (one frozen stand pose)
442const NXA_HDR_W: i64 = 16
443const NXA_H_LOADED: i64 = 0
444const NXA_H_NJ: i64 = 1
445const NXA_H_DUR: i64 = 2
446const NXA_H_MPB: i64 = 3
447const NXA_H_RJ: i64 = 4
448const NXA_H_POSE: i64 = 5
449const NXA_H_RV: i64 = 6 // six words: x0 y0 vx16 vy16 t0 span (nx_nxa_anim_lib NA_RV_W)
450const NXA_H_RC: i64 = 12
451const NXA_H_NAW: i64 = 13
452const NXA_MOB_W: i64 = 4 // per girl: idle weight q12, clip-time adjustment ms, two spare
453const NXA_M_IW: i64 = 0
454const NXA_M_CTADJ: i64 = 1
455// the page's SHIPPED idle behaviour, carried not re-chosen (nx_game_page_emit castFrame, 2026-08-30):
456// pose-hold eases in at 5/s and out at 2.5/s, the clip eases to the nearest double-support at 6/s
457// while the hold blends in below 0.95, and a hold under 0.004 snaps to zero.
458const NXA_IW_IN_TENTHS_PER_S: i64 = 50
459const NXA_IW_OUT_TENTHS_PER_S: i64 = 25
460const NXA_SNAP_TENTHS_PER_S: i64 = 60
461const NXA_SNAP_IW_MAX_Q12: i64 = 3891
462const NXA_IW_FLOOR_Q12: i64 = 16
463const NXA_DT_MS_MAX: i64 = 1000 // a tab that slept a minute eases one second, it does not slam
464const NXA_RC_NOT_LOADED: i64 = 0 - 1
465const NXA_RC_NO_MOB: i64 = 0 - 2
466const NXA_RC_SLOT: i64 = 0 - 3
467const NXA_RC_NJ_ZERO: i64 = 0 - 11
468const NXA_RC_NJ_CAP: i64 = 0 - 12
469const NXA_RC_NAW_ZERO: i64 = 0 - 13
470const NXA_RC_NAW_CAP: i64 = 0 - 14
471const NXA_RC_NPW_CAP: i64 = 0 - 15
472const NXA_RC_POSE_OVERRUN: i64 = 0 - 16
473const NXA_RC_TRACKS: i64 = 0 - 20 // plus nx_nxa_anim_lib's own rc: -21 bad tracks, -22 overrun
474const O_NXA: i64 = CRAFT_TOTAL
475const O_NXA_SKEL: i64 = O_NXA + NXA_HDR_W*8
476const O_NXA_TIDX: i64 = O_NXA_SKEL + NXA_NJ_CAP*NA_SKEL_W*8
477const O_NXA_PQ: i64 = O_NXA_TIDX + NXA_NJ_CAP*NA_IDX_W*8
478const O_NXA_PD: i64 = O_NXA_PQ + NXA_NJ_CAP*4*8
479const O_NXA_MOB: i64 = O_NXA_PD + NXA_NJ_CAP*3*8
480const O_NXA_SCR: i64 = O_NXA_MOB + WC_CAST_N*NXA_MOB_W*8
481const O_NXA_OUT: i64 = O_NXA_SCR + NA_SCR_W*8
482const O_NXA_POSE: i64 = O_NXA_OUT + WC_CAST_N*NXA_NJ_CAP*NA_ROW_W*8
483const O_NXA_ANIM: i64 = O_NXA_POSE + NXA_POSE_CAP_W*8
484// Append the ordering sidecar so all existing asset and output offsets remain stable.
485const O_NXA_ORDER: i64 = O_NXA_ANIM + NXA_ANIM_CAP_W*8
486const CRAFT_EXT_TOTAL: i64 = O_NXA_ORDER + NXA_NJ_CAP*8
487// the engine's OWN page request -- derived, the one place the extended size is written
488const WASM_PAGES_REQ: i64 = (CRAFT_EXT_TOTAL + WASM_PAGE_BYTES - 1)/WASM_PAGE_BYTES
489// LOAD-TIME INPUT SCRATCH inside the framebuffer: the page copies the asset's raw HSTR/VERT/SKEL/DYNA section
490// bytes here and calls hairload, which consumes them before it returns; the next render overwrites the frame.
491// The caps are addresses inside a region the engine already owns (W*H*8 = 18.4 MB), not guesses about an
492// asset: DYNA 1 MiB holds 10,922 rows, HSTR 4 MiB 21,845 strands, VERT 8 MiB 349,525 vertices, SKEL 2 MiB
493// 32,767 joints. hairload REFUSES by name when a count exceeds its cap rather than reading past it.
494const HG_MIB: i64 = 1048576
495const HG_SCR_HSTR: i64 = O_FB
496const HG_SCR_HSTR_CAP: i64 = 4*HG_MIB
497const HG_SCR_VERT: i64 = O_FB + 4*HG_MIB
498const HG_SCR_VERT_CAP: i64 = 8*HG_MIB
499const HG_SCR_SKEL: i64 = O_FB + 12*HG_MIB
500const HG_SCR_SKEL_CAP: i64 = 2*HG_MIB
501const HG_SCR_DYNA: i64 = O_FB + 14*HG_MIB
502const HG_SCR_DYNA_CAP: i64 = HG_MIB
503// plant + units
504const HG_TPS: i64 = 60 // see the header: the page accumulator's 16.6667 ms, hd_ticks_per_s()
505const HG_UNITS_PER_178CM: i64 = 171530 // the page's pairing (GPE_UNITS_PER_178CM), named as the same number
506const HG_CM_178: i64 = 178
507const HG_CM_PER_M: i64 = 100
508const HG_UPB: i64 = HG_UNITS_PER_178CM*HG_CM_PER_M/HG_CM_178 // bind units per world block (1 block = 1 m): 96365
509const HG_G_CMS2: i64 = 981 // standard gravity, cm/s^2
510const HG_G: i64 = HG_G_CMS2*HG_UNITS_PER_178CM/(HG_CM_178*HG_TPS*HG_TPS) // 262 units/tick^2 -- the dress lane's NXGD_G derivation
511const HG_FTL_S_PERMIL: i64 = 1000 - SB_C_HAIR*1000/SD_G // DFTL s_damping (Mueller 2012 "Fast Simulation of Inextensible Hair
512 // and Fur", band .9..1): 942, from the plant's own damping, not picked
513const HG_FENCE_DIV: i64 = 4 // the chain's small-angle validity fence: seglen/4 (SD_Q8/4 in the retired page solver)
514const HG_PROJ_ITERS: i64 = 3 // length/collision alternations per point; the LAST op is always the length projection
515const HG_LOCK_W_MIN: i64 = 900 // the ribbon builder's lock half-width floor (w09 = max(900, span*.045)), PRESERVED
516const HG_LOCK_W_PERMIL: i64 = 45
517const HG_SKIN_NUM: i64 = 16 // skin = lockw x 1.6: half-width + the .6 child scatter -- the farthest a render vertex sits off its guide
518const HG_SKIN_DEN: i64 = 10
519const HG_REACH_DIV: i64 = 2 // collider mask reach = strand length / 2: twice the static fence (L/4), for overshoot
520const HG_GUST_TICKS: i64 = 2*HG_TPS // gust envelope cadence 0.5 Hz: the incumbent shader sway (3.1 rad/s), PRESERVED
521const HG_GUST_SALT: i64 = 3400 // whash seed offset (the weather lib salts with seed + 3300)
522const HG_TORSO_GATE_X: i64 = 15000 // the MFS torso gate (axb < 15000.), the same half-width the paint bands use
523// Drillis-Contini stature ratios (permil of the MEASURED stature), the landmarks the page derives its bands from
524const HG_R_HEADBAND: i64 = 885
525const HG_R_HEAD_J: i64 = 955
526const HG_R_ACROMION: i64 = 818
527const HG_R_CHEST: i64 = 720
528const HG_R_HIP: i64 = 520
529const HG_R_ELBOW: i64 = 630
530const HG_R_NECK_LO: i64 = 840
531const HG_R_SH_LO: i64 = 770
532const HG_R_SH_HI: i64 = 860
533const HG_R_ARM_LO: i64 = 600
534const HG_R_ARM_HI: i64 = 800
535const HG_R_ARM_XMIN: i64 = 60
536const HG_R_ELBOW_XMIN: i64 = 50
537
538// C3 thought ids (RimWorld's inspectable-mood law: a mood is a LEDGER, never a scalar mystery)
539const TH_RAIN: i64 = 1
540const TH_FEAR: i64 = 2
541const TH_MET: i64 = 3
542const TH_SLEPT: i64 = 4
543const TH_HUNGRY: i64 = 5
544const TH_SAWTAKEN: i64 = 6
545const TH_HEARD_TAKEN: i64 = 7
546const TH_HEARD_FRIEND: i64 = 8
547const TH_COMPANY: i64 = 9
548// C5 gossip predicates (Talk of the Town's law -- the differentiator no shipped game has)
549const GO_FRIEND: i64 = 1
550const GO_TAKEN: i64 = 2
551const SAVE_MAGIC: i64 = 20260728001
552const SAVE_VER: i64 = 6 // v6 = STREAMING WORLD (origin + edit journal in the image);
553 // v5 still LOADS (origin 0, empty journal); others REFUSED
554const SAVE_VER5: i64 = 5
555// mob components (nx_entity_store comp slots)
556const MC_X: i64 = 0
557const MC_Y: i64 = 1
558const MC_Z: i64 = 2
559const MC_KIND: i64 = 3
560const MC_PH: i64 = 4
561const MC_DISG: i64 = 5 // 1 = a HUMAN girl disguised as a monster girl (the lore:
562 // monster girls took the world; humans hide among them --
563 // her features are a worn costume, not grown)
564const MC_GENE: i64 = 6 // 20-bit APPEARANCE GENOME rolled at spawn -- every girl is
565 // GENERATED, not hand-built: bits 0-2 hair hue, 3-4 hair
566 // length, 5-6 height, 7-8 outfit tint, 9-10 skin tone,
567 // 11-12 feature size. The slot bred inheritance plugs into.
568
569// state slots
570const S_SEED: i64 = 0
571const S_CX: i64 = 1 // camera pos Q8 (256 = one block)
572const S_CY: i64 = 2
573const S_CZ: i64 = 3
574const S_YAW: i64 = 4 // it-units (IT_PI = half turn)
575const S_PITCH: i64 = 5
576const S_T: i64 = 6
577const S_BROKEN: i64 = 7
578const S_PLACED: i64 = 8
579const S_HX: i64 = 9 // crosshair DDA hit cache
580const S_HY: i64 = 10
581const S_HZ: i64 = 11
582const S_HFACE: i64 = 12 // 0 none, 1 +x, 2 -x, 3 +y, 4 -y, 5 +z, 6 -z
583const S_HOK: i64 = 13 // 1 = in reach
584const S_VY: i64 = 14 // vertical velocity Q8/tick (jump + gravity)
585const S_GROUNDED: i64 = 15 // 1 = standing on a surface (jump allowed)
586const S_SEL: i64 = 16 // hotbar slot 0..4 (Q cycles; sel_block maps to a block type)
587const S_LBX: i64 = 17 // last-broken cell (probes/particles; the hit cache retargets
588const S_LBY: i64 = 18 // every frame, so "what did I just break" needs its own slots)
589const S_LBZ: i64 = 19
590const S_Q: i64 = 20 // ray pixel size 2..5 -- ADAPTIVE: the organ tunes itself from
591const S_FMA: i64 = 21 // the surface's reported frame ms (EMA x16); shim only reports
592const S_WMAX: i64 = 22 // highest solid y in the world -- rays above it heading up/flat
593 // are SKY immediately (kills the 110-step horizon marches)
594const S_SPX: i64 = 23 // spawn point (H key + void-fall auto-respawn return here)
595const S_SPY: i64 = 24
596const S_SPZ: i64 = 25
597const S_NPCT: i64 = 26 // crosshair NPC target: dense index of the girl under the
598 // gaze (occlusion-aware, walls block), -1 = none
599const S_FOUND: i64 = 27 // disguised humans FOUND (met + identified = under protection)
600const S_LOST: i64 = 28 // found humans TAKEN by wardens (the cost of not protecting)
601const S_BCD: i64 = 29 // birth cooldown ticks (community pacing)
602const S_WOX: i64 = 30 // WORLD ORIGIN of the window, in blocks (v6 streaming): the
603const S_WOZ: i64 = 31 // arena is a 128x48x128 CACHE over unbounded absolute terrain
604const S_EJDROP: i64 = 32 // edits REFUSED because the journal was full (loud, never silent)
605const S_HARV: i64 = 33 // MATURE crops harvested (the farming loop's payoff counter)
606const S_PLANT: i64 = 34 // seeds planted
607const S_GROWN: i64 = 35 // crops that reached maturity (world truth, gate-readable)
608const S_VAR: i64 = 36 // which world identity this arena runs (the passport's
609 // visited-mask bit; set once at init)
610const S_GENP: i64 = 37
611const S_TGT: i64 = 38 // DISPLAY-DERIVED frame budget, EMA-x16 units (0 = shipped
612 // defaults): the page measures the device's real rAF interval
613 // once and reports it via target_ms; every controller line then
614 // derives from the DEVICE instead of a hand-picked millisecond // W1c: gen-time override rows APPLIED at init (world truth --
615 // a gate can read whether the recipe actually reached genworld)
616const S_BLDG: i64 = 39 // W3 settlements: buildings stamped into this arena (a
617 // monotone stamp-event counter -- a window slide that
618 // repaints a building recounts it; right after init it IS
619 // the number of buildings in the window, which is what the
620 // gate reads). NON-PERSISTED world truth.
621const S_BLDGX: i64 = 40 // last-stamped building anchor, arena-local (gate probe)
622const S_BLDGY: i64 = 41 // its floor height
623const S_BLDGZ: i64 = 42
624const S_RESW: i64 = 44 // GE55: canvas size and origin, written by the door (set_res / set_yflip)
625const S_RESH: i64 = 45
626const S_YFLIP: i64 = 46
627const S_AGEN: i64 = 47
628const S_DPAPPLIED: i64 = 48 // GE59: the P_DAYPHASE value last applied to S_T (0 = never) -- one re-base per distinct poke, never per tick // GE57: arena GENERATION -- +1 per wc_shift that moves. The GPU tiers key every voxel-texture upload on it: a 16-block re-base drawn against the previous texture is the transport/fall-through the operator sees (2026-09-05).
629const S_GT_KEEP: i64 = 43 // ground-truth capture: when 1, render_impl leaves the depth plane in
630 // the framebuffer's high bits instead of stripping it (nx_world_snap
631 // gt verb reads it and emits depth/normal channels). Zeroed by init, so
632 // the page and every gate keep the published colour-only format.
633const SHIFT_STRIDE: i64 = 16 // one window slide, in blocks
634const SHIFT_MARGIN: i64 = 32 // player this close to a window edge -> the world slides under them
635
636// spec slots (the game-identity DATA the engine reads -- nx_voxel_world's style-is-a-spec doctrine)
637const P_TBASE: i64 = 0 // terrain base height
638const P_TAMP1: i64 = 1 // large-noise amplitude
639const P_TAMP2: i64 = 2 // detail-noise amplitude
640const P_WATER: i64 = 3 // flood level
641const P_SNOW: i64 = 4 // snow line (99 = never)
642const P_TREE: i64 = 5 // tree sparsity modulus (0 = no trees)
643const P_CAVETH: i64 = 6 // cave carve threshold (lower = more cavern)
644const P_SKYTOP: i64 = 7 // packed zenith colour
645const P_SKYHOR: i64 = 8 // packed horizon colour
646const P_SUN: i64 = 9 // 1 = sun disc
647const P_CLOUD: i64 = 10 // 1 = cloud deck
648// WEATHER EXPORTS (2026-08-26): written each tick by wc_weather_tick from nx_weather_lib -- the
649// gate-proven continuous system (rates preserved to P_RAINS, front persistence, typed precip,
650// gradient wind). The page reads these five slots exactly as it reads the sky tones.
651const P_WXCOV: i64 = 11 // cloud coverage Q12 (drives the cloud density band)
652const P_WXWINDX: i64 = 12 // wind x component, Q12-scaled speed*cos(dir)
653const P_WXWINDZ: i64 = 13 // wind z component
654const P_WXTYPE: i64 = 14 // precip type this eighth: 0 none, 1 rain, 2 snow
655const P_WXTEMP: i64 = 15 // temperature Q12 (relative scale, freezing=1024)
656const P_PAL: i64 = 16 // +blocktype = packed base colour (0..11)
657const P_MOBC: i64 = 28 // +kind*2+half = packed mob colours (3 kinds x 2 tones)
658const P_PAL2: i64 = 34 // +b-12 = packed colour for farm blocks 12..16 (5 rows, 34-38)
659const P_DAYLEN: i64 = 40 // A1 calendar: ticks per day (ALL timing is spec DATA)
660const P_SEASD: i64 = 41 // days per season (4 seasons cycle)
661const P_GROWE: i64 = 42 // farm-tick cadence: one grow/hydrate pass every N ticks
662const P_GROWW: i64 = 43 // crop speed on WET farmland (Minecraft stencil speed)
663const P_GROWD: i64 = 44 // crop speed on DRY farmland
664const P_RAINS: i64 = 45 // +season = rain permil per day-eighth (4 rows, 45-48)
665// ---------------------------------------------------------------------------------------------
666// ART STYLE IS DATA (2026-09-01). Spec idx 49 -- one of the TWO dead holes this plane has carried
667// since it was laid out (39 and 49; world_recipes.conf names them both). 49 is the safer of the
668// two: P_PAL2's five farm rows 34-38 could plausibly widen by one, while P_RAINS' four rows are
669// four SEASONS and cannot. It sits inside the 7..58 band the recipe plane pokes POST-BOOT, which
670// is where a LOOK belongs -- a style is a property of how the world is drawn, never of how the
671// ground was generated, so it must not become gen-time data.
672// THE VOCABULARY IS ADOPTED, NOT INVENTED. buildroot/runtime/nx_voxel_world.nx line 2 has read
673// "Style is a spec field, NOT a hardcode -- the engine is not locked to one look" since it was
674// written, with const SP_STYLE and STYLE_REAL / STYLE_TOON / STYLE_FLAT, and
675// _hdl_build/nx_world_style_gate.nx already PROVES the three produce three DISTINCT frames from
676// one spec. That organ has SEVEN importers and EVERY ONE OF THEM IS A GATE (measured 2026-09-01,
677// nx_absent matches=8 coverage_complete=1 corpus_complete=1): the doctrine was proven and never
678// shipped. This is that doctrine reaching the SERVED engine, under the SAME three ids so the two
679// implementations cannot drift into two vocabularies for one idea.
680// SCOPE, STATED SO IT CANNOT BE OVERREAD: this is the SHADING axis. The world is still made of
681// voxels -- vox_off() -> O_VOX is still the only world door, and a second REPRESENTATION is a
682// separate, larger contract. A toon or flat world is a voxel world that stops looking photoreal;
683// it is not a world that stopped being cubes.
684// 0 = REAL, and 0 is what wc_spec's zeroing loop (while k < WC_SPECN) already writes for every
685// identity, so every world published before this renders BYTE-IDENTICALLY BY CONSTRUCTION rather
686// than by a preserved branch anyone could later delete.
687const P_STYLE: i64 = 49
688const P_DAYPHASE: i64 = 39 // GE59 (2026-09-05): the boot clock as DATA -- the OTHER dead hole (world_recipes.conf names 39 and 49). Post-boot band, so it cannot be read at init: apply2_impl re-bases S_T to it on the next tick, once per distinct value; 0 (the zeroing loop) leaves the DAYLEN/4 boot phase, so every existing world is byte-identical by construction. If P_PAL2 ever widens, this slot moves with the ONE named const.
689const WC_STYLE_REAL: i64 = 0 // textured, smooth light -- the shipped look, unchanged
690const WC_STYLE_TOON: i64 = 1 // banded light (anime/cel)
691const WC_STYLE_FLAT: i64 = 2 // untextured facets (artsy low-poly)
692const WC_STYLE_SOFT: i64 = 3 // SMOOTH TERRAIN: ground lit by a heightfield normal interpolated across the block (no grid bevel, no per-block facet)
693// SOFT neighbour scan: how far above/below the hit cell a neighbour column's top is searched. Bounded by
694// where the heightfield normal SATURATES -- a rise of 3 blocks over 1 already lights as a near-vertical
695// face, so a taller step is a cliff and reads correctly as the flat fallback. Derived, not tasted.
696const WC_SOFT_SCAN: i64 = 3
697const WC_STYLE_MAX: i64 = 3 // the highest id this engine owns; set_style REFUSES above it
698// nx_voxel_world's toon_band works on a 0..255 "bright"; wshade2 carries light in PERMIL
699// (wface_l returns 420..1000). Each of its three constants is therefore converted x1000/255 and
700// NOTHING here is tasted: 150 -> 588.2 -> 588, 105 -> 411.8 -> 412, 235 -> 921.6 -> 922.
701const WC_TOON_CUT: i64 = 588
702const WC_TOON_LO: i64 = 412
703const WC_TOON_HI: i64 = 922
704// FLAT is DERIVED, not chosen. The texture term enters this shader as
705// light = light*(870 + tex*260/256)/1000
706// which is exactly 1.000 at tex = 128, so 128 IS this shader's own no-op texel and "flat" is
707// spelled "hold tex at the value that makes the texture term vanish". No second constant is
708// introduced, and the incumbent does the same thing from the other side: its texel_col computes
709// its noise term ONLY under STYLE_REAL and adds zero otherwise.
710const WC_TEX_NEUTRAL: i64 = 128
711const P_ADV: i64 = 50 // +i = smart-object ADVERTISEMENTS (C1): packed
712 // block<<16 | need<<8 | strength; 6 rows 50-55, 0 = end.
713 // NPC minds read OBJECTS as data -- adding a row changes
714 // village life with zero new NPC code (The Sims' law).
715const P_HISTAGE: i64 = 60 // DECLARED geomorphic stage (Strahler bands): 0 = mature/
716 // equilibrium (HI 300-600 permil), 1 = youthful (HI >= 600).
717 // A world DECLARES its stage and gate T53 checks the
718 // MEASUREMENT matches -- widening one band until everything
719 // fits would hide a young landscape. (Ported 2026-08-02:
720 // existed only on the laptop line; the NAS gate ran 52 teeth
721 // while local ran 53 -- the LOCAL-IS-NOT-A-SUPERSET fork.)
722const P_TREEMAX: i64 = 59 // tree altitude ceiling (0 = the historic hardcoded 19)
723const P_TREESPEC: i64 = 61 // W2 SPECIES GENOME (packed, 0 = legacy 4-block stamp):
724 // bits 0-3 trunk base, 4-7 trunk variance, 8-11 canopy
725 // radius (<=4, the widened seam scan bound), 12-14 shape
726 // (0 legacy, 1 dome, 2 pine), 15-17 leaf-drop density.
727 // Bits 18-20 UNDERSTORY density + 21-22 kind (0 tufts,
728 // 1 shrubs, 2 mixed) -- W2b ground vegetation in the same
729 // genome; tree bits 0..17 zero = legacy forest preserved.
730 // A world declares its FOREST the way it declares its
731 // terrain -- species = parameters, individuals = seeded
732 // variation inside the envelope (the Infinigen law).
733const P_WARD: i64 = 58 // warden daytime hunt radius in BLOCKS (0 = the 12-block
734 // default). A world declares its own danger; the arriving
735 // traveler's LEVEL scales it further (isekai: new world,
736 // new challenge, and the challenge KNOWS who showed up).
737const P_OUTFIT: i64 = 57 // outfit mode: 0 = identity keeps everyone clothed;
738 // 1 = MIXED (beach): genome bits 18-19 pick each girl's
739 // beachwear -- clothed / topless / nude -- stable per
740 // girl, bred like every trait. Identity DATA decides
741 // whether the dimension is active at all.
742const P_CURI: i64 = 56 // curiosity threshold: a girl approaches the player only
743 // while her loneliness exceeds this (HABITUATION -- the
744 // operator's "they just follow me around": company
745 // satisfies, satisfied girls walk on with their lives.
746 // Fear never habituates; shy girls always keep distance).
747
748// GAME-FEEL TUNABLES -- every one carries a row in knowledge/gamefeel_oracle.conf (banked
749// reference bands: biomechanics, shipped-sim constants, modding-community defaults) and
750// nx_gamefeel_oracle_gate REFUSES any value outside its cited band. Feel is derived, not tasted.
751const P_BLDG: i64 = 62 // W3 settlement density: site-cell modulus (0 = none; the
752 // recipe plane can override it, idx 62 is gen-time data)
753const P_BLDGSPEC: i64 = 63 // low 4 bits = archetype row in nx_indoorgen_data
754const P_ERODE: i64 = 64 // E1 TALUS EROSION BUDGET in relaxation ticks (0 = OFF).
755 // Gen-time recipe DATA (world_recipes.conf spec idx 64),
756 // applied to the spec BEFORE genworld reads it. 0 does not
757 // merely default to nothing: it takes the branch out
758 // entirely, so a world that does not opt in generates the
759 // byte-identical terrain it generated before this existed.
760const P_ERTAL: i64 = 65 // DERIVED talus threshold, WC_ER_Q units. NEVER authored:
761 // wc_er_talus MEASURES it as the terrain's own mean per-cell
762 // largest 4-neighbour drop -- wp_erode_talus_derived's rule
763 // -- so material sheds where the ground is steeper than the
764 // ground's own average and no repose angle is picked by
765 // taste. Sentinels: 0 = not yet measured, WC_ER_FLAT = -1 =
766 // measured and the world has NO relief, which is the case
767 // wp_erode_bake_derived REFUSES rather than reporting a bake
768 // it did not do.
769// GE13b -- CLOTHING IS DATA (2026-08-30). The 3-value outfit (0 clothed / 1 topless / 2 nude) WAS
770// the whole wardrobe, and SWIMWEAR-ONLY rendered identically to a clothed village because no
771// swimwear garment existed anywhere in the engine (operator: "random dresses not beach outfits").
772// The wardrobe is now a GARMENT TABLE in this same spec plane: rows P_GARM .. P_GARM+GAR_N-1, one
773// packed word per garment, seeded by wc_spec for EVERY identity before its branch (an identity or a
774// recipe row overrides a garment exactly the way it overrides the sky). Garment id 0 = nothing worn
775// and is not a row; id i lives at sp[P_GARM + i - 1]. A ZERO row means "this garment does not exist
776// here" -- the selector then hands out nothing in that slot, which is what makes the gate's
777// missing-swim-row neg-control bite instead of silently reading a bare word as a garment.
778// bits 0-1 COVERAGE bit0 covers the top, bit1 the bottom: 1 top / 2 bottom / 3 full (one-piece,
779// dresses). The legacy 0/1/2 is DERIVED from the worn set's OR of these bits.
780// bits 2-4 CATEGORY 0 beach-swim 1 beach-cover 2 casual 3 evening
781// bits 8-16 HUE BASE degrees 0..359, where this garment's palette starts
782// bits 17-22 HUE SPAN degrees/4: genome bits 7-8 (the documented OUTFIT-TINT bits, MG_OUTFIT_SH)
783// pick one of four stops across it -- a girl's swimsuit colour is BRED like
784// her hair, and a bikini top and bottom share the stop, so the set matches
785// bits 23-26 SATURATION /15 bits 27-30 LIGHTNESS /15 (integer HSL, wc_hsl_rgb)
786// Index 69 stays UNOWNED on purpose: nx_world_recipe_gate T19 pins "spec idx 69 refuses" as the
787// recipe validator's upper edge, and putting a garment there would turn a pinned edge into a row.
788const P_GARM: i64 = 70
789const GAR_N: i64 = 9
790const GAR_BIKINI_TOP: i64 = 1
791const GAR_BIKINI_BOT: i64 = 2
792const GAR_ONEPIECE: i64 = 3
793const GAR_SARONG: i64 = 4
794const GAR_SUNDRESS: i64 = 5
795const GAR_TEE: i64 = 6
796const GAR_SHORTS: i64 = 7
797const GAR_SKIRT: i64 = 8
798const GAR_DRESS: i64 = 9
799const GAR_COV_TOP: i64 = 1
800const GAR_COV_BOT: i64 = 2
801const GAR_COV_FULL: i64 = 3
802const GAR_CAT_SWIM: i64 = 0
803const GAR_CAT_COVER: i64 = 1
804const GAR_CAT_CASUAL: i64 = 2
805const GAR_CAT_EVENING: i64 = 3
806const GAR_CAT_NONE: i64 = 4 // what mobgarc reports for a girl wearing nothing
807const GAR_CAT_SH: i64 = 2
808const GAR_CAT_MASK: i64 = 7
809const GAR_HUE_SH: i64 = 8
810const GAR_HUE_MASK: i64 = 511
811const GAR_SPAN_SH: i64 = 17
812const GAR_SPAN_MASK: i64 = 63
813const GAR_SPAN_UNIT_DEG: i64 = 4
814const GAR_SAT_SH: i64 = 23
815const GAR_LIT_SH: i64 = 27
816const GAR_NIB_MASK: i64 = 15
817const GAR_TINT_STOPS: i64 = 4 // MG_OUTFIT_MASK + 1: two genome bits, four stops
818const GAR_SLOT_SH: i64 = 8 // outfit word = upper id << GAR_SLOT_SH | lower id
819const GAR_ID_MASK: i64 = 255
820const HSL_DEG_FULL: i64 = 360
821const HSL_SECTOR_DEG: i64 = 60
822const MG_BEACH_SH: i64 = 18 // the beachwear genome bits the MIXED beach breeds (P_OUTFIT)
823const MG_BEACH_MASK: i64 = 3
824const MOB_POSE_SLEEP: i64 = 2 // mob_pose's published sleep id (the pose door's contract)
825const B_WATER: i64 = 4 // the water block (wshade2's shimmer branch, the shore ramp)
826const WC_SPECN: i64 = 79 // spec words the engine owns: 69 -> 79 for the GE13b wardrobe
827 // rows 70-78 (69 left unowned, see P_GARM). Was a bare 64 inside wc_spec.
828 // O_EJRN - O_SPEC = 640 B = 80 words, so this grows without
829 // moving a single other offset (the same law O_GENP was
830 // placed under).
831// GE13 -- THE OCEAN IS DATA (wsp_beach_ocean). One packed spec row turns any identity's far
832// side into a sea: bits 0-1 EDGE (0=+z 1=-z 2=+x 3=-x), bits 2-11 SHORELINE absolute
833// coordinate + 512 (WORLD space, so the streaming window regenerates it consistently),
834// bits 12-17 SLOPE width in blocks (the shore ramps down across this run), bits 18-23
835// SEABED height. 0 = no ocean: wsp_beach_ocean returns the height UNTOUCHED, so every
836// ocean-less identity generates byte-identically BY CONSTRUCTION (the KAT the gate holds).
837const P_OCEAN: i64 = 66
838// GE14a -- THE STAGED PROCGEN BIND. 0 = OFF, and OFF is the LEGACY TERRAIN BYTE-FOR-BYTE by
839// construction (wheight_raw returns before touching any of it). Non-zero = ON, and the value IS
840// the VERTICAL DIVISOR that maps nx_worldpipe_core's height space into this engine's block
841// column -- one row carrying both the switch and the scale, the shape P_ERODE already uses.
842// WHY A DIVISOR AND NOT A TASTED CONSTANT: the core's spline runs about -430..+420 before
843// relief, while a column here is 1..WY-8 blocks, so SOME map is required and the only honest
844// place for it is the recipe -- a world declares how much of its own vertical band it wants to
845// use, and nx_wasm_craft_gate asserts the resulting spread actually FILLS that band instead of
846// clamping flat at either end (a divisor that collapses the world is a measurable defect, not a
847// matter of taste).
848const P_WPIPE: i64 = 67
849// GE14a -- THE HORIZONTAL SCALE, and it is not a cosmetic knob: it is the unit conversion
850// between two coordinate systems, and MEASUREMENT is what proved it necessary. The staged
851// core's parameter fields have wavelengths of 1300..3400 units (continentalness the longest),
852// while a world window here is 128 BLOCKS. At 1 unit per block the whole window samples
853// essentially ONE POINT of the continent field, so the spline contributes a constant and only
854// the short ridge/detail terms vary: T79 measured spread 9 at divisor 30 and spread 10 at
855// divisor 18 -- a 40 percent smaller divisor bought ONE BLOCK, which is the signature of a
856// term that is constant across the sample, not of a scale that needs tuning. No vertical
857// divisor can fix that; the window has to span more of the field. 0 or 1 = one unit per block
858// (the legacy sampling); N = N units per block, so a 128-block window spans 128*N units.
859const P_WPSCALE: i64 = 68
860const P_SURF: i64 = 69 // GE53 SURFACE SOURCE: 0 = the cell grid (all voxel, byte-identical to
861 // every world shipped before this row), 1 = the continuous heightfield
862 // (no voxel ground: the ray marches the Q8 field). An identity declares
863 // it as DATA in wc_spec. All voxel or no voxel -- there is no partial.
864// The field is bilinear BETWEEN columns, so a sample a quarter block apart can never skip a column: the
865// marcher's step and the gradient's baseline are both this one span (Q8 units). DERIVED from the field's
866// own resolution (one column per block), not tuned.
867const WC_SURF_STEP_Q8: i64 = 64
868const WC_GAIT_BOB_Q8: i64 = 7 // thorax oscillation AMPLITUDE (~2.7cm, 5.5cm p2p --
869 // the band is peak-to-peak/2; 14 was a p2p-vs-amplitude
870 // conflation the recorder exposed as 11.3cm of chest)
871// RESPIRATION -- the idle drive. Derived, not tasted: resting adult breathing is 12-16/min;
872// at 14/min on the fixed 60Hz tick that is 60*60/14 = 257 ticks per cycle (0.233 Hz, inside the
873// 0.20-0.30 Hz band this rung is gated on). Amplitude is scaled off the GAIT constant above so
874// the two drives share one ruler: 7 q8 ~ 2.7cm => 1 q8 ~ 0.386cm, so 2 q8 ~ 0.77cm, inside the
875// declared 0.4-0.8cm resting-excursion band. A third value would have been a guess; this is an
876// arithmetic consequence of a constant already cited.
877const WC_BREATH_TICKS: i64 = 257
878// ★ AMPLITUDE, NOT PEAK-TO-PEAK -- and I got this wrong ONCE ALREADY on the gait constant.
879// Conversion, derived from the cited gait row: 7 q8 amplitude is documented as ~2.7cm, and
880// 7/256*100cm-per-block = 2.73cm, so the unit is q8 world units and p2p = 2*amp/256*100 cm.
881// At 2 q8 that is 1.56cm p2p -- OUTSIDE the 0.4-0.8cm resting band this rung is gated on, i.e.
882// 2x too large, the SAME p2p-vs-amplitude conflation the recorder caught in WC_GAIT_BOB_Q8 (14
883// -> 7). At 1 q8: 2/256*100 = 0.78cm p2p, inside the band. Caught by doing the arithmetic in the
884// unit the CRITERION is written in, before it shipped visibly.
885// ⚠KNOWN LIMIT, to be measured not assumed: 1 q8 is near the world-q8 resolution floor, so the
886// anchor steps coarsely; the spring integrates and should low-pass it, but ?breath=1 reports the
887// actual excursion so this is decided by measurement rather than by my expectation.
888const WC_BREATH_Q8: i64 = 1
889// speed at which GAIT fully owns the anchor. PROVISIONAL and deliberately marked so: the units
890// of the per-tick mob delta were never measured, so this is the one number here without a
891// derivation. The mobspd door added below exists to REPLACE it with a measurement rather than
892// leave a guess sitting in the engine pretending to be a constant.
893const WC_BREATH_CUT_Q8: i64 = 24
894const WC_STEP_EASE_UP: i64 = 48 // mob Y climb rate Q8/tick (a 1-block rise ~ 5 ticks)
895const WC_NIGHT_X8: i64 = 5 // night begins at this eighth of the day (5/8)
896
897const EYE: i64 = 435 // eye height Q8 (~1.7 blocks)
898const SPEED: i64 = 40 // walk Q8 per tick
899// GAIT DRIVE CONSTANTS -- declared HERE, above their first reader in the mob tick. The compiler
900// refuses a module const used before its declaration because it would silently read 0, and for the
901// gait accumulator that would have meant a permanently frozen drive that still compiled, still ran,
902// and looked exactly like the metronome it was written to replace.
903const WC_PH_PER_TICK: i64 = 5 // the free-running advance MC_PH still uses for WANDER
904const WC_PH_ANG: i64 = 214 // angle-units per phase unit (the incumbent's drive scaling)
905const WC_NEED_GAIT: i64 = 3 // the needs row's spare slot, named now that it carries a value
906// ---- THE GAIT MODEL, CITED (knowledge/gamefeel_oracle.conf, gait_* rows) -----------------------
907// Every number below is a published normative measurement carrying its cohort and its instrument.
908// None is a taste, and none may be "tuned" without moving the conf row and its source with it.
909// ⚠UNIT DISCIPLINE, AND THIS FILE HAS ALREADY PAID ONCE FOR GETTING IT WRONG (WC_GAIT_BOB_Q8 went
910// 14 -> 7 on exactly this): the gait literature reports RANGE of motion -- rRoM, ROM -- which is
911// PEAK-TO-PEAK. The AMPLITUDE is HALF of it. Every constant here is stored as the PUBLISHED RANGE
912// in DECIDEGREES so it can be compared straight against the paper, and the halving happens in
913// exactly one function (wc_amp_ddeg) so no caller can forget it.
914const WC_DDEG_HALFTURN: i64 = 1800 // decidegrees in IT_PI (a half turn): the ddeg -> it-unit scale
915// STRIDE = 2 x step length 0.73 m (mean, 21-30 age band, markerless normative database).
916// 1 BLOCK = 1 m is NOT assumed here, it is read off the engine: EYE = 435 q8 = 1.70 blocks is the
917// player's eye height and adult eye height is ~1.6-1.7 m, so a block IS a metre to within the
918// figure's own tolerance. 1.46 m = 1.46 blocks = 374 q8.
919// ★THE PUBLISHED TRIPLE RECONCILES, WHICH IS A FREE AUDIT OF THE SOURCE: step 0.73 m x cadence
920// 108.40 steps/min / 60 = 1.319 m/s against that table's own reported gait speed of 1.30 m/s.
921const WC_STRIDE_Q8: i64 = 374
922// PELVIS transverse rotation, rRoM = 5.2 deg (n=201 healthy adults, 5 km/h, surface topography).
923// ⚠SCOPE, STATED: surface topography estimates the pelvis from PSIS surface markers and reads
924// LOWER than bone-pin and marker-cluster methods, which put pelvic transverse range nearer 10 deg.
925// 5.2 is therefore a FLOOR from one named instrument, not a settled value, and it is used as the
926// reference the genome varies AROUND rather than as a ceiling.
927const WC_PELVIS_ROT_DDEG: i64 = 52
928// THORACIC counter-rotation, rRoM at T7 = 14.6 deg -- and this is the paper's headline finding,
929// explicitly CONTRARY to the previous functional understanding that T7 is the near-static point of
930// intersection: T7/T8 carry the LARGEST rotary amplitude of every vertebra C7..L4, and "the largest
931// rotary opposition was also found between the pelvis and T7/T8". The thorax rotates roughly THREE
932// TIMES the pelvis, against it.
933// ★THIS PHASE RELATIONSHIP IS THE MECHANISM READ AS "SWINGING HIPS". It is not an amplitude: what
934// the eye sees is the pelvis and the shoulders twisting in OPPOSITION about the line of travel.
935// It is encoded as a SIGN below and therefore needs no constant of its own -- deliberately absent.
936const WC_THORAX_ROT_DDEG: i64 = 146
937// ELBOW flexion-extension ROM through the gait cycle = 29.7 deg (SD 10.2). The forearm is NOT
938// rigid in walking; a locked elbow is the "stiff arms" report stated as a measurement, and this
939// row is what makes it a defect rather than an opinion.
940const WC_ELBOW_ROM_DDEG: i64 = 297
941// TURN ALLOWANCE, expressed as heading change per STRIDE rather than per tick, so it is
942// speed-coupled BY CONSTRUCTION: a standing girl cannot turn and a walking one turns in
943// proportion to the ground she covers. ⚠DERIVED-BY-ARGUMENT, NOT MEASURED, AND LABELLED SO: a
944// walking body re-places its feet once per stride and cannot change heading by more than about a
945// quarter turn in that time. Cross-check against the two rulers this engine already owns -- at
946// the cited stride and a mob's 4 q8/tick it works out at 68 it-units/tick = 57 deg/s, a little
947// over half the player's own TURN slew of 140 it-units/tick = 117 deg/s, and inside the 60-90
948// deg/s that comfortable walking turns are usually quoted at. What would SETTLE it is a cited
949// walking turn-rate band in gamefeel_oracle.conf; there is not one, and inventing a row that
950// looked cited would be worse than naming the gap.
951const WC_TURN_PER_STRIDE: i64 = IT_PI/2
952const TURN: i64 = 140 // yaw it-units per tick (keyboard turn / legacy dir path)
953const PITCHV: i64 = 90 // pitch it-units per tick (R/F keyboard look)
954const PITCH_MAX: i64 = 5000
955const LOOKS: i64 = 6 // mouselook: it-units per accumulated look count
956const JUMPV: i64 = 66 // jump takeoff velocity Q8/tick
957const GRAV: i64 = 9 // gravity Q8/tick^2
958const VTERM: i64 = 120 // terminal fall velocity Q8/tick -- ONE ruler for the player AND every mob
959 // (GE12 mob_step_gait; was an inline 120 in apply2_impl)
960// FOOTSTRIKE/LANDING IMPULSE unit bridge -- ARITHMETIC of two existing conventions, not a tuning.
961// Mob positions are world-q8 (256/block) and the softbind anchor is fed pos/4 (the
962// sb_tick_mob_gene call in mob_tick), so ONE MODEL UNIT = 4 world-q8. sd_impulse's own contract
963// (nx_softdyn: "model-units/tick * 256") takes model-q8/tick velocity kicks. Therefore one
964// world-q8/tick of mob velocity = SD_Q8/4 solver velocity units, and a mob velocity change dv
965// converts as dv*WC_IMP_SCALE with no free parameter anywhere in the chain.
966const WC_IMP_SCALE: i64 = SD_Q8/4
967const MAXSTEPS: i64 = 110 // DDA cells per ray (view distance)
968const REACH_STEPS: i64 = 20 // crosshair edit reach in DDA cells
969
970func wst(base: i64) -> *i64 { return (base + O_ST) as *i64 }
971func wsp(base: i64) -> *i64 { return (base + O_SPEC) as *i64 }
972func wgp(base: i64) -> *i64 { return (base + O_GENP) as *i64 }
973func mobp(base: i64) -> *i64 { return (base + O_MOBS) as *i64 }
974func wfb(base: i64) -> *i64 { return (base + O_FB) as *i64 }
975func wvx(base: i64) -> *u8 { return (base + O_VOX) as *u8 }
976func winp(base: i64) -> *i64 { return (base + O_INPUT) as *i64 }
977func wpack(r: i64, g: i64, b: i64) -> i64 { return r | (g << 8) | (b << 16) }
978func wclamp(v: i64, lo: i64, hi: i64) -> i64 { if v < lo { return lo } if v > hi { return hi } return v }
979
980func vin(x: i64, y: i64, z: i64) -> i64 {
981 if x < 0 { return 0 } if x >= WX { return 0 }
982 if y < 0 { return 0 } if y >= WY { return 0 }
983 if z < 0 { return 0 } if z >= WZ { return 0 }
984 return 1
985}
986func vget(base: i64, x: i64, y: i64, z: i64) -> i64 {
987 if vin(x, y, z) == 0 { return 0 }
988 let v: *u8 = wvx(base)
989 return (v[(y*WZ + z)*WX + x] & 0xff) as i64
990}
991func vset(base: i64, x: i64, y: i64, z: i64, b: i64) -> i64 {
992 if vin(x, y, z) == 0 { return 0 }
993 let v: *u8 = wvx(base)
994 v[(y*WZ + z)*WX + x] = b as u8
995 return 1
996}
997
998// ---------- edit journal (v6 streaming): every PLAYER edit recorded at ABSOLUTE world coords ----------
999// One packed i64 hash slot per touched cell: (key+1)<<8 | block, key = ax+2^24 (25b) : az+2^24 (25b)
1000// : ay (6b). The journal is the persistent truth for touched land; the window is a cache over
1001// (seed + journal) -- the voxchunk law (virgin land from the seed, touched land from history) wired
1002// into the live game. Bounded EJRN_SLOTS edits; a FULL journal REFUSES the whole edit LOUDLY
1003// (S_EJDROP counts refusals) so world and history can never disagree.
1004func ejp(base: i64) -> *i64 { return (base + O_EJRN) as *i64 }
1005func ej_key(ax: i64, ay: i64, az: i64) -> i64 {
1006 return ((ax + WATER_MAGIC_16777216) << 31) | ((az + WATER_MAGIC_16777216) << 6) | ay
1007}
1008func ej_put(base: i64, ax: i64, ay: i64, az: i64, b: i64) -> i64 {
1009 let e: *i64 = ejp(base)
1010 let k: i64 = ej_key(ax, ay, az)
1011 let kv: i64 = ((k + 1) << 8) | (b & 255)
1012 var i: i64 = (k * EJRN_HASH_MUL) & (EJRN_SLOTS - 1)
1013 var n: i64 = 0
1014 while n < EJRN_SLOTS {
1015 let cur: i64 = e[i]
1016 if cur == 0 { e[i] = kv; return 1 }
1017 if (cur >> 8) == k + 1 { e[i] = kv; return 1 }
1018 i = (i + 1) & (EJRN_SLOTS - 1)
1019 n = n + 1
1020 }
1021 return 0 - 1
1022}
1023func ej_get(base: i64, ax: i64, ay: i64, az: i64) -> i64 {
1024 let e: *i64 = ejp(base)
1025 let k: i64 = ej_key(ax, ay, az)
1026 var i: i64 = (k * EJRN_HASH_MUL) & (EJRN_SLOTS - 1)
1027 var n: i64 = 0
1028 while n < EJRN_SLOTS {
1029 let cur: i64 = e[i]
1030 if cur == 0 { return 0 - 1 }
1031 if (cur >> 8) == k + 1 { return cur & 255 }
1032 i = (i + 1) & (EJRN_SLOTS - 1)
1033 n = n + 1
1034 }
1035 return 0 - 1
1036}
1037// overlay journal entries falling inside the local box [lx0..lx1) x [lz0..lz1) -- edits WIN over regen
1038func ej_overlay(base: i64, lx0: i64, lx1: i64, lz0: i64, lz1: i64) -> i64 {
1039 let s: *i64 = wst(base)
1040 let e: *i64 = ejp(base)
1041 var i: i64 = 0
1042 while i < EJRN_SLOTS {
1043 let cur: i64 = e[i]
1044 if cur != 0 {
1045 let k: i64 = (cur >> 8) - 1
1046 let ay: i64 = k & 63
1047 let az: i64 = ((k >> 6) & WATER_MAGIC_33554431) - WATER_MAGIC_16777216
1048 let ax: i64 = ((k >> 31) & WATER_MAGIC_33554431) - WATER_MAGIC_16777216
1049 let lx: i64 = ax - s[S_WOX]
1050 let lz: i64 = az - s[S_WOZ]
1051 if lx >= lx0 { if lx < lx1 { if lz >= lz0 { if lz < lz1 {
1052 vset(base, lx, ay, lz, cur & 255)
1053 } } } }
1054 }
1055 i = i + 1
1056 }
1057 return 0
1058}
1059// the PLAYER-EDIT write path: journal at absolute coords FIRST (a full journal refuses the whole
1060// edit -- world and history stay consistent), then the window write. Gen/save paths keep raw vset.
1061func wc_edit(base: i64, x: i64, y: i64, z: i64, b: i64) -> i64 {
1062 let s: *i64 = wst(base)
1063 if ej_put(base, x + s[S_WOX], y, z + s[S_WOZ], b) < 0 {
1064 s[S_EJDROP] = s[S_EJDROP] + 1
1065 return 0
1066 }
1067 return vset(base, x, y, z, b)
1068}
1069// solid for collision: everything but air, water and CROPS (plants are walked through, not on)
1070func vsolid(base: i64, x: i64, y: i64, z: i64) -> i64 {
1071 let b: i64 = vget(base, x, y, z)
1072 if b == 0 { return 0 }
1073 if b == 4 { return 0 }
1074 if b >= B_CROP0 { if b <= B_CROP2 { return 0 } }
1075 return 1
1076}
1077
1078// ---------- worldgen: integer value noise -> heightmap -> columns -> trees ----------
1079func whash(x: i64, z: i64, sd: i64) -> i64 {
1080 var n: i64 = x*TERRAIN_HASH_X + z*TERRAIN_HASH_Z + sd*TERRAIN_HASH_SEED
1081 n = n & SAVE_CK_MASK
1082 n = (n ^ (n >> 13)) * TERRAIN_HASH_MIX
1083 n = n & SAVE_CK_MASK
1084 return (n ^ (n >> 16)) % 256
1085}
1086// bilinear value noise, lattice spacing `sc`, result 0..255
1087func wnoise(x: i64, z: i64, sc: i64, sd: i64) -> i64 {
1088 let cx: i64 = x / sc
1089 let cz: i64 = z / sc
1090 let fx: i64 = x % sc
1091 let fz: i64 = z % sc
1092 let c00: i64 = whash(cx, cz, sd)
1093 let c10: i64 = whash(cx+1, cz, sd)
1094 let c01: i64 = whash(cx, cz+1, sd)
1095 let c11: i64 = whash(cx+1, cz+1, sd)
1096 let v0: i64 = c00 + (c10 - c00)*fx/sc
1097 let v1: i64 = c01 + (c11 - c01)*fx/sc
1098 return v0 + (v1 - v0)*fz/sc
1099}
1100// the RAW two-octave surface: what wheight was in full before E1, unchanged and still the only
1101// thing the talus measurement is allowed to read (a threshold derived from an already-eroded
1102// surface would be measuring its own output).
1103func wheight_raw(base: i64, x: i64, z: i64, sd: i64) -> i64 {
1104 let sp: *i64 = wsp(base)
1105 // GE14a: when the recipe binds the staged pipeline, the ground IS the six-field
1106 // parameter space (continentalness -> spline, ridged relief scaled by weirdness and
1107 // flattened by erosion) rather than this engine's two octaves of value noise. The
1108 // core statics are set PER CALL, not once at init, deliberately: gates hold several
1109 // arenas at different seeds at the same time and a module-global seeded once would
1110 // silently generate one arena's terrain inside another. Four assignments against a
1111 // per-column noise evaluation is not a cost worth a correctness risk.
1112 let wpd: i64 = sp[P_WPIPE]
1113 if wpd != 0 {
1114 WP_SEED = sd
1115 WP_TEMP_BIAS = 0
1116 WP_CONT_BIAS = 0
1117 WP_MOUNT_SCALE = WP_MAGIC_1400
1118 var wsc: i64 = sp[P_WPSCALE]
1119 if wsc <= 0 { wsc = 1 }
1120 return wclamp(sp[P_TBASE] + wp_height_raw(x*wsc, z*wsc)/wpd, 1, WY - 8)
1121 }
1122 let n1: i64 = wnoise(x, z, 16, sd)
1123 let n2: i64 = wnoise(x, z, 5, sd + 7)
1124 var h: i64 = sp[P_TBASE] + n1*sp[P_TAMP1]/256 + n2*sp[P_TAMP2]/256
1125 return wclamp(h, 1, WY - 8)
1126}
1127
1128// ---------- E1: TALUS EROSION, EVALUATED POINTWISE (2026-08-25) ----------
1129// THE WORLD SCROLLS, SO EROSION HERE CANNOT BE A BAKED GRID. gencol regenerates strips at
1130// ABSOLUTE coordinates as the window slides (s[S_WOX] = s[S_WOX] + dx), which is exactly why the
1131// header above gencol calls the terrain "a pure function of (absolute position, seed) -> unbounded
1132// land". nx_worldpipe's wp_erode is the baked-grid shape and its OWN docstring states the cost:
1133// it returns zero "outside the baked span", a case "a caller must not be able to tell apart from
1134// no erosion here, because that is what they are". Baking it here would put eroded ground behind
1135// the player and raw noise in front of him with a hard seam between the two. Storing a grid is
1136// not available either -- "wasm has no mmap; every buffer is an arena offset", and the
1137// framebuffer TAILS the arena (T60), so a 128x128 heightfield would have to move WASM_MEM_BYTES.
1138// SO THE OPERATOR IS EVALUATED, NOT STORED. k passes of a radius-1 stencil have a domain of
1139// dependence of radius k, so the unrolled composition below returns at (x,z) exactly what a
1140// k-pass relaxation over an INFINITE grid would put there. No seam can exist because there is no
1141// grid to have an edge, and nothing has to be saved, shifted or invalidated when the world slides.
1142// The pass is thermal (talus) erosion in its symmetric PAIRWISE-EXCHANGE form: across each of the
1143// 4 edges material moves from the higher cell to the lower one ONLY where the drop exceeds the
1144// talus threshold. wc_er_flux is ANTISYMMETRIC -- flux(-d) == -flux(d) exactly, truncation toward
1145// zero included -- so both ends of every edge move by equal and opposite amounts and the pass
1146// CONSERVES MASS. That is what makes this erosion and not a blur: a blur lowers everything, this
1147// cuts steep faces back and fills the hollows immediately below them, and it leaves ground gentler
1148// than the threshold untouched entirely.
1149const WC_ER_Q: i64 = 256 // fixed point the pass runs in. World heights are
1150 // small integers (craft relief ~22 blocks), so a
1151 // pass done in whole blocks could only ever move 0
1152 // or >=1 and would quantise the effect away.
1153const WC_ER_STABLE_Q: i64 = WC_ER_Q / 4 // DERIVED, not tasted: an explicit 4-neighbour
1154 // exchange h' = h + r*sum(h_n - h) is stable iff
1155 // 4r <= 1, so r = 1/4 is the marginal limit and
1156 // oscillates AT it.
1157const WC_ER_RATE_Q: i64 = WC_ER_STABLE_Q / 2 // WRITTEN AS THE ARITHMETIC: half the stability
1158 // limit. This factor of 2 is the ONLY judgement in
1159 // the whole operator and it is named right here;
1160 // the per-world lever is the tick budget, which is
1161 // recipe DATA, not code.
1162const WC_ER_MAXK: i64 = 3 // STRUCTURAL, not a clamp: three unrolled levels
1163 // are written below and no fourth one exists. Cost
1164 // is 5^k raw height evaluations per column -- 125
1165 // at k=3; 625 at k=4 would put worldgen past a
1166 // second and a visitor would meet a blank canvas.
1167const WC_ER_FLAT: i64 = 0 - 1 // P_ERTAL sentinel: measured, and there is no relief
1168
1169// THE SAME SURFACE AT SUB-BLOCK RESOLUTION -- AND THE FIX FOR A MEASURED, GATE-CAUGHT DEFECT.
1170// wheight_raw's two truncating divisions by 256 round the land to WHOLE BLOCKS before anything
1171// can weather it, and feeding that pre-rounded field to the pass made the first cut of this
1172// operator measurably do nothing. With the input quantised, every neighbour drop is a multiple of
1173// 256; the MEASURED talus came out at 264, so a one-block step (256) sits BELOW the threshold and
1174// does not move at all, and a two-block step moves (512-264)/8 = 31 of the 128 needed to change a
1175// block. The wasm-VM gate reported moved=0 of 576 with HI 477 -> 477 and REFUSED the ship. That is
1176// the tooth doing its job, and the answer is not a bigger rate -- it is to weather the CONTINUOUS
1177// field and quantise only the result, which is what erosion does to real ground anyway.
1178func wheight_q(base: i64, x: i64, z: i64, sd: i64) -> i64 {
1179 let sp: *i64 = wsp(base)
1180 let n1: i64 = wnoise(x, z, 16, sd)
1181 let n2: i64 = wnoise(x, z, 5, sd + 7)
1182 var hq: i64 = sp[P_TBASE]*WC_ER_Q + n1*sp[P_TAMP1] + n2*sp[P_TAMP2]
1183 return wclamp(hq, WC_ER_Q, (WY - 8)*WC_ER_Q)
1184}
1185// signed transfer INTO this cell across one edge, given the neighbour-minus-self drop d.
1186// Antisymmetric by construction, so the two ends of an edge always move by equal and opposite
1187// amounts. That identity is what conserves mass, and the gate ASSERTS it rather than assuming the
1188// dialect truncates the way this reasoning needs.
1189func wc_er_flux(d: i64, t: i64) -> i64 {
1190 if d > t { return (d - t)*WC_ER_RATE_Q/WC_ER_Q }
1191 if d < 0 - t { return (d + t)*WC_ER_RATE_Q/WC_ER_Q }
1192 return 0
1193}
1194// THE THRESHOLD IS MEASURED, NEVER AUTHORED. Mean over a FIXED reference patch of each cell's
1195// largest 4-neighbour drop -- wp_erode_talus_derived's rule, applied to the surface actually being
1196// eroded. The patch is the absolute [0,WX)x[0,WZ) square rather than the player's current window,
1197// so the threshold stays a pure function of (spec, seed) and does not drift as the world slides
1198// under him. Result is in WC_ER_Q units; a world with no relief measures 0 and turns erosion off.
1199func wc_er_talus(base: i64, sd: i64) -> i64 {
1200 var sum: i64 = 0
1201 var z: i64 = 0
1202 while z < WZ {
1203 var x: i64 = 0
1204 while x < WX {
1205 let h: i64 = wheight_q(base, x, z, sd)
1206 var m: i64 = 0
1207 let da: i64 = wheight_q(base, x - 1, z, sd) - h
1208 if da > m { m = da }
1209 if 0 - da > m { m = 0 - da }
1210 let db: i64 = wheight_q(base, x + 1, z, sd) - h
1211 if db > m { m = db }
1212 if 0 - db > m { m = 0 - db }
1213 let dc: i64 = wheight_q(base, x, z - 1, sd) - h
1214 if dc > m { m = dc }
1215 if 0 - dc > m { m = 0 - dc }
1216 let dd: i64 = wheight_q(base, x, z + 1, sd) - h
1217 if dd > m { m = dd }
1218 if 0 - dd > m { m = 0 - dd }
1219 sum = sum + m
1220 x = x + 1
1221 }
1222 z = z + 1
1223 }
1224 return sum/(WX*WZ) // already in WC_ER_Q units: the field it measured is
1225}
1226// memoised into the spec so the sweep above runs ONCE per arena, never once per column. The
1227// sentinel makes "measured and flat" distinguishable from "not measured yet", which is the whole
1228// reason a flat world does not pay the sweep on every single query.
1229func wc_er_arm(base: i64, sd: i64) -> i64 {
1230 let sp: *i64 = wsp(base)
1231 if sp[P_ERTAL] != 0 { return sp[P_ERTAL] }
1232 let t: i64 = wc_er_talus(base, sd)
1233 if t <= 0 { sp[P_ERTAL] = WC_ER_FLAT } else { sp[P_ERTAL] = t }
1234 return sp[P_ERTAL]
1235}
1236// ONE PASS, UNROLLED THREE DEEP. No recursion anywhere: each level is its own function and calls
1237// only the level below it, so the maximum budget is a property of the SOURCE that a reader can
1238// count, rather than a runtime clamp that has to be trusted.
1239func wc_er1(base: i64, x: i64, z: i64, sd: i64, t: i64) -> i64 {
1240 let h: i64 = wheight_q(base, x, z, sd)
1241 var a: i64 = h
1242 a = a + wc_er_flux(wheight_q(base, x - 1, z, sd) - h, t)
1243 a = a + wc_er_flux(wheight_q(base, x + 1, z, sd) - h, t)
1244 a = a + wc_er_flux(wheight_q(base, x, z - 1, sd) - h, t)
1245 a = a + wc_er_flux(wheight_q(base, x, z + 1, sd) - h, t)
1246 return a
1247}
1248func wc_er2(base: i64, x: i64, z: i64, sd: i64, t: i64) -> i64 {
1249 let h: i64 = wc_er1(base, x, z, sd, t)
1250 var a: i64 = h
1251 a = a + wc_er_flux(wc_er1(base, x - 1, z, sd, t) - h, t)
1252 a = a + wc_er_flux(wc_er1(base, x + 1, z, sd, t) - h, t)
1253 a = a + wc_er_flux(wc_er1(base, x, z - 1, sd, t) - h, t)
1254 a = a + wc_er_flux(wc_er1(base, x, z + 1, sd, t) - h, t)
1255 return a
1256}
1257func wc_er3(base: i64, x: i64, z: i64, sd: i64, t: i64) -> i64 {
1258 let h: i64 = wc_er2(base, x, z, sd, t)
1259 var a: i64 = h
1260 a = a + wc_er_flux(wc_er2(base, x - 1, z, sd, t) - h, t)
1261 a = a + wc_er_flux(wc_er2(base, x + 1, z, sd, t) - h, t)
1262 a = a + wc_er_flux(wc_er2(base, x, z - 1, sd, t) - h, t)
1263 a = a + wc_er_flux(wc_er2(base, x, z + 1, sd, t) - h, t)
1264 return a
1265}
1266// THE HEIGHT THE PLAYER WALKS ON. gencol builds every column from this and gentrees plants from
1267// it, so a change here is a change to the GROUND, not a shader drawn over it. All 7 call sites are
1268// worldgen or init; the renderer reads voxels, so the 5^k cost is paid once per strip and never
1269// per frame.
1270func wheight(base: i64, x: i64, z: i64, sd: i64) -> i64 {
1271 let sp: *i64 = wsp(base)
1272 var k: i64 = sp[P_ERODE]
1273 if k <= 0 { return wheight_raw(base, x, z, sd) }
1274 if k > WC_ER_MAXK { k = WC_ER_MAXK }
1275 let t: i64 = wc_er_arm(base, sd)
1276 if t <= 0 { return wheight_raw(base, x, z, sd) }
1277 var he: i64 = 0
1278 if k == 1 { he = wc_er1(base, x, z, sd, t) }
1279 if k == 2 { he = wc_er2(base, x, z, sd, t) }
1280 if k >= 3 { he = wc_er3(base, x, z, sd, t) }
1281 return wclamp((he + WC_ER_Q/2)/WC_ER_Q, 1, WY - 8)
1282}
1283// GE53: the SAME eroded height wheight returns, in WC_ER_Q units and NOT rounded -- the one value the
1284// surface source keeps that the voxel column throws away at wheight's last line.
1285func wheight_qe(base: i64, x: i64, z: i64, sd: i64) -> i64 {
1286 let sp: *i64 = wsp(base)
1287 var k: i64 = sp[P_ERODE]
1288 if k <= 0 { return wheight_q(base, x, z, sd) }
1289 if k > WC_ER_MAXK { k = WC_ER_MAXK }
1290 let t: i64 = wc_er_arm(base, sd)
1291 if t <= 0 { return wheight_q(base, x, z, sd) }
1292 var he: i64 = 0
1293 if k == 1 { he = wc_er1(base, x, z, sd, t) }
1294 if k == 2 { he = wc_er2(base, x, z, sd, t) }
1295 if k >= 3 { he = wc_er3(base, x, z, sd, t) }
1296 return wclamp(he, WC_ER_Q, (WY - 8)*WC_ER_Q)
1297}
1298// the ocean ramp in Q units -- the same arithmetic wsp_beach_ocean applies to whole blocks, so the field
1299// and the column agree on where the beach meets the sea.
1300func wsp_beach_ocean_q(sp: *i64, ax: i64, az: i64, hq: i64) -> i64 {
1301 let oc: i64 = sp[P_OCEAN]
1302 if oc == 0 { return hq }
1303 let d: i64 = wsp_ocean_d(sp, ax, az)
1304 if d <= 0 { return hq }
1305 let slopew: i64 = (oc >> 12) & 63
1306 let bedq: i64 = ((oc >> 18) & 63)*WC_ER_Q
1307 var t: i64 = bedq
1308 if d < slopew { t = hq - (hq - bedq)*d/slopew }
1309 if t < bedq { t = bedq }
1310 if t > hq { t = hq }
1311 return t
1312}
1313func hq_get(base: i64, x: i64, z: i64) -> i64 {
1314 if x < 0 { return 0 - 1 }
1315 if z < 0 { return 0 - 1 }
1316 if x >= WX { return 0 - 1 }
1317 if z >= WZ { return 0 - 1 }
1318 let p: *i64 = (base + O_HQ + (z*WX + x)*8) as *i64
1319 return p[0]
1320}
1321func hq_set(base: i64, x: i64, z: i64, v: i64) -> i64 {
1322 let p: *i64 = (base + O_HQ + (z*WX + x)*8) as *i64
1323 p[0] = v
1324 return 0
1325}
1326func hq_off() -> i64 { return O_HQ }
1327// wc_surface_source -- GE53's named symbol: the terrain SURFACE height (the top of the ground, Q8 world
1328// units) at a Q8 position, bilinear between the four surrounding columns. -1 outside the window. A
1329// column's Q height is the base of its top block, so the surface sits one block above it.
1330func wc_surface_source(base: i64, pxq: i64, pzq: i64) -> i64 {
1331 let cx: i64 = pxq >> 8
1332 let cz: i64 = pzq >> 8
1333 let fx: i64 = pxq & 255
1334 let fz: i64 = pzq & 255
1335 let h00: i64 = hq_get(base, cx, cz)
1336 let h10: i64 = hq_get(base, cx + 1, cz)
1337 let h01: i64 = hq_get(base, cx, cz + 1)
1338 let h11: i64 = hq_get(base, cx + 1, cz + 1)
1339 if h00 < 0 { return 0 - 1 }
1340 if h10 < 0 { return 0 - 1 }
1341 if h01 < 0 { return 0 - 1 }
1342 if h11 < 0 { return 0 - 1 }
1343 let top: i64 = h00*(256 - fx)*(256 - fz) + h10*fx*(256 - fz) + h01*(256 - fx)*fz + h11*fx*fz
1344 return top/65536 + 256
1345}
1346// terrain-class blocks: the ground the field replaces. Structures (wood, leaves, crops, water) stay cells.
1347func wterrain(b: i64) -> i64 {
1348 if b == 1 { return 1 }
1349 if b == 2 { return 1 }
1350 if b == 3 { return 1 }
1351 if b == 5 { return 1 }
1352 if b == 8 { return 1 }
1353 if b == 9 { return 1 }
1354 if b == 10 { return 1 }
1355 if b == 11 { return 1 }
1356 return 0
1357}
1358// trilinear 3D value noise 0..255 (cave carver)
1359func wnoise3(x: i64, y: i64, z: i64, sc: i64, sd: i64) -> i64 {
1360 let cx: i64 = x / sc
1361 let cy3: i64 = y / sc
1362 let cz: i64 = z / sc
1363 let fx: i64 = x % sc
1364 let fy: i64 = y % sc
1365 let fz: i64 = z % sc
1366 let a00: i64 = whash(cx + cy3*97, cz, sd)
1367 let a10: i64 = whash(cx + 1 + cy3*97, cz, sd)
1368 let a01: i64 = whash(cx + cy3*97, cz + 1, sd)
1369 let a11: i64 = whash(cx + 1 + cy3*97, cz + 1, sd)
1370 let b00: i64 = whash(cx + (cy3+1)*97, cz, sd)
1371 let b10: i64 = whash(cx + 1 + (cy3+1)*97, cz, sd)
1372 let b01: i64 = whash(cx + (cy3+1)*97, cz + 1, sd)
1373 let b11: i64 = whash(cx + 1 + (cy3+1)*97, cz + 1, sd)
1374 let a0: i64 = a00 + (a10 - a00)*fx/sc
1375 let a1: i64 = a01 + (a11 - a01)*fx/sc
1376 let av: i64 = a0 + (a1 - a0)*fz/sc
1377 let b0: i64 = b00 + (b10 - b00)*fx/sc
1378 let b1: i64 = b01 + (b11 - b01)*fx/sc
1379 let bv: i64 = b0 + (b1 - b0)*fz/sc
1380 return av + (bv - av)*fy/sc
1381}
1382// recompute the world's highest solid layer (init + save-load; place maintains it incrementally)
1383func wc_wmax(base: i64) -> i64 {
1384 let s: *i64 = wst(base)
1385 let vxp: *u8 = (base + O_VOX) as *u8
1386 var y: i64 = WY - 1
1387 while y >= 0 {
1388 var i: i64 = y*WZ*WX
1389 let end: i64 = (y + 1)*WZ*WX
1390 while i < end {
1391 if (vxp[i] & 0xff) != 0 { s[S_WMAX] = y; return y }
1392 i = i + 1
1393 }
1394 y = y - 1
1395 }
1396 s[S_WMAX] = 0
1397 return 0
1398}
1399
1400// generate ONE local column from ABSOLUTE world coords: the terrain is a pure function of
1401// (absolute position, seed) -> unbounded land, deterministic everywhere, no stored world needed
1402// for virgin ground (the voxchunk regen law wired into the live game).
1403// GE13 -- shore distance: how many blocks SEAWARD of the shoreline (ax,az) sits; <= 0 = inland,
1404// -1 = this identity has no ocean row at all.
1405func wsp_ocean_d(sp: *i64, ax: i64, az: i64) -> i64 {
1406 let oc: i64 = sp[P_OCEAN]
1407 if oc == 0 { return 0 - 1 }
1408 let edge: i64 = oc & 3
1409 let shore: i64 = ((oc >> 2) & 1023) - 512
1410 if edge == 0 { return az - shore }
1411 if edge == 1 { return shore - az }
1412 if edge == 2 { return ax - shore }
1413 return shore - ax
1414}
1415// GE45 REVIEW FRAMING (2026-09-04) -- the sea bearing of an identity, in yaw it-units (yaw 0 looks toward +z,
1416// x = sin, z = cos: the convention summon() and the page's review steering already share), or -1 when the
1417// identity carries no ocean row. Read by the spawn (the first frame faces the sea), by summon() (the review
1418// ring stands on the seaward arc so the shore is behind the girl the camera frames) and exported as the
1419// sea_yaw door for the HUD and the gate. Data: the same P_OCEAN row wsp_ocean_d decodes -- no second coastline.
1420func wsp_sea_yaw(sp: *i64) -> i64 {
1421 let oc: i64 = sp[P_OCEAN]
1422 if oc == 0 { return 0 - 1 }
1423 let edge: i64 = oc & 3
1424 if edge == 0 { return 0 }
1425 if edge == 1 { return IT_PI }
1426 if edge == 2 { return IT_PI/2 }
1427 return IT_PI*3/2
1428}
1429// the seaward review arc: the summoned cast spans one quarter turn centred on the sea bearing. A quarter turn
1430// is the widest arc whose every member still stands between the player and the shore (tan 45 degrees = 1, so
1431// the gate can assert it with integer arithmetic: |dx| <= dz for a +z sea).
1432const WC_REVIEW_ARC: i64 = IT_PI/2
1433// GE13 -- the ocean shaping rule (gameengine rung wsp_beach_ocean), applied to the PURE noise
1434// height wherever a column sits seaward of the shoreline: ramp the ground from its natural
1435// height down to the SEABED across the SLOPE width, then hold the seabed. gencol's existing
1436// flood rule (P_WATER) then fills the water and its sand rule paints the wet band -- beach,
1437// shallows and open sea all emerge from ONE data row, no per-world code. P_OCEAN == 0 returns
1438// h untouched, so every existing world is byte-identical by construction.
1439func wsp_beach_ocean(sp: *i64, ax: i64, az: i64, h: i64) -> i64 {
1440 let oc: i64 = sp[P_OCEAN]
1441 if oc == 0 { return h }
1442 let d: i64 = wsp_ocean_d(sp, ax, az)
1443 if d <= 0 { return h }
1444 let slopew: i64 = (oc >> 12) & 63
1445 let bed: i64 = (oc >> 18) & 63
1446 var t: i64 = bed
1447 if d < slopew { t = h - (h - bed)*d/slopew }
1448 if t < bed { t = bed }
1449 if t > h { t = h }
1450 return t
1451}
1452
1453func gencol(base: i64, x: i64, z: i64, ax: i64, az: i64, sd: i64) -> i64 {
1454 let sp: *i64 = wsp(base)
1455 let wl: i64 = sp[P_WATER]
1456 let h: i64 = wsp_beach_ocean(sp, ax, az, wheight(base, ax, az, sd)) // GE13: the ocean is data
1457 if sp[P_SURF] == 1 { hq_set(base, x, z, wsp_beach_ocean_q(sp, ax, az, wheight_qe(base, ax, az, sd))) } // GE53: the surface source, kept at Q8
1458 var y: i64 = 0
1459 while y < WY {
1460 var b: i64 = 0
1461 if y <= h {
1462 b = 3 // stone
1463 if y > h - 3 { b = 2 } // dirt cap
1464 if y == h {
1465 b = 1 // grass
1466 if h <= wl { b = 5 } // sand at/below waterline
1467 if h >= sp[P_SNOW] { b = 8 } // snow high up
1468 }
1469 // caves: carve inside the crust (never the floor, never the top 2 = no surface holes,
1470 // and never under water columns so lakes stay sealed)
1471 if b == 3 { if y >= 2 { if y <= h - 2 { if h > wl {
1472 if wnoise3(ax, y, az, 7, sd + 55) > sp[P_CAVETH] { b = 0 }
1473 } } } }
1474 // ores seed the remaining stone: coal common, iron deeper, gold deepest
1475 if b == 3 {
1476 let orr: i64 = whash(ax*7 + y*131, az*11 + y*17, sd + 31) % 997
1477 if orr < 22 { b = 9 }
1478 if y < 14 { if orr >= 22 { if orr < 32 { b = 10 } } }
1479 if y < 9 { if orr >= 32 { if orr < 38 { b = 11 } } }
1480 }
1481 }
1482 if y > h { if y <= wl { b = 4 } } // flood low ground
1483 vset(base, x, y, z, b)
1484 y = y + 1
1485 }
1486 return 0
1487}
1488// paint a voxel ONLY inside the regen box -- a tree may never overwrite terrain (and its journal
1489// overlay) outside the strip being regenerated
1490func vset_box(base: i64, x: i64, y: i64, z: i64, b: i64, lx0: i64, lx1: i64, lz0: i64, lz1: i64) -> i64 {
1491 if x < lx0 { return 0 }
1492 if x >= lx1 { return 0 }
1493 if z < lz0 { return 0 }
1494 if z >= lz1 { return 0 }
1495 return vset(base, x, y, z, b)
1496}
1497// GE17 wsp_palm_shape -- THE PALM (W2 shape id 3, mirroring nx_treegen species 3: dep0=0 trunk-
1498// only + "a ring of LONG radial frond blobs from the trunk top" -- ONE species canon, two lanes,
1499// the PG36 rule). A bare, gently curved trunk with NO branches below the crown, then a frond
1500// rosette. Everything DERIVED, nothing tuned:
1501// LEAN -- phototropic, TOWARD OPEN WATER when the world declares an ocean (P_OCEAN edge
1502// bits: shoreline palms lean seaward for light), hash-picked axis otherwise. Tip
1503// displacement = th9/3 (tan ~18deg of trunk height, the shoreline coconut habit); profile
1504// quadratic in height (uniform curvature): lean(dy) = dy*dy/(3*th9), which lands the tip at
1505// exactly th9/3 with no constant of its own.
1506// FRONDS -- 8 directions (4 axial + 4 diagonal, the voxel ring), length = the genome's own
1507// canopy radius cr9, arch droop(r) = r*r/(2*cr9): the tip ends half the frond length below
1508// the crown, the same quadratic droop nx_treegen's palm crown uses (seg*seg*11 there).
1509// Painting is clipped to the regen box (vset_box) so it is seam-safe under the widened scan
1510// margin gentrees derives for shape 3. Trunk = wood (6), fronds + apical bud = leaves (7);
1511// on the shore identity those palette rows read driftwood-brown and scrub-green already.
1512func wsp_palm_shape(base: i64, sp: *i64, tx: i64, tz: i64, h2: i64, th9: i64, cr9: i64,
1513 ax: i64, az: i64, sd: i64, lx0: i64, lx1: i64, lz0: i64, lz1: i64) -> i64 {
1514 var pldx: i64 = 0
1515 var pldz: i64 = 0
1516 let poc9: i64 = sp[P_OCEAN]
1517 if poc9 != 0 {
1518 let pe9: i64 = poc9 & 3
1519 if pe9 == 0 { pldz = 1 }
1520 if pe9 == 1 { pldz = 0 - 1 }
1521 if pe9 == 2 { pldx = 1 }
1522 if pe9 == 3 { pldx = 0 - 1 }
1523 }
1524 if poc9 == 0 {
1525 let pd9: i64 = whash(ax*19 + 13, az*23 + 29, sd + 251) % 4
1526 if pd9 == 0 { pldx = 1 }
1527 if pd9 == 1 { pldx = 0 - 1 }
1528 if pd9 == 2 { pldz = 1 }
1529 if pd9 == 3 { pldz = 0 - 1 }
1530 }
1531 var pox: i64 = 0
1532 var poz: i64 = 0
1533 var pty: i64 = 1
1534 while pty <= th9 {
1535 let pl9: i64 = pty*pty/(3*th9)
1536 pox = pldx*pl9
1537 poz = pldz*pl9
1538 vset_box(base, tx + pox, h2 + pty, tz + poz, 6, lx0, lx1, lz0, lz1)
1539 pty = pty + 1
1540 }
1541 let pcx: i64 = tx + pox
1542 let pcz: i64 = tz + poz
1543 let ptop: i64 = h2 + th9
1544 vset_box(base, pcx, ptop + 1, pcz, 7, lx0, lx1, lz0, lz1)
1545 var pfd: i64 = 0
1546 while pfd < 8 {
1547 var fdx9: i64 = 0
1548 var fdz9: i64 = 0
1549 if pfd == 0 { fdx9 = 1 }
1550 if pfd == 1 { fdx9 = 0 - 1 }
1551 if pfd == 2 { fdz9 = 1 }
1552 if pfd == 3 { fdz9 = 0 - 1 }
1553 if pfd == 4 { fdx9 = 1; fdz9 = 1 }
1554 if pfd == 5 { fdx9 = 1; fdz9 = 0 - 1 }
1555 if pfd == 6 { fdx9 = 0 - 1; fdz9 = 1 }
1556 if pfd == 7 { fdx9 = 0 - 1; fdz9 = 0 - 1 }
1557 var pr9: i64 = 1
1558 while pr9 <= cr9 {
1559 vset_box(base, pcx + fdx9*pr9, ptop + 1 - pr9*pr9/(2*cr9), pcz + fdz9*pr9, 7, lx0, lx1, lz0, lz1)
1560 pr9 = pr9 + 1
1561 }
1562 pfd = pfd + 1
1563 }
1564 return 0
1565}
1566// trees whose TRUNK falls in the +-2-expanded ABSOLUTE range paint their blocks CLIPPED to the
1567// local box, so a tree crossing a strip seam is completed by whichever strip regenerates later.
1568// The trunk condition is PURE (grass by the gen rules: above water, below snow) -- no window peeking.
1569func gentrees(base: i64, lx0: i64, lx1: i64, lz0: i64, lz1: i64, sd: i64) -> i64 {
1570 let sp: *i64 = wsp(base)
1571 if sp[P_TREE] == 0 { return 0 }
1572 let s: *i64 = wst(base)
1573 let wl: i64 = sp[P_WATER]
1574 // scan expanded to +-4 (was +-2): a SPECIES canopy reaches up to radius 4, so a trunk that
1575 // far outside the box can paint inside it -- the strip-seam completion law, wider. Legacy
1576 // worlds are unchanged: an R=2 stamp 3-4 cells out paints nothing in-box (no-op scan rows).
1577 // GE17: a palm's phototropic lean displaces its crown by up to (tb+tv)/3 beyond the frond
1578 // radius, so a palm-genome world widens the seam scan by that maximum lean -- DERIVED from
1579 // the same genome rows the grower reads, not a second constant. Every other shape keeps
1580 // the historic +-4 margin bit-exactly, and the extra scan rows are no-ops by the vset_box
1581 // clip (the standing seam law, same argument as the +-2 -> +-4 widening above).
1582 var mg9: i64 = 4
1583 if ((sp[P_TREESPEC] >> 12) & 7) == 3 { mg9 = 4 + ((sp[P_TREESPEC] & 15) + ((sp[P_TREESPEC] >> 4) & 15))/3 }
1584 var az: i64 = s[S_WOZ] + lz0 - mg9
1585 while az <= s[S_WOZ] + lz1 + mg9 - 1 {
1586 var ax: i64 = s[S_WOX] + lx0 - mg9
1587 while ax <= s[S_WOX] + lx1 + mg9 - 1 {
1588 let hr9: i64 = wheight(base, ax, az, sd)
1589 let h2: i64 = wsp_beach_ocean(sp, ax, az, hr9) // GE13: no trees grow on the sea
1590 // GE17: DRY LAND is where the ocean rule was a NO-OP (h2 == hr9). Seaward of the
1591 // shoreline the ramp lowered this column -- that whole band is the beach slope and
1592 // its wet sand, and vegetation stays off it (palms out of the water, off the wet
1593 // band, by the ocean row's own geometry rather than a second coastline rule).
1594 // Worlds without an ocean row have h2 == hr9 everywhere BY CONSTRUCTION
1595 // (wsp_beach_ocean returns h untouched), so this conjunct changes nothing there.
1596 var trunk: i64 = 0
1597 // the tree altitude ceiling is a SPEC ROW, not an engine constant: hardcoded 19
1598 // silently capped forests for any identity whose land sits higher (gate T52 found
1599 // it -- vale's P_TREE 43->19 barely moved the trunk count because the CEILING, not
1600 // the density row, was binding). 0 = the historic 19, so craft/depths are unchanged.
1601 var tmax: i64 = sp[P_TREEMAX]
1602 if tmax <= 0 { tmax = 19 }
1603 if h2 < tmax { if h2 > wl { if h2 < sp[P_SNOW] { if h2 == hr9 {
1604 if whash(ax*3 + 7, az*5 + 11, sd + 99) % sp[P_TREE] == 0 { trunk = 1 }
1605 } } } }
1606 if trunk == 1 {
1607 let tx: i64 = ax - s[S_WOX]
1608 let tz: i64 = az - s[S_WOZ]
1609 let ts9: i64 = sp[P_TREESPEC]
1610 // W2b: the TREE branch keys on the tree bits (0..17) alone -- a genome carrying
1611 // only understory bits (18+) keeps the LEGACY forest exactly (T57's wood-equality
1612 // tooth), and understory grows in its own pass below regardless of trunk sites.
1613 if (ts9 & 262143) == 0 {
1614 // legacy stamp, byte-for-byte: every identity-only world is unchanged
1615 var ty: i64 = h2 + 1
1616 while ty <= h2 + 4 { vset_box(base, tx, ty, tz, 6, lx0, lx1, lz0, lz1); ty = ty + 1 }
1617 var lz: i64 = tz - 2
1618 while lz <= tz + 2 {
1619 var lx: i64 = tx - 2
1620 while lx <= tx + 2 {
1621 var keep: i64 = 1
1622 if lx == tx { if lz == tz { keep = 0 } }
1623 if keep == 1 {
1624 vset_box(base, lx, h2 + 4, lz, 7, lx0, lx1, lz0, lz1)
1625 if lx >= tx - 1 { if lx <= tx + 1 { if lz >= tz - 1 { if lz <= tz + 1 {
1626 vset_box(base, lx, h2 + 5, lz, 7, lx0, lx1, lz0, lz1)
1627 } } } }
1628 }
1629 lx = lx + 1
1630 }
1631 lz = lz + 1
1632 }
1633 vset_box(base, tx, h2 + 6, tz, 7, lx0, lx1, lz0, lz1)
1634 } else {
1635 // W2 SPECIES GROWER: the individual varies inside the species envelope by
1636 // position hash -- deterministic, seam-safe (paint clipped to the box),
1637 // asset-free. Shapes: 1 dome (squashed integer ellipsoid), 2 pine (diamond
1638 // rings widening down from the tip). Density drops leaves by hash for airy
1639 // canopies. All bounds y-clamped by vset's own vin guard.
1640 let tb9: i64 = ts9 & 15
1641 let tv9: i64 = (ts9 >> 4) & 15
1642 var cr9: i64 = (ts9 >> 8) & 15
1643 if cr9 < 1 { cr9 = 1 }
1644 if cr9 > 4 { cr9 = 4 }
1645 let sh9: i64 = (ts9 >> 12) & 7
1646 let dn9: i64 = (ts9 >> 15) & 7
1647 let hv9: i64 = whash(ax*13 + 5, az*17 + 3, sd + 141)
1648 var th9: i64 = tb9 + hv9 % (tv9 + 1)
1649 if th9 < 2 { th9 = 2 }
1650 if sh9 == 3 {
1651 // GE17: shape 3 = PALM, the frond-rosette grower (wsp_palm_shape).
1652 // dn9 (leaf drop) is deliberately unused there: fronds are already
1653 // sparse lines, not a volume to thin.
1654 wsp_palm_shape(base, sp, tx, tz, h2, th9, cr9, ax, az, sd, lx0, lx1, lz0, lz1)
1655 } else {
1656 var ty2: i64 = h2 + 1
1657 while ty2 <= h2 + th9 { vset_box(base, tx, ty2, tz, 6, lx0, lx1, lz0, lz1); ty2 = ty2 + 1 }
1658 let top9: i64 = h2 + th9
1659 var dy9: i64 = 0 - cr9
1660 while dy9 <= cr9 + 1 {
1661 var dz9: i64 = 0 - cr9
1662 while dz9 <= cr9 {
1663 var dx9: i64 = 0 - cr9
1664 while dx9 <= cr9 {
1665 var put9: i64 = 0
1666 if sh9 == 2 {
1667 if dy9 <= 1 {
1668 var pr9: i64 = 1 - dy9
1669 if pr9 > cr9 { pr9 = cr9 }
1670 var ad9: i64 = dx9
1671 if ad9 < 0 { ad9 = 0 - ad9 }
1672 var az9: i64 = dz9
1673 if az9 < 0 { az9 = 0 - az9 }
1674 if ad9 + az9 <= pr9 { put9 = 1 }
1675 }
1676 } else {
1677 if dx9*dx9 + dz9*dz9 + dy9*dy9*2 <= cr9*cr9 { put9 = 1 }
1678 }
1679 if put9 == 1 { if dn9 > 0 {
1680 if whash(ax*29 + dx9*3 + dy9*7, az*31 + dz9*5, sd + 177) % 8 < dn9 { put9 = 0 }
1681 } }
1682 if put9 == 1 {
1683 var istrunk9: i64 = 0
1684 if dx9 == 0 { if dz9 == 0 { if dy9 < 0 { istrunk9 = 1 } } }
1685 if istrunk9 == 0 { vset_box(base, tx + dx9, top9 + dy9, tz + dz9, 7, lx0, lx1, lz0, lz1) }
1686 }
1687 dx9 = dx9 + 1
1688 }
1689 dz9 = dz9 + 1
1690 }
1691 dy9 = dy9 + 1
1692 }
1693 }
1694 }
1695 }
1696 // W2b UNDERSTORY (genome bits 18-20 density, 21-22 kind 0=tufts 1=shrubs 2=mixed):
1697 // ground vegetation per column, seeded independently of trunks, same grass belt,
1698 // trunk columns excluded; shrub = plus-clump radius 1 (inside the +-4 seam scan).
1699 let ud9: i64 = (sp[P_TREESPEC] >> 18) & 7
1700 if ud9 > 0 { if trunk == 0 { if h2 > wl { if h2 < sp[P_SNOW] { if h2 == hr9 {
1701 if whash(ax*41 + 3, az*43 + 9, sd + 203) % 8 < ud9 {
1702 let uk9: i64 = (sp[P_TREESPEC] >> 21) & 3
1703 let ux5: i64 = ax - s[S_WOX]
1704 let uz5: i64 = az - s[S_WOZ]
1705 var shrub9: i64 = 0
1706 if uk9 == 1 { shrub9 = 1 }
1707 if uk9 == 2 { if whash(ax*7 + 1, az*11 + 5, sd + 209) % 2 == 0 { shrub9 = 1 } }
1708 vset_box(base, ux5, h2 + 1, uz5, 7, lx0, lx1, lz0, lz1)
1709 if shrub9 == 1 {
1710 // vegetation FILLS AIR, never replaces solids: a clump reaching into a
1711 // neighbor column must not eat its trunk or its surface (T57 measured the
1712 // unguarded version eating 792 wood blocks). Deterministic under the fixed
1713 // scan order -- a later trunk overwrites a leaf, so wood wins either way.
1714 if vget(base, ux5 + 1, h2 + 1, uz5) == 0 { vset_box(base, ux5 + 1, h2 + 1, uz5, 7, lx0, lx1, lz0, lz1) }
1715 if vget(base, ux5 - 1, h2 + 1, uz5) == 0 { vset_box(base, ux5 - 1, h2 + 1, uz5, 7, lx0, lx1, lz0, lz1) }
1716 if vget(base, ux5, h2 + 1, uz5 + 1) == 0 { vset_box(base, ux5, h2 + 1, uz5 + 1, 7, lx0, lx1, lz0, lz1) }
1717 if vget(base, ux5, h2 + 1, uz5 - 1) == 0 { vset_box(base, ux5, h2 + 1, uz5 - 1, 7, lx0, lx1, lz0, lz1) }
1718 if vget(base, ux5, h2 + 2, uz5) == 0 { vset_box(base, ux5, h2 + 2, uz5, 7, lx0, lx1, lz0, lz1) }
1719 }
1720 }
1721 } } } } }
1722 ax = ax + 1
1723 }
1724 az = az + 1
1725 }
1726 return 0
1727}
1728
1729// ---------- W3 SETTLEMENTS (GR6, the Infinigen-Indoors expansion, 2026-08-17) ----------
1730// Room LAYOUTS are solved OFFLINE by nx_indoorgen -- declarative constraints through the estate's
1731// part-placement solver, quantized, accept-rule-checked, REFUSED when unsatisfiable -- and land
1732// here as the imported data table (nx_indoorgen_data.nx). The engine only STAMPS: sites ride a
1733// coarse grid of BLDG_SITE-block cells hashed on ABSOLUTE coords (window-independent, so a
1734// building regenerates identically when the streaming window slides back -- the tree seam law at
1735// building scale); jitter, rotation and layout variant come from the same hash; and a building
1736// always fits INSIDE its own site cell (jitter 8 + max half-footprint 5 < SITE/2 = 20), so two
1737// sites can never collide. Terrain must be near-flat (4 rotated corners within 1 of the anchor
1738// height), dry and below the snowline, or the site deterministically stays empty. Painting is
1739// CLIPPED to the regen box (vset_box), and ej_overlay runs after every strip regen -- players can
1740// remodel a building and their edits win, exactly as they do against trees.
1741const BLDG_SITE: i64 = 40 // site-cell stride, blocks
1742const IG_SCAN0: i64 = 10 // W3 solve: first candidate anchor offset inside the site cell
1743const IG_SCANSTEP: i64 = 10 // candidate grid step: 3x3 covers offsets 10/20/30; footprint
1744 // half-extent (bw/2+1 <= 4) keeps every candidate's shell inside
1745 // its own cell, so neighbor cells can never overlap -- the
1746 // no-overlap rule holds by construction, not by test
1747const IG_MAXSLOPE: i64 = 2 // pre-declared accept rule: corners may deviate <=2 blocks from
1748 // the anchor; the IG_FOUND skirt reaches ground at exactly this
1749 // bound, so an accepted site is grounded. An unsatisfiable cell
1750 // stamps NOTHING (refuse, never float -- the constraint-solve law)
1751const IG_FOUND: i64 = 3 // foundation skirt courses below the floor (IG_MAXSLOPE + 1)
1752
1753// floor division for ABSOLUTE (possibly negative) coords -- truncating division reads the cell
1754// left of origin as cell 0 twice, which would double-place one building at the world seam
1755func igfloor(a: i64, b: i64) -> i64 {
1756 var q: i64 = a/b
1757 if a % b < 0 { q = q - 1 }
1758 return q
1759}
1760func ig_rotx(r: i64, x: i64, z: i64) -> i64 {
1761 if r == 1 { return z }
1762 if r == 2 { return 0 - x }
1763 if r == 3 { return 0 - z }
1764 return x
1765}
1766func ig_rotz(r: i64, x: i64, z: i64) -> i64 {
1767 if r == 1 { return 0 - x }
1768 if r == 2 { return 0 - z }
1769 if r == 3 { return x }
1770 return z
1771}
1772func genbuildings(base: i64, lx0: i64, lx1: i64, lz0: i64, lz1: i64, sd: i64) -> i64 {
1773 let sp: *i64 = wsp(base)
1774 if sp[P_BLDG] <= 0 { return 0 }
1775 let s: *i64 = wst(base)
1776 let wl: i64 = sp[P_WATER]
1777 let arch: i64 = sp[P_BLDGSPEC] & 15
1778 let dims: i64 = igd_dims(arch)
1779 let bw: i64 = dims & 255
1780 let bd: i64 = (dims >> 8) & 255
1781 let bh: i64 = (dims >> 16) & 255
1782 let mats: i64 = igd_mats(arch)
1783 let mwall: i64 = mats & 255
1784 let mfloor: i64 = (mats >> 8) & 255
1785 let mroof: i64 = (mats >> 16) & 255
1786 let dxo: i64 = igd_doorx(arch)
1787 let gx0: i64 = igfloor(s[S_WOX] + lx0 - BLDG_SITE + 1, BLDG_SITE)
1788 let gx1: i64 = igfloor(s[S_WOX] + lx1 + BLDG_SITE - 1, BLDG_SITE)
1789 let gz0: i64 = igfloor(s[S_WOZ] + lz0 - BLDG_SITE + 1, BLDG_SITE)
1790 let gz1: i64 = igfloor(s[S_WOZ] + lz1 + BLDG_SITE - 1, BLDG_SITE)
1791 var gz: i64 = gz0
1792 while gz <= gz1 {
1793 var gx: i64 = gx0
1794 while gx <= gx1 {
1795 var put: i64 = 1
1796 if whash(gx*7 + 1, gz*13 + 5, sd + 331) % sp[P_BLDG] != 0 { put = 0 }
1797 let rot: i64 = whash(gx*17 + 2, gz*19 + 6, sd + 499) % 4
1798 let bv: i64 = whash(gx*23 + 4, gz*29 + 8, sd + 541) % IGD_NVAR
1799 // W3 SOLVE: the anchor is not rolled, it is SEARCHED. 3x3 candidates per cell,
1800 // flattest wins (first-min on the fixed scan order = deterministic), and wheight is
1801 // pure noise over absolute coords, so the choice is window-independent (T73's law).
1802 // The measured failure this replaces: one hash-jittered roll + a dh>1 veto rejected
1803 // EVERY vale and craft cell (probe 2026-08-18: dhmax 2..12 across 36 cells) -- a
1804 // stamp with a veto is not a solve, and the village never existed.
1805 var axc: i64 = 0
1806 var azc: i64 = 0
1807 var h: i64 = 0
1808 var bestd9: i64 = 99
1809 if put == 1 {
1810 var co9: i64 = 0
1811 while co9 < 9 {
1812 let cox9: i64 = gx*BLDG_SITE + IG_SCAN0 + (co9 % 3)*IG_SCANSTEP
1813 let coz9: i64 = gz*BLDG_SITE + IG_SCAN0 + (co9 / 3)*IG_SCANSTEP
1814 let hh9: i64 = wsp_beach_ocean(sp, cox9, coz9, wheight(base, cox9, coz9, sd)) // GE13: a site in the sea reads its SHAPED height -> the h<=wl veto below refuses it
1815 var dmax9: i64 = 0
1816 var ci: i64 = 0
1817 while ci < 4 {
1818 var clx: i64 = 0 - 1
1819 var clz: i64 = 0 - 1
1820 if ci == 1 { clx = bw }
1821 if ci == 2 { clz = bd }
1822 if ci == 3 { clx = bw; clz = bd }
1823 let cax: i64 = cox9 + ig_rotx(rot, clx - bw/2, clz - bd/2)
1824 let caz: i64 = coz9 + ig_rotz(rot, clx - bw/2, clz - bd/2)
1825 var dh: i64 = wsp_beach_ocean(sp, cax, caz, wheight(base, cax, caz, sd)) - hh9
1826 if dh < 0 { dh = 0 - dh }
1827 if dh > dmax9 { dmax9 = dh }
1828 ci = ci + 1
1829 }
1830 if dmax9 < bestd9 { bestd9 = dmax9; axc = cox9; azc = coz9; h = hh9 }
1831 co9 = co9 + 1
1832 }
1833 if bestd9 > IG_MAXSLOPE { put = 0 }
1834 if h <= wl { put = 0 }
1835 if h >= sp[P_SNOW] { put = 0 }
1836 if h + bh + 2 >= WY { put = 0 }
1837 }
1838 if put == 1 {
1839 // floor+foundation ring at h, walls h+1..h+bh (door and windows carved as air,
1840 // interior cleared of terrain and canopy), roof plate at h+bh+1
1841 var lz: i64 = 0 - 1
1842 while lz <= bd {
1843 var lx: i64 = 0 - 1
1844 while lx <= bw {
1845 let ax: i64 = axc + ig_rotx(rot, lx - bw/2, lz - bd/2) - s[S_WOX]
1846 let az: i64 = azc + ig_rotz(rot, lx - bw/2, lz - bd/2) - s[S_WOZ]
1847 var ring: i64 = 0
1848 if lx == 0 - 1 { ring = 1 }
1849 if lx == bw { ring = 1 }
1850 if lz == 0 - 1 { ring = 1 }
1851 if lz == bd { ring = 1 }
1852 var fb2: i64 = mfloor
1853 if ring == 1 { fb2 = mwall }
1854 vset_box(base, ax, h, az, fb2, lx0, lx1, lz0, lz1)
1855 // foundation skirt: IG_FOUND courses below the floor on the ring, so a
1856 // site accepted at the IG_MAXSLOPE bound is grounded on every side. On
1857 // flat ground the courses embed in terrain (a real footing, seen when
1858 // mined) -- bounded, deterministic, no reads.
1859 if ring == 1 {
1860 var fy9: i64 = h - 1
1861 while fy9 >= h - IG_FOUND {
1862 if fy9 >= 0 { vset_box(base, ax, fy9, az, mwall, lx0, lx1, lz0, lz1) }
1863 fy9 = fy9 - 1
1864 }
1865 }
1866 var wy2: i64 = h + 1
1867 while wy2 <= h + bh {
1868 var wb2: i64 = 0
1869 if ring == 1 {
1870 wb2 = mwall
1871 if lx == dxo { if lz == 0 - 1 { if wy2 <= h + 2 { wb2 = 0 } } }
1872 if wy2 == h + 2 { if lz == bd/2 {
1873 if lx == 0 - 1 { wb2 = 0 }
1874 if lx == bw { wb2 = 0 }
1875 } }
1876 }
1877 vset_box(base, ax, wy2, az, wb2, lx0, lx1, lz0, lz1)
1878 wy2 = wy2 + 1
1879 }
1880 vset_box(base, ax, h + bh + 1, az, mroof, lx0, lx1, lz0, lz1)
1881 lx = lx + 1
1882 }
1883 lz = lz + 1
1884 }
1885 // the solved furniture rows, rotated with the shell
1886 var fi: i64 = 0
1887 while fi < igd_nf(arch, bv) {
1888 let fr: i64 = igd_f(arch, bv, fi)
1889 let fax: i64 = axc + ig_rotx(rot, (fr & 255) - bw/2, ((fr >> 8) & 255) - bd/2) - s[S_WOX]
1890 let faz: i64 = azc + ig_rotz(rot, (fr & 255) - bw/2, ((fr >> 8) & 255) - bd/2) - s[S_WOZ]
1891 vset_box(base, fax, h + ((fr >> 24) & 255), faz, (fr >> 16) & 255, lx0, lx1, lz0, lz1)
1892 fi = fi + 1
1893 }
1894 // world truth: count the stamp when its anchor lands inside the regen box
1895 let axl: i64 = axc - s[S_WOX]
1896 let azl: i64 = azc - s[S_WOZ]
1897 if axl >= lx0 { if axl < lx1 { if azl >= lz0 { if azl < lz1 {
1898 s[S_BLDG] = s[S_BLDG] + 1
1899 s[S_BLDGX] = axl
1900 s[S_BLDGY] = h
1901 s[S_BLDGZ] = azl
1902 } } } }
1903 }
1904 gx = gx + 1
1905 }
1906 gz = gz + 1
1907 }
1908 return 0
1909}
1910
1911func genworld(base: i64, sd: i64) -> i64 {
1912 let s: *i64 = wst(base)
1913 var z: i64 = 0
1914 while z < WZ {
1915 var x: i64 = 0
1916 while x < WX {
1917 gencol(base, x, z, x + s[S_WOX], z + s[S_WOZ], sd)
1918 x = x + 1
1919 }
1920 z = z + 1
1921 }
1922 gentrees(base, 0, WX, 0, WZ, sd)
1923 genbuildings(base, 0, WX, 0, WZ, sd)
1924 return 0
1925}
1926
1927// ---------- v6 STREAMING: slide the window one stride along x or z ----------
1928// The world is unbounded; the arena is the cache. Order matters: move kept voxels -> bump the
1929// origin -> regen the incoming strip from absolute coords -> repaint its trees -> overlay the
1930// journal (edits WIN) -> translate every arena-space coordinate (player, spawn, mobs) -> rebuild
1931// wmax -> invalidate the crosshair cache. One call = 16 blocks; ticks trigger it at the margins.
1932func wc_shift(base: i64, dx: i64, dz: i64) -> i64 {
1933 let s: *i64 = wst(base)
1934 let v: *u8 = wvx(base)
1935 let sd: i64 = s[S_SEED]
1936 if dx != 0 {
1937 var y: i64 = 0
1938 while y < WY {
1939 var z: i64 = 0
1940 while z < WZ {
1941 let row: i64 = (y*WZ + z)*WX
1942 if dx > 0 {
1943 var x: i64 = 0
1944 while x < WX - dx { v[row + x] = v[row + x + dx]; x = x + 1 }
1945 } else {
1946 var x2: i64 = WX - 1
1947 while x2 >= 0 - dx { v[row + x2] = v[row + x2 + dx]; x2 = x2 - 1 }
1948 }
1949 z = z + 1
1950 }
1951 y = y + 1
1952 }
1953 s[S_WOX] = s[S_WOX] + dx
1954 var sx0: i64 = 0
1955 var sx1: i64 = 0 - dx
1956 if dx > 0 { sx0 = WX - dx; sx1 = WX }
1957 var rz: i64 = 0
1958 while rz < WZ {
1959 var rx: i64 = sx0
1960 while rx < sx1 { gencol(base, rx, rz, rx + s[S_WOX], rz + s[S_WOZ], sd); rx = rx + 1 }
1961 rz = rz + 1
1962 }
1963 gentrees(base, sx0, sx1, 0, WZ, sd)
1964 genbuildings(base, sx0, sx1, 0, WZ, sd)
1965 ej_overlay(base, sx0, sx1, 0, WZ)
1966 }
1967 if dz != 0 {
1968 var y2: i64 = 0
1969 while y2 < WY {
1970 let plane: i64 = y2*WZ*WX
1971 if dz > 0 {
1972 var z2: i64 = 0
1973 while z2 < WZ - dz {
1974 let dstr: i64 = plane + z2*WX
1975 let srcr: i64 = plane + (z2 + dz)*WX
1976 var x3: i64 = 0
1977 while x3 < WX { v[dstr + x3] = v[srcr + x3]; x3 = x3 + 1 }
1978 z2 = z2 + 1
1979 }
1980 } else {
1981 var z3: i64 = WZ - 1
1982 while z3 >= 0 - dz {
1983 let dstr2: i64 = plane + z3*WX
1984 let srcr2: i64 = plane + (z3 + dz)*WX
1985 var x4: i64 = 0
1986 while x4 < WX { v[dstr2 + x4] = v[srcr2 + x4]; x4 = x4 + 1 }
1987 z3 = z3 - 1
1988 }
1989 }
1990 y2 = y2 + 1
1991 }
1992 s[S_WOZ] = s[S_WOZ] + dz
1993 var sz0: i64 = 0
1994 var sz1: i64 = 0 - dz
1995 if dz > 0 { sz0 = WZ - dz; sz1 = WZ }
1996 var rz2: i64 = sz0
1997 while rz2 < sz1 {
1998 var rx2: i64 = 0
1999 while rx2 < WX { gencol(base, rx2, rz2, rx2 + s[S_WOX], rz2 + s[S_WOZ], sd); rx2 = rx2 + 1 }
2000 rz2 = rz2 + 1
2001 }
2002 gentrees(base, 0, WX, sz0, sz1, sd)
2003 genbuildings(base, 0, WX, sz0, sz1, sd)
2004 ej_overlay(base, 0, WX, sz0, sz1)
2005 }
2006 // translate every arena-space coordinate by the slide
2007 s[S_CX] = s[S_CX] - dx*256
2008 s[S_CZ] = s[S_CZ] - dz*256
2009 s[S_SPX] = wclamp(s[S_SPX] - dx*256, 2*256, (WX - 2)*256)
2010 s[S_SPZ] = wclamp(s[S_SPZ] - dz*256, 2*256, (WZ - 2)*256)
2011 s[S_LBX] = s[S_LBX] - dx
2012 s[S_LBZ] = s[S_LBZ] - dz
2013 let m: *i64 = mobp(base)
2014 var k: i64 = 0
2015 while k < en_count(m) {
2016 let h: i64 = en_nth(m, k)
2017 en_set(m, h, MC_X, wclamp(en_get(m, h, MC_X) - dx*256, 2*256, (WX - 2)*256))
2018 en_set(m, h, MC_Z, wclamp(en_get(m, h, MC_Z) - dz*256, 2*256, (WZ - 2)*256))
2019 k = k + 1
2020 }
2021 wc_wmax(base)
2022 wc_soft_seat_all(base) // the world slid under every girl: re-seat springs
2023 if dx != 0 { s[S_AGEN] = s[S_AGEN] + 1 } else { if dz != 0 { s[S_AGEN] = s[S_AGEN] + 1 } } // GE57: a shift that MOVED is a new arena generation (a zero shift is not)
2024 s[S_HOK] = 0
2025 return 0
2026}
2027
2028// ---------- DDA raycast ----------
2029// marches from (S_CX,S_CY,S_CZ) along (dx,dy,dz) (any scale). On hit writes out[0..4] =
2030// cellx,celly,cellz,face,tcross and returns the step count; returns -1 on miss (sky).
2031// skipw=1 treats water as air (the transparency continuation ray). The voxel read is INLINED --
2032// this loop runs ~2-8M times a second and a per-step call was the measured overhead.
2033// GE53: march the continuous surface. pos(t) = cam + d*t/65536 (Q8). Quarter-cell steps along the fastest
2034// axis, then six bisections at the crossing -- sub-block precision on a field that is itself sub-block.
2035// Returns the cells travelled (the fog term); writes the column's top cell, face 3 and t. -1 = no ground.
2036func wray_surf(base: i64, dx: i64, dy: i64, dz: i64, maxsteps: i64, out: *i64) -> i64 {
2037 let s: *i64 = wst(base)
2038 let cxq: i64 = s[S_CX]
2039 let cyq: i64 = s[S_CY]
2040 let czq: i64 = s[S_CZ]
2041 var dm: i64 = dx
2042 if dm < 0 { dm = 0 - dm }
2043 var ady: i64 = dy
2044 if ady < 0 { ady = 0 - ady }
2045 var adz: i64 = dz
2046 if adz < 0 { adz = 0 - adz }
2047 if ady > dm { dm = ady }
2048 if adz > dm { dm = adz }
2049 if dm == 0 { return 0 - 1 }
2050 let dt: i64 = WC_SURF_STEP_Q8*WATER_MAGIC_65536/dm // a quarter cell along the fastest axis
2051 var t: i64 = 0
2052 var tprev: i64 = 0
2053 var step: i64 = 0
2054 let nmax: i64 = maxsteps*4
2055 var hit: i64 = 0
2056 while step < nmax {
2057 t = t + dt
2058 let py: i64 = cyq + dy*t/WATER_MAGIC_65536
2059 let px: i64 = cxq + dx*t/WATER_MAGIC_65536
2060 let pz: i64 = czq + dz*t/WATER_MAGIC_65536
2061 if dy >= 0 { if py > WY*256 { return 0 - 1 } } // climbing above every column: sky
2062 if px < 0 - 8*256 { return 0 - 1 }
2063 if px > (WX + 8)*256 { return 0 - 1 }
2064 if pz < 0 - 8*256 { return 0 - 1 }
2065 if pz > (WZ + 8)*256 { return 0 - 1 }
2066 let hs: i64 = wc_surface_source(base, px, pz)
2067 if hs >= 0 { if py <= hs { hit = 1; step = nmax } }
2068 if hit == 0 { tprev = t; step = step + 1 }
2069 }
2070 if hit == 0 { return 0 - 1 }
2071 var lo: i64 = tprev
2072 var hi: i64 = t
2073 var k: i64 = 0
2074 while k < 6 {
2075 let mid: i64 = (lo + hi)/2
2076 let py2: i64 = cyq + dy*mid/WATER_MAGIC_65536
2077 let px2: i64 = cxq + dx*mid/WATER_MAGIC_65536
2078 let pz2: i64 = czq + dz*mid/WATER_MAGIC_65536
2079 let h2: i64 = wc_surface_source(base, px2, pz2)
2080 var below: i64 = 0
2081 if h2 >= 0 { if py2 <= h2 { below = 1 } }
2082 if below == 1 { hi = mid } else { lo = mid }
2083 k = k + 1
2084 }
2085 let th: i64 = hi
2086 let hx: i64 = cxq + dx*th/WATER_MAGIC_65536
2087 let hz: i64 = czq + dz*th/WATER_MAGIC_65536
2088 let cx: i64 = hx >> 8
2089 let cz: i64 = hz >> 8
2090 // the colour cell: the column's top SOLID block, found by scanning down from the hit cell -- so it
2091 // matches whatever gencol cut, whichever rounding convention built the column (the field rounds,
2092 // wheight_raw truncates; guessing the index from the field put the cell one block up, in air)
2093 let hy: i64 = cyq + dy*th/WATER_MAGIC_65536
2094 var yy: i64 = hy >> 8
2095 if yy > WY - 1 { yy = WY - 1 }
2096 var run9: i64 = 1
2097 while run9 == 1 {
2098 if yy <= 0 { run9 = 0 }
2099 if run9 == 1 { if vget(base, cx, yy, cz) != 0 { run9 = 0 } }
2100 if run9 == 1 { yy = yy - 1 }
2101 }
2102 out[0] = cx
2103 out[1] = yy
2104 out[2] = cz
2105 out[3] = 3
2106 out[4] = th
2107 return th/(dt*(256/WC_SURF_STEP_Q8)) + 1
2108}
2109// GE53: under P_SURF the ground is the field and structures stay cells -- the nearer hit wins. With the
2110// row OFF this is exactly the cell marcher (wray3 with no terrain skip), so every shipped world is untouched.
2111func wray2(base: i64, dx: i64, dy: i64, dz: i64, maxsteps: i64, out: *i64, skipw: i64) -> i64 {
2112 if wsp(base)[P_SURF] != 1 { return wray3(base, dx, dy, dz, maxsteps, out, skipw, 0) }
2113 let ss: i64 = wray_surf(base, dx, dy, dz, maxsteps, out)
2114 let s0: i64 = out[0]
2115 let s1: i64 = out[1]
2116 let s2: i64 = out[2]
2117 let s3: i64 = out[3]
2118 let s4: i64 = out[4]
2119 let sd: i64 = wray3(base, dx, dy, dz, maxsteps, out, skipw, 1)
2120 if sd < 0 {
2121 if ss < 0 { return 0 - 1 }
2122 out[0] = s0
2123 out[1] = s1
2124 out[2] = s2
2125 out[3] = s3
2126 out[4] = s4
2127 return ss
2128 }
2129 if ss < 0 { return sd }
2130 if out[4] <= s4 { return sd }
2131 out[0] = s0
2132 out[1] = s1
2133 out[2] = s2
2134 out[3] = s3
2135 out[4] = s4
2136 return ss
2137}
2138func wray3(base: i64, dx: i64, dy: i64, dz: i64, maxsteps: i64, out: *i64, skipw: i64, skipt: i64) -> i64 {
2139 let s: *i64 = wst(base)
2140 var cxq: i64 = s[S_CX]
2141 var cyq: i64 = s[S_CY]
2142 var czq: i64 = s[S_CZ]
2143 var cellx: i64 = cxq >> 8
2144 var celly: i64 = cyq >> 8
2145 var cellz: i64 = czq >> 8
2146 let HUGE: i64 = RAY_HUGE
2147 var stpx: i64 = 1
2148 if dx < 0 { stpx = 0-1 }
2149 var stpy: i64 = 1
2150 if dy < 0 { stpy = 0-1 }
2151 var stpz: i64 = 1
2152 if dz < 0 { stpz = 0-1 }
2153 // t units: pos_q8(t) = pos + d*t/65536; crossing one cell on axis c costs 256*65536/|dc|
2154 var tdx: i64 = HUGE
2155 if dx != 0 { tdx = 256*WATER_MAGIC_65536 / dx; if tdx < 0 { tdx = 0 - tdx } }
2156 var tdy: i64 = HUGE
2157 if dy != 0 { tdy = 256*WATER_MAGIC_65536 / dy; if tdy < 0 { tdy = 0 - tdy } }
2158 var tdz: i64 = HUGE
2159 if dz != 0 { tdz = 256*WATER_MAGIC_65536 / dz; if tdz < 0 { tdz = 0 - tdz } }
2160 var bx: i64 = (cellx << 8) - cxq // distance to boundary, Q8 (negative side)
2161 if stpx == 1 { bx = bx + 256 }
2162 var tmx: i64 = HUGE
2163 if dx != 0 { tmx = bx*WATER_MAGIC_65536 / dx; if tmx < 0 { tmx = 0 - tmx } }
2164 var by: i64 = (celly << 8) - cyq
2165 if stpy == 1 { by = by + 256 }
2166 var tmy: i64 = HUGE
2167 if dy != 0 { tmy = by*WATER_MAGIC_65536 / dy; if tmy < 0 { tmy = 0 - tmy } }
2168 var bz: i64 = (cellz << 8) - czq
2169 if stpz == 1 { bz = bz + 256 }
2170 var tmz: i64 = HUGE
2171 if dz != 0 { tmz = bz*WATER_MAGIC_65536 / dz; if tmz < 0 { tmz = 0 - tmz } }
2172 let vxp: *u8 = (base + O_VOX) as *u8
2173 var step: i64 = 0
2174 while step < maxsteps {
2175 var face: i64 = 0
2176 var tcross: i64 = 0
2177 if tmx <= tmy { if tmx <= tmz {
2178 cellx = cellx + stpx
2179 tcross = tmx
2180 tmx = tmx + tdx
2181 face = 1
2182 if stpx == 1 { face = 2 } // stepped +x => hit the -x... face 2 = west-lit
2183 } }
2184 if face == 0 { if tmy <= tmz {
2185 celly = celly + stpy
2186 tcross = tmy
2187 tmy = tmy + tdy
2188 face = 3
2189 if stpy == 1 { face = 4 } // stepped up => hit the block's bottom
2190 } }
2191 if face == 0 {
2192 cellz = cellz + stpz
2193 tcross = tmz
2194 tmz = tmz + tdz
2195 face = 5
2196 if stpz == 1 { face = 6 }
2197 }
2198 if celly >= WY { if stpy >= 0 { return 0 - 1 } } // heading skyward above the world
2199 if celly > s[S_WMAX] { if stpy >= 0 { return 0 - 1 } } // above every solid block, not diving
2200 if cellx < 0-8 { return 0 - 1 }
2201 if cellx > WX+8 { return 0 - 1 }
2202 if cellz < 0-8 { return 0 - 1 }
2203 if cellz > WZ+8 { return 0 - 1 }
2204 if celly < 0 { return 0 - 1 }
2205 var b: i64 = 0
2206 if cellx >= 0 { if cellx < WX { if celly < WY { if cellz >= 0 { if cellz < WZ {
2207 b = (vxp[(celly*WZ + cellz)*WX + cellx] & 0xff) as i64
2208 } } } } }
2209 if skipw == 1 { if b == 4 { b = 0 } }
2210 if skipt == 1 { if wterrain(b) == 1 { b = 0 } } // GE53: the ground is the field, not these cells
2211 if b != 0 {
2212 out[0] = cellx
2213 out[1] = celly
2214 out[2] = cellz
2215 out[3] = face
2216 out[4] = tcross // crossing t: hit point = pos + d*t/WATER_MAGIC_65536 (Q8)
2217 return step + 1
2218 }
2219 step = step + 1
2220 }
2221 return 0 - 1
2222}
2223
2224func wray(base: i64, dx: i64, dy: i64, dz: i64, maxsteps: i64, out: *i64) -> i64 {
2225 return wray2(base, dx, dy, dz, maxsteps, out, 0)
2226}
2227
2228// block base colour -- from the SPEC PALETTE (the game's look is data, not code)
2229func wcol(base: i64, b: i64) -> i64 {
2230 if b < 0 { return wpack(255, 0, 255) }
2231 if b >= 12 { if b <= 16 { return wsp(base)[P_PAL2 + b - 12] } }
2232 if b > 16 { return wpack(255, 0, 255) }
2233 if b > 11 { return wpack(255, 0, 255) }
2234 return wsp(base)[P_PAL + b]
2235}
2236// hotbar slot -> placeable block type
2237func sel_block(sel: i64) -> i64 {
2238 if sel == 1 { return 2 } // dirt
2239 if sel == 2 { return 6 } // wood
2240 if sel == 3 { return 7 } // leaves
2241 if sel == 4 { return 5 } // sand
2242 if sel == 5 { return B_FARM } // the HOE: tills the targeted grass/dirt top
2243 if sel == 6 { return B_CROP0 } // SEEDS: plant onto farmland
2244 return 3 // stone
2245}
2246// face brightness permil: top lit, bottom dark, x/z sides distinct so edges READ as 3d
2247func wface_l(face: i64) -> i64 {
2248 if face == 3 { return 1000 }
2249 if face == 4 { return 420 }
2250 if face == 1 { return 760 }
2251 if face == 2 { return 700 }
2252 if face == 5 { return 620 }
2253 return 560
2254}
2255func wsky(base: i64, syy: i64) -> i64 {
2256 let sp: *i64 = wsp(base)
2257 let hor: i64 = sp[P_SKYHOR]
2258 let top: i64 = sp[P_SKYTOP]
2259 // DERIVED, NOT PICKED (2026-08-29). RH is the q=8 fossil: line 91 of this file declares that
2260 // no resolution number below it is chosen by hand, and RW/RH/FOC three lines under that are
2261 // exactly 1920/8, 1200/8 and 1920/16. This function divided the horizon-to-zenith ramp by that
2262 // frozen 150 while the frame is wc_rh(base) tall, so for any q < 8 the ramp COMPLETED in the
2263 // bottom 150 rows and every row above saturated at P_SKYTOP -- the two spec rows describe a
2264 // gradient the visitor never saw. At q=1, which is exactly what nx_world_snap forces through
2265 // WS_Q_NATIVE for published world art, that is 87.5% of the sky column flat.
2266 // base is already a parameter here and wc_rh is the declared ONE OWNER of the written extent,
2267 // so the derivation needs no new plumbing and no hoist.
2268 var rh: i64 = wc_rh(base)
2269 if rh < 1 { rh = 1 } // q is 1..24 so this cannot fire; it exists so
2270 // that a bad q can never divide by zero here
2271 let t: i64 = wclamp(syy + rh/2, 0, rh) // 0 bottom .. rh top
2272 let r: i64 = (hor & 255) + ((top & 255) - (hor & 255))*t/rh
2273 let g: i64 = ((hor >> 8) & 255) + (((top >> 8) & 255) - ((hor >> 8) & 255))*t/rh
2274 let b: i64 = ((hor >> 16) & 255) + (((top >> 16) & 255) - ((hor >> 16) & 255))*t/rh
2275 return wpack(r, g, b)
2276}
2277// clouds: a flat noise plane above the world, sampled where an upward ray crosses it, slowly
2278// drifting with time. Returns 0..255 coverage for blending into the sky.
2279func wcloud(camx: i64, camy: i64, camz: i64, dx: i64, dy: i64, dz: i64, t: i64) -> i64 {
2280 if dy < 40 { return 0 }
2281 let plane: i64 = 56*256 // cloud deck above the 48-high world
2282 let tq: i64 = (plane - camy)*WATER_MAGIC_65536/dy
2283 if tq <= 0 { return 0 }
2284 let cx: i64 = (camx + dx*tq/WATER_MAGIC_65536) >> 9 // 2-block cloud texels
2285 let cz: i64 = (camz + dz*tq/WATER_MAGIC_65536) >> 9
2286 let n: i64 = wnoise(cx + t/40, cz + t/90, 6, 777)
2287 if n < 148 { return 0 }
2288 var cov: i64 = (n - 148)*255/60
2289 if cov > 235 { cov = 235 }
2290 return cov
2291}
2292// sky with a SUN: disc + glow around the sun's projected ray-pixel position (sunon=0 -> plain sky)
2293func wsky2(base: i64, syy: i64, px: i64, py: i64, sunx: i64, suny: i64, sunon: i64) -> i64 {
2294 let base2: i64 = wsky(base, syy)
2295 if sunon == 0 { return base2 }
2296 let ddx: i64 = px - sunx
2297 let ddy: i64 = py - suny
2298 let d2: i64 = ddx*ddx + ddy*ddy
2299 if d2 < 36 { return wpack(255, 250, 224) } // the disc
2300 if d2 < 900 { // the glow
2301 let w2: i64 = 255 - d2*255/900
2302 var r: i64 = (base2 & 255) + (255 - (base2 & 255))*w2/300
2303 var g: i64 = ((base2 >> 8) & 255) + (250 - ((base2 >> 8) & 255))*w2/340
2304 var b: i64 = ((base2 >> 16) & 255) + (224 - ((base2 >> 16) & 255))*w2/460
2305 return wpack(wclamp(r,0,255), wclamp(g,0,255), wclamp(b,0,255))
2306 }
2307 // ordered 2x2 dither breaks the gradient banding lines
2308 if ((px ^ py) & 1) == 1 {
2309 return wpack(wclamp((base2 & 255) + 2, 0, 255), wclamp(((base2 >> 8) & 255) + 2, 0, 255), wclamp(((base2 >> 16) & 255) + 2, 0, 255))
2310 }
2311 return base2
2312}
2313// textured face shading: per-texel hash variation from the HIT POINT's in-face UV (Q8 fractions),
2314// per-type character (wood grain, leaf speckle, water shimmer, ore mottle), edge BEVEL so the
2315// block grid reads crisply, then face light and step fog. All integer, all deterministic.
2316// corner AO: darken a face edge when a solid block sits beside it in the face's outward layer --
2317// the classic soft-corner cue that makes voxel scenes read as lit geometry instead of flat tiles
2318func wao(base: i64, cx2: i64, cy2b: i64, cz2: i64, face: i64, u: i64, v: i64) -> i64 {
2319 var ox2: i64 = cx2
2320 var oy2: i64 = cy2b
2321 var oz2: i64 = cz2
2322 if face == 1 { ox2 = ox2 + 1 }
2323 if face == 2 { ox2 = ox2 - 1 }
2324 if face == 3 { oy2 = oy2 + 1 }
2325 if face == 4 { oy2 = oy2 - 1 }
2326 if face == 5 { oz2 = oz2 + 1 }
2327 if face == 6 { oz2 = oz2 - 1 }
2328 var ao: i64 = 0
2329 // u-axis neighbours (u maps to x on y/z faces, to z on x faces)
2330 if u < 44 {
2331 var hit1: i64 = 0
2332 if face == 1 { hit1 = vsolid(base, ox2, oy2, oz2 - 1) }
2333 if face == 2 { hit1 = vsolid(base, ox2, oy2, oz2 - 1) }
2334 if face == 3 { hit1 = vsolid(base, ox2 - 1, oy2, oz2) }
2335 if face == 4 { hit1 = vsolid(base, ox2 - 1, oy2, oz2) }
2336 if face == 5 { hit1 = vsolid(base, ox2 - 1, oy2, oz2) }
2337 if face == 6 { hit1 = vsolid(base, ox2 - 1, oy2, oz2) }
2338 if hit1 == 1 { ao = ao + (44 - u)*30/44 }
2339 }
2340 if u > 211 {
2341 var hit2: i64 = 0
2342 if face == 1 { hit2 = vsolid(base, ox2, oy2, oz2 + 1) }
2343 if face == 2 { hit2 = vsolid(base, ox2, oy2, oz2 + 1) }
2344 if face == 3 { hit2 = vsolid(base, ox2 + 1, oy2, oz2) }
2345 if face == 4 { hit2 = vsolid(base, ox2 + 1, oy2, oz2) }
2346 if face == 5 { hit2 = vsolid(base, ox2 + 1, oy2, oz2) }
2347 if face == 6 { hit2 = vsolid(base, ox2 + 1, oy2, oz2) }
2348 if hit2 == 1 { ao = ao + (u - 211)*30/44 }
2349 }
2350 // v-axis neighbours (v maps to z on top/bottom faces, to y on side faces)
2351 if v < 44 {
2352 var hit3: i64 = 0
2353 if face == 3 { hit3 = vsolid(base, ox2, oy2, oz2 - 1) }
2354 if face == 4 { hit3 = vsolid(base, ox2, oy2, oz2 - 1) }
2355 if face == 1 { hit3 = vsolid(base, ox2, oy2 - 1, oz2) }
2356 if face == 2 { hit3 = vsolid(base, ox2, oy2 - 1, oz2) }
2357 if face == 5 { hit3 = vsolid(base, ox2, oy2 - 1, oz2) }
2358 if face == 6 { hit3 = vsolid(base, ox2, oy2 - 1, oz2) }
2359 if hit3 == 1 { ao = ao + (44 - v)*30/44 }
2360 }
2361 if v > 211 {
2362 var hit4: i64 = 0
2363 if face == 3 { hit4 = vsolid(base, ox2, oy2, oz2 + 1) }
2364 if face == 4 { hit4 = vsolid(base, ox2, oy2, oz2 + 1) }
2365 if face == 1 { hit4 = vsolid(base, ox2, oy2 + 1, oz2) }
2366 if face == 2 { hit4 = vsolid(base, ox2, oy2 + 1, oz2) }
2367 if face == 5 { hit4 = vsolid(base, ox2, oy2 + 1, oz2) }
2368 if face == 6 { hit4 = vsolid(base, ox2, oy2 + 1, oz2) }
2369 if hit4 == 1 { ao = ao + (v - 211)*30/44 }
2370 }
2371 if ao > 45 { ao = 45 }
2372 return ao
2373}
2374func wshade2(base: i64, b0: i64, face: i64, steps: i64, syy: i64, hpx: i64, hpy: i64, hpz: i64, t2: i64, clx: i64, cly: i64, clz: i64) -> i64 {
2375 var u: i64 = hpx & 255
2376 var v: i64 = hpz & 255
2377 if face == 1 { u = hpz & 255; v = hpy & 255 }
2378 if face == 2 { u = hpz & 255; v = hpy & 255 }
2379 if face == 5 { u = hpx & 255; v = hpy & 255 }
2380 if face == 6 { u = hpx & 255; v = hpy & 255 }
2381 // grass block SIDES are dirt with a grass strip at the top edge (the classic block identity)
2382 var b: i64 = b0
2383 if b0 == 1 { if face != 3 { if face != 4 {
2384 b = 2
2385 if v > 206 { b = 1 }
2386 } } }
2387 var tex: i64 = 0
2388 if b == 6 { // wood: vertical grain stripes
2389 tex = whash(u/12 + (hpx >> 8)*7 + (hpz >> 8)*13, 3, 99) % 256
2390 }
2391 if b != 6 {
2392 var amp: i64 = 70
2393 if b == 1 { amp = 95 }
2394 if b == 2 { amp = 85 }
2395 if b == 7 { amp = 130 }
2396 if b == 5 { amp = 55 }
2397 var us: i64 = u/16
2398 var vs: i64 = v/16
2399 if b == 4 { // water: time-drifted shimmer
2400 us = us + t2/3
2401 vs = vs - t2/5
2402 amp = 110
2403 }
2404 tex = whash(us + (hpx >> 8)*31, vs + (hpy >> 8)*17 + (hpz >> 8)*7, b*131) % 256
2405 tex = 128 + (tex - 128)*amp/130
2406 }
2407 // ART STYLE (P_STYLE), read once for this texel. An id this engine does not own falls through
2408 // BOTH style tests below and renders REAL. That is the fail-safe direction and it is chosen,
2409 // not inherited: REAL is the shipped look, so a garbage value poked into the plane degrades to
2410 // "unchanged", never to an undrawn or half-drawn world.
2411 let wst9: i64 = wsp(base)[P_STYLE]
2412 if wst9 == WC_STYLE_FLAT { tex = WC_TEX_NEUTRAL }
2413 var light: i64 = wface_l(face)
2414 // sun-directional face light (sun worlds only): east faces catch it, west faces shadow
2415 if wsp(base)[P_SUN] == 1 {
2416 if face == 1 { light = light*112/100 }
2417 if face == 2 { light = light*92/100 }
2418 if face == 5 { light = light*104/100 }
2419 }
2420 light = light*(870 + tex*260/256)/1000
2421 let wsurf9: i64 = wsp(base)[P_SURF]
2422 var smooth9: i64 = 0
2423 if wst9 == WC_STYLE_SOFT { smooth9 = 1 }
2424 if wsurf9 == 1 { smooth9 = 1 }
2425 if smooth9 == 0 {
2426 if u < 14 { light = light*82/100 } // edge bevel: the block grid (OFF on a smooth ground)
2427 if u > 241 { light = light*82/100 }
2428 if v < 14 { light = light*82/100 }
2429 if v > 241 { light = light*82/100 }
2430 }
2431 var ao2: i64 = 0
2432 if wsurf9 == 0 { ao2 = wao(base, clx, cly, clz, face, u, v) } // GE53: a continuous surface has no cell corners to darken
2433 if ao2 > 0 { light = light*(100 - ao2)/100 } // soft corners where geometry meets
2434 if wst9 == WC_STYLE_SOFT { if face == 3 {
2435 // ground: the heightfield normal replaces the flat top-face light; texture and AO re-applied
2436 light = wsoft_ground_light(base, clx, cly, clz, u, v)*(870 + tex*260/256)/1000
2437 if ao2 > 0 { light = light*(100 - ao2)/100 }
2438 } }
2439 if wsurf9 == 1 { if face == 3 {
2440 light = wsurf_light(base, hpx, hpz)*(870 + tex*260/256)/1000 // GE53: the field's own gradient lights the ground
2441 } }
2442 // TOON banding sits AFTER face light, texture and AO and BEFORE colour and fog -- the same
2443 // point in the pipeline at which the incumbent bands its own "bright". Fog must stay OUTSIDE
2444 // the band, or distance itself would quantise into two steps and the horizon would read as a
2445 // wall rather than as depth. Written as ONE assignment through a temporary rather than as two
2446 // ifs on the same variable: two ifs where the first mutates the value the second tests is the
2447 // desync idiom this estate has been bitten by, and it costs nothing to be immune to it.
2448 if wst9 == WC_STYLE_TOON {
2449 var lb9: i64 = WC_TOON_HI
2450 if light < WC_TOON_CUT { lb9 = WC_TOON_LO }
2451 light = lb9
2452 }
2453 let c: i64 = wcol(base, b)
2454 var r: i64 = (c & 255) * light / 1000
2455 var g: i64 = ((c >> 8) & 255) * light / 1000
2456 var bl: i64 = ((c >> 16) & 255) * light / 1000
2457 let sk: i64 = wsky(base, syy)
2458 var fog: i64 = steps*280/MAXSTEPS
2459 if fog > 236 { fog = 236 }
2460 r = r + ((sk & 255) - r)*fog/256
2461 g = g + (((sk >> 8) & 255) - g)*fog/256
2462 bl = bl + (((sk >> 16) & 255) - bl)*fog/256
2463 return wpack(wclamp(r,0,255), wclamp(g,0,255), wclamp(bl,0,255))
2464}
2465
2466// THE GAME SPEC WRITER -- two complete game identities as DATA over one engine.
2467// v0 = NISHI CRAFT (sunlit overworld). v1 = NISHI DEPTHS (twilight cavern plateau: no sun/clouds/
2468// trees, ember horizon, vast cave nets, mossy basalt, glowing ores and glow-mobs).
2469func wc_spec(base: i64, v: i64) -> i64 {
2470 let sp: *i64 = wsp(base)
2471 var k: i64 = 0
2472 while k < WC_SPECN { sp[k] = 0; k = k + 1 } // E1: was a bare 64. This loop is what re-arms
2473 // P_ERTAL to its not-yet-measured sentinel on
2474 // every identity load, so leaving it at 64 would
2475 // have carried one world's talus into the next.
2476 // GE13b THE WARDROBE -- identity-independent DATA, written before any identity branch so every
2477 // world owns the same table by default and may override a row below (or through a recipe row
2478 // once the page validator admits 70-78). Palettes are hue RANGES, never one colour: the genome
2479 // picks the stop. Swimwear vivid and wide (resort colour), the cover-up sun-bleached, casual
2480 // muted, the evening dress deep and narrow -- the one dress every girl used to wear.
2481 // coverage category hue span sat lit
2482 sp[P_GARM + GAR_BIKINI_TOP - 1] = gar_pack(GAR_COV_TOP, GAR_CAT_SWIM, 330, 120, 13, 8)
2483 sp[P_GARM + GAR_BIKINI_BOT - 1] = gar_pack(GAR_COV_BOT, GAR_CAT_SWIM, 330, 120, 13, 8)
2484 sp[P_GARM + GAR_ONEPIECE - 1] = gar_pack(GAR_COV_FULL, GAR_CAT_SWIM, 180, 100, 12, 6)
2485 sp[P_GARM + GAR_SARONG - 1] = gar_pack(GAR_COV_BOT, GAR_CAT_COVER, 20, 60, 9, 11)
2486 sp[P_GARM + GAR_SUNDRESS - 1] = gar_pack(GAR_COV_FULL, GAR_CAT_CASUAL, 40, 80, 8, 12)
2487 sp[P_GARM + GAR_TEE - 1] = gar_pack(GAR_COV_TOP, GAR_CAT_CASUAL, 200, 160, 6, 10)
2488 sp[P_GARM + GAR_SHORTS - 1] = gar_pack(GAR_COV_BOT, GAR_CAT_CASUAL, 30, 40, 5, 6)
2489 sp[P_GARM + GAR_SKIRT - 1] = gar_pack(GAR_COV_BOT, GAR_CAT_CASUAL, 340, 80, 7, 7)
2490 sp[P_GARM + GAR_DRESS - 1] = gar_pack(GAR_COV_FULL, GAR_CAT_EVENING, 250, 90, 10, 4)
2491 if v == 3 {
2492 // v3 = NISHI VALE, the MEDIEVAL VILLAGE: rolling green highland, deep woods, cold
2493 // snowline, cave-riddled stone. THIRD WORLD, ZERO ENGINE CODE -- the recombination
2494 // claim at n=3. Wardens run wide here (P_WARD), so a traveler arrives to REAL stakes.
2495 sp[P_TBASE] = 8
2496 sp[P_TAMP1] = 22
2497 sp[P_TAMP2] = 7
2498 sp[P_WATER] = 7
2499 sp[P_SNOW] = 26
2500 sp[P_TREE] = 11 // deep woods, tuned to the PUBLISHED
2501 // 500-800 trees/ha band -- 19 measured 372/ha,
2502 // barely half a real forest (T53; ported from
2503 // the laptop line 2026-08-02, same fork as
2504 // P_HISTAGE -- the fix existed and never landed)
2505 sp[P_TREEMAX] = 25 // woods climb to the snowline here
2506 sp[P_HISTAGE] = 0 // MEASURED, not assumed: symmetric value-
2507 // noise yields HI ~594 permil = mature
2508 sp[P_CAVETH] = 168 // more cavern than craft's 182
2509 sp[P_SKYTOP] = wpack(86, 122, 176)
2510 sp[P_SKYHOR] = wpack(196, 200, 206)
2511 sp[P_SUN] = 1
2512 sp[P_CLOUD] = 1
2513 sp[P_PAL + 1] = wpack(72, 124, 56) // highland grass
2514 sp[P_PAL + 2] = wpack(94, 72, 50) // loam
2515 sp[P_PAL + 3] = wpack(118, 116, 120) // quarry stone
2516 sp[P_PAL + 4] = wpack(44, 82, 140) // cold water
2517 sp[P_PAL + 5] = wpack(190, 178, 150) // riverbank
2518 sp[P_PAL + 6] = wpack(88, 62, 36) // timber
2519 sp[P_PAL + 7] = wpack(40, 96, 44) // dark canopy
2520 sp[P_PAL + 8] = wpack(238, 240, 246) // snow
2521 sp[P_PAL + 9] = wpack(66, 66, 72)
2522 sp[P_PAL + 10] = wpack(176, 142, 118)
2523 sp[P_PAL + 11] = wpack(224, 188, 92)
2524 sp[P_MOBC + 0] = wpack(206, 160, 96)
2525 sp[P_MOBC + 1] = wpack(160, 118, 62)
2526 sp[P_MOBC + 2] = wpack(120, 158, 190)
2527 sp[P_MOBC + 3] = wpack(78, 110, 146)
2528 sp[P_MOBC + 4] = wpack(178, 120, 160)
2529 sp[P_MOBC + 5] = wpack(134, 86, 122)
2530 sp[P_PAL2 + 0] = wpack(88, 60, 38)
2531 sp[P_PAL2 + 1] = wpack(120, 186, 90)
2532 sp[P_PAL2 + 2] = wpack(92, 168, 58)
2533 sp[P_PAL2 + 3] = wpack(230, 200, 82)
2534 sp[P_PAL2 + 4] = wpack(58, 42, 30)
2535 sp[P_DAYLEN] = 72000
2536 sp[P_SEASD] = 3
2537 sp[P_GROWE] = 2400
2538 sp[P_GROWW] = 5 // a harder land: crops take longer
2539 sp[P_GROWD] = 1
2540 sp[P_RAINS + 0] = 420 // wet spring
2541 sp[P_RAINS + 1] = 220
2542 sp[P_RAINS + 2] = 380
2543 sp[P_RAINS + 3] = 180
2544 sp[P_ADV + 0] = (B_CROP2 << 16) | 9
2545 sp[P_ADV + 1] = (1 << 16) | 3
2546 sp[P_ADV + 2] = (4 << 16) | (2 << 8) | 2
2547 sp[P_CURI] = 400
2548 sp[P_OUTFIT] = 0
2549 sp[P_WARD] = 20 // wardens hunt from 20 blocks by DAY here
2550 sp[P_BLDG] = 2 // W3: the medieval village -- densest settlement
2551 sp[P_BLDGSPEC] = 2 // stonecot archetype
2552 return 0
2553 }
2554 if v == 2 {
2555 sp[P_SURF] = 1 // GE53: NISHI SHORE is the NO-VOXEL world -- its ground is the continuous field
2556 // v2 = NISHI SHORE, the REVIEW STAGE (operator 2026-08-02): near-flat warm sand, a
2557 // sea, no trees/caves/snow -- body and sprite rendering judged WITHOUT environment
2558 // distraction, with the outfit dimension ACTIVE (mixed beach). One engine, one more
2559 // identity: this whole world is 30 data rows, which IS the recombination claim.
2560 sp[P_TBASE] = 11
2561 sp[P_TAMP1] = 2
2562 sp[P_TAMP2] = 1
2563 sp[P_WATER] = 9
2564 sp[P_SNOW] = 99
2565 sp[P_TREE] = 0
2566 sp[P_CAVETH] = 255
2567 // GE13: the sea this identity always PROMISED -- P_WATER 9 under base 11 never dipped
2568 // below the waterline, so the data declared a shore the terrain shape could not
2569 // produce (the operator's 2026-08-27 frame: a beach with no ocean). One row fixes it:
2570 // ocean toward +z, shoreline at absolute z=100 (inside the boot window, beyond the
2571 // review stage and both gate fixture plateaus), 20-block beach ramp, seabed 4 =>
2572 // up to 5 blocks of swimmable water at flood level 9.
2573 sp[P_OCEAN] = 0 + (612 << 2) + (20 << 12) + (4 << 18) // edge=+z shore=100+512 slope=20 bed=4
2574 sp[P_SKYTOP] = wpack(110, 168, 230)
2575 sp[P_SKYHOR] = wpack(214, 226, 240)
2576 sp[P_SUN] = 1
2577 sp[P_CLOUD] = 1
2578 sp[P_PAL + 1] = wpack(226, 204, 148) // shore sand (the "grass" slot)
2579 sp[P_PAL + 2] = wpack(206, 180, 128) // packed sand
2580 sp[P_PAL + 3] = wpack(180, 170, 158) // beach rock
2581 sp[P_PAL + 4] = wpack(56, 140, 196) // warm sea
2582 sp[P_PAL + 5] = wpack(232, 214, 160) // dry sand
2583 sp[P_PAL + 6] = wpack(150, 110, 70) // driftwood
2584 sp[P_PAL + 7] = wpack(96, 150, 96) // beach scrub
2585 sp[P_PAL + 8] = wpack(240, 240, 244)
2586 sp[P_PAL + 9] = wpack(120, 116, 110)
2587 sp[P_PAL + 10] = wpack(176, 142, 118)
2588 sp[P_PAL + 11] = wpack(224, 188, 92)
2589 sp[P_MOBC + 0] = wpack(240, 178, 84)
2590 sp[P_MOBC + 1] = wpack(196, 138, 56)
2591 sp[P_MOBC + 2] = wpack(96, 206, 196)
2592 sp[P_MOBC + 3] = wpack(64, 158, 150)
2593 sp[P_MOBC + 4] = wpack(238, 138, 186)
2594 sp[P_MOBC + 5] = wpack(190, 100, 144)
2595 sp[P_PAL2 + 0] = wpack(94, 64, 40)
2596 sp[P_PAL2 + 1] = wpack(120, 186, 90)
2597 sp[P_PAL2 + 2] = wpack(92, 168, 58)
2598 sp[P_PAL2 + 3] = wpack(230, 200, 82)
2599 sp[P_PAL2 + 4] = wpack(64, 46, 32)
2600 sp[P_DAYLEN] = 72000
2601 sp[P_SEASD] = 3
2602 sp[P_GROWE] = 2400
2603 sp[P_GROWW] = 6
2604 sp[P_GROWD] = 1
2605 sp[P_RAINS + 0] = 60 // a review stage stays mostly sunny
2606 sp[P_RAINS + 1] = 40
2607 sp[P_RAINS + 2] = 60
2608 sp[P_RAINS + 3] = 40
2609 sp[P_ADV + 0] = (4 << 16) | (2 << 8) | 4 // the water's edge is the gathering place
2610 sp[P_ADV + 1] = (1 << 16) | 3
2611 sp[P_CURI] = 350
2612 sp[P_OUTFIT] = 1
2613 sp[P_BLDG] = 4 // W3: sparse driftwood huts along the shore
2614 sp[P_BLDGSPEC] = 1 // hut archetype
2615 return 0
2616 }
2617 // FARMING-LIFE rows, shared by every identity (the loop itself is identity-independent DATA)
2618 sp[P_PAL2 + 0] = wpack(94, 64, 40) // tilled farmland, dry
2619 sp[P_PAL2 + 1] = wpack(120, 186, 90) // crop sprout
2620 sp[P_PAL2 + 2] = wpack(92, 168, 58) // crop growing
2621 sp[P_PAL2 + 3] = wpack(230, 200, 82) // crop MATURE (reads golden at a glance)
2622 sp[P_PAL2 + 4] = wpack(64, 46, 32) // farmland wet (soaked dark soil)
2623 sp[P_DAYLEN] = 72000 // 20 real minutes at the fixed 60Hz sim
2624 // tick -- the shipped-sim benchmark band
2625 // (Minecraft 20:00/day, Stardew 14:33,
2626 // Vintage Story ~24:00): a day you can
2627 // farm INSIDE, not a light show
2628 sp[P_SEASD] = 3
2629 sp[P_GROWE] = 2400 // one growth/hydration roll per farm cell
2630 // per ~40s -- Minecraft's random-tick
2631 // cadence (~47s/block avg) on our grid;
2632 // wet stencil den=5 => a watered crop
2633 // matures in ~6.7 min =~ 1/3 day (the
2634 // Minecraft wheat band)
2635 sp[P_GROWW] = 6
2636 sp[P_GROWD] = 1
2637 sp[P_RAINS + 0] = 340 // spring
2638 sp[P_RAINS + 1] = 180 // summer
2639 sp[P_RAINS + 2] = 300 // autumn
2640 sp[P_RAINS + 3] = 120 // winter
2641 sp[P_ADV + 0] = (B_CROP2 << 16) | 9 // a mature field draws foragers (need 0 = hunger)
2642 sp[P_ADV + 1] = (1 << 16) | 3 // grass meadow: light grazing
2643 sp[P_ADV + 2] = (4 << 16) | (2 << 8) | 2 // water's edge: where girls gather to talk
2644 sp[P_CURI] = 350
2645 if v == 1 {
2646 sp[P_TBASE] = 22
2647 sp[P_TAMP1] = 8
2648 sp[P_TAMP2] = 4
2649 sp[P_WATER] = 5
2650 sp[P_SNOW] = 99
2651 sp[P_TREE] = 0
2652 sp[P_CAVETH] = 148
2653 sp[P_SKYTOP] = wpack(28, 22, 40)
2654 sp[P_SKYHOR] = wpack(96, 48, 40)
2655 sp[P_SUN] = 0
2656 sp[P_CLOUD] = 0
2657 sp[P_PAL + 1] = wpack(74, 122, 84) // moss
2658 sp[P_PAL + 2] = wpack(88, 62, 48) // packed earth
2659 sp[P_PAL + 3] = wpack(98, 90, 104) // basalt
2660 sp[P_PAL + 4] = wpack(60, 190, 180) // glow-water
2661 sp[P_PAL + 5] = wpack(120, 100, 80)
2662 sp[P_PAL + 6] = wpack(70, 50, 36)
2663 sp[P_PAL + 7] = wpack(60, 90, 60)
2664 sp[P_PAL + 8] = wpack(200, 200, 210)
2665 sp[P_PAL + 9] = wpack(58, 58, 66)
2666 sp[P_PAL + 10] = wpack(198, 150, 116)
2667 sp[P_PAL + 11] = wpack(255, 206, 96) // gold veins GLOW down here
2668 sp[P_MOBC + 0] = wpack(255, 128, 64) // ember
2669 sp[P_MOBC + 1] = wpack(200, 84, 40)
2670 sp[P_MOBC + 2] = wpack(124, 220, 255) // ice-glow
2671 sp[P_MOBC + 3] = wpack(80, 160, 200)
2672 sp[P_MOBC + 4] = wpack(204, 124, 255) // violet
2673 sp[P_MOBC + 5] = wpack(150, 84, 200)
2674 return 0
2675 }
2676 sp[P_TBASE] = 4
2677 sp[P_TAMP1] = 17
2678 sp[P_TAMP2] = 5
2679 sp[P_WATER] = 9
2680 sp[P_SNOW] = 20
2681 sp[P_TREE] = 43
2682 sp[P_CAVETH] = 182
2683 // DEFAULT SKY TONES DERIVED, NOT PICKED (2026-08-26): zenith = 1 airmass, horizon = the
2684 // Kasten-Young 38-airmass bound, each channel single-scattered by lambda^-4 and display-
2685 // encoded -- nx_atmosphere's own worked example lands at (80,117,176) where the retired
2686 // hand-picked default sat at (86,122,176): the palette was approximating the physics.
2687 // Per-world identity overrides above KEEP their deliberate looks; only the default derives.
2688 let am_t: i64 = nx_atm_airmass_q(0)
2689 let am_h: i64 = nx_atm_airmass_q(NX_ATM_Q)
2690 sp[P_SKYTOP] = wpack(nx_atm_display_255(nx_atm_channel_q(NX_LAMBDA_R_NM, am_t)),
2691 nx_atm_display_255(nx_atm_channel_q(NX_LAMBDA_G_NM, am_t)),
2692 nx_atm_display_255(nx_atm_channel_q(NX_LAMBDA_B_NM, am_t)))
2693 sp[P_SKYHOR] = wpack(nx_atm_display_255(nx_atm_channel_q(NX_LAMBDA_R_NM, am_h)),
2694 nx_atm_display_255(nx_atm_channel_q(NX_LAMBDA_G_NM, am_h)),
2695 nx_atm_display_255(nx_atm_channel_q(NX_LAMBDA_B_NM, am_h)))
2696 sp[P_SUN] = 1
2697 sp[P_CLOUD] = 1
2698 sp[P_PAL + 1] = wpack(84, 158, 62) // grass
2699 sp[P_PAL + 2] = wpack(122, 86, 56) // dirt
2700 sp[P_PAL + 3] = wpack(126, 126, 132) // stone
2701 sp[P_PAL + 4] = wpack(52, 96, 190) // water
2702 sp[P_PAL + 5] = wpack(206, 190, 132) // sand
2703 sp[P_PAL + 6] = wpack(104, 74, 40) // wood
2704 sp[P_PAL + 7] = wpack(52, 122, 44) // leaves
2705 sp[P_PAL + 8] = wpack(232, 238, 244) // snow
2706 sp[P_PAL + 9] = wpack(72, 74, 80) // coal
2707 sp[P_PAL + 10] = wpack(176, 142, 118) // iron
2708 sp[P_PAL + 11] = wpack(224, 188, 92) // gold
2709 sp[P_MOBC + 0] = wpack(240, 178, 84)
2710 sp[P_MOBC + 1] = wpack(196, 138, 56)
2711 sp[P_MOBC + 2] = wpack(96, 206, 196)
2712 sp[P_MOBC + 3] = wpack(64, 158, 150)
2713 sp[P_MOBC + 4] = wpack(238, 138, 186)
2714 sp[P_MOBC + 5] = wpack(190, 100, 144)
2715 sp[P_BLDG] = 3 // W3: scattered cottages in the green hills
2716 sp[P_BLDGSPEC] = 0 // cottage archetype
2717 return 0
2718}
2719
2720// mobs: 12 creatures spawned on grass, deterministic, through the CERTIFIED entity store
2721// (handle = index+generation, stale-handle-proof by construction -- the part's gate owns that claim)
2722func genmobs(base: i64, sd: i64) -> i64 {
2723 let m: *i64 = mobp(base)
2724 en_init(m, MOB_CAP, MOB_NCOMP)
2725 var i: i64 = 0
2726 var placed: i64 = 0
2727 while i < 200 {
2728 if placed < WC_CAST_N {
2729 let mx: i64 = 4 + whash(i*13 + 5, i*7 + 1, sd + 400) % (WX - 8)
2730 let mz: i64 = 4 + whash(i*17 + 3, i*29 + 9, sd + 500) % (WZ - 8)
2731 let h: i64 = wheight(base, mx, mz, sd)
2732 if h < wsp(base)[P_SNOW] - 1 { if vget(base, mx, h, mz) == 1 {
2733 let hd: i64 = en_spawn(m)
2734 if hd > 0 {
2735 en_set(m, hd, MC_X, mx*256 + 128)
2736 en_set(m, hd, MC_Y, (h + 1)*256)
2737 en_set(m, hd, MC_Z, mz*256 + 128)
2738 en_set(m, hd, MC_KIND, whash(mx, mz, sd + 600) % 3)
2739 en_set(m, hd, MC_PH, whash(mz, mx, sd + 700) % WATER_MAGIC_4096)
2740 var dg: i64 = 0
2741 if whash(mx + 13, mz + 7, sd + 800) % 3 == 0 { dg = 1 }
2742 en_set(m, hd, MC_DISG, dg)
2743 let g1: i64 = whash(mx*3 + 1, mz*5 + 9, sd + 900)
2744 let g2: i64 = whash(mz*7 + 3, mx*11 + 5, sd + 901)
2745 // HAIR-HUE NIBBLE (2026-08-30, measured in-page: hist(gv&7)=[12,0,0,0,0,0,0,0]): bits 0-3 are
2746 // the declared hair-hue field (MC_GENE layout, L296) yet g1*4096 + g2*16 can never set them, so
2747 // every girl ever spawned shared ONE hair colour (blonde under the old palette, black under the
2748 // melanin one). Roll the low nibble from its own hash lane; the two existing lanes are untouched.
2749 let g3: i64 = whash(mx*13 + 7, mz*17 + 11, sd + 902) % 16
2750 en_set(m, hd, MC_GENE, (g1*WATER_MAGIC_4096 + g2*16 + g3) % WATER_MAGIC_1048576)
2751 placed = placed + 1
2752 }
2753 } }
2754 }
2755 i = i + 1
2756 }
2757 return placed
2758}
2759
2760// ---------- init / tick / render ----------
2761
2762// A spawn-relative scene fixture, written once into the same voxel buffer the GPU uploads.
2763// Preflight prevents partial stamps and preserves existing non-foliage geometry.
2764func wc_spawn_bush(base: i64, forward: i64, lateral: i64, radius: i64, height: i64, material: i64) -> i64 {
2765 if radius < 1 { return 0 - 1 }
2766 if height < 1 { return 0 - 1 }
2767 if radius >= WX/2 { return 0 - 1 }
2768 if height >= WY { return 0 - 1 }
2769 if material != BEACH_RELEASE_MATERIAL { return 0 - 1 }
2770 let s: *i64 = wst(base)
2771 let sy: i64 = it_sin4096(s[S_YAW])
2772 let co: i64 = it_cos4096(s[S_YAW])
2773 let cx: i64 = (s[S_SPX] >> 8) + (sy*forward + co*lateral)/4096
2774 let cz: i64 = (s[S_SPZ] >> 8) + (co*forward - sy*lateral)/4096
2775 if cx-radius < 0 { return 0 - 1 }
2776 if cx+radius >= WX { return 0 - 1 }
2777 if cz-radius < 0 { return 0 - 1 }
2778 if cz+radius >= WZ { return 0 - 1 }
2779 let raw: i64 = wheight(base, cx+s[S_WOX], cz+s[S_WOZ], s[S_SEED])
2780 let ground: i64 = wsp_beach_ocean(wsp(base), cx+s[S_WOX], cz+s[S_WOZ], raw)
2781 if ground <= wsp(base)[P_WATER] { return 0 - 1 }
2782 if ground+height >= WY { return 0 - 1 }
2783 var phase: i64 = 0
2784 var count: i64 = 0
2785 while phase < 2 {
2786 var y: i64 = 1
2787 while y <= height {
2788 var dz: i64 = 0-radius
2789 while dz <= radius {
2790 var dx: i64 = 0-radius
2791 while dx <= radius {
2792 var ax: i64 = dx; if ax < 0 { ax = 0-ax }
2793 var az: i64 = dz; if az < 0 { az = 0-az }
2794 if ax+az <= radius+height-y {
2795 if phase == 0 {
2796 let old: i64 = vget(base, cx+dx, ground+y, cz+dz)
2797 if old != 0 { if old != material { return 0 - 1 } }
2798 count = count+1
2799 } else {
2800 vset(base, cx+dx, ground+y, cz+dz, material)
2801 }
2802 }
2803 dx = dx+1
2804 }
2805 dz = dz+1
2806 }
2807 y = y+1
2808 }
2809 phase = phase+1
2810 }
2811 wc_wmax(base)
2812 return count
2813}
2814
2815func init_impl_v(base: i64, v: i64, seed: i64) -> i64 {
2816 wc_spec(base, v)
2817 // W1c: gen-time recipe overrides -- AFTER identity defaults, BEFORE genworld reads them.
2818 // Allowlist = the gen-time slots only (0..6 terrain shape, 59 treemax, 60 histage, 61
2819 // treespec, 62 settlement density, 63 settlement archetype); style
2820 // slots keep the post-boot poke lane so the two channels never blur. A zero region is a
2821 // clean no-op; the applied count lands in S_GENP below (announce-in-world-truth, sect-30d).
2822 let gp9: *i64 = wgp(base)
2823 let spg9: *i64 = wsp(base)
2824 var gpn9: i64 = 0
2825 if gp9[0] == GENP_MAGIC {
2826 var gk9: i64 = gp9[1]
2827 if gk9 < 0 { gk9 = 0 }
2828 if gk9 > GENP_MAXP { gk9 = GENP_MAXP }
2829 var gi9: i64 = 0
2830 while gi9 < gk9 {
2831 let gx9: i64 = gp9[2 + gi9*2]
2832 // THE ONE ALLOWLIST (nx_genp_allow_lib, 2026-09-04): this chain was hand-enumerated here and mirrored,
2833 // with drift, in nx_world_snap and nx_game_page_emit; all three now read genp_gen_ok, and T90 pins each
2834 // P_* slot to it. Gen-time because these rows shape what genworld is about to read -- poked after boot
2835 // they would be the silent no-op this whitelist exists for.
2836 let gok9: i64 = genp_gen_ok(gx9)
2837 if gok9 == 1 { spg9[gx9] = gp9[3 + gi9*2]; gpn9 = gpn9 + 1 }
2838 gi9 = gi9 + 1
2839 }
2840 }
2841 let s: *i64 = wst(base)
2842 var k: i64 = 0
2843 while k < 64 { s[k] = 0; k = k + 1 }
2844 let ej0: *i64 = ejp(base)
2845 var kj: i64 = 0
2846 while kj < EJRN_SLOTS { ej0[kj] = 0; kj = kj + 1 }
2847 s[S_SEED] = seed
2848 s[S_VAR] = v // after the zero loop -- an earlier set
2849 // here was wiped (the zero-loop-order class)
2850 s[S_GENP] = gpn9 // same zero-loop-order law as S_VAR above
2851 s[S_T] = wsp(base)[P_DAYLEN]/4 // first frame = MID-MORNING light (the
2852 // visitor's first impression; a dawn-dark
2853 // spawn read as night, screenshot-caught)
2854 genworld(base, seed)
2855 wc_wmax(base)
2856 genmobs(base, seed)
2857 // A4/C1: zero the spring + need regions (init may reuse a dirty arena), then seat and seed
2858 let sb0i: *i64 = (base + O_SOFT) as *i64
2859 var kzi: i64 = 0
2860 while kzi < MOB_CAP*SB_PTS*SD_STRIDE { sb0i[kzi] = 0; kzi = kzi + 1 }
2861 let nd0i: *i64 = (base + O_NEED) as *i64
2862 var kni: i64 = 0
2863 while kni < MOB_CAP*4 { nd0i[kni] = 0; kni = kni + 1 }
2864 wc_soft_seat_all(base)
2865 wc_need_seed(base)
2866 wc_mind_zero(base)
2867 // spawn on the TALLEST central column: from a local peak every direction is a vista,
2868 // never a cliff wall filling the first frame (the eyeball found exactly that)
2869 var sx: i64 = WX/2
2870 var sz: i64 = WZ/2
2871 var sh: i64 = 0
2872 var qz: i64 = WZ/2 - 12
2873 while qz < WZ/2 + 12 {
2874 var qx: i64 = WX/2 - 12
2875 while qx < WX/2 + 12 {
2876 let qh: i64 = wheight(base, qx, qz, seed)
2877 if qh > sh { sh = qh; sx = qx; sz = qz }
2878 qx = qx + 1
2879 }
2880 qz = qz + 1
2881 }
2882 // W2b: a spawn column may carry understory at feet height -- clear it. GUARDED so legacy
2883 // (understory-less) worlds stay byte-identical; gen-time vegetation is not journaled, so a
2884 // later window-slide repaint is harmless (the player has long moved).
2885 if ((wsp(base)[P_TREESPEC] >> 18) & 7) > 0 {
2886 vset(base, sx, sh + 1, sz, 0)
2887 vset(base, sx, sh + 2, sz, 0)
2888 }
2889 s[S_CX] = sx*256 + 128
2890 s[S_CZ] = sz*256 + 128
2891 s[S_CY] = (sh + 1)*256 + EYE
2892 s[S_SPX] = s[S_CX]
2893 s[S_SPY] = s[S_CY]
2894 s[S_SPZ] = s[S_CZ]
2895 s[S_YAW] = 0
2896 // GE45 review framing (2026-09-04): an identity with an ocean row spawns FACING the sea. The shore identity's
2897 // +z edge is yaw 0 already, so this is byte-identical there and correct for any other edge a recipe declares.
2898 let sy0: i64 = wsp_sea_yaw(wsp(base))
2899 if sy0 >= 0 { s[S_YAW] = sy0 }
2900 s[S_PITCH] = 0
2901 s[S_Q] = Q_START
2902 if v == BEACH_RELEASE_WORLD {
2903 let release_cells: i64 = wc_spawn_bush(base, BEACH_RELEASE_FORWARD, BEACH_RELEASE_LATERAL, BEACH_RELEASE_RADIUS, BEACH_RELEASE_HEIGHT, BEACH_RELEASE_MATERIAL)
2904 // The release fixture is non-critical: a crowded or submerged site must not
2905 // prevent the world's input bindings and remaining startup from completing.
2906 if release_cells > 0 {
2907 let release_spec: *i64 = wsp(base)
2908 release_spec[P_PAL + BEACH_RELEASE_MATERIAL] = wpack(BEACH_RELEASE_RED, BEACH_RELEASE_GREEN, BEACH_RELEASE_BLUE)
2909 }
2910 }
2911 ia_init(winp(base))
2912 ia_bind(winp(base), 82, IA_LUP) // R = look up (real action bits since seq1136)
2913 ia_bind(winp(base), 70, IA_LDOWN) // F = look down (G = party invite, below)
2914 ia_bind(winp(base), 32, IA_JUMP) // space = jump (touch center zone jumps too)
2915 ia_bind(winp(base), 81, IA_MOD) // Q = cycle the hotbar selection
2916 ia_bind(winp(base), 72, IA_HOME) // H = return to spawn (edge-fall rescue)
2917 return 0
2918}
2919func init_impl(base: i64, seed: i64) -> i64 { return init_impl_v(base, 0, seed) }
2920
2921// return to spawn: position + velocity reset, world untouched (the operator's reset button)
2922func wc_respawn(base: i64) -> i64 {
2923 let s: *i64 = wst(base)
2924 s[S_CX] = s[S_SPX]
2925 s[S_CY] = s[S_SPY]
2926 s[S_CZ] = s[S_SPZ]
2927 s[S_VY] = 0
2928 s[S_GROUNDED] = 1
2929 return 0
2930}
2931
2932// highest solid y at a column (collision floor), -1 if none
2933func wground(base: i64, x: i64, z: i64) -> i64 {
2934 var y: i64 = WY - 1
2935 while y >= 0 {
2936 if vsolid(base, x, y, z) == 1 { return y }
2937 y = y - 1
2938 }
2939 return 0 - 1
2940}
2941
2942// try to stand at (nxq, nzq): returns the required cam y (Q8) or -1 if the move is blocked
2943func wstand(base: i64, nxq: i64, nzq: i64, cyq: i64) -> i64 {
2944 let g: i64 = wground(base, nxq >> 8, nzq >> 8)
2945 var stand: i64 = (g + 1)*256 + EYE
2946 let wl2: i64 = wsp(base)[P_WATER]
2947 let wsurf: i64 = (wl2 + 1)*256 + EYE - 96 // float slightly sunk in water
2948 if stand < wsurf { if vget(base, nxq >> 8, wl2, nzq >> 8) == 4 { stand = wsurf } }
2949 // VOID COLUMN = WALL: no ground below means off the world's edge -- refuse the step (the
2950 // walk-off-and-vanish bug: wstand happily computed a hover height over nothing)
2951 if g < 0 { return 0 - 1 }
2952 if stand - cyq > 384 { return 0 - 1 } // more than a 1.5-block rise = wall
2953 let heady: i64 = (stand + 128) >> 8
2954 if vsolid(base, nxq >> 8, heady, nzq >> 8) == 1 { return 0 - 1 }
2955 return stand
2956}
2957
2958// stand height for the CURRENT column (no wall refusal -- the vertical-physics target)
2959func wstand_here(base: i64, xq: i64, zq: i64) -> i64 {
2960 let g: i64 = wground(base, xq >> 8, zq >> 8)
2961 var stand: i64 = (g + 1)*256 + EYE
2962 let wl3: i64 = wsp(base)[P_WATER]
2963 let wsurf: i64 = (wl3 + 1)*256 + EYE - 96
2964 if stand < wsurf { if vget(base, xq >> 8, wl3, zq >> 8) == 4 { stand = wsurf } }
2965 return stand
2966}
2967
2968// mob floor: first solid scanning DOWN from just above the mob's feet -- wground scans from the
2969// SKY and reads an overhanging leaf canopy as "ground 5 blocks up", which froze every mob under a
2970// tree (measured: phase advanced 982/tick = the refusal branch, position never moved).
2971// body clearance: a girl is ~2 blocks tall -- the two cells above the ground must be AIR or
2972// she stands with her head/shoulders inside an overhang (invisible as SDF; as a MESH her
2973// buried parts discard and the poking limbs read as floating shards -- operator-reported).
2974// GE12b -- BODY CLEARANCE. mob_fits (below) proves only her OWN cell's air column, but a girl
2975// is a CYLINDER of radius MOB_R_Q8, so standing near a cell edge her body overlaps the
2976// neighbouring solid: the operator's 2026-08-27 frame caught exactly that -- a girl leaning
2977// THROUGH a cliff wall with her shins inside the blocks. These clamp her fractional position
2978// away from any solid neighbour at either body course. Integer, deterministic, and a pure
2979// no-op in open ground (no solid neighbour => the position is returned untouched).
2980// RADIUS IS NOT A NEW NUMBER: npc_pick already models her body as r=.35 blocks for hit
2981// detection, and .35*256 = 89.6 -> 90 q8. One body, one radius; a second value here would be
2982// two rulers for one girl (she could be hittable where she is not present, and vice versa).
2983const MOB_R_Q8: i64 = 90
2984func mob_solid2(base: i64, x: i64, g: i64, z: i64) -> i64 {
2985 if vsolid(base, x, g + 1, z) == 1 { return 1 }
2986 if vsolid(base, x, g + 2, z) == 1 { return 1 }
2987 return 0
2988}
2989func mob_clear_x(base: i64, nx: i64, nz: i64, g: i64) -> i64 {
2990 let cx: i64 = nx >> 8
2991 let cz: i64 = nz >> 8
2992 let fr: i64 = nx - cx*256
2993 var r: i64 = nx
2994 if fr < MOB_R_Q8 { if mob_solid2(base, cx - 1, g, cz) == 1 { r = cx*256 + MOB_R_Q8 } }
2995 if fr > 256 - MOB_R_Q8 { if mob_solid2(base, cx + 1, g, cz) == 1 { r = cx*256 + 256 - MOB_R_Q8 } }
2996 return r
2997}
2998func mob_clear_z(base: i64, nx: i64, nz: i64, g: i64) -> i64 {
2999 let cx: i64 = nx >> 8
3000 let cz: i64 = nz >> 8
3001 let fr: i64 = nz - cz*256
3002 var r: i64 = nz
3003 if fr < MOB_R_Q8 { if mob_solid2(base, cx, g, cz - 1) == 1 { r = cz*256 + MOB_R_Q8 } }
3004 if fr > 256 - MOB_R_Q8 { if mob_solid2(base, cx, g, cz + 1) == 1 { r = cz*256 + 256 - MOB_R_Q8 } }
3005 return r
3006}
3007func mob_fits(base: i64, x: i64, g: i64, z: i64) -> i64 {
3008 if vsolid(base, x, g + 1, z) == 1 { return 0 }
3009 if vsolid(base, x, g + 2, z) == 1 { return 0 }
3010 return 1
3011}
3012
3013func mob_ground(base: i64, x: i64, ytop: i64, z: i64) -> i64 {
3014 var y: i64 = ytop
3015 if y > WY - 1 { y = WY - 1 }
3016 while y >= 0 {
3017 if vsolid(base, x, y, z) == 1 { return y }
3018 y = y - 1
3019 }
3020 return 0 - 1
3021}
3022
3023// GE12 -- GAIT-PHASED VERTICAL LOCOMOTION (gameengine rung mob_step_gait). A mob's Y no longer
3024// EASES linearly toward the stand height (the 64 q8/tick glide the operator caught 2026-08-26:
3025// a climb glided and a drop floated). Two named regimes, one ruler each:
3026// STEP UP -> the cited stair-ascent rate WC_STEP_EASE_UP (gamefeel row step_ease_up_q8), and
3027// only in a tick where a stride step landed -- the caller is the move branch, so the
3028// rise is gait-phased BY CONSTRUCTION, never a free-running ramp;
3029// DROP -> the PLAYER'S OWN integrator (v -= GRAV per tick, clamped at VTERM, landing snaps to
3030// the stand height and zeroes v): every falling body in the world obeys one gravity.
3031// vyp[k] is her vertical velocity (O_MOBVY). Returns the new Y (q8). All inputs are arena state,
3032// so replay stays bit-identical (T4/T17). ph/gait are untouched: this is Y only.
3033func mob_step_gait(vyp: *i64, k: i64, cy0: i64, ng: i64) -> i64 {
3034 var vy9: i64 = vyp[k]
3035 var ny9: i64 = cy0
3036 if ng > cy0 {
3037 ny9 = cy0 + WC_STEP_EASE_UP
3038 if ny9 > ng { ny9 = ng }
3039 vy9 = 0
3040 }
3041 if ng < cy0 {
3042 vy9 = vy9 - GRAV
3043 if vy9 < 0 - VTERM { vy9 = 0 - VTERM }
3044 ny9 = cy0 + vy9
3045 if ny9 <= ng { ny9 = ng; vy9 = 0 }
3046 }
3047 if ng == cy0 { vy9 = 0 }
3048 vyp[k] = vy9
3049 return ny9
3050}
3051
3052// NPC HIT DETECTION: march the view ray in half-block samples up to 7 blocks; a solid voxel
3053// blocks it (walls occlude, real detection); a sample inside a girl's body cylinder (r=.35,
3054// h=1.7) targets her. Returns the dense index or -1. The same pass PUSHES the player out of
3055// bodies (they have PRESENCE now -- no more walking through people).
3056func npc_pick(base: i64, fx: i64, fy: i64, fz: i64) -> i64 {
3057 let s: *i64 = wst(base)
3058 let m: *i64 = mobp(base)
3059 var t: i64 = 1
3060 while t <= 14 {
3061 let px2: i64 = s[S_CX] + fx*t*128/WATER_MAGIC_4096
3062 let py2: i64 = s[S_CY] + fy*t*128/WATER_MAGIC_4096
3063 let pz2: i64 = s[S_CZ] + fz*t*128/WATER_MAGIC_4096
3064 if vsolid(base, px2 >> 8, py2 >> 8, pz2 >> 8) == 1 { return 0 - 1 }
3065 var k: i64 = 0
3066 while k < en_count(m) {
3067 let h: i64 = en_nth(m, k)
3068 var ddx: i64 = px2 - en_get(m, h, MC_X)
3069 if ddx < 0 { ddx = 0 - ddx }
3070 var ddz: i64 = pz2 - en_get(m, h, MC_Z)
3071 if ddz < 0 { ddz = 0 - ddz }
3072 let dy2: i64 = py2 - en_get(m, h, MC_Y)
3073 if ddx < 90 { if ddz < 90 { if dy2 >= 0 { if dy2 < 440 { return k } } } }
3074 k = k + 1
3075 }
3076 t = t + 1
3077 }
3078 return 0 - 1
3079}
3080
3081// ---------- A1: the integer tick calendar (day/night + 4 seasons) ----------
3082// Pure functions of (S_T, spec) -> bit-exact across arenas by construction. Every phase compare
3083// CROSS-MULTIPLIES (the banked integer-truncation law: never divide a value before comparing it).
3084// RECOMBINATION CONTRACT: a different game = a different spec; a recombined identity turns a
3085// subsystem OFF by zeroing its rows, so every consumer guards the zero (endless day, no rain,
3086// no farm clock) instead of trapping on a modulo-by-zero. Off is a state, never a crash.
3087func cal_dayt(base: i64) -> i64 {
3088 let dl: i64 = wsp(base)[P_DAYLEN]
3089 if dl <= 0 { return 0 }
3090 let s: *i64 = wst(base)
3091 return s[S_T] % dl
3092}
3093func cal_day(base: i64) -> i64 {
3094 let dl: i64 = wsp(base)[P_DAYLEN]
3095 if dl <= 0 { return 0 }
3096 let s: *i64 = wst(base)
3097 return s[S_T] / dl
3098}
3099func cal_season(base: i64) -> i64 {
3100 let sd: i64 = wsp(base)[P_SEASD]
3101 if sd <= 0 { return 0 }
3102 return (cal_day(base) / sd) % 4
3103}
3104// night = the last 3/8 of each day: dayt*8 >= DAYLEN*5, cross-multiplied
3105func cal_night(base: i64) -> i64 {
3106 if wsp(base)[P_DAYLEN] <= 0 { return 0 }
3107 if cal_dayt(base)*8 >= wsp(base)[P_DAYLEN]*WC_NIGHT_X8 { return 1 }
3108 return 0
3109}
3110// sun elevation as a Q12 sine: rises 0 at dawn, +4096 at noon, negative through the night
3111func cal_sunel(base: i64) -> i64 {
3112 let dl: i64 = wsp(base)[P_DAYLEN]
3113 if dl <= 0 { return 4096 } // calendar off = eternal noon
3114 let dt2: i64 = cal_dayt(base)
3115 if dt2*8 < dl*WC_NIGHT_X8 { return it_sin4096(dt2*8*IT_PI/(dl*WC_NIGHT_X8)) }
3116 return 0 - it_sin4096((dt2*8 - dl*WC_NIGHT_X8)*IT_PI/(dl*(8 - WC_NIGHT_X8)))
3117}
3118// deterministic weather: ONE roll per absolute day-eighth from (seed, segment, season table) --
3119// the same world replays the same storms forever
3120func cal_rain(base: i64) -> i64 {
3121 let dl: i64 = wsp(base)[P_DAYLEN]
3122 if dl <= 0 { return 0 }
3123 let s: *i64 = wst(base)
3124 let seg: i64 = s[S_T]*8/dl
3125 // DELEGATED (2026-08-26) to nx_weather_lib's gate-proven wx_rain: same seed lineage (the
3126 // +3300 salt fold), same one-roll-per-absolute-day-eighth semantics (seg IS the eighth), and
3127 // the gate PROVES the long-run rate stays inside each P_RAINS row's binomial envelope -- this
3128 // delegation cannot change any world's climate, and it gains the front persistence, typed
3129 // precipitation and gradient wind the old coin structurally lacked.
3130 return wx_rain(s[S_SEED] + 3300, seg, wsp(base)[P_RAINS + cal_season(base)])
3131}
3132
3133// the per-tick weather export writer: recomputes the eighth's state (pure fns -- cheap, replay
3134// exact) and publishes it in the spec block for the page and every other consumer.
3135func wc_weather_tick(base: i64) -> i64 {
3136 let dl: i64 = wsp(base)[P_DAYLEN]
3137 if dl <= 0 { return 0 }
3138 let s: *i64 = wst(base)
3139 let seg: i64 = s[S_T]*8/dl
3140 let sd: i64 = s[S_SEED] + 3300
3141 let row: i64 = wsp(base)[P_RAINS + cal_season(base)]
3142 let sp: *i64 = wsp(base)
3143 sp[P_WXCOV] = wx_coverage(sd, seg, row)
3144 let ws: i64 = wx_wind_speed(sd, seg)
3145 let wd: i64 = wx_wind_dir_q(sd, seg)
3146 // direction Q -> components via the sim's own it_cos4096/it_sin4096 (angle = wd scaled to tau)
3147 sp[P_WXWINDX] = ws*it_cos4096(wd*IT_PI*2/WX_Q)/4096
3148 sp[P_WXWINDZ] = ws*it_sin4096(wd*IT_PI*2/WX_Q)/4096
3149 sp[P_WXTYPE] = wx_precip(sd, seg, row, cal_season(base), cal_night(base))
3150 sp[P_WXTEMP] = wx_temp(cal_season(base), cal_night(base), sd, seg)
3151 return 0
3152}
3153// write bst into MC_DISG bits 3-5 WITHOUT touching the disguise/met bits (0-2) or the
3154// FRIENDSHIP byte (bits 6-13) -- the old (dg & 7) mask silently erased any upper state
3155func dg_bst(dg: i64, b: i64) -> i64 { return dg - ((dg >> 3) & 7)*8 + b*8 }
3156
3157// ---------- B1/B2: soil + crops. THE EDIT JOURNAL IS THE SPARSE ACTIVE LIST ----------
3158// Every farm cell was player-touched, so it is already journaled at absolute coords: no scan,
3159// no extra memory, and growth writes go through the same journal so world and history can
3160// never disagree. Growth is Minecraft's integer stencil: p = 1/(floor(25/speed)+1), speed
3161// from soil wetness; rolls are seed+coord+epoch hashes -> replay-safe by construction.
3162func wc_grow_set(base: i64, lx: i64, ly: i64, lz: i64, b: i64) -> i64 {
3163 let s: *i64 = wst(base)
3164 if ej_put(base, lx + s[S_WOX], ly, lz + s[S_WOZ], b) < 0 {
3165 s[S_EJDROP] = s[S_EJDROP] + 1
3166 return 0
3167 }
3168 return vset(base, lx, ly, lz, b)
3169}
3170// water within the 5x5 ring at soil level or one below (ponds sit lower than their fields)
3171func wc_wet_near(base: i64, lx: i64, ly: i64, lz: i64) -> i64 {
3172 var dz2: i64 = 0 - 2
3173 while dz2 <= 2 {
3174 var dx2: i64 = 0 - 2
3175 while dx2 <= 2 {
3176 if vget(base, lx + dx2, ly, lz + dz2) == 4 { return 1 }
3177 if vget(base, lx + dx2, ly - 1, lz + dz2) == 4 { return 1 }
3178 dx2 = dx2 + 1
3179 }
3180 dz2 = dz2 + 1
3181 }
3182 return 0
3183}
3184func wc_farm_tick(base: i64) -> i64 {
3185 let s: *i64 = wst(base)
3186 let e: *i64 = ejp(base)
3187 let sp: *i64 = wsp(base)
3188 let rain: i64 = cal_rain(base)
3189 var i: i64 = 0
3190 while i < EJRN_SLOTS {
3191 let cur: i64 = e[i]
3192 if cur != 0 {
3193 let b: i64 = cur & 255
3194 if b >= B_FARM { if b <= B_FARMW {
3195 let k: i64 = (cur >> 8) - 1
3196 let ay: i64 = k & 63
3197 let az: i64 = ((k >> 6) & WATER_MAGIC_33554431) - WATER_MAGIC_16777216
3198 let ax: i64 = ((k >> 31) & WATER_MAGIC_33554431) - WATER_MAGIC_16777216
3199 let lx: i64 = ax - s[S_WOX]
3200 let lz: i64 = az - s[S_WOZ]
3201 // only cells inside the window tick (offscreen farms pause -- chunk law, honest)
3202 if vin(lx, ay, lz) == 1 { if vget(base, lx, ay, lz) == b {
3203 if b == B_FARM { if rain == 1 { wc_grow_set(base, lx, ay, lz, B_FARMW) } }
3204 if b == B_FARM { if rain == 0 { if wc_wet_near(base, lx, ay, lz) == 1 {
3205 wc_grow_set(base, lx, ay, lz, B_FARMW)
3206 } } }
3207 if b == B_FARMW { if rain == 0 { if wc_wet_near(base, lx, ay, lz) == 0 {
3208 wc_grow_set(base, lx, ay, lz, B_FARM)
3209 } } }
3210 if b >= B_CROP0 { if b <= B_CROP1 {
3211 var spd: i64 = sp[P_GROWD]
3212 let un: i64 = vget(base, lx, ay - 1, lz)
3213 if un == B_FARMW { spd = sp[P_GROWW] }
3214 if rain == 1 { if un == B_FARM { spd = sp[P_GROWW] } }
3215 if spd < 1 { spd = 1 }
3216 let den: i64 = 25/spd + 1
3217 if whash(ax*131 + ay, az*97, s[S_SEED] + s[S_T]/sp[P_GROWE]) % den == 0 {
3218 wc_grow_set(base, lx, ay, lz, b + 1)
3219 if b + 1 == B_CROP2 { s[S_GROWN] = s[S_GROWN] + 1 }
3220 }
3221 } }
3222 } }
3223 } }
3224 }
3225 i = i + 1
3226 }
3227 return 0
3228}
3229
3230// ---------- A4/C1 shared state maintenance (springs + needs) ----------
3231// GAIT DRIVE IS STILL A FREE-RUNNING CLOCK (debt 1785770635 STAYS OPEN) -- and the reason is now
3232// MEASURED rather than guessed, because the obvious fix was tried on 2026-08-25 and REFUTED by
3233// nx_wasm_craft_gate T36 before it could ship. Advancing MC_PH by distance travelled makes the
3234// tissue drive track her real cadence, which is correct and is what the oracle band wants. It is
3235// ALSO CIRCULAR: line ~1849 derives her wander heading FROM ph (dx = sin(ph*3), dz = cos(ph*3)),
3236// so a girl who stops moving freezes her phase, and a frozen phase freezes her heading -- she can
3237// never restart. The gate caught it exactly: the bold girl's approach distance stayed at her START
3238// value (1024, was 577) and the run went 52/53 RED. THE REAL FIX IS TO SPLIT THE TWO PHASES: a
3239// wander phase that must free-run for behaviour, and a gait phase that must track travel for
3240// tissue. That needs one word of per-mob state (accumulated stride distance), which touches the
3241// entity schema and therefore the save-format teeth (T15, T18, T50) -- a deliberate change, not a
3242// drive-by. Reverted here so the tree stays GREEN; the diagnosis is the deliverable.
3243func wc_soft_seat_all(base: i64) -> i64 {
3244 let m: *i64 = mobp(base)
3245 let sb0: *i64 = (base + O_SOFT) as *i64
3246 var k: i64 = 0
3247 while k < en_count(m) {
3248 let h: i64 = en_nth(m, k)
3249 if h > 0 { sb_seat_mob(sb0, k, en_get(m, h, MC_X)/4, en_get(m, h, MC_Y)/4, en_get(m, h, MC_Z)/4) }
3250 k = k + 1
3251 }
3252 return 0
3253}
3254// ---------- THE TRAVELER'S PASSPORT (isekai): character + party cross worlds ----------
3255func passp(base: i64) -> *i64 { return (base + O_PASS) as *i64 }
3256func wc_pass_ck(p: *i64) -> i64 {
3257 return gsc_fold_words(SAVE_CK_SEED, p, 0, 21, SAVE_CK_PRIME, SAVE_CK_MASK)
3258}
3259func wc_party_count(base: i64) -> i64 {
3260 let m: *i64 = mobp(base)
3261 var n: i64 = 0
3262 var k: i64 = 0
3263 while k < en_count(m) {
3264 let h: i64 = en_nth(m, k)
3265 if h > 0 { if (en_get(m, h, MC_DISG) & 4) == 4 { n = n + 1 } }
3266 k = k + 1
3267 }
3268 return n
3269}
3270// fold the session's deltas into the lifetime ledger (snapshot words 32+ make this idempotent:
3271// a world reload or a second build never double-counts a deed)
3272func wc_pass_sync(base: i64) -> i64 {
3273 let p: *i64 = passp(base)
3274 let s: *i64 = wst(base)
3275 if p[0] != PASS_MAGIC {
3276 var z: i64 = 0
3277 while z < 48 { p[z] = 0; z = z + 1 }
3278 p[0] = PASS_MAGIC
3279 p[1] = 1
3280 p[2] = whash(s[S_SEED], s[S_T], 20260802)
3281 // snapshot stays ZERO on creation: a traveler born mid-world ABSORBS what he has
3282 // already done here as his first deeds. Baselining at current values silently
3283 // discarded them (gate T50 caught exactly that: 7 harvests -> ledger 0).
3284 }
3285 var d: i64 = s[S_BROKEN] - p[32]
3286 if d > 0 { p[3] = p[3] + d }
3287 p[32] = s[S_BROKEN]
3288 d = s[S_PLACED] - p[33]
3289 if d > 0 { p[4] = p[4] + d }
3290 p[33] = s[S_PLACED]
3291 d = s[S_HARV] - p[34]
3292 if d > 0 { p[5] = p[5] + d }
3293 p[34] = s[S_HARV]
3294 d = s[S_PLANT] - p[35]
3295 if d > 0 { p[6] = p[6] + d }
3296 p[35] = s[S_PLANT]
3297 d = s[S_FOUND] - p[36]
3298 if d > 0 { p[7] = p[7] + d }
3299 p[36] = s[S_FOUND]
3300 d = s[S_LOST] - p[37]
3301 if d > 0 { p[8] = p[8] + d }
3302 p[37] = s[S_LOST]
3303 d = s[S_T] - p[38]
3304 if d > 0 { p[9] = p[9] + d }
3305 p[38] = s[S_T]
3306 p[10] = p[10] | (1 << s[S_VAR])
3307 // the party rides along: up to 3 companions' (kind, bond-word, genome)
3308 let m: *i64 = mobp(base)
3309 var pc: i64 = 0
3310 var k: i64 = 0
3311 while k < en_count(m) {
3312 let h: i64 = en_nth(m, k)
3313 if h > 0 { if (en_get(m, h, MC_DISG) & 4) == 4 { if pc < 3 {
3314 p[12 + pc*3] = en_get(m, h, MC_KIND)
3315 p[13 + pc*3] = en_get(m, h, MC_DISG)
3316 p[14 + pc*3] = en_get(m, h, MC_GENE)
3317 pc = pc + 1
3318 } } }
3319 k = k + 1
3320 }
3321 p[11] = pc
3322 p[21] = wc_pass_ck(p)
3323 return PASS_LEN
3324}
3325// arriving traveler: validate, re-baseline the snapshot, and WALK THE PARTY OFF THE BOAT --
3326// each companion respawns beside the player with bond and genome intact. Refusal touches
3327// nothing: -1 magic, -2 version, -3 checksum (the save doors' law).
3328func wc_pass_apply(base: i64) -> i64 {
3329 let p: *i64 = passp(base)
3330 if p[0] != PASS_MAGIC { return 0 - 1 }
3331 if p[1] != 1 { return 0 - 2 }
3332 if wc_pass_ck(p) != p[21] { return 0 - 3 }
3333 let s: *i64 = wst(base)
3334 p[32] = s[S_BROKEN]
3335 p[33] = s[S_PLACED]
3336 p[34] = s[S_HARV]
3337 p[35] = s[S_PLANT]
3338 p[36] = s[S_FOUND]
3339 p[37] = s[S_LOST]
3340 p[38] = s[S_T]
3341 p[10] = p[10] | (1 << s[S_VAR])
3342 let m: *i64 = mobp(base)
3343 var pc: i64 = 0
3344 while pc < p[11] { if pc < 3 {
3345 let hn: i64 = en_spawn(m)
3346 if hn > 0 {
3347 let ang: i64 = pc*2*IT_PI/3 + IT_PI/6
3348 var ax: i64 = s[S_CX] + it_sin4096(ang)*3*256/WATER_MAGIC_4096
3349 var az: i64 = s[S_CZ] + it_cos4096(ang)*3*256/WATER_MAGIC_4096
3350 ax = wclamp(ax, 2*256, (WX - 2)*256)
3351 az = wclamp(az, 2*256, (WZ - 2)*256)
3352 let gy: i64 = mob_ground(base, ax >> 8, WY - 1, az >> 8)
3353 en_set(m, hn, MC_X, ax)
3354 en_set(m, hn, MC_Z, az)
3355 en_set(m, hn, MC_Y, (gy + 1)*256)
3356 en_set(m, hn, MC_KIND, p[12 + pc*3])
3357 en_set(m, hn, MC_DISG, p[13 + pc*3])
3358 en_set(m, hn, MC_GENE, p[14 + pc*3])
3359 en_set(m, hn, MC_PH, whash(p[14 + pc*3], pc, s[S_SEED]) % WATER_MAGIC_4096)
3360 }
3361 pc = pc + 1
3362 } }
3363 wc_soft_seat_all(base)
3364 wc_need_seed(base)
3365 p[21] = wc_pass_ck(p)
3366 return 0
3367}
3368
3369// level + party-invite as BASE-PARAMETERIZED cores (the doors below are thin wrappers, so the
3370// gate exercises the same code the game runs -- one definition, zero drift)
3371func pass_level_of(base: i64) -> i64 {
3372 let p: *i64 = passp(base)
3373 if p[0] != PASS_MAGIC { return 1 }
3374 var sc: i64 = p[5]*4 + p[6] + p[7]*20 + p[3]/8 + p[4]/8 + p[9]/72000*3
3375 var lv: i64 = 1
3376 while sc >= lv*10 { sc = sc - lv*10; lv = lv + 1 }
3377 return lv
3378}
3379func party_of(base: i64) -> i64 {
3380 let s: *i64 = wst(base)
3381 if s[S_NPCT] < 0 { return 0 - 1 }
3382 let m: *i64 = mobp(base)
3383 let h: i64 = en_nth(m, s[S_NPCT])
3384 if h <= 0 { return 0 - 1 }
3385 let dg: i64 = en_get(m, h, MC_DISG)
3386 if ((dg >> 6) & 255) < 40 { return 0 - 2 } // a bond, not a kidnapping
3387 if (dg & 4) == 4 {
3388 en_set(m, h, MC_DISG, dg - 4) // toggle: she may also LEAVE
3389 return 0
3390 }
3391 if wc_party_count(base) >= 3 { return 0 - 3 }
3392 en_set(m, h, MC_DISG, dg | 4)
3393 th_add(base, s[S_NPCT], TH_MET, 20, 14400)
3394 return 1
3395}
3396
3397// outfit for girl k: identity data gates the dimension; her genome picks within it.
3398// 0 clothed · 1 topless · 2 nude (all figures are the one adult mesh)
3399// THE POSE DOOR (operator 2026-08-02: "interact with me and the environment in various poses
3400// beyond the one" -- the pose PIPELINE's first rung). The engine ALREADY computes behavioral
3401// states (T44: sleep/flee via MC_DISG bits 3-5; movement via O_MOBAUX deltas) and the renderer
3402// ignores every one of them: sleeping girls play the walk clip, idle girls walk in place. Same
3403// class as the face: THE DATA EXISTS AND DRIVES NOTHING -- so the consumer contract comes first.
3404// Pose ids (the page consumes; the coming NXA pose BANK indexes by these):
3405// 0 idle-stand (breath only) 1 walk 2 sleep-lying 3 flee-run 4 sit 5 lie-awake
3406// 4/5 are RESERVED rows: behaviors from the towel/smart-object chain select them; publishing
3407// the id space now means new poses arrive as DATA against a stable contract, not an engine edit.
3408func mob_pose(base: i64, k: i64) -> i64 {
3409 let m: *i64 = mobp(base)
3410 let st: i64 = (en_get(m, en_nth(m, k), MC_DISG) >> 3) & 7
3411 if st == 2 { return 2 }
3412 if st == 4 { return 3 }
3413 if mob_spd_q8i(base, k) > 0 { return 1 }
3414 return 0
3415}
3416func mobpose(k: i64) -> i64 { return mob_pose(0, k) }
3417
3418// GE13b THE SELECTOR: what a girl wears, from the world's outfit mode, her genome and her pose.
3419// Returns the OUTFIT word: upper garment id << GAR_SLOT_SH | lower garment id, 0 = nothing in that
3420// slot; one-piece and dresses cover both and ride the LOWER slot. Every branch is a table LOOKUP --
3421// no colour, no shape, no category is decided here; a world that rewrites a garment row rewrites
3422// what these ids mean, and a row the world does not carry (0) is not wearable.
3423// mode 0 (identity keeps everyone clothed): CASUAL/EVENING by the beach bits 18-19 -- tee+shorts,
3424// tee+skirt, sundress, evening dress. Before this every girl wore the one dress.
3425// mode 1 (MIXED beach): the incumbent bits, garment by garment -- 0 bikini top+bottom, 1 one-piece,
3426// 2 bikini bottom only (topless), 3 nothing (nude). Girl-for-girl the SAME 0/1/2 the old rule
3427// produced; gate T85 proves it against mob_outfit_v1 below.
3428// mode 2 (SWIMWEAR-ONLY): bit 18 picks bikini or one-piece; never a cover-up, never casual.
3429// mode 3 (NUDE BEACH): nothing worn.
3430// POSE OVERLAY: asleep (mob_pose SLEEP) on a beach with swim bottoms on, she wraps a SARONG
3431// (beach-cover) over them -- same coverage bit, so her legacy value never flickers -- unless
3432// she is in water, where a wrap is never worn (swim => swim).
3433func wc_mob_in_water(base: i64, k: i64) -> i64 {
3434 let m: *i64 = mobp(base)
3435 let h: i64 = en_nth(m, k)
3436 if vget(base, en_get(m, h, MC_X) >> 8, en_get(m, h, MC_Y) >> 8, en_get(m, h, MC_Z) >> 8) == B_WATER { return 1 }
3437 return 0
3438}
3439func wc_outfit(base: i64, k: i64) -> i64 {
3440 let md: i64 = wsp(base)[P_OUTFIT]
3441 let m: *i64 = mobp(base)
3442 let gb: i64 = (en_get(m, en_nth(m, k), MC_GENE) >> MG_BEACH_SH) & MG_BEACH_MASK
3443 var up: i64 = 0
3444 var lo: i64 = 0
3445 var beach: i64 = 0
3446 if md == 0 {
3447 if gb == 0 { up = GAR_TEE; lo = GAR_SHORTS }
3448 if gb == 1 { up = GAR_TEE; lo = GAR_SKIRT }
3449 if gb == 2 { lo = GAR_SUNDRESS }
3450 if gb == 3 { lo = GAR_DRESS }
3451 }
3452 if md == 1 {
3453 beach = 1
3454 if gb == 0 { up = GAR_BIKINI_TOP; lo = GAR_BIKINI_BOT }
3455 if gb == 1 { lo = GAR_ONEPIECE }
3456 if gb == 2 { lo = GAR_BIKINI_BOT }
3457 }
3458 if md == 2 {
3459 beach = 1
3460 if (gb & 1) == 0 { up = GAR_BIKINI_TOP; lo = GAR_BIKINI_BOT }
3461 if (gb & 1) == 1 { lo = GAR_ONEPIECE }
3462 }
3463 if beach == 1 { if lo > 0 { if gar_cat(gar_row(base, lo)) == GAR_CAT_SWIM { if gar_cov(gar_row(base, lo)) == GAR_COV_BOT {
3464 if mob_pose(base, k) == MOB_POSE_SLEEP { if wc_mob_in_water(base, k) == 0 { lo = GAR_SARONG } }
3465 } } } }
3466 // a row the world does not carry is not a garment: the slot empties (the neg-control's lever)
3467 if gar_row(base, up) == 0 { up = 0 }
3468 if gar_row(base, lo) == 0 { lo = 0 }
3469 return (up << GAR_SLOT_SH) | lo
3470}
3471// 0 clothed / 1 topless / 2 nude, DERIVED from the worn set -- the contract every existing consumer
3472// (the page's uOF bands, T49, T77) keeps, now a consequence of the wardrobe instead of the wardrobe.
3473func mob_outfit(base: i64, k: i64) -> i64 {
3474 let o: i64 = wc_outfit(base, k)
3475 return gar_legacy(gar_cov(gar_row(base, o >> GAR_SLOT_SH)) | gar_cov(gar_row(base, o & GAR_ID_MASK)))
3476}
3477// THE PRE-WARDROBE RULE, kept RUNNABLE as the gate's oracle: T85 proves mob_outfit == mob_outfit_v1
3478// for every girl in every mode, so no consumer sees a different girl. Not called by the game.
3479func mob_outfit_v1(base: i64, k: i64) -> i64 {
3480 let md9: i64 = wsp(base)[P_OUTFIT]
3481 if md9 == 0 { return 0 }
3482 // GE13: per-world outfit FLAGS, all data -- 1 = MIXED (genome bits decide; the incumbent
3483 // beach), 2 = SWIMWEAR-ONLY (dimension active, variety off), 3 = NUDE BEACH (all bare).
3484 if md9 == 2 { return 0 }
3485 if md9 == 3 { return 2 }
3486 let m: *i64 = mobp(base)
3487 let g: i64 = (en_get(m, en_nth(m, k), MC_GENE) >> 18) & 3
3488 if g <= 1 { return 0 }
3489 if g == 2 { return 1 }
3490 return 2
3491}
3492
3493func wc_need_seed(base: i64) -> i64 {
3494 let m: *i64 = mobp(base)
3495 let s: *i64 = wst(base)
3496 let nd0: *i64 = (base + O_NEED) as *i64
3497 var k: i64 = 0
3498 while k < en_count(m) {
3499 nd0[k*4] = 300 + (whash(k, 11, s[S_SEED]) % 200)
3500 nd0[k*4 + 1] = 700
3501 nd0[k*4 + 2] = 300 + (whash(k, 23, s[S_SEED]) % 200)
3502 nd0[k*4 + 3] = 0
3503 k = k + 1
3504 }
3505 return 0
3506}
3507
3508// ---------- C3/C5: the inner life (thought ledger + gossip records) ----------
3509func mindp(base: i64) -> *i64 { return (base + O_MIND) as *i64 }
3510func wc_mind_zero(base: i64) -> i64 {
3511 let mp: *i64 = mindp(base)
3512 var i: i64 = 0
3513 while i < MOB_CAP*24 { mp[i] = 0; i = i + 1 }
3514 return 0
3515}
3516// add/refresh a thought: same id refreshes in place; else a dead slot; else rotate one out
3517func th_add(base: i64, k: i64, id: i64, mag: i64, ttl: i64) -> i64 {
3518 let mp: *i64 = mindp(base)
3519 let s: *i64 = wst(base)
3520 let b: i64 = k*24
3521 var slot: i64 = 0 - 1
3522 var i: i64 = 0
3523 while i < 8 {
3524 if (mp[b + i*2] >> 8) == id { slot = i }
3525 i = i + 1
3526 }
3527 if slot < 0 {
3528 i = 0
3529 while i < 8 {
3530 if slot < 0 { if mp[b + i*2 + 1] <= s[S_T] { slot = i } }
3531 i = i + 1
3532 }
3533 }
3534 if slot < 0 { slot = (s[S_T] + k) % 8 }
3535 mp[b + slot*2] = id*256 + (mag + 128)
3536 mp[b + slot*2 + 1] = s[S_T] + ttl
3537 return 0
3538}
3539// mood = the sum of LIVE thoughts, clamped -- always decomposable back into its reasons
3540func th_mood(base: i64, k: i64) -> i64 {
3541 let mp: *i64 = mindp(base)
3542 let s: *i64 = wst(base)
3543 let b: i64 = k*24
3544 var mo: i64 = 0
3545 var i: i64 = 0
3546 while i < 8 {
3547 if mp[b + i*2] != 0 { if mp[b + i*2 + 1] > s[S_T] { mo = mo + (mp[b + i*2] & 255) - 128 } }
3548 i = i + 1
3549 }
3550 if mo > 100 { mo = 100 }
3551 if mo < 0 - 100 { mo = 0 - 100 }
3552 return mo
3553}
3554// a gossip fact: (subject, predicate, confidence, born). A better-known copy never downgrades.
3555func go_add(base: i64, k: i64, subj: i64, pred: i64, conf: i64) -> i64 {
3556 let mp: *i64 = mindp(base)
3557 let s: *i64 = wst(base)
3558 let b: i64 = k*24 + 16
3559 var slot: i64 = 0 - 1
3560 var free: i64 = 0 - 1
3561 var oldest: i64 = 0
3562 var oldt: i64 = SAVE_CK_MASK
3563 var i: i64 = 0
3564 while i < 4 {
3565 let w: i64 = mp[b + i*2]
3566 if w == 0 { if free < 0 { free = i } }
3567 if w != 0 { if (w >> 16) == subj { if ((w >> 8) & 255) == pred { slot = i } } }
3568 if mp[b + i*2 + 1] < oldt { oldt = mp[b + i*2 + 1]; oldest = i }
3569 i = i + 1
3570 }
3571 if slot >= 0 { if (mp[b + slot*2] & 255) >= conf { return 0 } }
3572 if slot < 0 { slot = free }
3573 if slot < 0 { slot = oldest }
3574 mp[b + slot*2] = subj*WATER_MAGIC_65536 + pred*256 + conf
3575 mp[b + slot*2 + 1] = s[S_T]
3576 return 0
3577}
3578func go_newest(base: i64, k: i64) -> i64 {
3579 let mp: *i64 = mindp(base)
3580 let b: i64 = k*24 + 16
3581 var best: i64 = 0 - 1
3582 var bt: i64 = 0 - 1
3583 var i: i64 = 0
3584 while i < 4 {
3585 if mp[b + i*2] != 0 { if mp[b + i*2 + 1] > bt { bt = mp[b + i*2 + 1]; best = i } }
3586 i = i + 1
3587 }
3588 return best
3589}
3590
3591// ---- GAIT: THE PARAMETRIC DRIVE ----------------------------------------------------------------
3592// decidegrees -> it-units. This is nx_quadruped's qdeg idiom at 10x resolution; that organ's
3593// quad_gait_pose_g is the working precedent in this estate for "a gait is DATA, amplitudes and
3594// per-limb phase offsets are the gait's parameters". It is deliberately NOT imported: it calls
3595// sys_mmap and this engine targets wasm, so the IDIOM is reused and the code is not duplicated.
3596func wc_ddeg(d: i64) -> i64 { return d*IT_PI/WC_DDEG_HALFTURN }
3597// AMPLITUDE from a PUBLISHED RANGE. The halving lives here, once, so no caller can forget it and
3598// no future reader has to work out which of the two a stored constant is.
3599func wc_amp_ddeg(range_ddeg: i64) -> i64 { return wc_ddeg(range_ddeg)/2 }
3600// STRIDE PHASE: one full cycle per WC_STRIDE_Q8 of PATH TRAVELLED. Distance-driven by
3601// construction, which is what makes pelvic rotation phase-locked to stride -- the coupling is
3602// structural, not a tuning. Two consequences fall out for free: a girl who stops has a FROZEN
3603// stride phase, so she stands instead of marching on the spot; and a girl who walks faster takes
3604// the same-length strides more often, so cadence emerges from speed instead of being a constant.
3605// ⚠THIS IS A SECOND, DIFFERENT QUANTITY FROM wc_gait_ang -- NOT A DUPLICATE RULER. wc_gait_ang
3606// drives the THORAX BOB, which is the SECOND HARMONIC of the stride (two steps per cycle, each
3607// with its own vertical rise and fall), and its scale is calibrated against the gait_bob_q8 row.
3608// ★THEIR RATIO IS A CHECKABLE CLAIM AND IT DOES NOT CURRENTLY HOLD. wc_gait_ang implies one bob
3609// per 962 q8 of travel, hence one stride per 1924 q8 = 7.5 m, against the cited 1.46 m -- the
3610// incumbent gait drive is calibrated 5.1x too long, and separately it is calibrated at SPEED (40
3611// q8/tick) which no mob ever reaches: mobs walk at 4-5 q8/tick, so the bob it produces in the
3612// live world is ~0.25 Hz where walking thorax oscillation is ~1.8-2 Hz.
3613// ⛔THAT DISAGREEMENT IS NAMED HERE AND DELIBERATELY NOT FIXED BY EDITING THE SHARED CONSTANT.
3614// wc_gait_ang feeds the soft-body anchor that gate teeth T40 and T54 are calibrated against, and
3615// silently moving a constant another tooth is calibrated on is how a green gate stops meaning
3616// anything. The renderer takes the cited stride; the bob keeps its own calibration; the gap goes
3617// on the board as a measurement with its arithmetic attached rather than into a quiet edit.
3618func wc_stride_ang(g_q8: i64) -> i64 { return g_q8*2*IT_PI/WC_STRIDE_Q8 }
3619// shortest signed representative of an angle difference, in (-IT_PI, IT_PI].
3620func wc_angwrap(d: i64) -> i64 {
3621 var r: i64 = d % (2*IT_PI)
3622 if r > IT_PI { r = r - 2*IT_PI }
3623 if r < 0 - IT_PI { r = r + 2*IT_PI }
3624 return r
3625}
3626// THE HEADING LOW-PASS. This one function is the whole wobble fix AND the fluidity parameter.
3627// WHAT IT REPLACES: nothing. There was no heading state at all -- the body's yaw was the PLAYER'S
3628// camera yaw, so every girl pivoted with the camera while translating along her own path, and the
3629// steering that produced that path is an 8-direction bang-bang controller (dx = +/-4 per axis with
3630// an independent per-axis deadband) whose sign can flip in a single tick and whose diagonal is
3631// 41 percent faster than its axis-aligned step. Facing derived from that signal inherits all of it.
3632// HOW IT STEERS WITHOUT atan2: there is no it_atan2 anywhere in the corpus (proven, corpus_complete
3633// =1), and adding one would be a second trigonometric approximation beside the incumbent. It is not
3634// needed. The CROSS PRODUCT of the current forward with the desired direction is sin(error) scaled
3635// by |desired|, which already carries both the SIGN and, near convergence, the MAGNITUDE of the
3636// error -- so a proportional controller can be built from the sin/cos this engine already has.
3637// cr < 0 / cr > 0 -> which way to turn
3638// dt < 0 -> the target is BEHIND: turn at the full allowance, sign broken deterministically
3639// The step is proportional to sin(error), so it goes to zero AS the error goes to zero: the
3640// approach is asymptotic and cannot overshoot, which is what "fluid" means here as opposed to a
3641// bang-bang controller that snaps (locked) or an unclamped one that rings (floppy).
3642// The allowance is speed-coupled: mx = 0 when she is not moving, so nobody spins on the spot.
3643func wc_hdg_step(hdg: i64, dx: i64, dz: i64) -> i64 {
3644 var ax: i64 = dx
3645 if ax < 0 { ax = 0 - ax }
3646 var az: i64 = dz
3647 if az < 0 { az = 0 - az }
3648 if ax + az == 0 { return hdg } // no intent, no turn: she is standing
3649 let mag: i64 = sd_isqrt(dx*dx + dz*dz)
3650 if mag <= 0 { return hdg }
3651 let fx: i64 = it_sin4096(hdg % (2*IT_PI))
3652 let fz: i64 = it_cos4096(hdg % (2*IT_PI))
3653 // cross and dot of forward (fx4096, unit) with the desired step (q8, magnitude mag)
3654 // ⚠NOT PRE-DIVIDED BY 4096. Dividing here first would floor cr to single digits at a mob's
3655 // 4-7 q8 step and quantise the whole controller into a handful of levels -- which is the
3656 // stutter this function exists to remove, reintroduced inside the fix. Both products stay in
3657 // 4096*q8 units and the one division happens at the end, against mag*4096.
3658 let cr: i64 = fx*dz - fz*dx // 4096 * mag * sin(error)
3659 let dt: i64 = fx*dx + fz*dz // 4096 * mag * cos(error)
3660 // quarter turn per stride, pro-rated by the ground this tick actually covers
3661 let mx: i64 = mag*WC_TURN_PER_STRIDE/WC_STRIDE_Q8
3662 // SIGN, AND IT WAS WRONG FIRST TIME. The (x,z) cross product fx*dz - fz*dx measures rotation
3663 // from +x toward +z, and this engine yaw runs the OTHER way round: forward is (sin yaw,
3664 // cos yaw), so increasing yaw turns from +z toward +x. The cross therefore carries
3665 // -sin(error), not +sin(error), and the correction is its NEGATION.
3666 // Settled against a case with an unambiguous answer rather than by staring at it: facing +z
3667 // (yaw 0) with the target at +x plainly needs yaw to INCREASE, and there the cross comes out
3668 // NEGATIVE -- so the turn is -cr.
3669 // WHAT THE WRONG SIGN DID, because it is not the failure anyone would predict: it does not
3670 // wobble or overshoot, it makes facing-directly-AWAY-from-the-target a stable point, so she
3671 // walks backwards away from where she is going and looks perfectly calm doing it. Gate tooth
3672 // T82 caught it as a 2-cycle that never converged out of a half-turn reversal.
3673 var st: i64 = 0 - mx*cr/(mag*WATER_MAGIC_4096) // = mx*sin(error): proportional, saturating
3674 if dt < 0 { // behind her: no proportional term is valid
3675 st = mx
3676 if cr > 0 { st = 0 - mx }
3677 }
3678 // THE INTEGER TAIL, and it is the FLOPPY failure mode arriving through rounding rather than
3679 // through a bad gain. The proportional term is mx*sin(error), so once sin(error) falls below
3680 // 1/mx it truncates to ZERO and she stops turning while still misaligned -- at the derived
3681 // allowance that floor sits near 0.84 degrees, so she would sit permanently just off her own
3682 // direction of travel and never arrive. Taking ONE UNIT instead is the smallest turn this
3683 // representation can express, so it is the smallest possible repair and needs no constant of
3684 // its own. Both guards require a nonzero cross product, so a girl who IS aligned still turns
3685 // by nothing at all.
3686 // ⚠STATED IMPRECISION: this leaves a one-unit limit cycle about the exact heading, which is
3687 // 0.014 degrees and bounded. It is preferred to the alternative, which is a permanent and
3688 // UNBOUNDED offset -- a bounded jitter below the angular resolution of the rig beats a
3689 // static error you can see.
3690 if st == 0 { if cr > 0 { st = 0 - 1 } }
3691 if st == 0 { if cr < 0 { st = 1 } }
3692 if st > mx { st = mx }
3693 if st < 0 - mx { st = 0 - mx }
3694 var nh: i64 = hdg + st
3695 if nh < 0 { nh = nh + 2*IT_PI }
3696 if nh >= 2*IT_PI { nh = nh - 2*IT_PI }
3697 return nh
3698}
3699func wc_hdg_of(base: i64, k: i64) -> i64 { let hp: *i64 = (base + O_MOBHDG) as *i64; return hp[k] }
3700// the girl's own stride phase, in it-units, from the path length she has actually walked
3701func wc_mob_sph(base: i64, k: i64) -> i64 {
3702 let nd: *i64 = (base + O_NEED) as *i64
3703 return wc_stride_ang(nd[k*4 + WC_NEED_GAIT]) % (2*IT_PI)
3704}
3705// ---- THE POSE, AS FIVE PURE FUNCTIONS OF STRIDE PHASE -------------------------------------------
3706// Extracted out of wc_mob_mesh deliberately: the renderer needs them and the GATE needs them, and a
3707// gate that recomputed these angles from the same formula would be testing a COPY of the mechanism
3708// rather than the mechanism. One definition, two callers, and a tooth that fails when the shipping
3709// code changes. They take only the phase, so they are trivially testable at any phase without a
3710// world, a camera, a rig or a framebuffer.
3711// SIGN CONVENTION, stated once: pelvis LEADS (+), thorax TRAILS (-, applied as a local yaw on the
3712// chest bone whose parent is the pelvis), legs and arms swing in elevation, elbow only flexes.
3713func wc_gp_pelvis(sph: i64) -> i64 { return wc_amp_ddeg(WC_PELVIS_ROT_DDEG)*it_sin4096(sph % (2*IT_PI))/WATER_MAGIC_4096 }
3714func wc_gp_thorax(sph: i64) -> i64 { return wc_amp_ddeg(WC_THORAX_ROT_DDEG)*it_sin4096(sph % (2*IT_PI))/WATER_MAGIC_4096 }
3715func wc_gp_leg(sph: i64) -> i64 { return it_sin4096(sph % (2*IT_PI))*(IT_PI/6)/WATER_MAGIC_4096 }
3716func wc_gp_arm(sph: i64) -> i64 { return it_sin4096(sph % (2*IT_PI))*(IT_PI/12)/WATER_MAGIC_4096 }
3717// (1 - cos)/2 scaled by the cited ROM: 0 at phase 0, ROM at the half cycle, never negative, and no
3718// free parameter -- the anatomical floor at zero and the published range fix both ends between them.
3719func wc_gp_elbow(sph: i64) -> i64 { return wc_ddeg(WC_ELBOW_ROM_DDEG)*(WATER_MAGIC_4096 - it_cos4096(sph % (2*IT_PI)))/(2*WATER_MAGIC_4096) }
3720
3721// mob wander: smooth deterministic drift (phase-driven sin/cos), terrain-following, player-curious
3722// within 6 blocks. Pure integer -- the T4 determinism checksum covers mobs by construction.
3723func mob_tick(base: i64) -> i64 {
3724 let m: *i64 = mobp(base)
3725 let s: *i64 = wst(base)
3726 let nd: *i64 = (base + O_NEED) as *i64
3727 let vyp: *i64 = (base + O_MOBVY) as *i64 // GE12 vertical velocity per mob
3728 let night: i64 = cal_night(base)
3729 var k: i64 = 0
3730 while k < en_count(m) {
3731 let h: i64 = en_nth(m, k)
3732 if h > 0 {
3733 var x: i64 = en_get(m, h, MC_X)
3734 var z: i64 = en_get(m, h, MC_Z)
3735 var ph: i64 = en_get(m, h, MC_PH) + 5
3736 // FOOTSTRIKE IMPULSE: bank the pre-move vertical velocity. After the movement code
3737 // below, (vy_pre < 0 && vyp[k] == 0) is precisely "GE12's integrator arrested a
3738 // fall this tick" -- ground contact -- and |vy_pre| IS the ground-reaction delta-v.
3739 let vy_pre: i64 = vyp[k]
3740 // C1 NEEDS drift (integer, staggered by handle -- the village never moves in lockstep)
3741 let nb: i64 = k*4
3742 if (s[S_T] + h) % 8 == 0 {
3743 if nd[nb] < 1000 { nd[nb] = nd[nb] + 2 } // hunger grows
3744 if nd[nb + 2] < 1000 { nd[nb + 2] = nd[nb + 2] + 3 } // loneliness grows
3745 if nd[nb + 1] > 0 { nd[nb + 1] = nd[nb + 1] - 1 } // energy drains
3746 }
3747 // C3: lived moments become LEDGER ENTRIES (staggered; each refreshes in place)
3748 if (s[S_T] + h) % 128 == 0 {
3749 let bst0: i64 = (en_get(m, h, MC_DISG) >> 3) & 7
3750 if cal_rain(base) == 1 { if bst0 != 2 { th_add(base, k, TH_RAIN, 0 - 8, 1200) } }
3751 if nd[nb] > 700 { th_add(base, k, TH_HUNGRY, 0 - 10, 900) }
3752 if night == 1 { if bst0 == 2 { th_add(base, k, TH_SLEPT, 6, 3600) } }
3753 }
3754 // BEHAVIOR LAYER (C1/C2 -- lives picked by NEEDS x the spec ADVERTISEMENT table,
3755 // not a dice roll): state in MC_DISG bits 3-5, repicked on the staggered 240-tick
3756 // clock. 0=WANDER 1=SOCIALIZE 2=REST 3=FORAGE 4=FLEE. At night the village SLEEPS
3757 // (the C2 schedule) -- while the wardens hunt wider. The old spec-weights TODO is
3758 // RESOLVED: behavior now reads needs + P_ADV rows, both data.
3759 let dg: i64 = en_get(m, h, MC_DISG)
3760 var bst: i64 = (dg >> 3) & 7
3761 if (s[S_T] + h*37) % 240 == 0 {
3762 var sc1: i64 = 220 + (whash(h*911, s[S_T]/240, s[S_SEED]) % 64)
3763 // C3 feedback: the ledger BENDS the life -- a glad girl seeks company, a sad
3764 // one withdraws to rest. Mood is data the argmax reads, not a scripted branch.
3765 let mo9: i64 = th_mood(base, k)
3766 var sc2: i64 = nd[nb + 2]
3767 if mo9 > 0 { sc2 = sc2 + mo9 }
3768 var sc3: i64 = 1000 - nd[nb + 1]
3769 if mo9 < 0 { sc3 = sc3 - mo9*2 }
3770 var sc4: i64 = 0
3771 var ai: i64 = 0
3772 while ai < 6 {
3773 let ad: i64 = wsp(base)[P_ADV + ai]
3774 if ad != 0 { if ((ad >> 8) & 255) == 0 {
3775 let ab: i64 = (ad >> 16) & 255
3776 var fnd: i64 = 0
3777 var az2: i64 = (z >> 8) - 3
3778 while az2 <= (z >> 8) + 3 {
3779 var ax2: i64 = (x >> 8) - 3
3780 while ax2 <= (x >> 8) + 3 {
3781 let gy2: i64 = mob_ground(base, ax2, (en_get(m, h, MC_Y) >> 8) + 2, az2)
3782 if gy2 >= 0 {
3783 if vget(base, ax2, gy2, az2) == ab { fnd = 1 }
3784 if vget(base, ax2, gy2 + 1, az2) == ab { fnd = 1 }
3785 }
3786 ax2 = ax2 + 1
3787 }
3788 az2 = az2 + 1
3789 }
3790 if fnd == 1 {
3791 let cand: i64 = nd[nb]*(ad & 255)/8
3792 if cand > sc4 { sc4 = cand }
3793 }
3794 } }
3795 ai = ai + 1
3796 }
3797 bst = 0
3798 var top: i64 = sc1
3799 if sc2 > top { top = sc2; bst = 1 }
3800 if sc3 > top { top = sc3; bst = 2 }
3801 if sc4 > top { top = sc4; bst = 3 }
3802 if night == 1 { bst = 2 }
3803 en_set(m, h, MC_DISG, dg_bst(dg, bst))
3804 }
3805 if (dg & 3) == 3 {
3806 // a hidden human checks for hunters -- night widens the danger radius
3807 var wr: i64 = 8*256
3808 if night == 1 { wr = 12*256 }
3809 var wnd: i64 = wr
3810 var wnx: i64 = 0
3811 var wnz: i64 = 0
3812 var kf: i64 = 0
3813 while kf < en_count(m) {
3814 let hf: i64 = en_nth(m, kf)
3815 if en_get(m, hf, MC_KIND) == 2 { if (en_get(m, hf, MC_DISG) & 1) == 0 {
3816 var fdx: i64 = en_get(m, hf, MC_X) - x
3817 if fdx < 0 { fdx = 0 - fdx }
3818 var fdz: i64 = en_get(m, hf, MC_Z) - z
3819 if fdz < 0 { fdz = 0 - fdz }
3820 if fdx + fdz < wnd { wnd = fdx + fdz; wnx = en_get(m, hf, MC_X); wnz = en_get(m, hf, MC_Z) }
3821 } }
3822 kf = kf + 1
3823 }
3824 if wnd < wr { bst = 4; en_set(m, h, MC_DISG, dg_bst(en_get(m, h, MC_DISG), 4)) }
3825 }
3826 var dx: i64 = it_sin4096(ph*3 % (2*IT_PI))*5/WATER_MAGIC_4096
3827 var dz: i64 = it_cos4096(ph*3 % (2*IT_PI))*5/WATER_MAGIC_4096
3828 if bst == 1 {
3829 // SOCIALIZE: walk to the nearest other girl; inside 1.2 blocks, stand together
3830 var snd: i64 = SAVE_CK_MASK
3831 var snx: i64 = x
3832 var snz: i64 = z
3833 var snk: i64 = 0 - 1
3834 var ks: i64 = 0
3835 while ks < en_count(m) {
3836 let hs: i64 = en_nth(m, ks)
3837 if hs != h {
3838 var sdx: i64 = en_get(m, hs, MC_X) - x
3839 if sdx < 0 { sdx = 0 - sdx }
3840 var sdz: i64 = en_get(m, hs, MC_Z) - z
3841 if sdz < 0 { sdz = 0 - sdz }
3842 if sdx + sdz < snd { snd = sdx + sdz; snx = en_get(m, hs, MC_X); snz = en_get(m, hs, MC_Z); snk = ks }
3843 }
3844 ks = ks + 1
3845 }
3846 dx = 0
3847 dz = 0
3848 if snd > 300 {
3849 if snx > x + 32 { dx = 4 }
3850 if snx < x - 32 { dx = 0 - 4 }
3851 if snz > z + 32 { dz = 4 }
3852 if snz < z - 32 { dz = 0 - 4 }
3853 }
3854 if snd <= 300 {
3855 ph = ph - 4 // stilled gait: talking, not marching
3856 if (s[S_T] + h) % 4 == 0 { // company relieves loneliness
3857 nd[nb + 2] = nd[nb + 2] - 10
3858 if nd[nb + 2] < 0 { nd[nb + 2] = 0 }
3859 }
3860 // C5: CONVERSATION MOVES FACTS. One fact passes speaker->listener with
3861 // confidence -15; below conf 30 the predicate may FLIP (1<->2) -- rumor
3862 // decays into misinformation BY MECHANIC, and hearing it leaves a thought.
3863 if (s[S_T] + h) % 128 == 0 {
3864 th_add(base, k, TH_COMPANY, 5, 1200)
3865 let gi: i64 = go_newest(base, k)
3866 if gi >= 0 { if snk >= 0 {
3867 let gw: i64 = mindp(base)[k*24 + 16 + gi*2]
3868 let conf2: i64 = (gw & 255) - 15
3869 if conf2 > 0 {
3870 var pred2: i64 = (gw >> 8) & 255
3871 if conf2 < 30 { if whash(s[S_T], k*31 + snk, s[S_SEED] + 777) % 3 == 0 {
3872 pred2 = 3 - pred2
3873 } }
3874 go_add(base, snk, gw >> 16, pred2, conf2)
3875 if pred2 == GO_TAKEN { th_add(base, snk, TH_HEARD_TAKEN, 0 - 10, 7200) }
3876 if pred2 == GO_FRIEND { th_add(base, snk, TH_HEARD_FRIEND, 6, 7200) }
3877 }
3878 } }
3879 }
3880 }
3881 }
3882 if bst == 2 {
3883 dx = 0
3884 dz = 0
3885 ph = ph - 4 // REST: seated stillness
3886 if (s[S_T] + h) % 4 == 0 { // sleep restores; night sleep restores MORE
3887 var rg: i64 = 2
3888 if night == 1 { rg = 8 }
3889 nd[nb + 1] = nd[nb + 1] + rg
3890 if nd[nb + 1] > 1000 { nd[nb + 1] = 1000 }
3891 }
3892 }
3893 if bst == 3 {
3894 // FORAGE: graze rhythm -- steps then a pause, head-down cadence; grazing feeds her
3895 if (s[S_T] + h) % 16 >= 6 { dx = 0; dz = 0; ph = ph - 3 }
3896 if (s[S_T] + h) % 8 == 0 {
3897 nd[nb] = nd[nb] - 12
3898 if nd[nb] < 0 { nd[nb] = 0 }
3899 }
3900 }
3901 if bst == 4 {
3902 // FLEE: straight away from the nearest warden, fast
3903 var wnd2: i64 = SAVE_CK_MASK
3904 var wax: i64 = x
3905 var waz: i64 = z
3906 var kf2: i64 = 0
3907 while kf2 < en_count(m) {
3908 let hf2: i64 = en_nth(m, kf2)
3909 if en_get(m, hf2, MC_KIND) == 2 { if (en_get(m, hf2, MC_DISG) & 1) == 0 {
3910 var fdx2: i64 = en_get(m, hf2, MC_X) - x
3911 if fdx2 < 0 { fdx2 = 0 - fdx2 }
3912 var fdz2: i64 = en_get(m, hf2, MC_Z) - z
3913 if fdz2 < 0 { fdz2 = 0 - fdz2 }
3914 if fdx2 + fdz2 < wnd2 { wnd2 = fdx2 + fdz2; wax = en_get(m, hf2, MC_X); waz = en_get(m, hf2, MC_Z) }
3915 } }
3916 kf2 = kf2 + 1
3917 }
3918 dx = 0
3919 dz = 0
3920 if wax > x { dx = 0 - 6 }
3921 if wax <= x { dx = 6 }
3922 if waz > z { dz = 0 - 6 }
3923 if waz <= z { dz = 6 }
3924 }
3925 // PERSONALITY (genome bits 13-15, INHERITED): the bold<->shy spectrum photographers
3926 // know -- engagement is a RANGE, not one script. Near the player: shy girls (0-1)
3927 // retreat and keep 5 blocks; watchful ones (2-3) freeze and observe, backing off
3928 // only if crowded; warm ones (4-5) approach to a polite arm's length; bold ones
3929 // (6-7) come right to your side. FORAGE also reads it: bold girls gather TOWARD
3930 // you (the obvious pick-up), shy girls drift their gathering away (the oblivious).
3931 let pdx: i64 = s[S_CX] - x
3932 let pdz: i64 = s[S_CZ] - z
3933 var pd: i64 = pdx
3934 if pd < 0 { pd = 0 - pd }
3935 var pdz2: i64 = pdz
3936 if pdz2 < 0 { pdz2 = 0 - pdz2 }
3937 let bold: i64 = (en_get(m, h, MC_GENE) >> 13) & 7
3938 // HABITUATION: the approach/watch drive runs only while she is lonely enough to
3939 // care (the player is a smart object too -- company below relieves the need, and a
3940 // satisfied girl walks on). Shy retreat is FEAR, not curiosity: never gated.
3941 var curi: i64 = 1
3942 if nd[nb + 2] < wsp(base)[P_CURI] { curi = 0 }
3943 if bold <= 1 { curi = 1 }
3944 if curi == 1 { if bst != 2 { if bst != 4 { if pd + pdz2 < 6*256 {
3945 var twd: i64 = 0 // -1 retreat, 0 hold, +1 approach
3946 var stopd: i64 = 400
3947 var spd: i64 = 3
3948 if bold <= 1 { twd = 0 - 1; stopd = 5*256; spd = 5 }
3949 if bold >= 2 { if bold <= 3 {
3950 twd = 0
3951 if pd + pdz2 < 2*256 { twd = 0 - 1; stopd = 2*256; spd = 4 }
3952 } }
3953 if bold >= 4 { if bold <= 5 { twd = 1; stopd = 400; spd = 3 } }
3954 if bold >= 6 { twd = 1; stopd = 200; spd = 5 }
3955 if bst == 3 { if twd != 0 { spd = 2 } } // gathering: the drift is subtle
3956 if twd == 1 { if pd + pdz2 > stopd {
3957 // S1 F-FORMATION SLOT (the social-interaction brief, rung S1): her approach
3958 // target is HER slot on the ring around the player, never the player center
3959 // -- N approaching girls used to converge on one point and bunch into a
3960 // queue ("they just follow me around"). Slot angle = a stable hash of her
3961 // handle; radius = her OWN personality stop distance, so bold girls ring
3962 // close and warm girls ring at arm's length (proxemics stays personal).
3963 let sa9: i64 = (h*1741) % (2*IT_PI)
3964 let tx9: i64 = s[S_CX] + it_cos4096(sa9)*stopd/WATER_MAGIC_4096
3965 let tz9: i64 = s[S_CZ] + it_sin4096(sa9)*stopd/WATER_MAGIC_4096
3966 let sdx9: i64 = tx9 - x
3967 let sdz9: i64 = tz9 - z
3968 dx = 0
3969 if sdx9 > 128 { dx = spd }
3970 if sdx9 < 0 - 128 { dx = 0 - spd }
3971 dz = 0
3972 if sdz9 > 128 { dz = spd }
3973 if sdz9 < 0 - 128 { dz = 0 - spd }
3974 } }
3975 if twd == 1 { if pd + pdz2 <= stopd { if bst == 0 { dx = 0; dz = 0; ph = ph - 4 } } }
3976 if twd == 0 - 1 { if pd + pdz2 < stopd {
3977 dx = spd
3978 if pdx > 0 { dx = 0 - spd }
3979 dz = spd
3980 if pdz > 0 { dz = 0 - spd }
3981 } }
3982 if twd == 0 { if bst == 0 { dx = 0; dz = 0; ph = ph - 4 } } // the watcher stands
3983 } } } }
3984 // RELATIONSHIP (the FLIAW arc): shared time within arm's reach becomes FRIENDSHIP,
3985 // stored in MC_DISG bits 6-13 (cap 250) -- persisted with the mob arena, so a bond
3986 // survives every save/load. Meeting her (E) adds a step-change on top.
3987 if pd + pdz2 < 3*256 { if (s[S_T] + h) % 64 == 0 {
3988 let dgf: i64 = en_get(m, h, MC_DISG)
3989 if ((dgf >> 6) & 255) < 250 { en_set(m, h, MC_DISG, dgf + 64) }
3990 } }
3991 // GAIT: TURN THE BODY TOWARD WHERE SHE IS GOING, RATE-LIMITED.
3992 // Placed HERE, on the INTENDED step, deliberately: (a) before the clearance clamp, so
3993 // a push out of a neighbouring solid can never feed the heading a direction she did
3994 // not choose -- the clamp exists to stop her entering a wall, not to steer her; and
3995 // (b) on the intent rather than the applied step, so a girl pressed against a wall
3996 // still turns away from it instead of standing frozen with a zero step and therefore
3997 // a zero turn allowance. The low-pass in wc_hdg_step is what makes this smooth; the
3998 // bang-bang steering above is left exactly as it was and is simply no longer read as
3999 // a facing.
4000 let hdgp: *i64 = (base + O_MOBHDG) as *i64
4001 hdgp[k] = wc_hdg_step(hdgp[k], dx, dz)
4002 let nx: i64 = x + dx
4003 let nz: i64 = z + dz
4004 let cy0: i64 = en_get(m, h, MC_Y)
4005 let g: i64 = mob_ground(base, nx >> 8, (cy0 >> 8) + 1, nz >> 8)
4006 var ok: i64 = 0
4007 if g >= 1 { if g < WY - 4 {
4008 let ng: i64 = (g + 1)*256
4009 var dyy: i64 = ng - cy0
4010 if dyy < 0 { dyy = 0 - dyy }
4011 if dyy <= 384 { if mob_fits(base, nx >> 8, g, nz >> 8) == 1 {
4012 ok = 1
4013 // GE12b: clamp her body out of any neighbouring solid, then RE-DERIVE the
4014 // applied step from the clamped result -- the motion doors and the gait
4015 // accumulator must report what actually happened, so a girl pinned against
4016 // a wall accumulates no stride (she is not walking; she is leaning).
4017 let cxq: i64 = mob_clear_x(base, nx, nz, g)
4018 let czq: i64 = mob_clear_z(base, nx, nz, g)
4019 dx = cxq - x
4020 dz = czq - z
4021 en_set(m, h, MC_X, cxq)
4022 en_set(m, h, MC_Z, czq)
4023 // STEP, don't teleport -- and DROP, don't glide (GE12): the vertical is
4024 // integrated by mob_step_gait (stair-ascent rate up, the player's own
4025 // gravity down), so a rise reads as a step and a drop accelerates.
4026 en_set(m, h, MC_Y, mob_step_gait(vyp, k, cy0, ng))
4027 } }
4028 } }
4029 let aux: *i64 = (base + O_MOBAUX) as *i64
4030 if ok == 1 { aux[k*3] = dx; aux[k*3 + 1] = dz; aux[k*3 + 2] = en_get(m, h, MC_Y) - cy0 }
4031 if ok == 0 {
4032 // wall/cliff: turn away -- but GE12 never leaves her hanging: if her own column's
4033 // stand height is below her (a block broke under her feet), she keeps falling.
4034 let g0: i64 = mob_ground(base, x >> 8, (cy0 >> 8) + 1, z >> 8)
4035 var dy0: i64 = 0
4036 if g0 >= 1 { if (g0 + 1)*256 < cy0 {
4037 let ny0: i64 = mob_step_gait(vyp, k, cy0, (g0 + 1)*256)
4038 en_set(m, h, MC_Y, ny0)
4039 dy0 = ny0 - cy0
4040 } }
4041 aux[k*3] = 0; aux[k*3 + 1] = 0; aux[k*3 + 2] = dy0; ph = ph + 977
4042 }
4043 // PATH LENGTH for the gait drive -- Euclidean, and deliberately NOT the Manhattan proxy
4044 // mob_spd_q8i uses: that one is a calibrated blend THRESHOLD, this is a metric integral.
4045 // Conflating them would make a diagonal walk bob up to 41 percent fast, and would also
4046 // move a threshold that was measured against the proxy. Two named quantities, one job each.
4047 let ds9: i64 = sd_isqrt(dx*dx + dz*dz)
4048 let ga9: i64 = nd[nb + WC_NEED_GAIT]
4049 nd[nb + WC_NEED_GAIT] = ga9 + ds9
4050 // FOOTSTRIKE IMPULSE (sb_impulse_mob's FIRST world wiring -- gate-proven in
4051 // nx_softbind_gate and called by NO engine until now, so heel strike put NOTHING
4052 // into the tissue: the sponsored footfall row). Two ground-contact events kick
4053 // every spring point of this girl, both magnitudes DERIVED, converted into
4054 // sd_impulse's own units by WC_IMP_SCALE:
4055 // (1) HEEL STRIKE -- the stride-phase machinery places foot plant at the
4056 // leg-swing extremes sph = IT_PI/2 and 3*IT_PI/2 (wc_gp_leg is sin(sph);
4057 // its extremes are where the leading leg lands and double support begins).
4058 // In accumulated-path units those are ga == WC_STRIDE_Q8/4 modulo
4059 // WC_STRIDE_Q8/2, so (ga + WC_STRIDE_Q8/4)/(WC_STRIDE_Q8/2) counts foot
4060 // plants and an increment across this tick's travel IS one. Magnitude: each
4061 // step the body falls the gait bob's own declared peak-to-peak
4062 // (2*WC_GAIT_BOB_Q8) onto the leading leg and contact arrests it --
4063 // ballistic contact speed isqrt(2*GRAV * 2*WC_GAIT_BOB_Q8) = 15
4064 // world-q8/tick: the engine's own gravity through the engine's own declared
4065 // vertical excursion, no free parameter. (GRAV is the engine's stylized
4066 // gravity, so this inherits the same stylization every drop landing already
4067 // has -- consistent BY CONSTRUCTION, not calibrated to real-world g.)
4068 // (2) DROP LANDING -- GE12's integrator zeroed a real fall this tick; the
4069 // arrested |vy_pre| IS the ground-reaction delta-v, bounded by VTERM.
4070 // Sign: +y, the recoil-inheritance convention every softbind gate kick uses.
4071 // A standing girl accumulates no path and keeps vy == 0, so she is kicked by
4072 // nothing -- rest stays rest (the sd_deadsnap law undisturbed).
4073 if (ga9 + ds9 + WC_STRIDE_Q8/4)/(WC_STRIDE_Q8/2) != (ga9 + WC_STRIDE_Q8/4)/(WC_STRIDE_Q8/2) {
4074 sb_impulse_mob((base + O_SOFT) as *i64, k, 0, sd_isqrt(2*GRAV*2*WC_GAIT_BOB_Q8)*WC_IMP_SCALE, 0)
4075 }
4076 if vy_pre < 0 { if vyp[k] == 0 {
4077 sb_impulse_mob((base + O_SOFT) as *i64, k, 0, (0 - vy_pre)*WC_IMP_SCALE, 0)
4078 } }
4079 en_set(m, h, MC_PH, ph)
4080 // A4: the soft-body springs tick from ROOT TRUTH + HER GAIT: the anchor carries a
4081 // cadence-coupled thorax bob (published gait biomechanics -- vertical thorax
4082 // oscillation is what drives chest motion; a flat anchor was why nothing moved),
4083 // and the chest band is BRED (genome bits 16-17: firm rides tight, soft swings).
4084 // ★THE SOLVER AND THE INSTRUMENT MUST SHARE ONE REFERENCE (fixed 2026-08-03).
4085 // This tick used to drive the spring toward a RAW gait sinusoid while sbyb and every
4086 // probe subtracted mob_anchor_q8 -- the gait<->breath crossfade. Their difference rode
4087 // the rendered offset as a spurious term, and because ph advances +5 EVERY TICK FOREVER
4088 // the raw bob never quiets, so a STANDING girl still bounced: the 2026-08-02 breath fix
4089 // landed in the INSTRUMENT path and never reached the PHYSICS path. (The comment that
4090 // stood here claimed the bob 'quiets when she stands'. It does not. A comment asserting
4091 // a behaviour the code does not have is a landmine, so it is deleted with the defect.)
4092 // Driving the solver from the SAME anchor the instrument reads makes rest-bouncing
4093 // impossible BY CONSTRUCTION rather than by tuning. At walking speed the crossfade
4094 // weight saturates and the anchor EQUALS the old raw bob exactly => no-op in motion,
4095 // fix at rest: the safest shape a drive change can have.
4096 // ⚠STILL OPEN (debt 1785770635): ph is a FREE-RUNNING clock, so the drive is a fixed
4097 // 2.50Hz regardless of her real cadence while the render clip is DISTANCE-synced. Until
4098 // ph advances from travel, foot plant and tissue drive remain uncorrelated -- which is
4099 // the operator's actual report and is NOT closed by this change.
4100 sb_tick_mob_gene((base + O_SOFT) as *i64, k,
4101 en_get(m, h, MC_X)/4, (en_get(m, h, MC_Y) + mob_anchor_q8i(base, k))/4, en_get(m, h, MC_Z)/4,
4102 en_get(m, h, MC_GENE))
4103 }
4104 k = k + 1
4105 }
4106 // THE STAKES (losability doctrine: a find that cannot be lost is hollow): WARDENS -- real
4107 // demonkin -- hunt FOUND humans. Reached = TAKEN (the certified store's destroy, live in
4108 // production). The player's BODY blocks a warden within 2 blocks. One take per tick, max.
4109 var kw: i64 = 0
4110 var took: i64 = 0
4111 while kw < en_count(m) {
4112 if took == 0 {
4113 let hw: i64 = en_nth(m, kw)
4114 if en_get(m, hw, MC_KIND) == 2 { if (en_get(m, hw, MC_DISG) & 1) == 0 {
4115 var bestj: i64 = 0 - 1
4116 // the world declares its danger (P_WARD), night widens it, and the ARRIVING
4117 // TRAVELER'S LEVEL widens it further: a veteran isekai's stakes rise with him
4118 var wbase: i64 = wsp(base)[P_WARD]
4119 if wbase <= 0 { wbase = 12 }
4120 var bestd: i64 = wbase*256 + pass_level_of(base)*128
4121 if night == 1 { bestd = bestd + 8*256 } // the night belongs to the wardens
4122 var kj: i64 = 0
4123 while kj < en_count(m) {
4124 let hj: i64 = en_nth(m, kj)
4125 if (en_get(m, hj, MC_DISG) & 3) == 3 {
4126 var ddx2: i64 = en_get(m, hj, MC_X) - en_get(m, hw, MC_X)
4127 if ddx2 < 0 { ddx2 = 0 - ddx2 }
4128 var ddz2: i64 = en_get(m, hj, MC_Z) - en_get(m, hw, MC_Z)
4129 if ddz2 < 0 { ddz2 = 0 - ddz2 }
4130 if ddx2 + ddz2 < bestd { bestd = ddx2 + ddz2; bestj = kj }
4131 }
4132 kj = kj + 1
4133 }
4134 if bestj >= 0 {
4135 var pdx3: i64 = s[S_CX] - en_get(m, hw, MC_X)
4136 if pdx3 < 0 { pdx3 = 0 - pdx3 }
4137 var pdz3: i64 = s[S_CZ] - en_get(m, hw, MC_Z)
4138 if pdz3 < 0 { pdz3 = 0 - pdz3 }
4139 if pdx3 + pdz3 > 2*256 { // your presence stays her hand
4140 let hj2: i64 = en_nth(m, bestj)
4141 if bestd < 220 {
4142 // C3/C5: a TAKING is WITNESSED -- every girl within 16 blocks
4143 // carries the grief as a thought and the fact as gossip (source =
4144 // her own eyes, conf 100). The village REMEMBERS its losses.
4145 let tkx: i64 = en_get(m, hj2, MC_X)
4146 let tkz: i64 = en_get(m, hj2, MC_Z)
4147 var tkk: i64 = 0 - 1
4148 var kwi: i64 = 0
4149 while kwi < en_count(m) {
4150 if en_nth(m, kwi) == hj2 { tkk = kwi }
4151 kwi = kwi + 1
4152 }
4153 en_destroy(m, hj2)
4154 s[S_LOST] = s[S_LOST] + 1
4155 var kwj: i64 = 0
4156 while kwj < en_count(m) {
4157 let hwj: i64 = en_nth(m, kwj)
4158 if hwj > 0 {
4159 var wdx: i64 = en_get(m, hwj, MC_X) - tkx
4160 if wdx < 0 { wdx = 0 - wdx }
4161 var wdz: i64 = en_get(m, hwj, MC_Z) - tkz
4162 if wdz < 0 { wdz = 0 - wdz }
4163 if wdx + wdz < 16*256 {
4164 th_add(base, kwj, TH_SAWTAKEN, 0 - 25, 14400)
4165 if tkk >= 0 { go_add(base, kwj, tkk, GO_TAKEN, 100) }
4166 }
4167 }
4168 kwj = kwj + 1
4169 }
4170 took = 1
4171 }
4172 if bestd >= 220 { if took == 0 {
4173 let txq: i64 = en_get(m, hj2, MC_X)
4174 let tzq: i64 = en_get(m, hj2, MC_Z)
4175 var wv: i64 = 4
4176 if night == 1 { wv = 7 } // she closes faster in the dark
4177 var mx3: i64 = 0
4178 if txq > en_get(m, hw, MC_X) + 32 { mx3 = wv }
4179 if txq < en_get(m, hw, MC_X) - 32 { mx3 = 0 - wv }
4180 var mz3: i64 = 0
4181 if tzq > en_get(m, hw, MC_Z) + 32 { mz3 = wv }
4182 if tzq < en_get(m, hw, MC_Z) - 32 { mz3 = 0 - wv }
4183 en_set(m, hw, MC_X, en_get(m, hw, MC_X) + mx3)
4184 en_set(m, hw, MC_Z, en_get(m, hw, MC_Z) + mz3)
4185 } }
4186 }
4187 }
4188 } }
4189 }
4190 kw = kw + 1
4191 }
4192 // INHERITANCE (the past-DAZ lever, live): two MET girls keeping close company bear a
4193 // DAUGHTER -- her genome FIELD-CROSSED from both parents (the breeders gcross pattern),
4194 // spawned through the certified store (capacity-bounded), cooldown-paced. Display cap 12.
4195 if s[S_BCD] > 0 { s[S_BCD] = s[S_BCD] - 1 }
4196 if s[S_BCD] == 0 { if en_count(m) < MOB_CAP {
4197 var ka: i64 = 0
4198 var born: i64 = 0
4199 while ka < en_count(m) {
4200 if born == 0 {
4201 let ha: i64 = en_nth(m, ka)
4202 if (en_get(m, ha, MC_DISG) & 2) == 2 {
4203 var kb2: i64 = ka + 1
4204 while kb2 < en_count(m) {
4205 if born == 0 {
4206 let hb2: i64 = en_nth(m, kb2)
4207 if (en_get(m, hb2, MC_DISG) & 2) == 2 {
4208 var bdx4: i64 = en_get(m, ha, MC_X) - en_get(m, hb2, MC_X)
4209 if bdx4 < 0 { bdx4 = 0 - bdx4 }
4210 var bdz4: i64 = en_get(m, ha, MC_Z) - en_get(m, hb2, MC_Z)
4211 if bdz4 < 0 { bdz4 = 0 - bdz4 }
4212 if bdx4 + bdz4 < 400 {
4213 let hn: i64 = en_spawn(m)
4214 if hn > 0 {
4215 let ga: i64 = en_get(m, ha, MC_GENE)
4216 let gb: i64 = en_get(m, hb2, MC_GENE)
4217 let pick: i64 = whash(ga, gb, s[S_T])
4218 var gn: i64 = 0
4219 var fm: i64 = 0
4220 while fm < 8 {
4221 var msk: i64 = 7
4222 if fm == 1 { msk = 24 }
4223 if fm == 2 { msk = 96 }
4224 if fm == 3 { msk = 384 }
4225 if fm == 4 { msk = WATER_MAGIC_1536 }
4226 if fm == 5 { msk = WATER_MAGIC_6144 }
4227 if fm == 6 { msk = GENE_MASK_TEMPERAMENT } // bits 13-15: TEMPERAMENT (bold<->shy) is bred too
4228 if fm == 7 { msk = 983040 } // bits 16-19: BODY NATURE (firmness 16-17 +
4229 // beachwear nature 18-19) bred as one field --
4230 // un-crossed genome bits default every daughter
4231 // to one body (the class this row eats); pick
4232 // has only 8 hash bits, so 16-19 share fm 7
4233 if ((pick >> fm) & 1) == 1 { gn = gn | (ga & msk) }
4234 if ((pick >> fm) & 1) == 0 { gn = gn | (gb & msk) }
4235 fm = fm + 1
4236 }
4237 en_set(m, hn, MC_GENE, gn)
4238 en_set(m, hn, MC_X, (en_get(m, ha, MC_X) + en_get(m, hb2, MC_X))/2)
4239 en_set(m, hn, MC_Y, en_get(m, ha, MC_Y))
4240 en_set(m, hn, MC_Z, (en_get(m, ha, MC_Z) + en_get(m, hb2, MC_Z))/2)
4241 var kk: i64 = en_get(m, ha, MC_KIND)
4242 if ((pick >> 6) & 1) == 1 { kk = en_get(m, hb2, MC_KIND) }
4243 en_set(m, hn, MC_KIND, kk)
4244 let da: i64 = en_get(m, ha, MC_DISG) & 1
4245 let db: i64 = en_get(m, hb2, MC_DISG) & 1
4246 var dn: i64 = 0
4247 if da == 1 { if db == 1 { dn = 1 } }
4248 if da != db { dn = (pick >> 7) & 1 }
4249 en_set(m, hn, MC_DISG, dn)
4250 en_set(m, hn, MC_PH, whash(gn, pick, s[S_T]) % WATER_MAGIC_4096)
4251 // seat the newborn's springs + needs (dense tail slot)
4252 let nk9: i64 = en_count(m) - 1
4253 sb_seat_mob((base + O_SOFT) as *i64, nk9,
4254 en_get(m, hn, MC_X)/4, en_get(m, hn, MC_Y)/4, en_get(m, hn, MC_Z)/4)
4255 let nd9: *i64 = (base + O_NEED) as *i64
4256 nd9[nk9*4] = 400
4257 nd9[nk9*4 + 1] = 800
4258 nd9[nk9*4 + 2] = 500
4259 nd9[nk9*4 + 3] = 0
4260 s[S_BCD] = 900
4261 born = 1
4262 }
4263 }
4264 }
4265 }
4266 kb2 = kb2 + 1
4267 }
4268 }
4269 }
4270 ka = ka + 1
4271 }
4272 } }
4273 return 0
4274}
4275
4276// the full FPS control core: fwd/strafe movement, keyboard turn + analog mouselook, keyboard pitch,
4277// jump with real gravity (auto-step <=1.5 blocks stays; 2+ needs a jump), then targeting + edits.
4278func apply2_impl(base: i64, fwd: i64, strafe: i64, turn: i64,
4279 lookx: i64, looky: i64, pit: i64, jump: i64, brk: i64, plc: i64) -> i64 {
4280 let s: *i64 = wst(base)
4281 let dp59: i64 = wsp(base)[P_DAYPHASE]
4282 if dp59 > 0 { if s[S_DPAPPLIED] != dp59 { s[S_T] = dp59; s[S_DPAPPLIED] = dp59 } } // GE59: a poked day phase re-bases the clock ONCE per distinct value (0 = the DAYLEN/4 boot phase, untouched)
4283 s[S_T] = s[S_T] + 1
4284 s[S_YAW] = s[S_YAW] + turn*TURN + lookx*LOOKS
4285 if s[S_YAW] < 0 { s[S_YAW] = s[S_YAW] + 2*IT_PI }
4286 if s[S_YAW] >= 2*IT_PI { s[S_YAW] = s[S_YAW] - 2*IT_PI }
4287 s[S_PITCH] = wclamp(s[S_PITCH] + pit*PITCHV - looky*LOOKS, 0 - PITCH_MAX, PITCH_MAX)
4288 let sy: i64 = it_sin4096(s[S_YAW])
4289 let cy2: i64 = it_cos4096(s[S_YAW])
4290 // horizontal: move vector = fwd*forward + strafe*right, axis-split against walls
4291 let mx: i64 = (sy*fwd + cy2*strafe)*SPEED/WATER_MAGIC_4096
4292 let mz: i64 = (cy2*fwd - sy*strafe)*SPEED/WATER_MAGIC_4096
4293 if mx != 0 {
4294 let nx: i64 = s[S_CX] + mx
4295 if wstand(base, nx, s[S_CZ], s[S_CY]) >= 0 { s[S_CX] = nx }
4296 }
4297 if mz != 0 {
4298 let nz: i64 = s[S_CZ] + mz
4299 if wstand(base, s[S_CX], nz, s[S_CY]) >= 0 { s[S_CZ] = nz }
4300 }
4301 // vertical: jump + gravity against the current column's stand height (replaces the glide)
4302 var vy: i64 = s[S_VY]
4303 if jump == 1 { if s[S_GROUNDED] == 1 { vy = JUMPV } }
4304 vy = vy - GRAV
4305 if vy < 0 - VTERM { vy = 0 - VTERM } // terminal velocity (VTERM is shared with mob_step_gait)
4306 var ny: i64 = s[S_CY] + vy
4307 if vy > 0 { // ceiling while rising
4308 let hc: i64 = (ny + 128) >> 8
4309 if vsolid(base, s[S_CX] >> 8, hc, s[S_CZ] >> 8) == 1 { ny = s[S_CY]; vy = 0 }
4310 }
4311 let stand: i64 = wstand_here(base, s[S_CX], s[S_CZ])
4312 s[S_GROUNDED] = 0
4313 if ny <= stand { ny = stand; vy = 0; s[S_GROUNDED] = 1 }
4314 s[S_VY] = vy
4315 s[S_CY] = ny
4316 // void-fall rescue: below the world floor (edge slip despite the wall) -> respawn, never vanish
4317 if ny < 0 - 512 { wc_respawn(base) }
4318 if wground(base, s[S_CX] >> 8, s[S_CZ] >> 8) < 0 { wc_respawn(base) }
4319 // crosshair target: one ray straight along the view
4320 let sp: i64 = it_sin4096(s[S_PITCH])
4321 let cp: i64 = it_cos4096(s[S_PITCH])
4322 let fx: i64 = sy*cp/WATER_MAGIC_4096
4323 let fy: i64 = sp
4324 let fz: i64 = cy2*cp/WATER_MAGIC_4096
4325 let hout: *i64 = (base + O_ST + 40*8) as *i64 // scratch slots 40-44 (wray writes FIVE outs;
4326 let hs: i64 = wray(base, fx*FOC, fy*FOC, fz*FOC, REACH_STEPS, hout) // 56+ overflowed into O_INPUT once)
4327 s[S_NPCT] = npc_pick(base, fx, fy, fz)
4328 // BODY PRESENCE: push the player out of any girl's personal cylinder (no ghost-walking)
4329 let mB: *i64 = mobp(base)
4330 var kB: i64 = 0
4331 while kB < en_count(mB) {
4332 let hB: i64 = en_nth(mB, kB)
4333 let bdx: i64 = s[S_CX] - en_get(mB, hB, MC_X)
4334 let bdz: i64 = s[S_CZ] - en_get(mB, hB, MC_Z)
4335 var adx: i64 = bdx
4336 if adx < 0 { adx = 0 - adx }
4337 var adz: i64 = bdz
4338 if adz < 0 { adz = 0 - adz }
4339 // SAME-ALTITUDE ONLY: a girl in a cave below (or ledge above) must NOT wall you off up
4340 // here -- the "invisible walls" bug was cylinders with no vertical extent
4341 var ady: i64 = s[S_CY] - EYE - en_get(mB, hB, MC_Y)
4342 if ady < 0 { ady = 0 - ady }
4343 if ady < 460 { if adx < 100 { if adz < 100 {
4344 if adx > adz {
4345 if bdx > 0 { s[S_CX] = en_get(mB, hB, MC_X) + 100 }
4346 if bdx <= 0 { s[S_CX] = en_get(mB, hB, MC_X) - 100 }
4347 }
4348 if adx <= adz {
4349 if bdz > 0 { s[S_CZ] = en_get(mB, hB, MC_Z) + 100 }
4350 if bdz <= 0 { s[S_CZ] = en_get(mB, hB, MC_Z) - 100 }
4351 }
4352 } } }
4353 kB = kB + 1
4354 }
4355 s[S_HOK] = 0
4356 if hs > 0 {
4357 s[S_HX] = hout[0]
4358 s[S_HY] = hout[1]
4359 s[S_HZ] = hout[2]
4360 s[S_HFACE] = hout[3]
4361 s[S_HOK] = 1
4362 }
4363 // THE INTERACT VERB: E on a targeted girl MEETS her (block-breaking is suppressed -- you are
4364 // talking to a person, not mining through her). Meeting marks her met-bit; a disguised human,
4365 // once met, is FOUND and counted (the identify-and-protect loop of the lore).
4366 var dobrk: i64 = brk
4367 if brk == 1 { if s[S_NPCT] >= 0 {
4368 dobrk = 0
4369 let mV: *i64 = mobp(base)
4370 let hV: i64 = en_nth(mV, s[S_NPCT])
4371 let dv: i64 = en_get(mV, hV, MC_DISG)
4372 if (dv & 2) == 0 {
4373 var dvn: i64 = dv | 2
4374 if ((dvn >> 6) & 255) <= 240 { dvn = dvn + 640 } // meeting her: +10 friendship
4375 en_set(mV, hV, MC_DISG, dvn)
4376 if (dv & 1) == 1 { s[S_FOUND] = s[S_FOUND] + 1 }
4377 // C3/C5: the meeting lands in HER ledger, and the fact of it enters the village's
4378 // gossip graph with herself as subject -- her story spreads without another line
4379 th_add(base, s[S_NPCT], TH_MET, 15, 14400)
4380 go_add(base, s[S_NPCT], s[S_NPCT], GO_FRIEND, 100)
4381 }
4382 } }
4383 if dobrk == 1 { if s[S_HOK] == 1 { if s[S_HY] > 0 {
4384 let bwas: i64 = vget(base, s[S_HX], s[S_HY], s[S_HZ])
4385 if wc_edit(base, s[S_HX], s[S_HY], s[S_HZ], 0) == 1 {
4386 if bwas == B_CROP2 { s[S_HARV] = s[S_HARV] + 1 } // the HARVEST: the loop pays out
4387 s[S_LBX] = s[S_HX]
4388 s[S_LBY] = s[S_HY]
4389 s[S_LBZ] = s[S_HZ]
4390 s[S_BROKEN] = s[S_BROKEN] + 1
4391 s[S_HOK] = 0
4392 }
4393 } } }
4394 if plc == 1 { if s[S_HOK] == 1 {
4395 let want: i64 = sel_block(s[S_SEL])
4396 if want == B_FARM { if s[S_HFACE] == 3 {
4397 // the HOE: tilling converts the TARGETED grass/dirt block itself -- no new block
4398 let tb: i64 = vget(base, s[S_HX], s[S_HY], s[S_HZ])
4399 if tb == 1 { if wc_edit(base, s[S_HX], s[S_HY], s[S_HZ], B_FARM) == 1 { s[S_PLACED] = s[S_PLACED] + 1 } }
4400 if tb == 2 { if wc_edit(base, s[S_HX], s[S_HY], s[S_HZ], B_FARM) == 1 { s[S_PLACED] = s[S_PLACED] + 1 } }
4401 } }
4402 if want == B_CROP0 { if s[S_HFACE] == 3 { if s[S_HY] + 1 < WY {
4403 // SEEDS: only into the air directly above targeted farmland (wet or dry)
4404 let sb2: i64 = vget(base, s[S_HX], s[S_HY], s[S_HZ])
4405 var okf: i64 = 0
4406 if sb2 == B_FARM { okf = 1 }
4407 if sb2 == B_FARMW { okf = 1 }
4408 if okf == 1 { if vget(base, s[S_HX], s[S_HY] + 1, s[S_HZ]) == 0 {
4409 if wc_edit(base, s[S_HX], s[S_HY] + 1, s[S_HZ], B_CROP0) == 1 {
4410 s[S_PLANT] = s[S_PLANT] + 1
4411 s[S_PLACED] = s[S_PLACED] + 1
4412 }
4413 } }
4414 } } }
4415 if want != B_FARM { if want != B_CROP0 {
4416 var px: i64 = s[S_HX]
4417 var py: i64 = s[S_HY]
4418 var pz: i64 = s[S_HZ]
4419 let f: i64 = s[S_HFACE]
4420 if f == 1 { px = px + 1 }
4421 if f == 2 { px = px - 1 }
4422 if f == 3 { py = py + 1 }
4423 if f == 4 { py = py - 1 }
4424 if f == 5 { pz = pz + 1 }
4425 if f == 6 { pz = pz - 1 }
4426 var okp: i64 = 0
4427 if vin(px, py, pz) == 1 { if vget(base, px, py, pz) == 0 { okp = 1 } }
4428 // never inside the player's own two cells
4429 if px == s[S_CX] >> 8 { if pz == s[S_CZ] >> 8 {
4430 let feet: i64 = (s[S_CY] - EYE) >> 8
4431 if py >= feet { if py <= feet + 1 { okp = 0 } }
4432 } }
4433 if okp == 1 {
4434 if wc_edit(base, px, py, pz, want) == 1 {
4435 s[S_PLACED] = s[S_PLACED] + 1
4436 if py > s[S_WMAX] { s[S_WMAX] = py } // a tower can rise past the old ceiling
4437 }
4438 }
4439 } }
4440 } }
4441 mob_tick(base)
4442 // B1/B2: the farm breathes on its own clock (soil hydration + crop growth, journal-driven).
4443 // P_GROWE <= 0 = a recombined identity with no farming: the clock simply never fires.
4444 let ge9: i64 = wsp(base)[P_GROWE]
4445 if ge9 >= 1 { if s[S_T] % ge9 == 0 { wc_farm_tick(base) } }
4446 // v6 STREAMING: approach a window margin -> the world slides under you (unbounded land).
4447 // Runs after the whole frame so every in-frame coordinate was consistent; one stride per
4448 // axis per tick is plenty at player speeds.
4449 if (s[S_CX] >> 8) >= WX - SHIFT_MARGIN { wc_shift(base, SHIFT_STRIDE, 0) }
4450 if (s[S_CX] >> 8) < SHIFT_MARGIN { wc_shift(base, 0 - SHIFT_STRIDE, 0) }
4451 if (s[S_CZ] >> 8) >= WZ - SHIFT_MARGIN { wc_shift(base, 0, SHIFT_STRIDE) }
4452 if (s[S_CZ] >> 8) < SHIFT_MARGIN { wc_shift(base, 0, 0 - SHIFT_STRIDE) }
4453 return 0
4454}
4455
4456func wc_cycle(base: i64) -> i64 {
4457 let s: *i64 = wst(base)
4458 s[S_SEL] = (s[S_SEL] + 1) % 7
4459 return s[S_SEL]
4460}
4461
4462// adaptive quality: the SURFACE reports how long its last frame took (transport only); the ORGAN
4463// owns the response. EMA x16 with hysteresis bands so it never flaps: sustained >24ms coarsens,
4464// sustained <11ms refines. Returns the current ray pixel size.
4465func wc_framems(base: i64, ms: i64) -> i64 {
4466 let s: *i64 = wst(base)
4467 var m2: i64 = ms
4468 if m2 < 0 { m2 = 0 }
4469 if m2 > 100 { m2 = 100 }
4470 s[S_FMA] = (s[S_FMA]*3 + m2*16)/4
4471 // ★EVERY LINE DERIVES FROM ONE BUDGET, AND THE BUDGET COMES FROM THE DEVICE when the page has
4472 // reported its measured display interval (target_ms): tolerate = 1.5x the interval, refine line =
4473 // tolerate minus one-sixth (the proven no-touch margin as a RATIO), recovery seed = two-thirds of
4474 // tolerate. With no report the shipped values hold BIT-EXACTLY (384/320/256 -- 24ms tolerate),
4475 // so gate T20 and every headless run measure the same controller they always did.
4476 var tol: i64 = 384
4477 if s[S_TGT] > 0 { tol = s[S_TGT]*3/2 }
4478 let refl: i64 = tol - tol/6
4479 let seed: i64 = tol*2/3
4480 var coarsened: i64 = 0
4481 if s[S_FMA] > tol { if s[S_Q] < Q_MAX { // sustained past the tolerate line
4482 s[S_Q] = s[S_Q] + 1
4483 s[S_FMA] = seed
4484 coarsened = 1
4485 } }
4486 // PREDICTIVE REFINE (operator 2026-08-17: "incredibly pixelated ... we have a 5080"). The old
4487 // rule refined only under a fixed 11ms line while coarsening at 24ms, so any machine whose frame
4488 // cost sat between them stayed pixelated forever at 41 percent of its own budget -- a magic
4489 // threshold under-using every device. The decision now uses the renderer's OWN cost model: rays
4490 // scale as 1/q^2, so the predicted EMA at the finer q is FMA*q^2/(q-1)^2. Refine when that fits
4491 // UNDER the coarsen line minus one 4ms hysteresis margin (320 = 384-64) -- the two bands cannot
4492 // touch, so it cannot oscillate; and the EMA is SEEDED with the prediction (a reset to 256 would
4493 // re-trigger blind and skip quality levels before a real frame lands). FMA>0 guards the first
4494 // frames: no refine before any real report has been folded in.
4495 // ⚠AND NEVER ON THE CALL THAT COARSENED: the coarsen branch's FMA=256 reset is a SEEDED value,
4496 // not a measurement, and at q>3 the ratio test on that seed always says "you can afford finer" --
4497 // gate T20's slow case caught the same-call refine undoing every coarsen step (2/3 RED). One real
4498 // frame must fold into the EMA before refine may judge again.
4499 if coarsened == 0 { if s[S_Q] > 1 { if s[S_FMA] > 0 {
4500 let qc: i64 = s[S_Q]
4501 let qn: i64 = qc - 1
4502 if s[S_FMA]*qc*qc < refl*qn*qn {
4503 s[S_Q] = qn
4504 s[S_FMA] = s[S_FMA]*qc*qc/(qn*qn)
4505 }
4506 } } }
4507 return s[S_Q]
4508}
4509
4510// ---------- persistence: a sovereign save image (the nx_gamesave laws, wasm-compatible) ----------
4511// [0]=magic [1]=version [2]=seed [3..7]=camx/y/z/yaw/pitch [8]=sel [9]=broken [10]=placed
4512// [11]=checksum (order-sensitive rolling fold, 63-bit masked) then the raw voxel bytes.
4513// The surface (file on native, localStorage in the browser) moves BYTES ONLY; meaning stays here.
4514// seq1151 (2026-07-30): the rolling-fold mechanism now composes nx_gamesave_core -- ONE definition
4515// shared with the gs_save file surface; this format's constants stay ITS OWN (image bytes unchanged,
4516// golden gate checksums are the acceptance).
4517func wc_save_ck(base: i64) -> i64 {
4518 let hd: *i64 = (base + O_SAVE) as *i64
4519 let vb: *u8 = (base + O_SAVE + SAVE_HDR) as *u8
4520 var ck: i64 = gsc_fold_words(SAVE_CK_SEED, hd, 2, 11, SAVE_CK_PRIME, SAVE_CK_MASK)
4521 ck = gsc_fold(ck, hd[12], SAVE_CK_PRIME, SAVE_CK_MASK)
4522 ck = gsc_fold(ck, hd[13], SAVE_CK_PRIME, SAVE_CK_MASK)
4523 ck = gsc_fold(ck, hd[14], SAVE_CK_PRIME, SAVE_CK_MASK)
4524 ck = gsc_fold(ck, hd[15], SAVE_CK_PRIME, SAVE_CK_MASK)
4525 ck = gsc_fold_bytes(ck, vb, SAVE_BODY, 131, SAVE_CK_MASK)
4526 return ck
4527}
4528// pre-streaming (v5) checksum geometry -- LOAD-COMPAT ONLY, so old saves keep opening
4529func wc_save_ck_v5(base: i64) -> i64 {
4530 let hd: *i64 = (base + O_SAVE) as *i64
4531 let vb: *u8 = (base + O_SAVE + SAVE_HDR) as *u8
4532 var ck: i64 = gsc_fold_words(SAVE_CK_SEED, hd, 2, 11, SAVE_CK_PRIME, SAVE_CK_MASK)
4533 ck = gsc_fold(ck, hd[12], SAVE_CK_PRIME, SAVE_CK_MASK)
4534 ck = gsc_fold(ck, hd[13], SAVE_CK_PRIME, SAVE_CK_MASK)
4535 ck = gsc_fold_bytes(ck, vb, SAVE_BODY_V5, 131, SAVE_CK_MASK)
4536 return ck
4537}
4538// build the image from live state; returns its byte length
4539func wc_save_build(base: i64) -> i64 {
4540 let s: *i64 = wst(base)
4541 let hd: *i64 = (base + O_SAVE) as *i64
4542 hd[0] = SAVE_MAGIC
4543 hd[1] = SAVE_VER
4544 hd[2] = s[S_SEED]
4545 hd[3] = s[S_CX]
4546 hd[4] = s[S_CY]
4547 hd[5] = s[S_CZ]
4548 hd[6] = s[S_YAW]
4549 hd[7] = s[S_PITCH]
4550 hd[8] = s[S_SEL]
4551 hd[9] = s[S_BROKEN]
4552 hd[10] = s[S_PLACED]
4553 hd[12] = s[S_FOUND]
4554 hd[13] = s[S_LOST]
4555 hd[14] = s[S_WOX]
4556 hd[15] = s[S_WOZ]
4557 let vb: *u8 = (base + O_SAVE + SAVE_HDR) as *u8
4558 let vx: *u8 = wvx(base)
4559 var p: i64 = 0
4560 while p < WX*WY*WZ { vb[p] = vx[p]; p = p + 1 }
4561 let mb: *u8 = (base + O_MOBS) as *u8
4562 var q: i64 = 0
4563 while q < MOB_BYTES { vb[WX*WY*WZ + q] = mb[q]; q = q + 1 }
4564 let eb: *u8 = (base + O_EJRN) as *u8
4565 var r: i64 = 0
4566 while r < EJRN_BYTES { vb[WX*WY*WZ + MOB_BYTES + r] = eb[r]; r = r + 1 }
4567 hd[11] = wc_save_ck(base)
4568 return SAVE_LEN
4569}
4570// validate the image in place, then restore world + camera from it. NOTHING is touched on refusal.
4571// returns 0, or negative: -1 magic, -2 version, -3 checksum
4572func wc_save_apply(base: i64) -> i64 {
4573 let hd: *i64 = (base + O_SAVE) as *i64
4574 if hd[0] != SAVE_MAGIC { return 0 - 1 }
4575 var v5: i64 = 0
4576 if hd[1] == SAVE_VER5 { v5 = 1 }
4577 if hd[1] != SAVE_VER { if v5 == 0 { return 0 - 2 } }
4578 let want: i64 = hd[11]
4579 if v5 == 0 { if wc_save_ck(base) != want { return 0 - 3 } }
4580 if v5 == 1 { if wc_save_ck_v5(base) != want { return 0 - 3 } }
4581 let s: *i64 = wst(base)
4582 s[S_SEED] = hd[2]
4583 s[S_CX] = hd[3]
4584 s[S_CY] = hd[4]
4585 s[S_CZ] = hd[5]
4586 s[S_YAW] = hd[6]
4587 s[S_PITCH] = hd[7]
4588 s[S_SEL] = hd[8]
4589 s[S_BROKEN] = hd[9]
4590 s[S_PLACED] = hd[10]
4591 s[S_FOUND] = hd[12]
4592 s[S_LOST] = hd[13]
4593 s[S_WOX] = 0
4594 s[S_WOZ] = 0
4595 if v5 == 0 { s[S_WOX] = hd[14]; s[S_WOZ] = hd[15] }
4596 s[S_VY] = 0
4597 let vb: *u8 = (base + O_SAVE + SAVE_HDR) as *u8
4598 let vx: *u8 = wvx(base)
4599 var p: i64 = 0
4600 while p < WX*WY*WZ { vx[p] = vb[p]; p = p + 1 }
4601 let mb: *u8 = (base + O_MOBS) as *u8
4602 var q: i64 = 0
4603 while q < MOB_BYTES { mb[q] = vb[WX*WY*WZ + q]; q = q + 1 }
4604 // journal: v6 restores it from the image; a v5 save simply has no touched-land history yet
4605 let eb: *u8 = (base + O_EJRN) as *u8
4606 var r: i64 = 0
4607 if v5 == 0 {
4608 while r < EJRN_BYTES { eb[r] = vb[WX*WY*WZ + MOB_BYTES + r]; r = r + 1 }
4609 } else {
4610 while r < EJRN_BYTES { eb[r] = 0 as u8; r = r + 1 }
4611 }
4612 wc_wmax(base) // the restored world's ceiling, not the fresh one's
4613 wc_soft_seat_all(base) // springs + needs are NON-PERSISTED: re-derive
4614 wc_need_seed(base)
4615 wc_mind_zero(base) // a loaded world wakes with a clear head
4616 return 0
4617}
4618
4619// legacy 6-arg form: forwards into the full core (no strafe/look/jump). Gate callers use this.
4620func apply_impl(base: i64, fwd: i64, turn: i64, pit: i64, brk: i64, plc: i64) -> i64 {
4621 return apply2_impl(base, fwd, 0, turn, 0, 0, pit, 0, brk, plc)
4622}
4623
4624// legacy single-action tick (gate + old shim): 0 fwd 1 back 2 turnL 3 turnR 4 break 5 place
4625func tick_impl(base: i64, d: i64) -> i64 {
4626 wc_weather_tick(base)
4627 var fwd: i64 = 0
4628 var turn: i64 = 0
4629 var brk: i64 = 0
4630 var plc: i64 = 0
4631 if d == 0 { fwd = 1 }
4632 if d == 1 { fwd = 0 - 1 }
4633 if d == 2 { turn = 0 - 1 }
4634 if d == 3 { turn = 1 }
4635 if d == 4 { brk = 1 }
4636 if d == 5 { plc = 1 }
4637 if d == 6 { wc_cycle(base) }
4638 return apply_impl(base, fwd, turn, 0, brk, plc)
4639}
4640
4641// multi-action input frame, FPS scheme: W/S forward/back + A/D (and arrows) STRAFE from HELD,
4642// yaw from the analog LOOK channel (mouse), pitch from mouse + R/F, jump held (auto-rehop on
4643// landing, Minecraft-style), break/place from PRESSED edges so they cannot repeat-fire.
4644func step_impl(base: i64) -> i64 {
4645 // WEATHER ON THE FPS FRAME (2026-08-30, measured on the live page: window.__wxw=[0,0,0,0]): only the
4646 // legacy tick_impl ticked the weather, so the frame path every shipped page drives left spec slots
4647 // 11-15 at zero -- no wind field, no hair sway at rest, no gusts, for every world. Same call, same
4648 // order as tick_impl: weather first, then input.
4649 wc_weather_tick(base)
4650 let q: *i64 = winp(base)
4651 let held: i64 = ia_held(q)
4652 let pressed: i64 = ia_frame(q)
4653 var fwd: i64 = 0
4654 if (held & IA_UP) != 0 { fwd = 1 }
4655 if (held & IA_DOWN) != 0 { fwd = fwd - 1 }
4656 var strafe: i64 = 0
4657 if (held & IA_RIGHT) != 0 { strafe = 1 }
4658 if (held & IA_LEFT) != 0 { strafe = strafe - 1 }
4659 var pit: i64 = 0
4660 if (held & IA_LUP) != 0 { pit = 1 }
4661 if (held & IA_LDOWN) != 0 { pit = pit - 1 }
4662 var jump: i64 = 0
4663 if (held & IA_JUMP) != 0 { jump = 1 }
4664 var brk: i64 = 0
4665 if (pressed & IA_A) != 0 { brk = 1 }
4666 var plc: i64 = 0
4667 if (pressed & IA_B) != 0 { plc = 1 }
4668 if (pressed & IA_MOD) != 0 { wc_cycle(base) } // Q taps cycle the hotbar (edge, no auto-repeat)
4669 if (pressed & IA_HOME) != 0 { wc_respawn(base) } // H = the reset button
4670 let lk: *i64 = (base + O_INPUT + 62*8) as *i64 // scratch beyond the ia arena's live slots
4671 ia_look_take(q, lk)
4672 return apply2_impl(base, fwd, strafe, 0, lk[0], lk[1], pit,