1 | /* SCCS Id: @(#)steal.c 3.3 1999/02/13 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #include "hack.h"
6 |
7 | STATIC_PTR int NDECL(stealarm);
8 |
9 | #ifdef OVLB
10 | STATIC_DCL const char *FDECL(equipname, (struct obj *));
11 |
12 | STATIC_OVL const char *
13 | equipname(otmp)
14 | register struct obj *otmp;
15 | {
16 | return (
17 | #ifdef TOURIST
18 | (otmp == uarmu) ? "shirt" :
19 | #endif
20 | (otmp == uarmf) ? "boots" :
21 | (otmp == uarms) ? "shield" :
22 | (otmp == uarmg) ? "gloves" :
23 | (otmp == uarmc) ? "cloak" :
24 | (otmp == uarmh) ? "helmet" : "armor");
25 | }
26 |
27 | long /* actually returns something that fits in an int */
28 | somegold()
29 | {
30 | #ifdef LINT /* long conv. ok */
31 | return(0L);
32 | #else
33 | return (long)( (u.ugold < 100) ? u.ugold :
34 | (u.ugold > 10000) ? rnd(10000) : rnd((int) u.ugold) );
35 | #endif
36 | }
37 |
38 | void
39 | stealgold(mtmp)
40 | register struct monst *mtmp;
41 | {
42 | register struct obj *gold = g_at(u.ux, u.uy);
43 | register long tmp;
44 |
45 | if (gold && ( !u.ugold || gold->quan > u.ugold || !rn2(5))) {
46 | mtmp->mgold += gold->quan;
47 | delobj(gold);
48 | newsym(u.ux, u.uy);
49 | pline("%s quickly snatches some gold from between your %s!",
50 | Monnam(mtmp), makeplural(body_part(FOOT)));
51 | if(!u.ugold || !rn2(5)) {
52 | if (!tele_restrict(mtmp)) rloc(mtmp);
53 | mtmp->mflee = 1;
54 | }
55 | } else if(u.ugold) {
56 | u.ugold -= (tmp = somegold());
57 | Your("purse feels lighter.");
58 | mtmp->mgold += tmp;
59 | if (!tele_restrict(mtmp)) rloc(mtmp);
60 | mtmp->mflee = 1;
61 | flags.botl = 1;
62 | }
63 | }
64 |
65 | /* steal armor after you finish taking it off */
66 | unsigned int stealoid; /* object to be stolen */
67 | unsigned int stealmid; /* monster doing the stealing */
68 |
69 | STATIC_PTR int
70 | stealarm()
71 | {
72 | register struct monst *mtmp;
73 | register struct obj *otmp;
74 |
75 | for(otmp = invent; otmp; otmp = otmp->nobj) {
76 | if(otmp->o_id == stealoid) {
77 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
78 | if(mtmp->m_id == stealmid) {
79 | if(DEADMONSTER(mtmp)) impossible("stealarm(): dead monster stealing");
80 | if(!dmgtype(mtmp->data, AD_SITM)) /* polymorphed */
81 | goto botm;
82 | if(otmp->unpaid)
83 | subfrombill(otmp, shop_keeper(*u.ushops));
84 | freeinv(otmp);
85 | pline("%s steals %s!", Monnam(mtmp), doname(otmp));
86 | (void) mpickobj(mtmp,otmp); /* may free otmp */
87 | mtmp->mflee = 1;
88 | if (!tele_restrict(mtmp)) rloc(mtmp);
89 | break;
90 | }
91 | }
92 | break;
93 | }
94 | }
95 | botm: stealoid = 0;
96 | return 0;
97 | }
98 |
99 | /* An object you're wearing has been taken off my a monster (theft or
100 | seduction). Also used if a worn item gets transformed (stone to flesh). */
101 | void
102 | remove_worn_item(obj)
103 | struct obj *obj;
104 | {
105 | if (donning(obj))
106 | cancel_don();
107 | if (!obj->owornmask)
108 | return;
109 |
110 | switch (obj->oclass) {
111 | case TOOL_CLASS:
112 | if (obj == ublindf) Blindf_off(obj);
113 | break;
114 | case AMULET_CLASS:
115 | Amulet_off();
116 | break;
117 | case RING_CLASS:
118 | case FOOD_CLASS: /* meat ring */
119 | Ring_gone(obj);
120 | break;
121 | case ARMOR_CLASS:
122 | if (obj == uarm) (void) Armor_off();
123 | else if (obj == uarmc) (void) Cloak_off();
124 | else if (obj == uarmf) (void) Boots_off();
125 | else if (obj == uarmg) (void) Gloves_off();
126 | else if (obj == uarmh) (void) Helmet_off();
127 | else if (obj == uarms) (void) Shield_off();
128 | else setworn((struct obj *)0, obj->owornmask & W_ARMOR);
129 | break;
130 | default:
131 | /* shouldn't reach here, but just in case... */
132 | setnotworn(obj);
133 | break;
134 | }
135 | }
136 |
137 | /* Returns 1 when something was stolen (or at least, when N should flee now)
138 | * Returns -1 if the monster died in the attempt
139 | * Avoid stealing the object stealoid
140 | */
141 | int
142 | steal(mtmp)
143 | struct monst *mtmp;
144 | {
145 | struct obj *otmp;
146 | int tmp, could_petrify, named = 0;
147 |
148 | /* the following is true if successful on first of two attacks. */
149 | if(!monnear(mtmp, u.ux, u.uy)) return(0);
150 |
151 | if (!invent || (inv_cnt() == 1 && uskin)) {
152 | nothing_to_steal:
153 | /* Not even a thousand men in armor can strip a naked man. */
154 | if(Blind)
155 | pline("Somebody tries to rob you, but finds nothing to steal.");
156 | else
157 | pline("%s tries to rob you, but there is nothing to steal!",
158 | Monnam(mtmp));
159 | return(1); /* let her flee */
160 | }
161 |
162 | if (Adornment & LEFT_RING) {
163 | otmp = uleft;
164 | goto gotobj;
165 | } else if (Adornment & RIGHT_RING) {
166 | otmp = uright;
167 | goto gotobj;
168 | }
169 |
170 | tmp = 0;
171 | for(otmp = invent; otmp; otmp = otmp->nobj)
172 | if ((!uarm || otmp != uarmc) && otmp != uskin
173 | #ifdef INVISIBLE_OBJECTS
174 | && (!otmp->oinvis || perceives(mtmp->data))
175 | #endif
176 | )
177 | tmp += ((otmp->owornmask &
178 | (W_ARMOR | W_RING | W_AMUL | W_TOOL)) ? 5 : 1);
179 | if (!tmp) goto nothing_to_steal;
180 | tmp = rn2(tmp);
181 | for(otmp = invent; otmp; otmp = otmp->nobj)
182 | if ((!uarm || otmp != uarmc) && otmp != uskin
183 | #ifdef INVISIBLE_OBJECTS
184 | && (!otmp->oinvis || perceives(mtmp->data))
185 | #endif
186 | )
187 | if((tmp -= ((otmp->owornmask &
188 | (W_ARMOR | W_RING | W_AMUL | W_TOOL)) ? 5 : 1)) < 0)
189 | break;
190 | if(!otmp) {
191 | impossible("Steal fails!");
192 | return(0);
193 | }
194 | /* can't steal gloves while wielding - so steal the wielded item. */
195 | if (otmp == uarmg && uwep)
196 | otmp = uwep;
197 | /* can't steal armor while wearing cloak - so steal the cloak. */
198 | else if(otmp == uarm && uarmc) otmp = uarmc;
199 | #ifdef TOURIST
200 | else if(otmp == uarmu && uarmc) otmp = uarmc;
201 | else if(otmp == uarmu && uarm) otmp = uarm;
202 | #endif
203 | gotobj:
204 | if(otmp->o_id == stealoid) return(0);
205 |
206 | if(otmp->otyp == LEASH && otmp->leashmon) o_unleash(otmp);
207 |
208 | if((otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))){
209 | switch(otmp->oclass) {
210 | case TOOL_CLASS:
211 | case AMULET_CLASS:
212 | case RING_CLASS:
213 | case FOOD_CLASS: /* meat ring */
214 | remove_worn_item(otmp);
215 | break;
216 | case ARMOR_CLASS:
217 | /* Stop putting on armor which has been stolen. */
218 | if (donning(otmp) || is_animal(mtmp->data)) {
219 | remove_worn_item(otmp);
220 | break;
221 | } else {
222 | int curssv = otmp->cursed;
223 |
224 | otmp->cursed = 0;
225 | stop_occupation();
226 | if(flags.female)
227 | pline("%s charms you. You gladly %s your %s.",
228 | Blind ? "She" : Monnam(mtmp),
229 | curssv ? "let her take" :
230 | (objects[otmp->otyp].oc_delay > 1) ? "start removing" : "hand over",
231 | equipname(otmp));
232 | else
233 | pline("%s seduces you and %s off your %s.",
234 | Blind ? "It" : Adjmonnam(mtmp, "beautiful"),
235 | curssv ? "helps you to take" :
236 | (objects[otmp->otyp].oc_delay > 1) ? "you start taking" : "you take",
237 | equipname(otmp));
238 | named++;
239 | /* the following is to set multi for later on */
240 | nomul(-objects[otmp->otyp].oc_delay);
241 | remove_worn_item(otmp);
242 | otmp->cursed = curssv;
243 | if(multi < 0){
244 | /*
245 | multi = 0;
246 | nomovemsg = 0;
247 | afternmv = 0;
248 | */
249 | stealoid = otmp->o_id;
250 | stealmid = mtmp->m_id;
251 | afternmv = stealarm;
252 | return(0);
253 | }
254 | }
255 | break;
256 | default:
257 | impossible("Tried to steal a strange worn thing.");
258 | }
259 | }
260 | else if (otmp == uwep) uwepgone();
261 | else if (otmp == uquiver) uqwepgone();
262 | else if (otmp == uswapwep) uswapwepgone();
263 |
264 | if(otmp == uball) unpunish();
265 |
266 | freeinv(otmp);
267 | pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp));
268 | could_petrify = otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]);
269 | (void) mpickobj(mtmp,otmp); /* may free otmp */
270 | if (could_petrify && !(mtmp->misc_worn_check & W_ARMG)) {
271 | minstapetrify(mtmp, TRUE);
272 | return -1;
273 | }
274 | return((multi < 0) ? 0 : 1);
275 | }
276 |
277 | #endif /* OVLB */
278 | #ifdef OVL1
279 |
280 | /* Returns 1 if otmp is free'd, 0 otherwise. */
281 | int
282 | mpickobj(mtmp,otmp)
283 | register struct monst *mtmp;
284 | register struct obj *otmp;
285 | {
286 | int freed_otmp;
287 |
288 | if (otmp->oclass == GOLD_CLASS) {
289 | mtmp->mgold += otmp->quan;
290 | obfree(otmp, (struct obj *)0);
291 | freed_otmp = 1;
292 | } else {
293 | boolean snuff_otmp = FALSE;
294 | /* don't want hidden light source inside the monster; assumes that
295 | engulfers won't have external inventories; whirly monsters cause
296 | the light to be extinguished rather than letting it shine thru */
297 | if (otmp->lamplit && /* hack to avoid function calls for most objs */
298 | obj_sheds_light(otmp) &&
299 | attacktype(mtmp->data, AT_ENGL)) {
300 | /* this is probably a burning object that you dropped or threw */
301 | if (u.uswallow && mtmp == u.ustuck && !Blind)
302 | pline("%s go%s out.", The(xname(otmp)),
303 | otmp->quan == 1L ? "es" : "");
304 | snuff_otmp = TRUE;
305 | }
306 | /* Must do carrying effects on object prior to add_to_minv() */
307 | carry_obj_effects(otmp);
308 | /* add_to_minv() might free otmp [if merged with something else],
309 | so we have to call it after doing the object checks */
310 | freed_otmp = add_to_minv(mtmp, otmp);
311 | /* and we had to defer this until object is in mtmp's inventory */
312 | if (snuff_otmp) snuff_light_source(mtmp->mx, mtmp->my);
313 | }
314 | return freed_otmp;
315 | }
316 |
317 | #endif /* OVL1 */
318 | #ifdef OVLB
319 |
320 | void
321 | stealamulet(mtmp)
322 | struct monst *mtmp;
323 | {
324 | struct obj *otmp = (struct obj *)0;
325 | int real=0, fake=0;
326 |
327 | /* select the artifact to steal */
328 | if(u.uhave.amulet) {
329 | real = AMULET_OF_YENDOR;
330 | fake = FAKE_AMULET_OF_YENDOR;
331 | } else if(u.uhave.questart) {
332 | for(otmp = invent; otmp; otmp = otmp->nobj)
333 | if(is_quest_artifact(otmp)) break;
334 | if (!otmp) return; /* should we panic instead? */
335 | } else if(u.uhave.bell) {
336 | real = BELL_OF_OPENING;
337 | fake = BELL;
338 | } else if(u.uhave.book) {
339 | real = SPE_BOOK_OF_THE_DEAD;
340 | } else if(u.uhave.menorah) {
341 | real = CANDELABRUM_OF_INVOCATION;
342 | } else return; /* you have nothing of special interest */
343 |
344 | if (!otmp) {
345 | /* If we get here, real and fake have been set up. */
346 | for(otmp = invent; otmp; otmp = otmp->nobj)
347 | if(otmp->otyp == real || (otmp->otyp == fake && !mtmp->iswiz))
348 | break;
349 | }
350 |
351 | if (otmp) { /* we have something to snatch */
352 | if (otmp->owornmask)
353 | remove_worn_item(otmp);
354 | freeinv(otmp);
355 | /* mpickobj wont merge otmp because none of the above things
356 | to steal are mergable */
357 | (void) mpickobj(mtmp,otmp); /* may merge and free otmp */
358 | pline("%s stole %s!", Monnam(mtmp), doname(otmp));
359 | if (can_teleport(mtmp->data) && !tele_restrict(mtmp))
360 | rloc(mtmp);
361 | }
362 | }
363 |
364 | #endif /* OVLB */
365 | #ifdef OVL0
366 |
367 | /* release the objects the creature is carrying */
368 | void
369 | relobj(mtmp,show,is_pet)
370 | register struct monst *mtmp;
371 | register int show;
372 | boolean is_pet; /* If true, pet should keep wielded/worn items */
373 | {
374 | register struct obj *otmp;
375 | register int omx = mtmp->mx, omy = mtmp->my;
376 | struct obj *keepobj = 0;
377 | struct obj *wep = MON_WEP(mtmp);
378 | boolean item1 = FALSE, item2 = FALSE;
379 |
380 | if (!is_pet || mindless(mtmp->data) || is_animal(mtmp->data))
381 | item1 = item2 = TRUE;
382 | if (!tunnels(mtmp->data) || !needspick(mtmp->data))
383 | item1 = TRUE;
384 | while ((otmp = mtmp->minvent) != 0) {
385 | obj_extract_self(otmp);
386 | /* special case: pick-axe and unicorn horn are non-worn */
387 | /* items that we also want pets to keep 1 of */
388 | /* (It is a coincidence that these can also be wielded. */
389 | if (otmp->owornmask || otmp == wep ||
390 | ((!item1 && otmp->otyp == PICK_AXE) ||
391 | (!item2 && otmp->otyp == UNICORN_HORN && !otmp->cursed))) {
392 | if (is_pet) { /* dont drop worn/wielded item */
393 | if (otmp->otyp == PICK_AXE)
394 | item1 = TRUE;
395 | if (otmp->otyp == UNICORN_HORN && !otmp->cursed)
396 | item2 = TRUE;
397 | otmp->nobj = keepobj;
398 | keepobj = otmp;
399 | continue;
400 | }
401 | mtmp->misc_worn_check &= ~(otmp->owornmask);
402 | otmp->owornmask = 0L;
403 | }
404 | if (is_pet && cansee(omx, omy) && flags.verbose)
405 | pline("%s drops %s.", Monnam(mtmp),
406 | distant_name(otmp, doname));
407 | if (flooreffects(otmp, omx, omy, "fall")) continue;
408 | place_object(otmp, omx, omy);
409 | stackobj(otmp);
410 | }
411 | /* put kept objects back */
412 | while ((otmp = keepobj) != (struct obj *)0) {
413 | keepobj = otmp->nobj;
414 | (void) add_to_minv(mtmp, otmp);
415 | }
416 |
417 | if (mtmp->mgold) {
418 | register long g = mtmp->mgold;
419 | (void) mkgold(g, omx, omy);
420 | if (is_pet && cansee(omx, omy) && flags.verbose)
421 | pline("%s drops %ld gold piece%s.", Monnam(mtmp),
422 | g, plur(g));
423 | mtmp->mgold = 0L;
424 | }
425 | if (show & cansee(omx, omy))
426 | newsym(omx, omy);
427 | }
428 |
429 | #endif /* OVL0 */
430 |
431 | /*steal.c*/