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*/