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}