1 /* NetHack 3.7	steed.c	$NHDT-Date: 1582155885 2020/02/19 23:44:45 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.79 $ */
2 /* Copyright (c) Kevin Hugo, 1998-1999. */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 #include "hack.h"
6 
7 /* Monsters that might be ridden */
8 static NEARDATA const char steeds[] = { S_QUADRUPED, S_UNICORN, S_ANGEL,
9                                         S_CENTAUR,   S_DRAGON,  S_JABBERWOCK,
10                                         '\0' };
11 
12 static boolean landing_spot(coord *, int, int);
13 static void maybewakesteed(struct monst *);
14 
15 /* caller has decided that hero can't reach something while mounted */
16 void
rider_cant_reach(void)17 rider_cant_reach(void)
18 {
19     You("aren't skilled enough to reach from %s.", y_monnam(u.usteed));
20 }
21 
22 /*** Putting the saddle on ***/
23 
24 /* Can this monster wear a saddle? */
25 boolean
can_saddle(struct monst * mtmp)26 can_saddle(struct monst* mtmp)
27 {
28     struct permonst *ptr = mtmp->data;
29 
30     return (index(steeds, ptr->mlet) && (ptr->msize >= MZ_MEDIUM)
31             && (!humanoid(ptr) || ptr->mlet == S_CENTAUR) && !amorphous(ptr)
32             && !noncorporeal(ptr) && !is_whirly(ptr) && !unsolid(ptr));
33 }
34 
35 int
use_saddle(struct obj * otmp)36 use_saddle(struct obj* otmp)
37 {
38     struct monst *mtmp;
39     struct permonst *ptr;
40     int chance;
41 
42     if (!u_handsy())
43         return 0;
44 
45     /* Select an animal */
46     if (u.uswallow || Underwater || !getdir((char *) 0)) {
47         pline1(Never_mind);
48         return 0;
49     }
50     if (!u.dx && !u.dy) {
51         pline("Saddle yourself?  Very funny...");
52         return 0;
53     }
54     if (!isok(u.ux + u.dx, u.uy + u.dy)
55         || !(mtmp = m_at(u.ux + u.dx, u.uy + u.dy)) || !canspotmon(mtmp)) {
56         pline("I see nobody there.");
57         return 1;
58     }
59 
60     /* Is this a valid monster? */
61     if ((mtmp->misc_worn_check & W_SADDLE) != 0L
62         || which_armor(mtmp, W_SADDLE)) {
63         pline("%s doesn't need another one.", Monnam(mtmp));
64         return 1;
65     }
66     ptr = mtmp->data;
67     if (touch_petrifies(ptr) && !uarmg && !Stone_resistance) {
68         char kbuf[BUFSZ];
69 
70         You("touch %s.", mon_nam(mtmp));
71         if (!(poly_when_stoned(g.youmonst.data)
72               && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS))) {
73             Sprintf(kbuf, "attempting to saddle %s",
74                     an(pmname(mtmp->data, Mgender(mtmp))));
75             instapetrify(kbuf);
76         }
77     }
78     if (ptr == &mons[PM_AMOROUS_DEMON]) {
79         pline("Shame on you!");
80         exercise(A_WIS, FALSE);
81         return 1;
82     }
83     if (mtmp->isminion || mtmp->isshk || mtmp->ispriest || mtmp->isgd
84         || mtmp->iswiz) {
85         pline("I think %s would mind.", mon_nam(mtmp));
86         return 1;
87     }
88     if (!can_saddle(mtmp)) {
89         You_cant("saddle such a creature.");
90         return 1;
91     }
92 
93     /* Calculate your chance */
94     chance = ACURR(A_DEX) + ACURR(A_CHA) / 2 + 2 * mtmp->mtame;
95     chance += u.ulevel * (mtmp->mtame ? 20 : 5);
96     if (!mtmp->mtame)
97         chance -= 10 * mtmp->m_lev;
98     if (Role_if(PM_KNIGHT))
99         chance += 20;
100     switch (P_SKILL(P_RIDING)) {
101     case P_ISRESTRICTED:
102     case P_UNSKILLED:
103     default:
104         chance -= 20;
105         break;
106     case P_BASIC:
107         break;
108     case P_SKILLED:
109         chance += 15;
110         break;
111     case P_EXPERT:
112         chance += 30;
113         break;
114     }
115     if (Confusion || Fumbling || Glib)
116         chance -= 20;
117     else if (uarmg && objdescr_is(uarmg, "riding gloves"))
118         /* Bonus for wearing "riding" (but not fumbling) gloves */
119         chance += 10;
120     else if (uarmf && objdescr_is(uarmf, "riding boots"))
121         /* ... or for "riding boots" */
122         chance += 10;
123     if (otmp->cursed)
124         chance -= 50;
125 
126     /* [intended] steed becomes alert if possible */
127     maybewakesteed(mtmp);
128 
129     /* Make the attempt */
130     if (rn2(100) < chance) {
131         You("put the saddle on %s.", mon_nam(mtmp));
132         if (otmp->owornmask)
133             remove_worn_item(otmp, FALSE);
134         freeinv(otmp);
135         put_saddle_on_mon(otmp, mtmp);
136     } else
137         pline("%s resists!", Monnam(mtmp));
138     return 1;
139 }
140 
141 void
put_saddle_on_mon(struct obj * saddle,struct monst * mtmp)142 put_saddle_on_mon(struct obj* saddle, struct monst* mtmp)
143 {
144     if (!can_saddle(mtmp) || which_armor(mtmp, W_SADDLE))
145         return;
146     if (mpickobj(mtmp, saddle))
147         panic("merged saddle?");
148     mtmp->misc_worn_check |= W_SADDLE;
149     saddle->owornmask = W_SADDLE;
150     saddle->leashmon = mtmp->m_id;
151     update_mon_intrinsics(mtmp, saddle, TRUE, FALSE);
152 }
153 
154 /*** Riding the monster ***/
155 
156 /* Can we ride this monster?  Caller should also check can_saddle() */
157 boolean
can_ride(struct monst * mtmp)158 can_ride(struct monst* mtmp)
159 {
160     return (mtmp->mtame && humanoid(g.youmonst.data)
161             && !verysmall(g.youmonst.data) && !bigmonst(g.youmonst.data)
162             && (!Underwater || is_swimmer(mtmp->data)));
163 }
164 
165 int
doride(void)166 doride(void)
167 {
168     boolean forcemount = FALSE;
169 
170     if (u.usteed) {
171         dismount_steed(DISMOUNT_BYCHOICE);
172     } else if (getdir((char *) 0) && isok(u.ux + u.dx, u.uy + u.dy)) {
173         if (wizard && yn("Force the mount to succeed?") == 'y')
174             forcemount = TRUE;
175         return (mount_steed(m_at(u.ux + u.dx, u.uy + u.dy), forcemount));
176     } else {
177         return 0;
178     }
179     return 1;
180 }
181 
182 /* Start riding, with the given monster */
183 boolean
mount_steed(struct monst * mtmp,boolean force)184 mount_steed(
185     struct monst *mtmp, /* The animal */
186     boolean force)      /* Quietly force this animal */
187 {
188     struct obj *otmp;
189     char buf[BUFSZ];
190     struct permonst *ptr;
191 
192     /* Sanity checks */
193     if (u.usteed) {
194         You("are already riding %s.", mon_nam(u.usteed));
195         return (FALSE);
196     }
197 
198     /* Is the player in the right form? */
199     if (Hallucination && !force) {
200         pline("Maybe you should find a designated driver.");
201         return (FALSE);
202     }
203     /* While riding Wounded_legs refers to the steed's,
204      * not the hero's legs.
205      * That opens up a potential abuse where the player
206      * can mount a steed, then dismount immediately to
207      * heal leg damage, because leg damage is always
208      * healed upon dismount (Wounded_legs context switch).
209      * By preventing a hero with Wounded_legs from
210      * mounting a steed, the potential for abuse is
211      * reduced.  However, dismounting still immediately
212      * heals the steed's wounded legs.  [In 3.4.3 and
213      * earlier, that unintentionally made the hero's
214      * temporary 1 point Dex loss become permanent.]
215      */
216     if (Wounded_legs) {
217         char qbuf[QBUFSZ];
218 
219         legs_in_no_shape("riding", FALSE);
220         Sprintf(qbuf, "Heal your leg%s?",
221                 ((HWounded_legs & BOTH_SIDES) == BOTH_SIDES) ? "s" : "");
222         if (force && wizard && yn(qbuf) == 'y')
223             heal_legs(0);
224         else
225             return (FALSE);
226     }
227 
228     if (Upolyd && (!humanoid(g.youmonst.data) || verysmall(g.youmonst.data)
229                    || bigmonst(g.youmonst.data) || slithy(g.youmonst.data))) {
230         You("won't fit on a saddle.");
231         return (FALSE);
232     }
233     if (!force && (near_capacity() > SLT_ENCUMBER)) {
234         You_cant("do that while carrying so much stuff.");
235         return (FALSE);
236     }
237 
238     /* Can the player reach and see the monster? */
239     if (!mtmp || (!force && ((Blind && !Blind_telepat) || mtmp->mundetected
240                              || M_AP_TYPE(mtmp) == M_AP_FURNITURE
241                              || M_AP_TYPE(mtmp) == M_AP_OBJECT))) {
242         pline("I see nobody there.");
243         return (FALSE);
244     }
245     if (mtmp->data == &mons[PM_LONG_WORM]
246         && (u.ux + u.dx != mtmp->mx || u.uy + u.dy != mtmp->my)) {
247         /* As of 3.6.2:  test_move(below) is used to check for trying to mount
248            diagonally into or out of a doorway or through a tight squeeze;
249            attempting to mount a tail segment when hero was not adjacent
250            to worm's head could trigger an impossible() in worm_cross()
251            called from test_move(), so handle not-on-head before that */
252         You("couldn't ride %s, let alone its tail.", a_monnam(mtmp));
253         return FALSE;
254     }
255     if (u.uswallow || u.ustuck || u.utrap || Punished
256         || !test_move(u.ux, u.uy, mtmp->mx - u.ux, mtmp->my - u.uy,
257                       TEST_MOVE)) {
258         if (Punished || !(u.uswallow || u.ustuck || u.utrap))
259             You("are unable to swing your %s over.", body_part(LEG));
260         else
261             You("are stuck here for now.");
262         return (FALSE);
263     }
264 
265     /* Is this a valid monster? */
266     otmp = which_armor(mtmp, W_SADDLE);
267     if (!otmp) {
268         pline("%s is not saddled.", Monnam(mtmp));
269         return (FALSE);
270     }
271     ptr = mtmp->data;
272     if (touch_petrifies(ptr) && !Stone_resistance) {
273         char kbuf[BUFSZ];
274 
275         You("touch %s.", mon_nam(mtmp));
276         Sprintf(kbuf, "attempting to ride %s",
277                 an(pmname(mtmp->data, Mgender(mtmp))));
278         instapetrify(kbuf);
279     }
280     if (!mtmp->mtame || mtmp->isminion) {
281         pline("I think %s would mind.", mon_nam(mtmp));
282         return (FALSE);
283     }
284     if (mtmp->mtrapped) {
285         struct trap *t = t_at(mtmp->mx, mtmp->my);
286 
287         You_cant("mount %s while %s's trapped in %s.", mon_nam(mtmp),
288                  mhe(mtmp), an(trapname(t->ttyp, FALSE)));
289         return (FALSE);
290     }
291 
292     if (!force && !Role_if(PM_KNIGHT) && !(--mtmp->mtame)) {
293         /* no longer tame */
294         newsym(mtmp->mx, mtmp->my);
295         pline("%s resists%s!", Monnam(mtmp),
296               mtmp->mleashed ? " and its leash comes off" : "");
297         if (mtmp->mleashed)
298             m_unleash(mtmp, FALSE);
299         return (FALSE);
300     }
301     if (!force && Underwater && !is_swimmer(ptr)) {
302         You_cant("ride that creature while under %s.",
303                  hliquid("water"));
304         return (FALSE);
305     }
306     if (!can_saddle(mtmp) || !can_ride(mtmp)) {
307         You_cant("ride such a creature.");
308         return FALSE;
309     }
310 
311     /* Is the player impaired? */
312     if (!force && grounded(ptr) && Levitation
313         && !Lev_at_will) {
314         You("cannot reach %s.", mon_nam(mtmp));
315         return (FALSE);
316     }
317     if (!force && uarm && is_metallic(uarm) && greatest_erosion(uarm)) {
318         Your("%s armor is too stiff to be able to mount %s.",
319              uarm->oeroded ? "rusty" : "corroded", mon_nam(mtmp));
320         return (FALSE);
321     }
322     if (!force
323         && (Confusion || Fumbling || Glib || Wounded_legs || otmp->cursed
324             || (u.ulevel + mtmp->mtame < rnd(MAXULEV / 2 + 5)))) {
325         if (Levitation) {
326             pline("%s slips away from you.", Monnam(mtmp));
327             return FALSE;
328         }
329         You("slip while trying to get on %s.", mon_nam(mtmp));
330 
331         Sprintf(buf, "slipped while mounting %s",
332                 /* "a saddled mumak" or "a saddled pony called Dobbin" */
333                 x_monnam(mtmp, ARTICLE_A, (char *) 0,
334                          SUPPRESS_IT | SUPPRESS_INVISIBLE
335                              | SUPPRESS_HALLUCINATION,
336                          TRUE));
337         losehp(Maybe_Half_Phys(rn1(5, 10)), buf, NO_KILLER_PREFIX);
338         return (FALSE);
339     }
340 
341     /* Success */
342     maybewakesteed(mtmp);
343     if (!force) {
344         if (Levitation && !is_floater(ptr) && !is_flyer(ptr))
345             /* Must have Lev_at_will at this point */
346             pline("%s magically floats up!", Monnam(mtmp));
347         You("mount %s.", mon_nam(mtmp));
348         if (Flying)
349             You("and %s take flight together.", mon_nam(mtmp));
350     }
351     /* setuwep handles polearms differently when you're mounted */
352     if (uwep && is_pole(uwep))
353         g.unweapon = FALSE;
354     u.usteed = mtmp;
355     remove_monster(mtmp->mx, mtmp->my);
356     teleds(mtmp->mx, mtmp->my, TELEDS_ALLOW_DRAG);
357     g.context.botl = TRUE;
358     return TRUE;
359 }
360 
361 /* You and your steed have moved */
362 void
exercise_steed(void)363 exercise_steed(void)
364 {
365     if (!u.usteed)
366         return;
367 
368     /* It takes many turns of riding to exercise skill */
369     if (++u.urideturns >= 100) {
370         u.urideturns = 0;
371         use_skill(P_RIDING, 1);
372     }
373     return;
374 }
375 
376 /* The player kicks or whips the steed */
377 void
kick_steed(void)378 kick_steed(void)
379 {
380     char He[BUFSZ]; /* monverbself() appends to the "He"/"She"/"It" value */
381     if (!u.usteed)
382         return;
383 
384     /* [ALI] Various effects of kicking sleeping/paralyzed steeds */
385     if (u.usteed->msleeping || !u.usteed->mcanmove) {
386         /* We assume a message has just been output of the form
387          * "You kick <steed>."
388          */
389         Strcpy(He, mhe(u.usteed));
390         *He = highc(*He);
391         if ((u.usteed->mcanmove || u.usteed->mfrozen) && !rn2(2)) {
392             if (u.usteed->mcanmove)
393                 u.usteed->msleeping = 0;
394             else if (u.usteed->mfrozen > 2)
395                 u.usteed->mfrozen -= 2;
396             else {
397                 u.usteed->mfrozen = 0;
398                 u.usteed->mcanmove = 1;
399             }
400             if (u.usteed->msleeping || !u.usteed->mcanmove)
401                 pline("%s stirs.", He);
402             else
403                 /* if hallucinating, might yield "He rouses herself" or
404                    "She rouses himself" */
405                 pline("%s!", monverbself(u.usteed, He, "rouse", (char *) 0));
406         } else
407             pline("%s does not respond.", He);
408         return;
409     }
410 
411     /* Make the steed less tame and check if it resists */
412     if (u.usteed->mtame)
413         u.usteed->mtame--;
414     if (!u.usteed->mtame && u.usteed->mleashed)
415         m_unleash(u.usteed, TRUE);
416     if (!u.usteed->mtame
417         || (u.ulevel + u.usteed->mtame < rnd(MAXULEV / 2 + 5))) {
418         newsym(u.usteed->mx, u.usteed->my);
419         dismount_steed(DISMOUNT_THROWN);
420         return;
421     }
422 
423     pline("%s gallops!", Monnam(u.usteed));
424     u.ugallop += rn1(20, 30);
425     return;
426 }
427 
428 /*
429  * Try to find a dismount point adjacent to the steed's location.
430  * If all else fails, try enexto().  Use enexto() as a last resort because
431  * enexto() chooses its point randomly, possibly even outside the
432  * room's walls, which is not what we want.
433  * Adapted from mail daemon code.
434  */
435 static boolean
landing_spot(coord * spot,int reason,int forceit)436 landing_spot(
437     coord *spot, /* landing position (we fill it in) */
438     int reason,
439     int forceit)
440 {
441     int i = 0, x, y, distance, min_distance = -1;
442     boolean found = FALSE;
443     struct trap *t;
444 
445     /* avoid known traps (i == 0) and boulders, but allow them as a backup */
446     if (reason != DISMOUNT_BYCHOICE || Stunned || Confusion || Fumbling)
447         i = 1;
448     for (; !found && i < 2; ++i) {
449         for (x = u.ux - 1; x <= u.ux + 1; x++)
450             for (y = u.uy - 1; y <= u.uy + 1; y++) {
451                 if (!isok(x, y) || (x == u.ux && y == u.uy))
452                     continue;
453 
454                 if (accessible(x, y) && !MON_AT(x, y)
455                     && test_move(u.ux, u.uy, x - u.ux, y - u.uy, TEST_MOVE)) {
456                     distance = distu(x, y);
457                     if (min_distance < 0 || distance < min_distance
458                         || (distance == min_distance && rn2(2))) {
459                         if (i > 0 || (((t = t_at(x, y)) == 0 || !t->tseen)
460                                       && (!sobj_at(BOULDER, x, y)
461                                           || throws_rocks(g.youmonst.data)))) {
462                             spot->x = x;
463                             spot->y = y;
464                             min_distance = distance;
465                             found = TRUE;
466                         }
467                     }
468                 }
469             }
470     }
471 
472     /* If we didn't find a good spot and forceit is on, try enexto(). */
473     if (forceit && min_distance < 0
474         && !enexto(spot, u.ux, u.uy, g.youmonst.data))
475         return FALSE;
476 
477     return found;
478 }
479 
480 /* Stop riding the current steed */
481 void
dismount_steed(int reason)482 dismount_steed(
483     int reason) /* Player was thrown off etc. */
484 {
485     struct monst *mtmp;
486     struct obj *otmp;
487     const char *verb;
488     coord cc, steedcc;
489     unsigned save_utrap = u.utrap;
490     boolean ulev, ufly,
491             repair_leg_damage = (Wounded_legs != 0L),
492             have_spot = landing_spot(&cc, reason, 0);
493 
494     mtmp = u.usteed; /* make a copy of steed pointer */
495     /* Sanity check */
496     if (!mtmp) /* Just return silently */
497         return;
498     u.usteed = 0; /* affects Fly test; could hypothetically affect Lev */
499     ufly = Flying ? TRUE : FALSE;
500     ulev = Levitation ? TRUE : FALSE;
501     u.usteed = mtmp;
502 
503     /* Check the reason for dismounting */
504     otmp = which_armor(mtmp, W_SADDLE);
505     switch (reason) {
506     case DISMOUNT_THROWN:
507     case DISMOUNT_FELL:
508         verb = (reason == DISMOUNT_THROWN) ? "are thrown"
509                : ulev ? "float" : ufly ? "fly" : "fall";
510         You("%s off of %s!", verb, mon_nam(mtmp));
511         if (!have_spot)
512             have_spot = landing_spot(&cc, reason, 1);
513         if (!ulev && !ufly) {
514             losehp(Maybe_Half_Phys(rn1(10, 10)), "riding accident",
515                    KILLED_BY_AN);
516             set_wounded_legs(BOTH_SIDES, (int) HWounded_legs + rn1(5, 5));
517             repair_leg_damage = FALSE;
518         }
519         break;
520     case DISMOUNT_POLY:
521         You("can no longer ride %s.", mon_nam(u.usteed));
522         if (!have_spot)
523             have_spot = landing_spot(&cc, reason, 1);
524         break;
525     case DISMOUNT_ENGULFED:
526         /* caller displays message */
527         break;
528     case DISMOUNT_BONES:
529         /* hero has just died... */
530         break;
531     case DISMOUNT_GENERIC:
532         /* no messages, just make it so */
533         break;
534     case DISMOUNT_BYCHOICE:
535     default:
536         if (otmp && otmp->cursed) {
537             You("can't.  The saddle %s cursed.",
538                 otmp->bknown ? "is" : "seems to be");
539             otmp->bknown = 1; /* ok to skip set_bknown() here */
540             return;
541         }
542         if (!have_spot) {
543             You("can't.  There isn't anywhere for you to stand.");
544             return;
545         }
546         if (!has_mgivenname(mtmp)) {
547             pline("You've been through the dungeon on %s with no name.",
548                   an(pmname(mtmp->data, Mgender(mtmp))));
549             if (Hallucination)
550                 pline("It felt good to get out of the rain.");
551         } else
552             You("dismount %s.", mon_nam(mtmp));
553     }
554     /* While riding, Wounded_legs refers to the steed's legs;
555        after dismounting, it reverts to the hero's legs. */
556     if (repair_leg_damage)
557         heal_legs(1);
558 
559     /* Release the steed and saddle */
560     u.usteed = 0;
561     u.ugallop = 0L;
562     /*
563      * rloc(), rloc_to(), and monkilled()->mondead()->m_detach() all
564      * expect mtmp to be on the map or else have mtmp->mx be 0, but
565      * setting the latter to 0 here would interfere with dropping
566      * the saddle.  Prior to 3.6.2, being off the map didn't matter.
567      *
568      * place_monster() expects mtmp to be alive and not be u.usteed.
569      *
570      * Unfortunately, <u.ux,u.uy> (former steed's implicit location)
571      * might now be occupied by an engulfer, so we can't just put mtmp
572      * at that spot.  An engulfer's previous spot will be unoccupied
573      * but we don't know where that was and even if we did, it might
574      * be hostile terrain.
575      */
576     steedcc.x = u.ux, steedcc.y = u.uy;
577     if (m_at(u.ux, u.uy)) {
578         /* hero's spot has a monster in it; hero must have been plucked
579            from saddle as engulfer moved into his spot--other dismounts
580            shouldn't run into this situation; find nearest viable spot */
581         if (!enexto(&steedcc, u.ux, u.uy, mtmp->data)
582             /* no spot? must have been engulfed by a lurker-above over
583                water or lava; try requesting a location for a flyer */
584             && !enexto(&steedcc, u.ux, u.uy, &mons[PM_BAT]))
585             /* still no spot; last resort is any spot within bounds */
586             (void) enexto(&steedcc, u.ux, u.uy, &mons[PM_GHOST]);
587     }
588     if (!m_at(steedcc.x, steedcc.y)) {
589         if (mtmp->mhp < 1)
590             mtmp->mhp = 0; /* make sure it isn't negative */
591         mtmp->mhp++; /* force at least one hit point, possibly resurrecting */
592         place_monster(mtmp, steedcc.x, steedcc.y);
593         mtmp->mhp--; /* take the extra hit point away: cancel resurrection */
594     } else {
595         impossible("Dismounting: can't place former steed on map.");
596     }
597 
598     if (!DEADMONSTER(mtmp)) {
599         /* if for bones, there's no reason to place the hero;
600            we want to make room for potential ghost, so move steed */
601         if (reason == DISMOUNT_BONES) {
602             /* move the steed to an adjacent square */
603             if (enexto(&cc, u.ux, u.uy, mtmp->data))
604                 rloc_to(mtmp, cc.x, cc.y);
605             else /* evidently no room nearby; move steed elsewhere */
606                 (void) rloc(mtmp, FALSE);
607             return;
608         }
609 
610         /* Set hero's and/or steed's positions.  Try moving the hero first. */
611         if (!u.uswallow && !u.ustuck && have_spot) {
612             struct permonst *mdat = mtmp->data;
613 
614             /* The steed may drop into water/lava */
615             if (grounded(mdat)) {
616                 if (is_pool(u.ux, u.uy)) {
617                     if (!Underwater)
618                         pline("%s falls into the %s!", Monnam(mtmp),
619                               surface(u.ux, u.uy));
620                     if (!is_swimmer(mdat) && !amphibious(mdat)) {
621                         killed(mtmp);
622                         adjalign(-1);
623                     }
624                 } else if (is_lava(u.ux, u.uy)) {
625                     pline("%s is pulled into the %s!", Monnam(mtmp),
626                           hliquid("lava"));
627                     if (!likes_lava(mdat)) {
628                         killed(mtmp);
629                         adjalign(-1);
630                     }
631                 }
632             }
633             /* Steed dismounting consists of two steps: being moved to another
634              * square, and descending to the floor.  We have functions to do
635              * each of these activities, but they're normally called
636              * individually and include an attempt to look at or pick up the
637              * objects on the floor:
638              * teleds() --> spoteffects() --> pickup()
639              * float_down() --> pickup()
640              * We use this kludge to make sure there is only one such attempt.
641              *
642              * Clearly this is not the best way to do it.  A full fix would
643              * involve having these functions not call pickup() at all,
644              * instead
645              * calling them first and calling pickup() afterwards.  But it
646              * would take a lot of work to keep this change from having any
647              * unforeseen side effects (for instance, you would no longer be
648              * able to walk onto a square with a hole, and autopickup before
649              * falling into the hole).
650              */
651             /* [ALI] No need to move the player if the steed died. */
652             if (!DEADMONSTER(mtmp)) {
653                 /* Keep steed here, move the player to cc;
654                  * teleds() clears u.utrap
655                  */
656                 g.in_steed_dismounting = TRUE;
657                 teleds(cc.x, cc.y, TELEDS_ALLOW_DRAG);
658                 if (sobj_at(BOULDER, cc.x, cc.y))
659                     sokoban_guilt();
660                 g.in_steed_dismounting = FALSE;
661 
662                 /* Put your steed in your trap */
663                 if (save_utrap)
664                     (void) mintrap(mtmp);
665             }
666 
667         /* Couldn't move hero... try moving the steed. */
668         } else if (enexto(&cc, u.ux, u.uy, mtmp->data)) {
669             /* Keep player here, move the steed to cc */
670             rloc_to(mtmp, cc.x, cc.y);
671             /* Player stays put */
672 
673         /* Otherwise, kill the steed. */
674         } else {
675             if (reason == DISMOUNT_BYCHOICE) {
676                 /* [un]#ride: hero gets credit/blame for killing steed */
677                 killed(mtmp);
678                 adjalign(-1);
679             } else {
680                 /* other dismount: kill former steed with no penalty;
681                    damage type is just "neither AD_DGST nor -AD_RBRE" */
682                 monkilled(mtmp, "", -AD_PHYS);
683             }
684         }
685     } /* !DEADMONST(mtmp) */
686 
687     /* usually return the hero to the surface */
688     if (reason != DISMOUNT_ENGULFED && reason != DISMOUNT_BONES) {
689         g.in_steed_dismounting = TRUE;
690         (void) float_down(0L, W_SADDLE);
691         g.in_steed_dismounting = FALSE;
692         g.context.botl = TRUE;
693         (void) encumber_msg();
694         g.vision_full_recalc = 1;
695     } else
696         g.context.botl = TRUE;
697     /* polearms behave differently when not mounted */
698     if (uwep && is_pole(uwep))
699         g.unweapon = TRUE;
700     return;
701 }
702 
703 /* when attempting to saddle or mount a sleeping steed, try to wake it up
704    (for the saddling case, it won't be u.usteed yet) */
705 static void
maybewakesteed(struct monst * steed)706 maybewakesteed(struct monst* steed)
707 {
708     int frozen = (int) steed->mfrozen;
709     boolean wasimmobile = steed->msleeping || !steed->mcanmove;
710 
711     steed->msleeping = 0;
712     if (frozen) {
713         frozen = (frozen + 1) / 2; /* half */
714         /* might break out of timed sleep or paralysis */
715         if (!rn2(frozen)) {
716             steed->mfrozen = 0;
717             steed->mcanmove = 1;
718         } else {
719             /* didn't awake, but remaining duration is halved */
720             steed->mfrozen = frozen;
721         }
722     }
723     if (wasimmobile && !steed->msleeping && steed->mcanmove)
724         pline("%s wakes up.", Monnam(steed));
725     /* regardless of waking, terminate any meal in progress */
726     finish_meating(steed);
727 }
728 
729 /* decide whether hero's steed is able to move;
730    doesn't check for holding traps--those affect the hero directly */
731 boolean
stucksteed(boolean checkfeeding)732 stucksteed(boolean checkfeeding)
733 {
734     struct monst *steed = u.usteed;
735 
736     if (steed) {
737         /* check whether steed can move */
738         if (steed->msleeping || !steed->mcanmove) {
739             pline("%s won't move!", upstart(y_monnam(steed)));
740             return TRUE;
741         }
742         /* optionally check whether steed is in the midst of a meal */
743         if (checkfeeding && steed->meating) {
744             pline("%s is still eating.", upstart(y_monnam(steed)));
745             return TRUE;
746         }
747     }
748     return FALSE;
749 }
750 
751 void
place_monster(struct monst * mon,int x,int y)752 place_monster(struct monst* mon, int x, int y)
753 {
754     struct monst *othermon;
755     const char *monnm, *othnm;
756     char buf[QBUFSZ];
757     fuzl_mtmp("place_monster", mon);
758     fuzl_xy(" -> place_monster", x,y);
759 
760     buf[0] = '\0';
761     /* normal map bounds are <1..COLNO-1,0..ROWNO-1> but sometimes
762        vault guards (either living or dead) are parked at <0,0> */
763     if (!isok(x, y) && (x != 0 || y != 0 || !mon->isgd)) {
764         describe_level(buf);
765         impossible("trying to place %s at <%d,%d> mstate:%lx on %s",
766                    minimal_monnam(mon, TRUE), x, y, mon->mstate, buf);
767         x = y = 0;
768     }
769     if (mon == u.usteed
770         /* special case is for convoluted vault guard handling */
771         || (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) {
772         describe_level(buf);
773         impossible("placing %s onto map, mstate:%lx, on %s?",
774                    (mon == u.usteed) ? "steed" : "defunct monster",
775                    mon->mstate, buf);
776         return;
777     }
778     if ((othermon = g.level.monsters[x][y]) != 0) {
779         describe_level(buf);
780         monnm = minimal_monnam(mon, FALSE);
781         othnm = (mon != othermon) ? minimal_monnam(othermon, TRUE) : "itself";
782         impossible("placing %s over %s at <%d,%d>, mstates:%lx %lx on %s?",
783                    monnm, othnm, x, y, othermon->mstate, mon->mstate, buf);
784     }
785     mon->mx = x, mon->my = y;
786     g.level.monsters[x][y] = mon;
787     mon->mstate &= ~(MON_OFFMAP | MON_MIGRATING | MON_LIMBO | MON_BUBBLEMOVE
788                      | MON_ENDGAME_FREE | MON_ENDGAME_MIGR);
789 }
790 
791 /*steed.c*/
792