code wiki / (root) / nx_wasm_craft.nx

nx_wasm_craft.nx source

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