code wiki / (root) / nx_hammered_zone.nx

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}