1 | /* SCCS Id: @(#)mkobj.c 3.3 2000/02/19 */ 2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3 | /* NetHack may be freely redistributed. See license for details. */ 4 | 5 | #include "hack.h" 6 | #include "artifact.h" 7 | #include "prop.h" 8 | 9 | STATIC_DCL void FDECL(mkbox_cnts,(struct obj *)); 10 | STATIC_DCL void FDECL(obj_timer_checks,(struct obj *, XCHAR_P, XCHAR_P, int)); 11 | #ifdef OVL1 12 | STATIC_DCL void FDECL(container_weight, (struct obj *)); 13 | STATIC_DCL struct obj *FDECL(save_mtraits, (struct obj *, struct monst *)); 14 | #ifdef WIZARD 15 | STATIC_DCL const char *FDECL(where_name, (int)); 16 | STATIC_DCL void FDECL(check_contained, (struct obj *,const char *)); 17 | #endif 18 | #endif /* OVL1 */ 19 | 20 | /*#define DEBUG_EFFECTS*/ /* show some messages for debugging */ 21 | 22 | struct icp { 23 | int iprob; /* probability of an item type */ 24 | char iclass; /* item class */ 25 | }; 26 | 27 | #ifdef OVL1 28 | 29 | const struct icp mkobjprobs[] = { 30 | {10, WEAPON_CLASS}, 31 | {10, ARMOR_CLASS}, 32 | {20, FOOD_CLASS}, 33 | { 8, TOOL_CLASS}, 34 | { 8, GEM_CLASS}, 35 | {16, POTION_CLASS}, 36 | {16, SCROLL_CLASS}, 37 | { 4, SPBOOK_CLASS}, 38 | { 4, WAND_CLASS}, 39 | { 3, RING_CLASS}, 40 | { 1, AMULET_CLASS} 41 | }; 42 | 43 | const struct icp boxiprobs[] = { 44 | {18, GEM_CLASS}, 45 | {15, FOOD_CLASS}, 46 | {18, POTION_CLASS}, 47 | {18, SCROLL_CLASS}, 48 | {12, SPBOOK_CLASS}, 49 | { 7, GOLD_CLASS}, 50 | { 6, WAND_CLASS}, 51 | { 5, RING_CLASS}, 52 | { 1, AMULET_CLASS} 53 | }; 54 | 55 | #ifdef REINCARNATION 56 | const struct icp rogueprobs[] = { 57 | {12, WEAPON_CLASS}, 58 | {12, ARMOR_CLASS}, 59 | {22, FOOD_CLASS}, 60 | {22, POTION_CLASS}, 61 | {22, SCROLL_CLASS}, 62 | { 5, WAND_CLASS}, 63 | { 5, RING_CLASS} 64 | }; 65 | #endif 66 | 67 | const struct icp hellprobs[] = { 68 | {20, WEAPON_CLASS}, 69 | {20, ARMOR_CLASS}, 70 | {16, FOOD_CLASS}, 71 | {12, TOOL_CLASS}, 72 | {10, GEM_CLASS}, 73 | { 1, POTION_CLASS}, 74 | { 1, SCROLL_CLASS}, 75 | { 8, WAND_CLASS}, 76 | { 8, RING_CLASS}, 77 | { 4, AMULET_CLASS} 78 | }; 79 | 80 | struct obj * 81 | mkobj_at(let,x,y, artif) 82 | char let; 83 | int x,y; 84 | boolean artif; 85 | { 86 | register struct obj *otmp; 87 | 88 | otmp = mkobj(let,artif); 89 | place_object(otmp, x, y); 90 | return(otmp); 91 | } 92 | 93 | struct obj * 94 | mksobj_at(otyp,x,y,init) 95 | int otyp,x,y; 96 | boolean init; 97 | { 98 | register struct obj *otmp; 99 | 100 | otmp = mksobj(otyp,init,TRUE); 101 | place_object(otmp, x, y); 102 | return(otmp); 103 | } 104 | 105 | struct obj * 106 | mkobj(oclass, artif) 107 | char oclass; 108 | boolean artif; 109 | { 110 | register int tprob, i, prob = rnd(1000); 111 | 112 | if(oclass == RANDOM_CLASS) { 113 | const struct icp *iprobs = 114 | #ifdef REINCARNATION 115 | (Is_rogue_level(&u.uz)) ? 116 | (const struct icp *)rogueprobs : 117 | #endif 118 | Inhell ? (const struct icp *)hellprobs : 119 | (const struct icp *)mkobjprobs; 120 | 121 | for(tprob = rnd(100); 122 | (tprob -= iprobs->iprob) > 0; 123 | iprobs++); 124 | oclass = iprobs->iclass; 125 | } 126 | 127 | i = bases[(int)oclass]; 128 | while((prob -= objects[i].oc_prob) > 0) i++; 129 | 130 | if(objects[i].oc_class != oclass || !OBJ_NAME(objects[i])) 131 | panic("probtype error, oclass=%d i=%d", (int) oclass, i); 132 | 133 | return(mksobj(i, TRUE, artif)); 134 | } 135 | 136 | STATIC_OVL void 137 | mkbox_cnts(box) 138 | struct obj *box; 139 | { 140 | register int n; 141 | register struct obj *otmp, *gold = 0; 142 | 143 | box->cobj = (struct obj *) 0; 144 | 145 | switch(box->otyp) { 146 | case ICE_BOX: n = 20; break; 147 | case CHEST: n = 5; break; 148 | case LARGE_BOX: n = 3; break; 149 | case SACK: 150 | case OILSKIN_SACK: 151 | /* initial inventory: sack starts out empty */ 152 | if (moves <= 1 && !in_mklev) { n = 0; break; } 153 | /*else FALLTHRU*/ 154 | case BAG_OF_HOLDING: n = 1; break; 155 | default: n = 0; break; 156 | } 157 | 158 | for (n = rn2(n+1); n > 0; n--) { 159 | if (box->otyp == ICE_BOX) { 160 | if (!(otmp = mksobj(CORPSE, TRUE, TRUE))) continue; 161 | /* Note: setting age to 0 is correct. Age has a different 162 | * from usual meaning for objects stored in ice boxes. -KAA 163 | */ 164 | otmp->age = 0L; 165 | if (otmp->timed) { 166 | (void) stop_timer(ROT_CORPSE, (genericptr_t)otmp); 167 | (void) stop_timer(REVIVE_MON, (genericptr_t)otmp); 168 | } 169 | } else { 170 | register int tprob; 171 | const struct icp *iprobs = boxiprobs; 172 | 173 | for (tprob = rnd(100); (tprob -= iprobs->iprob) > 0; iprobs++) 174 | ; 175 | if (!(otmp = mkobj(iprobs->iclass, TRUE))) continue; 176 | 177 | /* handle a couple of special cases */ 178 | if (otmp->oclass == GOLD_CLASS) { 179 | /* 2.5 x level's usual amount; weight adjusted below */ 180 | otmp->quan = (long)(rnd(level_difficulty()+2) * rnd(75)); 181 | if (gold) { /* gold already in this box */ 182 | gold->quan += otmp->quan; /* merge */ 183 | dealloc_obj(otmp); /* note: not yet in any chain */ 184 | continue; 185 | } else { 186 | gold = otmp; /* remember this object */ 187 | } 188 | } else while (otmp->otyp == ROCK) { 189 | otmp->otyp = rnd_class(DILITHIUM_CRYSTAL, LOADSTONE); 190 | if (otmp->quan > 2L) otmp->quan = 1L; 191 | otmp->owt = weight(otmp); 192 | } 193 | if (box->otyp == BAG_OF_HOLDING) { 194 | if (Is_mbag(otmp)) { 195 | otmp->otyp = SACK; 196 | otmp->spe = 0; 197 | otmp->owt = weight(otmp); 198 | } else while (otmp->otyp == WAN_CANCELLATION) 199 | otmp->otyp = rnd_class(WAN_LIGHT, WAN_LIGHTNING); 200 | } 201 | } 202 | add_to_container(box, otmp); 203 | } 204 | if (gold) gold->owt = weight(gold); /* quantity was diddled */ 205 | return; 206 | } 207 | 208 | int 209 | rndmonnum() /* select a random, common monster type */ 210 | { 211 | register struct permonst *ptr; 212 | register int i; 213 | 214 | /* Plan A: get a level-appropriate common monster */ 215 | ptr = rndmonst(); 216 | if (ptr) return(monsndx(ptr)); 217 | 218 | /* Plan B: get any common monster */ 219 | do { 220 | i = rn1(SPECIAL_PM - LOW_PM, LOW_PM); 221 | ptr = &mons[i]; 222 | } while((ptr->geno & G_NOGEN) || (!Inhell && (ptr->geno & G_HELL))); 223 | 224 | return(i); 225 | } 226 | 227 | /* 228 | * Split obj so that it gets size num. The remainder is put in the object 229 | * structure delivered by this call. The object is positioned just 230 | * following the original in the nobj chain (and nexthere chain when on 231 | * the floor). 232 | */ 233 | struct obj * 234 | splitobj(obj, num) 235 | struct obj *obj; 236 | long num; 237 | { 238 | struct obj *otmp; 239 | 240 | if (obj->cobj || num <= 0L || obj->quan < num) 241 | panic("splitobj"); /* can't split containers */ 242 | otmp = newobj(obj->oxlth + obj->onamelth); 243 | *otmp = *obj; /* copies whole structure */ 244 | otmp->o_id = flags.ident++; 245 | if (!otmp->o_id) otmp->o_id = flags.ident++; /* ident overflowed */ 246 | otmp->timed = 0; /* not timed, yet */ 247 | otmp->lamplit = 0; /* ditto */ 248 | obj->quan = num; 249 | obj->owt = weight(obj); 250 | otmp->quan -= num; 251 | otmp->owt = weight(otmp); /* -= obj->owt ? */ 252 | obj->nobj = otmp; 253 | /* Only set nexthere when on the floor, nexthere is also used */ 254 | /* as a back pointer to the container object when contained. */ 255 | if (obj->where == OBJ_FLOOR) 256 | obj->nexthere = otmp; 257 | if (obj->oxlth) 258 | (void)memcpy((genericptr_t)otmp->oextra, (genericptr_t)obj->oextra, 259 | obj->oxlth); 260 | if (obj->onamelth) 261 | (void)strncpy(ONAME(otmp), ONAME(obj), (int)obj->onamelth); 262 | if (obj->unpaid) splitbill(obj,otmp); 263 | if (obj->timed) obj_split_timers(obj, otmp); 264 | if (obj_sheds_light(obj)) obj_split_light_source(obj, otmp); 265 | return otmp; 266 | } 267 | 268 | /* 269 | * Insert otmp right after obj in whatever chain(s) it is on. Then extract 270 | * obj from the chain(s). This function does a literal swap. It is up to 271 | * the caller to provide a valid context for the swap. When done, obj will 272 | * still exist, but not on any chain. 273 | * 274 | * Note: Don't use use obj_extract_self() -- we are doing an in-place swap, 275 | * not actually moving something. 276 | */ 277 | void 278 | replace_object(obj, otmp) 279 | struct obj *obj; 280 | struct obj *otmp; 281 | { 282 | otmp->where = obj->where; 283 | switch (obj->where) { 284 | case OBJ_FREE: 285 | /* do nothing */ 286 | break; 287 | case OBJ_INVENT: 288 | otmp->nobj = obj->nobj; 289 | obj->nobj = otmp; 290 | extract_nobj(obj, &invent); 291 | break; 292 | case OBJ_CONTAINED: 293 | otmp->nobj = obj->nobj; 294 | otmp->ocontainer = obj->ocontainer; 295 | obj->nobj = otmp; 296 | extract_nobj(obj, &obj->ocontainer->cobj); 297 | break; 298 | case OBJ_MINVENT: 299 | otmp->nobj = obj->nobj; 300 | otmp->ocarry = obj->ocarry; 301 | obj->nobj = otmp; 302 | extract_nobj(obj, &obj->ocarry->minvent); 303 | break; 304 | case OBJ_FLOOR: 305 | otmp->nobj = obj->nobj; 306 | otmp->nexthere = obj->nexthere; 307 | otmp->ox = obj->ox; 308 | otmp->oy = obj->oy; 309 | obj->nobj = otmp; 310 | obj->nexthere = otmp; 311 | extract_nobj(obj, &fobj); 312 | extract_nexthere(obj, &level.objects[obj->ox][obj->oy]); 313 | break; 314 | default: 315 | panic("replace_object: obj position"); 316 | break; 317 | } 318 | } 319 | 320 | /* 321 | * Create a dummy duplicate to put on shop bill. The duplicate exists 322 | * only in the billobjs chain. This function is used when a shop object 323 | * is being altered, and a copy of the original is needed for billing 324 | * purposes. For example, when eating, where an interruption will yield 325 | * an object which is different from what it started out as; the "I x" 326 | * command needs to display the original object. 327 | * 328 | * The caller is responsible for checking otmp->unpaid and 329 | * costly_spot(u.ux, u.uy). This function will make otmp no charge. 330 | * 331 | * Note that check_unpaid_usage() should be used instead for partial 332 | * usage of an object. 333 | */ 334 | void 335 | bill_dummy_object(otmp) 336 | register struct obj *otmp; 337 | { 338 | register struct obj *dummy; 339 | 340 | if (otmp->unpaid) 341 | subfrombill(otmp, shop_keeper(*u.ushops)); 342 | dummy = newobj(otmp->oxlth + otmp->onamelth); 343 | *dummy = *otmp; 344 | dummy->where = OBJ_FREE; 345 | dummy->o_id = flags.ident++; 346 | if (!dummy->o_id) dummy->o_id = flags.ident++; /* ident overflowed */ 347 | dummy->timed = 0; 348 | if (otmp->oxlth) 349 | (void)memcpy((genericptr_t)dummy->oextra, 350 | (genericptr_t)otmp->oextra, otmp->oxlth); 351 | if (otmp->onamelth) 352 | (void)strncpy(ONAME(dummy), ONAME(otmp), (int)otmp->onamelth); 353 | if (Is_candle(dummy)) dummy->lamplit = 0; 354 | addtobill(dummy, FALSE, TRUE, TRUE); 355 | otmp->no_charge = 1; 356 | otmp->unpaid = 0; 357 | return; 358 | } 359 | 360 | #endif /* OVL1 */ 361 | #ifdef OVLB 362 | 363 | static const char dknowns[] = { 364 | WAND_CLASS, RING_CLASS, POTION_CLASS, SCROLL_CLASS, 365 | GEM_CLASS, SPBOOK_CLASS, WEAPON_CLASS, TOOL_CLASS, 0 366 | }; 367 | 368 | struct obj * 369 | mksobj(otyp, init, artif) 370 | int otyp; 371 | boolean init; 372 | boolean artif; 373 | { 374 | int mndx, tryct; 375 | struct obj *otmp; 376 | char let = objects[otyp].oc_class; 377 | 378 | otmp = newobj(0); 379 | *otmp = zeroobj; 380 | otmp->age = monstermoves; 381 | otmp->o_id = flags.ident++; 382 | if (!otmp->o_id) otmp->o_id = flags.ident++; /* ident overflowed */ 383 | otmp->quan = 1L; 384 | otmp->oclass = let; 385 | otmp->otyp = otyp; 386 | otmp->where = OBJ_FREE; 387 | otmp->dknown = index(dknowns, let) ? 0 : 1; 388 | if ((otmp->otyp >= ELVEN_SHIELD && otmp->otyp <= ORCISH_SHIELD) || 389 | otmp->otyp == SHIELD_OF_REFLECTION) 390 | otmp->dknown = 0; 391 | if (!objects[otmp->otyp].oc_uses_known) 392 | otmp->known = 1; 393 | #ifdef INVISIBLE_OBJECTS 394 | otmp->oinvis = !rn2(1250); 395 | #endif 396 | if (init) switch (let) { 397 | case WEAPON_CLASS: 398 | otmp->quan = is_multigen(otmp) ? (long) rn1(6,6) : 1L; 399 | if(!rn2(11)) { 400 | otmp->spe = rne(3); 401 | otmp->blessed = rn2(2); 402 | } else if(!rn2(10)) { 403 | curse(otmp); 404 | otmp->spe = -rne(3); 405 | } else blessorcurse(otmp, 10); 406 | if (is_poisonable(otmp) && !rn2(100)) 407 | otmp->opoisoned = 1; 408 | 409 | if (artif && !rn2(20)) 410 | otmp = mk_artifact(otmp, (aligntyp)A_NONE); 411 | break; 412 | case FOOD_CLASS: 413 | otmp->oeaten = 0; 414 | switch(otmp->otyp) { 415 | case CORPSE: 416 | /* possibly overridden by mkcorpstat() */ 417 | tryct = 50; 418 | do otmp->corpsenm = undead_to_corpse(rndmonnum()); 419 | while ((mvitals[otmp->corpsenm].mvflags & G_NOCORPSE) && (--tryct > 0)); 420 | if (tryct == 0) { 421 | /* perhaps rndmonnum() only wants to make G_NOCORPSE monsters on 422 | this level; let's create an adventurer's corpse instead, then */ 423 | otmp->corpsenm = PM_HUMAN; 424 | } 425 | start_corpse_timeout(otmp); 426 | break; 427 | case EGG: 428 | otmp->corpsenm = NON_PM; /* generic egg */ 429 | if (!rn2(3)) for (tryct = 200; tryct > 0; --tryct) { 430 | mndx = can_be_hatched(rndmonnum()); 431 | if (mndx != NON_PM && !dead_species(mndx, TRUE)) { 432 | otmp->corpsenm = mndx; /* typed egg */ 433 | attach_egg_hatch_timeout(otmp); 434 | break; 435 | } 436 | } 437 | break; 438 | case TIN: 439 | otmp->corpsenm = NON_PM; /* empty (so far) */ 440 | if (!rn2(6)) 441 | otmp->spe = 1; /* spinach */ 442 | else for (tryct = 200; tryct > 0; --tryct) { 443 | mndx = undead_to_corpse(rndmonnum()); 444 | if (mons[mndx].cnutrit && 445 | !(mvitals[mndx].mvflags & G_NOCORPSE)) { 446 | otmp->corpsenm = mndx; 447 | break; 448 | } 449 | } 450 | blessorcurse(otmp, 10); 451 | break; 452 | case SLIME_MOLD: 453 | otmp->spe = current_fruit; 454 | break; 455 | } 456 | if (otmp->otyp == CORPSE || otmp->otyp == MEAT_RING) break; 457 | /* fall into next case */ 458 | 459 | case GEM_CLASS: 460 | if (otmp->otyp == LOADSTONE) curse(otmp); 461 | else if (otmp->otyp == ROCK) otmp->quan = (long) rn1(6,6); 462 | else if (otmp->otyp == KELP_FROND) otmp->quan = (long) rnd(2); 463 | else if (otmp->otyp != LUCKSTONE && !rn2(6)) otmp->quan = 2L; 464 | else otmp->quan = 1L; 465 | break; 466 | case TOOL_CLASS: 467 | switch(otmp->otyp) { 468 | case TALLOW_CANDLE: 469 | case WAX_CANDLE: otmp->spe = 1; 470 | otmp->age = 20L * /* 400 or 200 */ 471 | (long)objects[otmp->otyp].oc_cost; 472 | otmp->lamplit = 0; 473 | otmp->quan = 1L + 474 | (long)(rn2(2) ? rn2(7) : 0); 475 | blessorcurse(otmp, 5); 476 | break; 477 | case BRASS_LANTERN: 478 | case OIL_LAMP: otmp->spe = 1; 479 | otmp->age = (long) rn1(500,1000); 480 | otmp->lamplit = 0; 481 | blessorcurse(otmp, 5); 482 | break; 483 | case MAGIC_LAMP: otmp->spe = 1; 484 | otmp->lamplit = 0; 485 | blessorcurse(otmp, 2); 486 | break; 487 | case CHEST: 488 | case LARGE_BOX: otmp->olocked = !!(rn2(5)); 489 | otmp->otrapped = !(rn2(10)); 490 | case ICE_BOX: 491 | case SACK: 492 | case OILSKIN_SACK: 493 | case BAG_OF_HOLDING: mkbox_cnts(otmp); 494 | break; 495 | #ifdef TOURIST 496 | case EXPENSIVE_CAMERA: 497 | #endif 498 | case TINNING_KIT: 499 | case MAGIC_MARKER: otmp->spe = rn1(70,30); 500 | break; 501 | case CAN_OF_GREASE: otmp->spe = rnd(25); 502 | blessorcurse(otmp, 10); 503 | break; 504 | case CRYSTAL_BALL: otmp->spe = rnd(5); 505 | blessorcurse(otmp, 2); 506 | break; 507 | case HORN_OF_PLENTY: 508 | case BAG_OF_TRICKS: otmp->spe = rnd(20); 509 | break; 510 | case FIGURINE: { int tryct2 = 0; 511 | do 512 | otmp->corpsenm = rndmonnum(); 513 | while(is_human(&mons[otmp->corpsenm]) 514 | && tryct2++ < 30); 515 | blessorcurse(otmp, 4); 516 | break; 517 | } 518 | case BELL_OF_OPENING: otmp->spe = 3; 519 | break; 520 | case MAGIC_FLUTE: 521 | case MAGIC_HARP: 522 | case FROST_HORN: 523 | case FIRE_HORN: 524 | case DRUM_OF_EARTHQUAKE: 525 | otmp->spe = rn1(5,4); 526 | break; 527 | } 528 | break; 529 | case AMULET_CLASS: 530 | if (otmp->otyp == AMULET_OF_YENDOR) flags.made_amulet = TRUE; 531 | if(rn2(10) && (otmp->otyp == AMULET_OF_STRANGULATION || 532 | otmp->otyp == AMULET_OF_CHANGE || 533 | otmp->otyp == AMULET_OF_RESTFUL_SLEEP)) { 534 | curse(otmp); 535 | } else blessorcurse(otmp, 10); 536 | case VENOM_CLASS: 537 | case CHAIN_CLASS: 538 | case BALL_CLASS: 539 | break; 540 | case POTION_CLASS: 541 | if (otmp->otyp == POT_OIL) 542 | otmp->age = MAX_OIL_IN_FLASK; /* amount of oil */ 543 | /* fall through */ 544 | case SCROLL_CLASS: 545 | #ifdef MAIL 546 | if (otmp->otyp != SCR_MAIL) 547 | #endif 548 | blessorcurse(otmp, 4); 549 | break; 550 | case SPBOOK_CLASS: 551 | blessorcurse(otmp, 17); 552 | break; 553 | case ARMOR_CLASS: 554 | if(rn2(10) && (otmp->otyp == FUMBLE_BOOTS || 555 | otmp->otyp == LEVITATION_BOOTS || 556 | otmp->otyp == HELM_OF_OPPOSITE_ALIGNMENT || 557 | otmp->otyp == GAUNTLETS_OF_FUMBLING || 558 | !rn2(11))) { 559 | curse(otmp); 560 | otmp->spe = -rne(3); 561 | } else if(!rn2(10)) { 562 | otmp->blessed = rn2(2); 563 | otmp->spe = rne(3); 564 | } else blessorcurse(otmp, 10); 565 | if (artif && !rn2(40)) 566 | otmp = mk_artifact(otmp, (aligntyp)A_NONE); 567 | /* simulate lacquered armor for samurai */ 568 | if (Role_if(PM_SAMURAI) && otmp->otyp == SPLINT_MAIL && 569 | (moves <= 1 || In_quest(&u.uz))) { 570 | #ifdef UNIXPC 571 | /* optimizer bitfield bug */ 572 | otmp->oerodeproof = 1; 573 | otmp->rknown = 1; 574 | #else 575 | otmp->oerodeproof = otmp->rknown = 1; 576 | #endif 577 | } 578 | break; 579 | case WAND_CLASS: 580 | if(otmp->otyp == WAN_WISHING) otmp->spe = rnd(3); else 581 | otmp->spe = rn1(5, 582 | (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4); 583 | blessorcurse(otmp, 17); 584 | otmp->recharged = 0; /* used to control recharging */ 585 | break; 586 | case RING_CLASS: 587 | if(objects[otmp->otyp].oc_charged) { 588 | blessorcurse(otmp, 3); 589 | if(rn2(10)) { 590 | if(rn2(10) && bcsign(otmp)) 591 | otmp->spe = bcsign(otmp) * rne(3); 592 | else otmp->spe = rn2(2) ? rne(3) : -rne(3); 593 | } 594 | /* make useless +0 rings much less common */ 595 | if (otmp->spe == 0) otmp->spe = rn2(4) - rn2(3); 596 | /* negative rings are usually cursed */ 597 | if (otmp->spe < 0 && rn2(5)) curse(otmp); 598 | } else if(rn2(10) && (otmp->otyp == RIN_TELEPORTATION || 599 | otmp->otyp == RIN_POLYMORPH || 600 | otmp->otyp == RIN_AGGRAVATE_MONSTER || 601 | otmp->otyp == RIN_HUNGER || !rn2(9))) { 602 | curse(otmp); 603 | } 604 | break; 605 | case ROCK_CLASS: 606 | switch (otmp->otyp) { 607 | case STATUE: 608 | /* possibly overridden by mkcorpstat() */ 609 | otmp->corpsenm = rndmonnum(); 610 | if (!verysmall(&mons[otmp->corpsenm]) && 611 | rn2(level_difficulty()/2 + 10) > 10) 612 | add_to_container(otmp, mkobj(SPBOOK_CLASS,FALSE)); 613 | } 614 | break; 615 | case GOLD_CLASS: 616 | break; /* do nothing */ 617 | default: 618 | impossible("impossible mkobj %d, sym '%c'.", otmp->otyp, 619 | objects[otmp->otyp].oc_class); 620 | return (struct obj *)0; 621 | } 622 | /* unique objects may have an associated artifact entry */ 623 | if (objects[otyp].oc_unique && !otmp->oartifact) 624 | otmp = mk_artifact(otmp, (aligntyp)A_NONE); 625 | otmp->owt = weight(otmp); 626 | return(otmp); 627 | } 628 | 629 | /* 630 | * Start a corpse decay or revive timer. This assumes that the corpse 631 | * was just dropped and its age is 0. 632 | */ 633 | void 634 | start_corpse_timeout(body) 635 | struct obj *body; 636 | { 637 | long when; 638 | int rot_adjust; 639 | short action; 640 | 641 | #define TAINT_AGE (50L) /* age when corpses go bad */ 642 | #define TROLL_REVIVE_CHANCE 37 /* 1/37 chance for 50 turns ~ 75% chance */ 643 | #define ROT_AGE (250L) /* age when corpses rot away */ 644 | 645 | /* lizards and lichen don't rot or revive */ 646 | if (body->corpsenm == PM_LIZARD || body->corpsenm == PM_LICHEN) return; 647 | 648 | action = ROT_CORPSE; /* default action: rot away */ 649 | when = ROT_AGE; /* rot away when this old */ 650 | rot_adjust = in_mklev ? 25 : 10; /* give some variation */ 651 | when += (long)(rnz(rot_adjust) - rot_adjust); 652 | 653 | if (is_rider(&mons[body->corpsenm])) { 654 | /* 655 | * Riders always revive. They have a 1/3 chance per turn 656 | * of reviving after 12 turns. Always revive by 500. 657 | */ 658 | action = REVIVE_MON; 659 | for (when = 12L; when < 500L; when++) 660 | if (!rn2(3)) break; 661 | 662 | } else if (mons[body->corpsenm].mlet == S_TROLL) { 663 | long age; 664 | for (age = 2; age <= TAINT_AGE; age++) 665 | if (!rn2(TROLL_REVIVE_CHANCE)) { /* troll revives */ 666 | action = REVIVE_MON; 667 | when = age; 668 | break; 669 | } 670 | } 671 | 672 | (void) start_timer(when, TIMER_OBJECT, action, (genericptr_t)body); 673 | } 674 | 675 | void 676 | bless(otmp) 677 | register struct obj *otmp; 678 | { 679 | otmp->cursed = 0; 680 | otmp->blessed = 1; 681 | if (otmp->otyp == LUCKSTONE 682 | || (otmp->oartifact && spec_ability(otmp, SPFX_LUCK))) 683 | set_moreluck(); 684 | else if (otmp->otyp == BAG_OF_HOLDING) 685 | otmp->owt = weight(otmp); 686 | else if (otmp->otyp == FIGURINE && otmp->timed) 687 | (void) stop_timer(FIG_TRANSFORM, (genericptr_t) otmp); 688 | return; 689 | } 690 | 691 | void 692 | unbless(otmp) 693 | register struct obj *otmp; 694 | { 695 | otmp->blessed = 0; 696 | if (otmp->otyp == LUCKSTONE 697 | || (otmp->oartifact && spec_ability(otmp, SPFX_LUCK))) 698 | set_moreluck(); 699 | else if (otmp->otyp == BAG_OF_HOLDING) 700 | otmp->owt = weight(otmp); 701 | } 702 | 703 | void 704 | curse(otmp) 705 | register struct obj *otmp; 706 | { 707 | otmp->blessed = 0; 708 | otmp->cursed = 1; 709 | if (otmp->otyp == LUCKSTONE 710 | || (otmp->oartifact && spec_ability(otmp, SPFX_LUCK))) 711 | set_moreluck(); 712 | else if (otmp->otyp == BAG_OF_HOLDING) 713 | otmp->owt = weight(otmp); 714 | else if (otmp->otyp == FIGURINE) { 715 | if (otmp->corpsenm != NON_PM 716 | && !dead_species(otmp->corpsenm,TRUE) 717 | && (carried(otmp) || mcarried(otmp))) 718 | attach_fig_transform_timeout(otmp); 719 | } 720 | return; 721 | } 722 | 723 | void 724 | uncurse(otmp) 725 | register struct obj *otmp; 726 | { 727 | otmp->cursed = 0; 728 | if (otmp->otyp == LUCKSTONE 729 | || (otmp->oartifact && spec_ability(otmp, SPFX_LUCK))) 730 | set_moreluck(); 731 | else if (otmp->otyp == BAG_OF_HOLDING) 732 | otmp->owt = weight(otmp); 733 | else if (otmp->otyp == FIGURINE && otmp->timed) 734 | (void) stop_timer(FIG_TRANSFORM, (genericptr_t) otmp); 735 | return; 736 | } 737 | 738 | #endif /* OVLB */ 739 | #ifdef OVL1 740 | 741 | void 742 | blessorcurse(otmp, chance) 743 | register struct obj *otmp; 744 | register int chance; 745 | { 746 | if(otmp->blessed || otmp->cursed) return; 747 | 748 | if(!rn2(chance)) { 749 | if(!rn2(2)) { 750 | curse(otmp); 751 | } else { 752 | bless(otmp); 753 | } 754 | } 755 | return; 756 | } 757 | 758 | #endif /* OVL1 */ 759 | #ifdef OVLB 760 | 761 | int 762 | bcsign(otmp) 763 | register struct obj *otmp; 764 | { 765 | return(!!otmp->blessed - !!otmp->cursed); 766 | } 767 | 768 | #endif /* OVLB */ 769 | #ifdef OVL0 770 | 771 | /* 772 | * Calculate the weight of the given object. This will recursively follow 773 | * and calculate the weight of any containers. 774 | * 775 | * Note: It is possible to end up with an incorrect weight if some part 776 | * of the code messes with a contained object and doesn't update the 777 | * container's weight. 778 | */ 779 | int 780 | weight(obj) 781 | register struct obj *obj; 782 | { 783 | int wt = objects[obj->otyp].oc_weight; 784 | 785 | if (obj->otyp == LARGE_BOX && obj->spe == 1) /* Schroedinger's Cat */ 786 | wt += mons[PM_HOUSECAT].cwt; 787 | if (Is_container(obj) || obj->otyp == STATUE) { 788 | struct obj *contents; 789 | register int cwt = 0; 790 | 791 | if (obj->otyp == STATUE && obj->corpsenm >= LOW_PM) 792 | wt = (int)obj->quan * 793 | ((int)mons[obj->corpsenm].cwt * 3 / 2); 794 | 795 | for(contents=obj->cobj; contents; contents=contents->nobj) 796 | cwt += weight(contents); 797 | /* 798 | * The weight of bags of holding is calculated as the weight 799 | * of the bag plus the weight of the bag's contents modified 800 | * as follows: 801 | * 802 | * Bag status Weight of contents 803 | * ---------- ------------------ 804 | * cursed 2x 805 | * blessed x/4 + 1 806 | * otherwise x/2 + 1 807 | * 808 | * The macro DELTA_CWT in pickup.c also implements these 809 | * weight equations. 810 | * 811 | * Note: The above checks are performed in the given order. 812 | * this means that if an object is both blessed and 813 | * cursed (not supposed to happen), it will be treated 814 | * as cursed. 815 | */ 816 | if (obj->otyp == BAG_OF_HOLDING) 817 | cwt = obj->cursed ? (cwt * 2) : 818 | (1 + (cwt / (obj->blessed ? 4 : 2))); 819 | 820 | return wt + cwt; 821 | } 822 | if (obj->otyp == CORPSE && obj->corpsenm >= LOW_PM) 823 | return (int)obj->quan * mons[obj->corpsenm].cwt; 824 | else if (obj->oclass == GOLD_CLASS) 825 | return (int)((obj->quan + 50L) / 100L); 826 | else if (obj->otyp == HEAVY_IRON_BALL && obj->owt != 0) 827 | return((int)(obj->owt)); /* kludge for "very" heavy iron ball */ 828 | return(wt ? wt*(int)obj->quan : ((int)obj->quan + 1)>>1); 829 | } 830 | 831 | static int treefruits[] = {APPLE,ORANGE,PEAR,BANANA,EUCALYPTUS_LEAF}; 832 | 833 | struct obj * 834 | rnd_treefruit_at(x,y) 835 | { 836 | return mksobj_at(treefruits[rn2(SIZE(treefruits)-1)],x,y,TRUE); 837 | } 838 | #endif /* OVL0 */ 839 | #ifdef OVLB 840 | 841 | struct obj * 842 | mkgold(amount, x, y) 843 | long amount; 844 | int x, y; 845 | { 846 | register struct obj *gold = g_at(x,y); 847 | 848 | if (amount <= 0L) amount = (long)(1 + rnd(level_difficulty()+2) * rnd(30)); 849 | if (gold) { 850 | gold->quan += amount; 851 | } else { 852 | gold = mksobj_at(GOLD_PIECE,x,y,TRUE); 853 | gold->quan = amount; 854 | } 855 | gold->owt = weight(gold); 856 | return (gold); 857 | } 858 | 859 | #endif /* OVLB */ 860 | #ifdef OVL1 861 | 862 | /* return TRUE if the corpse has special timing */ 863 | #define special_corpse(num) (((num) == PM_LIZARD) \ 864 | || ((num) == PM_LICHEN) \ 865 | || (is_rider(&mons[num])) \ 866 | || (mons[num].mlet == S_TROLL)) 867 | 868 | /* 869 | * OEXTRA note: Passing mtmp causes mtraits to be saved 870 | * even if ptr passed as well, but ptr is always used for 871 | * the corpse type (corpsenm). That allows the corpse type 872 | * to be different from the original monster, 873 | * i.e. vampire -> human corpse 874 | * yet still allow restoration of the original monster upon 875 | * resurrection. 876 | */ 877 | struct obj * 878 | mkcorpstat(objtype, mtmp, ptr, x, y, init) 879 | int objtype; /* CORPSE or STATUE */ 880 | struct monst *mtmp; 881 | struct permonst *ptr; 882 | int x, y; 883 | boolean init; 884 | { 885 | register struct obj *otmp; 886 | 887 | if (objtype != CORPSE && objtype != STATUE) 888 | impossible("making corpstat type %d", objtype); 889 | otmp = mksobj_at(objtype, x, y, init); 890 | if (otmp) { 891 | if (mtmp) { 892 | struct obj *otmp2; 893 | 894 | if (!ptr) ptr = mtmp->data; 895 | /* save_mtraits frees original data pointed to by otmp */ 896 | otmp2 = save_mtraits(otmp, mtmp); 897 | if (otmp2) otmp = otmp2; 898 | } 899 | /* use the corpse or statue produced by mksobj() as-is 900 | unless `ptr' is non-null */ 901 | if (ptr) { 902 | int old_corpsenm = otmp->corpsenm; 903 | 904 | otmp->corpsenm = monsndx(ptr); 905 | otmp->owt = weight(otmp); 906 | if (otmp->otyp == CORPSE && 907 | (special_corpse(old_corpsenm) || 908 | special_corpse(otmp->corpsenm))) { 909 | obj_stop_timers(otmp); 910 | start_corpse_timeout(otmp); 911 | } 912 | } 913 | } 914 | return(otmp); 915 | } 916 | 917 | /* 918 | * Attach a monster id to an object, to provide 919 | * a lasting association between the two. 920 | */ 921 | struct obj * 922 | obj_attach_mid(obj, mid) 923 | struct obj *obj; 924 | unsigned mid; 925 | { 926 | struct obj *otmp; 927 | int lth, namelth; 928 | 929 | if (!mid || !obj) return (struct obj *)0; 930 | lth = sizeof(mid); 931 | namelth = obj->onamelth ? strlen(ONAME(obj)) + 1 : 0; 932 | if (namelth) 933 | otmp = realloc_obj(obj, lth, (genericptr_t) &mid, namelth, ONAME(obj)); 934 | else { 935 | otmp = obj; 936 | otmp->oxlth = sizeof(mid); 937 | (void) memcpy((genericptr_t)otmp->oextra, (genericptr_t)&mid, 938 | sizeof(mid)); 939 | } 940 | if (otmp && otmp->oxlth) otmp->oattached = OATTACHED_M_ID; /* mark it */ 941 | return otmp; 942 | } 943 | 944 | static struct obj * 945 | save_mtraits(obj, mtmp) 946 | struct obj *obj; 947 | struct monst *mtmp; 948 | { 949 | struct obj *otmp; 950 | int lth, namelth; 951 | 952 | lth = sizeof(struct monst) + mtmp->mxlth + mtmp->mnamelth; 953 | namelth = obj->onamelth ? strlen(ONAME(obj)) + 1 : 0; 954 | otmp = realloc_obj(obj, lth, (genericptr_t) mtmp, namelth, ONAME(obj)); 955 | if (otmp && otmp->oxlth) { 956 | struct monst *mtmp2 = (struct monst *)otmp->oextra; 957 | if (mtmp->data) mtmp2->mnum = monsndx(mtmp->data); 958 | /* invalidate pointers and m_id */ 959 | mtmp2->m_id = 0; 960 | mtmp2->nmon = (struct monst *)0; 961 | mtmp2->data = (struct permonst *)0; 962 | mtmp2->minvent = (struct obj *)0; 963 | otmp->oattached = OATTACHED_MONST; /* mark it */ 964 | } 965 | return otmp; 966 | } 967 | 968 | /* returns a pointer to a new monst structure based on 969 | * the one contained within the obj. 970 | */ 971 | struct monst * 972 | get_mtraits(obj, copyof) 973 | struct obj *obj; 974 | boolean copyof; 975 | { 976 | struct monst *mtmp = (struct monst *)0; 977 | struct monst *mnew = (struct monst *)0; 978 | 979 | if (obj->oxlth && obj->oattached == OATTACHED_MONST) 980 | mtmp = (struct monst *)obj->oextra; 981 | if (mtmp) { 982 | if (copyof) { 983 | int lth = mtmp->mxlth + mtmp->mnamelth; 984 | mnew = newmonst(lth); 985 | lth += sizeof(struct monst); 986 | (void) memcpy((genericptr_t)mnew, 987 | (genericptr_t)mtmp, lth); 988 | } else { 989 | /* Never insert this returned pointer into mon chains! */ 990 | mnew = mtmp; 991 | } 992 | } 993 | return mnew; 994 | } 995 | 996 | #endif /* OVL1 */ 997 | #ifdef OVLB 998 | 999 | /* make an object named after someone listed in the scoreboard file */ 1000 | struct obj * 1001 | mk_tt_object(objtype, x, y) 1002 | int objtype; /* CORPSE or STATUE */ 1003 | register int x, y; 1004 | { 1005 | register struct obj *otmp, *otmp2; 1006 | boolean initialize_it; 1007 | 1008 | /* player statues never contain books */ 1009 | initialize_it = (objtype != STATUE); 1010 | if ((otmp = mksobj_at(objtype, x, y, initialize_it)) != 0) { 1011 | /* tt_oname will return null if the scoreboard is empty */ 1012 | if ((otmp2 = tt_oname(otmp)) != 0) otmp = otmp2; 1013 | } 1014 | return(otmp); 1015 | } 1016 | 1017 | /* make a new corpse or statue, uninitialized if a statue (i.e. no books) */ 1018 | struct obj * 1019 | mk_named_object(objtype, ptr, x, y, nm) 1020 | int objtype; /* CORPSE or STATUE */ 1021 | struct permonst *ptr; 1022 | int x, y; 1023 | const char *nm; 1024 | { 1025 | struct obj *otmp; 1026 | 1027 | otmp = mkcorpstat(objtype, (struct monst *)0, ptr, 1028 | x, y, (boolean)(objtype != STATUE)); 1029 | if (nm) 1030 | otmp = oname(otmp, nm); 1031 | return(otmp); 1032 | } 1033 | 1034 | boolean 1035 | is_flammable(otmp) 1036 | register struct obj *otmp; 1037 | { 1038 | int otyp = otmp->otyp; 1039 | 1040 | if (objects[otyp].oc_oprop == FIRE_RES) return FALSE; 1041 | 1042 | return((boolean)(objects[otyp].oc_material <= WOOD && 1043 | objects[otyp].oc_material != LIQUID)); 1044 | } 1045 | 1046 | #endif /* OVLB */ 1047 | #ifdef OVL1 1048 | 1049 | /* 1050 | * These routines maintain the single-linked lists headed in level.objects[][] 1051 | * and threaded through the nexthere fields in the object-instance structure. 1052 | */ 1053 | 1054 | /* put the object at the given location */ 1055 | void 1056 | place_object(otmp, x, y) 1057 | register struct obj *otmp; 1058 | int x, y; 1059 | { 1060 | register struct obj *otmp2 = level.objects[x][y]; 1061 | 1062 | if (otmp->where != OBJ_FREE) 1063 | panic("place_object: obj not free"); 1064 | 1065 | if (otmp->otyp == BOULDER) block_point(x,y); /* vision */ 1066 | 1067 | /* obj goes under boulders */ 1068 | if (otmp2 && (otmp2->otyp == BOULDER)) { 1069 | otmp->nexthere = otmp2->nexthere; 1070 | otmp2->nexthere = otmp; 1071 | } else { 1072 | otmp->nexthere = otmp2; 1073 | level.objects[x][y] = otmp; 1074 | } 1075 | 1076 | /* set the new object's location */ 1077 | otmp->ox = x; 1078 | otmp->oy = y; 1079 | 1080 | otmp->where = OBJ_FLOOR; 1081 | 1082 | /* add to floor chain */ 1083 | otmp->nobj = fobj; 1084 | fobj = otmp; 1085 | if (otmp->timed) obj_timer_checks(otmp, x, y, 0); 1086 | } 1087 | 1088 | #define ON_ICE(a) ((a)->recharged) 1089 | #define ROT_ICE_ADJUSTMENT 2 /* rotting on ice takes 2 times as long */ 1090 | 1091 | /* If ice was affecting any objects correct that now 1092 | * Also used for starting ice effects too. [zap.c] 1093 | */ 1094 | void 1095 | obj_ice_effects(x, y, do_buried) 1096 | int x, y; 1097 | boolean do_buried; 1098 | { 1099 | struct obj *otmp; 1100 | 1101 | for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) { 1102 | if (otmp->timed) obj_timer_checks(otmp, x, y, 0); 1103 | } 1104 | if (do_buried) { 1105 | for (otmp = level.buriedobjlist; otmp; otmp = otmp->nobj) { 1106 | if (otmp->ox == x && otmp->oy == y) { 1107 | if (otmp->timed) obj_timer_checks(otmp, x, y, 0); 1108 | } 1109 | } 1110 | } 1111 | } 1112 | 1113 | /* 1114 | * Returns an obj->age for a corpse object on ice, that would be the 1115 | * actual obj->age if the corpse had just been lifted from the ice. 1116 | * This is useful when just using obj->age in a check or calculation because 1117 | * rot timers pertaining to the object don't have to be stopped and 1118 | * restarted etc. 1119 | */ 1120 | long 1121 | peek_at_iced_corpse_age(otmp) 1122 | struct obj *otmp; 1123 | { 1124 | long age, retval = otmp->age; 1125 | 1126 | if (otmp->otyp == CORPSE && ON_ICE(otmp)) { 1127 | /* Adjust the age; must be same as obj_timer_checks() for off ice*/ 1128 | age = monstermoves - otmp->age; 1129 | retval = otmp->age + (age / ROT_ICE_ADJUSTMENT); 1130 | #ifdef DEBUG_EFFECTS 1131 | pline_The("%s age has ice modifications:otmp->age = %ld, returning %ld.", 1132 | s_suffix(doname(otmp)),otmp->age, retval); 1133 | pline("Effective age of corpse: %ld.", 1134 | monstermoves - retval); 1135 | #endif 1136 | } 1137 | return retval; 1138 | } 1139 | 1140 | STATIC_OVL void 1141 | obj_timer_checks(otmp, x, y, force) 1142 | struct obj *otmp; 1143 | xchar x, y; 1144 | int force; /* 0 = no force so do checks, <0 = force off, >0 force on */ 1145 | { 1146 | long tleft = 0L; 1147 | short action = ROT_CORPSE; 1148 | boolean restart_timer = FALSE; 1149 | boolean on_floor = (otmp->where == OBJ_FLOOR); 1150 | boolean buried = (otmp->where == OBJ_BURIED); 1151 | 1152 | /* Check for corpses just placed on or in ice */ 1153 | if (otmp->otyp == CORPSE && (on_floor || buried) && is_ice(x,y)) { 1154 | tleft = stop_timer(action, (genericptr_t)otmp); 1155 | if (tleft == 0L) { 1156 | action = REVIVE_MON; 1157 | tleft = stop_timer(action, (genericptr_t)otmp); 1158 | } 1159 | if (tleft != 0L) { 1160 | long age; 1161 | 1162 | tleft = tleft - monstermoves; 1163 | /* mark the corpse as being on ice */ 1164 | ON_ICE(otmp) = 1; 1165 | #ifdef DEBUG_EFFECTS 1166 | pline("%s is now on ice at %d,%d.", The(xname(otmp)),x,y); 1167 | #endif 1168 | /* Adjust the time remaining */ 1169 | tleft *= ROT_ICE_ADJUSTMENT; 1170 | restart_timer = TRUE; 1171 | /* Adjust the age; must be same as in obj_ice_age() */ 1172 | age = monstermoves - otmp->age; 1173 | otmp->age = monstermoves - (age * ROT_ICE_ADJUSTMENT); 1174 | } 1175 | } 1176 | /* Check for corpses coming off ice */ 1177 | else if ((force < 0) || 1178 | (otmp->otyp == CORPSE && ON_ICE(otmp) && 1179 | ((on_floor && !is_ice(x,y)) || !on_floor))) { 1180 | tleft = stop_timer(action, (genericptr_t)otmp); 1181 | if (tleft == 0L) { 1182 | action = REVIVE_MON; 1183 | tleft = stop_timer(action, (genericptr_t)otmp); 1184 | } 1185 | if (tleft != 0L) { 1186 | long age; 1187 | 1188 | tleft = tleft - monstermoves; 1189 | ON_ICE(otmp) = 0; 1190 | #ifdef DEBUG_EFFECTS 1191 | pline("%s is no longer on ice at %d,%d.", The(xname(otmp)),x,y); 1192 | #endif 1193 | /* Adjust the remaining time */ 1194 | tleft /= ROT_ICE_ADJUSTMENT; 1195 | restart_timer = TRUE; 1196 | /* Adjust the age */ 1197 | age = monstermoves - otmp->age; 1198 | otmp->age = otmp->age + (age / ROT_ICE_ADJUSTMENT); 1199 | } 1200 | } 1201 | /* now re-start the timer with the appropriate modifications */ 1202 | if (restart_timer) 1203 | (void) start_timer(tleft, TIMER_OBJECT, action, (genericptr_t)otmp); 1204 | } 1205 | 1206 | #undef ON_ICE 1207 | #undef ROT_ICE_ADJUSTMENT 1208 | 1209 | void 1210 | remove_object(otmp) 1211 | register struct obj *otmp; 1212 | { 1213 | xchar x = otmp->ox; 1214 | xchar y = otmp->oy; 1215 | 1216 | if (otmp->where != OBJ_FLOOR) 1217 | panic("remove_object: obj not on floor"); 1218 | if (otmp->otyp == BOULDER) unblock_point(x,y); /* vision */ 1219 | extract_nexthere(otmp, &level.objects[x][y]); 1220 | extract_nobj(otmp, &fobj); 1221 | if (otmp->timed) obj_timer_checks(otmp,x,y,0); 1222 | } 1223 | 1224 | /* throw away all of a monster's inventory */ 1225 | void 1226 | discard_minvent(mtmp) 1227 | struct monst *mtmp; 1228 | { 1229 | struct obj *otmp; 1230 | 1231 | while ((otmp = mtmp->minvent) != 0) { 1232 | obj_extract_self(otmp); 1233 | obfree(otmp, (struct obj *)0); /* dealloc_obj() isn't sufficient */ 1234 | } 1235 | } 1236 | 1237 | /* 1238 | * Free obj from whatever list it is on in preperation of deleting it or 1239 | * moving it elsewhere. This will perform all high-level consequences 1240 | * involved with removing the item. E.g. if the object is in the hero's 1241 | * inventory and confers heat resistance, the hero will lose it. 1242 | * 1243 | * Object positions: 1244 | * OBJ_FREE not on any list 1245 | * OBJ_FLOOR fobj, level.locations[][] chains (use remove_object) 1246 | * OBJ_CONTAINED cobj chain of container object 1247 | * OBJ_INVENT hero's invent chain (use freeinv) 1248 | * OBJ_MINVENT monster's invent chain 1249 | * OBJ_MIGRATING migrating chain 1250 | * OBJ_BURIED level.buriedobjs chain 1251 | * OBJ_ONBILL on billobjs chain 1252 | */ 1253 | void 1254 | obj_extract_self(obj) 1255 | struct obj *obj; 1256 | { 1257 | switch (obj->where) { 1258 | case OBJ_FREE: 1259 | break; 1260 | case OBJ_FLOOR: 1261 | remove_object(obj); 1262 | break; 1263 | case OBJ_CONTAINED: 1264 | extract_nobj(obj, &obj->ocontainer->cobj); 1265 | container_weight(obj->ocontainer); 1266 | break; 1267 | case OBJ_INVENT: 1268 | freeinv(obj); 1269 | break; 1270 | case OBJ_MINVENT: 1271 | extract_nobj(obj, &obj->ocarry->minvent); 1272 | break; 1273 | case OBJ_MIGRATING: 1274 | extract_nobj(obj, &migrating_objs); 1275 | break; 1276 | case OBJ_BURIED: 1277 | extract_nobj(obj, &level.buriedobjlist); 1278 | break; 1279 | case OBJ_ONBILL: 1280 | extract_nobj(obj, &billobjs); 1281 | break; 1282 | default: 1283 | panic("obj_extract_self"); 1284 | break; 1285 | } 1286 | } 1287 | 1288 | 1289 | /* Extract the given object from the chain, following nobj chain. */ 1290 | void 1291 | extract_nobj(obj, head_ptr) 1292 | struct obj *obj, **head_ptr; 1293 | { 1294 | struct obj *curr, *prev; 1295 | 1296 | curr = *head_ptr; 1297 | for (prev = (struct obj *) 0; curr; prev = curr, curr = curr->nobj) { 1298 | if (curr == obj) { 1299 | if (prev) 1300 | prev->nobj = curr->nobj; 1301 | else 1302 | *head_ptr = curr->nobj; 1303 | break; 1304 | } 1305 | } 1306 | if (!curr) panic("extract_nobj: object lost"); 1307 | obj->where = OBJ_FREE; 1308 | } 1309 | 1310 | 1311 | /* 1312 | * Extract the given object from the chain, following nexthere chain. 1313 | * 1314 | * This does not set obj->where, this function is expected to be called 1315 | * in tandem with extract_nobj, which does set it. 1316 | */ 1317 | void 1318 | extract_nexthere(obj, head_ptr) 1319 | struct obj *obj, **head_ptr; 1320 | { 1321 | struct obj *curr, *prev; 1322 | 1323 | curr = *head_ptr; 1324 | for (prev = (struct obj *) 0; curr; prev = curr, curr = curr->nexthere) { 1325 | if (curr == obj) { 1326 | if (prev) 1327 | prev->nexthere = curr->nexthere; 1328 | else 1329 | *head_ptr = curr->nexthere; 1330 | break; 1331 | } 1332 | } 1333 | if (!curr) panic("extract_nexthere: object lost"); 1334 | } 1335 | 1336 | 1337 | /* 1338 | * Add obj to mon's inventory. If obj is able to merge with something already 1339 | * in the inventory, then the passed obj is deleted and 1 is returned. 1340 | * Otherwise 0 is returned. 1341 | */ 1342 | int 1343 | add_to_minv(mon, obj) 1344 | struct monst *mon; 1345 | struct obj *obj; 1346 | { 1347 | struct obj *otmp; 1348 | 1349 | if (obj->where != OBJ_FREE) 1350 | panic("add_to_minv: obj not free"); 1351 | 1352 | /* merge if possible */ 1353 | for (otmp = mon->minvent; otmp; otmp = otmp->nobj) 1354 | if (merged(&otmp, &obj)) 1355 | return 1; /* obj merged and then free'd */ 1356 | /* else insert; don't bother forcing it to end of chain */ 1357 | obj->where = OBJ_MINVENT; 1358 | obj->ocarry = mon; 1359 | obj->nobj = mon->minvent; 1360 | mon->minvent = obj; 1361 | return 0; /* obj on mon's inventory chain */ 1362 | } 1363 | 1364 | void 1365 | add_to_container(container, obj) 1366 | struct obj *container, *obj; 1367 | { 1368 | if (obj->where != OBJ_FREE) 1369 | panic("add_to_container: obj not free"); 1370 | 1371 | obj->where = OBJ_CONTAINED; 1372 | obj->ocontainer = container; 1373 | obj->nobj = container->cobj; 1374 | container->cobj = obj; 1375 | } 1376 | 1377 | void 1378 | add_to_migration(obj) 1379 | struct obj *obj; 1380 | { 1381 | if (obj->where != OBJ_FREE) 1382 | panic("add_to_migration: obj not free"); 1383 | 1384 | obj->where = OBJ_MIGRATING; 1385 | obj->nobj = migrating_objs; 1386 | migrating_objs = obj; 1387 | } 1388 | 1389 | void 1390 | add_to_buried(obj) 1391 | struct obj *obj; 1392 | { 1393 | if (obj->where != OBJ_FREE) 1394 | panic("add_to_buried: obj not free"); 1395 | 1396 | obj->where = OBJ_BURIED; 1397 | obj->nobj = level.buriedobjlist; 1398 | level.buriedobjlist = obj; 1399 | } 1400 | 1401 | /* Recalculate the weight of this container and all of _its_ containers. */ 1402 | STATIC_OVL void 1403 | container_weight(container) 1404 | struct obj *container; 1405 | { 1406 | container->owt = weight(container); 1407 | if (container->where == OBJ_CONTAINED) 1408 | container_weight(container->ocontainer); 1409 | /* 1410 | else if (container->where == OBJ_INVENT) 1411 | recalculate load delay here ??? 1412 | */ 1413 | } 1414 | 1415 | /* 1416 | * Deallocate the object. _All_ objects should be run through here for 1417 | * them to be deallocated. 1418 | */ 1419 | void 1420 | dealloc_obj(obj) 1421 | struct obj *obj; 1422 | { 1423 | if (obj->where != OBJ_FREE) 1424 | panic("dealloc_obj: obj not free"); 1425 | 1426 | /* free up any timers attached to the object */ 1427 | if (obj->timed) 1428 | obj_stop_timers(obj); 1429 | 1430 | /* 1431 | * Free up any light sources attached to the object. 1432 | * 1433 | * We may want to just call del_light_source() without any 1434 | * checks (requires a code change there). Otherwise this 1435 | * list must track all objects that can have a light source 1436 | * attached to it (and also requires lamplit to be set). 1437 | */ 1438 | if (obj_sheds_light(obj)) 1439 | del_light_source(LS_OBJECT, (genericptr_t) obj); 1440 | 1441 | free((genericptr_t) obj); 1442 | } 1443 | 1444 | #ifdef WIZARD 1445 | /* Check all object lists for consistency. */ 1446 | void 1447 | obj_sanity_check() 1448 | { 1449 | int x, y; 1450 | struct obj *obj; 1451 | struct monst *mon; 1452 | const char *mesg; 1453 | char obj_address[20], mon_address[20]; /* room for formatted pointers */ 1454 | 1455 | mesg = "fobj sanity"; 1456 | for (obj = fobj; obj; obj = obj->nobj) { 1457 | if (obj->where != OBJ_FLOOR) { 1458 | pline("%s obj %s %s@(%d,%d): %s\n", mesg, 1459 | fmt_ptr((genericptr_t)obj, obj_address), 1460 | where_name(obj->where), 1461 | obj->ox, obj->oy, doname(obj)); 1462 | } 1463 | check_contained(obj, mesg); 1464 | } 1465 | 1466 | mesg = "location sanity"; 1467 | for (x = 0; x < COLNO; x++) 1468 | for (y = 0; y < ROWNO; y++) 1469 | for (obj = level.objects[x][y]; obj; obj = obj->nexthere) 1470 | if (obj->where != OBJ_FLOOR) { 1471 | pline("%s obj %s %s@(%d,%d): %s\n", mesg, 1472 | fmt_ptr((genericptr_t)obj, obj_address), 1473 | where_name(obj->where), 1474 | obj->ox, obj->oy, doname(obj)); 1475 | } 1476 | 1477 | mesg = "invent sanity"; 1478 | for (obj = invent; obj; obj = obj->nobj) { 1479 | if (obj->where != OBJ_INVENT) { 1480 | pline("%s obj %s %s: %s\n", mesg, 1481 | fmt_ptr((genericptr_t)obj, obj_address), 1482 | where_name(obj->where), doname(obj)); 1483 | } 1484 | check_contained(obj, mesg); 1485 | } 1486 | 1487 | mesg = "migrating sanity"; 1488 | for (obj = migrating_objs; obj; obj = obj->nobj) { 1489 | if (obj->where != OBJ_MIGRATING) { 1490 | pline("%s obj %s %s: %s\n", mesg, 1491 | fmt_ptr((genericptr_t)obj, obj_address), 1492 | where_name(obj->where), doname(obj)); 1493 | } 1494 | check_contained(obj, mesg); 1495 | } 1496 | 1497 | mesg = "buried sanity"; 1498 | for (obj = level.buriedobjlist; obj; obj = obj->nobj) { 1499 | if (obj->where != OBJ_BURIED) { 1500 | pline("%s obj %s %s: %s\n", mesg, 1501 | fmt_ptr((genericptr_t)obj, obj_address), 1502 | where_name(obj->where), doname(obj)); 1503 | } 1504 | check_contained(obj, mesg); 1505 | } 1506 | 1507 | mesg = "bill sanity"; 1508 | for (obj = billobjs; obj; obj = obj->nobj) { 1509 | if (obj->where != OBJ_ONBILL) { 1510 | pline("%s obj %s %s: %s\n", mesg, 1511 | fmt_ptr((genericptr_t)obj, obj_address), 1512 | where_name(obj->where), doname(obj)); 1513 | } 1514 | /* shouldn't be a full container on the bill */ 1515 | if (obj->cobj) { 1516 | pline("%s obj %s contains %s! %s\n", mesg, 1517 | fmt_ptr((genericptr_t)obj, obj_address), 1518 | something, doname(obj)); 1519 | } 1520 | } 1521 | 1522 | mesg = "minvent sanity"; 1523 | for (mon = fmon; mon; mon = mon->nmon) 1524 | for (obj = mon->minvent; obj; obj = obj->nobj) { 1525 | if (obj->where != OBJ_MINVENT) { 1526 | pline("%s obj %s %s: %s\n", mesg, 1527 | fmt_ptr((genericptr_t)obj, obj_address), 1528 | where_name(obj->where), doname(obj)); 1529 | } 1530 | if (obj->ocarry != mon) { 1531 | pline("%s obj %s (%s) not held by mon %s (%s)\n", mesg, 1532 | fmt_ptr((genericptr_t)obj, obj_address), 1533 | doname(obj), 1534 | fmt_ptr((genericptr_t)mon, mon_address), 1535 | mon_nam(mon)); 1536 | } 1537 | check_contained(obj, mesg); 1538 | } 1539 | } 1540 | 1541 | /* This must stay consistent with the defines in obj.h. */ 1542 | static const char *obj_state_names[NOBJ_STATES] = { 1543 | "free", "floor", "contained", "invent", 1544 | "minvent", "migrating", "buried", "onbill" 1545 | }; 1546 | 1547 | STATIC_OVL const char * 1548 | where_name(where) 1549 | int where; 1550 | { 1551 | return (where<0 || where>=NOBJ_STATES) ? "unknown" : obj_state_names[where]; 1552 | } 1553 | 1554 | /* obj sanity check: check objs contained by container */ 1555 | STATIC_OVL void 1556 | check_contained(container, mesg) 1557 | struct obj *container; 1558 | const char *mesg; 1559 | { 1560 | struct obj *obj; 1561 | char obj1_address[20], obj2_address[20]; 1562 | 1563 | for (obj = container->cobj; obj; obj = obj->nobj) { 1564 | if (obj->where != OBJ_CONTAINED) 1565 | pline("contained %s obj %s: %s\n", mesg, 1566 | fmt_ptr((genericptr_t)obj, obj1_address), 1567 | where_name(obj->where)); 1568 | else if (obj->ocontainer != container) 1569 | pline("%s obj %s not in container %s\n", mesg, 1570 | fmt_ptr((genericptr_t)obj, obj1_address), 1571 | fmt_ptr((genericptr_t)container, obj2_address)); 1572 | } 1573 | } 1574 | #endif /* WIZARD */ 1575 | 1576 | #endif /* OVL1 */ 1577 | 1578 | /*mkobj.c*/