Blindfold removal fix
[slashemextended.git] / src / dogmove.c
blob8abf0fd175f4b0e5fab3200ddfcc8052b7ba79d8
1 /* SCCS Id: @(#)dogmove.c 3.4 2002/09/10 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
7 #include "mfndpos.h"
8 #include "edog.h"
9 #include "emin.h"
10 #include "epri.h"
12 /* #define DEBUG */ /* uncomment to enable debugging */
14 #ifdef DEBUG
15 # ifdef WIZARD
16 #define debugpline if (wizard) pline
17 # else
18 #define debugpline pline
19 # endif
20 #endif
22 extern boolean notonhead;
24 #ifdef OVL0
26 STATIC_DCL boolean dog_hunger(struct monst *,struct edog *);
27 STATIC_DCL int dog_invent(struct monst *,struct edog *,int);
28 STATIC_DCL int dog_goal(struct monst *,struct edog *,int,int,int);
30 STATIC_DCL struct obj *DROPPABLES(struct monst *);
31 STATIC_DCL boolean can_reach_location(struct monst *,XCHAR_P,XCHAR_P,
32 XCHAR_P,XCHAR_P);
33 STATIC_DCL boolean could_reach_item(struct monst *, XCHAR_P,XCHAR_P);
35 STATIC_OVL struct obj *
36 DROPPABLES(mon)
37 register struct monst *mon;
39 register struct obj *obj;
40 struct obj *wep = MON_WEP(mon);
41 boolean item1 = FALSE, item2 = FALSE;
43 if (is_animal(mon->data) || mindless(mon->data))
44 item1 = item2 = TRUE;
45 if (!tunnels(mon->data) || !needspick(mon->data))
46 item1 = TRUE;
47 for(obj = mon->minvent; obj; obj = obj->nobj) {
48 if (!item1 && is_pick(obj) && ((obj->otyp != DWARVISH_MATTOCK && obj->otyp != SOFT_MATTOCK && obj->otyp != ETERNIUM_MATTOCK)
49 || !which_armor(mon, W_ARMS))) {
50 item1 = TRUE;
51 continue;
53 if (!item2 && obj->otyp == UNICORN_HORN && !obj->cursed) {
54 item2 = TRUE;
55 continue;
57 if (obj->mstartinvent) continue;
58 if (obj->mstartinventB) continue;
59 if (obj->mstartinventC) continue;
60 if (obj->mstartinventD) continue;
61 if (obj->mstartinventE) continue;
62 if (obj->mstartinventX) continue;
63 if (obj->petmarked) continue;
65 if (!obj->owornmask && obj != wep) return obj;
67 return (struct obj *)0;
70 static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 };
72 #endif /* OVL0 */
74 STATIC_OVL boolean cursed_object_at(struct monst *, int, int);
76 STATIC_VAR xchar gtyp, gx, gy; /* type and position of dog's current goal */
78 STATIC_PTR void wantdoor(int, int, void *);
80 #ifdef OVLB
81 STATIC_OVL boolean
82 cursed_object_at(mtmp, x, y)
83 register struct monst *mtmp;
84 int x, y;
86 struct obj *otmp;
88 for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
89 /* [Tom] demons & undead don't care, though */
90 /* [ALI] demons & undead avoid blessed items instead */
91 if ((is_demon(mtmp->data) || is_undead(mtmp->data) || mtmp->egotype_undead) ?
92 otmp->blessed : otmp->cursed)
94 #ifdef DEBUG
95 debugpline("%s thinks %s at (%d,%d) is `cursed'",
96 noit_Monnam(mtmp), doname(otmp), x, y);
97 #endif
98 return TRUE;
100 return FALSE;
104 dog_nutrition(mtmp, obj)
105 struct monst *mtmp;
106 struct obj *obj;
108 int nutrit;
111 * It is arbitrary that the pet takes the same length of time to eat
112 * as a human, but gets more nutritional value.
114 if (obj->oclass == FOOD_CLASS) {
115 if(obj->otyp == CORPSE) {
116 mtmp->meating = (issoviet ? 5 : 2) + (mons[obj->corpsenm].cwt >> (issoviet ? 6 : 8) );
117 nutrit = mons[obj->corpsenm].cnutrit;
118 } else {
119 mtmp->meating = objects[obj->otyp].oc_delay;
120 nutrit = objects[obj->otyp].oc_nutrition;
123 /* it was insane how much nutrition they got from corpses, since I increased the average amount corpses give,
124 * so I hereby decide to reduce it. Use tripe rations or slime molds to keep your pets fed :P --Amy */
125 if (obj->otyp != CORPSE) {
126 switch(mtmp->data->msize) {
127 case MZ_TINY: nutrit *= 8; break;
128 case MZ_SMALL: nutrit *= 6; break;
129 default:
130 case MZ_MEDIUM: nutrit *= 5; break;
131 case MZ_LARGE: nutrit *= 4; break;
132 case MZ_HUGE: nutrit *= 3; break;
133 case MZ_GIGANTIC: nutrit *= 2; break;
136 if(obj->oeaten) {
137 mtmp->meating = eaten_stat(mtmp->meating, obj);
138 nutrit = eaten_stat(nutrit, obj);
140 } else if (obj->oclass == COIN_CLASS) {
141 mtmp->meating = (int)(obj->quan/2000) + 1;
142 if (mtmp->meating < 0) mtmp->meating = 1;
143 nutrit = (int)(obj->quan/20);
144 if (nutrit < 0) nutrit = 0;
145 } else {
146 /* Unusual pet such as gelatinous cube eating odd stuff.
147 * meating made consistent with wild monsters in mon.c.
148 * nutrit made consistent with polymorphed player nutrit in
149 * eat.c. (This also applies to pets eating gold.)
151 mtmp->meating = obj->owt/20 + 1;
152 /* problem discovered by potato44: very heavy items like boulders could "paralyze" the pet for 100s of turns */
153 if (mtmp->meating > 50) mtmp->meating = 50;
154 nutrit = 5*(objects[obj->otyp].oc_nutrition + 1);
155 if (nutrit > 25000) nutrit = 25000; /* don't make boulders and stuff give 100k+ nutrition --Amy */
156 /* old factor restored by Amy; the +1 is so that zero-weight objects don't give zero nutrition */
158 use_skill(P_PETKEEPING,1);
160 if (!(PlayerCannotUseSkills)) {
161 switch (P_SKILL(P_PETKEEPING)) {
162 default: break;
163 case P_BASIC: nutrit = (nutrit * 11 / 10); break;
164 case P_SKILLED: nutrit = (nutrit * 12 / 10); break;
165 case P_EXPERT: nutrit = (nutrit * 13 / 10); break;
166 case P_MASTER: nutrit = (nutrit * 14 / 10); break;
167 case P_GRAND_MASTER: nutrit = (nutrit * 15 / 10); break;
168 case P_SUPREME_MASTER: nutrit = (nutrit * 16 / 10); break;
172 if (obj && obj->oartifact == ART_FEED_THE_HORSE) nutrit += 100000;
174 if (isfriday) nutrit /= 2;
175 if (PetstarveEffect || u.uprops[PETSTARVE_EFFECT].extrinsic || have_petstarvestone()) nutrit /= 3;
177 return nutrit;
180 /* pets should have a chance to gain max health when eating, depending on how much nutrition the comestible gives --Amy
181 * this is not affected by things like monster size, petkeeping skill etc. */
183 dog_growpoints(mtmp, obj)
184 struct monst *mtmp;
185 struct obj *obj;
187 int nutrit;
189 if (obj->oclass == FOOD_CLASS) {
190 if(obj->otyp == CORPSE) {
191 nutrit = mons[obj->corpsenm].cnutrit;
192 } else {
193 nutrit = objects[obj->otyp].oc_nutrition;
195 } else if (obj->oclass == COIN_CLASS) {
196 nutrit = (int)(obj->quan/20);
197 if (nutrit < 0) nutrit = 0;
198 } else {
199 nutrit = 5*(objects[obj->otyp].oc_nutrition + 1);
202 if (obj && obj->oartifact == ART_FEED_THE_HORSE) nutrit += 100000;
204 if (isfriday) nutrit /= 2;
206 return nutrit;
209 /* returns 2 if pet dies, otherwise 1 */
211 dog_eat(mtmp, obj, x, y, devour)
212 register struct monst *mtmp;
213 register struct obj * obj;
214 int x, y;
215 boolean devour;
217 register struct edog *edog = EDOG(mtmp);
218 boolean poly = FALSE, grow = FALSE, heal = FALSE;
219 int nutrit;
220 int growpoints;
221 boolean vis = (cansee(x, y) || cansee(mtmp->mx, mtmp->my));
222 boolean vampiric = is_vampire(mtmp->data);
223 struct permonst *fptr = &mons[obj->corpsenm];
224 struct monst *potentialpet;
226 if(edog->hungrytime < monstermoves)
227 edog->hungrytime = monstermoves;
228 nutrit = dog_nutrition(mtmp, obj);
229 growpoints = dog_growpoints(mtmp, obj);
230 poly = polyfodder(obj);
231 grow = mlevelgain(obj);
232 heal = mhealup(obj);
233 if (devour) {
234 if (mtmp->meating > 1) mtmp->meating /= 2;
235 if (nutrit > 1) nutrit = (nutrit * 3) / 4;
238 if (growpoints >= 5) {
239 int linechance = 0;
240 int hownum = growpoints;
241 while (hownum > 0) {
242 if (hownum >= 500) {
243 hownum -= 500;
244 linechance++;
245 } else {
246 if (hownum > rn2(500)) linechance++;
247 hownum = 0;
250 while (linechance > 0) {
252 int pethealthgainchance = 1;
253 if (mtmp->mhpmax >= 25) pethealthgainchance = 2;
254 if (mtmp->mhpmax >= 40) pethealthgainchance = 3;
255 if (mtmp->mhpmax >= 50) pethealthgainchance = 4;
256 if (mtmp->mhpmax >= 60) pethealthgainchance = (mtmp->mhpmax / 10) ;
257 if (mtmp->mhpmax >= 100) pethealthgainchance = (mtmp->mhpmax / 5) ;
259 if (!rn2(pethealthgainchance) && (mtmp->mhpmax < 400) ) {
260 mtmp->mhpmax++;
263 linechance--;
267 /* vampires only get 1/5 normal nutrition */
268 if (vampiric) {
269 mtmp->meating = (mtmp->meating + 4) / 5;
270 nutrit = (nutrit + 4) / 5;
273 edog->hungrytime += nutrit;
274 mtmp->mconf = 0;
275 if (edog->mhpmax_penalty) {
276 /* no longer starving */
277 mtmp->mhpmax += edog->mhpmax_penalty;
278 edog->mhpmax_penalty = 0;
280 if (edog->abouttostarve) edog->abouttostarve = 0;
281 if (mtmp->mflee && mtmp->mfleetim > 1) mtmp->mfleetim /= 2;
282 if (mtmp->mtame < 20 && !FemtrapActiveAntje) mtmp->mtame++;
283 if (x != mtmp->mx || y != mtmp->my) { /* moved & ate on same turn */
284 newsym(x, y);
285 newsym(mtmp->mx, mtmp->my);
287 if ((is_waterypool(x, y) || is_watertunnel(x,y)) && !Underwater) {
288 /* Don't print obj */
289 /* TODO: Reveal presence of sea monster (especially sharks) */
290 } else
291 /* hack: observe the action if either new or old location is in view */
292 /* However, invisible monsters should still be "it" even though out of
293 sight locations should not. */
294 if (vis) {
295 pline("%s %s %s.", mon_visible(mtmp) ? noit_Monnam(mtmp) : "It",
296 vampiric ? "drains" : devour ? "devours" : "eats",
297 (obj->oclass == FOOD_CLASS) ?
298 singular(obj, doname) : doname(obj));
299 if (issoviet && (obj->otyp == CORPSE) && (fptr->cnutrit < 1) ) pline("On on on-kha-kha-kha ya smeyus' nad vami truslivogo smertnyy! - Tip bloka l'da.");
301 if (issoviet && (obj->otyp == CORPSE) && ((((potentialpet = get_mtraits(obj, FALSE)) != (struct monst *)0) ) && potentialpet->mtame) ) pline("on on on vash pitomets teper' ushel navsegda, potomu chto ya vsemogushchiy tip bloka l'da ya velichayshiy!");
304 /* It's a reward if it's DOGFOOD and the player dropped/threw it. */
305 /* We know the player had it if invlet is set -dlc */
306 if(dogfood(mtmp,obj) == DOGFOOD && obj->invlet)
307 #ifdef LINT
308 edog->apport = 0;
309 #else
310 edog->apport += (int)(200L/
311 ((long)edog->dropdist + monstermoves - edog->droptime));
312 #endif
313 if (mtmp->data == &mons[PM_RUST_MONSTER] && obj->oerodeproof) {
314 /* The object's rustproofing is gone now */
315 obj->oerodeproof = 0;
316 mtmp->mstun = 1;
317 if (canseemon(mtmp) && flags.verbose) {
318 pline("%s spits %s out in disgust!",
319 Monnam(mtmp), distant_name(obj,doname));
321 } else if (vampiric) {
322 /* Split Object */
323 if (obj->quan > 1L) {
324 if(!carried(obj)) {
325 (void) splitobj(obj, 1L);
326 } else {
327 /* Carried */
328 obj = splitobj(obj, obj->quan - 1L);
330 freeinv(obj);
331 if (inv_cnt() >= 52 && !merge_choice(invent, obj))
332 dropy(obj);
333 else
334 obj = addinv(obj); /* unlikely but a merge is possible */
336 #ifdef DEBUG
337 debugpline("split object,");
338 #endif
341 /* Take away blood nutrition */
342 obj->oeaten = drainlevel(obj);
343 obj->odrained = 1;
344 } else if (obj == uball) {
345 unpunish();
347 if (obj->otyp == MEDICAL_KIT)
348 delete_contents(obj);
349 if (Has_contents(obj)) {
350 register struct obj *otmp3;
351 while ((otmp3 = obj->cobj) != 0) {
352 obj_extract_self(otmp3);
353 if ( (obj->otyp == ICE_BOX || obj->otyp == DISPERSION_BOX || obj->otyp == ICE_BOX_OF_HOLDING || obj->otyp == ICE_BOX_OF_WATERPROOFING || obj->otyp == ICE_BOX_OF_DIGESTION) && otmp3->otyp == CORPSE) {
354 otmp3->icedobject = TRUE;
355 otmp3->age = monstermoves - otmp3->age;
356 start_corpse_timeout(otmp3);
358 (void) mpickobj(mtmp, otmp3, FALSE);
362 delobj(obj);
363 } else if (obj == uchain) {
365 if (obj->otyp == MEDICAL_KIT)
366 delete_contents(obj);
367 if (Has_contents(obj)) {
368 register struct obj *otmp3;
369 while ((otmp3 = obj->cobj) != 0) {
370 obj_extract_self(otmp3);
371 if ( (obj->otyp == ICE_BOX || obj->otyp == DISPERSION_BOX || obj->otyp == ICE_BOX_OF_HOLDING || obj->otyp == ICE_BOX_OF_WATERPROOFING || obj->otyp == ICE_BOX_OF_DIGESTION) && otmp3->otyp == CORPSE) {
372 otmp3->icedobject = TRUE;
373 otmp3->age = monstermoves - otmp3->age;
374 start_corpse_timeout(otmp3);
376 (void) mpickobj(mtmp, otmp3, FALSE);
380 unpunish();
381 } else if (obj->quan > 1L && obj->oclass == FOOD_CLASS) {
382 obj->quan--;
383 obj->owt = weight(obj);
384 } else {
386 if (obj->otyp == MEDICAL_KIT)
387 delete_contents(obj);
388 if (Has_contents(obj)) {
389 register struct obj *otmp3;
390 while ((otmp3 = obj->cobj) != 0) {
391 obj_extract_self(otmp3);
392 if ( (obj->otyp == ICE_BOX || obj->otyp == DISPERSION_BOX || obj->otyp == ICE_BOX_OF_HOLDING || obj->otyp == ICE_BOX_OF_WATERPROOFING || obj->otyp == ICE_BOX_OF_DIGESTION) && otmp3->otyp == CORPSE) {
393 otmp3->icedobject = TRUE;
394 otmp3->age = monstermoves - otmp3->age;
395 start_corpse_timeout(otmp3);
397 (void) mpickobj(mtmp, otmp3, FALSE);
400 delobj(obj);
402 if (poly) {
403 (void) mon_spec_poly(mtmp, (struct permonst *)0, 0L, FALSE,
404 cansee(mtmp->mx, mtmp->my), FALSE, FALSE);
405 #if 0
406 (void) newcham(mtmp, (struct permonst *)0, FALSE,
407 cansee(mtmp->mx, mtmp->my));
408 #endif
410 /* limit "instant" growth to prevent potential abuse */
411 if (grow && (int) mtmp->m_lev < (int)mtmp->data->mlevel + 15) {
412 if (!grow_up(mtmp, (struct monst *)0)) return 2;
414 if (heal) mtmp->mhp = mtmp->mhpmax;
415 return 1;
418 #endif /* OVLB */
419 #ifdef OVL0
421 /* hunger effects -- returns TRUE on starvation */
422 STATIC_OVL boolean
423 dog_hunger(mtmp, edog)
424 register struct monst *mtmp;
425 register struct edog *edog;
427 if (monstermoves <= (edog->hungrytime + 500)) edog->abouttostarve = FALSE;
429 if (monstermoves > edog->hungrytime + 500) {
430 if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data) && !metallivorous(mtmp->data) && !mtmp->egotype_lithivore && !mtmp->egotype_metallivore && !mtmp->egotype_allivore && !lithivorous(mtmp->data)) {
431 edog->hungrytime = monstermoves + 500;
432 /* but not too high; it might polymorph */
433 } else if (!edog->mhpmax_penalty) {
434 /* starving pets are limited in healing */
435 int newmhpmax = mtmp->mhpmax / 3;
436 mtmp->mconf = 1;
437 edog->mhpmax_penalty = mtmp->mhpmax - newmhpmax;
438 mtmp->mhpmax = newmhpmax;
439 if (mtmp->mhp > mtmp->mhpmax)
440 mtmp->mhp = mtmp->mhpmax;
441 if (mtmp->mhp < 1) goto dog_died;
442 if (cansee(mtmp->mx, mtmp->my))
443 pline("%s is confused from hunger.", Monnam(mtmp));
444 else if (couldsee(mtmp->mx, mtmp->my))
445 beg(mtmp);
446 else
447 You_feel("worried about %s.", y_monnam(mtmp));
448 stop_occupation();
449 } else if (!edog->abouttostarve && (monstermoves > edog->hungrytime + 750)) {
450 edog->abouttostarve = 5;
451 } else if (edog->abouttostarve > 1) {
452 edog->abouttostarve--;
453 if (edog->abouttostarve == 4) { /* give several warnings that the pet is going to starve --Amy */
454 if (couldsee(mtmp->mx, mtmp->my)) {
455 beg(mtmp);
456 You_feel("that %s is in dire need of food.", y_monnam(mtmp));
457 } else
458 You_feel("that %s is about to starve.", y_monnam(mtmp));
460 if (edog->abouttostarve == 2) {
461 if (couldsee(mtmp->mx, mtmp->my)) {
462 beg(mtmp);
463 You_feel("that %s is in dire need of food.", y_monnam(mtmp));
464 } else
465 You_feel("that %s is about to starve.", y_monnam(mtmp));
467 if (edog->abouttostarve == 1) {
468 if (couldsee(mtmp->mx, mtmp->my)) {
469 beg(mtmp);
470 You_feel("that %s needs food immediately!", y_monnam(mtmp));
471 } else
472 You_feel("that %s is moments away from dying of starvation!", y_monnam(mtmp));
474 } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) {
475 dog_died:
476 if (mtmp->mleashed && mtmp != u.usteed)
477 Your("leash goes slack.");
478 else if (cansee(mtmp->mx, mtmp->my))
479 pline("%s starves.", Monnam(mtmp));
480 else {
481 You_feel("%s for a moment.",
482 FunnyHallu ? "bummed" : "sad");
483 if (PlayerHearsSoundEffects) pline(issoviet ? "Tipichnyy igrok. Vy dazhe ne sposobny kormit' vashego pitomtsa." : "Tschwieaeaeh!");
486 edog->abouttostarve = FALSE;
487 mondied(mtmp);
488 return(TRUE);
491 return(FALSE);
494 /* do something with object (drop, pick up, eat) at current position
495 * returns 1 if object eaten (since that counts as dog's move), 2 if died
497 STATIC_OVL int
498 dog_invent(mtmp, edog, udist)
499 register struct monst *mtmp;
500 register struct edog *edog;
501 int udist;
503 /* KMH, balance patch -- quantity picked up should depend on dog's level */
504 int dogquan = /*10*/5 * mtmp->m_lev; /* halved by Amy */
505 register int omx, omy;
506 struct obj *obj;
508 struct obj *floor_obj;
509 int temp_quan;
511 if (mtmp->msleeping || !mtmp->mcanmove) return(0);
513 omx = mtmp->mx;
514 omy = mtmp->my;
516 /* if we are carrying sth then we drop it (perhaps near @) */
517 /* Note: if apport == 1 then our behaviour is independent of udist */
518 /* Use udist+1 so steed won't cause divide by zero */
519 #ifndef GOLDOBJ
520 if(DROPPABLES(mtmp) || mtmp->mgold) {
521 #else
522 if(DROPPABLES(mtmp)) {
523 #endif
524 if (!rn2(udist+1) || !rn2(edog->apport))
525 if(rn2(10) < edog->apport){
526 mon_wield_item(mtmp); /* to hopefully fix the GIGAbug where pets would drop their weapon upon save and restore --Amy */
527 relobj(mtmp, (int)mtmp->minvis, TRUE);
528 if(edog->apport > 1) edog->apport--;
529 edog->dropdist = udist; /* hpscdi!jon */
530 edog->droptime = moves;
532 } else {
533 if((obj=level.objects[omx][omy]) && !index(nofetch,obj->oclass)
534 #ifdef MAIL
535 && obj->otyp != SCR_MAIL
536 #endif
538 int edible = dogfood(mtmp, obj);
540 if (((edible <= CADAVER ||
541 /* starving pet is more aggressive about eating */
542 (edog->mhpmax_penalty && edible == ACCFOOD)) &&
543 could_reach_item(mtmp, obj->ox, obj->oy)) && u.petcaneat)
544 return dog_eat(mtmp, obj, omx, omy, FALSE);
546 /* [Tom] demonic & undead pets don't mind cursed items */
547 if(can_carry(mtmp, obj) && u.petcollectitems && (issoviet || !Has_contents(obj)) &&
548 !(obj == uchain) && !(obj == uball) &&
549 could_reach_item(mtmp, obj->ox, obj->oy) &&
550 (!obj->cursed || is_demon(mtmp->data) || is_undead(mtmp->data) || mtmp->egotype_undead) &&
551 (!obj->blessed || (!is_demon(mtmp->data) && !is_undead(mtmp->data) && (!mtmp->egotype_undead) ))) {
552 if(rn2(20) < edog->apport+3) {
553 if (rn2(udist) || !rn2(edog->apport)) {
554 /* KMH, balance patch -- 10*level
555 * oh-my-god by Amy: why the hell did you make this depend on the mon having hands... */
556 if (((!nohands(mtmp->data)) || (obj->quan <= dogquan)) && !(obj->oclass == COIN_CLASS && obj->quan > dogquan))
558 if (cansee(omx, omy) && flags.verbose)
559 pline("%s picks up %s.", Monnam(mtmp),
560 distant_name(obj, doname));
561 obj_extract_self(obj);
562 newsym(omx,omy);
563 (void) mpickobj(mtmp,obj,FALSE);
565 else /* picking up a few objects from a pile... */
566 /* KMH -- fix picking up zero quantity */
567 if (dogquan > 0 || (obj->oclass == COIN_CLASS)) {
568 if (obj->oclass == COIN_CLASS) {
569 /* KMH, balance patch -- 10*level */
570 if (dogquan < 1) dogquan = 1; /* fail safe by Amy */
571 if (obj->quan < dogquan) dogquan = obj->quan;
572 if (dogquan < 1) return 0; /* BUG */
573 #ifndef GOLDOBJ
574 obj->quan -= dogquan;
575 if (cansee(omx, omy) && flags.verbose)
576 pline("%s picks up %d gold pieces.", Monnam(mtmp), dogquan);
577 mtmp->mgold += dogquan;
578 #else
579 if (obj->quan != dogquan)
580 obj = splitobj(obj, dogquan);
581 if (cansee(omx, omy) && flags.verbose)
582 pline("%s picks up %s.",
583 Monnam(mtmp),
584 doname(obj));
585 obj_extract_self(obj);
586 newsym(omx,omy);
587 (void) mpickobj(mtmp,obj,FALSE);
588 #endif
589 } else {
591 struct obj *floor_obj;
592 int temp_quan;
594 obj->quan -= dogquan;
595 temp_quan = obj->quan;
596 floor_obj = level.objects[omx][omy];
597 mpickobj(mtmp,obj,FALSE);
598 obj->quan = dogquan;
599 if (cansee(omx, omy) && flags.verbose)
600 pline("%s picks up %s.", Monnam(mtmp),
601 distant_name(obj, doname));
602 floor_obj->quan = temp_quan;*/
605 if (attacktype(mtmp->data, AT_WEAP) &&
606 mtmp->weapon_check == NEED_WEAPON) {
607 mtmp->weapon_check = NEED_HTH_WEAPON;
608 (void) mon_wield_item(mtmp);
610 m_dowear(mtmp, FALSE);
616 return 0;
619 /* set dog's goal -- gtyp, gx, gy
620 * returns -1/0/1 (dog's desire to approach player) or -2 (abort move)
622 STATIC_OVL int
623 dog_goal(mtmp, edog, after, udist, whappr)
624 register struct monst *mtmp;
625 struct edog *edog;
626 int after, udist, whappr;
628 register int omx, omy;
629 boolean in_masters_sight, dog_has_minvent;
630 register struct obj *obj;
631 xchar otyp;
632 int appr;
634 /* Steeds don't move on their own will */
635 if (mtmp == u.usteed)
636 return (-2);
638 omx = mtmp->mx;
639 omy = mtmp->my;
641 in_masters_sight = couldsee(omx, omy);
642 dog_has_minvent = (DROPPABLES(mtmp) != 0);
644 if (!edog || mtmp->mleashed) { /* he's not going anywhere... */
645 gtyp = APPORT;
646 gx = u.ux;
647 gy = u.uy;
648 } else {
649 #define DDIST(x,y) (dist2(x,y,omx,omy))
650 #define SQSRCHRADIUS 5
651 int min_x, max_x, min_y, max_y;
652 register int nx, ny;
654 gtyp = UNDEF; /* no goal as yet */
655 gx = gy = 0; /* suppress 'used before set' message */
657 if ((min_x = omx - SQSRCHRADIUS) < 1) min_x = 1;
658 if ((max_x = omx + SQSRCHRADIUS) >= COLNO) max_x = COLNO - 1;
659 if ((min_y = omy - SQSRCHRADIUS) < 0) min_y = 0;
660 if ((max_y = omy + SQSRCHRADIUS) >= ROWNO) max_y = ROWNO - 1;
662 /* nearby food is the first choice, then other objects */
663 for (obj = fobj; obj; obj = obj->nobj) {
664 nx = obj->ox;
665 ny = obj->oy;
666 if (nx >= min_x && nx <= max_x && ny >= min_y && ny <= max_y) {
667 otyp = dogfood(mtmp, obj);
668 /* skip inferior goals */
669 if (otyp > gtyp || otyp == UNDEF)
670 continue;
671 /* avoid cursed items unless starving */
672 if (cursed_object_at(mtmp, nx, ny) &&
673 !(edog->mhpmax_penalty && otyp < MANFOOD))
674 continue;
675 /* skip completely unreacheable goals */
676 if (!could_reach_item(mtmp, nx, ny) ||
677 !can_reach_location(mtmp, mtmp->mx, mtmp->my, nx, ny))
678 continue;
679 if (otyp < MANFOOD) {
680 if (otyp < gtyp || DDIST(nx,ny) < DDIST(gx,gy)) {
681 gx = nx;
682 gy = ny;
683 gtyp = otyp;
685 } else if(gtyp == UNDEF && in_masters_sight &&
686 !dog_has_minvent &&
687 (!levl[omx][omy].lit || levl[u.ux][u.uy].lit) &&
688 (otyp == MANFOOD || m_cansee(mtmp, nx, ny)) &&
689 edog->apport > rn2(8) && u.petcollectitems &&
690 can_carry(mtmp,obj)) {
691 gx = nx;
692 gy = ny;
693 gtyp = APPORT;
699 /* follow player if appropriate */
700 if (gtyp == UNDEF ||
701 (gtyp != DOGFOOD && gtyp != APPORT && monstermoves < edog->hungrytime)) {
702 gx = u.ux;
703 gy = u.uy;
704 if (after && udist <= 4 && gx == u.ux && gy == u.uy)
705 return(-2);
706 appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
707 if (udist > 1) {
708 if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) ||
709 whappr ||
710 (dog_has_minvent && rn2(edog->apport)))
711 appr = 1;
713 /* if you have dog food it'll follow you more closely */
714 if (appr == 0) {
715 obj = invent;
716 while (obj) {
717 if(dogfood(mtmp, obj) == DOGFOOD) {
718 appr = 1;
719 break;
721 obj = obj->nobj;
724 } else
725 appr = 1; /* gtyp != UNDEF */
726 if(mtmp->mconf)
727 appr = 0;
729 #define FARAWAY (COLNO + 2) /* position outside screen */
730 if (gx == u.ux && gy == u.uy && !in_masters_sight) {
731 register coord *cp;
733 cp = gettrack(omx,omy);
734 if (cp) {
735 gx = cp->x;
736 gy = cp->y;
737 if(edog) edog->ogoal.x = 0;
738 } else {
739 /* assume master hasn't moved far, and reuse previous goal */
740 if(edog && edog->ogoal.x &&
741 ((edog->ogoal.x != omx) || (edog->ogoal.y != omy))) {
742 gx = edog->ogoal.x;
743 gy = edog->ogoal.y;
744 edog->ogoal.x = 0;
745 } else {
746 int fardist = FARAWAY * FARAWAY;
747 gx = gy = FARAWAY; /* random */
748 do_clear_area(omx, omy, 9, wantdoor,
749 (void *)&fardist);
751 /* here gx == FARAWAY e.g. when dog is in a vault */
752 if (gx == FARAWAY || (gx == omx && gy == omy)) {
753 gx = u.ux;
754 gy = u.uy;
755 } else if(edog) {
756 edog->ogoal.x = gx;
757 edog->ogoal.y = gy;
761 } else if(edog) {
762 edog->ogoal.x = 0;
764 if (!u.petcanfollow && appr == 1) appr = 0;
765 if (mtmp && mtmp->data == &mons[PM_ASSBALL_FOOTBALL] && appr == 1) appr = 0;
766 if ((PetAIScrewed || u.uprops[PET_AI_SCREWED].extrinsic || have_petaistone()) && appr == 1) appr = 0;
767 return appr;
770 #ifdef DEBUG
771 #define CHECK_ALLOW(flag,str) if ((allowflags & (flag)) == (flag)) { \
772 allowflags ^= (flag); \
773 if (bp != buf) { *bp++=','; *bp++=' '; } \
774 strcpy(bp, str); \
775 bp += strlen(bp); \
776 } else
778 STATIC_OVL char *
779 allow_set(allowflags)
780 long allowflags;
782 static char buf[500];
783 char *bp = buf;
784 if (allowflags == 0)
785 return "(none)";
786 *bp = '\0';
787 CHECK_ALLOW(ALLOW_TRAPS, "can enter traps");
788 CHECK_ALLOW(ALLOW_U, "can attack you");
789 CHECK_ALLOW(ALLOW_M, "can attack other monsters");
790 CHECK_ALLOW(ALLOW_TM, "can attack tame monsters");
791 CHECK_ALLOW(NOTONL, "avoids direct line to player");
792 CHECK_ALLOW(OPENDOOR, "opens closed doors");
793 CHECK_ALLOW(UNLOCKDOOR, "unlocks locked doors");
794 CHECK_ALLOW(BUSTDOOR, "breaks any doors");
795 CHECK_ALLOW(ALLOW_ROCK, "pushes rocks");
796 CHECK_ALLOW(ALLOW_WALL, "walks thru walls");
797 CHECK_ALLOW(ALLOW_DIG, "digs");
798 CHECK_ALLOW(ALLOW_SANCT, "enters temples");
799 CHECK_ALLOW(ALLOW_SSM, "ignores scare monster");
800 CHECK_ALLOW(NOGARLIC, "hates garlic");
801 if (allowflags) {
802 if (bp != buf) { *bp++=','; *bp++=' '; }
803 sprintf(bp, "0x%lX", allowflags);
805 return buf;
808 #undef CHECK_ALLOW
809 #endif
811 boolean
812 betrayed(mtmp)
813 register struct monst *mtmp;
815 boolean has_edog = !mtmp->isminion;
816 struct edog *edog = EDOG(mtmp);
817 int udist = distu(mtmp->mx, mtmp->my);
819 int hasbeenbetrayed = 0;
821 if (uarmu && uarmu->oartifact == ART_HEEEEELEEEEEN) return FALSE;
823 if (Role_if(PM_SLAVE_MASTER) && rn2(10)) return FALSE; /* can keep monsters tame more easily --Amy */
824 if (Race_if(PM_CELTIC) && mtmp->data->mlet == S_GOLEM) return FALSE; /* everything else betrays you more often */
825 if (Role_if(PM_POKEMON) && is_pokemon(mtmp->data) && rn2(10)) return FALSE;
826 if (uarmu && uarmu->oartifact == ART_EIGHTH_BADGE && is_pokemon(mtmp->data)) return FALSE;
828 /* dragonmaster can of course wear DSM (sorry AntiGulp) and it prevents dragons from rebelling --Amy */
829 if (Role_if(PM_DRAGONMASTER) && mtmp->data->mlet == S_DRAGON && uarm && Is_dragon_armor(uarm) ) return FALSE;
831 /* secret advice member starting pet never rebels --Amy */
832 if (Role_if(PM_SECRET_ADVICE_MEMBER) && mtmp->data == &mons[PM_BUST_SUPERSECRET_ADVICE_RIFLING_UNVERIFIED_BOSOMING]) return FALSE;
834 /* changed the way this works: first see whether the monster can betray you at all, then whether it actually does
835 * if the latter is the case, "hasbeenbetrayed" is set to 2 and the actual betrayal code runs where we roll against
836 * tameness, charisma and abuse --Amy */
838 if (udist < 4 && has_edog && (!mtmp->isspell || (mtmp->data == &mons[PM_SUMMONED_FIRE_GOLEM]) || (mtmp->data == &mons[PM_ULTRA_EVIL_QUASIT]) ) && !rn2(3)) {
839 if (can_betray(mtmp->data)) hasbeenbetrayed = 1;
840 if (Race_if(PM_CELTIC)) hasbeenbetrayed = 1;
841 if (isfriday && !rn2(10)) hasbeenbetrayed = 1;
842 if (is_jonadabmonster(mtmp->data)) hasbeenbetrayed = 1;
843 if (mtmp->data->mlevel >= 50) hasbeenbetrayed = 1;
844 if (mtmp->data == &mons[PM_SUMMONED_FIRE_GOLEM]) hasbeenbetrayed = 1;
845 if (mtmp->data == &mons[PM_ULTRA_EVIL_QUASIT]) hasbeenbetrayed = 1;
846 if (uarmc && uarmc->oartifact == ART_ARTIFICIAL_FAKE_DIFFICULTY && !rn2(3)) hasbeenbetrayed = 1;
847 if (Role_if(PM_FAILED_EXISTENCE)) hasbeenbetrayed = 1;
848 if (u.uprops[REBELLION_EFFECT].extrinsic || Rebellions || have_rebelstone() || autismweaponcheck(ART_POST_OFFICE_COURSE) || (uarmf && uarmf->oartifact == ART_KATIE_MELUA_S_FLEECINESS)) hasbeenbetrayed = 1;
849 if (Role_if(PM_UNDEAD_SLAYER) && is_undead(mtmp->data)) hasbeenbetrayed = 1;
850 if (mtmp->m_lev >= 40) hasbeenbetrayed = 1;
852 /* used to test for mindless here, but mindless creatures may still decide to attack randomly --Amy
853 * this is so that you can't simply tame a mindless pet and have it forever be loyal, of course */
855 if (hasbeenbetrayed >= 1) { /* will it really betray you? */
856 if (mtmp->mhp >= u.uhp) hasbeenbetrayed = 2;
857 if (!rn2(5)) hasbeenbetrayed = 2;
858 if (Race_if(PM_CELTIC)) hasbeenbetrayed = 2;
859 if (Role_if(PM_UNDEAD_SLAYER) && is_undead(mtmp->data)) hasbeenbetrayed = 2;
860 if (u.uprops[REBELLION_EFFECT].extrinsic || Rebellions || have_rebelstone() || autismweaponcheck(ART_POST_OFFICE_COURSE) || (uarmf && uarmf->oartifact == ART_KATIE_MELUA_S_FLEECINESS)) hasbeenbetrayed = 2;
861 if (is_jonadabmonster(mtmp->data)) hasbeenbetrayed = 2;
862 if (isfriday && !rn2(10)) hasbeenbetrayed = 2;
863 if (mtmp->data->mlevel >= 50) hasbeenbetrayed = 2;
864 if (mtmp->data == &mons[PM_SUMMONED_FIRE_GOLEM]) hasbeenbetrayed = 2;
865 if (mtmp->data == &mons[PM_ULTRA_EVIL_QUASIT]) hasbeenbetrayed = 2;
866 if (uarmc && uarmc->oartifact == ART_ARTIFICIAL_FAKE_DIFFICULTY) hasbeenbetrayed = 2;
867 if (Role_if(PM_FAILED_EXISTENCE)) hasbeenbetrayed = 2;
870 if (hasbeenbetrayed >= 2 /* here the monster needs to pass a couple rolls to be allowed to betray you */
871 && ( (rn2(22) > mtmp->mtame) /* Roll against tameness */
872 && (!((rnd(30 - ACURR(A_CHA))) < 4)) /* Roll against charisma */
873 && rn2(edog->abuse + rnd(2)) ) ) { /* Roll against abuse */
875 /* Treason */
876 if (canseemon(mtmp))
877 pline("%s turns on you!", Monnam(mtmp));
878 else
879 You_feel("uneasy about %s.", y_monnam(mtmp));
880 mtmp->mpeaceful = 0;
881 mtmp->mtame = 0;
882 mtmp->mtraitor = TRUE;
883 mtmp->isspell = 0;
884 mtmp->uexp = 0;
886 /* if the monster is a domestic animal, you could just re-tame it indefinitely... prevent that :P --Amy */
887 if (!rn2(5)) {
888 mtmp->mfrenzied = 1;
889 if (canseemon(mtmp))
890 pline("In fact, %s apparently decides to stop at nothing until you're dead!", mon_nam(mtmp));
893 /* Do we need to call newsym() here? */
894 newsym(mtmp->mx, mtmp->my);
895 return TRUE;
897 return FALSE;
900 /* return 0 (no move), 1 (move) or 2 (dead) */
902 dog_move(mtmp, after)
903 register struct monst *mtmp;
904 register int after; /* this is extra fast monster movement */
906 int omx, omy; /* original mtmp position */
907 int appr, whappr, udist;
908 int i, j, k;
909 register struct edog *edog = EDOG(mtmp);
910 struct obj *obj = (struct obj *) 0;
911 xchar otyp;
912 boolean has_edog, cursemsg[9], is_spell, do_eat = FALSE;
913 xchar nix, niy; /* position mtmp is (considering) moving to */
914 register int nx, ny; /* temporary coordinates */
915 xchar cnt, uncursedcnt, chcnt;
916 int chi = -1, nidist, ndist;
917 coord poss[9];
918 long info[9], allowflags;
919 #define GDIST(x,y) (dist2(x,y,gx,gy))
921 /* stupid bug where monsters can spawn both hostile and tame: placing this check in monmove.c does nothing --Amy */
922 if (mtmp->mfrenzied && mtmp->mpeaceful) mtmp->mpeaceful = 0;
923 if (mtmp->mfrenzied && mtmp->mtame) {
924 mtmp->mtame = 0;
925 return 0;
927 if (mtmp->mtame && !mtmp->mpeaceful && !mtmp->mfrenzied) mtmp->mpeaceful = TRUE;
929 if (mtmp->willbebanished) {
930 mtmp->willbebanished = FALSE;
931 if (u.usteed && u.usteed == mtmp) {
932 if (((u.uhave.amulet) && !u.freeplaymode) || CannotTeleport || (u.usteed && mon_has_amulet(u.usteed)) ) { pline("You shudder for a moment.");
934 if (playerlevelportdisabled()) {
935 pline("For some reason you resist the banishment!");
938 make_stunned(HStun + 2, FALSE); /* to suppress teleport control that you might have */
940 if (!u.banishmentbeam) {
941 u.banishmentbeam = 1;
942 nomul(-2, "being banished", FALSE); /* because it's not called until you get another turn... */
945 } else {
946 u_teleport_monB(mtmp, TRUE);
948 return 0;
952 * Tame Angels have isminion set and an ispriest structure instead of
953 * an edog structure. Fortunately, guardian Angels need not worry
954 * about mundane things like eating and fetching objects, and can
955 * spend all their energy defending the player. (They are the only
956 * monsters with other structures that can be tame.)
958 has_edog = !mtmp->isminion;
961 * Similar to Angels and Guardians are spell beings - temporary
962 * magical manifestations of the spellcaster's mind.
963 * They don't eat/pickup objects - only fight.
964 * But, they aren't dismissed by conflict.
966 is_spell = mtmp->isspell;
968 omx = mtmp->mx;
969 omy = mtmp->my;
970 if (has_edog && !is_spell && dog_hunger(mtmp, edog)) return(2); /* starved */
972 udist = distu(omx,omy);
973 /* Let steeds eat and maybe throw rider during Conflict */
974 if (mtmp == u.usteed) {
975 if (Conflict && (issoviet || !rn2(100)) && !resist(mtmp, RING_CLASS, 0, 0)) {
976 /* happens much less often now, so riding while causing conflict is no longer impossible --Amy */
978 if (!mayfalloffsteed()) {
979 dismount_steed(DISMOUNT_THROWN);
980 return (1);
983 udist = 1;
984 } else
985 /* maybe we tamed him while being swallowed --jgm */
986 if (!udist) return(0);
988 /* Intelligent pets may rebel (apart from minions, spell beings) */
989 /* if it's a species that's supposed to not be tameable, make it happen much more often --Amy */
990 if (!rn2( cannot_be_tamed(mtmp->data) ? 85 : 850) && betrayed(mtmp)) return 1;
991 if (!rn2(85) && is_jonadabmonster(mtmp->data) && betrayed(mtmp)) return 1;
992 if (!rn2(850) && (mtmp->data->mlevel >= 50) && betrayed(mtmp)) return 1;
993 if (Aggravate_monster && !rn2( cannot_be_tamed(mtmp->data) ? 85 : 850) && betrayed(mtmp)) return 1;
994 if (!rn2(10) && mtmp->data == &mons[PM_SUMMONED_FIRE_GOLEM] && betrayed(mtmp)) return 1;
995 if (mtmp->data == &mons[PM_ULTRA_EVIL_QUASIT]) { /* REALLY doesn't want to be tame --Amy */
996 if (betrayed(mtmp)) return 1;
997 if (betrayed(mtmp)) return 1;
998 if (betrayed(mtmp)) return 1;
999 if (betrayed(mtmp)) return 1;
1000 if (betrayed(mtmp)) return 1;
1001 if (betrayed(mtmp)) return 1;
1002 if (betrayed(mtmp)) return 1;
1003 if (betrayed(mtmp)) return 1;
1004 if (betrayed(mtmp)) return 1;
1005 if (betrayed(mtmp)) return 1;
1007 if ((u.uprops[REBELLION_EFFECT].extrinsic || Rebellions || have_rebelstone() || autismweaponcheck(ART_POST_OFFICE_COURSE) || (uarmf && uarmf->oartifact == ART_KATIE_MELUA_S_FLEECINESS) ) && !rn2(85) && betrayed(mtmp)) return 1;
1008 if (Role_if(PM_UNDEAD_SLAYER) && is_undead(mtmp->data)) return 1;
1010 /* If you abused your pet, it will _very_ slowly time out. --Amy */
1011 if (!rn2(10000) && has_edog && edog->abuse) {
1012 edog->abuse--;
1013 if (!(PlayerCannotUseSkills)) {
1014 if (!rn2(10) && edog->abuse && P_SKILL(P_PETKEEPING) >= P_BASIC) edog->abuse--;
1015 if (!rn2(10) && edog->abuse && P_SKILL(P_PETKEEPING) >= P_SKILLED) edog->abuse--;
1016 if (!rn2(10) && edog->abuse && P_SKILL(P_PETKEEPING) >= P_EXPERT) edog->abuse--;
1017 if (!rn2(10) && edog->abuse && P_SKILL(P_PETKEEPING) >= P_MASTER) edog->abuse--;
1018 if (!rn2(10) && edog->abuse && P_SKILL(P_PETKEEPING) >= P_GRAND_MASTER) edog->abuse--;
1019 if (!rn2(10) && edog->abuse && P_SKILL(P_PETKEEPING) >= P_SUPREME_MASTER) edog->abuse--;
1021 if (edog->abuse < 0) edog->abuse = 0; /* fail safe */
1025 nix = omx; /* set before newdogpos */
1026 niy = omy;
1027 cursemsg[0] = FALSE; /* lint suppression */
1028 info[0] = 0; /* ditto */
1030 if (has_edog && !is_spell) {
1031 j = dog_invent(mtmp, edog, udist);
1032 if (j == 2) return 2; /* died */
1033 else if (j == 1) goto newdogpos; /* eating something */
1035 whappr = (monstermoves - edog->whistletime < 5);
1036 } else
1037 whappr = 0;
1039 appr = dog_goal(mtmp, (has_edog && !is_spell) ? edog : (struct edog *)0,
1040 after, udist, whappr);
1041 #ifdef DEBUG
1043 char *goal;
1044 switch(gtyp)
1046 case DOGFOOD: goal = "dogfood"; break;
1047 case CADAVER: goal = "cadaver"; break;
1048 case ACCFOOD: goal = "accfood"; break;
1049 case MANFOOD: goal = "manfood"; break;
1050 case APPORT: goal = "apport"; break;
1051 case POISON: goal = "poison"; break;
1052 case UNDEF: goal = "undef"; break;
1053 case TABU: goal = "tabu"; break;
1054 default: goal = "???"; break;
1056 debugpline("G(%s): %s @ (%d,%d), appr = %d",
1057 mon_nam(mtmp), goal, gx, gy, appr);
1059 #endif
1060 if (appr == -2) return(0);
1062 allowflags = ALLOW_M | ALLOW_TRAPS | ALLOW_SSM | ALLOW_SANCT;
1063 if (passes_walls(mtmp->data) || (mtmp->egotype_wallwalk) ) allowflags |= (ALLOW_ROCK | ALLOW_WALL);
1064 if (passes_bars(mtmp->data) && !In_sokoban(&u.uz))
1065 allowflags |= ALLOW_BARS;
1066 if (throws_rocks(mtmp->data)) allowflags |= ALLOW_ROCK;
1067 if (Conflict && !resist(mtmp, RING_CLASS, 0, 0) && In_endgame(&u.uz)) {
1068 allowflags |= ALLOW_U;
1069 if (!has_edog && !is_spell) {
1070 coord mm;
1071 /* Guardian angel refuses to be conflicted; rather,
1072 * it disappears, angrily, and sends in some nasties
1074 if (canspotmon(mtmp)) {
1075 pline("%s rebukes you, saying:", Monnam(mtmp));
1076 verbalize("Since you desire conflict, have some more!");
1078 mongone(mtmp);
1079 i = rnd(4);
1080 while(i--) {
1081 mm.x = u.ux;
1082 mm.y = u.uy;
1083 if(enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL]))
1084 (void) mk_roamer(&mons[PM_ANGEL], u.ualign.type,
1085 mm.x, mm.y, FALSE);
1087 return(2);
1091 if (StrongConflict && !resist(mtmp, RING_CLASS, 0, 0) && In_endgame(&u.uz)) {
1092 allowflags |= ALLOW_U;
1093 if (!has_edog && !is_spell) {
1094 coord mm;
1095 /* Guardian angel refuses to be conflicted; rather,
1096 * it disappears, angrily, and sends in some nasties
1098 if (canspotmon(mtmp)) {
1099 pline("%s rebukes you, saying:", Monnam(mtmp));
1100 verbalize("Since you desire conflict, have some more!");
1102 mongone(mtmp);
1103 i = rnd(4);
1104 while(i--) {
1105 mm.x = u.ux;
1106 mm.y = u.uy;
1107 if(enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL]))
1108 (void) mk_roamer(&mons[PM_ANGEL], u.ualign.type,
1109 mm.x, mm.y, FALSE);
1111 return(2);
1116 /* ALI -- Mindless pets shouldn't attack monsters when
1117 * scared; they have no sense of allegiance to the hero,
1118 * only self-preservation. This prevents weak pets blocking
1119 * your exit from a shop by constantly missing shopkeeper.
1121 if (mindless(mtmp->data) && mtmp->mflee)
1122 allowflags &= ~ALLOW_M;
1124 if (!Conflict && !mtmp->mconf &&
1125 mtmp == u.ustuck && !sticks(youmonst.data)) {
1126 unstuck(mtmp); /* swallowed case handled above */
1127 You("get released!");
1129 if (!nohands(mtmp->data) && !verysmall(mtmp->data)) {
1130 allowflags |= OPENDOOR;
1131 if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR;
1132 if (m_carrying(mtmp, CONTROVERSY_CODE)) allowflags |= BUSTDOOR;
1133 if (m_carrying(mtmp, SECRET_KEY)) allowflags |= BUSTDOOR;
1135 if (is_giant(mtmp->data)) allowflags |= BUSTDOOR;
1136 if (tunnels(mtmp->data)) allowflags |= ALLOW_DIG;
1137 cnt = mfndpos(mtmp, poss, info, allowflags);
1138 #ifdef DEBUG
1139 debugpline("%d positions found with allow: %s", cnt,
1140 allow_set(allowflags));
1141 for (i = 0; i < cnt; i++) {
1142 debugpline("[%d] %s @ (%d, %d)", i,
1143 allow_set(info[i]), poss[i].x, poss[i].y);
1145 #endif
1147 /* Normally dogs don't step on cursed items, but if they have no
1148 * other choice they will. This requires checking ahead of time
1149 * to see how many uncursed item squares are around.
1152 uncursedcnt = 0;
1153 for (i = 0; i < cnt; i++) {
1154 nx = poss[i].x; ny = poss[i].y;
1155 if (MON_AT(nx,ny) && !(info[i] & ALLOW_M)) continue;
1156 if (cursed_object_at(mtmp, nx, ny)) continue;
1157 uncursedcnt++;
1160 chcnt = 0;
1161 chi = -1;
1162 nidist = GDIST(nix,niy);
1164 for (i = 0; i < cnt; i++) {
1165 nx = poss[i].x;
1166 ny = poss[i].y;
1167 cursemsg[i] = FALSE;
1169 /* if leashed, we drag him along. */
1170 if (mtmp->mleashed && distu(nx, ny) > 4) continue;
1172 /* if a guardian, try to stay close by choice */
1173 if ((!has_edog || is_spell) &&
1174 (j = distu(nx, ny)) > 16 && j >= udist) continue;
1176 if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) {
1177 int mstatus;
1178 register struct monst *mtmp2 = m_at(nx,ny);
1179 aligntyp align1, align2; /* For priests, minions etc. */
1181 if (u.petattackenemies < 1) continue;
1183 if (mtmp2->data == &mons[PM_CHAREY]) continue;
1185 if (mtmp->isminion) align1 = EMIN(mtmp)->min_align;
1186 else if (is_unicorn(mtmp->data))
1187 align1 = sgn(mtmp->data->maligntyp);
1188 else if (mtmp->ispriest) align1 = EPRI(mtmp)->shralign;
1189 else align1 = A_NONE;
1190 if (mtmp2->isminion) align2 = EMIN(mtmp2)->min_align;
1191 else if (is_unicorn(mtmp2->data))
1192 align2 = sgn(mtmp2->data->maligntyp);
1193 else if (mtmp2->ispriest) align2 = EPRI(mtmp2)->shralign;
1194 else align2 = A_NONE;
1196 /* Mindless monsters and spelled monsters have no fear of
1197 * attacking higher level monsters
1198 * Amy edit: and high-level pets are less afraid of high-level monsters; it's really stupid if your
1199 * level 45 pet still won't attack Jubilex just because the latter is level 48...
1201 if (
1204 (((int)mtmp2->m_lev >= (int)mtmp->m_lev+2) && mtmp->m_lev < 20) ||
1205 (((int)mtmp2->m_lev >= (int)mtmp->m_lev+10) && mtmp->m_lev < 30)
1207 && !is_spell && !mindless(mtmp->data))
1209 || (mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10) &&
1210 mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee
1211 && !mtmp2->minvisreal && (perceives(mtmp->data) || !mtmp2->minvis)) ||
1212 (mtmp2->data==&mons[PM_GELATINOUS_CUBE] && rn2(10)) ||
1213 (mtmp2->data==&mons[PM_GAS_SPORE] && rn2(16)) ||
1214 (!attacktype(mtmp->data, AT_EXPL) &&
1215 (max_passive_dmg(mtmp2, mtmp) >= mtmp->mhp) && mtmp->mhpmax > 1) ||
1216 /* Minions/Angels don't attack
1217 * coaligned minions/priests/angels/unicorns.
1219 (align1 == align2 && align1 != A_NONE) ||
1220 ( (mtmp->mhp*10 < mtmp->mhpmax) && mtmp->mhpmax > 2 && !Conflict ) ||
1221 (( ((mtmp->mhp*4 < mtmp->mhpmax) && mtmp->mhpmax > 1)
1222 || mtmp2->data->msound == MS_GUARDIAN
1223 || mtmp2->data->msound == MS_LEADER) &&
1224 /* the activistor quest shouldn't be trivialized by bringing a high-level pet or using charm monster. --Amy */
1225 mtmp2->mpeaceful && !Conflict) ||
1226 (Role_if(PM_ACTIVISTOR) && mtmp2->data == &mons[PM_TOPMODEL]) ||
1227 (Race_if(PM_PEACEMAKER) && mtmp2->data == &mons[PM_TOPMODEL]) ||
1228 /* for Rodneyan race characters, the real Rodney is supposed to be their buddy so he doesn't get attacked */
1229 (mtmp2->data == &mons[PM_WIZARD_OF_YENDOR] && Race_if(PM_RODNEYAN)) ||
1230 (mtmp2->data == &mons[PM_THE_ZRUTINATOR] && Race_if(PM_RODNEYAN)) ||
1231 /* troves only drop their items if the player kills them, so keep pets away from them */
1232 (mtmp2->data->mlet == S_TROVE) ||
1233 /* pets aren't allowed to attack monsters that are on the phone */
1234 (mtmp2->handytime) ||
1235 /* fear and other status effects should screw over pets */
1236 (mtmp->mflee && rn2(10)) || (mtmp->mstun && rn2(4)) || (mtmp->mconf && !rn2(3)) || (mtmp->mblinded && haseyes(mtmp->data) && !rn2(3)) ||
1237 /* invisible monsters need see invis to be attacked reliably */
1238 (mtmp2->minvis && haseyes(mtmp->data) && !perceives(mtmp->data) && rn2(2)) ||
1239 (mtmp2->minvisreal && rn2(haseyes(mtmp->data) ? 4 : 2)) ||
1240 /* petshielder egotype is never attacked by pets either */
1241 (mtmp2->egotype_petshielder || mtmp2->data == &mons[PM_TUXIE]) ||
1242 /* directive can be used to make them not attack peacefuls */
1243 (u.petattackenemies == 1 && mtmp2->mpeaceful) ||
1244 /* sing/kati trap should prevent pets from killing the shoe you're cleaning */
1245 (u.singtrapocc && mtmp2->mpeaceful) ||
1246 (u.katitrapocc && mtmp2->mpeaceful) ||
1247 /* Moldoux is special-cased */
1248 (mtmp2->data == &mons[PM_MOLDOUX__THE_DEFENCELESS_MOLD]) ||
1249 /* the "spretty" isn't attacked either */
1250 (mtmp2->data == &mons[PM_SPRETTY]) ||
1251 /* superdeep types and certain others are special */
1252 (mtmp2->data == &mons[PM_SHEER_SPACER]) || (mtmp2->data == &mons[PM_ARABELLA_SHOE]) || (mtmp2->data == &mons[PM_ANASTASIA_SHOE]) || (mtmp2->data == &mons[PM_HENRIETTA_SHOE]) || (mtmp2->data == &mons[PM_KATRIN_SHOE]) || (mtmp2->data == &mons[PM_JANA_SHOE]) || (mtmp2->data == &mons[PM_SLICK_RUEA]) || (mtmp2->data == &mons[PM_DOUBLE_AURORA_BOMBER]) || (mtmp2->data == &mons[PM_SUPERDEEP_TYPE]) || (mtmp2->data == &mons[PM_DEEP_ROCK]) || (mtmp2->data == &mons[PM_CRITICALLY_INJURED_PERCENTS]) || (mtmp2->data == &mons[PM_OGRE_PERCENTS]) || (mtmp2->data == &mons[PM_IDE_BY__]) || (mtmp2->data == &mons[PM_VAILABLE__EXIT_ANYWAY_]) || (mtmp2->data == &mons[PM_MAND_PENDING__MAGIC_SPELL___]) || (mtmp2->data == &mons[PM_E_PALE_WRAITH_WITH_A_LIGHTNING_STROKE_]) || (mtmp2->data == &mons[PM_HIGHSCORE_DUMMY]) ||
1253 /* Mister Gribbs is protected */
1254 (mtmp2->data == &mons[PM_MISTER_GRIBBS]) ||
1255 /* no thrall guards */
1256 (mtmp2->data == &mons[PM_THRALL_GUARD]) || (mtmp2->data == &mons[PM_THRALL_GATE_GUARD]) ||
1257 /* specific demon lord who is immune */
1258 (mtmp2->data == &mons[PM_OBSCURING_FLIER]) || (mtmp2->data == &mons[PM_LOOMING_SVETTE]) ||
1259 /* bulletator zero isn't either */
1260 (mtmp2->data == &mons[PM_BULLETATOR_ZERO]) ||
1261 /* your one-way girlfriend is never attacked by pets */
1262 (mtmp2->data == &mons[PM_YOUR_ONE_WAY_GIRLFRIEND]) ||
1263 /* if Izchak dies, the player gets disintegrated, so stop pets from killing them
1264 well screw it, just completely prevent them from attacking shopkeepers, priests and vault guards --Amy */
1265 /* In Soviet Russia, pets are totally stupid (in fact, even more so than modders). They simply attack everything,
1266 * even if it's something the player might want to use. --Amy */
1267 (!issoviet && mtmp2->isshk ) || (!issoviet && mtmp2->data == &mons[PM_BLACKSMITH]) || (!issoviet && mtmp2->data == &mons[PM_CROUPIER]) || (!issoviet && mtmp2->data == &mons[PM_MASTER_CROUPIER]) || (!issoviet && mtmp2->data == &mons[PM_EXPERIENCED_CROUPIER]) || (!issoviet && mtmp2->data == &mons[PM_EXCEPTIONAL_CROUPIER]) || (!issoviet && mtmp2->data == &mons[PM_ELITE_CROUPIER]) || (!issoviet && mtmp2->data == &mons[PM_SHOPKEEPER]) || (!issoviet && mtmp2->data == &mons[PM_MASTER_SHOPKEEPER]) || (!issoviet && mtmp2->data == &mons[PM_EXPERIENCED_SHOPKEEPER]) || (!issoviet && mtmp2->data == &mons[PM_BLACK_MARKETEER]) || (!issoviet && mtmp2->data == &mons[PM_EXCEPTIONAL_SHOPKEEPER]) || (!issoviet && mtmp2->data == &mons[PM_ELITE_SHOPKEEPER]) || (!issoviet && mtmp2->isgd ) || (!issoviet && mtmp2->ispriest ) ||
1268 /* extra check for priests, because roamers might not have ispriest set */
1269 ((mtmp2->data == &mons[PM_ALIGNED_PRIEST] || mtmp2->data == &mons[PM_MASTER_PRIEST] || mtmp2->data == &mons[PM_EXPERIENCED_PRIEST] || mtmp2->data == &mons[PM_EXCEPTIONAL_PRIEST] || mtmp2->data == &mons[PM_ELITE_PRIEST] || mtmp2->data == &mons[PM_DNETHACK_ELDER_PRIEST_TM_] || mtmp2->data == &mons[PM_HIGH_PRIEST]) && !issoviet && mtmp2->malign <= 0 && (int)EPRI(mtmp2)->shralign != A_NONE ) ||
1270 (touch_petrifies(mtmp2->data) &&
1271 !resists_ston(mtmp)))
1272 continue;
1274 if (after) return(0); /* hit only once each move */
1276 notonhead = 0;
1277 mstatus = mattackm(mtmp, mtmp2);
1279 /* aggressor (pet) died */
1280 if (mstatus & MM_AGR_DIED) return 2;
1282 if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) &&
1283 rn2(4) && mtmp2->mlstmv != monstermoves &&
1284 !onscary(mtmp->mx, mtmp->my, mtmp2) &&
1285 /* monnear check needed: long worms hit on tail */
1286 monnear(mtmp2, mtmp->mx, mtmp->my)) {
1287 mstatus = mattackm(mtmp2, mtmp); /* return attack */
1288 if (mstatus & MM_DEF_DIED) return 2;
1289 } else if (!(mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) && !rn2(10) && mtmp2->mlstmv != monstermoves &&
1290 !onscary(mtmp->mx, mtmp->my, mtmp2) && monnear(mtmp2, mtmp->mx, mtmp->my)) {
1292 /* Amy edit: allow monsters to occasionally fight back even if your pet missed them */
1293 mstatus = mattackm(mtmp2, mtmp); /* return attack */
1294 if (mstatus & MM_DEF_DIED) return 2;
1297 return 0;
1300 { /* Dog avoids harmful traps, but perhaps it has to pass one
1301 * in order to follow player. (Non-harmful traps do not
1302 * have ALLOW_TRAPS in info[].) The dog only avoids the
1303 * trap if you've seen it, unlike enemies who avoid traps
1304 * if they've seen some trap of that type sometime in the
1305 * past. (Neither behavior is really realistic.)
1307 struct trap *trap;
1309 if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx,ny))) {
1310 if (mtmp->mleashed) {
1311 if (flags.soundok) whimper(mtmp);
1312 } else {
1313 /* 1/40 chance of stepping on it anyway, in case
1314 * it has to pass one to follow the player...
1316 if (flags.soundok && uarmf && uarmf->oartifact == ART_HOUSE_ANIMAL_WARNER) whimper(mtmp);
1317 if (trap->tseen && rn2(40)) continue;
1322 /* dog eschews cursed objects, but likes dog food */
1323 /* [Tom] except demons & undead, who don't care */
1324 /* (minion isn't interested; `cursemsg' stays FALSE) */
1325 if (has_edog && !is_spell) {
1326 for (obj = level.objects[nx][ny]; obj; obj = obj->nexthere) {
1327 if ((obj->cursed) && has_edog && !is_demon(mtmp->data)
1328 && !is_undead(mtmp->data) && (!mtmp->egotype_undead) ) cursemsg[i] = TRUE;
1329 if (obj->blessed && has_edog && (is_demon(mtmp->data)
1330 || is_undead(mtmp->data) || mtmp->egotype_undead)) cursemsg[i] = TRUE;
1331 else if ((otyp = dogfood(mtmp, obj)) < MANFOOD && u.petcaneat &&
1332 (otyp < ACCFOOD || edog->hungrytime <= monstermoves)) {
1333 /* Note: our dog likes the food so much that he
1334 * might eat it even when it conceals a cursed object */
1335 nix = nx;
1336 niy = ny;
1337 chi = i;
1338 do_eat = TRUE;
1339 cursemsg[i] = FALSE; /* not reluctant */
1340 goto newdogpos;
1344 /* didn't find something to eat; if we saw a cursed item and
1345 aren't being forced to walk on it, usually keep looking */
1346 if (cursemsg[i] && !mtmp->mleashed && uncursedcnt > 0 &&
1347 rn2(13 * uncursedcnt)) continue;
1349 /* lessen the chance of backtracking to previous position(s) */
1351 /* FIQ says that this code is supposedly bad. While I don't agree with him (it makes the pet follow too
1352 * closely for my taste, and if you have more than one pet, it will lead to clogging up corridors, and
1353 * those will then be ultra annoying if you're stunned, confused or punished, because you can't displace
1354 * them), I can see how if you're using a leash or tin whistle, you want your pets to stay close,
1355 * so yeah, those will now turn off the "offending" code. --Amy */
1357 if (!(mtmp->mleashed || (has_edog && (monstermoves - edog->whistletime < 10)) )) {
1359 k = (has_edog && !is_spell) ? uncursedcnt : cnt;
1360 for (j = 0; j < MTSZ && j < k - 1; j++)
1361 if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y)
1362 if (rn2(MTSZ * (k - j))) goto nxti;
1365 j = ((ndist = GDIST(nx,ny)) - nidist) * appr;
1366 if ((j == 0 && !rn2(++chcnt)) || j < 0 ||
1367 (j > 0 && !whappr &&
1368 ((omx == nix && omy == niy && !rn2(3))
1369 || !rn2(12))
1370 )) {
1371 nix = nx;
1372 niy = ny;
1373 nidist = ndist;
1374 if(j < 0) chcnt = 0;
1375 chi = i;
1377 nxti: ;
1380 /* monmove.c now allows stationary pets to get turns, but they're not supposed to actually walk around --Amy
1381 * the "goto newdogpos" above means the pet can cheat and move anyway if it wants to get food :P */
1382 if (mtmp->data->mlet == S_TURRET || stationary(mtmp->data) || ((is_hider(mtmp->data) || mtmp->egotype_hide || mtmp->egotype_mimic) && (mtmp->mundetected || mtmp->m_ap_type == M_AP_FURNITURE || mtmp->m_ap_type == M_AP_OBJECT) ) ) {
1383 return 0;
1386 newdogpos:
1387 if (nix != omx || niy != omy) {
1388 struct obj *mw_tmp;
1390 if (info[chi] & ALLOW_U) {
1391 if (mtmp->mleashed) { /* play it safe */
1392 pline("%s breaks loose of %s leash!",
1393 Monnam(mtmp), mhis(mtmp));
1394 m_unleash(mtmp, FALSE);
1396 (void) mattacku(mtmp);
1397 return(0);
1399 if (!m_in_out_region(mtmp, nix, niy))
1400 return 1;
1401 if (((IS_ROCK(levl[nix][niy].typ) && !(IS_FARMLAND(levl[nix][niy].typ)) && !(IS_GRAVEWALL(levl[nix][niy].typ)) && !(IS_MOUNTAIN(levl[nix][niy].typ)) && may_dig(nix,niy)) ||
1402 closed_door(nix, niy)) &&
1403 mtmp->weapon_check != NO_WEAPON_WANTED &&
1404 tunnels(mtmp->data) && needspick(mtmp->data)) {
1405 if (closed_door(nix, niy)) {
1406 if (!(mw_tmp = MON_WEP(mtmp)) ||
1407 !is_pick(mw_tmp) || !is_axe(mw_tmp))
1408 mtmp->weapon_check = NEED_PICK_OR_AXE;
1409 } else if (IS_TREE(levl[nix][niy].typ)) {
1410 if (!(mw_tmp = MON_WEP(mtmp)) || !is_axe(mw_tmp))
1411 mtmp->weapon_check = NEED_AXE;
1412 } else if (!(mw_tmp = MON_WEP(mtmp)) || !is_pick(mw_tmp)) {
1413 mtmp->weapon_check = NEED_PICK_AXE;
1415 if (mtmp->weapon_check >= NEED_PICK_AXE &&
1416 mon_wield_item(mtmp))
1417 return 0;
1419 /* insert a worm_move() if worms ever begin to eat things */
1420 remove_monster(omx, omy);
1421 place_monster(mtmp, nix, niy);
1423 /* evil patch idea by jonadab: 1% chance for pets to step on cursed items anyway */
1424 if (has_edog && rn2(100) && !is_spell && cursemsg[chi] && (cansee(omx,omy) || cansee(nix,niy)))
1425 pline("%s moves only reluctantly.", Monnam(mtmp));
1426 for (j=MTSZ-1; j>0; j--) mtmp->mtrack[j] = mtmp->mtrack[j-1];
1427 mtmp->mtrack[0].x = omx;
1428 mtmp->mtrack[0].y = omy;
1429 /* We have to know if the pet's gonna do a combined eat and
1430 * move before moving it, but it can't eat until after being
1431 * moved. Thus the do_eat flag.
1433 if (do_eat) {
1434 if (dog_eat(mtmp, obj, omx, omy, FALSE) == 2) return 2;
1436 } else if (mtmp->mleashed && distu(omx, omy) > 4) {
1437 /* an incredible kludge, but the only way to keep pooch near
1438 * after it spends time eating or in a trap, etc.
1440 coord cc;
1442 nx = sgn(omx - u.ux);
1443 ny = sgn(omy - u.uy);
1444 cc.x = u.ux + nx;
1445 cc.y = u.uy + ny;
1446 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext;
1448 i = xytod(nx, ny);
1449 for (j = (i + 7)%8; j < (i + 1)%8; j++) {
1450 dtoxy(&cc, j);
1451 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext;
1453 for (j = (i + 6)%8; j < (i + 2)%8; j++) {
1454 dtoxy(&cc, j);
1455 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext;
1457 cc.x = mtmp->mx;
1458 cc.y = mtmp->my;
1459 dognext:
1460 if (!m_in_out_region(mtmp, nix, niy))
1461 return 1;
1462 remove_monster(mtmp->mx, mtmp->my);
1463 place_monster(mtmp, cc.x, cc.y);
1464 newsym(cc.x,cc.y);
1465 set_apparxy(mtmp);
1467 return(1);
1470 /* check if a monster could pick up objects from a location */
1471 STATIC_OVL boolean
1472 could_reach_item(mon, nx, ny)
1473 struct monst *mon;
1474 xchar nx, ny;
1476 if ((!is_waterypool(nx,ny) || mon->egotype_watersplasher || is_swimmer(mon->data)) &&
1477 (!is_lava(nx,ny) || likes_lava(mon->data)) &&
1478 (!sobj_at(BOULDER,nx,ny) || throws_rocks(mon->data)))
1479 return TRUE;
1480 return FALSE;
1483 /* Hack to prevent a dog from being endlessly stuck near an object that
1484 * it can't reach, such as caught in a teleport scroll niche. It recursively
1485 * checks to see if the squares in between are good. The checking could be a
1486 * little smarter; a full check would probably be useful in m_move() too.
1487 * Since the maximum food distance is 5, this should never be more than 5 calls
1488 * deep.
1490 STATIC_OVL boolean
1491 can_reach_location(mon, mx, my, fx, fy)
1492 struct monst *mon;
1493 xchar mx, my, fx, fy;
1495 int i, j;
1496 int dist;
1498 if (mx == fx && my == fy) return TRUE;
1499 if (!isok(mx, my)) return FALSE; /* should not happen */
1501 dist = dist2(mx, my, fx, fy);
1502 for(i=mx-1; i<=mx+1; i++) {
1503 for(j=my-1; j<=my+1; j++) {
1504 if (!isok(i, j))
1505 continue;
1506 if (dist2(i, j, fx, fy) >= dist)
1507 continue;
1508 if (IS_ROCK(levl[i][j].typ) && !(IS_FARMLAND(levl[i][j].typ)) && !passes_walls(mon->data) && (!mon->egotype_wallwalk) &&
1509 (!may_dig(i,j) || !tunnels(mon->data)))
1510 continue;
1511 if (IS_MOUNTAIN(levl[i][j].typ) && !passes_walls(mon->data) && (!mon->egotype_wallwalk))
1512 continue;
1513 if (IS_DOOR(levl[i][j].typ) &&
1514 (levl[i][j].doormask & (D_CLOSED | D_LOCKED)))
1515 continue;
1516 if (!could_reach_item(mon, i, j))
1517 continue;
1518 if (can_reach_location(mon, i, j, fx, fy))
1519 return TRUE;
1522 return FALSE;
1525 #endif /* OVL0 */
1526 #ifdef OVLB
1528 /*ARGSUSED*/ /* do_clear_area client */
1529 STATIC_PTR void
1530 wantdoor(x, y, distance)
1531 int x, y;
1532 void * distance;
1534 int ndist;
1536 if (*(int*)distance > (ndist = distu(x, y))) {
1537 gx = x;
1538 gy = y;
1539 *(int*)distance = ndist;
1543 #endif /* OVLB */
1545 /*dogmove.c*/