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. */
12 /* #define DEBUG */ /* uncomment to enable debugging */
16 #define debugpline if (wizard) pline
18 #define debugpline pline
22 extern boolean notonhead
;
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
,
33 STATIC_DCL boolean
could_reach_item(struct monst
*, XCHAR_P
,XCHAR_P
);
35 STATIC_OVL
struct obj
*
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
))
45 if (!tunnels(mon
->data
) || !needspick(mon
->data
))
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
))) {
53 if (!item2
&& obj
->otyp
== UNICORN_HORN
&& !obj
->cursed
) {
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 };
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 *);
82 cursed_object_at(mtmp
, x
, y
)
83 register struct monst
*mtmp
;
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
)
95 debugpline("%s thinks %s at (%d,%d) is `cursed'",
96 noit_Monnam(mtmp
), doname(otmp
), x
, y
);
104 dog_nutrition(mtmp
, obj
)
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
;
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;
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;
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;
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
)) {
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;
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
)
189 if (obj
->oclass
== FOOD_CLASS
) {
190 if(obj
->otyp
== CORPSE
) {
191 nutrit
= mons
[obj
->corpsenm
].cnutrit
;
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;
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;
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
;
217 register struct edog
*edog
= EDOG(mtmp
);
218 boolean poly
= FALSE
, grow
= FALSE
, heal
= FALSE
;
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
);
234 if (mtmp
->meating
> 1) mtmp
->meating
/= 2;
235 if (nutrit
> 1) nutrit
= (nutrit
* 3) / 4;
238 if (growpoints
>= 5) {
240 int hownum
= growpoints
;
246 if (hownum
> rn2(500)) linechance
++;
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) ) {
267 /* vampires only get 1/5 normal nutrition */
269 mtmp
->meating
= (mtmp
->meating
+ 4) / 5;
270 nutrit
= (nutrit
+ 4) / 5;
273 edog
->hungrytime
+= nutrit
;
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 */
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) */
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. */
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
)
310 edog
->apport
+= (int)(200L/
311 ((long)edog
->dropdist
+ monstermoves
- edog
->droptime
));
313 if (mtmp
->data
== &mons
[PM_RUST_MONSTER
] && obj
->oerodeproof
) {
314 /* The object's rustproofing is gone now */
315 obj
->oerodeproof
= 0;
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
) {
323 if (obj
->quan
> 1L) {
325 (void) splitobj(obj
, 1L);
328 obj
= splitobj(obj
, obj
->quan
- 1L);
331 if (inv_cnt() >= 52 && !merge_choice(invent
, obj
))
334 obj
= addinv(obj
); /* unlikely but a merge is possible */
337 debugpline("split object,");
341 /* Take away blood nutrition */
342 obj
->oeaten
= drainlevel(obj
);
344 } else if (obj
== uball
) {
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
);
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
);
381 } else if (obj
->quan
> 1L && obj
->oclass
== FOOD_CLASS
) {
383 obj
->owt
= weight(obj
);
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
);
403 (void) mon_spec_poly(mtmp
, (struct permonst
*)0, 0L, FALSE
,
404 cansee(mtmp
->mx
, mtmp
->my
), FALSE
, FALSE
);
406 (void) newcham(mtmp
, (struct permonst
*)0, FALSE
,
407 cansee(mtmp
->mx
, mtmp
->my
));
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
;
421 /* hunger effects -- returns TRUE on starvation */
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;
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
))
447 You_feel("worried about %s.", y_monnam(mtmp
));
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
)) {
456 You_feel("that %s is in dire need of food.", y_monnam(mtmp
));
458 You_feel("that %s is about to starve.", y_monnam(mtmp
));
460 if (edog
->abouttostarve
== 2) {
461 if (couldsee(mtmp
->mx
, mtmp
->my
)) {
463 You_feel("that %s is in dire need of food.", y_monnam(mtmp
));
465 You_feel("that %s is about to starve.", y_monnam(mtmp
));
467 if (edog
->abouttostarve
== 1) {
468 if (couldsee(mtmp
->mx
, mtmp
->my
)) {
470 You_feel("that %s needs food immediately!", y_monnam(mtmp
));
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) {
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
));
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
;
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
498 dog_invent(mtmp
, edog
, udist
)
499 register struct monst
*mtmp
;
500 register struct edog
*edog
;
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
;
508 struct obj *floor_obj;
511 if (mtmp
->msleeping
|| !mtmp
->mcanmove
) return(0);
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 */
520 if(DROPPABLES(mtmp
) || mtmp
->mgold
) {
522 if(DROPPABLES(mtmp
)) {
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
;
533 if((obj
=level
.objects
[omx
][omy
]) && !index(nofetch
,obj
->oclass
)
535 && obj
->otyp
!= SCR_MAIL
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
);
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 */
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
;
579 if (obj
->quan
!= dogquan
)
580 obj
= splitobj(obj
, dogquan
);
581 if (cansee(omx
, omy
) && flags
.verbose
)
582 pline("%s picks up %s.",
585 obj_extract_self(obj
);
587 (void) mpickobj(mtmp
,obj
,FALSE
);
591 struct obj *floor_obj;
594 obj->quan -= dogquan;
595 temp_quan = obj->quan;
596 floor_obj = level.objects[omx][omy];
597 mpickobj(mtmp,obj,FALSE);
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
);
619 /* set dog's goal -- gtyp, gx, gy
620 * returns -1/0/1 (dog's desire to approach player) or -2 (abort move)
623 dog_goal(mtmp
, edog
, after
, udist
, whappr
)
624 register struct monst
*mtmp
;
626 int after
, udist
, whappr
;
628 register int omx
, omy
;
629 boolean in_masters_sight
, dog_has_minvent
;
630 register struct obj
*obj
;
634 /* Steeds don't move on their own will */
635 if (mtmp
== u
.usteed
)
641 in_masters_sight
= couldsee(omx
, omy
);
642 dog_has_minvent
= (DROPPABLES(mtmp
) != 0);
644 if (!edog
|| mtmp
->mleashed
) { /* he's not going anywhere... */
649 #define DDIST(x,y) (dist2(x,y,omx,omy))
650 #define SQSRCHRADIUS 5
651 int min_x
, max_x
, min_y
, max_y
;
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
) {
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
)
671 /* avoid cursed items unless starving */
672 if (cursed_object_at(mtmp
, nx
, ny
) &&
673 !(edog
->mhpmax_penalty
&& otyp
< MANFOOD
))
675 /* skip completely unreacheable goals */
676 if (!could_reach_item(mtmp
, nx
, ny
) ||
677 !can_reach_location(mtmp
, mtmp
->mx
, mtmp
->my
, nx
, ny
))
679 if (otyp
< MANFOOD
) {
680 if (otyp
< gtyp
|| DDIST(nx
,ny
) < DDIST(gx
,gy
)) {
685 } else if(gtyp
== UNDEF
&& in_masters_sight
&&
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
)) {
699 /* follow player if appropriate */
701 (gtyp
!= DOGFOOD
&& gtyp
!= APPORT
&& monstermoves
< edog
->hungrytime
)) {
704 if (after
&& udist
<= 4 && gx
== u
.ux
&& gy
== u
.uy
)
706 appr
= (udist
>= 9) ? 1 : (mtmp
->mflee
) ? -1 : 0;
708 if (!IS_ROOM(levl
[u
.ux
][u
.uy
].typ
) || !rn2(4) ||
710 (dog_has_minvent
&& rn2(edog
->apport
)))
713 /* if you have dog food it'll follow you more closely */
717 if(dogfood(mtmp
, obj
) == DOGFOOD
) {
725 appr
= 1; /* gtyp != UNDEF */
729 #define FARAWAY (COLNO + 2) /* position outside screen */
730 if (gx
== u
.ux
&& gy
== u
.uy
&& !in_masters_sight
) {
733 cp
= gettrack(omx
,omy
);
737 if(edog
) edog
->ogoal
.x
= 0;
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
))) {
746 int fardist
= FARAWAY
* FARAWAY
;
747 gx
= gy
= FARAWAY
; /* random */
748 do_clear_area(omx
, omy
, 9, wantdoor
,
751 /* here gx == FARAWAY e.g. when dog is in a vault */
752 if (gx
== FARAWAY
|| (gx
== omx
&& gy
== omy
)) {
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;
771 #define CHECK_ALLOW(flag,str) if ((allowflags & (flag)) == (flag)) { \
772 allowflags ^= (flag); \
773 if (bp != buf) { *bp++=','; *bp++=' '; } \
779 allow_set(allowflags
)
782 static char buf
[500];
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");
802 if (bp
!= buf
) { *bp
++=','; *bp
++=' '; }
803 sprintf(bp
, "0x%lX", allowflags
);
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 */
877 pline("%s turns on you!", Monnam(mtmp
));
879 You_feel("uneasy about %s.", y_monnam(mtmp
));
882 mtmp
->mtraitor
= TRUE
;
886 /* if the monster is a domestic animal, you could just re-tame it indefinitely... prevent that :P --Amy */
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
);
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
;
909 register struct edog
*edog
= EDOG(mtmp
);
910 struct obj
*obj
= (struct obj
*) 0;
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
;
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
) {
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... */
946 u_teleport_monB(mtmp
, TRUE
);
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
;
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
);
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
) {
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 */
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);
1039 appr
= dog_goal(mtmp
, (has_edog
&& !is_spell
) ? edog
: (struct edog
*)0,
1040 after
, udist
, whappr
);
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
);
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
) {
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!");
1083 if(enexto(&mm
, mm
.x
, mm
.y
, &mons
[PM_ANGEL
]))
1084 (void) mk_roamer(&mons
[PM_ANGEL
], u
.ualign
.type
,
1091 if (StrongConflict
&& !resist(mtmp
, RING_CLASS
, 0, 0) && In_endgame(&u
.uz
)) {
1092 allowflags
|= ALLOW_U
;
1093 if (!has_edog
&& !is_spell
) {
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!");
1107 if(enexto(&mm
, mm
.x
, mm
.y
, &mons
[PM_ANGEL
]))
1108 (void) mk_roamer(&mons
[PM_ANGEL
], u
.ualign
.type
,
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
);
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
);
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.
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;
1162 nidist
= GDIST(nix
,niy
);
1164 for (i
= 0; i
< cnt
; i
++) {
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
)) {
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...
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
)))
1274 if (after
) return(0); /* hit only once each move */
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;
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.)
1309 if ((info
[i
] & ALLOW_TRAPS
) && (trap
= t_at(nx
,ny
))) {
1310 if (mtmp
->mleashed
) {
1311 if (flags
.soundok
) whimper(mtmp
);
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 */
1339 cursemsg
[i
] = FALSE
; /* not reluctant */
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))
1374 if(j
< 0) chcnt
= 0;
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
) ) ) {
1387 if (nix
!= omx
|| niy
!= omy
) {
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
);
1399 if (!m_in_out_region(mtmp
, nix
, niy
))
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
))
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.
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.
1442 nx
= sgn(omx
- u
.ux
);
1443 ny
= sgn(omy
- u
.uy
);
1446 if (goodpos(cc
.x
, cc
.y
, mtmp
, 0)) goto dognext
;
1449 for (j
= (i
+ 7)%8; j
< (i
+ 1)%8; j
++) {
1451 if (goodpos(cc
.x
, cc
.y
, mtmp
, 0)) goto dognext
;
1453 for (j
= (i
+ 6)%8; j
< (i
+ 2)%8; j
++) {
1455 if (goodpos(cc
.x
, cc
.y
, mtmp
, 0)) goto dognext
;
1460 if (!m_in_out_region(mtmp
, nix
, niy
))
1462 remove_monster(mtmp
->mx
, mtmp
->my
);
1463 place_monster(mtmp
, cc
.x
, cc
.y
);
1470 /* check if a monster could pick up objects from a location */
1472 could_reach_item(mon
, 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
)))
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
1491 can_reach_location(mon
, mx
, my
, fx
, fy
)
1493 xchar mx
, my
, fx
, fy
;
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
++) {
1506 if (dist2(i
, j
, fx
, fy
) >= dist
)
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
)))
1511 if (IS_MOUNTAIN(levl
[i
][j
].typ
) && !passes_walls(mon
->data
) && (!mon
->egotype_wallwalk
))
1513 if (IS_DOOR(levl
[i
][j
].typ
) &&
1514 (levl
[i
][j
].doormask
& (D_CLOSED
| D_LOCKED
)))
1516 if (!could_reach_item(mon
, i
, j
))
1518 if (can_reach_location(mon
, i
, j
, fx
, fy
))
1528 /*ARGSUSED*/ /* do_clear_area client */
1530 wantdoor(x
, y
, distance
)
1536 if (*(int*)distance
> (ndist
= distu(x
, y
))) {
1539 *(int*)distance
= ndist
;