1 /* NetHack 3.6	steed.c	$NHDT-Date: 1575245090 2019/12/02 00:04:50 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.68 $ */
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_DCL boolean FDECL(landing_spot, (coord *, int, int));
13 STATIC_DCL void FDECL(maybewakesteed, (struct monst *));
14 
15 /* caller has decided that hero can't reach something while mounted */
16 void
rider_cant_reach()17 rider_cant_reach()
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(mtmp)26 can_saddle(mtmp)
27 struct monst *mtmp;
28 {
29     struct permonst *ptr = mtmp->data;
30 
31     return (index(steeds, ptr->mlet) && (ptr->msize >= MZ_MEDIUM)
32             && (!humanoid(ptr) || ptr->mlet == S_CENTAUR) && !amorphous(ptr)
33             && !noncorporeal(ptr) && !is_whirly(ptr) && !unsolid(ptr));
34 }
35 
36 int
use_saddle(otmp)37 use_saddle(otmp)
38 struct obj *otmp;
39 {
40     struct monst *mtmp;
41     struct permonst *ptr;
42     int chance;
43     const char *s;
44 
45     if (!u_handsy())
46         return 0;
47 
48     /* Select an animal */
49     if (u.uswallow || Underwater || !getdir((char *) 0)) {
50         pline1(Never_mind);
51         return 0;
52     }
53     if (!u.dx && !u.dy) {
54         pline("Saddle yourself?  Very funny...");
55         return 0;
56     }
57     if (!isok(u.ux + u.dx, u.uy + u.dy)
58         || !(mtmp = m_at(u.ux + u.dx, u.uy + u.dy)) || !canspotmon(mtmp)) {
59         pline("I see nobody there.");
60         return 1;
61     }
62 
63     /* Is this a valid monster? */
64     if (mtmp->misc_worn_check & W_SADDLE || which_armor(mtmp, W_SADDLE)) {
65         pline("%s doesn't need another one.", Monnam(mtmp));
66         return 1;
67     }
68     ptr = mtmp->data;
69     if (touch_petrifies(ptr) && !uarmg && !Stone_resistance) {
70         char kbuf[BUFSZ];
71 
72         You("touch %s.", mon_nam(mtmp));
73         if (!(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) {
74             Sprintf(kbuf, "attempting to saddle %s", an(mtmp->data->mname));
75             instapetrify(kbuf);
76         }
77     }
78     if (ptr == &mons[PM_INCUBUS] || ptr == &mons[PM_SUCCUBUS]) {
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 && (s = OBJ_DESCR(objects[uarmg->otyp])) != (char *) 0
118              && !strncmp(s, "riding ", 7))
119         /* Bonus for wearing "riding" (but not fumbling) gloves */
120         chance += 10;
121     else if (uarmf && (s = OBJ_DESCR(objects[uarmf->otyp])) != (char *) 0
122              && !strncmp(s, "riding ", 7))
123         /* ... or for "riding boots" */
124         chance += 10;
125     if (otmp->cursed)
126         chance -= 50;
127 
128     /* [intended] steed becomes alert if possible */
129     maybewakesteed(mtmp);
130 
131     /* Make the attempt */
132     if (rn2(100) < chance) {
133         You("put the saddle on %s.", mon_nam(mtmp));
134         if (otmp->owornmask)
135             remove_worn_item(otmp, FALSE);
136         freeinv(otmp);
137         put_saddle_on_mon(otmp, mtmp);
138     } else
139         pline("%s resists!", Monnam(mtmp));
140     return 1;
141 }
142 
143 void
put_saddle_on_mon(saddle,mtmp)144 put_saddle_on_mon(saddle, mtmp)
145 struct obj *saddle;
146 struct monst *mtmp;
147 {
148     if (!can_saddle(mtmp) || which_armor(mtmp, W_SADDLE))
149         return;
150     if (mpickobj(mtmp, saddle))
151         panic("merged saddle?");
152     mtmp->misc_worn_check |= W_SADDLE;
153     saddle->owornmask = W_SADDLE;
154     saddle->leashmon = mtmp->m_id;
155     update_mon_intrinsics(mtmp, saddle, TRUE, FALSE);
156 }
157 
158 /*** Riding the monster ***/
159 
160 /* Can we ride this monster?  Caller should also check can_saddle() */
161 boolean
can_ride(mtmp)162 can_ride(mtmp)
163 struct monst *mtmp;
164 {
165     return (mtmp->mtame && humanoid(youmonst.data)
166             && !verysmall(youmonst.data) && !bigmonst(youmonst.data)
167             && (!Underwater || is_swimmer(mtmp->data)));
168 }
169 
170 int
doride()171 doride()
172 {
173     boolean forcemount = FALSE;
174 
175     if (u.usteed) {
176         dismount_steed(DISMOUNT_BYCHOICE);
177     } else if (getdir((char *) 0) && isok(u.ux + u.dx, u.uy + u.dy)) {
178         if (wizard && yn("Force the mount to succeed?") == 'y')
179             forcemount = TRUE;
180         return (mount_steed(m_at(u.ux + u.dx, u.uy + u.dy), forcemount));
181     } else {
182         return 0;
183     }
184     return 1;
185 }
186 
187 /* Start riding, with the given monster */
188 boolean
mount_steed(mtmp,force)189 mount_steed(mtmp, force)
190 struct monst *mtmp; /* The animal */
191 boolean force;      /* Quietly force this animal */
192 {
193     struct obj *otmp;
194     char buf[BUFSZ];
195     struct permonst *ptr;
196 
197     /* Sanity checks */
198     if (u.usteed) {
199         You("are already riding %s.", mon_nam(u.usteed));
200         return (FALSE);
201     }
202 
203     /* Is the player in the right form? */
204     if (Hallucination && !force) {
205         pline("Maybe you should find a designated driver.");
206         return (FALSE);
207     }
208     /* While riding Wounded_legs refers to the steed's,
209      * not the hero's legs.
210      * That opens up a potential abuse where the player
211      * can mount a steed, then dismount immediately to
212      * heal leg damage, because leg damage is always
213      * healed upon dismount (Wounded_legs context switch).
214      * By preventing a hero with Wounded_legs from
215      * mounting a steed, the potential for abuse is
216      * reduced.  However, dismounting still immediately
217      * heals the steed's wounded legs.  [In 3.4.3 and
218      * earlier, that unintentionally made the hero's
219      * temporary 1 point Dex loss become permanent.]
220      */
221     if (Wounded_legs) {
222         Your("%s are in no shape for riding.", makeplural(body_part(LEG)));
223         if (force && wizard && yn("Heal your legs?") == 'y')
224             HWounded_legs = EWounded_legs = 0L;
225         else
226             return (FALSE);
227     }
228 
229     if (Upolyd && (!humanoid(youmonst.data) || verysmall(youmonst.data)
230                    || bigmonst(youmonst.data) || slithy(youmonst.data))) {
231         You("won't fit on a saddle.");
232         return (FALSE);
233     }
234     if (!force && (near_capacity() > SLT_ENCUMBER)) {
235         You_cant("do that while carrying so much stuff.");
236         return (FALSE);
237     }
238 
239     /* Can the player reach and see the monster? */
240     if (!mtmp || (!force && ((Blind && !Blind_telepat) || mtmp->mundetected
241                              || M_AP_TYPE(mtmp) == M_AP_FURNITURE
242                              || M_AP_TYPE(mtmp) == M_AP_OBJECT))) {
243         pline("I see nobody there.");
244         return (FALSE);
245     }
246     if (mtmp->data == &mons[PM_LONG_WORM]
247         && (u.ux + u.dx != mtmp->mx || u.uy + u.dy != mtmp->my)) {
248         /* As of 3.6.2:  test_move(below) is used to check for trying to mount
249            diagonally into or out of a doorway or through a tight squeeze;
250            attempting to mount a tail segment when hero was not adjacent
251            to worm's head could trigger an impossible() in worm_cross()
252            called from test_move(), so handle not-on-head before that */
253         You("couldn't ride %s, let alone its tail.", a_monnam(mtmp));
254         return FALSE;
255     }
256     if (u.uswallow || u.ustuck || u.utrap || Punished
257         || !test_move(u.ux, u.uy, mtmp->mx - u.ux, mtmp->my - u.uy,
258                       TEST_MOVE)) {
259         if (Punished || !(u.uswallow || u.ustuck || u.utrap))
260             You("are unable to swing your %s over.", body_part(LEG));
261         else
262             You("are stuck here for now.");
263         return (FALSE);
264     }
265 
266     /* Is this a valid monster? */
267     otmp = which_armor(mtmp, W_SADDLE);
268     if (!otmp) {
269         pline("%s is not saddled.", Monnam(mtmp));
270         return (FALSE);
271     }
272     ptr = mtmp->data;
273     if (touch_petrifies(ptr) && !Stone_resistance) {
274         char kbuf[BUFSZ];
275 
276         You("touch %s.", mon_nam(mtmp));
277         Sprintf(kbuf, "attempting to ride %s", an(mtmp->data->mname));
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(defsyms[trap_to_defsym(t->ttyp)].explanation));
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 && !is_floater(ptr) && !is_flyer(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         unweapon = FALSE;
354     u.usteed = mtmp;
355     remove_monster(mtmp->mx, mtmp->my);
356     teleds(mtmp->mx, mtmp->my, TRUE);
357     context.botl = TRUE;
358     return TRUE;
359 }
360 
361 /* You and your steed have moved */
362 void
exercise_steed()363 exercise_steed()
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()378 kick_steed()
379 {
380     char He[4];
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                 pline("%s rouses %sself!", He, mhim(u.usteed));
404         } else
405             pline("%s does not respond.", He);
406         return;
407     }
408 
409     /* Make the steed less tame and check if it resists */
410     if (u.usteed->mtame)
411         u.usteed->mtame--;
412     if (!u.usteed->mtame && u.usteed->mleashed)
413         m_unleash(u.usteed, TRUE);
414     if (!u.usteed->mtame
415         || (u.ulevel + u.usteed->mtame < rnd(MAXULEV / 2 + 5))) {
416         newsym(u.usteed->mx, u.usteed->my);
417         dismount_steed(DISMOUNT_THROWN);
418         return;
419     }
420 
421     pline("%s gallops!", Monnam(u.usteed));
422     u.ugallop += rn1(20, 30);
423     return;
424 }
425 
426 /*
427  * Try to find a dismount point adjacent to the steed's location.
428  * If all else fails, try enexto().  Use enexto() as a last resort because
429  * enexto() chooses its point randomly, possibly even outside the
430  * room's walls, which is not what we want.
431  * Adapted from mail daemon code.
432  */
433 STATIC_OVL boolean
landing_spot(spot,reason,forceit)434 landing_spot(spot, reason, forceit)
435 coord *spot; /* landing position (we fill it in) */
436 int reason;
437 int forceit;
438 {
439     int i = 0, x, y, distance, min_distance = -1;
440     boolean found = FALSE;
441     struct trap *t;
442 
443     /* avoid known traps (i == 0) and boulders, but allow them as a backup */
444     if (reason != DISMOUNT_BYCHOICE || Stunned || Confusion || Fumbling)
445         i = 1;
446     for (; !found && i < 2; ++i) {
447         for (x = u.ux - 1; x <= u.ux + 1; x++)
448             for (y = u.uy - 1; y <= u.uy + 1; y++) {
449                 if (!isok(x, y) || (x == u.ux && y == u.uy))
450                     continue;
451 
452                 if (accessible(x, y) && !MON_AT(x, y)) {
453                     distance = distu(x, y);
454                     if (min_distance < 0 || distance < min_distance
455                         || (distance == min_distance && rn2(2))) {
456                         if (i > 0 || (((t = t_at(x, y)) == 0 || !t->tseen)
457                                       && (!sobj_at(BOULDER, x, y)
458                                           || throws_rocks(youmonst.data)))) {
459                             spot->x = x;
460                             spot->y = y;
461                             min_distance = distance;
462                             found = TRUE;
463                         }
464                     }
465                 }
466             }
467     }
468 
469     /* If we didn't find a good spot and forceit is on, try enexto(). */
470     if (forceit && min_distance < 0
471         && !enexto(spot, u.ux, u.uy, youmonst.data))
472         return FALSE;
473 
474     return found;
475 }
476 
477 /* Stop riding the current steed */
478 void
dismount_steed(reason)479 dismount_steed(reason)
480 int reason; /* Player was thrown off etc. */
481 {
482     struct monst *mtmp;
483     struct obj *otmp;
484     coord cc, steedcc;
485     const char *verb = "fall";
486     boolean repair_leg_damage = (Wounded_legs != 0L);
487     unsigned save_utrap = u.utrap;
488     boolean have_spot = landing_spot(&cc, reason, 0);
489 
490     mtmp = u.usteed; /* make a copy of steed pointer */
491     /* Sanity check */
492     if (!mtmp) /* Just return silently */
493         return;
494 
495     /* Check the reason for dismounting */
496     otmp = which_armor(mtmp, W_SADDLE);
497     switch (reason) {
498     case DISMOUNT_THROWN:
499         verb = "are thrown";
500         /*FALLTHRU*/
501     case DISMOUNT_FELL:
502         You("%s off of %s!", verb, mon_nam(mtmp));
503         if (!have_spot)
504             have_spot = landing_spot(&cc, reason, 1);
505         losehp(Maybe_Half_Phys(rn1(10, 10)), "riding accident", KILLED_BY_AN);
506         set_wounded_legs(BOTH_SIDES, (int) HWounded_legs + rn1(5, 5));
507         repair_leg_damage = FALSE;
508         break;
509     case DISMOUNT_POLY:
510         You("can no longer ride %s.", mon_nam(u.usteed));
511         if (!have_spot)
512             have_spot = landing_spot(&cc, reason, 1);
513         break;
514     case DISMOUNT_ENGULFED:
515         /* caller displays message */
516         break;
517     case DISMOUNT_BONES:
518         /* hero has just died... */
519         break;
520     case DISMOUNT_GENERIC:
521         /* no messages, just make it so */
522         break;
523     case DISMOUNT_BYCHOICE:
524     default:
525         if (otmp && otmp->cursed) {
526             You("can't.  The saddle %s cursed.",
527                 otmp->bknown ? "is" : "seems to be");
528             otmp->bknown = 1; /* ok to skip set_bknown() here */
529             return;
530         }
531         if (!have_spot) {
532             You("can't.  There isn't anywhere for you to stand.");
533             return;
534         }
535         if (!has_mname(mtmp)) {
536             pline("You've been through the dungeon on %s with no name.",
537                   an(mtmp->data->mname));
538             if (Hallucination)
539                 pline("It felt good to get out of the rain.");
540         } else
541             You("dismount %s.", mon_nam(mtmp));
542     }
543     /* While riding, Wounded_legs refers to the steed's legs;
544        after dismounting, it reverts to the hero's legs. */
545     if (repair_leg_damage)
546         heal_legs(1);
547 
548     /* Release the steed and saddle */
549     u.usteed = 0;
550     u.ugallop = 0L;
551     /*
552      * rloc(), rloc_to(), and monkilled()->mondead()->m_detach() all
553      * expect mtmp to be on the map or else have mtmp->mx be 0, but
554      * setting the latter to 0 here would interfere with dropping
555      * the saddle.  Prior to 3.6.2, being off the map didn't matter.
556      *
557      * place_monster() expects mtmp to be alive and not be u.usteed.
558      *
559      * Unfortunately, <u.ux,u.uy> (former steed's implicit location)
560      * might now be occupied by an engulfer, so we can't just put mtmp
561      * at that spot.  An engulfer's previous spot will be unoccupied
562      * but we don't know where that was and even if we did, it might
563      * be hostile terrain.
564      */
565     steedcc.x = u.ux, steedcc.y = u.uy;
566     if (m_at(u.ux, u.uy)) {
567         /* hero's spot has a monster in it; hero must have been plucked
568            from saddle as engulfer moved into his spot--other dismounts
569            shouldn't run into this situation; find nearest viable spot */
570         if (!enexto(&steedcc, u.ux, u.uy, mtmp->data)
571             /* no spot? must have been engulfed by a lurker-above over
572                water or lava; try requesting a location for a flyer */
573             && !enexto(&steedcc, u.ux, u.uy, &mons[PM_BAT]))
574             /* still no spot; last resort is any spot within bounds */
575             (void) enexto(&steedcc, u.ux, u.uy, &mons[PM_GHOST]);
576     }
577     if (!m_at(steedcc.x, steedcc.y)) {
578         if (mtmp->mhp < 1)
579             mtmp->mhp = 0; /* make sure it isn't negative */
580         mtmp->mhp++; /* force at least one hit point, possibly resurrecting */
581         place_monster(mtmp, steedcc.x, steedcc.y);
582         mtmp->mhp--; /* take the extra hit point away: cancel resurrection */
583     } else {
584         impossible("Dismounting: can't place former steed on map.");
585     }
586 
587     if (!DEADMONSTER(mtmp)) {
588         /* if for bones, there's no reason to place the hero;
589            we want to make room for potential ghost, so move steed */
590         if (reason == DISMOUNT_BONES) {
591             /* move the steed to an adjacent square */
592             if (enexto(&cc, u.ux, u.uy, mtmp->data))
593                 rloc_to(mtmp, cc.x, cc.y);
594             else /* evidently no room nearby; move steed elsewhere */
595                 (void) rloc(mtmp, FALSE);
596             return;
597         }
598 
599         /* Set hero's and/or steed's positions.  Try moving the hero first. */
600         if (!u.uswallow && !u.ustuck && have_spot) {
601             struct permonst *mdat = mtmp->data;
602 
603             /* The steed may drop into water/lava */
604             if (!is_flyer(mdat) && !is_floater(mdat) && !is_clinger(mdat)) {
605                 if (is_pool(u.ux, u.uy)) {
606                     if (!Underwater)
607                         pline("%s falls into the %s!", Monnam(mtmp),
608                               surface(u.ux, u.uy));
609                     if (!is_swimmer(mdat) && !amphibious(mdat)) {
610                         killed(mtmp);
611                         adjalign(-1);
612                     }
613                 } else if (is_lava(u.ux, u.uy)) {
614                     pline("%s is pulled into the %s!", Monnam(mtmp),
615                           hliquid("lava"));
616                     if (!likes_lava(mdat)) {
617                         killed(mtmp);
618                         adjalign(-1);
619                     }
620                 }
621             }
622             /* Steed dismounting consists of two steps: being moved to another
623              * square, and descending to the floor.  We have functions to do
624              * each of these activities, but they're normally called
625              * individually and include an attempt to look at or pick up the
626              * objects on the floor:
627              * teleds() --> spoteffects() --> pickup()
628              * float_down() --> pickup()
629              * We use this kludge to make sure there is only one such attempt.
630              *
631              * Clearly this is not the best way to do it.  A full fix would
632              * involve having these functions not call pickup() at all,
633              * instead
634              * calling them first and calling pickup() afterwards.  But it
635              * would take a lot of work to keep this change from having any
636              * unforeseen side effects (for instance, you would no longer be
637              * able to walk onto a square with a hole, and autopickup before
638              * falling into the hole).
639              */
640             /* [ALI] No need to move the player if the steed died. */
641             if (!DEADMONSTER(mtmp)) {
642                 /* Keep steed here, move the player to cc;
643                  * teleds() clears u.utrap
644                  */
645                 in_steed_dismounting = TRUE;
646                 teleds(cc.x, cc.y, TRUE);
647                 in_steed_dismounting = FALSE;
648 
649                 /* Put your steed in your trap */
650                 if (save_utrap)
651                     (void) mintrap(mtmp);
652             }
653 
654         /* Couldn't move hero... try moving the steed. */
655         } else if (enexto(&cc, u.ux, u.uy, mtmp->data)) {
656             /* Keep player here, move the steed to cc */
657             rloc_to(mtmp, cc.x, cc.y);
658             /* Player stays put */
659 
660         /* Otherwise, kill the steed. */
661         } else {
662             if (reason == DISMOUNT_BYCHOICE) {
663                 /* [un]#ride: hero gets credit/blame for killing steed */
664                 killed(mtmp);
665                 adjalign(-1);
666             } else {
667                 /* other dismount: kill former steed with no penalty;
668                    damage type is just "neither AD_DGST nor -AD_RBRE" */
669                 monkilled(mtmp, "", -AD_PHYS);
670             }
671         }
672     } /* !DEADMONST(mtmp) */
673 
674     /* usually return the hero to the surface */
675     if (reason != DISMOUNT_ENGULFED && reason != DISMOUNT_BONES) {
676         in_steed_dismounting = TRUE;
677         (void) float_down(0L, W_SADDLE);
678         in_steed_dismounting = FALSE;
679         context.botl = TRUE;
680         (void) encumber_msg();
681         vision_full_recalc = 1;
682     } else
683         context.botl = TRUE;
684     /* polearms behave differently when not mounted */
685     if (uwep && is_pole(uwep))
686         unweapon = TRUE;
687     return;
688 }
689 
690 /* when attempting to saddle or mount a sleeping steed, try to wake it up
691    (for the saddling case, it won't be u.usteed yet) */
692 STATIC_OVL void
maybewakesteed(steed)693 maybewakesteed(steed)
694 struct monst *steed;
695 {
696     int frozen = (int) steed->mfrozen;
697     boolean wasimmobile = steed->msleeping || !steed->mcanmove;
698 
699     steed->msleeping = 0;
700     if (frozen) {
701         frozen = (frozen + 1) / 2; /* half */
702         /* might break out of timed sleep or paralysis */
703         if (!rn2(frozen)) {
704             steed->mfrozen = 0;
705             steed->mcanmove = 1;
706         } else {
707             /* didn't awake, but remaining duration is halved */
708             steed->mfrozen = frozen;
709         }
710     }
711     if (wasimmobile && !steed->msleeping && steed->mcanmove)
712         pline("%s wakes up.", Monnam(steed));
713     /* regardless of waking, terminate any meal in progress */
714     finish_meating(steed);
715 }
716 
717 /* decide whether hero's steed is able to move;
718    doesn't check for holding traps--those affect the hero directly */
719 boolean
stucksteed(checkfeeding)720 stucksteed(checkfeeding)
721 boolean checkfeeding;
722 {
723     struct monst *steed = u.usteed;
724 
725     if (steed) {
726         /* check whether steed can move */
727         if (steed->msleeping || !steed->mcanmove) {
728             pline("%s won't move!", upstart(y_monnam(steed)));
729             return TRUE;
730         }
731         /* optionally check whether steed is in the midst of a meal */
732         if (checkfeeding && steed->meating) {
733             pline("%s is still eating.", upstart(y_monnam(steed)));
734             return TRUE;
735         }
736     }
737     return FALSE;
738 }
739 
740 void
place_monster(mon,x,y)741 place_monster(mon, x, y)
742 struct monst *mon;
743 int x, y;
744 {
745     struct monst *othermon;
746     const char *monnm, *othnm;
747     char buf[QBUFSZ];
748 
749     buf[0] = '\0';
750     /* normal map bounds are <1..COLNO-1,0..ROWNO-1> but sometimes
751        vault guards (either living or dead) are parked at <0,0> */
752     if (!isok(x, y) && (x != 0 || y != 0 || !mon->isgd)) {
753         describe_level(buf);
754         impossible("trying to place %s at <%d,%d> mstate:%lx on %s",
755                    minimal_monnam(mon, TRUE), x, y, mon->mstate, buf);
756         x = y = 0;
757     }
758     if (mon == u.usteed
759         /* special case is for convoluted vault guard handling */
760         || (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) {
761         describe_level(buf);
762         impossible("placing %s onto map, mstate:%lx, on %s?",
763                    (mon == u.usteed) ? "steed" : "defunct monster",
764                    mon->mstate, buf);
765         return;
766     }
767     if ((othermon = level.monsters[x][y]) != 0) {
768         describe_level(buf);
769         monnm = minimal_monnam(mon, FALSE);
770         othnm = (mon != othermon) ? minimal_monnam(othermon, TRUE) : "itself";
771         impossible("placing %s over %s at <%d,%d>, mstates:%lx %lx on %s?",
772                    monnm, othnm, x, y, othermon->mstate, mon->mstate, buf);
773     }
774     mon->mx = x, mon->my = y;
775     level.monsters[x][y] = mon;
776     mon->mstate &= ~(MON_OFFMAP | MON_MIGRATING | MON_LIMBO | MON_BUBBLEMOVE
777                      | MON_ENDGAME_FREE | MON_ENDGAME_MIGR);
778 }
779 
780 /*steed.c*/
781