code wiki / (root) / nx_wasm_craft.nx

nx_wasm_craft.nx source

↩ module page · 3087 lines · 149620 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_softbind.nx" 20import "_hdl_build/nx_entity_store.nx" 21import "_hdl_build/nx_gamesave_core.nx" 22const WATER_MAGIC_16777216: i64 = 16777216 23const WATER_MAGIC_40503: i64 = 40503 24const WATER_MAGIC_33554431: i64 = 33554431 25const WATER_MAGIC_374761393: i64 = 374761393 26const WATER_MAGIC_668265263: i64 = 668265263 27const WATER_MAGIC_1442695041: i64 = 1442695041 28const WATER_MAGIC_4611686018427387903: i64 = 4611686018427387903 29const WATER_MAGIC_1274126177: i64 = 1274126177 30const WATER_MAGIC_1099511627776: i64 = 1099511627776 31const WATER_MAGIC_65536: i64 = 65536 32const WATER_MAGIC_4096: i64 = 4096 33const WATER_MAGIC_1048576: i64 = 1048576 34const WATER_MAGIC_1536: i64 = 1536 35const WATER_MAGIC_6144: i64 = 6144 36const WATER_MAGIC_57344: i64 = 57344 37const WATER_MAGIC_1469598103: i64 = 1469598103 38const WATER_MAGIC_1099511: i64 = 1099511 39const WATER_MAGIC_2400: i64 = 2400 40const WATER_MAGIC_2500: i64 = 2500 41const WATER_MAGIC_1400: i64 = 1400 42const WATER_MAGIC_20260728: i64 = 20260728 43 44const W: i64 = 480 45const H: i64 = 300 46const RES: i64 = 2 // ray per RESxRES pixel block (240x150 rays) 47const RW: i64 = 240 48const RH: i64 = 150 49const FOC: i64 = 120 // ~90 degree fov at RW=240 50 51const WX: i64 = 128 // world blocks (v2: 4x the area, taller) 52const WY: i64 = 48 53const WZ: i64 = 128 54const WATER_H: i64 = 9 55 56// FARMING-LIFE blocks (roadmap B1/B2): wetness lives IN the id and crops are separate stage ids, 57// so the v6 save format is UNTOUCHED -- voxels + the edit journal already persist every farm cell. 58const B_FARM: i64 = 12 // tilled farmland, dry 59const B_CROP0: i64 = 13 // crop sprout (placed by SEEDS onto farmland) 60const B_CROP1: i64 = 14 // crop growing 61const B_CROP2: i64 = 15 // crop MATURE -- breaking it is the HARVEST 62const B_FARMW: i64 = 16 // farmland, wet (rain or water within the 5x5 ring) 63 64const O_FB: i64 = 0 // W*H i64 packed R|G<<8|B<<16 65const O_ST: i64 = 1152000 // 64 state slots 66const O_INPUT: i64 = 1152512 // nx_input_abstract arena (64 words) 67const O_VOX: i64 = 1153024 // WX*WY*WZ u8 blocks 68const O_MOBS: i64 = 1939456 // nx_entity_store arena (certified part): en_bytes(16,7)=1632 69const MOB_CAP: i64 = 16 70const MOB_NCOMP: i64 = 7 71const MOB_BYTES: i64 = 1632 72const O_SPEC: i64 = 1941120 // GAME SPEC block (64 words) -- AFTER the arena's 1632B end 73const O_EJRN: i64 = 1941760 // EDIT JOURNAL: 8192 packed i64 hash slots at ABSOLUTE world 74 // coords (v6 streaming). Reuses the pre-v6 save staging spot. 75const EJRN_SLOTS: i64 = 8192 76const EJRN_BYTES: i64 = 65536 77const O_SAVE: i64 = 2730336 // v6 save image (relocated past MOBAUX): 128B header + voxels 78 // + mob arena + edit journal. The page reads save_off() so the 79 // relocation is invisible to every surface. 80const SAVE_HDR: i64 = 128 81const SAVE_BODY: i64 = 853600 // WX*WY*WZ + MOB_BYTES + EJRN_BYTES 82const SAVE_BODY_V5: i64 = 788064 // pre-streaming body (load-compat: v5 saves still open) 83const SAVE_LEN: i64 = 853728 // SAVE_HDR + SAVE_BODY 84const CRAFT_BYTES: i64 = 3584064 // O_SAVE + SAVE_LEN 85const O_MOBAUX: i64 = 2729952 // 16 mobs x (dx,dz,dy) last-applied step, i64 triples 86 // (384B, fits the 544B rounding slack past the save image; 87 // save format untouched). NON-PERSISTED display truth. 88 // The page's heading/speed used to be a JS derivative of 89 // positions -- a workaround; the ORGAN owns motion, so it 90 // exports the applied step incl the vertical component 91 // (climb cadence needs dy). 92const O_SOFT: i64 = 3584064 // A4 soft-body springs: 16 mobs x SB_PTS x SD_STRIDE words. 93 // NON-PERSISTED display truth (pure function of root motion; 94 // re-seated on init/load/shift/summon -- the MOBAUX pattern). 95const O_NEED: i64 = 3586368 // C1 needs: 16 mobs x 4 words (hunger, energy, social, spare). 96 // NON-PERSISTED; re-seeded deterministically on init/load. 97const O_MIND: i64 = 3586880 // C3/C5 THE INNER LIFE: 16 girls x 24 words -- 8 THOUGHT 98 // slots (id<<8 | mag+128, expiry-tick) then 4 GOSSIP slots 99 // (subj<<16 | pred<<8 | conf, born-tick). NON-PERSISTED; 100 // every write derives from deterministic state, so the 101 // T4/T17 two-arena checksums hold by construction. 102const O_PASS: i64 = 3589952 // THE TRAVELER'S PASSPORT (the isekai carry): the 103 // CHARACTER crosses worlds while each world keeps its own 104 // v6 save. Wire = 12 words (magic ver charseed broken 105 // placed harv plant found lost ticks worldmask ck); 106 // words 16-24 = the in-arena delta SNAPSHOT so a world 107 // reload never double-counts a deed into the ledger. 108const PASS_MAGIC: i64 = 20260802001 109const PASS_LEN: i64 = 192 // 24 wire words: [0]magic [1]ver [2]charseed [3]broken 110 // [4]placed [5]harv [6]plant [7]found [8]lost [9]ticks 111 // [10]worldmask [11]party-count [12..20]=3x(kind,disg, 112 // gene) -- COMPANIONS CROSS WORLDS with their bond and 113 // bred genome intact -- [21]ck. Snapshot at words 32+. 114const CRAFT_TOTAL: i64 = 3590336 // O_PASS + 384 -- the full arena an allocator must map 115 116// C3 thought ids (RimWorld's inspectable-mood law: a mood is a LEDGER, never a scalar mystery) 117const TH_RAIN: i64 = 1 118const TH_FEAR: i64 = 2 119const TH_MET: i64 = 3 120const TH_SLEPT: i64 = 4 121const TH_HUNGRY: i64 = 5 122const TH_SAWTAKEN: i64 = 6 123const TH_HEARD_TAKEN: i64 = 7 124const TH_HEARD_FRIEND: i64 = 8 125const TH_COMPANY: i64 = 9 126// C5 gossip predicates (Talk of the Town's law -- the differentiator no shipped game has) 127const GO_FRIEND: i64 = 1 128const GO_TAKEN: i64 = 2 129const SAVE_MAGIC: i64 = 20260728001 130const SAVE_VER: i64 = 6 // v6 = STREAMING WORLD (origin + edit journal in the image); 131 // v5 still LOADS (origin 0, empty journal); others REFUSED 132const SAVE_VER5: i64 = 5 133// mob components (nx_entity_store comp slots) 134const MC_X: i64 = 0 135const MC_Y: i64 = 1 136const MC_Z: i64 = 2 137const MC_KIND: i64 = 3 138const MC_PH: i64 = 4 139const MC_DISG: i64 = 5 // 1 = a HUMAN girl disguised as a monster girl (the lore: 140 // monster girls took the world; humans hide among them -- 141 // her features are a worn costume, not grown) 142const MC_GENE: i64 = 6 // 20-bit APPEARANCE GENOME rolled at spawn -- every girl is 143 // GENERATED, not hand-built: bits 0-2 hair hue, 3-4 hair 144 // length, 5-6 height, 7-8 outfit tint, 9-10 skin tone, 145 // 11-12 feature size. The slot bred inheritance plugs into. 146 147// state slots 148const S_SEED: i64 = 0 149const S_CX: i64 = 1 // camera pos Q8 (256 = one block) 150const S_CY: i64 = 2 151const S_CZ: i64 = 3 152const S_YAW: i64 = 4 // it-units (IT_PI = half turn) 153const S_PITCH: i64 = 5 154const S_T: i64 = 6 155const S_BROKEN: i64 = 7 156const S_PLACED: i64 = 8 157const S_HX: i64 = 9 // crosshair DDA hit cache 158const S_HY: i64 = 10 159const S_HZ: i64 = 11 160const S_HFACE: i64 = 12 // 0 none, 1 +x, 2 -x, 3 +y, 4 -y, 5 +z, 6 -z 161const S_HOK: i64 = 13 // 1 = in reach 162const S_VY: i64 = 14 // vertical velocity Q8/tick (jump + gravity) 163const S_GROUNDED: i64 = 15 // 1 = standing on a surface (jump allowed) 164const S_SEL: i64 = 16 // hotbar slot 0..4 (Q cycles; sel_block maps to a block type) 165const S_LBX: i64 = 17 // last-broken cell (probes/particles; the hit cache retargets 166const S_LBY: i64 = 18 // every frame, so "what did I just break" needs its own slots) 167const S_LBZ: i64 = 19 168const S_Q: i64 = 20 // ray pixel size 2..5 -- ADAPTIVE: the organ tunes itself from 169const S_FMA: i64 = 21 // the surface's reported frame ms (EMA x16); shim only reports 170const S_WMAX: i64 = 22 // highest solid y in the world -- rays above it heading up/flat 171 // are SKY immediately (kills the 110-step horizon marches) 172const S_SPX: i64 = 23 // spawn point (H key + void-fall auto-respawn return here) 173const S_SPY: i64 = 24 174const S_SPZ: i64 = 25 175const S_NPCT: i64 = 26 // crosshair NPC target: dense index of the girl under the 176 // gaze (occlusion-aware, walls block), -1 = none 177const S_FOUND: i64 = 27 // disguised humans FOUND (met + identified = under protection) 178const S_LOST: i64 = 28 // found humans TAKEN by wardens (the cost of not protecting) 179const S_BCD: i64 = 29 // birth cooldown ticks (community pacing) 180const S_WOX: i64 = 30 // WORLD ORIGIN of the window, in blocks (v6 streaming): the 181const S_WOZ: i64 = 31 // arena is a 128x48x128 CACHE over unbounded absolute terrain 182const S_EJDROP: i64 = 32 // edits REFUSED because the journal was full (loud, never silent) 183const S_HARV: i64 = 33 // MATURE crops harvested (the farming loop's payoff counter) 184const S_PLANT: i64 = 34 // seeds planted 185const S_GROWN: i64 = 35 // crops that reached maturity (world truth, gate-readable) 186const S_VAR: i64 = 36 // which world identity this arena runs (the passport's 187 // visited-mask bit; set once at init) 188const SHIFT_STRIDE: i64 = 16 // one window slide, in blocks 189const SHIFT_MARGIN: i64 = 32 // player this close to a window edge -> the world slides under them 190 191// spec slots (the game-identity DATA the engine reads -- nx_voxel_world's style-is-a-spec doctrine) 192const P_TBASE: i64 = 0 // terrain base height 193const P_TAMP1: i64 = 1 // large-noise amplitude 194const P_TAMP2: i64 = 2 // detail-noise amplitude 195const P_WATER: i64 = 3 // flood level 196const P_SNOW: i64 = 4 // snow line (99 = never) 197const P_TREE: i64 = 5 // tree sparsity modulus (0 = no trees) 198const P_CAVETH: i64 = 6 // cave carve threshold (lower = more cavern) 199const P_SKYTOP: i64 = 7 // packed zenith colour 200const P_SKYHOR: i64 = 8 // packed horizon colour 201const P_SUN: i64 = 9 // 1 = sun disc 202const P_CLOUD: i64 = 10 // 1 = cloud deck 203const P_PAL: i64 = 16 // +blocktype = packed base colour (0..11) 204const P_MOBC: i64 = 28 // +kind*2+half = packed mob colours (3 kinds x 2 tones) 205const P_PAL2: i64 = 34 // +b-12 = packed colour for farm blocks 12..16 (5 rows, 34-38) 206const P_DAYLEN: i64 = 40 // A1 calendar: ticks per day (ALL timing is spec DATA) 207const P_SEASD: i64 = 41 // days per season (4 seasons cycle) 208const P_GROWE: i64 = 42 // farm-tick cadence: one grow/hydrate pass every N ticks 209const P_GROWW: i64 = 43 // crop speed on WET farmland (Minecraft stencil speed) 210const P_GROWD: i64 = 44 // crop speed on DRY farmland 211const P_RAINS: i64 = 45 // +season = rain permil per day-eighth (4 rows, 45-48) 212const P_ADV: i64 = 50 // +i = smart-object ADVERTISEMENTS (C1): packed 213 // block<<16 | need<<8 | strength; 6 rows 50-55, 0 = end. 214 // NPC minds read OBJECTS as data -- adding a row changes 215 // village life with zero new NPC code (The Sims' law). 216const P_HISTAGE: i64 = 60 // DECLARED geomorphic stage (Strahler bands): 0 = mature/ 217 // equilibrium (HI 300-600 permil), 1 = youthful (HI >= 600). 218 // A world DECLARES its stage and gate T53 checks the 219 // MEASUREMENT matches -- widening one band until everything 220 // fits would hide a young landscape. (Ported 2026-08-02: 221 // existed only on the laptop line; the NAS gate ran 52 teeth 222 // while local ran 53 -- the LOCAL-IS-NOT-A-SUPERSET fork.) 223const P_TREEMAX: i64 = 59 // tree altitude ceiling (0 = the historic hardcoded 19) 224const P_WARD: i64 = 58 // warden daytime hunt radius in BLOCKS (0 = the 12-block 225 // default). A world declares its own danger; the arriving 226 // traveler's LEVEL scales it further (isekai: new world, 227 // new challenge, and the challenge KNOWS who showed up). 228const P_OUTFIT: i64 = 57 // outfit mode: 0 = identity keeps everyone clothed; 229 // 1 = MIXED (beach): genome bits 18-19 pick each girl's 230 // beachwear -- clothed / topless / nude -- stable per 231 // girl, bred like every trait. Identity DATA decides 232 // whether the dimension is active at all. 233const P_CURI: i64 = 56 // curiosity threshold: a girl approaches the player only 234 // while her loneliness exceeds this (HABITUATION -- the 235 // operator's "they just follow me around": company 236 // satisfies, satisfied girls walk on with their lives. 237 // Fear never habituates; shy girls always keep distance). 238 239// GAME-FEEL TUNABLES -- every one carries a row in knowledge/gamefeel_oracle.conf (banked 240// reference bands: biomechanics, shipped-sim constants, modding-community defaults) and 241// nx_gamefeel_oracle_gate REFUSES any value outside its cited band. Feel is derived, not tasted. 242const WC_GAIT_BOB_Q8: i64 = 7 // thorax oscillation AMPLITUDE (~2.7cm, 5.5cm p2p -- 243 // the band is peak-to-peak/2; 14 was a p2p-vs-amplitude 244 // conflation the recorder exposed as 11.3cm of chest) 245// RESPIRATION -- the idle drive. Derived, not tasted: resting adult breathing is 12-16/min; 246// at 14/min on the fixed 60Hz tick that is 60*60/14 = 257 ticks per cycle (0.233 Hz, inside the 247// 0.20-0.30 Hz band this rung is gated on). Amplitude is scaled off the GAIT constant above so 248// the two drives share one ruler: 7 q8 ~ 2.7cm => 1 q8 ~ 0.386cm, so 2 q8 ~ 0.77cm, inside the 249// declared 0.4-0.8cm resting-excursion band. A third value would have been a guess; this is an 250// arithmetic consequence of a constant already cited. 251const WC_BREATH_TICKS: i64 = 257 252// ★ AMPLITUDE, NOT PEAK-TO-PEAK -- and I got this wrong ONCE ALREADY on the gait constant. 253// Conversion, derived from the cited gait row: 7 q8 amplitude is documented as ~2.7cm, and 254// 7/256*100cm-per-block = 2.73cm, so the unit is q8 world units and p2p = 2*amp/256*100 cm. 255// At 2 q8 that is 1.56cm p2p -- OUTSIDE the 0.4-0.8cm resting band this rung is gated on, i.e. 256// 2x too large, the SAME p2p-vs-amplitude conflation the recorder caught in WC_GAIT_BOB_Q8 (14 257// -> 7). At 1 q8: 2/256*100 = 0.78cm p2p, inside the band. Caught by doing the arithmetic in the 258// unit the CRITERION is written in, before it shipped visibly. 259// ⚠KNOWN LIMIT, to be measured not assumed: 1 q8 is near the world-q8 resolution floor, so the 260// anchor steps coarsely; the spring integrates and should low-pass it, but ?breath=1 reports the 261// actual excursion so this is decided by measurement rather than by my expectation. 262const WC_BREATH_Q8: i64 = 1 263// speed at which GAIT fully owns the anchor. PROVISIONAL and deliberately marked so: the units 264// of the per-tick mob delta were never measured, so this is the one number here without a 265// derivation. The mobspd door added below exists to REPLACE it with a measurement rather than 266// leave a guess sitting in the engine pretending to be a constant. 267const WC_BREATH_CUT_Q8: i64 = 24 268const WC_STEP_EASE_UP: i64 = 48 // mob Y climb rate Q8/tick (a 1-block rise ~ 5 ticks) 269const WC_STEP_EASE_DN: i64 = 64 // mob Y descend rate Q8/tick 270const WC_NIGHT_X8: i64 = 5 // night begins at this eighth of the day (5/8) 271 272const EYE: i64 = 435 // eye height Q8 (~1.7 blocks) 273const SPEED: i64 = 40 // walk Q8 per tick 274const TURN: i64 = 140 // yaw it-units per tick (keyboard turn / legacy dir path) 275const PITCHV: i64 = 90 // pitch it-units per tick (R/F keyboard look) 276const PITCH_MAX: i64 = 5000 277const LOOKS: i64 = 6 // mouselook: it-units per accumulated look count 278const JUMPV: i64 = 66 // jump takeoff velocity Q8/tick 279const GRAV: i64 = 9 // gravity Q8/tick^2 280const MAXSTEPS: i64 = 110 // DDA cells per ray (view distance) 281const REACH_STEPS: i64 = 20 // crosshair edit reach in DDA cells 282 283func wst(base: i64) -> *i64 { return (base + O_ST) as *i64 } 284func wsp(base: i64) -> *i64 { return (base + O_SPEC) as *i64 } 285func mobp(base: i64) -> *i64 { return (base + O_MOBS) as *i64 } 286func wfb(base: i64) -> *i64 { return (base + O_FB) as *i64 } 287func wvx(base: i64) -> *u8 { return (base + O_VOX) as *u8 } 288func winp(base: i64) -> *i64 { return (base + O_INPUT) as *i64 } 289func wpack(r: i64, g: i64, b: i64) -> i64 { return r | (g << 8) | (b << 16) } 290func wclamp(v: i64, lo: i64, hi: i64) -> i64 { if v < lo { return lo } if v > hi { return hi } return v } 291 292func vin(x: i64, y: i64, z: i64) -> i64 { 293 if x < 0 { return 0 } if x >= WX { return 0 } 294 if y < 0 { return 0 } if y >= WY { return 0 } 295 if z < 0 { return 0 } if z >= WZ { return 0 } 296 return 1 297} 298func vget(base: i64, x: i64, y: i64, z: i64) -> i64 { 299 if vin(x, y, z) == 0 { return 0 } 300 let v: *u8 = wvx(base) 301 return (v[(y*WZ + z)*WX + x] & 0xff) as i64 302} 303func vset(base: i64, x: i64, y: i64, z: i64, b: i64) -> i64 { 304 if vin(x, y, z) == 0 { return 0 } 305 let v: *u8 = wvx(base) 306 v[(y*WZ + z)*WX + x] = b as u8 307 return 1 308} 309 310// ---------- edit journal (v6 streaming): every PLAYER edit recorded at ABSOLUTE world coords ---------- 311// One packed i64 hash slot per touched cell: (key+1)<<8 | block, key = ax+2^24 (25b) : az+2^24 (25b) 312// : ay (6b). The journal is the persistent truth for touched land; the window is a cache over 313// (seed + journal) -- the voxchunk law (virgin land from the seed, touched land from history) wired 314// into the live game. Bounded EJRN_SLOTS edits; a FULL journal REFUSES the whole edit LOUDLY 315// (S_EJDROP counts refusals) so world and history can never disagree. 316func ejp(base: i64) -> *i64 { return (base + O_EJRN) as *i64 } 317func ej_key(ax: i64, ay: i64, az: i64) -> i64 { 318 return ((ax + WATER_MAGIC_16777216) << 31) | ((az + WATER_MAGIC_16777216) << 6) | ay 319} 320func ej_put(base: i64, ax: i64, ay: i64, az: i64, b: i64) -> i64 { 321 let e: *i64 = ejp(base) 322 let k: i64 = ej_key(ax, ay, az) 323 let kv: i64 = ((k + 1) << 8) | (b & 255) 324 var i: i64 = (k * WATER_MAGIC_40503) & (EJRN_SLOTS - 1) 325 var n: i64 = 0 326 while n < EJRN_SLOTS { 327 let cur: i64 = e[i] 328 if cur == 0 { e[i] = kv; return 1 } 329 if (cur >> 8) == k + 1 { e[i] = kv; return 1 } 330 i = (i + 1) & (EJRN_SLOTS - 1) 331 n = n + 1 332 } 333 return 0 - 1 334} 335func ej_get(base: i64, ax: i64, ay: i64, az: i64) -> i64 { 336 let e: *i64 = ejp(base) 337 let k: i64 = ej_key(ax, ay, az) 338 var i: i64 = (k * WATER_MAGIC_40503) & (EJRN_SLOTS - 1) 339 var n: i64 = 0 340 while n < EJRN_SLOTS { 341 let cur: i64 = e[i] 342 if cur == 0 { return 0 - 1 } 343 if (cur >> 8) == k + 1 { return cur & 255 } 344 i = (i + 1) & (EJRN_SLOTS - 1) 345 n = n + 1 346 } 347 return 0 - 1 348} 349// overlay journal entries falling inside the local box [lx0..lx1) x [lz0..lz1) -- edits WIN over regen 350func ej_overlay(base: i64, lx0: i64, lx1: i64, lz0: i64, lz1: i64) -> i64 { 351 let s: *i64 = wst(base) 352 let e: *i64 = ejp(base) 353 var i: i64 = 0 354 while i < EJRN_SLOTS { 355 let cur: i64 = e[i] 356 if cur != 0 { 357 let k: i64 = (cur >> 8) - 1 358 let ay: i64 = k & 63 359 let az: i64 = ((k >> 6) & WATER_MAGIC_33554431) - WATER_MAGIC_16777216 360 let ax: i64 = ((k >> 31) & WATER_MAGIC_33554431) - WATER_MAGIC_16777216 361 let lx: i64 = ax - s[S_WOX] 362 let lz: i64 = az - s[S_WOZ] 363 if lx >= lx0 { if lx < lx1 { if lz >= lz0 { if lz < lz1 { 364 vset(base, lx, ay, lz, cur & 255) 365 } } } } 366 } 367 i = i + 1 368 } 369 return 0 370} 371// the PLAYER-EDIT write path: journal at absolute coords FIRST (a full journal refuses the whole 372// edit -- world and history stay consistent), then the window write. Gen/save paths keep raw vset. 373func wc_edit(base: i64, x: i64, y: i64, z: i64, b: i64) -> i64 { 374 let s: *i64 = wst(base) 375 if ej_put(base, x + s[S_WOX], y, z + s[S_WOZ], b) < 0 { 376 s[S_EJDROP] = s[S_EJDROP] + 1 377 return 0 378 } 379 return vset(base, x, y, z, b) 380} 381// solid for collision: everything but air, water and CROPS (plants are walked through, not on) 382func vsolid(base: i64, x: i64, y: i64, z: i64) -> i64 { 383 let b: i64 = vget(base, x, y, z) 384 if b == 0 { return 0 } 385 if b == 4 { return 0 } 386 if b >= B_CROP0 { if b <= B_CROP2 { return 0 } } 387 return 1 388} 389 390// ---------- worldgen: integer value noise -> heightmap -> columns -> trees ---------- 391func whash(x: i64, z: i64, sd: i64) -> i64 { 392 var n: i64 = x*WATER_MAGIC_374761393 + z*WATER_MAGIC_668265263 + sd*WATER_MAGIC_1442695041 393 n = n & WATER_MAGIC_4611686018427387903 394 n = (n ^ (n >> 13)) * WATER_MAGIC_1274126177 395 n = n & WATER_MAGIC_4611686018427387903 396 return (n ^ (n >> 16)) % 256 397} 398// bilinear value noise, lattice spacing `sc`, result 0..255 399func wnoise(x: i64, z: i64, sc: i64, sd: i64) -> i64 { 400 let cx: i64 = x / sc 401 let cz: i64 = z / sc 402 let fx: i64 = x % sc 403 let fz: i64 = z % sc 404 let c00: i64 = whash(cx, cz, sd) 405 let c10: i64 = whash(cx+1, cz, sd) 406 let c01: i64 = whash(cx, cz+1, sd) 407 let c11: i64 = whash(cx+1, cz+1, sd) 408 let v0: i64 = c00 + (c10 - c00)*fx/sc 409 let v1: i64 = c01 + (c11 - c01)*fx/sc 410 return v0 + (v1 - v0)*fz/sc 411} 412func wheight(base: i64, x: i64, z: i64, sd: i64) -> i64 { 413 let sp: *i64 = wsp(base) 414 let n1: i64 = wnoise(x, z, 16, sd) 415 let n2: i64 = wnoise(x, z, 5, sd + 7) 416 var h: i64 = sp[P_TBASE] + n1*sp[P_TAMP1]/256 + n2*sp[P_TAMP2]/256 417 return wclamp(h, 1, WY - 8) 418} 419// trilinear 3D value noise 0..255 (cave carver) 420func wnoise3(x: i64, y: i64, z: i64, sc: i64, sd: i64) -> i64 { 421 let cx: i64 = x / sc 422 let cy3: i64 = y / sc 423 let cz: i64 = z / sc 424 let fx: i64 = x % sc 425 let fy: i64 = y % sc 426 let fz: i64 = z % sc 427 let a00: i64 = whash(cx + cy3*97, cz, sd) 428 let a10: i64 = whash(cx + 1 + cy3*97, cz, sd) 429 let a01: i64 = whash(cx + cy3*97, cz + 1, sd) 430 let a11: i64 = whash(cx + 1 + cy3*97, cz + 1, sd) 431 let b00: i64 = whash(cx + (cy3+1)*97, cz, sd) 432 let b10: i64 = whash(cx + 1 + (cy3+1)*97, cz, sd) 433 let b01: i64 = whash(cx + (cy3+1)*97, cz + 1, sd) 434 let b11: i64 = whash(cx + 1 + (cy3+1)*97, cz + 1, sd) 435 let a0: i64 = a00 + (a10 - a00)*fx/sc 436 let a1: i64 = a01 + (a11 - a01)*fx/sc 437 let av: i64 = a0 + (a1 - a0)*fz/sc 438 let b0: i64 = b00 + (b10 - b00)*fx/sc 439 let b1: i64 = b01 + (b11 - b01)*fx/sc 440 let bv: i64 = b0 + (b1 - b0)*fz/sc 441 return av + (bv - av)*fy/sc 442} 443// recompute the world's highest solid layer (init + save-load; place maintains it incrementally) 444func wc_wmax(base: i64) -> i64 { 445 let s: *i64 = wst(base) 446 let vxp: *u8 = (base + O_VOX) as *u8 447 var y: i64 = WY - 1 448 while y >= 0 { 449 var i: i64 = y*WZ*WX 450 let end: i64 = (y + 1)*WZ*WX 451 while i < end { 452 if (vxp[i] & 0xff) != 0 { s[S_WMAX] = y; return y } 453 i = i + 1 454 } 455 y = y - 1 456 } 457 s[S_WMAX] = 0 458 return 0 459} 460 461// generate ONE local column from ABSOLUTE world coords: the terrain is a pure function of 462// (absolute position, seed) -> unbounded land, deterministic everywhere, no stored world needed 463// for virgin ground (the voxchunk regen law wired into the live game). 464func gencol(base: i64, x: i64, z: i64, ax: i64, az: i64, sd: i64) -> i64 { 465 let sp: *i64 = wsp(base) 466 let wl: i64 = sp[P_WATER] 467 let h: i64 = wheight(base, ax, az, sd) 468 var y: i64 = 0 469 while y < WY { 470 var b: i64 = 0 471 if y <= h { 472 b = 3 // stone 473 if y > h - 3 { b = 2 } // dirt cap 474 if y == h { 475 b = 1 // grass 476 if h <= wl { b = 5 } // sand at/below waterline 477 if h >= sp[P_SNOW] { b = 8 } // snow high up 478 } 479 // caves: carve inside the crust (never the floor, never the top 2 = no surface holes, 480 // and never under water columns so lakes stay sealed) 481 if b == 3 { if y >= 2 { if y <= h - 2 { if h > wl { 482 if wnoise3(ax, y, az, 7, sd + 55) > sp[P_CAVETH] { b = 0 } 483 } } } } 484 // ores seed the remaining stone: coal common, iron deeper, gold deepest 485 if b == 3 { 486 let orr: i64 = whash(ax*7 + y*131, az*11 + y*17, sd + 31) % 997 487 if orr < 22 { b = 9 } 488 if y < 14 { if orr >= 22 { if orr < 32 { b = 10 } } } 489 if y < 9 { if orr >= 32 { if orr < 38 { b = 11 } } } 490 } 491 } 492 if y > h { if y <= wl { b = 4 } } // flood low ground 493 vset(base, x, y, z, b) 494 y = y + 1 495 } 496 return 0 497} 498// paint a voxel ONLY inside the regen box -- a tree may never overwrite terrain (and its journal 499// overlay) outside the strip being regenerated 500func vset_box(base: i64, x: i64, y: i64, z: i64, b: i64, lx0: i64, lx1: i64, lz0: i64, lz1: i64) -> i64 { 501 if x < lx0 { return 0 } 502 if x >= lx1 { return 0 } 503 if z < lz0 { return 0 } 504 if z >= lz1 { return 0 } 505 return vset(base, x, y, z, b) 506} 507// trees whose TRUNK falls in the +-2-expanded ABSOLUTE range paint their blocks CLIPPED to the 508// local box, so a tree crossing a strip seam is completed by whichever strip regenerates later. 509// The trunk condition is PURE (grass by the gen rules: above water, below snow) -- no window peeking. 510func gentrees(base: i64, lx0: i64, lx1: i64, lz0: i64, lz1: i64, sd: i64) -> i64 { 511 let sp: *i64 = wsp(base) 512 if sp[P_TREE] == 0 { return 0 } 513 let s: *i64 = wst(base) 514 let wl: i64 = sp[P_WATER] 515 var az: i64 = s[S_WOZ] + lz0 - 2 516 while az <= s[S_WOZ] + lz1 + 1 { 517 var ax: i64 = s[S_WOX] + lx0 - 2 518 while ax <= s[S_WOX] + lx1 + 1 { 519 let h2: i64 = wheight(base, ax, az, sd) 520 var trunk: i64 = 0 521 // the tree altitude ceiling is a SPEC ROW, not an engine constant: hardcoded 19 522 // silently capped forests for any identity whose land sits higher (gate T52 found 523 // it -- vale's P_TREE 43->19 barely moved the trunk count because the CEILING, not 524 // the density row, was binding). 0 = the historic 19, so craft/depths are unchanged. 525 var tmax: i64 = sp[P_TREEMAX] 526 if tmax <= 0 { tmax = 19 } 527 if h2 < tmax { if h2 > wl { if h2 < sp[P_SNOW] { 528 if whash(ax*3 + 7, az*5 + 11, sd + 99) % sp[P_TREE] == 0 { trunk = 1 } 529 } } } 530 if trunk == 1 { 531 let tx: i64 = ax - s[S_WOX] 532 let tz: i64 = az - s[S_WOZ] 533 var ty: i64 = h2 + 1 534 while ty <= h2 + 4 { vset_box(base, tx, ty, tz, 6, lx0, lx1, lz0, lz1); ty = ty + 1 } 535 var lz: i64 = tz - 2 536 while lz <= tz + 2 { 537 var lx: i64 = tx - 2 538 while lx <= tx + 2 { 539 var keep: i64 = 1 540 if lx == tx { if lz == tz { keep = 0 } } 541 if keep == 1 { 542 vset_box(base, lx, h2 + 4, lz, 7, lx0, lx1, lz0, lz1) 543 if lx >= tx - 1 { if lx <= tx + 1 { if lz >= tz - 1 { if lz <= tz + 1 { 544 vset_box(base, lx, h2 + 5, lz, 7, lx0, lx1, lz0, lz1) 545 } } } } 546 } 547 lx = lx + 1 548 } 549 lz = lz + 1 550 } 551 vset_box(base, tx, h2 + 6, tz, 7, lx0, lx1, lz0, lz1) 552 } 553 ax = ax + 1 554 } 555 az = az + 1 556 } 557 return 0 558} 559 560func genworld(base: i64, sd: i64) -> i64 { 561 let s: *i64 = wst(base) 562 var z: i64 = 0 563 while z < WZ { 564 var x: i64 = 0 565 while x < WX { 566 gencol(base, x, z, x + s[S_WOX], z + s[S_WOZ], sd) 567 x = x + 1 568 } 569 z = z + 1 570 } 571 gentrees(base, 0, WX, 0, WZ, sd) 572 return 0 573} 574 575// ---------- v6 STREAMING: slide the window one stride along x or z ---------- 576// The world is unbounded; the arena is the cache. Order matters: move kept voxels -> bump the 577// origin -> regen the incoming strip from absolute coords -> repaint its trees -> overlay the 578// journal (edits WIN) -> translate every arena-space coordinate (player, spawn, mobs) -> rebuild 579// wmax -> invalidate the crosshair cache. One call = 16 blocks; ticks trigger it at the margins. 580func wc_shift(base: i64, dx: i64, dz: i64) -> i64 { 581 let s: *i64 = wst(base) 582 let v: *u8 = wvx(base) 583 let sd: i64 = s[S_SEED] 584 if dx != 0 { 585 var y: i64 = 0 586 while y < WY { 587 var z: i64 = 0 588 while z < WZ { 589 let row: i64 = (y*WZ + z)*WX 590 if dx > 0 { 591 var x: i64 = 0 592 while x < WX - dx { v[row + x] = v[row + x + dx]; x = x + 1 } 593 } else { 594 var x2: i64 = WX - 1 595 while x2 >= 0 - dx { v[row + x2] = v[row + x2 + dx]; x2 = x2 - 1 } 596 } 597 z = z + 1 598 } 599 y = y + 1 600 } 601 s[S_WOX] = s[S_WOX] + dx 602 var sx0: i64 = 0 603 var sx1: i64 = 0 - dx 604 if dx > 0 { sx0 = WX - dx; sx1 = WX } 605 var rz: i64 = 0 606 while rz < WZ { 607 var rx: i64 = sx0 608 while rx < sx1 { gencol(base, rx, rz, rx + s[S_WOX], rz + s[S_WOZ], sd); rx = rx + 1 } 609 rz = rz + 1 610 } 611 gentrees(base, sx0, sx1, 0, WZ, sd) 612 ej_overlay(base, sx0, sx1, 0, WZ) 613 } 614 if dz != 0 { 615 var y2: i64 = 0 616 while y2 < WY { 617 let plane: i64 = y2*WZ*WX 618 if dz > 0 { 619 var z2: i64 = 0 620 while z2 < WZ - dz { 621 let dstr: i64 = plane + z2*WX 622 let srcr: i64 = plane + (z2 + dz)*WX 623 var x3: i64 = 0 624 while x3 < WX { v[dstr + x3] = v[srcr + x3]; x3 = x3 + 1 } 625 z2 = z2 + 1 626 } 627 } else { 628 var z3: i64 = WZ - 1 629 while z3 >= 0 - dz { 630 let dstr2: i64 = plane + z3*WX 631 let srcr2: i64 = plane + (z3 + dz)*WX 632 var x4: i64 = 0 633 while x4 < WX { v[dstr2 + x4] = v[srcr2 + x4]; x4 = x4 + 1 } 634 z3 = z3 - 1 635 } 636 } 637 y2 = y2 + 1 638 } 639 s[S_WOZ] = s[S_WOZ] + dz 640 var sz0: i64 = 0 641 var sz1: i64 = 0 - dz 642 if dz > 0 { sz0 = WZ - dz; sz1 = WZ } 643 var rz2: i64 = sz0 644 while rz2 < sz1 { 645 var rx2: i64 = 0 646 while rx2 < WX { gencol(base, rx2, rz2, rx2 + s[S_WOX], rz2 + s[S_WOZ], sd); rx2 = rx2 + 1 } 647 rz2 = rz2 + 1 648 } 649 gentrees(base, 0, WX, sz0, sz1, sd) 650 ej_overlay(base, 0, WX, sz0, sz1) 651 } 652 // translate every arena-space coordinate by the slide 653 s[S_CX] = s[S_CX] - dx*256 654 s[S_CZ] = s[S_CZ] - dz*256 655 s[S_SPX] = wclamp(s[S_SPX] - dx*256, 2*256, (WX - 2)*256) 656 s[S_SPZ] = wclamp(s[S_SPZ] - dz*256, 2*256, (WZ - 2)*256) 657 s[S_LBX] = s[S_LBX] - dx 658 s[S_LBZ] = s[S_LBZ] - dz 659 let m: *i64 = mobp(base) 660 var k: i64 = 0 661 while k < en_count(m) { 662 let h: i64 = en_nth(m, k) 663 en_set(m, h, MC_X, wclamp(en_get(m, h, MC_X) - dx*256, 2*256, (WX - 2)*256)) 664 en_set(m, h, MC_Z, wclamp(en_get(m, h, MC_Z) - dz*256, 2*256, (WZ - 2)*256)) 665 k = k + 1 666 } 667 wc_wmax(base) 668 wc_soft_seat_all(base) // the world slid under every girl: re-seat springs 669 s[S_HOK] = 0 670 return 0 671} 672 673// ---------- DDA raycast ---------- 674// marches from (S_CX,S_CY,S_CZ) along (dx,dy,dz) (any scale). On hit writes out[0..4] = 675// cellx,celly,cellz,face,tcross and returns the step count; returns -1 on miss (sky). 676// skipw=1 treats water as air (the transparency continuation ray). The voxel read is INLINED -- 677// this loop runs ~2-8M times a second and a per-step call was the measured overhead. 678func wray2(base: i64, dx: i64, dy: i64, dz: i64, maxsteps: i64, out: *i64, skipw: i64) -> i64 { 679 let s: *i64 = wst(base) 680 var cxq: i64 = s[S_CX] 681 var cyq: i64 = s[S_CY] 682 var czq: i64 = s[S_CZ] 683 var cellx: i64 = cxq >> 8 684 var celly: i64 = cyq >> 8 685 var cellz: i64 = czq >> 8 686 let HUGE: i64 = WATER_MAGIC_1099511627776 687 var stpx: i64 = 1 688 if dx < 0 { stpx = 0-1 } 689 var stpy: i64 = 1 690 if dy < 0 { stpy = 0-1 } 691 var stpz: i64 = 1 692 if dz < 0 { stpz = 0-1 } 693 // t units: pos_q8(t) = pos + d*t/65536; crossing one cell on axis c costs 256*65536/|dc| 694 var tdx: i64 = HUGE 695 if dx != 0 { tdx = 256*WATER_MAGIC_65536 / dx; if tdx < 0 { tdx = 0 - tdx } } 696 var tdy: i64 = HUGE 697 if dy != 0 { tdy = 256*WATER_MAGIC_65536 / dy; if tdy < 0 { tdy = 0 - tdy } } 698 var tdz: i64 = HUGE 699 if dz != 0 { tdz = 256*WATER_MAGIC_65536 / dz; if tdz < 0 { tdz = 0 - tdz } } 700 var bx: i64 = (cellx << 8) - cxq // distance to boundary, Q8 (negative side) 701 if stpx == 1 { bx = bx + 256 } 702 var tmx: i64 = HUGE 703 if dx != 0 { tmx = bx*WATER_MAGIC_65536 / dx; if tmx < 0 { tmx = 0 - tmx } } 704 var by: i64 = (celly << 8) - cyq 705 if stpy == 1 { by = by + 256 } 706 var tmy: i64 = HUGE 707 if dy != 0 { tmy = by*WATER_MAGIC_65536 / dy; if tmy < 0 { tmy = 0 - tmy } } 708 var bz: i64 = (cellz << 8) - czq 709 if stpz == 1 { bz = bz + 256 } 710 var tmz: i64 = HUGE 711 if dz != 0 { tmz = bz*WATER_MAGIC_65536 / dz; if tmz < 0 { tmz = 0 - tmz } } 712 let vxp: *u8 = (base + O_VOX) as *u8 713 var step: i64 = 0 714 while step < maxsteps { 715 var face: i64 = 0 716 var tcross: i64 = 0 717 if tmx <= tmy { if tmx <= tmz { 718 cellx = cellx + stpx 719 tcross = tmx 720 tmx = tmx + tdx 721 face = 1 722 if stpx == 1 { face = 2 } // stepped +x => hit the -x... face 2 = west-lit 723 } } 724 if face == 0 { if tmy <= tmz { 725 celly = celly + stpy 726 tcross = tmy 727 tmy = tmy + tdy 728 face = 3 729 if stpy == 1 { face = 4 } // stepped up => hit the block's bottom 730 } } 731 if face == 0 { 732 cellz = cellz + stpz 733 tcross = tmz 734 tmz = tmz + tdz 735 face = 5 736 if stpz == 1 { face = 6 } 737 } 738 if celly >= WY { if stpy >= 0 { return 0 - 1 } } // heading skyward above the world 739 if celly > s[S_WMAX] { if stpy >= 0 { return 0 - 1 } } // above every solid block, not diving 740 if cellx < 0-8 { return 0 - 1 } 741 if cellx > WX+8 { return 0 - 1 } 742 if cellz < 0-8 { return 0 - 1 } 743 if cellz > WZ+8 { return 0 - 1 } 744 if celly < 0 { return 0 - 1 } 745 var b: i64 = 0 746 if cellx >= 0 { if cellx < WX { if celly < WY { if cellz >= 0 { if cellz < WZ { 747 b = (vxp[(celly*WZ + cellz)*WX + cellx] & 0xff) as i64 748 } } } } } 749 if skipw == 1 { if b == 4 { b = 0 } } 750 if b != 0 { 751 out[0] = cellx 752 out[1] = celly 753 out[2] = cellz 754 out[3] = face 755 out[4] = tcross // crossing t: hit point = pos + d*t/WATER_MAGIC_65536 (Q8) 756 return step + 1 757 } 758 step = step + 1 759 } 760 return 0 - 1 761} 762 763func wray(base: i64, dx: i64, dy: i64, dz: i64, maxsteps: i64, out: *i64) -> i64 { 764 return wray2(base, dx, dy, dz, maxsteps, out, 0) 765} 766 767// block base colour -- from the SPEC PALETTE (the game's look is data, not code) 768func wcol(base: i64, b: i64) -> i64 { 769 if b < 0 { return wpack(255, 0, 255) } 770 if b >= 12 { if b <= 16 { return wsp(base)[P_PAL2 + b - 12] } } 771 if b > 16 { return wpack(255, 0, 255) } 772 if b > 11 { return wpack(255, 0, 255) } 773 return wsp(base)[P_PAL + b] 774} 775// hotbar slot -> placeable block type 776func sel_block(sel: i64) -> i64 { 777 if sel == 1 { return 2 } // dirt 778 if sel == 2 { return 6 } // wood 779 if sel == 3 { return 7 } // leaves 780 if sel == 4 { return 5 } // sand 781 if sel == 5 { return B_FARM } // the HOE: tills the targeted grass/dirt top 782 if sel == 6 { return B_CROP0 } // SEEDS: plant onto farmland 783 return 3 // stone 784} 785// face brightness permil: top lit, bottom dark, x/z sides distinct so edges READ as 3d 786func wface_l(face: i64) -> i64 { 787 if face == 3 { return 1000 } 788 if face == 4 { return 420 } 789 if face == 1 { return 760 } 790 if face == 2 { return 700 } 791 if face == 5 { return 620 } 792 return 560 793} 794func wsky(base: i64, syy: i64) -> i64 { 795 let sp: *i64 = wsp(base) 796 let hor: i64 = sp[P_SKYHOR] 797 let top: i64 = sp[P_SKYTOP] 798 let t: i64 = wclamp(syy + RH/2, 0, RH) // 0 bottom .. RH top 799 let r: i64 = (hor & 255) + ((top & 255) - (hor & 255))*t/RH 800 let g: i64 = ((hor >> 8) & 255) + (((top >> 8) & 255) - ((hor >> 8) & 255))*t/RH 801 let b: i64 = ((hor >> 16) & 255) + (((top >> 16) & 255) - ((hor >> 16) & 255))*t/RH 802 return wpack(r, g, b) 803} 804// clouds: a flat noise plane above the world, sampled where an upward ray crosses it, slowly 805// drifting with time. Returns 0..255 coverage for blending into the sky. 806func wcloud(camx: i64, camy: i64, camz: i64, dx: i64, dy: i64, dz: i64, t: i64) -> i64 { 807 if dy < 40 { return 0 } 808 let plane: i64 = 56*256 // cloud deck above the 48-high world 809 let tq: i64 = (plane - camy)*WATER_MAGIC_65536/dy 810 if tq <= 0 { return 0 } 811 let cx: i64 = (camx + dx*tq/WATER_MAGIC_65536) >> 9 // 2-block cloud texels 812 let cz: i64 = (camz + dz*tq/WATER_MAGIC_65536) >> 9 813 let n: i64 = wnoise(cx + t/40, cz + t/90, 6, 777) 814 if n < 148 { return 0 } 815 var cov: i64 = (n - 148)*255/60 816 if cov > 235 { cov = 235 } 817 return cov 818} 819// sky with a SUN: disc + glow around the sun's projected ray-pixel position (sunon=0 -> plain sky) 820func wsky2(base: i64, syy: i64, px: i64, py: i64, sunx: i64, suny: i64, sunon: i64) -> i64 { 821 let base2: i64 = wsky(base, syy) 822 if sunon == 0 { return base2 } 823 let ddx: i64 = px - sunx 824 let ddy: i64 = py - suny 825 let d2: i64 = ddx*ddx + ddy*ddy 826 if d2 < 36 { return wpack(255, 250, 224) } // the disc 827 if d2 < 900 { // the glow 828 let w2: i64 = 255 - d2*255/900 829 var r: i64 = (base2 & 255) + (255 - (base2 & 255))*w2/300 830 var g: i64 = ((base2 >> 8) & 255) + (250 - ((base2 >> 8) & 255))*w2/340 831 var b: i64 = ((base2 >> 16) & 255) + (224 - ((base2 >> 16) & 255))*w2/460 832 return wpack(wclamp(r,0,255), wclamp(g,0,255), wclamp(b,0,255)) 833 } 834 // ordered 2x2 dither breaks the gradient banding lines 835 if ((px ^ py) & 1) == 1 { 836 return wpack(wclamp((base2 & 255) + 2, 0, 255), wclamp(((base2 >> 8) & 255) + 2, 0, 255), wclamp(((base2 >> 16) & 255) + 2, 0, 255)) 837 } 838 return base2 839} 840// textured face shading: per-texel hash variation from the HIT POINT's in-face UV (Q8 fractions), 841// per-type character (wood grain, leaf speckle, water shimmer, ore mottle), edge BEVEL so the 842// block grid reads crisply, then face light and step fog. All integer, all deterministic. 843// corner AO: darken a face edge when a solid block sits beside it in the face's outward layer -- 844// the classic soft-corner cue that makes voxel scenes read as lit geometry instead of flat tiles 845func wao(base: i64, cx2: i64, cy2b: i64, cz2: i64, face: i64, u: i64, v: i64) -> i64 { 846 var ox2: i64 = cx2 847 var oy2: i64 = cy2b 848 var oz2: i64 = cz2 849 if face == 1 { ox2 = ox2 + 1 } 850 if face == 2 { ox2 = ox2 - 1 } 851 if face == 3 { oy2 = oy2 + 1 } 852 if face == 4 { oy2 = oy2 - 1 } 853 if face == 5 { oz2 = oz2 + 1 } 854 if face == 6 { oz2 = oz2 - 1 } 855 var ao: i64 = 0 856 // u-axis neighbours (u maps to x on y/z faces, to z on x faces) 857 if u < 44 { 858 var hit1: i64 = 0 859 if face == 1 { hit1 = vsolid(base, ox2, oy2, oz2 - 1) } 860 if face == 2 { hit1 = vsolid(base, ox2, oy2, oz2 - 1) } 861 if face == 3 { hit1 = vsolid(base, ox2 - 1, oy2, oz2) } 862 if face == 4 { hit1 = vsolid(base, ox2 - 1, oy2, oz2) } 863 if face == 5 { hit1 = vsolid(base, ox2 - 1, oy2, oz2) } 864 if face == 6 { hit1 = vsolid(base, ox2 - 1, oy2, oz2) } 865 if hit1 == 1 { ao = ao + (44 - u)*30/44 } 866 } 867 if u > 211 { 868 var hit2: i64 = 0 869 if face == 1 { hit2 = vsolid(base, ox2, oy2, oz2 + 1) } 870 if face == 2 { hit2 = vsolid(base, ox2, oy2, oz2 + 1) } 871 if face == 3 { hit2 = vsolid(base, ox2 + 1, oy2, oz2) } 872 if face == 4 { hit2 = vsolid(base, ox2 + 1, oy2, oz2) } 873 if face == 5 { hit2 = vsolid(base, ox2 + 1, oy2, oz2) } 874 if face == 6 { hit2 = vsolid(base, ox2 + 1, oy2, oz2) } 875 if hit2 == 1 { ao = ao + (u - 211)*30/44 } 876 } 877 // v-axis neighbours (v maps to z on top/bottom faces, to y on side faces) 878 if v < 44 { 879 var hit3: i64 = 0 880 if face == 3 { hit3 = vsolid(base, ox2, oy2, oz2 - 1) } 881 if face == 4 { hit3 = vsolid(base, ox2, oy2, oz2 - 1) } 882 if face == 1 { hit3 = vsolid(base, ox2, oy2 - 1, oz2) } 883 if face == 2 { hit3 = vsolid(base, ox2, oy2 - 1, oz2) } 884 if face == 5 { hit3 = vsolid(base, ox2, oy2 - 1, oz2) } 885 if face == 6 { hit3 = vsolid(base, ox2, oy2 - 1, oz2) } 886 if hit3 == 1 { ao = ao + (44 - v)*30/44 } 887 } 888 if v > 211 { 889 var hit4: i64 = 0 890 if face == 3 { hit4 = vsolid(base, ox2, oy2, oz2 + 1) } 891 if face == 4 { hit4 = vsolid(base, ox2, oy2, oz2 + 1) } 892 if face == 1 { hit4 = vsolid(base, ox2, oy2 + 1, oz2) } 893 if face == 2 { hit4 = vsolid(base, ox2, oy2 + 1, oz2) } 894 if face == 5 { hit4 = vsolid(base, ox2, oy2 + 1, oz2) } 895 if face == 6 { hit4 = vsolid(base, ox2, oy2 + 1, oz2) } 896 if hit4 == 1 { ao = ao + (v - 211)*30/44 } 897 } 898 if ao > 45 { ao = 45 } 899 return ao 900} 901func 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 { 902 var u: i64 = hpx & 255 903 var v: i64 = hpz & 255 904 if face == 1 { u = hpz & 255; v = hpy & 255 } 905 if face == 2 { u = hpz & 255; v = hpy & 255 } 906 if face == 5 { u = hpx & 255; v = hpy & 255 } 907 if face == 6 { u = hpx & 255; v = hpy & 255 } 908 // grass block SIDES are dirt with a grass strip at the top edge (the classic block identity) 909 var b: i64 = b0 910 if b0 == 1 { if face != 3 { if face != 4 { 911 b = 2 912 if v > 206 { b = 1 } 913 } } } 914 var tex: i64 = 0 915 if b == 6 { // wood: vertical grain stripes 916 tex = whash(u/12 + (hpx >> 8)*7 + (hpz >> 8)*13, 3, 99) % 256 917 } 918 if b != 6 { 919 var amp: i64 = 70 920 if b == 1 { amp = 95 } 921 if b == 2 { amp = 85 } 922 if b == 7 { amp = 130 } 923 if b == 5 { amp = 55 } 924 var us: i64 = u/16 925 var vs: i64 = v/16 926 if b == 4 { // water: time-drifted shimmer 927 us = us + t2/3 928 vs = vs - t2/5 929 amp = 110 930 } 931 tex = whash(us + (hpx >> 8)*31, vs + (hpy >> 8)*17 + (hpz >> 8)*7, b*131) % 256 932 tex = 128 + (tex - 128)*amp/130 933 } 934 var light: i64 = wface_l(face) 935 // sun-directional face light (sun worlds only): east faces catch it, west faces shadow 936 if wsp(base)[P_SUN] == 1 { 937 if face == 1 { light = light*112/100 } 938 if face == 2 { light = light*92/100 } 939 if face == 5 { light = light*104/100 } 940 } 941 light = light*(870 + tex*260/256)/1000 942 if u < 14 { light = light*82/100 } // edge bevel: the block grid 943 if u > 241 { light = light*82/100 } 944 if v < 14 { light = light*82/100 } 945 if v > 241 { light = light*82/100 } 946 let ao2: i64 = wao(base, clx, cly, clz, face, u, v) 947 if ao2 > 0 { light = light*(100 - ao2)/100 } // soft corners where geometry meets 948 let c: i64 = wcol(base, b) 949 var r: i64 = (c & 255) * light / 1000 950 var g: i64 = ((c >> 8) & 255) * light / 1000 951 var bl: i64 = ((c >> 16) & 255) * light / 1000 952 let sk: i64 = wsky(base, syy) 953 var fog: i64 = steps*280/MAXSTEPS 954 if fog > 236 { fog = 236 } 955 r = r + ((sk & 255) - r)*fog/256 956 g = g + (((sk >> 8) & 255) - g)*fog/256 957 bl = bl + (((sk >> 16) & 255) - bl)*fog/256 958 return wpack(wclamp(r,0,255), wclamp(g,0,255), wclamp(bl,0,255)) 959} 960 961// THE GAME SPEC WRITER -- two complete game identities as DATA over one engine. 962// v0 = NISHI CRAFT (sunlit overworld). v1 = NISHI DEPTHS (twilight cavern plateau: no sun/clouds/ 963// trees, ember horizon, vast cave nets, mossy basalt, glowing ores and glow-mobs). 964func wc_spec(base: i64, v: i64) -> i64 { 965 let sp: *i64 = wsp(base) 966 var k: i64 = 0 967 while k < 64 { sp[k] = 0; k = k + 1 } 968 if v == 3 { 969 // v3 = NISHI VALE, the MEDIEVAL VILLAGE: rolling green highland, deep woods, cold 970 // snowline, cave-riddled stone. THIRD WORLD, ZERO ENGINE CODE -- the recombination 971 // claim at n=3. Wardens run wide here (P_WARD), so a traveler arrives to REAL stakes. 972 sp[P_TBASE] = 8 973 sp[P_TAMP1] = 22 974 sp[P_TAMP2] = 7 975 sp[P_WATER] = 7 976 sp[P_SNOW] = 26 977 sp[P_TREE] = 11 // deep woods, tuned to the PUBLISHED 978 // 500-800 trees/ha band -- 19 measured 372/ha, 979 // barely half a real forest (T53; ported from 980 // the laptop line 2026-08-02, same fork as 981 // P_HISTAGE -- the fix existed and never landed) 982 sp[P_TREEMAX] = 25 // woods climb to the snowline here 983 sp[P_HISTAGE] = 0 // MEASURED, not assumed: symmetric value- 984 // noise yields HI ~594 permil = mature 985 sp[P_CAVETH] = 168 // more cavern than craft's 182 986 sp[P_SKYTOP] = wpack(86, 122, 176) 987 sp[P_SKYHOR] = wpack(196, 200, 206) 988 sp[P_SUN] = 1 989 sp[P_CLOUD] = 1 990 sp[P_PAL + 1] = wpack(72, 124, 56) // highland grass 991 sp[P_PAL + 2] = wpack(94, 72, 50) // loam 992 sp[P_PAL + 3] = wpack(118, 116, 120) // quarry stone 993 sp[P_PAL + 4] = wpack(44, 82, 140) // cold water 994 sp[P_PAL + 5] = wpack(190, 178, 150) // riverbank 995 sp[P_PAL + 6] = wpack(88, 62, 36) // timber 996 sp[P_PAL + 7] = wpack(40, 96, 44) // dark canopy 997 sp[P_PAL + 8] = wpack(238, 240, 246) // snow 998 sp[P_PAL + 9] = wpack(66, 66, 72) 999 sp[P_PAL + 10] = wpack(176, 142, 118) 1000 sp[P_PAL + 11] = wpack(224, 188, 92) 1001 sp[P_MOBC + 0] = wpack(206, 160, 96) 1002 sp[P_MOBC + 1] = wpack(160, 118, 62) 1003 sp[P_MOBC + 2] = wpack(120, 158, 190) 1004 sp[P_MOBC + 3] = wpack(78, 110, 146) 1005 sp[P_MOBC + 4] = wpack(178, 120, 160) 1006 sp[P_MOBC + 5] = wpack(134, 86, 122) 1007 sp[P_PAL2 + 0] = wpack(88, 60, 38) 1008 sp[P_PAL2 + 1] = wpack(120, 186, 90) 1009 sp[P_PAL2 + 2] = wpack(92, 168, 58) 1010 sp[P_PAL2 + 3] = wpack(230, 200, 82) 1011 sp[P_PAL2 + 4] = wpack(58, 42, 30) 1012 sp[P_DAYLEN] = 72000 1013 sp[P_SEASD] = 3 1014 sp[P_GROWE] = 2400 1015 sp[P_GROWW] = 5 // a harder land: crops take longer 1016 sp[P_GROWD] = 1 1017 sp[P_RAINS + 0] = 420 // wet spring 1018 sp[P_RAINS + 1] = 220 1019 sp[P_RAINS + 2] = 380 1020 sp[P_RAINS + 3] = 180 1021 sp[P_ADV + 0] = (B_CROP2 << 16) | 9 1022 sp[P_ADV + 1] = (1 << 16) | 3 1023 sp[P_ADV + 2] = (4 << 16) | (2 << 8) | 2 1024 sp[P_CURI] = 400 1025 sp[P_OUTFIT] = 0 1026 sp[P_WARD] = 20 // wardens hunt from 20 blocks by DAY here 1027 return 0 1028 } 1029 if v == 2 { 1030 // v2 = NISHI SHORE, the REVIEW STAGE (operator 2026-08-02): near-flat warm sand, a 1031 // sea, no trees/caves/snow -- body and sprite rendering judged WITHOUT environment 1032 // distraction, with the outfit dimension ACTIVE (mixed beach). One engine, one more 1033 // identity: this whole world is 30 data rows, which IS the recombination claim. 1034 sp[P_TBASE] = 11 1035 sp[P_TAMP1] = 2 1036 sp[P_TAMP2] = 1 1037 sp[P_WATER] = 9 1038 sp[P_SNOW] = 99 1039 sp[P_TREE] = 0 1040 sp[P_CAVETH] = 255 1041 sp[P_SKYTOP] = wpack(110, 168, 230) 1042 sp[P_SKYHOR] = wpack(214, 226, 240) 1043 sp[P_SUN] = 1 1044 sp[P_CLOUD] = 1 1045 sp[P_PAL + 1] = wpack(226, 204, 148) // shore sand (the "grass" slot) 1046 sp[P_PAL + 2] = wpack(206, 180, 128) // packed sand 1047 sp[P_PAL + 3] = wpack(180, 170, 158) // beach rock 1048 sp[P_PAL + 4] = wpack(56, 140, 196) // warm sea 1049 sp[P_PAL + 5] = wpack(232, 214, 160) // dry sand 1050 sp[P_PAL + 6] = wpack(150, 110, 70) // driftwood 1051 sp[P_PAL + 7] = wpack(96, 150, 96) // beach scrub 1052 sp[P_PAL + 8] = wpack(240, 240, 244) 1053 sp[P_PAL + 9] = wpack(120, 116, 110) 1054 sp[P_PAL + 10] = wpack(176, 142, 118) 1055 sp[P_PAL + 11] = wpack(224, 188, 92) 1056 sp[P_MOBC + 0] = wpack(240, 178, 84) 1057 sp[P_MOBC + 1] = wpack(196, 138, 56) 1058 sp[P_MOBC + 2] = wpack(96, 206, 196) 1059 sp[P_MOBC + 3] = wpack(64, 158, 150) 1060 sp[P_MOBC + 4] = wpack(238, 138, 186) 1061 sp[P_MOBC + 5] = wpack(190, 100, 144) 1062 sp[P_PAL2 + 0] = wpack(94, 64, 40) 1063 sp[P_PAL2 + 1] = wpack(120, 186, 90) 1064 sp[P_PAL2 + 2] = wpack(92, 168, 58) 1065 sp[P_PAL2 + 3] = wpack(230, 200, 82) 1066 sp[P_PAL2 + 4] = wpack(64, 46, 32) 1067 sp[P_DAYLEN] = 72000 1068 sp[P_SEASD] = 3 1069 sp[P_GROWE] = 2400 1070 sp[P_GROWW] = 6 1071 sp[P_GROWD] = 1 1072 sp[P_RAINS + 0] = 60 // a review stage stays mostly sunny 1073 sp[P_RAINS + 1] = 40 1074 sp[P_RAINS + 2] = 60 1075 sp[P_RAINS + 3] = 40 1076 sp[P_ADV + 0] = (4 << 16) | (2 << 8) | 4 // the water's edge is the gathering place 1077 sp[P_ADV + 1] = (1 << 16) | 3 1078 sp[P_CURI] = 350 1079 sp[P_OUTFIT] = 1 1080 return 0 1081 } 1082 // FARMING-LIFE rows, shared by every identity (the loop itself is identity-independent DATA) 1083 sp[P_PAL2 + 0] = wpack(94, 64, 40) // tilled farmland, dry 1084 sp[P_PAL2 + 1] = wpack(120, 186, 90) // crop sprout 1085 sp[P_PAL2 + 2] = wpack(92, 168, 58) // crop growing 1086 sp[P_PAL2 + 3] = wpack(230, 200, 82) // crop MATURE (reads golden at a glance) 1087 sp[P_PAL2 + 4] = wpack(64, 46, 32) // farmland wet (soaked dark soil) 1088 sp[P_DAYLEN] = 72000 // 20 real minutes at the fixed 60Hz sim 1089 // tick -- the shipped-sim benchmark band 1090 // (Minecraft 20:00/day, Stardew 14:33, 1091 // Vintage Story ~24:00): a day you can 1092 // farm INSIDE, not a light show 1093 sp[P_SEASD] = 3 1094 sp[P_GROWE] = 2400 // one growth/hydration roll per farm cell 1095 // per ~40s -- Minecraft's random-tick 1096 // cadence (~47s/block avg) on our grid; 1097 // wet stencil den=5 => a watered crop 1098 // matures in ~6.7 min =~ 1/3 day (the 1099 // Minecraft wheat band) 1100 sp[P_GROWW] = 6 1101 sp[P_GROWD] = 1 1102 sp[P_RAINS + 0] = 340 // spring 1103 sp[P_RAINS + 1] = 180 // summer 1104 sp[P_RAINS + 2] = 300 // autumn 1105 sp[P_RAINS + 3] = 120 // winter 1106 sp[P_ADV + 0] = (B_CROP2 << 16) | 9 // a mature field draws foragers (need 0 = hunger) 1107 sp[P_ADV + 1] = (1 << 16) | 3 // grass meadow: light grazing 1108 sp[P_ADV + 2] = (4 << 16) | (2 << 8) | 2 // water's edge: where girls gather to talk 1109 sp[P_CURI] = 350 1110 if v == 1 { 1111 sp[P_TBASE] = 22 1112 sp[P_TAMP1] = 8 1113 sp[P_TAMP2] = 4 1114 sp[P_WATER] = 5 1115 sp[P_SNOW] = 99 1116 sp[P_TREE] = 0 1117 sp[P_CAVETH] = 148 1118 sp[P_SKYTOP] = wpack(28, 22, 40) 1119 sp[P_SKYHOR] = wpack(96, 48, 40) 1120 sp[P_SUN] = 0 1121 sp[P_CLOUD] = 0 1122 sp[P_PAL + 1] = wpack(74, 122, 84) // moss 1123 sp[P_PAL + 2] = wpack(88, 62, 48) // packed earth 1124 sp[P_PAL + 3] = wpack(98, 90, 104) // basalt 1125 sp[P_PAL + 4] = wpack(60, 190, 180) // glow-water 1126 sp[P_PAL + 5] = wpack(120, 100, 80) 1127 sp[P_PAL + 6] = wpack(70, 50, 36) 1128 sp[P_PAL + 7] = wpack(60, 90, 60) 1129 sp[P_PAL + 8] = wpack(200, 200, 210) 1130 sp[P_PAL + 9] = wpack(58, 58, 66) 1131 sp[P_PAL + 10] = wpack(198, 150, 116) 1132 sp[P_PAL + 11] = wpack(255, 206, 96) // gold veins GLOW down here 1133 sp[P_MOBC + 0] = wpack(255, 128, 64) // ember 1134 sp[P_MOBC + 1] = wpack(200, 84, 40) 1135 sp[P_MOBC + 2] = wpack(124, 220, 255) // ice-glow 1136 sp[P_MOBC + 3] = wpack(80, 160, 200) 1137 sp[P_MOBC + 4] = wpack(204, 124, 255) // violet 1138 sp[P_MOBC + 5] = wpack(150, 84, 200) 1139 return 0 1140 } 1141 sp[P_TBASE] = 4 1142 sp[P_TAMP1] = 17 1143 sp[P_TAMP2] = 5 1144 sp[P_WATER] = 9 1145 sp[P_SNOW] = 20 1146 sp[P_TREE] = 43 1147 sp[P_CAVETH] = 182 1148 sp[P_SKYTOP] = wpack(98, 152, 223) 1149 sp[P_SKYHOR] = wpack(168, 206, 245) 1150 sp[P_SUN] = 1 1151 sp[P_CLOUD] = 1 1152 sp[P_PAL + 1] = wpack(84, 158, 62) // grass 1153 sp[P_PAL + 2] = wpack(122, 86, 56) // dirt 1154 sp[P_PAL + 3] = wpack(126, 126, 132) // stone 1155 sp[P_PAL + 4] = wpack(52, 96, 190) // water 1156 sp[P_PAL + 5] = wpack(206, 190, 132) // sand 1157 sp[P_PAL + 6] = wpack(104, 74, 40) // wood 1158 sp[P_PAL + 7] = wpack(52, 122, 44) // leaves 1159 sp[P_PAL + 8] = wpack(232, 238, 244) // snow 1160 sp[P_PAL + 9] = wpack(72, 74, 80) // coal 1161 sp[P_PAL + 10] = wpack(176, 142, 118) // iron 1162 sp[P_PAL + 11] = wpack(224, 188, 92) // gold 1163 sp[P_MOBC + 0] = wpack(240, 178, 84) 1164 sp[P_MOBC + 1] = wpack(196, 138, 56) 1165 sp[P_MOBC + 2] = wpack(96, 206, 196) 1166 sp[P_MOBC + 3] = wpack(64, 158, 150) 1167 sp[P_MOBC + 4] = wpack(238, 138, 186) 1168 sp[P_MOBC + 5] = wpack(190, 100, 144) 1169 return 0 1170} 1171 1172// mobs: 12 creatures spawned on grass, deterministic, through the CERTIFIED entity store 1173// (handle = index+generation, stale-handle-proof by construction -- the part's gate owns that claim) 1174func genmobs(base: i64, sd: i64) -> i64 { 1175 let m: *i64 = mobp(base) 1176 en_init(m, MOB_CAP, MOB_NCOMP) 1177 var i: i64 = 0 1178 var placed: i64 = 0 1179 while i < 200 { 1180 if placed < 12 { 1181 let mx: i64 = 4 + whash(i*13 + 5, i*7 + 1, sd + 400) % (WX - 8) 1182 let mz: i64 = 4 + whash(i*17 + 3, i*29 + 9, sd + 500) % (WZ - 8) 1183 let h: i64 = wheight(base, mx, mz, sd) 1184 if h < wsp(base)[P_SNOW] - 1 { if vget(base, mx, h, mz) == 1 { 1185 let hd: i64 = en_spawn(m) 1186 if hd > 0 { 1187 en_set(m, hd, MC_X, mx*256 + 128) 1188 en_set(m, hd, MC_Y, (h + 1)*256) 1189 en_set(m, hd, MC_Z, mz*256 + 128) 1190 en_set(m, hd, MC_KIND, whash(mx, mz, sd + 600) % 3) 1191 en_set(m, hd, MC_PH, whash(mz, mx, sd + 700) % WATER_MAGIC_4096) 1192 var dg: i64 = 0 1193 if whash(mx + 13, mz + 7, sd + 800) % 3 == 0 { dg = 1 } 1194 en_set(m, hd, MC_DISG, dg) 1195 let g1: i64 = whash(mx*3 + 1, mz*5 + 9, sd + 900) 1196 let g2: i64 = whash(mz*7 + 3, mx*11 + 5, sd + 901) 1197 en_set(m, hd, MC_GENE, (g1*WATER_MAGIC_4096 + g2*16) % WATER_MAGIC_1048576) 1198 placed = placed + 1 1199 } 1200 } } 1201 } 1202 i = i + 1 1203 } 1204 return placed 1205} 1206 1207// ---------- init / tick / render ---------- 1208func init_impl_v(base: i64, v: i64, seed: i64) -> i64 { 1209 wc_spec(base, v) 1210 let s: *i64 = wst(base) 1211 var k: i64 = 0 1212 while k < 64 { s[k] = 0; k = k + 1 } 1213 let ej0: *i64 = ejp(base) 1214 var kj: i64 = 0 1215 while kj < EJRN_SLOTS { ej0[kj] = 0; kj = kj + 1 } 1216 s[S_SEED] = seed 1217 s[S_VAR] = v // after the zero loop -- an earlier set 1218 // here was wiped (the zero-loop-order class) 1219 s[S_T] = wsp(base)[P_DAYLEN]/4 // first frame = MID-MORNING light (the 1220 // visitor's first impression; a dawn-dark 1221 // spawn read as night, screenshot-caught) 1222 genworld(base, seed) 1223 wc_wmax(base) 1224 genmobs(base, seed) 1225 // A4/C1: zero the spring + need regions (init may reuse a dirty arena), then seat and seed 1226 let sb0i: *i64 = (base + O_SOFT) as *i64 1227 var kzi: i64 = 0 1228 while kzi < MOB_CAP*SB_PTS*SD_STRIDE { sb0i[kzi] = 0; kzi = kzi + 1 } 1229 let nd0i: *i64 = (base + O_NEED) as *i64 1230 var kni: i64 = 0 1231 while kni < MOB_CAP*4 { nd0i[kni] = 0; kni = kni + 1 } 1232 wc_soft_seat_all(base) 1233 wc_need_seed(base) 1234 wc_mind_zero(base) 1235 // spawn on the TALLEST central column: from a local peak every direction is a vista, 1236 // never a cliff wall filling the first frame (the eyeball found exactly that) 1237 var sx: i64 = WX/2 1238 var sz: i64 = WZ/2 1239 var sh: i64 = 0 1240 var qz: i64 = WZ/2 - 12 1241 while qz < WZ/2 + 12 { 1242 var qx: i64 = WX/2 - 12 1243 while qx < WX/2 + 12 { 1244 let qh: i64 = wheight(base, qx, qz, seed) 1245 if qh > sh { sh = qh; sx = qx; sz = qz } 1246 qx = qx + 1 1247 } 1248 qz = qz + 1 1249 } 1250 s[S_CX] = sx*256 + 128 1251 s[S_CZ] = sz*256 + 128 1252 s[S_CY] = (sh + 1)*256 + EYE 1253 s[S_SPX] = s[S_CX] 1254 s[S_SPY] = s[S_CY] 1255 s[S_SPZ] = s[S_CZ] 1256 s[S_YAW] = 0 1257 s[S_PITCH] = 0 1258 s[S_Q] = 2 1259 ia_init(winp(base)) 1260 ia_bind(winp(base), 82, IA_LUP) // R = look up (real action bits since seq1136) 1261 ia_bind(winp(base), 70, IA_LDOWN) // F = look down (G = party invite, below) 1262 ia_bind(winp(base), 32, IA_JUMP) // space = jump (touch center zone jumps too) 1263 ia_bind(winp(base), 81, IA_MOD) // Q = cycle the hotbar selection 1264 ia_bind(winp(base), 72, IA_HOME) // H = return to spawn (edge-fall rescue) 1265 return 0 1266} 1267func init_impl(base: i64, seed: i64) -> i64 { return init_impl_v(base, 0, seed) } 1268 1269// return to spawn: position + velocity reset, world untouched (the operator's reset button) 1270func wc_respawn(base: i64) -> i64 { 1271 let s: *i64 = wst(base) 1272 s[S_CX] = s[S_SPX] 1273 s[S_CY] = s[S_SPY] 1274 s[S_CZ] = s[S_SPZ] 1275 s[S_VY] = 0 1276 s[S_GROUNDED] = 1 1277 return 0 1278} 1279 1280// highest solid y at a column (collision floor), -1 if none 1281func wground(base: i64, x: i64, z: i64) -> i64 { 1282 var y: i64 = WY - 1 1283 while y >= 0 { 1284 if vsolid(base, x, y, z) == 1 { return y } 1285 y = y - 1 1286 } 1287 return 0 - 1 1288} 1289 1290// try to stand at (nxq, nzq): returns the required cam y (Q8) or -1 if the move is blocked 1291func wstand(base: i64, nxq: i64, nzq: i64, cyq: i64) -> i64 { 1292 let g: i64 = wground(base, nxq >> 8, nzq >> 8) 1293 var stand: i64 = (g + 1)*256 + EYE 1294 let wl2: i64 = wsp(base)[P_WATER] 1295 let wsurf: i64 = (wl2 + 1)*256 + EYE - 96 // float slightly sunk in water 1296 if stand < wsurf { if vget(base, nxq >> 8, wl2, nzq >> 8) == 4 { stand = wsurf } } 1297 // VOID COLUMN = WALL: no ground below means off the world's edge -- refuse the step (the 1298 // walk-off-and-vanish bug: wstand happily computed a hover height over nothing) 1299 if g < 0 { return 0 - 1 } 1300 if stand - cyq > 384 { return 0 - 1 } // more than a 1.5-block rise = wall 1301 let heady: i64 = (stand + 128) >> 8 1302 if vsolid(base, nxq >> 8, heady, nzq >> 8) == 1 { return 0 - 1 } 1303 return stand 1304} 1305 1306// stand height for the CURRENT column (no wall refusal -- the vertical-physics target) 1307func wstand_here(base: i64, xq: i64, zq: i64) -> i64 { 1308 let g: i64 = wground(base, xq >> 8, zq >> 8) 1309 var stand: i64 = (g + 1)*256 + EYE 1310 let wl3: i64 = wsp(base)[P_WATER] 1311 let wsurf: i64 = (wl3 + 1)*256 + EYE - 96 1312 if stand < wsurf { if vget(base, xq >> 8, wl3, zq >> 8) == 4 { stand = wsurf } } 1313 return stand 1314} 1315 1316// mob floor: first solid scanning DOWN from just above the mob's feet -- wground scans from the 1317// SKY and reads an overhanging leaf canopy as "ground 5 blocks up", which froze every mob under a 1318// tree (measured: phase advanced 982/tick = the refusal branch, position never moved). 1319// body clearance: a girl is ~2 blocks tall -- the two cells above the ground must be AIR or 1320// she stands with her head/shoulders inside an overhang (invisible as SDF; as a MESH her 1321// buried parts discard and the poking limbs read as floating shards -- operator-reported). 1322func mob_fits(base: i64, x: i64, g: i64, z: i64) -> i64 { 1323 if vsolid(base, x, g + 1, z) == 1 { return 0 } 1324 if vsolid(base, x, g + 2, z) == 1 { return 0 } 1325 return 1 1326} 1327 1328func mob_ground(base: i64, x: i64, ytop: i64, z: i64) -> i64 { 1329 var y: i64 = ytop 1330 if y > WY - 1 { y = WY - 1 } 1331 while y >= 0 { 1332 if vsolid(base, x, y, z) == 1 { return y } 1333 y = y - 1 1334 } 1335 return 0 - 1 1336} 1337 1338// NPC HIT DETECTION: march the view ray in half-block samples up to 7 blocks; a solid voxel 1339// blocks it (walls occlude, real detection); a sample inside a girl's body cylinder (r=.35, 1340// h=1.7) targets her. Returns the dense index or -1. The same pass PUSHES the player out of 1341// bodies (they have PRESENCE now -- no more walking through people). 1342func npc_pick(base: i64, fx: i64, fy: i64, fz: i64) -> i64 { 1343 let s: *i64 = wst(base) 1344 let m: *i64 = mobp(base) 1345 var t: i64 = 1 1346 while t <= 14 { 1347 let px2: i64 = s[S_CX] + fx*t*128/WATER_MAGIC_4096 1348 let py2: i64 = s[S_CY] + fy*t*128/WATER_MAGIC_4096 1349 let pz2: i64 = s[S_CZ] + fz*t*128/WATER_MAGIC_4096 1350 if vsolid(base, px2 >> 8, py2 >> 8, pz2 >> 8) == 1 { return 0 - 1 } 1351 var k: i64 = 0 1352 while k < en_count(m) { 1353 let h: i64 = en_nth(m, k) 1354 var ddx: i64 = px2 - en_get(m, h, MC_X) 1355 if ddx < 0 { ddx = 0 - ddx } 1356 var ddz: i64 = pz2 - en_get(m, h, MC_Z) 1357 if ddz < 0 { ddz = 0 - ddz } 1358 let dy2: i64 = py2 - en_get(m, h, MC_Y) 1359 if ddx < 90 { if ddz < 90 { if dy2 >= 0 { if dy2 < 440 { return k } } } } 1360 k = k + 1 1361 } 1362 t = t + 1 1363 } 1364 return 0 - 1 1365} 1366 1367// ---------- A1: the integer tick calendar (day/night + 4 seasons) ---------- 1368// Pure functions of (S_T, spec) -> bit-exact across arenas by construction. Every phase compare 1369// CROSS-MULTIPLIES (the banked integer-truncation law: never divide a value before comparing it). 1370// RECOMBINATION CONTRACT: a different game = a different spec; a recombined identity turns a 1371// subsystem OFF by zeroing its rows, so every consumer guards the zero (endless day, no rain, 1372// no farm clock) instead of trapping on a modulo-by-zero. Off is a state, never a crash. 1373func cal_dayt(base: i64) -> i64 { 1374 let dl: i64 = wsp(base)[P_DAYLEN] 1375 if dl <= 0 { return 0 } 1376 let s: *i64 = wst(base) 1377 return s[S_T] % dl 1378} 1379func cal_day(base: i64) -> i64 { 1380 let dl: i64 = wsp(base)[P_DAYLEN] 1381 if dl <= 0 { return 0 } 1382 let s: *i64 = wst(base) 1383 return s[S_T] / dl 1384} 1385func cal_season(base: i64) -> i64 { 1386 let sd: i64 = wsp(base)[P_SEASD] 1387 if sd <= 0 { return 0 } 1388 return (cal_day(base) / sd) % 4 1389} 1390// night = the last 3/8 of each day: dayt*8 >= DAYLEN*5, cross-multiplied 1391func cal_night(base: i64) -> i64 { 1392 if wsp(base)[P_DAYLEN] <= 0 { return 0 } 1393 if cal_dayt(base)*8 >= wsp(base)[P_DAYLEN]*WC_NIGHT_X8 { return 1 } 1394 return 0 1395} 1396// sun elevation as a Q12 sine: rises 0 at dawn, +4096 at noon, negative through the night 1397func cal_sunel(base: i64) -> i64 { 1398 let dl: i64 = wsp(base)[P_DAYLEN] 1399 if dl <= 0 { return 4096 } // calendar off = eternal noon 1400 let dt2: i64 = cal_dayt(base) 1401 if dt2*8 < dl*WC_NIGHT_X8 { return it_sin4096(dt2*8*IT_PI/(dl*WC_NIGHT_X8)) } 1402 return 0 - it_sin4096((dt2*8 - dl*WC_NIGHT_X8)*IT_PI/(dl*(8 - WC_NIGHT_X8))) 1403} 1404// deterministic weather: ONE roll per absolute day-eighth from (seed, segment, season table) -- 1405// the same world replays the same storms forever 1406func cal_rain(base: i64) -> i64 { 1407 let dl: i64 = wsp(base)[P_DAYLEN] 1408 if dl <= 0 { return 0 } 1409 let s: *i64 = wst(base) 1410 let seg: i64 = s[S_T]*8/dl 1411 if whash(seg, 771, s[S_SEED] + 3300) % 1000 < wsp(base)[P_RAINS + cal_season(base)] { return 1 } 1412 return 0 1413} 1414// write bst into MC_DISG bits 3-5 WITHOUT touching the disguise/met bits (0-2) or the 1415// FRIENDSHIP byte (bits 6-13) -- the old (dg & 7) mask silently erased any upper state 1416func dg_bst(dg: i64, b: i64) -> i64 { return dg - ((dg >> 3) & 7)*8 + b*8 } 1417 1418// ---------- B1/B2: soil + crops. THE EDIT JOURNAL IS THE SPARSE ACTIVE LIST ---------- 1419// Every farm cell was player-touched, so it is already journaled at absolute coords: no scan, 1420// no extra memory, and growth writes go through the same journal so world and history can 1421// never disagree. Growth is Minecraft's integer stencil: p = 1/(floor(25/speed)+1), speed 1422// from soil wetness; rolls are seed+coord+epoch hashes -> replay-safe by construction. 1423func wc_grow_set(base: i64, lx: i64, ly: i64, lz: i64, b: i64) -> i64 { 1424 let s: *i64 = wst(base) 1425 if ej_put(base, lx + s[S_WOX], ly, lz + s[S_WOZ], b) < 0 { 1426 s[S_EJDROP] = s[S_EJDROP] + 1 1427 return 0 1428 } 1429 return vset(base, lx, ly, lz, b) 1430} 1431// water within the 5x5 ring at soil level or one below (ponds sit lower than their fields) 1432func wc_wet_near(base: i64, lx: i64, ly: i64, lz: i64) -> i64 { 1433 var dz2: i64 = 0 - 2 1434 while dz2 <= 2 { 1435 var dx2: i64 = 0 - 2 1436 while dx2 <= 2 { 1437 if vget(base, lx + dx2, ly, lz + dz2) == 4 { return 1 } 1438 if vget(base, lx + dx2, ly - 1, lz + dz2) == 4 { return 1 } 1439 dx2 = dx2 + 1 1440 } 1441 dz2 = dz2 + 1 1442 } 1443 return 0 1444} 1445func wc_farm_tick(base: i64) -> i64 { 1446 let s: *i64 = wst(base) 1447 let e: *i64 = ejp(base) 1448 let sp: *i64 = wsp(base) 1449 let rain: i64 = cal_rain(base) 1450 var i: i64 = 0 1451 while i < EJRN_SLOTS { 1452 let cur: i64 = e[i] 1453 if cur != 0 { 1454 let b: i64 = cur & 255 1455 if b >= B_FARM { if b <= B_FARMW { 1456 let k: i64 = (cur >> 8) - 1 1457 let ay: i64 = k & 63 1458 let az: i64 = ((k >> 6) & WATER_MAGIC_33554431) - WATER_MAGIC_16777216 1459 let ax: i64 = ((k >> 31) & WATER_MAGIC_33554431) - WATER_MAGIC_16777216 1460 let lx: i64 = ax - s[S_WOX] 1461 let lz: i64 = az - s[S_WOZ] 1462 // only cells inside the window tick (offscreen farms pause -- chunk law, honest) 1463 if vin(lx, ay, lz) == 1 { if vget(base, lx, ay, lz) == b { 1464 if b == B_FARM { if rain == 1 { wc_grow_set(base, lx, ay, lz, B_FARMW) } } 1465 if b == B_FARM { if rain == 0 { if wc_wet_near(base, lx, ay, lz) == 1 { 1466 wc_grow_set(base, lx, ay, lz, B_FARMW) 1467 } } } 1468 if b == B_FARMW { if rain == 0 { if wc_wet_near(base, lx, ay, lz) == 0 { 1469 wc_grow_set(base, lx, ay, lz, B_FARM) 1470 } } } 1471 if b >= B_CROP0 { if b <= B_CROP1 { 1472 var spd: i64 = sp[P_GROWD] 1473 let un: i64 = vget(base, lx, ay - 1, lz) 1474 if un == B_FARMW { spd = sp[P_GROWW] } 1475 if rain == 1 { if un == B_FARM { spd = sp[P_GROWW] } } 1476 if spd < 1 { spd = 1 } 1477 let den: i64 = 25/spd + 1 1478 if whash(ax*131 + ay, az*97, s[S_SEED] + s[S_T]/sp[P_GROWE]) % den == 0 { 1479 wc_grow_set(base, lx, ay, lz, b + 1) 1480 if b + 1 == B_CROP2 { s[S_GROWN] = s[S_GROWN] + 1 } 1481 } 1482 } } 1483 } } 1484 } } 1485 } 1486 i = i + 1 1487 } 1488 return 0 1489} 1490 1491// ---------- A4/C1 shared state maintenance (springs + needs) ---------- 1492func wc_soft_seat_all(base: i64) -> i64 { 1493 let m: *i64 = mobp(base) 1494 let sb0: *i64 = (base + O_SOFT) as *i64 1495 var k: i64 = 0 1496 while k < en_count(m) { 1497 let h: i64 = en_nth(m, k) 1498 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) } 1499 k = k + 1 1500 } 1501 return 0 1502} 1503// ---------- THE TRAVELER'S PASSPORT (isekai): character + party cross worlds ---------- 1504func passp(base: i64) -> *i64 { return (base + O_PASS) as *i64 } 1505func wc_pass_ck(p: *i64) -> i64 { 1506 return gsc_fold_words(WATER_MAGIC_1469598103, p, 0, 21, WATER_MAGIC_1099511, WATER_MAGIC_4611686018427387903) 1507} 1508func wc_party_count(base: i64) -> i64 { 1509 let m: *i64 = mobp(base) 1510 var n: i64 = 0 1511 var k: i64 = 0 1512 while k < en_count(m) { 1513 let h: i64 = en_nth(m, k) 1514 if h > 0 { if (en_get(m, h, MC_DISG) & 4) == 4 { n = n + 1 } } 1515 k = k + 1 1516 } 1517 return n 1518} 1519// fold the session's deltas into the lifetime ledger (snapshot words 32+ make this idempotent: 1520// a world reload or a second build never double-counts a deed) 1521func wc_pass_sync(base: i64) -> i64 { 1522 let p: *i64 = passp(base) 1523 let s: *i64 = wst(base) 1524 if p[0] != PASS_MAGIC { 1525 var z: i64 = 0 1526 while z < 48 { p[z] = 0; z = z + 1 } 1527 p[0] = PASS_MAGIC 1528 p[1] = 1 1529 p[2] = whash(s[S_SEED], s[S_T], 20260802) 1530 // snapshot stays ZERO on creation: a traveler born mid-world ABSORBS what he has 1531 // already done here as his first deeds. Baselining at current values silently 1532 // discarded them (gate T50 caught exactly that: 7 harvests -> ledger 0). 1533 } 1534 var d: i64 = s[S_BROKEN] - p[32] 1535 if d > 0 { p[3] = p[3] + d } 1536 p[32] = s[S_BROKEN] 1537 d = s[S_PLACED] - p[33] 1538 if d > 0 { p[4] = p[4] + d } 1539 p[33] = s[S_PLACED] 1540 d = s[S_HARV] - p[34] 1541 if d > 0 { p[5] = p[5] + d } 1542 p[34] = s[S_HARV] 1543 d = s[S_PLANT] - p[35] 1544 if d > 0 { p[6] = p[6] + d } 1545 p[35] = s[S_PLANT] 1546 d = s[S_FOUND] - p[36] 1547 if d > 0 { p[7] = p[7] + d } 1548 p[36] = s[S_FOUND] 1549 d = s[S_LOST] - p[37] 1550 if d > 0 { p[8] = p[8] + d } 1551 p[37] = s[S_LOST] 1552 d = s[S_T] - p[38] 1553 if d > 0 { p[9] = p[9] + d } 1554 p[38] = s[S_T] 1555 p[10] = p[10] | (1 << s[S_VAR]) 1556 // the party rides along: up to 3 companions' (kind, bond-word, genome) 1557 let m: *i64 = mobp(base) 1558 var pc: i64 = 0 1559 var k: i64 = 0 1560 while k < en_count(m) { 1561 let h: i64 = en_nth(m, k) 1562 if h > 0 { if (en_get(m, h, MC_DISG) & 4) == 4 { if pc < 3 { 1563 p[12 + pc*3] = en_get(m, h, MC_KIND) 1564 p[13 + pc*3] = en_get(m, h, MC_DISG) 1565 p[14 + pc*3] = en_get(m, h, MC_GENE) 1566 pc = pc + 1 1567 } } } 1568 k = k + 1 1569 } 1570 p[11] = pc 1571 p[21] = wc_pass_ck(p) 1572 return PASS_LEN 1573} 1574// arriving traveler: validate, re-baseline the snapshot, and WALK THE PARTY OFF THE BOAT -- 1575// each companion respawns beside the player with bond and genome intact. Refusal touches 1576// nothing: -1 magic, -2 version, -3 checksum (the save doors' law). 1577func wc_pass_apply(base: i64) -> i64 { 1578 let p: *i64 = passp(base) 1579 if p[0] != PASS_MAGIC { return 0 - 1 } 1580 if p[1] != 1 { return 0 - 2 } 1581 if wc_pass_ck(p) != p[21] { return 0 - 3 } 1582 let s: *i64 = wst(base) 1583 p[32] = s[S_BROKEN] 1584 p[33] = s[S_PLACED] 1585 p[34] = s[S_HARV] 1586 p[35] = s[S_PLANT] 1587 p[36] = s[S_FOUND] 1588 p[37] = s[S_LOST] 1589 p[38] = s[S_T] 1590 p[10] = p[10] | (1 << s[S_VAR]) 1591 let m: *i64 = mobp(base) 1592 var pc: i64 = 0 1593 while pc < p[11] { if pc < 3 { 1594 let hn: i64 = en_spawn(m) 1595 if hn > 0 { 1596 let ang: i64 = pc*2*IT_PI/3 + IT_PI/6 1597 var ax: i64 = s[S_CX] + it_sin4096(ang)*3*256/WATER_MAGIC_4096 1598 var az: i64 = s[S_CZ] + it_cos4096(ang)*3*256/WATER_MAGIC_4096 1599 ax = wclamp(ax, 2*256, (WX - 2)*256) 1600 az = wclamp(az, 2*256, (WZ - 2)*256) 1601 let gy: i64 = mob_ground(base, ax >> 8, WY - 1, az >> 8) 1602 en_set(m, hn, MC_X, ax) 1603 en_set(m, hn, MC_Z, az) 1604 en_set(m, hn, MC_Y, (gy + 1)*256) 1605 en_set(m, hn, MC_KIND, p[12 + pc*3]) 1606 en_set(m, hn, MC_DISG, p[13 + pc*3]) 1607 en_set(m, hn, MC_GENE, p[14 + pc*3]) 1608 en_set(m, hn, MC_PH, whash(p[14 + pc*3], pc, s[S_SEED]) % WATER_MAGIC_4096) 1609 } 1610 pc = pc + 1 1611 } } 1612 wc_soft_seat_all(base) 1613 wc_need_seed(base) 1614 p[21] = wc_pass_ck(p) 1615 return 0 1616} 1617 1618// level + party-invite as BASE-PARAMETERIZED cores (the doors below are thin wrappers, so the 1619// gate exercises the same code the game runs -- one definition, zero drift) 1620func pass_level_of(base: i64) -> i64 { 1621 let p: *i64 = passp(base) 1622 if p[0] != PASS_MAGIC { return 1 } 1623 var sc: i64 = p[5]*4 + p[6] + p[7]*20 + p[3]/8 + p[4]/8 + p[9]/72000*3 1624 var lv: i64 = 1 1625 while sc >= lv*10 { sc = sc - lv*10; lv = lv + 1 } 1626 return lv 1627} 1628func party_of(base: i64) -> i64 { 1629 let s: *i64 = wst(base) 1630 if s[S_NPCT] < 0 { return 0 - 1 } 1631 let m: *i64 = mobp(base) 1632 let h: i64 = en_nth(m, s[S_NPCT]) 1633 if h <= 0 { return 0 - 1 } 1634 let dg: i64 = en_get(m, h, MC_DISG) 1635 if ((dg >> 6) & 255) < 40 { return 0 - 2 } // a bond, not a kidnapping 1636 if (dg & 4) == 4 { 1637 en_set(m, h, MC_DISG, dg - 4) // toggle: she may also LEAVE 1638 return 0 1639 } 1640 if wc_party_count(base) >= 3 { return 0 - 3 } 1641 en_set(m, h, MC_DISG, dg | 4) 1642 th_add(base, s[S_NPCT], TH_MET, 20, 14400) 1643 return 1 1644} 1645 1646// outfit for girl k: identity data gates the dimension; her genome picks within it. 1647// 0 clothed · 1 topless · 2 nude (all figures are the one adult mesh) 1648// THE POSE DOOR (operator 2026-08-02: "interact with me and the environment in various poses 1649// beyond the one" -- the pose PIPELINE's first rung). The engine ALREADY computes behavioral 1650// states (T44: sleep/flee via MC_DISG bits 3-5; movement via O_MOBAUX deltas) and the renderer 1651// ignores every one of them: sleeping girls play the walk clip, idle girls walk in place. Same 1652// class as the face: THE DATA EXISTS AND DRIVES NOTHING -- so the consumer contract comes first. 1653// Pose ids (the page consumes; the coming NXA pose BANK indexes by these): 1654// 0 idle-stand (breath only) 1 walk 2 sleep-lying 3 flee-run 4 sit 5 lie-awake 1655// 4/5 are RESERVED rows: behaviors from the towel/smart-object chain select them; publishing 1656// the id space now means new poses arrive as DATA against a stable contract, not an engine edit. 1657func mob_pose(base: i64, k: i64) -> i64 { 1658 let m: *i64 = mobp(base) 1659 let st: i64 = (en_get(m, en_nth(m, k), MC_DISG) >> 3) & 7 1660 if st == 2 { return 2 } 1661 if st == 4 { return 3 } 1662 if mob_spd_q8i(base, k) > 0 { return 1 } 1663 return 0 1664} 1665func mobpose(k: i64) -> i64 { return mob_pose(0, k) } 1666 1667func mob_outfit(base: i64, k: i64) -> i64 { 1668 if wsp(base)[P_OUTFIT] == 0 { return 0 } 1669 let m: *i64 = mobp(base) 1670 let g: i64 = (en_get(m, en_nth(m, k), MC_GENE) >> 18) & 3 1671 if g <= 1 { return 0 } 1672 if g == 2 { return 1 } 1673 return 2 1674} 1675 1676func wc_need_seed(base: i64) -> i64 { 1677 let m: *i64 = mobp(base) 1678 let s: *i64 = wst(base) 1679 let nd0: *i64 = (base + O_NEED) as *i64 1680 var k: i64 = 0 1681 while k < en_count(m) { 1682 nd0[k*4] = 300 + (whash(k, 11, s[S_SEED]) % 200) 1683 nd0[k*4 + 1] = 700 1684 nd0[k*4 + 2] = 300 + (whash(k, 23, s[S_SEED]) % 200) 1685 nd0[k*4 + 3] = 0 1686 k = k + 1 1687 } 1688 return 0 1689} 1690 1691// ---------- C3/C5: the inner life (thought ledger + gossip records) ---------- 1692func mindp(base: i64) -> *i64 { return (base + O_MIND) as *i64 } 1693func wc_mind_zero(base: i64) -> i64 { 1694 let mp: *i64 = mindp(base) 1695 var i: i64 = 0 1696 while i < MOB_CAP*24 { mp[i] = 0; i = i + 1 } 1697 return 0 1698} 1699// add/refresh a thought: same id refreshes in place; else a dead slot; else rotate one out 1700func th_add(base: i64, k: i64, id: i64, mag: i64, ttl: i64) -> i64 { 1701 let mp: *i64 = mindp(base) 1702 let s: *i64 = wst(base) 1703 let b: i64 = k*24 1704 var slot: i64 = 0 - 1 1705 var i: i64 = 0 1706 while i < 8 { 1707 if (mp[b + i*2] >> 8) == id { slot = i } 1708 i = i + 1 1709 } 1710 if slot < 0 { 1711 i = 0 1712 while i < 8 { 1713 if slot < 0 { if mp[b + i*2 + 1] <= s[S_T] { slot = i } } 1714 i = i + 1 1715 } 1716 } 1717 if slot < 0 { slot = (s[S_T] + k) % 8 } 1718 mp[b + slot*2] = id*256 + (mag + 128) 1719 mp[b + slot*2 + 1] = s[S_T] + ttl 1720 return 0 1721} 1722// mood = the sum of LIVE thoughts, clamped -- always decomposable back into its reasons 1723func th_mood(base: i64, k: i64) -> i64 { 1724 let mp: *i64 = mindp(base) 1725 let s: *i64 = wst(base) 1726 let b: i64 = k*24 1727 var mo: i64 = 0 1728 var i: i64 = 0 1729 while i < 8 { 1730 if mp[b + i*2] != 0 { if mp[b + i*2 + 1] > s[S_T] { mo = mo + (mp[b + i*2] & 255) - 128 } } 1731 i = i + 1 1732 } 1733 if mo > 100 { mo = 100 } 1734 if mo < 0 - 100 { mo = 0 - 100 } 1735 return mo 1736} 1737// a gossip fact: (subject, predicate, confidence, born). A better-known copy never downgrades. 1738func go_add(base: i64, k: i64, subj: i64, pred: i64, conf: i64) -> i64 { 1739 let mp: *i64 = mindp(base) 1740 let s: *i64 = wst(base) 1741 let b: i64 = k*24 + 16 1742 var slot: i64 = 0 - 1 1743 var free: i64 = 0 - 1 1744 var oldest: i64 = 0 1745 var oldt: i64 = WATER_MAGIC_4611686018427387903 1746 var i: i64 = 0 1747 while i < 4 { 1748 let w: i64 = mp[b + i*2] 1749 if w == 0 { if free < 0 { free = i } } 1750 if w != 0 { if (w >> 16) == subj { if ((w >> 8) & 255) == pred { slot = i } } } 1751 if mp[b + i*2 + 1] < oldt { oldt = mp[b + i*2 + 1]; oldest = i } 1752 i = i + 1 1753 } 1754 if slot >= 0 { if (mp[b + slot*2] & 255) >= conf { return 0 } } 1755 if slot < 0 { slot = free } 1756 if slot < 0 { slot = oldest } 1757 mp[b + slot*2] = subj*WATER_MAGIC_65536 + pred*256 + conf 1758 mp[b + slot*2 + 1] = s[S_T] 1759 return 0 1760} 1761func go_newest(base: i64, k: i64) -> i64 { 1762 let mp: *i64 = mindp(base) 1763 let b: i64 = k*24 + 16 1764 var best: i64 = 0 - 1 1765 var bt: i64 = 0 - 1 1766 var i: i64 = 0 1767 while i < 4 { 1768 if mp[b + i*2] != 0 { if mp[b + i*2 + 1] > bt { bt = mp[b + i*2 + 1]; best = i } } 1769 i = i + 1 1770 } 1771 return best 1772} 1773 1774// mob wander: smooth deterministic drift (phase-driven sin/cos), terrain-following, player-curious 1775// within 6 blocks. Pure integer -- the T4 determinism checksum covers mobs by construction. 1776func mob_tick(base: i64) -> i64 { 1777 let m: *i64 = mobp(base) 1778 let s: *i64 = wst(base) 1779 let nd: *i64 = (base + O_NEED) as *i64 1780 let night: i64 = cal_night(base) 1781 var k: i64 = 0 1782 while k < en_count(m) { 1783 let h: i64 = en_nth(m, k) 1784 if h > 0 { 1785 var x: i64 = en_get(m, h, MC_X) 1786 var z: i64 = en_get(m, h, MC_Z) 1787 var ph: i64 = en_get(m, h, MC_PH) + 5 1788 // C1 NEEDS drift (integer, staggered by handle -- the village never moves in lockstep) 1789 let nb: i64 = k*4 1790 if (s[S_T] + h) % 8 == 0 { 1791 if nd[nb] < 1000 { nd[nb] = nd[nb] + 2 } // hunger grows 1792 if nd[nb + 2] < 1000 { nd[nb + 2] = nd[nb + 2] + 3 } // loneliness grows 1793 if nd[nb + 1] > 0 { nd[nb + 1] = nd[nb + 1] - 1 } // energy drains 1794 } 1795 // C3: lived moments become LEDGER ENTRIES (staggered; each refreshes in place) 1796 if (s[S_T] + h) % 128 == 0 { 1797 let bst0: i64 = (en_get(m, h, MC_DISG) >> 3) & 7 1798 if cal_rain(base) == 1 { if bst0 != 2 { th_add(base, k, TH_RAIN, 0 - 8, 1200) } } 1799 if nd[nb] > 700 { th_add(base, k, TH_HUNGRY, 0 - 10, 900) } 1800 if night == 1 { if bst0 == 2 { th_add(base, k, TH_SLEPT, 6, 3600) } } 1801 } 1802 // BEHAVIOR LAYER (C1/C2 -- lives picked by NEEDS x the spec ADVERTISEMENT table, 1803 // not a dice roll): state in MC_DISG bits 3-5, repicked on the staggered 240-tick 1804 // clock. 0=WANDER 1=SOCIALIZE 2=REST 3=FORAGE 4=FLEE. At night the village SLEEPS 1805 // (the C2 schedule) -- while the wardens hunt wider. The old spec-weights TODO is 1806 // RESOLVED: behavior now reads needs + P_ADV rows, both data. 1807 let dg: i64 = en_get(m, h, MC_DISG) 1808 var bst: i64 = (dg >> 3) & 7 1809 if (s[S_T] + h*37) % 240 == 0 { 1810 var sc1: i64 = 220 + (whash(h*911, s[S_T]/240, s[S_SEED]) % 64) 1811 // C3 feedback: the ledger BENDS the life -- a glad girl seeks company, a sad 1812 // one withdraws to rest. Mood is data the argmax reads, not a scripted branch. 1813 let mo9: i64 = th_mood(base, k) 1814 var sc2: i64 = nd[nb + 2] 1815 if mo9 > 0 { sc2 = sc2 + mo9 } 1816 var sc3: i64 = 1000 - nd[nb + 1] 1817 if mo9 < 0 { sc3 = sc3 - mo9*2 } 1818 var sc4: i64 = 0 1819 var ai: i64 = 0 1820 while ai < 6 { 1821 let ad: i64 = wsp(base)[P_ADV + ai] 1822 if ad != 0 { if ((ad >> 8) & 255) == 0 { 1823 let ab: i64 = (ad >> 16) & 255 1824 var fnd: i64 = 0 1825 var az2: i64 = (z >> 8) - 3 1826 while az2 <= (z >> 8) + 3 { 1827 var ax2: i64 = (x >> 8) - 3 1828 while ax2 <= (x >> 8) + 3 { 1829 let gy2: i64 = mob_ground(base, ax2, (en_get(m, h, MC_Y) >> 8) + 2, az2) 1830 if gy2 >= 0 { 1831 if vget(base, ax2, gy2, az2) == ab { fnd = 1 } 1832 if vget(base, ax2, gy2 + 1, az2) == ab { fnd = 1 } 1833 } 1834 ax2 = ax2 + 1 1835 } 1836 az2 = az2 + 1 1837 } 1838 if fnd == 1 { 1839 let cand: i64 = nd[nb]*(ad & 255)/8 1840 if cand > sc4 { sc4 = cand } 1841 } 1842 } } 1843 ai = ai + 1 1844 } 1845 bst = 0 1846 var top: i64 = sc1 1847 if sc2 > top { top = sc2; bst = 1 } 1848 if sc3 > top { top = sc3; bst = 2 } 1849 if sc4 > top { top = sc4; bst = 3 } 1850 if night == 1 { bst = 2 } 1851 en_set(m, h, MC_DISG, dg_bst(dg, bst)) 1852 } 1853 if (dg & 3) == 3 { 1854 // a hidden human checks for hunters -- night widens the danger radius 1855 var wr: i64 = 8*256 1856 if night == 1 { wr = 12*256 } 1857 var wnd: i64 = wr 1858 var wnx: i64 = 0 1859 var wnz: i64 = 0 1860 var kf: i64 = 0 1861 while kf < en_count(m) { 1862 let hf: i64 = en_nth(m, kf) 1863 if en_get(m, hf, MC_KIND) == 2 { if (en_get(m, hf, MC_DISG) & 1) == 0 { 1864 var fdx: i64 = en_get(m, hf, MC_X) - x 1865 if fdx < 0 { fdx = 0 - fdx } 1866 var fdz: i64 = en_get(m, hf, MC_Z) - z 1867 if fdz < 0 { fdz = 0 - fdz } 1868 if fdx + fdz < wnd { wnd = fdx + fdz; wnx = en_get(m, hf, MC_X); wnz = en_get(m, hf, MC_Z) } 1869 } } 1870 kf = kf + 1 1871 } 1872 if wnd < wr { bst = 4; en_set(m, h, MC_DISG, dg_bst(en_get(m, h, MC_DISG), 4)) } 1873 } 1874 var dx: i64 = it_sin4096(ph*3 % (2*IT_PI))*5/WATER_MAGIC_4096 1875 var dz: i64 = it_cos4096(ph*3 % (2*IT_PI))*5/WATER_MAGIC_4096 1876 if bst == 1 { 1877 // SOCIALIZE: walk to the nearest other girl; inside 1.2 blocks, stand together 1878 var snd: i64 = WATER_MAGIC_4611686018427387903 1879 var snx: i64 = x 1880 var snz: i64 = z 1881 var snk: i64 = 0 - 1 1882 var ks: i64 = 0 1883 while ks < en_count(m) { 1884 let hs: i64 = en_nth(m, ks) 1885 if hs != h { 1886 var sdx: i64 = en_get(m, hs, MC_X) - x 1887 if sdx < 0 { sdx = 0 - sdx } 1888 var sdz: i64 = en_get(m, hs, MC_Z) - z 1889 if sdz < 0 { sdz = 0 - sdz } 1890 if sdx + sdz < snd { snd = sdx + sdz; snx = en_get(m, hs, MC_X); snz = en_get(m, hs, MC_Z); snk = ks } 1891 } 1892 ks = ks + 1 1893 } 1894 dx = 0 1895 dz = 0 1896 if snd > 300 { 1897 if snx > x + 32 { dx = 4 } 1898 if snx < x - 32 { dx = 0 - 4 } 1899 if snz > z + 32 { dz = 4 } 1900 if snz < z - 32 { dz = 0 - 4 } 1901 } 1902 if snd <= 300 { 1903 ph = ph - 4 // stilled gait: talking, not marching 1904 if (s[S_T] + h) % 4 == 0 { // company relieves loneliness 1905 nd[nb + 2] = nd[nb + 2] - 10 1906 if nd[nb + 2] < 0 { nd[nb + 2] = 0 } 1907 } 1908 // C5: CONVERSATION MOVES FACTS. One fact passes speaker->listener with 1909 // confidence -15; below conf 30 the predicate may FLIP (1<->2) -- rumor 1910 // decays into misinformation BY MECHANIC, and hearing it leaves a thought. 1911 if (s[S_T] + h) % 128 == 0 { 1912 th_add(base, k, TH_COMPANY, 5, 1200) 1913 let gi: i64 = go_newest(base, k) 1914 if gi >= 0 { if snk >= 0 { 1915 let gw: i64 = mindp(base)[k*24 + 16 + gi*2] 1916 let conf2: i64 = (gw & 255) - 15 1917 if conf2 > 0 { 1918 var pred2: i64 = (gw >> 8) & 255 1919 if conf2 < 30 { if whash(s[S_T], k*31 + snk, s[S_SEED] + 777) % 3 == 0 { 1920 pred2 = 3 - pred2 1921 } } 1922 go_add(base, snk, gw >> 16, pred2, conf2) 1923 if pred2 == GO_TAKEN { th_add(base, snk, TH_HEARD_TAKEN, 0 - 10, 7200) } 1924 if pred2 == GO_FRIEND { th_add(base, snk, TH_HEARD_FRIEND, 6, 7200) } 1925 } 1926 } } 1927 } 1928 } 1929 } 1930 if bst == 2 { 1931 dx = 0 1932 dz = 0 1933 ph = ph - 4 // REST: seated stillness 1934 if (s[S_T] + h) % 4 == 0 { // sleep restores; night sleep restores MORE 1935 var rg: i64 = 2 1936 if night == 1 { rg = 8 } 1937 nd[nb + 1] = nd[nb + 1] + rg 1938 if nd[nb + 1] > 1000 { nd[nb + 1] = 1000 } 1939 } 1940 } 1941 if bst == 3 { 1942 // FORAGE: graze rhythm -- steps then a pause, head-down cadence; grazing feeds her 1943 if (s[S_T] + h) % 16 >= 6 { dx = 0; dz = 0; ph = ph - 3 } 1944 if (s[S_T] + h) % 8 == 0 { 1945 nd[nb] = nd[nb] - 12 1946 if nd[nb] < 0 { nd[nb] = 0 } 1947 } 1948 } 1949 if bst == 4 { 1950 // FLEE: straight away from the nearest warden, fast 1951 var wnd2: i64 = WATER_MAGIC_4611686018427387903 1952 var wax: i64 = x 1953 var waz: i64 = z 1954 var kf2: i64 = 0 1955 while kf2 < en_count(m) { 1956 let hf2: i64 = en_nth(m, kf2) 1957 if en_get(m, hf2, MC_KIND) == 2 { if (en_get(m, hf2, MC_DISG) & 1) == 0 { 1958 var fdx2: i64 = en_get(m, hf2, MC_X) - x 1959 if fdx2 < 0 { fdx2 = 0 - fdx2 } 1960 var fdz2: i64 = en_get(m, hf2, MC_Z) - z 1961 if fdz2 < 0 { fdz2 = 0 - fdz2 } 1962 if fdx2 + fdz2 < wnd2 { wnd2 = fdx2 + fdz2; wax = en_get(m, hf2, MC_X); waz = en_get(m, hf2, MC_Z) } 1963 } } 1964 kf2 = kf2 + 1 1965 } 1966 dx = 0 1967 dz = 0 1968 if wax > x { dx = 0 - 6 } 1969 if wax <= x { dx = 6 } 1970 if waz > z { dz = 0 - 6 } 1971 if waz <= z { dz = 6 } 1972 } 1973 // PERSONALITY (genome bits 13-15, INHERITED): the bold<->shy spectrum photographers 1974 // know -- engagement is a RANGE, not one script. Near the player: shy girls (0-1) 1975 // retreat and keep 5 blocks; watchful ones (2-3) freeze and observe, backing off 1976 // only if crowded; warm ones (4-5) approach to a polite arm's length; bold ones 1977 // (6-7) come right to your side. FORAGE also reads it: bold girls gather TOWARD 1978 // you (the obvious pick-up), shy girls drift their gathering away (the oblivious). 1979 let pdx: i64 = s[S_CX] - x 1980 let pdz: i64 = s[S_CZ] - z 1981 var pd: i64 = pdx 1982 if pd < 0 { pd = 0 - pd } 1983 var pdz2: i64 = pdz 1984 if pdz2 < 0 { pdz2 = 0 - pdz2 } 1985 let bold: i64 = (en_get(m, h, MC_GENE) >> 13) & 7 1986 // HABITUATION: the approach/watch drive runs only while she is lonely enough to 1987 // care (the player is a smart object too -- company below relieves the need, and a 1988 // satisfied girl walks on). Shy retreat is FEAR, not curiosity: never gated. 1989 var curi: i64 = 1 1990 if nd[nb + 2] < wsp(base)[P_CURI] { curi = 0 } 1991 if bold <= 1 { curi = 1 } 1992 if curi == 1 { if bst != 2 { if bst != 4 { if pd + pdz2 < 6*256 { 1993 var twd: i64 = 0 // -1 retreat, 0 hold, +1 approach 1994 var stopd: i64 = 400 1995 var spd: i64 = 3 1996 if bold <= 1 { twd = 0 - 1; stopd = 5*256; spd = 5 } 1997 if bold >= 2 { if bold <= 3 { 1998 twd = 0 1999 if pd + pdz2 < 2*256 { twd = 0 - 1; stopd = 2*256; spd = 4 } 2000 } } 2001 if bold >= 4 { if bold <= 5 { twd = 1; stopd = 400; spd = 3 } } 2002 if bold >= 6 { twd = 1; stopd = 200; spd = 5 } 2003 if bst == 3 { if twd != 0 { spd = 2 } } // gathering: the drift is subtle 2004 if twd == 1 { if pd + pdz2 > stopd { 2005 // S1 F-FORMATION SLOT (the social-interaction brief, rung S1): her approach 2006 // target is HER slot on the ring around the player, never the player center 2007 // -- N approaching girls used to converge on one point and bunch into a 2008 // queue ("they just follow me around"). Slot angle = a stable hash of her 2009 // handle; radius = her OWN personality stop distance, so bold girls ring 2010 // close and warm girls ring at arm's length (proxemics stays personal). 2011 let sa9: i64 = (h*1741) % (2*IT_PI) 2012 let tx9: i64 = s[S_CX] + it_cos4096(sa9)*stopd/WATER_MAGIC_4096 2013 let tz9: i64 = s[S_CZ] + it_sin4096(sa9)*stopd/WATER_MAGIC_4096 2014 let sdx9: i64 = tx9 - x 2015 let sdz9: i64 = tz9 - z 2016 dx = 0 2017 if sdx9 > 128 { dx = spd } 2018 if sdx9 < 0 - 128 { dx = 0 - spd } 2019 dz = 0 2020 if sdz9 > 128 { dz = spd } 2021 if sdz9 < 0 - 128 { dz = 0 - spd } 2022 } } 2023 if twd == 1 { if pd + pdz2 <= stopd { if bst == 0 { dx = 0; dz = 0; ph = ph - 4 } } } 2024 if twd == 0 - 1 { if pd + pdz2 < stopd { 2025 dx = spd 2026 if pdx > 0 { dx = 0 - spd } 2027 dz = spd 2028 if pdz > 0 { dz = 0 - spd } 2029 } } 2030 if twd == 0 { if bst == 0 { dx = 0; dz = 0; ph = ph - 4 } } // the watcher stands 2031 } } } } 2032 // RELATIONSHIP (the FLIAW arc): shared time within arm's reach becomes FRIENDSHIP, 2033 // stored in MC_DISG bits 6-13 (cap 250) -- persisted with the mob arena, so a bond 2034 // survives every save/load. Meeting her (E) adds a step-change on top. 2035 if pd + pdz2 < 3*256 { if (s[S_T] + h) % 64 == 0 { 2036 let dgf: i64 = en_get(m, h, MC_DISG) 2037 if ((dgf >> 6) & 255) < 250 { en_set(m, h, MC_DISG, dgf + 64) } 2038 } } 2039 let nx: i64 = x + dx 2040 let nz: i64 = z + dz 2041 let cy0: i64 = en_get(m, h, MC_Y) 2042 let g: i64 = mob_ground(base, nx >> 8, (cy0 >> 8) + 1, nz >> 8) 2043 var ok: i64 = 0 2044 if g >= 1 { if g < WY - 4 { 2045 let ng: i64 = (g + 1)*256 2046 var dyy: i64 = ng - cy0 2047 if dyy < 0 { dyy = 0 - dyy } 2048 if dyy <= 384 { if mob_fits(base, nx >> 8, g, nz >> 8) == 1 { 2049 ok = 1 2050 en_set(m, h, MC_X, nx) 2051 en_set(m, h, MC_Z, nz) 2052 // STEP, don't teleport: Y eases toward the stand height (<=48 Q8 up / 2053 // 64 down per tick) so a 1-block rise reads as ~5 ticks of CLIMBING, 2054 // not a vertical pop (screenshot-caught; deterministic either way) 2055 var ny9: i64 = ng 2056 if ng > cy0 + WC_STEP_EASE_UP { ny9 = cy0 + WC_STEP_EASE_UP } 2057 if ng < cy0 - WC_STEP_EASE_DN { ny9 = cy0 - WC_STEP_EASE_DN } 2058 en_set(m, h, MC_Y, ny9) 2059 } } 2060 } } 2061 let aux: *i64 = (base + O_MOBAUX) as *i64 2062 if ok == 1 { aux[k*3] = dx; aux[k*3 + 1] = dz; aux[k*3 + 2] = en_get(m, h, MC_Y) - cy0 } 2063 if ok == 0 { aux[k*3] = 0; aux[k*3 + 1] = 0; aux[k*3 + 2] = 0; ph = ph + 977 } // wall/cliff: turn away 2064 en_set(m, h, MC_PH, ph) 2065 // A4: the soft-body springs tick from ROOT TRUTH + HER GAIT: the anchor carries a 2066 // cadence-coupled thorax bob (published gait biomechanics -- vertical thorax 2067 // oscillation is what drives chest motion; a flat anchor was why nothing moved), 2068 // and the chest band is BRED (genome bits 16-17: firm rides tight, soft swings). 2069 // ★THE SOLVER AND THE INSTRUMENT MUST SHARE ONE REFERENCE (fixed 2026-08-03). 2070 // This tick used to drive the spring toward a RAW gait sinusoid while sbyb and every 2071 // probe subtracted mob_anchor_q8 -- the gait<->breath crossfade. Their difference rode 2072 // the rendered offset as a spurious term, and because ph advances +5 EVERY TICK FOREVER 2073 // the raw bob never quiets, so a STANDING girl still bounced: the 2026-08-02 breath fix 2074 // landed in the INSTRUMENT path and never reached the PHYSICS path. (The comment that 2075 // stood here claimed the bob 'quiets when she stands'. It does not. A comment asserting 2076 // a behaviour the code does not have is a landmine, so it is deleted with the defect.) 2077 // Driving the solver from the SAME anchor the instrument reads makes rest-bouncing 2078 // impossible BY CONSTRUCTION rather than by tuning. At walking speed the crossfade 2079 // weight saturates and the anchor EQUALS the old raw bob exactly => no-op in motion, 2080 // fix at rest: the safest shape a drive change can have. 2081 // ⚠STILL OPEN (debt 1785770635): ph is a FREE-RUNNING clock, so the drive is a fixed 2082 // 2.50Hz regardless of her real cadence while the render clip is DISTANCE-synced. Until 2083 // ph advances from travel, foot plant and tissue drive remain uncorrelated -- which is 2084 // the operator's actual report and is NOT closed by this change. 2085 sb_tick_mob_firm((base + O_SOFT) as *i64, k, 2086 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, 2087 (en_get(m, h, MC_GENE) >> 16) & 3) 2088 } 2089 k = k + 1 2090 } 2091 // THE STAKES (losability doctrine: a find that cannot be lost is hollow): WARDENS -- real 2092 // demonkin -- hunt FOUND humans. Reached = TAKEN (the certified store's destroy, live in 2093 // production). The player's BODY blocks a warden within 2 blocks. One take per tick, max. 2094 var kw: i64 = 0 2095 var took: i64 = 0 2096 while kw < en_count(m) { 2097 if took == 0 { 2098 let hw: i64 = en_nth(m, kw) 2099 if en_get(m, hw, MC_KIND) == 2 { if (en_get(m, hw, MC_DISG) & 1) == 0 { 2100 var bestj: i64 = 0 - 1 2101 // the world declares its danger (P_WARD), night widens it, and the ARRIVING 2102 // TRAVELER'S LEVEL widens it further: a veteran isekai's stakes rise with him 2103 var wbase: i64 = wsp(base)[P_WARD] 2104 if wbase <= 0 { wbase = 12 } 2105 var bestd: i64 = wbase*256 + pass_level_of(base)*128 2106 if night == 1 { bestd = bestd + 8*256 } // the night belongs to the wardens 2107 var kj: i64 = 0 2108 while kj < en_count(m) { 2109 let hj: i64 = en_nth(m, kj) 2110 if (en_get(m, hj, MC_DISG) & 3) == 3 { 2111 var ddx2: i64 = en_get(m, hj, MC_X) - en_get(m, hw, MC_X) 2112 if ddx2 < 0 { ddx2 = 0 - ddx2 } 2113 var ddz2: i64 = en_get(m, hj, MC_Z) - en_get(m, hw, MC_Z) 2114 if ddz2 < 0 { ddz2 = 0 - ddz2 } 2115 if ddx2 + ddz2 < bestd { bestd = ddx2 + ddz2; bestj = kj } 2116 } 2117 kj = kj + 1 2118 } 2119 if bestj >= 0 { 2120 var pdx3: i64 = s[S_CX] - en_get(m, hw, MC_X) 2121 if pdx3 < 0 { pdx3 = 0 - pdx3 } 2122 var pdz3: i64 = s[S_CZ] - en_get(m, hw, MC_Z) 2123 if pdz3 < 0 { pdz3 = 0 - pdz3 } 2124 if pdx3 + pdz3 > 2*256 { // your presence stays her hand 2125 let hj2: i64 = en_nth(m, bestj) 2126 if bestd < 220 { 2127 // C3/C5: a TAKING is WITNESSED -- every girl within 16 blocks 2128 // carries the grief as a thought and the fact as gossip (source = 2129 // her own eyes, conf 100). The village REMEMBERS its losses. 2130 let tkx: i64 = en_get(m, hj2, MC_X) 2131 let tkz: i64 = en_get(m, hj2, MC_Z) 2132 var tkk: i64 = 0 - 1 2133 var kwi: i64 = 0 2134 while kwi < en_count(m) { 2135 if en_nth(m, kwi) == hj2 { tkk = kwi } 2136 kwi = kwi + 1 2137 } 2138 en_destroy(m, hj2) 2139 s[S_LOST] = s[S_LOST] + 1 2140 var kwj: i64 = 0 2141 while kwj < en_count(m) { 2142 let hwj: i64 = en_nth(m, kwj) 2143 if hwj > 0 { 2144 var wdx: i64 = en_get(m, hwj, MC_X) - tkx 2145 if wdx < 0 { wdx = 0 - wdx } 2146 var wdz: i64 = en_get(m, hwj, MC_Z) - tkz 2147 if wdz < 0 { wdz = 0 - wdz } 2148 if wdx + wdz < 16*256 { 2149 th_add(base, kwj, TH_SAWTAKEN, 0 - 25, 14400) 2150 if tkk >= 0 { go_add(base, kwj, tkk, GO_TAKEN, 100) } 2151 } 2152 } 2153 kwj = kwj + 1 2154 } 2155 took = 1 2156 } 2157 if bestd >= 220 { if took == 0 { 2158 let txq: i64 = en_get(m, hj2, MC_X) 2159 let tzq: i64 = en_get(m, hj2, MC_Z) 2160 var wv: i64 = 4 2161 if night == 1 { wv = 7 } // she closes faster in the dark 2162 var mx3: i64 = 0 2163 if txq > en_get(m, hw, MC_X) + 32 { mx3 = wv } 2164 if txq < en_get(m, hw, MC_X) - 32 { mx3 = 0 - wv } 2165 var mz3: i64 = 0 2166 if tzq > en_get(m, hw, MC_Z) + 32 { mz3 = wv } 2167 if tzq < en_get(m, hw, MC_Z) - 32 { mz3 = 0 - wv } 2168 en_set(m, hw, MC_X, en_get(m, hw, MC_X) + mx3) 2169 en_set(m, hw, MC_Z, en_get(m, hw, MC_Z) + mz3) 2170 } } 2171 } 2172 } 2173 } } 2174 } 2175 kw = kw + 1 2176 } 2177 // INHERITANCE (the past-DAZ lever, live): two MET girls keeping close company bear a 2178 // DAUGHTER -- her genome FIELD-CROSSED from both parents (the breeders gcross pattern), 2179 // spawned through the certified store (capacity-bounded), cooldown-paced. Display cap 12. 2180 if s[S_BCD] > 0 { s[S_BCD] = s[S_BCD] - 1 } 2181 if s[S_BCD] == 0 { if en_count(m) < MOB_CAP { 2182 var ka: i64 = 0 2183 var born: i64 = 0 2184 while ka < en_count(m) { 2185 if born == 0 { 2186 let ha: i64 = en_nth(m, ka) 2187 if (en_get(m, ha, MC_DISG) & 2) == 2 { 2188 var kb2: i64 = ka + 1 2189 while kb2 < en_count(m) { 2190 if born == 0 { 2191 let hb2: i64 = en_nth(m, kb2) 2192 if (en_get(m, hb2, MC_DISG) & 2) == 2 { 2193 var bdx4: i64 = en_get(m, ha, MC_X) - en_get(m, hb2, MC_X) 2194 if bdx4 < 0 { bdx4 = 0 - bdx4 } 2195 var bdz4: i64 = en_get(m, ha, MC_Z) - en_get(m, hb2, MC_Z) 2196 if bdz4 < 0 { bdz4 = 0 - bdz4 } 2197 if bdx4 + bdz4 < 400 { 2198 let hn: i64 = en_spawn(m) 2199 if hn > 0 { 2200 let ga: i64 = en_get(m, ha, MC_GENE) 2201 let gb: i64 = en_get(m, hb2, MC_GENE) 2202 let pick: i64 = whash(ga, gb, s[S_T]) 2203 var gn: i64 = 0 2204 var fm: i64 = 0 2205 while fm < 8 { 2206 var msk: i64 = 7 2207 if fm == 1 { msk = 24 } 2208 if fm == 2 { msk = 96 } 2209 if fm == 3 { msk = 384 } 2210 if fm == 4 { msk = WATER_MAGIC_1536 } 2211 if fm == 5 { msk = WATER_MAGIC_6144 } 2212 if fm == 6 { msk = WATER_MAGIC_57344 } // bits 13-15: TEMPERAMENT (bold<->shy) is bred too 2213 if fm == 7 { msk = 983040 } // bits 16-19: BODY NATURE (firmness 16-17 + 2214 // beachwear nature 18-19) bred as one field -- 2215 // un-crossed genome bits default every daughter 2216 // to one body (the class this row eats); pick 2217 // has only 8 hash bits, so 16-19 share fm 7 2218 if ((pick >> fm) & 1) == 1 { gn = gn | (ga & msk) } 2219 if ((pick >> fm) & 1) == 0 { gn = gn | (gb & msk) } 2220 fm = fm + 1 2221 } 2222 en_set(m, hn, MC_GENE, gn) 2223 en_set(m, hn, MC_X, (en_get(m, ha, MC_X) + en_get(m, hb2, MC_X))/2) 2224 en_set(m, hn, MC_Y, en_get(m, ha, MC_Y)) 2225 en_set(m, hn, MC_Z, (en_get(m, ha, MC_Z) + en_get(m, hb2, MC_Z))/2) 2226 var kk: i64 = en_get(m, ha, MC_KIND) 2227 if ((pick >> 6) & 1) == 1 { kk = en_get(m, hb2, MC_KIND) } 2228 en_set(m, hn, MC_KIND, kk) 2229 let da: i64 = en_get(m, ha, MC_DISG) & 1 2230 let db: i64 = en_get(m, hb2, MC_DISG) & 1 2231 var dn: i64 = 0 2232 if da == 1 { if db == 1 { dn = 1 } } 2233 if da != db { dn = (pick >> 7) & 1 } 2234 en_set(m, hn, MC_DISG, dn) 2235 en_set(m, hn, MC_PH, whash(gn, pick, s[S_T]) % WATER_MAGIC_4096) 2236 // seat the newborn's springs + needs (dense tail slot) 2237 let nk9: i64 = en_count(m) - 1 2238 sb_seat_mob((base + O_SOFT) as *i64, nk9, 2239 en_get(m, hn, MC_X)/4, en_get(m, hn, MC_Y)/4, en_get(m, hn, MC_Z)/4) 2240 let nd9: *i64 = (base + O_NEED) as *i64 2241 nd9[nk9*4] = 400 2242 nd9[nk9*4 + 1] = 800 2243 nd9[nk9*4 + 2] = 500 2244 nd9[nk9*4 + 3] = 0 2245 s[S_BCD] = 900 2246 born = 1 2247 } 2248 } 2249 } 2250 } 2251 kb2 = kb2 + 1 2252 } 2253 } 2254 } 2255 ka = ka + 1 2256 } 2257 } } 2258 return 0 2259} 2260 2261// the full FPS control core: fwd/strafe movement, keyboard turn + analog mouselook, keyboard pitch, 2262// jump with real gravity (auto-step <=1.5 blocks stays; 2+ needs a jump), then targeting + edits. 2263func apply2_impl(base: i64, fwd: i64, strafe: i64, turn: i64, 2264 lookx: i64, looky: i64, pit: i64, jump: i64, brk: i64, plc: i64) -> i64 { 2265 let s: *i64 = wst(base) 2266 s[S_T] = s[S_T] + 1 2267 s[S_YAW] = s[S_YAW] + turn*TURN + lookx*LOOKS 2268 if s[S_YAW] < 0 { s[S_YAW] = s[S_YAW] + 2*IT_PI } 2269 if s[S_YAW] >= 2*IT_PI { s[S_YAW] = s[S_YAW] - 2*IT_PI } 2270 s[S_PITCH] = wclamp(s[S_PITCH] + pit*PITCHV - looky*LOOKS, 0 - PITCH_MAX, PITCH_MAX) 2271 let sy: i64 = it_sin4096(s[S_YAW]) 2272 let cy2: i64 = it_cos4096(s[S_YAW]) 2273 // horizontal: move vector = fwd*forward + strafe*right, axis-split against walls 2274 let mx: i64 = (sy*fwd + cy2*strafe)*SPEED/WATER_MAGIC_4096 2275 let mz: i64 = (cy2*fwd - sy*strafe)*SPEED/WATER_MAGIC_4096 2276 if mx != 0 { 2277 let nx: i64 = s[S_CX] + mx 2278 if wstand(base, nx, s[S_CZ], s[S_CY]) >= 0 { s[S_CX] = nx } 2279 } 2280 if mz != 0 { 2281 let nz: i64 = s[S_CZ] + mz 2282 if wstand(base, s[S_CX], nz, s[S_CY]) >= 0 { s[S_CZ] = nz } 2283 } 2284 // vertical: jump + gravity against the current column's stand height (replaces the glide) 2285 var vy: i64 = s[S_VY] 2286 if jump == 1 { if s[S_GROUNDED] == 1 { vy = JUMPV } } 2287 vy = vy - GRAV 2288 if vy < 0 - 120 { vy = 0 - 120 } // terminal velocity 2289 var ny: i64 = s[S_CY] + vy 2290 if vy > 0 { // ceiling while rising 2291 let hc: i64 = (ny + 128) >> 8 2292 if vsolid(base, s[S_CX] >> 8, hc, s[S_CZ] >> 8) == 1 { ny = s[S_CY]; vy = 0 } 2293 } 2294 let stand: i64 = wstand_here(base, s[S_CX], s[S_CZ]) 2295 s[S_GROUNDED] = 0 2296 if ny <= stand { ny = stand; vy = 0; s[S_GROUNDED] = 1 } 2297 s[S_VY] = vy 2298 s[S_CY] = ny 2299 // void-fall rescue: below the world floor (edge slip despite the wall) -> respawn, never vanish 2300 if ny < 0 - 512 { wc_respawn(base) } 2301 if wground(base, s[S_CX] >> 8, s[S_CZ] >> 8) < 0 { wc_respawn(base) } 2302 // crosshair target: one ray straight along the view 2303 let sp: i64 = it_sin4096(s[S_PITCH]) 2304 let cp: i64 = it_cos4096(s[S_PITCH]) 2305 let fx: i64 = sy*cp/WATER_MAGIC_4096 2306 let fy: i64 = sp 2307 let fz: i64 = cy2*cp/WATER_MAGIC_4096 2308 let hout: *i64 = (base + O_ST + 40*8) as *i64 // scratch slots 40-44 (wray writes FIVE outs; 2309 let hs: i64 = wray(base, fx*FOC, fy*FOC, fz*FOC, REACH_STEPS, hout) // 56+ overflowed into O_INPUT once) 2310 s[S_NPCT] = npc_pick(base, fx, fy, fz) 2311 // BODY PRESENCE: push the player out of any girl's personal cylinder (no ghost-walking) 2312 let mB: *i64 = mobp(base) 2313 var kB: i64 = 0 2314 while kB < en_count(mB) { 2315 let hB: i64 = en_nth(mB, kB) 2316 let bdx: i64 = s[S_CX] - en_get(mB, hB, MC_X) 2317 let bdz: i64 = s[S_CZ] - en_get(mB, hB, MC_Z) 2318 var adx: i64 = bdx 2319 if adx < 0 { adx = 0 - adx } 2320 var adz: i64 = bdz 2321 if adz < 0 { adz = 0 - adz } 2322 // SAME-ALTITUDE ONLY: a girl in a cave below (or ledge above) must NOT wall you off up 2323 // here -- the "invisible walls" bug was cylinders with no vertical extent 2324 var ady: i64 = s[S_CY] - EYE - en_get(mB, hB, MC_Y) 2325 if ady < 0 { ady = 0 - ady } 2326 if ady < 460 { if adx < 100 { if adz < 100 { 2327 if adx > adz { 2328 if bdx > 0 { s[S_CX] = en_get(mB, hB, MC_X) + 100 } 2329 if bdx <= 0 { s[S_CX] = en_get(mB, hB, MC_X) - 100 } 2330 } 2331 if adx <= adz { 2332 if bdz > 0 { s[S_CZ] = en_get(mB, hB, MC_Z) + 100 } 2333 if bdz <= 0 { s[S_CZ] = en_get(mB, hB, MC_Z) - 100 } 2334 } 2335 } } } 2336 kB = kB + 1 2337 } 2338 s[S_HOK] = 0 2339 if hs > 0 { 2340 s[S_HX] = hout[0] 2341 s[S_HY] = hout[1] 2342 s[S_HZ] = hout[2] 2343 s[S_HFACE] = hout[3] 2344 s[S_HOK] = 1 2345 } 2346 // THE INTERACT VERB: E on a targeted girl MEETS her (block-breaking is suppressed -- you are 2347 // talking to a person, not mining through her). Meeting marks her met-bit; a disguised human, 2348 // once met, is FOUND and counted (the identify-and-protect loop of the lore). 2349 var dobrk: i64 = brk 2350 if brk == 1 { if s[S_NPCT] >= 0 { 2351 dobrk = 0 2352 let mV: *i64 = mobp(base) 2353 let hV: i64 = en_nth(mV, s[S_NPCT]) 2354 let dv: i64 = en_get(mV, hV, MC_DISG) 2355 if (dv & 2) == 0 { 2356 var dvn: i64 = dv | 2 2357 if ((dvn >> 6) & 255) <= 240 { dvn = dvn + 640 } // meeting her: +10 friendship 2358 en_set(mV, hV, MC_DISG, dvn) 2359 if (dv & 1) == 1 { s[S_FOUND] = s[S_FOUND] + 1 } 2360 // C3/C5: the meeting lands in HER ledger, and the fact of it enters the village's 2361 // gossip graph with herself as subject -- her story spreads without another line 2362 th_add(base, s[S_NPCT], TH_MET, 15, 14400) 2363 go_add(base, s[S_NPCT], s[S_NPCT], GO_FRIEND, 100) 2364 } 2365 } } 2366 if dobrk == 1 { if s[S_HOK] == 1 { if s[S_HY] > 0 { 2367 let bwas: i64 = vget(base, s[S_HX], s[S_HY], s[S_HZ]) 2368 if wc_edit(base, s[S_HX], s[S_HY], s[S_HZ], 0) == 1 { 2369 if bwas == B_CROP2 { s[S_HARV] = s[S_HARV] + 1 } // the HARVEST: the loop pays out 2370 s[S_LBX] = s[S_HX] 2371 s[S_LBY] = s[S_HY] 2372 s[S_LBZ] = s[S_HZ] 2373 s[S_BROKEN] = s[S_BROKEN] + 1 2374 s[S_HOK] = 0 2375 } 2376 } } } 2377 if plc == 1 { if s[S_HOK] == 1 { 2378 let want: i64 = sel_block(s[S_SEL]) 2379 if want == B_FARM { if s[S_HFACE] == 3 { 2380 // the HOE: tilling converts the TARGETED grass/dirt block itself -- no new block 2381 let tb: i64 = vget(base, s[S_HX], s[S_HY], s[S_HZ]) 2382 if tb == 1 { if wc_edit(base, s[S_HX], s[S_HY], s[S_HZ], B_FARM) == 1 { s[S_PLACED] = s[S_PLACED] + 1 } } 2383 if tb == 2 { if wc_edit(base, s[S_HX], s[S_HY], s[S_HZ], B_FARM) == 1 { s[S_PLACED] = s[S_PLACED] + 1 } } 2384 } } 2385 if want == B_CROP0 { if s[S_HFACE] == 3 { if s[S_HY] + 1 < WY { 2386 // SEEDS: only into the air directly above targeted farmland (wet or dry) 2387 let sb2: i64 = vget(base, s[S_HX], s[S_HY], s[S_HZ]) 2388 var okf: i64 = 0 2389 if sb2 == B_FARM { okf = 1 } 2390 if sb2 == B_FARMW { okf = 1 } 2391 if okf == 1 { if vget(base, s[S_HX], s[S_HY] + 1, s[S_HZ]) == 0 { 2392 if wc_edit(base, s[S_HX], s[S_HY] + 1, s[S_HZ], B_CROP0) == 1 { 2393 s[S_PLANT] = s[S_PLANT] + 1 2394 s[S_PLACED] = s[S_PLACED] + 1 2395 } 2396 } } 2397 } } } 2398 if want != B_FARM { if want != B_CROP0 { 2399 var px: i64 = s[S_HX] 2400 var py: i64 = s[S_HY] 2401 var pz: i64 = s[S_HZ] 2402 let f: i64 = s[S_HFACE] 2403 if f == 1 { px = px + 1 } 2404 if f == 2 { px = px - 1 } 2405 if f == 3 { py = py + 1 } 2406 if f == 4 { py = py - 1 } 2407 if f == 5 { pz = pz + 1 } 2408 if f == 6 { pz = pz - 1 } 2409 var okp: i64 = 0 2410 if vin(px, py, pz) == 1 { if vget(base, px, py, pz) == 0 { okp = 1 } } 2411 // never inside the player's own two cells 2412 if px == s[S_CX] >> 8 { if pz == s[S_CZ] >> 8 { 2413 let feet: i64 = (s[S_CY] - EYE) >> 8 2414 if py >= feet { if py <= feet + 1 { okp = 0 } } 2415 } } 2416 if okp == 1 { 2417 if wc_edit(base, px, py, pz, want) == 1 { 2418 s[S_PLACED] = s[S_PLACED] + 1 2419 if py > s[S_WMAX] { s[S_WMAX] = py } // a tower can rise past the old ceiling 2420 } 2421 } 2422 } } 2423 } } 2424 mob_tick(base) 2425 // B1/B2: the farm breathes on its own clock (soil hydration + crop growth, journal-driven). 2426 // P_GROWE <= 0 = a recombined identity with no farming: the clock simply never fires. 2427 let ge9: i64 = wsp(base)[P_GROWE] 2428 if ge9 >= 1 { if s[S_T] % ge9 == 0 { wc_farm_tick(base) } } 2429 // v6 STREAMING: approach a window margin -> the world slides under you (unbounded land). 2430 // Runs after the whole frame so every in-frame coordinate was consistent; one stride per 2431 // axis per tick is plenty at player speeds. 2432 if (s[S_CX] >> 8) >= WX - SHIFT_MARGIN { wc_shift(base, SHIFT_STRIDE, 0) } 2433 if (s[S_CX] >> 8) < SHIFT_MARGIN { wc_shift(base, 0 - SHIFT_STRIDE, 0) } 2434 if (s[S_CZ] >> 8) >= WZ - SHIFT_MARGIN { wc_shift(base, 0, SHIFT_STRIDE) } 2435 if (s[S_CZ] >> 8) < SHIFT_MARGIN { wc_shift(base, 0, 0 - SHIFT_STRIDE) } 2436 return 0 2437} 2438 2439func wc_cycle(base: i64) -> i64 { 2440 let s: *i64 = wst(base) 2441 s[S_SEL] = (s[S_SEL] + 1) % 7 2442 return s[S_SEL] 2443} 2444 2445// adaptive quality: the SURFACE reports how long its last frame took (transport only); the ORGAN 2446// owns the response. EMA x16 with hysteresis bands so it never flaps: sustained >24ms coarsens, 2447// sustained <11ms refines. Returns the current ray pixel size. 2448func wc_framems(base: i64, ms: i64) -> i64 { 2449 let s: *i64 = wst(base) 2450 var m2: i64 = ms 2451 if m2 < 0 { m2 = 0 } 2452 if m2 > 100 { m2 = 100 } 2453 s[S_FMA] = (s[S_FMA]*3 + m2*16)/4 2454 if s[S_FMA] > 384 { if s[S_Q] < 5 { // >24ms sustained 2455 s[S_Q] = s[S_Q] + 1 2456 s[S_FMA] = 256 2457 } } 2458 if s[S_FMA] < 176 { if s[S_Q] > 1 { // <11ms sustained -> refine, down to NATIVE res 2459 s[S_Q] = s[S_Q] - 1 2460 s[S_FMA] = 256 2461 } } 2462 return s[S_Q] 2463} 2464 2465// ---------- persistence: a sovereign save image (the nx_gamesave laws, wasm-compatible) ---------- 2466// [0]=magic [1]=version [2]=seed [3..7]=camx/y/z/yaw/pitch [8]=sel [9]=broken [10]=placed 2467// [11]=checksum (order-sensitive rolling fold, 63-bit masked) then the raw voxel bytes. 2468// The surface (file on native, localStorage in the browser) moves BYTES ONLY; meaning stays here. 2469// seq1151 (2026-07-30): the rolling-fold mechanism now composes nx_gamesave_core -- ONE definition 2470// shared with the gs_save file surface; this format's constants stay ITS OWN (image bytes unchanged, 2471// golden gate checksums are the acceptance). 2472func wc_save_ck(base: i64) -> i64 { 2473 let hd: *i64 = (base + O_SAVE) as *i64 2474 let vb: *u8 = (base + O_SAVE + SAVE_HDR) as *u8 2475 var ck: i64 = gsc_fold_words(WATER_MAGIC_1469598103, hd, 2, 11, WATER_MAGIC_1099511, WATER_MAGIC_4611686018427387903) 2476 ck = gsc_fold(ck, hd[12], WATER_MAGIC_1099511, WATER_MAGIC_4611686018427387903) 2477 ck = gsc_fold(ck, hd[13], WATER_MAGIC_1099511, WATER_MAGIC_4611686018427387903) 2478 ck = gsc_fold(ck, hd[14], WATER_MAGIC_1099511, WATER_MAGIC_4611686018427387903) 2479 ck = gsc_fold(ck, hd[15], WATER_MAGIC_1099511, WATER_MAGIC_4611686018427387903) 2480 ck = gsc_fold_bytes(ck, vb, SAVE_BODY, 131, WATER_MAGIC_4611686018427387903) 2481 return ck 2482} 2483// pre-streaming (v5) checksum geometry -- LOAD-COMPAT ONLY, so old saves keep opening 2484func wc_save_ck_v5(base: i64) -> i64 { 2485 let hd: *i64 = (base + O_SAVE) as *i64 2486 let vb: *u8 = (base + O_SAVE + SAVE_HDR) as *u8 2487 var ck: i64 = gsc_fold_words(WATER_MAGIC_1469598103, hd, 2, 11, WATER_MAGIC_1099511, WATER_MAGIC_4611686018427387903) 2488 ck = gsc_fold(ck, hd[12], WATER_MAGIC_1099511, WATER_MAGIC_4611686018427387903) 2489 ck = gsc_fold(ck, hd[13], WATER_MAGIC_1099511, WATER_MAGIC_4611686018427387903) 2490 ck = gsc_fold_bytes(ck, vb, SAVE_BODY_V5, 131, WATER_MAGIC_4611686018427387903) 2491 return ck 2492} 2493// build the image from live state; returns its byte length 2494func wc_save_build(base: i64) -> i64 { 2495 let s: *i64 = wst(base) 2496 let hd: *i64 = (base + O_SAVE) as *i64 2497 hd[0] = SAVE_MAGIC 2498 hd[1] = SAVE_VER 2499 hd[2] = s[S_SEED] 2500 hd[3] = s[S_CX] 2501 hd[4] = s[S_CY] 2502 hd[5] = s[S_CZ] 2503 hd[6] = s[S_YAW] 2504 hd[7] = s[S_PITCH] 2505 hd[8] = s[S_SEL] 2506 hd[9] = s[S_BROKEN] 2507 hd[10] = s[S_PLACED] 2508 hd[12] = s[S_FOUND] 2509 hd[13] = s[S_LOST] 2510 hd[14] = s[S_WOX] 2511 hd[15] = s[S_WOZ] 2512 let vb: *u8 = (base + O_SAVE + SAVE_HDR) as *u8 2513 let vx: *u8 = wvx(base) 2514 var p: i64 = 0 2515 while p < WX*WY*WZ { vb[p] = vx[p]; p = p + 1 } 2516 let mb: *u8 = (base + O_MOBS) as *u8 2517 var q: i64 = 0 2518 while q < MOB_BYTES { vb[WX*WY*WZ + q] = mb[q]; q = q + 1 } 2519 let eb: *u8 = (base + O_EJRN) as *u8 2520 var r: i64 = 0 2521 while r < EJRN_BYTES { vb[WX*WY*WZ + MOB_BYTES + r] = eb[r]; r = r + 1 } 2522 hd[11] = wc_save_ck(base) 2523 return SAVE_LEN 2524} 2525// validate the image in place, then restore world + camera from it. NOTHING is touched on refusal. 2526// returns 0, or negative: -1 magic, -2 version, -3 checksum 2527func wc_save_apply(base: i64) -> i64 { 2528 let hd: *i64 = (base + O_SAVE) as *i64 2529 if hd[0] != SAVE_MAGIC { return 0 - 1 } 2530 var v5: i64 = 0 2531 if hd[1] == SAVE_VER5 { v5 = 1 } 2532 if hd[1] != SAVE_VER { if v5 == 0 { return 0 - 2 } } 2533 let want: i64 = hd[11] 2534 if v5 == 0 { if wc_save_ck(base) != want { return 0 - 3 } } 2535 if v5 == 1 { if wc_save_ck_v5(base) != want { return 0 - 3 } } 2536 let s: *i64 = wst(base) 2537 s[S_SEED] = hd[2] 2538 s[S_CX] = hd[3] 2539 s[S_CY] = hd[4] 2540 s[S_CZ] = hd[5] 2541 s[S_YAW] = hd[6] 2542 s[S_PITCH] = hd[7] 2543 s[S_SEL] = hd[8] 2544 s[S_BROKEN] = hd[9] 2545 s[S_PLACED] = hd[10] 2546 s[S_FOUND] = hd[12] 2547 s[S_LOST] = hd[13] 2548 s[S_WOX] = 0 2549 s[S_WOZ] = 0 2550 if v5 == 0 { s[S_WOX] = hd[14]; s[S_WOZ] = hd[15] } 2551 s[S_VY] = 0 2552 let vb: *u8 = (base + O_SAVE + SAVE_HDR) as *u8 2553 let vx: *u8 = wvx(base) 2554 var p: i64 = 0 2555 while p < WX*WY*WZ { vx[p] = vb[p]; p = p + 1 } 2556 let mb: *u8 = (base + O_MOBS) as *u8 2557 var q: i64 = 0 2558 while q < MOB_BYTES { mb[q] = vb[WX*WY*WZ + q]; q = q + 1 } 2559 // journal: v6 restores it from the image; a v5 save simply has no touched-land history yet 2560 let eb: *u8 = (base + O_EJRN) as *u8 2561 var r: i64 = 0 2562 if v5 == 0 { 2563 while r < EJRN_BYTES { eb[r] = vb[WX*WY*WZ + MOB_BYTES + r]; r = r + 1 } 2564 } else { 2565 while r < EJRN_BYTES { eb[r] = 0 as u8; r = r + 1 } 2566 } 2567 wc_wmax(base) // the restored world's ceiling, not the fresh one's 2568 wc_soft_seat_all(base) // springs + needs are NON-PERSISTED: re-derive 2569 wc_need_seed(base) 2570 wc_mind_zero(base) // a loaded world wakes with a clear head 2571 return 0 2572} 2573 2574// legacy 6-arg form: forwards into the full core (no strafe/look/jump). Gate callers use this. 2575func apply_impl(base: i64, fwd: i64, turn: i64, pit: i64, brk: i64, plc: i64) -> i64 { 2576 return apply2_impl(base, fwd, 0, turn, 0, 0, pit, 0, brk, plc) 2577} 2578 2579// legacy single-action tick (gate + old shim): 0 fwd 1 back 2 turnL 3 turnR 4 break 5 place 2580func tick_impl(base: i64, d: i64) -> i64 { 2581 var fwd: i64 = 0 2582 var turn: i64 = 0 2583 var brk: i64 = 0 2584 var plc: i64 = 0 2585 if d == 0 { fwd = 1 } 2586 if d == 1 { fwd = 0 - 1 } 2587 if d == 2 { turn = 0 - 1 } 2588 if d == 3 { turn = 1 } 2589 if d == 4 { brk = 1 } 2590 if d == 5 { plc = 1 } 2591 if d == 6 { wc_cycle(base) } 2592 return apply_impl(base, fwd, turn, 0, brk, plc) 2593} 2594 2595// multi-action input frame, FPS scheme: W/S forward/back + A/D (and arrows) STRAFE from HELD, 2596// yaw from the analog LOOK channel (mouse), pitch from mouse + R/F, jump held (auto-rehop on 2597// landing, Minecraft-style), break/place from PRESSED edges so they cannot repeat-fire. 2598func step_impl(base: i64) -> i64 { 2599 let q: *i64 = winp(base) 2600 let held: i64 = ia_held(q) 2601 let pressed: i64 = ia_frame(q) 2602 var fwd: i64 = 0 2603 if (held & IA_UP) != 0 { fwd = 1 } 2604 if (held & IA_DOWN) != 0 { fwd = fwd - 1 } 2605 var strafe: i64 = 0 2606 if (held & IA_RIGHT) != 0 { strafe = 1 } 2607 if (held & IA_LEFT) != 0 { strafe = strafe - 1 } 2608 var pit: i64 = 0 2609 if (held & IA_LUP) != 0 { pit = 1 } 2610 if (held & IA_LDOWN) != 0 { pit = pit - 1 } 2611 var jump: i64 = 0 2612 if (held & IA_JUMP) != 0 { jump = 1 } 2613 var brk: i64 = 0 2614 if (pressed & IA_A) != 0 { brk = 1 } 2615 var plc: i64 = 0 2616 if (pressed & IA_B) != 0 { plc = 1 } 2617 if (pressed & IA_MOD) != 0 { wc_cycle(base) } // Q taps cycle the hotbar (edge, no auto-repeat) 2618 if (pressed & IA_HOME) != 0 { wc_respawn(base) } // H = the reset button 2619 let lk: *i64 = (base + O_INPUT + 62*8) as *i64 // scratch beyond the ia arena's live slots 2620 ia_look_take(q, lk) 2621 return apply2_impl(base, fwd, strafe, 0, lk[0], lk[1], pit, jump, brk, plc) 2622} 2623 2624// mob billboards: project into the camera basis, occlusion by ONE ray per mob (budgeted to stop 2625// just short of the mob's cell -- a solid hit before that means hidden). An approximation, stated 2626// as such: no per-pixel z against terrain; correct enough at 12 mobs and zero fps cost. 2627func mob_col(base: i64, kind: i64, half: i64) -> i64 { 2628 var k2: i64 = kind 2629 if k2 < 0 { k2 = 0 } 2630 if k2 > 2 { k2 = 2 } 2631 return wsp(base)[P_MOBC + k2*2 + half] 2632} 2633func mob_draw(base: i64, fxv: i64, fyv: i64, fzv: i64, rxv: i64, rzv: i64, 2634 uxv: i64, uyv: i64, uzv: i64) -> i64 { 2635 let m: *i64 = mobp(base) 2636 let s: *i64 = wst(base) 2637 let f: *i64 = wfb(base) 2638 let tmp: *i64 = (base + O_ST + 32*8) as *i64 // scratch slots 32-36 (5 wray outs) 2639 var k: i64 = 0 2640 while k < en_count(m) { 2641 let h: i64 = en_nth(m, k) 2642 if h > 0 { 2643 let bob: i64 = it_sin4096((en_get(m, h, MC_PH)*9) % (2*IT_PI))*22/WATER_MAGIC_4096 2644 let dxq: i64 = en_get(m, h, MC_X) - s[S_CX] 2645 let dyq: i64 = en_get(m, h, MC_Y) + 128 + bob - s[S_CY] 2646 let dzq: i64 = en_get(m, h, MC_Z) - s[S_CZ] 2647 let vz: i64 = (dxq*fxv + dyq*fyv + dzq*fzv)/WATER_MAGIC_4096 2648 var vis: i64 = 0 2649 if vz > 200 { if vz < 40*256 { vis = 1 } } 2650 if vis == 1 { 2651 var man: i64 = dxq 2652 if man < 0 { man = 0 - man } 2653 var t2: i64 = dyq 2654 if t2 < 0 { t2 = 0 - t2 } 2655 man = man + t2 2656 t2 = dzq 2657 if t2 < 0 { t2 = 0 - t2 } 2658 man = (man + t2) >> 8 2659 if man > 2 { 2660 if wray(base, dxq, dyq, dzq, man - 1, tmp) > 0 { vis = 0 } 2661 } 2662 } 2663 if vis == 1 { 2664 let vx: i64 = (dxq*rxv + dzq*rzv)/WATER_MAGIC_4096 2665 let vy: i64 = (dxq*uxv + dyq*uyv + dzq*uzv)/WATER_MAGIC_4096 2666 let sx: i64 = W/2 + vx*FOC*RES/vz 2667 let sy: i64 = H/2 - vy*FOC*RES/vz 2668 var halfw: i64 = 96*FOC*RES/vz 2669 if halfw > 90 { halfw = 90 } 2670 if halfw >= 2 { 2671 let kind: i64 = en_get(m, h, MC_KIND) 2672 let hh2: i64 = halfw*5/4 2673 var yy: i64 = 0 - hh2 2674 while yy <= hh2 { 2675 var xx: i64 = 0 - halfw 2676 while xx <= halfw { 2677 let px2: i64 = sx + xx 2678 let py2: i64 = sy + yy 2679 if px2 >= 0 { if px2 < W { if py2 >= 0 { if py2 < H { 2680 var hf: i64 = 0 2681 if yy > 0 { hf = 1 } 2682 f[py2*W + px2] = mob_col(base, kind, hf) 2683 } } } } 2684 xx = xx + 1 2685 } 2686 yy = yy + 1 2687 } 2688 // eyes so it reads as a creature, not a box 2689 if halfw >= 6 { 2690 let ex2: i64 = halfw/2 2691 var e3: i64 = 0 2692 while e3 < 2 { 2693 var ei: i64 = 0 - 1 2694 while ei <= 0 { 2695 let epx: i64 = sx - ex2 + e3*(ex2*2) + ei 2696 let epy: i64 = sy - hh2/2 2697 if epx >= 0 { if epx < W { if epy >= 0 { if epy < H { 2698 f[epy*W + epx] = wpack(24, 20, 34) 2699 f[(epy+1)*W + epx] = wpack(24, 20, 34) 2700 } } } } 2701 ei = ei + 1 2702 } 2703 e3 = e3 + 1 2704 } 2705 } 2706 } 2707 } 2708 } 2709 k = k + 1 2710 } 2711 return 0 2712} 2713 2714func render_impl(base: i64) -> i64 { 2715 let s: *i64 = wst(base) 2716 let f: *i64 = wfb(base) 2717 let sy: i64 = it_sin4096(s[S_YAW]) 2718 let cy2: i64 = it_cos4096(s[S_YAW]) 2719 let sp: i64 = it_sin4096(s[S_PITCH]) 2720 let cp: i64 = it_cos4096(s[S_PITCH]) 2721 // camera basis fx4096: forward (with pitch), right (flat), up (anti-pitch) 2722 let fxv: i64 = sy*cp/WATER_MAGIC_4096 2723 let fyv: i64 = sp 2724 let fzv: i64 = cy2*cp/WATER_MAGIC_4096 2725 let rxv: i64 = cy2 2726 let rzv: i64 = 0 - sy 2727 let uxv: i64 = 0 - sy*sp/WATER_MAGIC_4096 2728 let uyv: i64 = cp 2729 let uzv: i64 = 0 - cy2*sp/WATER_MAGIC_4096 2730 let hout: *i64 = (base + O_ST + 46*8) as *i64 // scratch slots 46-50: wray writes FIVE outs; 2731 // the old slot-60 scratch put out[4] at slot 64 = O_INPUT[0] = the KEYBOARD MASK -- every pixel 2732 // stomped a crossing-t into held keys (5 bits stuck, pitch pinned skyward, 15fps of sky rays) 2733 // adaptive quality: ray pixel size q (2..5); fov held constant by foc = W/(2q) 2734 var q: i64 = s[S_Q] 2735 if q < 1 { q = 1 } 2736 if q > 5 { q = 5 } 2737 let rw: i64 = W/q 2738 let rh: i64 = H/q 2739 let foc: i64 = W/(2*q) 2740 // project the sun direction through the camera basis (a billboard at infinity) 2741 let SUX: i64 = WATER_MAGIC_2400 2742 let SUY: i64 = WATER_MAGIC_2500 2743 let SUZ: i64 = WATER_MAGIC_1400 2744 let svz: i64 = (SUX*fxv + SUY*fyv + SUZ*fzv)/WATER_MAGIC_4096 2745 var sunon: i64 = 0 2746 var sunx: i64 = 0 2747 var suny: i64 = 0 2748 if wsp(base)[P_SUN] == 1 { if svz > 400 { // spec decides whether this world HAS a sun 2749 sunon = 1 2750 sunx = rw/2 + (SUX*rxv + SUZ*rzv)/WATER_MAGIC_4096*foc/svz 2751 suny = rh/2 - (SUX*uxv + SUY*uyv + SUZ*uzv)/WATER_MAGIC_4096*foc/svz 2752 } } 2753 let cloudon: i64 = wsp(base)[P_CLOUD] 2754 var py: i64 = 0 2755 while py < rh { 2756 let syy: i64 = rh/2 - py 2757 var px: i64 = 0 2758 while px < rw { 2759 let sxx: i64 = px - rw/2 2760 let dx: i64 = fxv*foc + rxv*sxx + uxv*syy 2761 let dy: i64 = fyv*foc + uyv*syy 2762 let dz: i64 = fzv*foc + rzv*sxx + uzv*syy 2763 let hs: i64 = wray(base, dx, dy, dz, MAXSTEPS, hout) 2764 var col: i64 = 0 2765 if hs < 0 { 2766 col = wsky2(base, syy, px, py, sunx, suny, sunon) 2767 var cov: i64 = 0 2768 if cloudon == 1 { cov = wcloud(s[S_CX], s[S_CY], s[S_CZ], dx, dy, dz, s[S_T]) } 2769 if cov > 0 { 2770 var cr: i64 = (col & 255) + (250 - (col & 255))*cov/255 2771 var cg: i64 = ((col >> 8) & 255) + (252 - ((col >> 8) & 255))*cov/255 2772 var cb: i64 = ((col >> 16) & 255) + (254 - ((col >> 16) & 255))*cov/255 2773 col = wpack(cr, cg, cb) 2774 } 2775 } 2776 if hs >= 0 { 2777 let t2: i64 = hout[4] 2778 let hpx: i64 = s[S_CX] + dx*t2/WATER_MAGIC_65536 2779 let hpy: i64 = s[S_CY] + dy*t2/WATER_MAGIC_65536 2780 let hpz: i64 = s[S_CZ] + dz*t2/WATER_MAGIC_65536 2781 let hb: i64 = vget(base, hout[0], hout[1], hout[2]) 2782 col = wshade2(base, hb, hout[3], hs, syy, hpx, hpy, hpz, s[S_T], hout[0], hout[1], hout[2]) 2783 if hb == 4 { 2784 // WATER IS TRANSPARENT: continue the ray past water, blend 42% of what lies under 2785 let hs2: i64 = wray2(base, dx, dy, dz, MAXSTEPS, hout, 1) 2786 if hs2 >= 0 { 2787 let t3: i64 = hout[4] 2788 let qx: i64 = s[S_CX] + dx*t3/WATER_MAGIC_65536 2789 let qy: i64 = s[S_CY] + dy*t3/WATER_MAGIC_65536 2790 let qz: i64 = s[S_CZ] + dz*t3/WATER_MAGIC_65536 2791 let under: i64 = wshade2(base, vget(base, hout[0], hout[1], hout[2]), hout[3], hs2, syy, qx, qy, qz, s[S_T], hout[0], hout[1], hout[2]) 2792 var br2: i64 = (col & 255)*58/100 + (under & 255)*42/100 2793 var bg2: i64 = ((col >> 8) & 255)*58/100 + ((under >> 8) & 255)*42/100 2794 var bb2: i64 = ((col >> 16) & 255)*58/100 + ((under >> 16) & 255)*42/100 2795 col = wpack(br2, bg2, bb2) 2796 } 2797 } 2798 } 2799 // write the qxq block 2800 let ox: i64 = px*q 2801 let oy: i64 = py*q 2802 var yy: i64 = 0 2803 while yy < q { 2804 var xx: i64 = 0 2805 while xx < q { 2806 f[(oy + yy)*W + ox + xx] = col 2807 xx = xx + 1 2808 } 2809 yy = yy + 1 2810 } 2811 px = px + 1 2812 } 2813 py = py + 1 2814 } 2815 mob_draw(base, fxv, fyv, fzv, rxv, rzv, uxv, uyv, uzv) 2816 // crosshair: white, or gold when a block is in reach 2817 var cc: i64 = wpack(255, 255, 255) 2818 if s[S_HOK] == 1 { cc = wpack(255, 210, 90) } 2819 var ci: i64 = 0 - 6 2820 while ci <= 6 { 2821 f[(H/2)*W + W/2 + ci] = cc 2822 f[(H/2 + ci)*W + W/2] = cc 2823 ci = ci + 1 2824 } 2825 // HUD pips: broken (red) and placed (green) counters, capped at 24 each 2826 var bq: i64 = 0 2827 while bq < s[S_BROKEN] { if bq < 24 { 2828 f[8*W + 8 + bq*7] = wpack(235, 96, 96) 2829 f[8*W + 9 + bq*7] = wpack(235, 96, 96) 2830 f[9*W + 8 + bq*7] = wpack(235, 96, 96) 2831 f[9*W + 9 + bq*7] = wpack(235, 96, 96) 2832 } bq = bq + 1 } 2833 var pq: i64 = 0 2834 while pq < s[S_PLACED] { if pq < 24 { 2835 f[14*W + 8 + pq*7] = wpack(120, 235, 130) 2836 f[14*W + 9 + pq*7] = wpack(120, 235, 130) 2837 f[15*W + 8 + pq*7] = wpack(120, 235, 130) 2838 f[15*W + 9 + pq*7] = wpack(120, 235, 130) 2839 } pq = pq + 1 } 2840 // hotbar: 7 swatches bottom-left (5 blocks + hoe + seeds), the selected one framed white 2841 var hb: i64 = 0 2842 while hb < 7 { 2843 let bx0: i64 = 8 + hb*18 2844 let by0: i64 = H - 22 2845 let bc: i64 = wcol(base, sel_block(hb)) 2846 var yy2: i64 = 0 2847 while yy2 < 14 { 2848 var xx2: i64 = 0 2849 while xx2 < 14 { f[(by0 + yy2)*W + bx0 + xx2] = bc; xx2 = xx2 + 1 } 2850 yy2 = yy2 + 1 2851 } 2852 if hb == s[S_SEL] { 2853 var e2: i64 = 0 - 2 2854 while e2 <= 15 { 2855 f[(by0 - 2)*W + bx0 + e2] = wpack(255,255,255) 2856 f[(by0 + 15)*W + bx0 + e2] = wpack(255,255,255) 2857 if e2 >= 0 { if e2 < 14 { 2858 f[(by0 + e2)*W + bx0 - 2] = wpack(255,255,255) 2859 f[(by0 + e2)*W + bx0 + 15] = wpack(255,255,255) 2860 } } 2861 e2 = e2 + 1 2862 } 2863 } 2864 hb = hb + 1 2865 } 2866 return 0 2867} 2868 2869// ---------- the exported wasm surface (base = 0 in linear memory) ---------- 2870func init() -> i64 { return init_impl(0, WATER_MAGIC_20260728) } 2871func init_seed(sd: i64) -> i64 { return init_impl(0, sd) } 2872func init_v(v: i64, sd: i64) -> i64 { return init_impl_v(0, v, sd) } 2873func tick(d: i64) -> i64 { return tick_impl(0, d) } 2874func render() -> i64 { return render_impl(0) } 2875func fb_off() -> i64 { return O_FB } 2876func ww() -> i64 { return W } 2877func hh() -> i64 { return H } 2878func broken() -> i64 { let s: *i64 = wst(0); return s[S_BROKEN] } 2879func placed() -> i64 { let s: *i64 = wst(0); return s[S_PLACED] } 2880func camx() -> i64 { let s: *i64 = wst(0); return s[S_CX] } 2881func camy() -> i64 { let s: *i64 = wst(0); return s[S_CY] } 2882func camz() -> i64 { let s: *i64 = wst(0); return s[S_CZ] } 2883// device doors (nx_input_abstract): the shim forwards RAW events; meaning lives sovereign-side 2884func key(code: i64, down: i64) -> i64 { return ia_key(winp(0), code, down) } 2885func padb(btn: i64, down: i64) -> i64 { return ia_pad(winp(0), btn, down) } 2886func paxis(idx: i64, milli: i64) -> i64 { return ia_axis(winp(0), idx, milli) } 2887func touch(tx: i64, ty: i64, down: i64) -> i64 { return ia_touch(winp(0), tx, ty, W, H, down) } 2888func look(dx: i64, dy: i64) -> i64 { return ia_look(winp(0), dx, dy) } 2889func step() -> i64 { return step_impl(0) } 2890// persistence doors: the surface moves save-image BYTES; build/validate/apply stay sovereign 2891func save_off() -> i64 { return O_SAVE } 2892// v6 streaming doors (probe truth: where the window sits over the unbounded world, and whether 2893// any edit was ever refused by a full journal -- silence here would be a lie) 2894func wox() -> i64 { let s: *i64 = wst(0); return s[S_WOX] } 2895func woz() -> i64 { let s: *i64 = wst(0); return s[S_WOZ] } 2896func ejdrop() -> i64 { let s: *i64 = wst(0); return s[S_EJDROP] } 2897func save_build() -> i64 { return wc_save_build(0) } 2898func save_apply() -> i64 { return wc_save_apply(0) } 2899func sel() -> i64 { let s: *i64 = wst(0); return s[S_SEL] } 2900func hx() -> i64 { let s: *i64 = wst(0); return s[S_HX] } 2901func hy() -> i64 { let s: *i64 = wst(0); return s[S_HY] } 2902func hz() -> i64 { let s: *i64 = wst(0); return s[S_HZ] } 2903func lbx() -> i64 { let s: *i64 = wst(0); return s[S_LBX] } 2904func lby() -> i64 { let s: *i64 = wst(0); return s[S_LBY] } 2905func lbz() -> i64 { let s: *i64 = wst(0); return s[S_LBZ] } 2906func frame_ms(ms: i64) -> i64 { return wc_framems(0, ms) } 2907func quality() -> i64 { let s: *i64 = wst(0); return s[S_Q] } 2908func work_ms() -> i64 { let s: *i64 = wst(0); return s[S_FMA]/16 } 2909func mobs() -> i64 { return en_count(mobp(0)) } 2910func mobx(k: i64) -> i64 { let m: *i64 = mobp(0); return en_get(m, en_nth(m, k), MC_X) } 2911func mobz(k: i64) -> i64 { let m: *i64 = mobp(0); return en_get(m, en_nth(m, k), MC_Z) } 2912// GPU-display doors: the shader is only a RENDERER of this organ's state -- it reads the voxel 2913// bytes and spec palette straight from linear memory and gets camera/mob state per frame 2914func moby(k: i64) -> i64 { let m: *i64 = mobp(0); return en_get(m, en_nth(m, k), MC_Y) } 2915func mobk(k: i64) -> i64 { let m: *i64 = mobp(0); return en_get(m, en_nth(m, k), MC_KIND) } 2916func mobd(k: i64) -> i64 { let m: *i64 = mobp(0); return en_get(m, en_nth(m, k), MC_DISG) } 2917func mobg(k: i64) -> i64 { let m: *i64 = mobp(0); return en_get(m, en_nth(m, k), MC_GENE) } 2918func npc_t() -> i64 { let s: *i64 = wst(0); return s[S_NPCT] } 2919func found() -> i64 { let s: *i64 = wst(0); return s[S_FOUND] } 2920func lost() -> i64 { let s: *i64 = wst(0); return s[S_LOST] } 2921func mobph(k: i64) -> i64 { let m: *i64 = mobp(0); return en_get(m, en_nth(m, k), MC_PH) } 2922// motion doors (seq1258): the girl's LAST APPLIED step -- heading/speed as ORGAN TRUTH, 2923// retiring the page's position-delta derivative. Zero when blocked/still. 2924func mobdx(k: i64) -> i64 { let a: *i64 = (0 + O_MOBAUX) as *i64; return a[k*3] } 2925func mobdz(k: i64) -> i64 { let a: *i64 = (0 + O_MOBAUX) as *i64; return a[k*3 + 1] } 2926func mobdy(k: i64) -> i64 { let a: *i64 = (0 + O_MOBAUX) as *i64; return a[k*3 + 2] } 2927// SUMMON door (operator review + the many-NPC perf test): teleport every girl into a grounded 2928// ring 4-7 blocks around the player. A door like look/padb -- the page binds N to it; nothing 2929// in the tick calls it, so determinism (T4) and the save format are untouched. They resume 2930// their behavior states immediately after placement. 2931func summon() -> i64 { 2932 let m: *i64 = mobp(0) 2933 let s: *i64 = wst(0) 2934 let n: i64 = en_count(m) 2935 if n <= 0 { return 0 } 2936 var k: i64 = 0 2937 while k < n { 2938 let h: i64 = en_nth(m, k) 2939 if h > 0 { 2940 let ang: i64 = k * 2 * IT_PI / n 2941 let r: i64 = (4 + (k & 3)) * 256 2942 var nx: i64 = s[S_CX] + it_sin4096(ang) * r / WATER_MAGIC_4096 2943 var nz: i64 = s[S_CZ] + it_cos4096(ang) * r / WATER_MAGIC_4096 2944 if nx < 512 { nx = 512 } 2945 if nx > (WX - 2) * 256 { nx = (WX - 2) * 256 } 2946 if nz < 512 { nz = 512 } 2947 if nz > (WZ - 2) * 256 { nz = (WZ - 2) * 256 } 2948 let gy: i64 = mob_ground(0, nx / 256, WY - 1, nz / 256) 2949 if gy >= 0 { if mob_fits(0, nx / 256, gy, nz / 256) == 1 { 2950 en_set(m, h, MC_X, nx) 2951 en_set(m, h, MC_Z, nz) 2952 en_set(m, h, MC_Y, (gy + 1) * 256) 2953 } } 2954 } 2955 k = k + 1 2956 } 2957 wc_soft_seat_all(0) // summon teleports every girl: re-seat springs 2958 return n 2959} 2960func yaw() -> i64 { let s: *i64 = wst(0); return s[S_YAW] } 2961func pitch() -> i64 { let s: *i64 = wst(0); return s[S_PITCH] } 2962func wmax() -> i64 { let s: *i64 = wst(0); return s[S_WMAX] } 2963func tsel() -> i64 { let s: *i64 = wst(0); return sel_block(s[S_SEL]) } 2964func vox_off() -> i64 { return O_VOX } 2965func spec_off() -> i64 { return O_SPEC } 2966func tnow() -> i64 { let s: *i64 = wst(0); return s[S_T] } 2967func hok() -> i64 { let s: *i64 = wst(0); return s[S_HOK] } 2968// ---- A4 doors: signed Q8 soft-body offsets (model units = worldQ8/4) per girl per point-kind 2969// (0 hair, 1 chest, 2 skirt). The page ADDS these to its joint bands -- helper offsets over the 2970// animation, never replacing it. sbck = the replay/netsync checksum over every spring word. 2971func sbx(k: i64, kd: i64) -> i64 { let m: *i64 = mobp(0); return sb_off_x_q8((0 + O_SOFT) as *i64, k, kd, en_get(m, en_nth(m, k), MC_X)/4) } 2972func sby(k: i64, kd: i64) -> i64 { let m: *i64 = mobp(0); return sb_off_y_q8((0 + O_SOFT) as *i64, k, kd, en_get(m, en_nth(m, k), MC_Y)/4) } 2973func sbz(k: i64, kd: i64) -> i64 { let m: *i64 = mobp(0); return sb_off_z_q8((0 + O_SOFT) as *i64, k, kd, en_get(m, en_nth(m, k), MC_Z)/4) } 2974func sbck() -> i64 { return sb_ck((0 + O_SOFT) as *i64, MOB_CAP) } 2975// TISSUE-VS-BONE (the door renderers and the recorder must both use): offset from the TRUE 2976// anchor -- the one the solver ticked, INCLUDING the gait bob. Measured 2026-08-02: reading 2977// from the un-bobbed root handed the mesh bob + resonant lag = 20.1cm of chest travel on a 2978// torso that does not bob (drive 2.5Hz vs natural 2.75Hz => ~2.6x resonant gain). The bob's 2979// job is to DRIVE the spring; only the LAG belongs on the joint. Bob is a pure function of 2980// her stored phase, so this stays deterministic and needs no extra state. 2981func mob_bob_q8(k: i64) -> i64 { 2982 let m: *i64 = mobp(0) 2983 let ph: i64 = en_get(m, en_nth(m, k), MC_PH) 2984 return it_sin4096((ph*214) % (2*IT_PI))*WC_GAIT_BOB_Q8/WATER_MAGIC_4096 2985} 2986// RESPIRATION: one slow cycle, phase-offset per girl so a crowd never inhales in unison -- 2987// synchronised breathing is the tell that betrays a canned idle. Pure function of world tick 2988// and index, so it stays deterministic and needs no per-mob state. 2989// BASE-PARAMETERIZED IMPLS (doors below stay thin arena-0 wrappers): the gate verifies engine 2990// behavior on ITS OWN world arenas, and an arena-0-only door is a capability the gate cannot 2991// measure -- which, per the standing law, is indistinguishable from an absent one. 2992func mob_breath_q8i(bb: i64, k: i64) -> i64 { 2993 let s: *i64 = wst(bb) 2994 let ph: i64 = ((s[S_T] % WC_BREATH_TICKS)*2*IT_PI/WC_BREATH_TICKS + k*811) % (2*IT_PI) 2995 return it_sin4096(ph)*WC_BREATH_Q8/WATER_MAGIC_4096 2996} 2997func mob_breath_q8(k: i64) -> i64 { return mob_breath_q8i(0, k) } 2998// per-tick speed, exposed so the blend threshold can be MEASURED instead of guessed 2999func mob_spd_q8i(bb: i64, k: i64) -> i64 { 3000 let a: *i64 = (bb + O_MOBAUX) as *i64 3001 var dx: i64 = a[k*3] 3002 if dx < 0 { dx = 0 - dx } 3003 var dz: i64 = a[k*3 + 1] 3004 if dz < 0 { dz = 0 - dz } 3005 return dx + dz 3006} 3007func mob_spd_q8(k: i64) -> i64 { return mob_spd_q8i(0, k) } 3008// THE ANCHOR THE SPRING ACTUALLY SEES. Before this, the anchor was driven by GAIT PHASE alone -- 3009// and gait phase does not zero when she stops, so a standing girl was still being driven by a 3010// locomotion signal with no locomotion. That is exactly the operator's report: "she bounces 3011// while stopped instead of breathing". Two drives, cross-faded on speed: gait owns motion, 3012// respiration owns rest, and the anchor is never unforced. Cross-multiplied, never divided 3013// before compare, so the blend cannot silently truncate to zero. 3014func mob_anchor_q8i(bb: i64, k: i64) -> i64 { 3015 var w: i64 = mob_spd_q8i(bb, k)*WATER_MAGIC_4096/WC_BREATH_CUT_Q8 3016 if w > WATER_MAGIC_4096 { w = WATER_MAGIC_4096 } 3017 if w < 0 { w = 0 } 3018 let m: *i64 = mobp(bb) 3019 let ph: i64 = en_get(m, en_nth(m, k), MC_PH) 3020 let bob: i64 = it_sin4096((ph*214) % (2*IT_PI))*WC_GAIT_BOB_Q8/WATER_MAGIC_4096 3021 return (bob*w + mob_breath_q8i(bb, k)*(WATER_MAGIC_4096 - w))/WATER_MAGIC_4096 3022} 3023func mob_anchor_q8(k: i64) -> i64 { return mob_anchor_q8i(0, k) } 3024func sbyb(k: i64, kd: i64) -> i64 { 3025 let m: *i64 = mobp(0) 3026 return sb_off_y_q8((0 + O_SOFT) as *i64, k, kd, (en_get(m, en_nth(m, k), MC_Y) + mob_anchor_q8(k))/4) 3027} 3028// ---- A1 doors: the calendar as organ truth (the page only DRAWS day, night and weather) 3029func dayt() -> i64 { return cal_dayt(0) } 3030func dlen() -> i64 { return wsp(0)[P_DAYLEN] } 3031func dayn() -> i64 { return cal_day(0) } 3032func season() -> i64 { return cal_season(0) } 3033func nightf() -> i64 { return cal_night(0) } 3034func sunel() -> i64 { return cal_sunel(0) } 3035func rainf() -> i64 { return cal_rain(0) } 3036// ---- B doors: the farming loop's counters (till -> plant -> weather -> grow -> HARVEST) 3037func harv() -> i64 { let s: *i64 = wst(0); return s[S_HARV] } 3038func planted() -> i64 { let s: *i64 = wst(0); return s[S_PLANT] } 3039func grown() -> i64 { let s: *i64 = wst(0); return s[S_GROWN] } 3040// ---- C doors: relationships + needs + THE INNER LIFE (the emergent-story surface) 3041func mobfr(k: i64) -> i64 { let m: *i64 = mobp(0); return (en_get(m, en_nth(m, k), MC_DISG) >> 6) & 255 } 3042func mobmood(k: i64) -> i64 { return th_mood(0, k) } 3043func mobth(k: i64, i2: i64) -> i64 { 3044 var i3: i64 = i2 3045 if i3 < 0 { i3 = 0 } 3046 if i3 > 7 { i3 = 7 } 3047 let mp: *i64 = mindp(0) 3048 let s: *i64 = wst(0) 3049 if mp[k*24 + i3*2 + 1] <= s[S_T] { return 0 } 3050 return mp[k*24 + i3*2] 3051} 3052func mobgo(k: i64, i2: i64) -> i64 { 3053 var i3: i64 = i2 3054 if i3 < 0 { i3 = 0 } 3055 if i3 > 3 { i3 = 3 } 3056 return mindp(0)[k*24 + 16 + i3*2] 3057} 3058func mobout(k: i64) -> i64 { return mob_outfit(0, k) } 3059// ---- ISEKAI doors: the passport is BYTES the surface moves; meaning stays sovereign 3060func pass_off() -> i64 { return O_PASS } 3061func pass_len() -> i64 { return PASS_LEN } 3062func pass_build() -> i64 { return wc_pass_sync(0) } 3063func pass_apply() -> i64 { return wc_pass_apply(0) } 3064func pass_get(i2: i64) -> i64 { 3065 var i3: i64 = i2 3066 if i3 < 0 { i3 = 0 } 3067 if i3 > 23 { i3 = 23 } 3068 return passp(0)[i3] 3069} 3070// LEVEL: the lifetime ledger read as one number (deeds + lives touched + days), so a fresh 3071// world can scale its challenge to WHO ARRIVED (the isekai contract: nothing is wasted) 3072func pass_level() -> i64 { return pass_level_of(0) } 3073// PARTY: G invites the girl under the crosshair once her bond is real (>= 40); she carries 3074// bit 2 of MC_DISG from then on -- persisted in the world save AND written to the passport. 3075func party() -> i64 { return party_of(0) } 3076func partyn() -> i64 { return wc_party_count(0) } 3077func mobparty(k: i64) -> i64 { 3078 let m: *i64 = mobp(0) 3079 return (en_get(m, en_nth(m, k), MC_DISG) >> 2) & 1 3080} 3081func mobneed(k: i64, w: i64) -> i64 { 3082 var w2: i64 = w 3083 if w2 < 0 { w2 = 0 } 3084 if w2 > 3 { w2 = 3 } 3085 let nd0: *i64 = (0 + O_NEED) as *i64 3086 return nd0[k*4 + w2] 3087}