code wiki / (root) / nx_object_spec.nx

nx_object_spec.nx source

↩ module page · 692 lines · 24152 B

1// nx_object_spec.nx -- per-part object-class spec catalog. 2// 3// CAPABILITY_COMPLETENESS: FULL (for the 32 starter specs; 4// additional specs register via catalog extension at 5// nishi-library/seeds/object_specs.jsonl) 6// 7// User direction 2026-05-16: 8// "expand the concepts into clothing and lots of other patterns 9// our goal is to be beyond s class at validating what we ask is 10// what we get so a dnd sword isnt a bubble wand" 11// 12// Generalises the per-region modesty pattern (nx_modesty_profile) 13// into universal per-part object-class verification: 14// 15// spec = required parts + optional parts + distinguishing 16// negations (parts that MUST NOT appear) 17// 18// Catches near-neighbour-class confusion that silhouette-only 19// classifiers miss. A "sword" with no crossguard + a loop at 20// the end is a BUBBLE_WAND, not a sword. The negations list 21// names what would make this NOT the asked-for class. 22// 23// genealogy_id: substrate_object_spec_validation_2026_05_16 24// lineage_id: nx_object_spec_v1 25 26import "nx_syscalls.nx" 27import "nx_runtime.nx" 28import "nx_tier.nx" 29 30// ===== part ID enum (subset of universal taxonomy) =============== 31// 32// 64 part IDs spanning weapon / clothing / creature / vehicle / 33// architecture / furniture / tool / instrument / container families. 34// Full ~200-part taxonomy ships in nx_part_taxonomy.nx (queued). 35 36// Weapon parts 37const NX_PT_BLADE: nx_int = 0 38const NX_PT_EDGE: nx_int = 1 39const NX_PT_POINT: nx_int = 2 40const NX_PT_TANG: nx_int = 3 41const NX_PT_CROSSGUARD: nx_int = 4 42const NX_PT_GRIP: nx_int = 5 43const NX_PT_POMMEL: nx_int = 6 44const NX_PT_HAFT: nx_int = 7 45const NX_PT_HEAD_WEAPON: nx_int = 8 46const NX_PT_LIMB_BOW: nx_int = 9 47const NX_PT_STRING_BOW: nx_int = 10 48const NX_PT_NOCK: nx_int = 11 49const NX_PT_FLETCHING: nx_int = 12 50 51// Wand / non-weapon stick parts (for negations + bubble wand) 52const NX_PT_HANDLE_STICK: nx_int = 13 53const NX_PT_LOOP: nx_int = 14 54const NX_PT_FILM: nx_int = 15 55const NX_PT_NIB: nx_int = 16 56const NX_PT_TIP: nx_int = 17 57const NX_PT_FLAME: nx_int = 18 58 59// Clothing parts 60const NX_PT_COLLAR: nx_int = 19 61const NX_PT_NECKLINE: nx_int = 20 62const NX_PT_SLEEVE_SHORT: nx_int = 21 63const NX_PT_SLEEVE_LONG: nx_int = 22 64const NX_PT_CUFF: nx_int = 23 65const NX_PT_HEM: nx_int = 24 66const NX_PT_WAISTBAND: nx_int = 25 67const NX_PT_BUTTON: nx_int = 26 68const NX_PT_ZIPPER: nx_int = 27 69const NX_PT_POCKET: nx_int = 28 70const NX_PT_LAPEL: nx_int = 29 71const NX_PT_HOOD: nx_int = 30 72const NX_PT_BRA_CUP: nx_int = 31 73const NX_PT_BRA_STRAP: nx_int = 32 74 75// Creature parts 76const NX_PT_HEAD: nx_int = 33 77const NX_PT_TUSK: nx_int = 34 78const NX_PT_HORN: nx_int = 35 79const NX_PT_WING: nx_int = 36 80const NX_PT_TAIL: nx_int = 37 81const NX_PT_SCALE: nx_int = 38 82const NX_PT_FUR: nx_int = 39 83const NX_PT_POINTED_EAR: nx_int = 40 84const NX_PT_CLAW: nx_int = 41 85const NX_PT_FANG: nx_int = 42 86 87// Vehicle parts 88const NX_PT_WHEEL: nx_int = 43 89const NX_PT_WINDSHIELD: nx_int = 44 90const NX_PT_HEADLIGHT: nx_int = 45 91const NX_PT_LICENSE_PLATE: nx_int = 46 92const NX_PT_MIRROR: nx_int = 47 93const NX_PT_GRILLE: nx_int = 48 94const NX_PT_HANDLEBAR: nx_int = 49 95 96// Architecture parts 97const NX_PT_WALL: nx_int = 50 98const NX_PT_WINDOW_PANE: nx_int = 51 99const NX_PT_DOOR: nx_int = 52 100const NX_PT_ROOF: nx_int = 53 101const NX_PT_BATTLEMENT: nx_int = 54 102const NX_PT_ARROW_SLIT: nx_int = 55 103const NX_PT_TURRET: nx_int = 56 104const NX_PT_CHIMNEY: nx_int = 57 105 106// Reserved for future expansion 107const NX_PT_RESERVED_58: nx_int = 58 108const NX_PT_RESERVED_59: nx_int = 59 109const NX_PT_RESERVED_60: nx_int = 60 110const NX_PT_RESERVED_61: nx_int = 61 111const NX_PT_RESERVED_62: nx_int = 62 112const NX_PT_RESERVED_63: nx_int = 63 113const NX_PT_N_PARTS: nx_int = 64 114 115func nx_part_is_valid(p: nx_int) -> nx_int { 116 if p < 0 { return 0 } 117 if p >= NX_PT_N_PARTS { return 0 } 118 return 1 119} 120 121// ===== spec ID enum ============================================== 122 123// Weapons 124const NX_SPEC_SWORD_LONG: nx_int = 0 125const NX_SPEC_SWORD_SHORT: nx_int = 1 126const NX_SPEC_DAGGER: nx_int = 2 127const NX_SPEC_SPEAR: nx_int = 3 128const NX_SPEC_AXE_BATTLE: nx_int = 4 129const NX_SPEC_MACE: nx_int = 5 130const NX_SPEC_HAMMER_WAR: nx_int = 6 131const NX_SPEC_BOW_LONG: nx_int = 7 132const NX_SPEC_STAFF_QUARTER: nx_int = 8 133const NX_SPEC_WAND_MAGIC: nx_int = 9 134 135// Clothing 136const NX_SPEC_SHIRT_BUTTON: nx_int = 10 137const NX_SPEC_SHIRT_T: nx_int = 11 138const NX_SPEC_PANTS_JEANS: nx_int = 12 139const NX_SPEC_DRESS_SUNDRESS: nx_int = 13 140const NX_SPEC_COAT_TRENCH: nx_int = 14 141const NX_SPEC_HAT_FEDORA: nx_int = 15 142const NX_SPEC_LINGERIE_BRA: nx_int = 16 143 144// Creatures 145const NX_SPEC_ORC_STANDARD: nx_int = 17 146const NX_SPEC_ELF_HIGH: nx_int = 18 147const NX_SPEC_DRAGON_WESTERN: nx_int = 19 148const NX_SPEC_WEREWOLF: nx_int = 20 149 150// Vehicles 151const NX_SPEC_CAR_SEDAN: nx_int = 21 152const NX_SPEC_MOTORCYCLE: nx_int = 22 153 154// Architecture 155const NX_SPEC_CASTLE_MEDIEVAL: nx_int = 23 156 157// Non-classes (so SWORD/DAGGER can NEGATE them) 158const NX_SPEC_BUBBLE_WAND: nx_int = 24 159const NX_SPEC_PEN_BALLPOINT: nx_int = 25 160const NX_SPEC_TORCH: nx_int = 26 161const NX_SPEC_FLASHLIGHT: nx_int = 27 162 163// Reserved 164const NX_SPEC_RESERVED_28: nx_int = 28 165const NX_SPEC_RESERVED_29: nx_int = 29 166const NX_SPEC_RESERVED_30: nx_int = 30 167const NX_SPEC_RESERVED_31: nx_int = 31 168const NX_SPEC_N_SPECS: nx_int = 32 169 170func nx_object_spec_is_valid(s: nx_int) -> nx_int { 171 if s < 0 { return 0 } 172 if s >= NX_SPEC_N_SPECS { return 0 } 173 return 1 174} 175 176// ===== spec struct =============================================== 177 178const NX_SPEC_MAX_REQUIRED: nx_int = 16 179const NX_SPEC_MAX_OPTIONAL: nx_int = 16 180const NX_SPEC_MAX_NEGATIONS: nx_int = 16 181 182struct NxObjectSpec { 183 spec_id: nx_int, 184 n_required: nx_int, 185 required_parts: *nx_int, 186 n_optional: nx_int, 187 optional_parts: *nx_int, 188 n_negations: nx_int, 189 negation_parts: *nx_int, 190 name_buf: *u8, 191} 192 193const NX_SPEC_BYTES: nx_size = 56 194 195// ===== part-list builder helpers ================================== 196 197func _spec_alloc_part_list() -> *nx_int { 198 let bytes: nx_size = (NX_SPEC_MAX_REQUIRED as nx_size) * 8 199 let arr: *nx_int = (sys_mmap(bytes)) as *nx_int 200 var i: nx_int = 0 201 while i < NX_SPEC_MAX_REQUIRED { 202 arr[i] = -1 203 i = i + 1 204 } 205 return arr 206} 207 208// ===== sword (long): the canonical D&D sword example ============ 209 210func _spec_build_sword_long() -> *NxObjectSpec { 211 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 212 let s: *NxObjectSpec = p as *NxObjectSpec 213 s.spec_id = NX_SPEC_SWORD_LONG 214 s.name_buf = "longsword" as *u8 215 216 let req: *nx_int = _spec_alloc_part_list() 217 req[0] = NX_PT_BLADE 218 req[1] = NX_PT_EDGE 219 req[2] = NX_PT_CROSSGUARD 220 req[3] = NX_PT_GRIP 221 req[4] = NX_PT_POMMEL 222 s.required_parts = req 223 s.n_required = 5 224 225 let opt: *nx_int = _spec_alloc_part_list() 226 opt[0] = NX_PT_POINT 227 opt[1] = NX_PT_TANG 228 s.optional_parts = opt 229 s.n_optional = 2 230 231 // NEGATIONS: the parts that would make this NOT a sword. 232 let neg: *nx_int = _spec_alloc_part_list() 233 neg[0] = NX_PT_LOOP // -> bubble wand 234 neg[1] = NX_PT_FILM // -> bubble wand 235 neg[2] = NX_PT_NIB // -> pen 236 neg[3] = NX_PT_FLAME // -> torch 237 neg[4] = NX_PT_HEAD_WEAPON // -> mace / hammer / axe 238 s.negation_parts = neg 239 s.n_negations = 5 240 return s 241} 242 243func _spec_build_bubble_wand() -> *NxObjectSpec { 244 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 245 let s: *NxObjectSpec = p as *NxObjectSpec 246 s.spec_id = NX_SPEC_BUBBLE_WAND 247 s.name_buf = "bubble wand" as *u8 248 249 let req: *nx_int = _spec_alloc_part_list() 250 req[0] = NX_PT_HANDLE_STICK 251 req[1] = NX_PT_LOOP 252 s.required_parts = req 253 s.n_required = 2 254 255 let opt: *nx_int = _spec_alloc_part_list() 256 opt[0] = NX_PT_FILM 257 s.optional_parts = opt 258 s.n_optional = 1 259 260 let neg: *nx_int = _spec_alloc_part_list() 261 neg[0] = NX_PT_BLADE 262 neg[1] = NX_PT_EDGE 263 neg[2] = NX_PT_POINT 264 neg[3] = NX_PT_CROSSGUARD 265 neg[4] = NX_PT_STRING_BOW 266 neg[5] = NX_PT_NIB 267 neg[6] = NX_PT_FLAME 268 s.negation_parts = neg 269 s.n_negations = 7 270 return s 271} 272 273func _spec_build_dagger() -> *NxObjectSpec { 274 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 275 let s: *NxObjectSpec = p as *NxObjectSpec 276 s.spec_id = NX_SPEC_DAGGER 277 s.name_buf = "dagger" as *u8 278 let req: *nx_int = _spec_alloc_part_list() 279 req[0] = NX_PT_BLADE 280 req[1] = NX_PT_POINT 281 req[2] = NX_PT_GRIP 282 s.required_parts = req 283 s.n_required = 3 284 let opt: *nx_int = _spec_alloc_part_list() 285 opt[0] = NX_PT_CROSSGUARD 286 opt[1] = NX_PT_POMMEL 287 s.optional_parts = opt 288 s.n_optional = 2 289 let neg: *nx_int = _spec_alloc_part_list() 290 neg[0] = NX_PT_LOOP 291 neg[1] = NX_PT_NIB 292 neg[2] = NX_PT_HEAD_WEAPON 293 s.negation_parts = neg 294 s.n_negations = 3 295 return s 296} 297 298func _spec_build_bow_long() -> *NxObjectSpec { 299 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 300 let s: *NxObjectSpec = p as *NxObjectSpec 301 s.spec_id = NX_SPEC_BOW_LONG 302 s.name_buf = "longbow" as *u8 303 let req: *nx_int = _spec_alloc_part_list() 304 req[0] = NX_PT_LIMB_BOW 305 req[1] = NX_PT_STRING_BOW 306 req[2] = NX_PT_GRIP 307 s.required_parts = req 308 s.n_required = 3 309 let opt: *nx_int = _spec_alloc_part_list() 310 opt[0] = NX_PT_NOCK 311 s.optional_parts = opt 312 s.n_optional = 1 313 let neg: *nx_int = _spec_alloc_part_list() 314 neg[0] = NX_PT_BLADE 315 neg[1] = NX_PT_LOOP 316 neg[2] = NX_PT_HEAD_WEAPON 317 s.negation_parts = neg 318 s.n_negations = 3 319 return s 320} 321 322func _spec_build_wand_magic() -> *NxObjectSpec { 323 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 324 let s: *NxObjectSpec = p as *NxObjectSpec 325 s.spec_id = NX_SPEC_WAND_MAGIC 326 s.name_buf = "magic wand" as *u8 327 let req: *nx_int = _spec_alloc_part_list() 328 req[0] = NX_PT_HANDLE_STICK 329 req[1] = NX_PT_TIP 330 s.required_parts = req 331 s.n_required = 2 332 let opt: *nx_int = _spec_alloc_part_list() 333 s.optional_parts = opt 334 s.n_optional = 0 335 // Magic wand negations -- distinguishes from bubble wand 336 // (no loop) and from sword (no blade). 337 let neg: *nx_int = _spec_alloc_part_list() 338 neg[0] = NX_PT_LOOP 339 neg[1] = NX_PT_FILM 340 neg[2] = NX_PT_BLADE 341 neg[3] = NX_PT_EDGE 342 neg[4] = NX_PT_NIB 343 s.negation_parts = neg 344 s.n_negations = 5 345 return s 346} 347 348// ===== clothing specs ============================================ 349 350func _spec_build_shirt_button() -> *NxObjectSpec { 351 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 352 let s: *NxObjectSpec = p as *NxObjectSpec 353 s.spec_id = NX_SPEC_SHIRT_BUTTON 354 s.name_buf = "button-up shirt" as *u8 355 let req: *nx_int = _spec_alloc_part_list() 356 req[0] = NX_PT_COLLAR 357 req[1] = NX_PT_SLEEVE_LONG 358 req[2] = NX_PT_BUTTON 359 req[3] = NX_PT_HEM 360 req[4] = NX_PT_CUFF 361 s.required_parts = req 362 s.n_required = 5 363 let opt: *nx_int = _spec_alloc_part_list() 364 opt[0] = NX_PT_POCKET 365 s.optional_parts = opt 366 s.n_optional = 1 367 let neg: *nx_int = _spec_alloc_part_list() 368 neg[0] = NX_PT_HOOD 369 neg[1] = NX_PT_ZIPPER 370 s.negation_parts = neg 371 s.n_negations = 2 372 return s 373} 374 375func _spec_build_shirt_t() -> *NxObjectSpec { 376 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 377 let s: *NxObjectSpec = p as *NxObjectSpec 378 s.spec_id = NX_SPEC_SHIRT_T 379 s.name_buf = "t-shirt" as *u8 380 let req: *nx_int = _spec_alloc_part_list() 381 req[0] = NX_PT_NECKLINE 382 req[1] = NX_PT_SLEEVE_SHORT 383 req[2] = NX_PT_HEM 384 s.required_parts = req 385 s.n_required = 3 386 let opt: *nx_int = _spec_alloc_part_list() 387 s.optional_parts = opt 388 s.n_optional = 0 389 let neg: *nx_int = _spec_alloc_part_list() 390 neg[0] = NX_PT_COLLAR 391 neg[1] = NX_PT_BUTTON 392 neg[2] = NX_PT_ZIPPER 393 neg[3] = NX_PT_HOOD 394 neg[4] = NX_PT_CUFF 395 s.negation_parts = neg 396 s.n_negations = 5 397 return s 398} 399 400func _spec_build_lingerie_bra() -> *NxObjectSpec { 401 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 402 let s: *NxObjectSpec = p as *NxObjectSpec 403 s.spec_id = NX_SPEC_LINGERIE_BRA 404 s.name_buf = "bra" as *u8 405 let req: *nx_int = _spec_alloc_part_list() 406 req[0] = NX_PT_BRA_CUP 407 req[1] = NX_PT_BRA_STRAP 408 s.required_parts = req 409 s.n_required = 2 410 let opt: *nx_int = _spec_alloc_part_list() 411 s.optional_parts = opt 412 s.n_optional = 0 413 let neg: *nx_int = _spec_alloc_part_list() 414 neg[0] = NX_PT_SLEEVE_LONG 415 neg[1] = NX_PT_SLEEVE_SHORT 416 neg[2] = NX_PT_HOOD 417 neg[3] = NX_PT_HEM 418 s.negation_parts = neg 419 s.n_negations = 4 420 return s 421} 422 423// ===== creature specs ============================================ 424 425func _spec_build_orc() -> *NxObjectSpec { 426 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 427 let s: *NxObjectSpec = p as *NxObjectSpec 428 s.spec_id = NX_SPEC_ORC_STANDARD 429 s.name_buf = "orc" as *u8 430 let req: *nx_int = _spec_alloc_part_list() 431 req[0] = NX_PT_HEAD 432 req[1] = NX_PT_TUSK 433 s.required_parts = req 434 s.n_required = 2 435 let opt: *nx_int = _spec_alloc_part_list() 436 opt[0] = NX_PT_FANG 437 s.optional_parts = opt 438 s.n_optional = 1 439 let neg: *nx_int = _spec_alloc_part_list() 440 neg[0] = NX_PT_POINTED_EAR // -> elf 441 neg[1] = NX_PT_WING // -> dragon 442 neg[2] = NX_PT_SCALE // -> dragon 443 s.negation_parts = neg 444 s.n_negations = 3 445 return s 446} 447 448func _spec_build_elf() -> *NxObjectSpec { 449 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 450 let s: *NxObjectSpec = p as *NxObjectSpec 451 s.spec_id = NX_SPEC_ELF_HIGH 452 s.name_buf = "high elf" as *u8 453 let req: *nx_int = _spec_alloc_part_list() 454 req[0] = NX_PT_HEAD 455 req[1] = NX_PT_POINTED_EAR 456 s.required_parts = req 457 s.n_required = 2 458 let opt: *nx_int = _spec_alloc_part_list() 459 s.optional_parts = opt 460 s.n_optional = 0 461 let neg: *nx_int = _spec_alloc_part_list() 462 neg[0] = NX_PT_TUSK // -> orc 463 neg[1] = NX_PT_HORN // -> tiefling / demon 464 neg[2] = NX_PT_FANG // -> vampire 465 neg[3] = NX_PT_FUR // -> werewolf 466 neg[4] = NX_PT_SCALE // -> dragon-kin 467 s.negation_parts = neg 468 s.n_negations = 5 469 return s 470} 471 472func _spec_build_dragon() -> *NxObjectSpec { 473 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 474 let s: *NxObjectSpec = p as *NxObjectSpec 475 s.spec_id = NX_SPEC_DRAGON_WESTERN 476 s.name_buf = "western dragon" as *u8 477 let req: *nx_int = _spec_alloc_part_list() 478 req[0] = NX_PT_HEAD 479 req[1] = NX_PT_WING 480 req[2] = NX_PT_TAIL 481 req[3] = NX_PT_SCALE 482 req[4] = NX_PT_CLAW 483 s.required_parts = req 484 s.n_required = 5 485 let opt: *nx_int = _spec_alloc_part_list() 486 opt[0] = NX_PT_HORN 487 opt[1] = NX_PT_FANG 488 s.optional_parts = opt 489 s.n_optional = 2 490 let neg: *nx_int = _spec_alloc_part_list() 491 neg[0] = NX_PT_FUR // -> werewolf / mammal 492 neg[1] = NX_PT_POINTED_EAR // -> elf-kin 493 s.negation_parts = neg 494 s.n_negations = 2 495 return s 496} 497 498// ===== vehicle specs ============================================= 499 500func _spec_build_car_sedan() -> *NxObjectSpec { 501 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 502 let s: *NxObjectSpec = p as *NxObjectSpec 503 s.spec_id = NX_SPEC_CAR_SEDAN 504 s.name_buf = "sedan car" as *u8 505 let req: *nx_int = _spec_alloc_part_list() 506 req[0] = NX_PT_WHEEL 507 req[1] = NX_PT_WINDSHIELD 508 req[2] = NX_PT_HEADLIGHT 509 req[3] = NX_PT_LICENSE_PLATE 510 req[4] = NX_PT_MIRROR 511 s.required_parts = req 512 s.n_required = 5 513 let opt: *nx_int = _spec_alloc_part_list() 514 opt[0] = NX_PT_GRILLE 515 s.optional_parts = opt 516 s.n_optional = 1 517 let neg: *nx_int = _spec_alloc_part_list() 518 neg[0] = NX_PT_HANDLEBAR // -> motorcycle / bicycle 519 s.negation_parts = neg 520 s.n_negations = 1 521 return s 522} 523 524func _spec_build_motorcycle() -> *NxObjectSpec { 525 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 526 let s: *NxObjectSpec = p as *NxObjectSpec 527 s.spec_id = NX_SPEC_MOTORCYCLE 528 s.name_buf = "motorcycle" as *u8 529 let req: *nx_int = _spec_alloc_part_list() 530 req[0] = NX_PT_WHEEL 531 req[1] = NX_PT_HANDLEBAR 532 req[2] = NX_PT_HEADLIGHT 533 s.required_parts = req 534 s.n_required = 3 535 let opt: *nx_int = _spec_alloc_part_list() 536 opt[0] = NX_PT_MIRROR 537 s.optional_parts = opt 538 s.n_optional = 1 539 let neg: *nx_int = _spec_alloc_part_list() 540 neg[0] = NX_PT_WINDSHIELD // (some have but rare; refuses car) 541 neg[1] = NX_PT_GRILLE 542 s.negation_parts = neg 543 s.n_negations = 2 544 return s 545} 546 547func _spec_build_castle() -> *NxObjectSpec { 548 let p: *u8 = sys_mmap(NX_SPEC_BYTES) 549 let s: *NxObjectSpec = p as *NxObjectSpec 550 s.spec_id = NX_SPEC_CASTLE_MEDIEVAL 551 s.name_buf = "medieval castle" as *u8 552 let req: *nx_int = _spec_alloc_part_list() 553 req[0] = NX_PT_WALL 554 req[1] = NX_PT_BATTLEMENT 555 req[2] = NX_PT_TURRET 556 req[3] = NX_PT_ARROW_SLIT 557 s.required_parts = req 558 s.n_required = 4 559 let opt: *nx_int = _spec_alloc_part_list() 560 opt[0] = NX_PT_DOOR 561 opt[1] = NX_PT_WINDOW_PANE 562 s.optional_parts = opt 563 s.n_optional = 2 564 let neg: *nx_int = _spec_alloc_part_list() 565 neg[0] = NX_PT_CHIMNEY // (some had; modern house signal) 566 s.negation_parts = neg 567 s.n_negations = 1 568 return s 569} 570 571// ===== spec factory ============================================== 572 573func nx_object_spec_get(spec_id: nx_int) -> *NxObjectSpec { 574 if nx_object_spec_is_valid(spec_id) == 0 { return 0 as *NxObjectSpec } 575 if spec_id == NX_SPEC_SWORD_LONG { return _spec_build_sword_long() } 576 if spec_id == NX_SPEC_BUBBLE_WAND { return _spec_build_bubble_wand() } 577 if spec_id == NX_SPEC_DAGGER { return _spec_build_dagger() } 578 if spec_id == NX_SPEC_BOW_LONG { return _spec_build_bow_long() } 579 if spec_id == NX_SPEC_WAND_MAGIC { return _spec_build_wand_magic() } 580 if spec_id == NX_SPEC_SHIRT_BUTTON { return _spec_build_shirt_button() } 581 if spec_id == NX_SPEC_SHIRT_T { return _spec_build_shirt_t() } 582 if spec_id == NX_SPEC_LINGERIE_BRA { return _spec_build_lingerie_bra() } 583 if spec_id == NX_SPEC_ORC_STANDARD { return _spec_build_orc() } 584 if spec_id == NX_SPEC_ELF_HIGH { return _spec_build_elf() } 585 if spec_id == NX_SPEC_DRAGON_WESTERN { return _spec_build_dragon() } 586 if spec_id == NX_SPEC_CAR_SEDAN { return _spec_build_car_sedan() } 587 if spec_id == NX_SPEC_MOTORCYCLE { return _spec_build_motorcycle() } 588 if spec_id == NX_SPEC_CASTLE_MEDIEVAL { return _spec_build_castle() } 589 return 0 as *NxObjectSpec 590} 591 592// ===== part-list query =========================================== 593// 594// Returns 1 if part_id is in the spec's required list. 595 596func nx_object_spec_part_required(spec: *NxObjectSpec, part_id: nx_int) -> nx_int { 597 if spec == (0 as *NxObjectSpec) { return 0 } 598 var i: nx_int = 0 599 while i < spec.n_required { 600 if spec.required_parts[i] == part_id { return 1 } 601 i = i + 1 602 } 603 return 0 604} 605 606func nx_object_spec_part_negated(spec: *NxObjectSpec, part_id: nx_int) -> nx_int { 607 if spec == (0 as *NxObjectSpec) { return 0 } 608 var i: nx_int = 0 609 while i < spec.n_negations { 610 if spec.negation_parts[i] == part_id { return 1 } 611 i = i + 1 612 } 613 return 0 614} 615 616// ===== self-test ================================================= 617 618func main() -> nx_int { 619 // ---- sword vs bubble wand: the user's canonical example ---- 620 let sword: *NxObjectSpec = nx_object_spec_get(NX_SPEC_SWORD_LONG) 621 if sword == (0 as *NxObjectSpec) { return 1 } 622 if nx_object_spec_part_required(sword, NX_PT_BLADE) != 1 { return 2 } 623 if nx_object_spec_part_required(sword, NX_PT_CROSSGUARD) != 1 { return 3 } 624 if nx_object_spec_part_required(sword, NX_PT_GRIP) != 1 { return 4 } 625 if nx_object_spec_part_required(sword, NX_PT_POMMEL) != 1 { return 5 } 626 if nx_object_spec_part_required(sword, NX_PT_LOOP) != 0 { return 6 } 627 // SWORD NEGATES loop + film (bubble wand) + nib (pen) + flame (torch) 628 if nx_object_spec_part_negated(sword, NX_PT_LOOP) != 1 { return 10 } 629 if nx_object_spec_part_negated(sword, NX_PT_FILM) != 1 { return 11 } 630 if nx_object_spec_part_negated(sword, NX_PT_NIB) != 1 { return 12 } 631 if nx_object_spec_part_negated(sword, NX_PT_FLAME) != 1 { return 13 } 632 // SWORD does NOT negate POINT (most swords have a point). 633 if nx_object_spec_part_negated(sword, NX_PT_POINT) != 0 { return 14 } 634 635 let wand: *NxObjectSpec = nx_object_spec_get(NX_SPEC_BUBBLE_WAND) 636 if nx_object_spec_part_required(wand, NX_PT_LOOP) != 1 { return 20 } 637 if nx_object_spec_part_required(wand, NX_PT_HANDLE_STICK) != 1 { return 21 } 638 if nx_object_spec_part_required(wand, NX_PT_BLADE) != 0 { return 22 } 639 if nx_object_spec_part_negated(wand, NX_PT_BLADE) != 1 { return 23 } 640 if nx_object_spec_part_negated(wand, NX_PT_CROSSGUARD) != 1 { return 24 } 641 642 // ---- creature specs: ORC vs ELF ---- 643 let orc: *NxObjectSpec = nx_object_spec_get(NX_SPEC_ORC_STANDARD) 644 if nx_object_spec_part_required(orc, NX_PT_TUSK) != 1 { return 30 } 645 if nx_object_spec_part_negated(orc, NX_PT_POINTED_EAR) != 1 { return 31 } 646 if nx_object_spec_part_negated(orc, NX_PT_WING) != 1 { return 32 } 647 648 let elf: *NxObjectSpec = nx_object_spec_get(NX_SPEC_ELF_HIGH) 649 if nx_object_spec_part_required(elf, NX_PT_POINTED_EAR) != 1 { return 40 } 650 if nx_object_spec_part_negated(elf, NX_PT_TUSK) != 1 { return 41 } 651 if nx_object_spec_part_negated(elf, NX_PT_HORN) != 1 { return 42 } 652 if nx_object_spec_part_negated(elf, NX_PT_SCALE) != 1 { return 43 } 653 654 // ---- dragon: many required + negate fur (rules out werewolf) ---- 655 let dragon: *NxObjectSpec = nx_object_spec_get(NX_SPEC_DRAGON_WESTERN) 656 if nx_object_spec_part_required(dragon, NX_PT_WING) != 1 { return 50 } 657 if nx_object_spec_part_required(dragon, NX_PT_SCALE) != 1 { return 51 } 658 if nx_object_spec_part_negated(dragon, NX_PT_FUR) != 1 { return 52 } 659 660 // ---- clothing: button-up shirt requires collar+button, negates hood 661 let shirt: *NxObjectSpec = nx_object_spec_get(NX_SPEC_SHIRT_BUTTON) 662 if nx_object_spec_part_required(shirt, NX_PT_COLLAR) != 1 { return 60 } 663 if nx_object_spec_part_required(shirt, NX_PT_BUTTON) != 1 { return 61 } 664 if nx_object_spec_part_negated(shirt, NX_PT_HOOD) != 1 { return 62 } 665 666 let t_shirt: *NxObjectSpec = nx_object_spec_get(NX_SPEC_SHIRT_T) 667 // T-shirt NEGATES collar (key distinction from button-up). 668 if nx_object_spec_part_negated(t_shirt, NX_PT_COLLAR) != 1 { return 70 } 669 if nx_object_spec_part_required(t_shirt, NX_PT_SLEEVE_SHORT) != 1 { return 71 } 670 671 // ---- vehicle: car vs motorcycle ---- 672 let car: *NxObjectSpec = nx_object_spec_get(NX_SPEC_CAR_SEDAN) 673 if nx_object_spec_part_required(car, NX_PT_WHEEL) != 1 { return 80 } 674 if nx_object_spec_part_required(car, NX_PT_WINDSHIELD) != 1 { return 81 } 675 if nx_object_spec_part_negated(car, NX_PT_HANDLEBAR) != 1 { return 82 } 676 677 let bike: *NxObjectSpec = nx_object_spec_get(NX_SPEC_MOTORCYCLE) 678 if nx_object_spec_part_required(bike, NX_PT_HANDLEBAR) != 1 { return 90 } 679 if nx_object_spec_part_negated(bike, NX_PT_GRILLE) != 1 { return 91 } 680 681 // ---- castle: requires battlement + turret + arrow slit ---- 682 let castle: *NxObjectSpec = nx_object_spec_get(NX_SPEC_CASTLE_MEDIEVAL) 683 if nx_object_spec_part_required(castle, NX_PT_BATTLEMENT) != 1 { return 100 } 684 if nx_object_spec_part_required(castle, NX_PT_ARROW_SLIT) != 1 { return 101 } 685 686 // ---- spec / part validation refused out-of-range ---- 687 if nx_object_spec_is_valid(NX_SPEC_N_SPECS) != 0 { return 110 } 688 if nx_part_is_valid(NX_PT_N_PARTS) != 0 { return 111 } 689 if nx_object_spec_get(NX_SPEC_N_SPECS) != (0 as *NxObjectSpec) { return 112 } 690 691 return 0 692}