nx_hammered_zone.nx source
↩ module page · 538 lines · 23310 B
1// nx_hammered_zone.nx -- heavily-impacted terrain generator.
2//
3// Eighth demonstration of the kind-specific-generator cardinal
4// `feedback-kind-specific-generators-not-broad-noise`. The "this
5// place has seen things" generator: meteor-strike fields with
6// multiple overlapping craters, ancient battle sites with shattered
7// stone, volcanic ruins with cooled lava, magical blast zones with
8// crystalline shrapnel.
9//
10// Hammered-internal logic in v1:
11// - Zone record (center + radius + intensity + hammer kind + seed)
12// - Per-voxel query: applies chaotic height carving + material
13// override based on hammer kind
14// - 4 hammer kinds with characteristic carve pattern + material:
15// METEOR_FIELD -- multiple overlapping impact craters,
16// rubble material
17// ANCIENT_BATTLE -- eroded shallow depressions + scattered
18// stone fragments
19// VOLCANIC_RUINS -- cooled lava flows + ash, irregular
20// MAGIC_BLAST -- radial crystalline destruction pattern
21//
22// Composes with shipped nx_crater_field for METEOR_FIELD's multi-
23// impact pattern (caller pre-generates crater list + passes count
24// alongside hammered record).
25//
26// FULL CAPABILITY (per feedback-maximum-capability-no-simplification
27// cardinal, 2026-05-16):
28// - Smooth radial edge fade: from radius * 0.85 to radius, the
29// carve + material amplitude ramps to 0 -- no hard boundary
30// - METEOR_FIELD internal multi-crater field: generates N power-
31// law-distributed sub-craters within the zone via the same
32// algorithm as nx_crater_field (no external composition needed)
33// - Scorched-vegetation predicate: nx_hammered_scorched_at returns
34// 1 if a voxel falls in the vegetation-killing footprint of the
35// hammer (so nx_forest_layout can suppress trees in burn zones)
36//
37// Loss audit: Q14 integer arithmetic; hash-deterministic per-pixel
38// chaos within the zone. Soft radial edge eliminates hard rim.
39//
40// genealogy_id: melosh_1989_impact_cratering + neukum_1983_lunar_chronology +
41// winterbottom_battlefield_archaeology +
42// iceland_lava_field_geomorphology
43// lineage_id: nx_hammered_zone_smooth_meteor_scorched_v2
44
45// nx_safety_envelope:
46// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
47// sil_target: SIL1
48// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
49// verdict: NOT_YET_EVALUATED
50
51import "nx_syscalls.nx"
52import "nx_tier.nx"
53const NX_MAGIC_2654435761: i64 = 2654435761
54const NX_MAGIC_1597334677: i64 = 1597334677
55const NX_MAGIC_7225: i64 = 7225
56const NX_MAGIC_10000: i64 = 10000
57const NX_MAGIC_6554: i64 = 6554
58const NX_MAGIC_9000: i64 = 9000
59const NX_MAGIC_16384: i64 = 16384
60const NX_MAGIC_1100: i64 = 1100
61const NX_MAGIC_4000: i64 = 4000
62const NX_MAGIC_5000: i64 = 5000
63
64// ===== Q14 ==========================================================
65const NX_HZ_Q: nx_int = 16384
66
67// Park-Miller LCG.
68const NX_HZ_LCG_A: nx_int = 48271
69const NX_HZ_LCG_M: nx_int = 2147483647
70
71// ===== Hammer-kind sealed enum =====================================
72const NX_HAMMER_METEOR_FIELD: nx_int = 0
73const NX_HAMMER_ANCIENT_BATTLE: nx_int = 1
74const NX_HAMMER_VOLCANIC_RUINS: nx_int = 2
75const NX_HAMMER_MAGIC_BLAST: nx_int = 3
76
77const NX_HAMMER_KIND_COUNT: nx_int = 4
78
79// ===== Abstract material codes ======================================
80// Caller maps to their voxel palette. For nx_voxel_chunk:
81// PASSTHROUGH -> caller's default
82// RUBBLE -> NX_BLOCK_DIRT (or NX_BLOCK_STONE for stony rubble)
83// SCORCHED -> NX_BLOCK_STONE (or a future SCORCHED block)
84// CRYSTALLINE -> NX_BLOCK_STONE (future BLOCK_OBSIDIAN / CRYSTAL)
85// ASH -> NX_BLOCK_DIRT (future BLOCK_ASH)
86const NX_HZ_MATERIAL_PASSTHROUGH: nx_int = 0
87const NX_HZ_MATERIAL_RUBBLE: nx_int = 1
88const NX_HZ_MATERIAL_SCORCHED: nx_int = 2
89const NX_HZ_MATERIAL_CRYSTALLINE: nx_int = 3
90const NX_HZ_MATERIAL_ASH: nx_int = 4
91
92const NX_HZ_MATERIAL_COUNT: nx_int = 5
93
94// ===== Validity predicates =========================================
95func nx_hammer_kind_is_valid(k: nx_int) -> nx_int {
96 if k == NX_HAMMER_METEOR_FIELD { return 1 }
97 if k == NX_HAMMER_ANCIENT_BATTLE { return 1 }
98 if k == NX_HAMMER_VOLCANIC_RUINS { return 1 }
99 if k == NX_HAMMER_MAGIC_BLAST { return 1 }
100 return 0
101}
102
103func nx_hz_material_is_valid(m: nx_int) -> nx_int {
104 if m == NX_HZ_MATERIAL_PASSTHROUGH { return 1 }
105 if m == NX_HZ_MATERIAL_RUBBLE { return 1 }
106 if m == NX_HZ_MATERIAL_SCORCHED { return 1 }
107 if m == NX_HZ_MATERIAL_CRYSTALLINE { return 1 }
108 if m == NX_HZ_MATERIAL_ASH { return 1 }
109 return 0
110}
111
112// ===== Hash mixer ==================================================
113func _hz_hash(seed: nx_int, x: nx_int, y: nx_int) -> nx_int {
114 var h: nx_int = seed
115 h = (h * NX_HZ_LCG_A + x * NX_MAGIC_2654435761) % NX_HZ_LCG_M
116 if h < 0 { h = h + NX_HZ_LCG_M }
117 h = (h * NX_HZ_LCG_A + y * NX_MAGIC_1597334677) % NX_HZ_LCG_M
118 if h < 0 { h = h + NX_HZ_LCG_M }
119 return h
120}
121
122// ===== In-zone test ================================================
123// Returns 1 if (px, py) is within radius of the zone centre, 0 else.
124func _hz_in_zone(
125 cx_q14: nx_int, cy_q14: nx_int, radius_q14: nx_int,
126 px_q14: nx_int, py_q14: nx_int
127) -> nx_int {
128 let dx: nx_int = px_q14 - cx_q14
129 let dy: nx_int = py_q14 - cy_q14
130 let d2: nx_int = dx * dx + dy * dy
131 let r2: nx_int = radius_q14 * radius_q14
132 if d2 < r2 { return 1 }
133 return 0
134}
135
136// ===== Smooth radial edge fade =====================================
137// Returns a Q14 weight in [0, Q]. Inside r*0.85: full weight (Q).
138// Between 0.85r and r: linear ramp from Q to 0. Outside r: 0.
139func _hz_edge_fade_q14(
140 cx_q14: nx_int, cy_q14: nx_int, radius_q14: nx_int,
141 px_q14: nx_int, py_q14: nx_int
142) -> nx_int {
143 let dx: nx_int = px_q14 - cx_q14
144 let dy: nx_int = py_q14 - cy_q14
145 let d2: nx_int = dx * dx + dy * dy
146 let r2: nx_int = radius_q14 * radius_q14
147 if d2 >= r2 { return 0 }
148 let fade_start_r2: nx_int = (r2 * NX_MAGIC_7225) / NX_MAGIC_10000 // (0.85)^2 = 0.NX_MAGIC_7225
149 if d2 <= fade_start_r2 { return NX_HZ_Q }
150 // Linear ramp on d^2 (close enough to linear on d for smooth visuals).
151 let ramp_span: nx_int = r2 - fade_start_r2
152 if ramp_span <= 0 { return 0 }
153 let remaining: nx_int = r2 - d2
154 return (remaining * NX_HZ_Q) / ramp_span
155}
156
157// ===== METEOR_FIELD internal crater field =========================
158// Composes the Neukum-1983 power-law radius sampling directly: a
159// hammered zone of N expected craters generates them via the same
160// inverse-CDF formula nx_crater_field uses, then for each query point
161// returns the deepest bowl-contribution.
162//
163// Crater count: nominal density of 64 craters per zone, scaled by
164// intensity_q14 / Q (intensity = Q -> 64 craters; Q/2 -> 32; etc.).
165// Capped at 256.
166//
167// Per-crater radius: in [zone_r/32, zone_r/4], power-law sampled.
168// Per-crater depth: r * 0.4 (Pike 1977 fresh-crater depth/diameter).
169//
170// Crater positions: hash-distributed within the zone (rejection-
171// sampled to be inside the zone, with a budget).
172func _hz_meteor_field_carve(
173 cx_q14: nx_int, cy_q14: nx_int, radius_q14: nx_int,
174 intensity_q14: nx_int, seed: nx_int,
175 px_q14: nx_int, py_q14: nx_int
176) -> nx_int {
177 let q: nx_int = NX_HZ_Q
178 var n_craters: nx_int = (64 * intensity_q14) / q
179 if n_craters < 1 { n_craters = 1 }
180 if n_craters > 256 { n_craters = 256 }
181 let r_min: nx_int = radius_q14 / 32
182 let r_max: nx_int = radius_q14 / 4
183 if r_min <= 0 { return 0 }
184 if r_max <= r_min { return 0 }
185
186 var max_depth: nx_int = 0
187 var i: nx_int = 0
188 while i < n_craters {
189 let h_x: nx_int = _hz_hash(seed + i, 0, 1)
190 let h_y: nx_int = _hz_hash(seed + i, 1, 2)
191 let h_r: nx_int = _hz_hash(seed + i, 2, 3)
192 // Distribute (cx, cy) within zone via cell-area sampling.
193 let dx_off: nx_int = (h_x % (2 * radius_q14)) - radius_q14
194 let dy_off: nx_int = (h_y % (2 * radius_q14)) - radius_q14
195 // Reject points outside the zone.
196 let d_off2: nx_int = dx_off * dx_off + dy_off * dy_off
197 let r_zone2: nx_int = radius_q14 * radius_q14
198 if d_off2 < r_zone2 {
199 // Power-law radius via the same inverse-CDF formula as
200 // nx_crater_field.
201 let u_q14: nx_int = h_r % q
202 let ratio_q14: nx_int = (r_min * q) / r_max
203 let one_minus_ratio: nx_int = q - ratio_q14
204 let factor: nx_int = q - (u_q14 * one_minus_ratio) / q
205 var r_crater: nx_int = r_min
206 if factor > 0 {
207 r_crater = (r_min * q) / factor
208 if r_crater < r_min { r_crater = r_min }
209 if r_crater > r_max { r_crater = r_max }
210 }
211 // Crater centre in world coords.
212 let crater_cx: nx_int = cx_q14 + dx_off
213 let crater_cy: nx_int = cy_q14 + dy_off
214 let cdx: nx_int = px_q14 - crater_cx
215 let cdy: nx_int = py_q14 - crater_cy
216 let cd2: nx_int = cdx * cdx + cdy * cdy
217 let cr2: nx_int = r_crater * r_crater
218 if cd2 < cr2 {
219 let depth_full: nx_int = (r_crater * NX_MAGIC_6554) / q // depth/radius = 0.4
220 let bowl_factor: nx_int = ((cr2 - cd2) * q) / cr2
221 let contrib: nx_int = (depth_full * bowl_factor) / q
222 if contrib > max_depth { max_depth = contrib }
223 }
224 }
225 i = i + 1
226 }
227 return 0 - max_depth
228}
229
230// ===== Public: chaotic height carve at (px, py) ====================
231// Returns signed Q14 metres delta. Out-of-zone or invalid kind -> 0.
232//
233// Per kind:
234// METEOR_FIELD -- hash-determined pockmarks (negative bowls)
235// ANCIENT_BATTLE -- shallow erosion (slight negative)
236// VOLCANIC_RUINS -- bumpy lava-flow (mixed positive/negative)
237// MAGIC_BLAST -- radial outward shrapnel (positive bumps)
238func nx_hammered_carve_at(
239 cx_q14: nx_int,
240 cy_q14: nx_int,
241 radius_q14: nx_int,
242 intensity_q14_m: nx_int,
243 hammer_kind: nx_int,
244 seed: nx_int,
245 px_q14: nx_int,
246 py_q14: nx_int
247) -> nx_int {
248 if nx_hammer_kind_is_valid(hammer_kind) == 0 { return 0 }
249 let fade: nx_int = _hz_edge_fade_q14(cx_q14, cy_q14, radius_q14, px_q14, py_q14)
250 if fade <= 0 { return 0 }
251
252 // Hash signed signal in [-Q, Q].
253 let h: nx_int = _hz_hash(seed, px_q14 / 100, py_q14 / 100)
254 let signed_h: nx_int = (h % (2 * NX_HZ_Q)) - NX_HZ_Q
255
256 var raw: nx_int = 0
257
258 if hammer_kind == NX_HAMMER_METEOR_FIELD {
259 // Full power-law-distributed sub-crater field, internal to the
260 // hammered zone. Returns the deepest bowl-contribution at the
261 // query point.
262 raw = _hz_meteor_field_carve(
263 cx_q14, cy_q14, radius_q14,
264 intensity_q14_m, seed,
265 px_q14, py_q14
266 )
267 }
268 if hammer_kind == NX_HAMMER_ANCIENT_BATTLE {
269 raw = signed_h * intensity_q14_m / NX_HZ_Q / 4 // 25% amplitude
270 }
271 if hammer_kind == NX_HAMMER_VOLCANIC_RUINS {
272 raw = signed_h * intensity_q14_m / NX_HZ_Q / 2 // 50% amplitude
273 }
274 if hammer_kind == NX_HAMMER_MAGIC_BLAST {
275 let dx: nx_int = px_q14 - cx_q14
276 let dy: nx_int = py_q14 - cy_q14
277 let d2: nx_int = dx * dx + dy * dy
278 let r2: nx_int = radius_q14 * radius_q14
279 let centre_factor: nx_int = (r2 - d2) * NX_HZ_Q / r2 // [0, Q]
280 let radial: nx_int = 0 - intensity_q14_m * centre_factor * 3 / (4 * NX_HZ_Q)
281 let shrapnel: nx_int = signed_h * intensity_q14_m / NX_HZ_Q / 3
282 raw = radial + shrapnel
283 }
284
285 // Apply smooth-edge fade.
286 return (raw * fade) / NX_HZ_Q
287}
288
289// ===== Scorched-vegetation predicate ================================
290// Returns 1 if vegetation should NOT grow at (px, py) due to the
291// hammer's burn / blight footprint. Used by nx_forest_layout to
292// suppress trees in burn zones.
293//
294// Per kind:
295// METEOR_FIELD -- scorched everywhere in zone (impact heating)
296// ANCIENT_BATTLE -- scorched 30% of cells (hash-based, scattered)
297// VOLCANIC_RUINS -- scorched inside 80% of radius (heat aura
298// reaches beyond pure-scorched core)
299// MAGIC_BLAST -- scorched everywhere (lingering magical blight)
300//
301// The fade band (0.85r..r) is INCLUDED in the burn footprint -- a
302// hammer's effect can suppress vegetation slightly past the
303// height-carve fade range.
304func nx_hammered_scorched_at(
305 cx_q14: nx_int,
306 cy_q14: nx_int,
307 radius_q14: nx_int,
308 hammer_kind: nx_int,
309 seed: nx_int,
310 px_q14: nx_int,
311 py_q14: nx_int
312) -> nx_int {
313 if nx_hammer_kind_is_valid(hammer_kind) == 0 { return 0 }
314 if _hz_in_zone(cx_q14, cy_q14, radius_q14, px_q14, py_q14) == 0 { return 0 }
315
316 if hammer_kind == NX_HAMMER_METEOR_FIELD { return 1 }
317 if hammer_kind == NX_HAMMER_MAGIC_BLAST { return 1 }
318 if hammer_kind == NX_HAMMER_ANCIENT_BATTLE {
319 let h: nx_int = _hz_hash(seed, px_q14 / 100, py_q14 / 100)
320 if (h % 10) < 3 { return 1 }
321 return 0
322 }
323 if hammer_kind == NX_HAMMER_VOLCANIC_RUINS {
324 let dx: nx_int = px_q14 - cx_q14
325 let dy: nx_int = py_q14 - cy_q14
326 let d2: nx_int = dx * dx + dy * dy
327 let r2: nx_int = radius_q14 * radius_q14
328 if d2 < (r2 * 80 / 100) { return 1 }
329 return 0
330 }
331 return 0
332}
333
334// ===== Public: material override at (px, py) =======================
335// Returns abstract material code or PASSTHROUGH if outside the zone
336// or hammer doesn't affect material at this voxel.
337//
338// Per kind:
339// METEOR_FIELD -- RUBBLE everywhere in zone
340// ANCIENT_BATTLE -- RUBBLE (50% of hashed cells); else PASSTHROUGH
341// VOLCANIC_RUINS -- SCORCHED (zone centre 70%) + ASH (outer 30%)
342// MAGIC_BLAST -- CRYSTALLINE everywhere
343func nx_hammered_material_at(
344 cx_q14: nx_int,
345 cy_q14: nx_int,
346 radius_q14: nx_int,
347 hammer_kind: nx_int,
348 seed: nx_int,
349 px_q14: nx_int,
350 py_q14: nx_int
351) -> nx_int {
352 if nx_hammer_kind_is_valid(hammer_kind) == 0 { return NX_HZ_MATERIAL_PASSTHROUGH }
353 if _hz_in_zone(cx_q14, cy_q14, radius_q14, px_q14, py_q14) == 0 { return NX_HZ_MATERIAL_PASSTHROUGH }
354
355 if hammer_kind == NX_HAMMER_METEOR_FIELD {
356 return NX_HZ_MATERIAL_RUBBLE
357 }
358 if hammer_kind == NX_HAMMER_ANCIENT_BATTLE {
359 let h: nx_int = _hz_hash(seed, px_q14 / 100, py_q14 / 100)
360 if h % 2 == 0 { return NX_HZ_MATERIAL_RUBBLE }
361 return NX_HZ_MATERIAL_PASSTHROUGH
362 }
363 if hammer_kind == NX_HAMMER_VOLCANIC_RUINS {
364 let dx: nx_int = px_q14 - cx_q14
365 let dy: nx_int = py_q14 - cy_q14
366 let d2: nx_int = dx * dx + dy * dy
367 let r2: nx_int = radius_q14 * radius_q14
368 // Inside 70% of radius = scorched core.
369 if d2 < r2 * 70 / 100 { return NX_HZ_MATERIAL_SCORCHED }
370 return NX_HZ_MATERIAL_ASH
371 }
372 if hammer_kind == NX_HAMMER_MAGIC_BLAST {
373 return NX_HZ_MATERIAL_CRYSTALLINE
374 }
375
376 return NX_HZ_MATERIAL_PASSTHROUGH
377}
378
379// ===== Self-test ====================================================
380func main() -> i64 {
381 let q: nx_int = NX_HZ_Q
382
383 // T1: Validity predicates.
384 if nx_hammer_kind_is_valid(NX_HAMMER_METEOR_FIELD) != 1 { return __syscall(93, 1, 0, 0, 0, 0, 0) }
385 if nx_hammer_kind_is_valid(NX_HAMMER_MAGIC_BLAST) != 1 { return __syscall(93, 2, 0, 0, 0, 0, 0) }
386 if nx_hammer_kind_is_valid(99) != 0 { return __syscall(93, 3, 0, 0, 0, 0, 0) }
387 if nx_hz_material_is_valid(NX_HZ_MATERIAL_RUBBLE) != 1 { return __syscall(93, 4, 0, 0, 0, 0, 0) }
388 if nx_hz_material_is_valid(NX_HZ_MATERIAL_CRYSTALLINE) != 1 { return __syscall(93, 5, 0, 0, 0, 0, 0) }
389 if nx_hz_material_is_valid(99) != 0 { return __syscall(93, 6, 0, 0, 0, 0, 0) }
390
391 // T2: Out-of-zone query -> 0 carve + PASSTHROUGH material.
392 if nx_hammered_carve_at(0, 0, 100, 1000, NX_HAMMER_METEOR_FIELD, 42, NX_MAGIC_10000, NX_MAGIC_10000) != 0 {
393 return __syscall(93, 10, 0, 0, 0, 0, 0)
394 }
395 if nx_hammered_material_at(0, 0, 100, NX_HAMMER_METEOR_FIELD, 42, NX_MAGIC_10000, NX_MAGIC_10000) != NX_HZ_MATERIAL_PASSTHROUGH {
396 return __syscall(93, 11, 0, 0, 0, 0, 0)
397 }
398
399 // T3: In-zone METEOR_FIELD material always RUBBLE.
400 if nx_hammered_material_at(0, 0, 1000, NX_HAMMER_METEOR_FIELD, 42, 100, 100) != NX_HZ_MATERIAL_RUBBLE {
401 return __syscall(93, 20, 0, 0, 0, 0, 0)
402 }
403 if nx_hammered_material_at(0, 0, 1000, NX_HAMMER_METEOR_FIELD, 42, 500, 500) != NX_HZ_MATERIAL_RUBBLE {
404 return __syscall(93, 21, 0, 0, 0, 0, 0)
405 }
406
407 // T4: MAGIC_BLAST material always CRYSTALLINE in-zone.
408 if nx_hammered_material_at(0, 0, 1000, NX_HAMMER_MAGIC_BLAST, 42, 100, 100) != NX_HZ_MATERIAL_CRYSTALLINE {
409 return __syscall(93, 30, 0, 0, 0, 0, 0)
410 }
411
412 // T5: VOLCANIC_RUINS -- centre is SCORCHED, outer ring is ASH.
413 // r=1000; 70% threshold at d^2 = 700000; centre d=0 -> SCORCHED;
414 // outer d=900 -> d^2=810000 > 700000 -> ASH.
415 if nx_hammered_material_at(0, 0, 1000, NX_HAMMER_VOLCANIC_RUINS, 42, 0, 0) != NX_HZ_MATERIAL_SCORCHED {
416 return __syscall(93, 40, 0, 0, 0, 0, 0)
417 }
418 if nx_hammered_material_at(0, 0, 1000, NX_HAMMER_VOLCANIC_RUINS, 42, 900, 0) != NX_HZ_MATERIAL_ASH {
419 return __syscall(93, 41, 0, 0, 0, 0, 0)
420 }
421
422 // T6: ANCIENT_BATTLE -- hash-based; 50% of cells are RUBBLE.
423 // Run 32 queries; at least 5 should be RUBBLE and at least 5
424 // PASSTHROUGH (probabilistic with very low failure rate).
425 var n_rubble: nx_int = 0
426 var n_pass: nx_int = 0
427 var tx: nx_int = 0
428 while tx < 32 {
429 let m: nx_int = nx_hammered_material_at(0, 0, NX_MAGIC_10000, NX_HAMMER_ANCIENT_BATTLE, 42, tx * 50, tx * 73)
430 if m == NX_HZ_MATERIAL_RUBBLE { n_rubble = n_rubble + 1 }
431 if m == NX_HZ_MATERIAL_PASSTHROUGH { n_pass = n_pass + 1 }
432 tx = tx + 1
433 }
434 if n_rubble < 5 { return __syscall(93, 50, 0, 0, 0, 0, 0) }
435 if n_pass < 5 { return __syscall(93, 51, 0, 0, 0, 0, 0) }
436
437 // T7: Carve depth signs per kind.
438 // MAGIC_BLAST at centre: strong negative (radial = -intensity * 3/4).
439 let d_mb_centre: nx_int = nx_hammered_carve_at(0, 0, 1000, 200, NX_HAMMER_MAGIC_BLAST, 42, 0, 0)
440 if d_mb_centre >= 0 { return __syscall(93, 60, 0, 0, 0, 0, 0) }
441 // METEOR_FIELD: at least sometimes negative; dense sample grid
442 // ensures we hit at least one of the 64 deterministic sub-craters.
443 var any_meteor_neg: nx_int = 0
444 var mx: nx_int = 0 - NX_MAGIC_9000
445 while mx <= NX_MAGIC_9000 {
446 var my: nx_int = 0 - NX_MAGIC_9000
447 while my <= NX_MAGIC_9000 {
448 let d: nx_int = nx_hammered_carve_at(0, 0, NX_MAGIC_10000, NX_MAGIC_16384, NX_HAMMER_METEOR_FIELD, 42, mx, my)
449 if d < 0 { any_meteor_neg = 1 }
450 my = my + 1000
451 }
452 mx = mx + 1000
453 }
454 if any_meteor_neg == 0 { return __syscall(93, 61, 0, 0, 0, 0, 0) }
455
456 // T8: Invalid hammer_kind -> 0 + PASSTHROUGH.
457 if nx_hammered_carve_at(0, 0, 1000, 500, 99, 42, 0, 0) != 0 { return __syscall(93, 70, 0, 0, 0, 0, 0) }
458 if nx_hammered_material_at(0, 0, 1000, 99, 42, 0, 0) != NX_HZ_MATERIAL_PASSTHROUGH { return __syscall(93, 71, 0, 0, 0, 0, 0) }
459
460 // T9: Determinism -- same (kind, seed, px, py) -> same carve + material.
461 let d1: nx_int = nx_hammered_carve_at(0, 0, 1000, 500, NX_HAMMER_VOLCANIC_RUINS, 42, 100, 100)
462 let d2: nx_int = nx_hammered_carve_at(0, 0, 1000, 500, NX_HAMMER_VOLCANIC_RUINS, 42, 100, 100)
463 if d1 != d2 { return __syscall(93, 80, 0, 0, 0, 0, 0) }
464
465 // T10: Smooth edge fade. Inside 0.85r: full amplitude; near edge:
466 // reduced amplitude. Pick a kind with strong centre signal.
467 // r=1000, intensity=10000. MAGIC_BLAST centre is strongly negative.
468 let centre_carve: nx_int = nx_hammered_carve_at(0, 0, 1000, NX_MAGIC_10000, NX_HAMMER_MAGIC_BLAST, 42, 0, 0)
469 let mid_carve: nx_int = nx_hammered_carve_at(0, 0, 1000, NX_MAGIC_10000, NX_HAMMER_MAGIC_BLAST, 42, 500, 0)
470 let near_edge: nx_int = nx_hammered_carve_at(0, 0, 1000, NX_MAGIC_10000, NX_HAMMER_MAGIC_BLAST, 42, 950, 0)
471 // |near_edge| should be smaller than |mid_carve| due to fade.
472 var abs_near: nx_int = near_edge
473 if abs_near < 0 { abs_near = 0 - abs_near }
474 var abs_mid: nx_int = mid_carve
475 if abs_mid < 0 { abs_mid = 0 - abs_mid }
476 if abs_near >= abs_mid { return __syscall(93, 90, 0, 0, 0, 0, 0) }
477 // Beyond radius -> 0.
478 if nx_hammered_carve_at(0, 0, 1000, NX_MAGIC_10000, NX_HAMMER_MAGIC_BLAST, 42, NX_MAGIC_1100, 0) != 0 {
479 return __syscall(93, 91, 0, 0, 0, 0, 0)
480 }
481
482 // T11: METEOR_FIELD internal crater field produces bowls in some
483 // cells. Sample a dense grid; expect at least 1 negative cell.
484 // depth/radius = 0.4 and r_max = r_zone/4; for r_zone=5000 the
485 // deepest crater is r=1250 -> depth 500. Use a tolerant threshold.
486 var n_deep: nx_int = 0
487 var mfx: nx_int = 0 - NX_MAGIC_4000
488 while mfx <= NX_MAGIC_4000 {
489 var mfy: nx_int = 0 - NX_MAGIC_4000
490 while mfy <= NX_MAGIC_4000 {
491 let d: nx_int = nx_hammered_carve_at(0, 0, NX_MAGIC_5000, NX_MAGIC_16384, NX_HAMMER_METEOR_FIELD, 999, mfx, mfy)
492 if d < 0 - 50 { n_deep = n_deep + 1 }
493 mfy = mfy + 500
494 }
495 mfx = mfx + 500
496 }
497 if n_deep < 1 { return __syscall(93, 100, 0, 0, 0, 0, 0) }
498
499 // T12: Scorched-vegetation predicate.
500 // METEOR_FIELD: scorched everywhere in zone.
501 if nx_hammered_scorched_at(0, 0, 1000, NX_HAMMER_METEOR_FIELD, 42, 100, 100) != 1 {
502 return __syscall(93, 110, 0, 0, 0, 0, 0)
503 }
504 // Outside zone: not scorched.
505 if nx_hammered_scorched_at(0, 0, 1000, NX_HAMMER_METEOR_FIELD, 42, NX_MAGIC_10000, NX_MAGIC_10000) != 0 {
506 return __syscall(93, 111, 0, 0, 0, 0, 0)
507 }
508 // MAGIC_BLAST: scorched everywhere in zone.
509 if nx_hammered_scorched_at(0, 0, 1000, NX_HAMMER_MAGIC_BLAST, 42, 100, 100) != 1 {
510 return __syscall(93, 112, 0, 0, 0, 0, 0)
511 }
512 // VOLCANIC_RUINS: scorched inside 80% radius. d=0 -> scorched.
513 if nx_hammered_scorched_at(0, 0, 1000, NX_HAMMER_VOLCANIC_RUINS, 42, 0, 0) != 1 {
514 return __syscall(93, 113, 0, 0, 0, 0, 0)
515 }
516 // VOLCANIC_RUINS at d=950: outside 80% (d^2 = 902500 > r^2 * 0.8 = 800000) -> not scorched.
517 if nx_hammered_scorched_at(0, 0, 1000, NX_HAMMER_VOLCANIC_RUINS, 42, 950, 0) != 0 {
518 return __syscall(93, 114, 0, 0, 0, 0, 0)
519 }
520 // ANCIENT_BATTLE: scattered. Sample several cells; expect mix.
521 var n_scorched: nx_int = 0
522 var n_notburn: nx_int = 0
523 var sx: nx_int = 0
524 while sx < 32 {
525 let s: nx_int = nx_hammered_scorched_at(0, 0, NX_MAGIC_10000, NX_HAMMER_ANCIENT_BATTLE, 42, sx * 50, sx * 73)
526 if s == 1 { n_scorched = n_scorched + 1 }
527 if s == 0 { n_notburn = n_notburn + 1 }
528 sx = sx + 1
529 }
530 if n_scorched < 1 { return __syscall(93, 115, 0, 0, 0, 0, 0) }
531 if n_notburn < 5 { return __syscall(93, 116, 0, 0, 0, 0, 0) }
532 // Invalid kind: not scorched.
533 if nx_hammered_scorched_at(0, 0, 1000, 99, 42, 0, 0) != 0 {
534 return __syscall(93, 117, 0, 0, 0, 0, 0)
535 }
536
537 return 0
538}