make rank() static again
[NetHack.git] / src / steed.c
blob48d537b99a5aea1199c956f3b5074c0a4e866f85
1 /* NetHack 3.7 steed.c $NHDT-Date: 1720128167 2024/07/04 21:22:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.121 $ */
2 /* Copyright (c) Kevin Hugo, 1998-1999. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
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' };
12 staticfn boolean landing_spot(coord *, int, int);
13 staticfn void maybewakesteed(struct monst *);
15 /* caller has decided that hero can't reach something while mounted */
16 void
17 rider_cant_reach(void)
19 You("aren't skilled enough to reach from %s.", y_monnam(u.usteed));
22 /*** Putting the saddle on ***/
24 /* Can this monster wear a saddle? */
25 boolean
26 can_saddle(struct monst *mtmp)
28 struct permonst *ptr = mtmp->data;
30 return (strchr(steeds, ptr->mlet) && (ptr->msize >= MZ_MEDIUM)
31 && (!humanoid(ptr) || ptr->mlet == S_CENTAUR) && !amorphous(ptr)
32 && !noncorporeal(ptr) && !is_whirly(ptr) && !unsolid(ptr));
35 int
36 use_saddle(struct obj *otmp)
38 struct monst *mtmp;
39 struct permonst *ptr;
40 int chance;
42 if (!u_handsy())
43 return ECMD_OK;
45 /* Select an animal */
46 if (u.uswallow || Underwater || !getdir((char *) 0)) {
47 pline1(Never_mind);
48 return ECMD_CANCEL;
50 if (!u.dx && !u.dy) {
51 pline("Saddle yourself? Very funny...");
52 return ECMD_OK;
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 ECMD_TIME;
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 ECMD_TIME;
66 ptr = mtmp->data;
67 if (touch_petrifies(ptr) && !uarmg && !Stone_resistance) {
68 char kbuf[BUFSZ];
70 You("touch %s.", mon_nam(mtmp));
71 if (!(poly_when_stoned(gy.youmonst.data) && polymon(PM_STONE_GOLEM))) {
72 Sprintf(kbuf, "attempting to saddle %s",
73 an(pmname(mtmp->data, Mgender(mtmp))));
74 instapetrify(kbuf);
77 if (ptr == &mons[PM_AMOROUS_DEMON]) {
78 pline("Shame on you!");
79 exercise(A_WIS, FALSE);
80 return ECMD_TIME;
82 if (mtmp->isminion || mtmp->isshk || mtmp->ispriest || mtmp->isgd
83 || mtmp->iswiz) {
84 pline("I think %s would mind.", mon_nam(mtmp));
85 return ECMD_TIME;
87 if (!can_saddle(mtmp)) {
88 You_cant("saddle such a creature.");
89 return ECMD_TIME;
92 /* Calculate your chance */
93 chance = ACURR(A_DEX) + ACURR(A_CHA) / 2 + 2 * mtmp->mtame;
94 chance += u.ulevel * (mtmp->mtame ? 20 : 5);
95 if (!mtmp->mtame)
96 chance -= 10 * mtmp->m_lev;
97 if (Role_if(PM_KNIGHT))
98 chance += 20;
99 switch (P_SKILL(P_RIDING)) {
100 case P_ISRESTRICTED:
101 case P_UNSKILLED:
102 default:
103 chance -= 20;
104 break;
105 case P_BASIC:
106 break;
107 case P_SKILLED:
108 chance += 15;
109 break;
110 case P_EXPERT:
111 chance += 30;
112 break;
114 if (Confusion || Fumbling || Glib)
115 chance -= 20;
116 else if (uarmg && objdescr_is(uarmg, "riding gloves"))
117 /* Bonus for wearing "riding" (but not fumbling) gloves */
118 chance += 10;
119 else if (uarmf && objdescr_is(uarmf, "riding boots"))
120 /* ... or for "riding boots" */
121 chance += 10;
122 if (otmp->cursed)
123 chance -= 50;
125 /* [intended] steed becomes alert if possible */
126 maybewakesteed(mtmp);
128 /* Make the attempt */
129 if (rn2(100) < chance) {
130 You("put the saddle on %s.", mon_nam(mtmp));
131 if (otmp->owornmask)
132 remove_worn_item(otmp, FALSE);
133 freeinv(otmp);
134 /* !can_saddle(mtmp) already eliminated above */
135 put_saddle_on_mon(otmp, mtmp);
136 } else
137 pline("%s resists!", Monnam(mtmp));
138 return ECMD_TIME;
141 void
142 put_saddle_on_mon(struct obj *saddle, struct monst *mtmp)
144 if (!can_saddle(mtmp) || which_armor(mtmp, W_SADDLE)) {
145 if (saddle)
146 impossible("put_saddle_on_mon: saddle obj could get orphaned");
147 return;
149 if (!saddle) {
150 if ((saddle = mksobj(SADDLE, TRUE, FALSE)) != 0) {
151 fully_identify_obj(saddle);
152 /* mpickobj can later override identification if out-of-view */
153 } else {
154 return;
157 if (mpickobj(mtmp, saddle))
158 panic("merged saddle?");
159 mtmp->misc_worn_check |= W_SADDLE;
160 saddle->owornmask = W_SADDLE;
161 saddle->leashmon = mtmp->m_id;
162 update_mon_extrinsics(mtmp, saddle, TRUE, FALSE);
165 /*** Riding the monster ***/
167 /* Can we ride this monster? Caller should also check can_saddle() */
168 boolean
169 can_ride(struct monst *mtmp)
171 return (mtmp->mtame && humanoid(gy.youmonst.data)
172 && !verysmall(gy.youmonst.data) && !bigmonst(gy.youmonst.data)
173 && (!Underwater || is_swimmer(mtmp->data)));
176 /* the #ride command */
178 doride(void)
180 boolean forcemount = FALSE;
182 if (u.usteed) {
183 dismount_steed(DISMOUNT_BYCHOICE);
184 } else if (getdir((char *) 0) && isok(u.ux + u.dx, u.uy + u.dy)) {
185 if (wizard && y_n("Force the mount to succeed?") == 'y')
186 forcemount = TRUE;
187 return (mount_steed(m_at(u.ux + u.dx, u.uy + u.dy), forcemount)
188 ? ECMD_TIME : ECMD_OK);
189 } else {
190 return ECMD_CANCEL;
192 return ECMD_TIME;
195 /* Start riding, with the given monster */
196 boolean
197 mount_steed(
198 struct monst *mtmp, /* The animal */
199 boolean force) /* Quietly force this animal */
201 struct obj *otmp;
202 char buf[BUFSZ];
203 struct permonst *ptr;
205 /* Sanity checks */
206 if (u.usteed) {
207 You("are already riding %s.", mon_nam(u.usteed));
208 return (FALSE);
211 /* Is the player in the right form? */
212 if (Hallucination && !force) {
213 pline("Maybe you should find a designated driver.");
214 return (FALSE);
216 /* While riding Wounded_legs refers to the steed's,
217 * not the hero's legs.
218 * That opens up a potential abuse where the player
219 * can mount a steed, then dismount immediately to
220 * heal leg damage, because leg damage is always
221 * healed upon dismount (Wounded_legs context switch).
222 * By preventing a hero with Wounded_legs from
223 * mounting a steed, the potential for abuse is
224 * reduced. However, dismounting still immediately
225 * heals the steed's wounded legs. [In 3.4.3 and
226 * earlier, that unintentionally made the hero's
227 * temporary 1 point Dex loss become permanent.]
229 if (Wounded_legs) {
230 char qbuf[QBUFSZ];
232 legs_in_no_shape("riding", FALSE);
233 Sprintf(qbuf, "Heal your leg%s?",
234 ((HWounded_legs & BOTH_SIDES) == BOTH_SIDES) ? "s" : "");
235 if (force && wizard && y_n(qbuf) == 'y')
236 heal_legs(0);
237 else
238 return (FALSE);
241 if (Upolyd && (!humanoid(gy.youmonst.data)
242 || verysmall(gy.youmonst.data)
243 || bigmonst(gy.youmonst.data)
244 || slithy(gy.youmonst.data))) {
245 You("won't fit on a saddle.");
246 return (FALSE);
248 if (!force && (near_capacity() > SLT_ENCUMBER)) {
249 You_cant("do that while carrying so much stuff.");
250 return (FALSE);
253 /* Can the player reach and see the monster? */
254 if (!mtmp || (!force && ((Blind && !Blind_telepat) || mtmp->mundetected
255 || M_AP_TYPE(mtmp) == M_AP_FURNITURE
256 || M_AP_TYPE(mtmp) == M_AP_OBJECT))) {
257 pline("I see nobody there.");
258 return (FALSE);
260 if (mtmp->data == &mons[PM_LONG_WORM]
261 && (u.ux + u.dx != mtmp->mx || u.uy + u.dy != mtmp->my)) {
262 /* As of 3.6.2: test_move(below) is used to check for trying to mount
263 diagonally into or out of a doorway or through a tight squeeze;
264 attempting to mount a tail segment when hero was not adjacent
265 to worm's head could trigger an impossible() in worm_cross()
266 called from test_move(), so handle not-on-head before that */
267 You("couldn't ride %s, let alone its tail.", a_monnam(mtmp));
268 return FALSE;
270 if (u.uswallow || u.ustuck || u.utrap || Punished
271 || !test_move(u.ux, u.uy, mtmp->mx - u.ux, mtmp->my - u.uy,
272 TEST_MOVE)) {
273 if (Punished || !(u.uswallow || u.ustuck || u.utrap))
274 You("are unable to swing your %s over.", body_part(LEG));
275 else
276 You("are stuck here for now.");
277 return (FALSE);
280 /* Is this a valid monster? */
281 otmp = which_armor(mtmp, W_SADDLE);
282 if (!otmp) {
283 pline("%s is not saddled.", Monnam(mtmp));
284 return (FALSE);
287 ptr = mtmp->data;
288 if (touch_petrifies(ptr) && !Stone_resistance) {
289 char kbuf[BUFSZ];
291 You("touch %s.", mon_nam(mtmp));
292 Sprintf(kbuf, "attempting to ride %s",
293 an(pmname(mtmp->data, Mgender(mtmp))));
294 instapetrify(kbuf);
296 if (!mtmp->mtame || mtmp->isminion) {
297 pline("I think %s would mind.", mon_nam(mtmp));
298 return (FALSE);
300 if (mtmp->mtrapped) {
301 struct trap *t = t_at(mtmp->mx, mtmp->my);
303 You_cant("mount %s while %s's trapped in %s.", mon_nam(mtmp),
304 mhe(mtmp), an(trapname(t->ttyp, FALSE)));
305 return (FALSE);
308 if (!force && !Role_if(PM_KNIGHT) && !(--mtmp->mtame)) {
309 /* no longer tame */
310 newsym(mtmp->mx, mtmp->my);
311 pline("%s resists%s!", Monnam(mtmp),
312 mtmp->mleashed ? " and its leash comes off" : "");
313 if (mtmp->mleashed)
314 m_unleash(mtmp, FALSE);
315 return (FALSE);
317 if (!force && Underwater && !is_swimmer(ptr)) {
318 You_cant("ride that creature while under %s.",
319 hliquid("water"));
320 return (FALSE);
322 if (!can_saddle(mtmp) || !can_ride(mtmp)) {
323 You_cant("ride such a creature.");
324 return FALSE;
327 /* Is the player impaired? */
328 if (!force && !is_floater(ptr) && !is_flyer(ptr) && Levitation
329 && !Lev_at_will) {
330 You("cannot reach %s.", mon_nam(mtmp));
331 return (FALSE);
333 if (!force && uarm && is_metallic(uarm) && greatest_erosion(uarm)) {
334 Your("%s armor is too stiff to be able to mount %s.",
335 uarm->oeroded ? "rusty" : "corroded", mon_nam(mtmp));
336 return (FALSE);
338 if (!force
339 && (Confusion || Fumbling || Glib || Wounded_legs || otmp->cursed
340 || otmp->greased
341 || (u.ulevel + mtmp->mtame < rnd(MAXULEV / 2 + 5)))) {
342 if (Levitation) {
343 pline("%s slips away from you.", Monnam(mtmp));
344 return FALSE;
346 You("slip while trying to get on %s.", mon_nam(mtmp));
348 Sprintf(buf, "slipped while mounting %s",
349 /* "a saddled mumak" or "a saddled pony called Dobbin" */
350 x_monnam(mtmp, ARTICLE_A, (char *) 0,
351 SUPPRESS_IT | SUPPRESS_INVISIBLE
352 | SUPPRESS_HALLUCINATION,
353 TRUE));
354 losehp(Maybe_Half_Phys(rn1(5, 10)), buf, NO_KILLER_PREFIX);
355 return (FALSE);
358 /* Success */
359 maybewakesteed(mtmp);
360 if (!force) {
361 if (Levitation && !is_floater(ptr) && !is_flyer(ptr))
362 /* Must have Lev_at_will at this point */
363 pline("%s magically floats up!", Monnam(mtmp));
364 You("mount %s.", mon_nam(mtmp));
365 if (Flying)
366 You("and %s take flight together.", mon_nam(mtmp));
368 /* setuwep handles polearms differently when you're mounted */
369 if (uwep && is_pole(uwep))
370 gu.unweapon = FALSE;
371 u.usteed = mtmp;
373 boolean was_stealthy = Stealth != 0;
375 steed_vs_stealth();
376 if (was_stealthy && !Stealth)
377 You("aren't stealthy anymore.");
379 remove_monster(mtmp->mx, mtmp->my);
380 teleds(mtmp->mx, mtmp->my, TELEDS_ALLOW_DRAG);
381 disp.botl = TRUE;
382 return TRUE;
385 /* You and your steed have moved */
386 void
387 exercise_steed(void)
389 if (!u.usteed)
390 return;
392 /* It takes many turns of riding to exercise skill */
393 if (++u.urideturns >= 100) {
394 u.urideturns = 0;
395 use_skill(P_RIDING, 1);
397 return;
400 /* The player kicks or whips the steed */
401 void
402 kick_steed(void)
404 char He[BUFSZ]; /* monverbself() appends to the "He"/"She"/"It" value */
405 if (!u.usteed)
406 return;
408 /* [ALI] Various effects of kicking sleeping/paralyzed steeds */
409 if (helpless(u.usteed)) {
410 /* We assume a message has just been output of the form
411 * "You kick <steed>."
413 Strcpy(He, mhe(u.usteed));
414 *He = highc(*He);
415 if ((u.usteed->mcanmove || u.usteed->mfrozen) && !rn2(2)) {
416 if (u.usteed->mcanmove)
417 u.usteed->msleeping = 0;
418 else if (u.usteed->mfrozen > 2)
419 u.usteed->mfrozen -= 2;
420 else {
421 u.usteed->mfrozen = 0;
422 u.usteed->mcanmove = 1;
424 if (helpless(u.usteed))
425 pline("%s stirs.", He);
426 else
427 /* if hallucinating, might yield "He rouses herself" or
428 "She rouses himself" */
429 pline("%s!", monverbself(u.usteed, He, "rouse", (char *) 0));
430 } else
431 pline("%s does not respond.", He);
432 return;
435 /* Make the steed less tame and check if it resists */
436 if (u.usteed->mtame)
437 u.usteed->mtame--;
438 if (!u.usteed->mtame && u.usteed->mleashed)
439 m_unleash(u.usteed, TRUE);
440 if (!u.usteed->mtame
441 || (u.ulevel + u.usteed->mtame < rnd(MAXULEV / 2 + 5))) {
442 newsym(u.usteed->mx, u.usteed->my);
443 dismount_steed(DISMOUNT_THROWN);
444 return;
447 pline("%s gallops!", Monnam(u.usteed));
448 u.ugallop += rn1(20, 30);
449 return;
453 * Try to find a dismount point adjacent to the steed's location.
454 * If all else fails, try enexto(). Use enexto() as a last resort because
455 * enexto() chooses its point randomly, possibly even outside the
456 * room's walls, which is not what we want.
457 * Adapted from mail daemon code.
459 staticfn boolean
460 landing_spot(
461 coord *spot, /* landing position (we fill it in) */
462 int reason,
463 int forceit)
465 coord cc, try[8]; /* 8: the 8 spots adjacent to the hero's spot */
466 int i, j, best_j, clockwise_j, counterclk_j,
467 n, viable, distance, min_distance = -1;
468 coordxy x, y;
469 boolean found, impaird, kn_trap, boulder;
470 struct trap *t;
472 (void) memset((genericptr_t) try, 0, sizeof try);
473 n = 0;
474 j = xytod(u.dx, u.dy);
475 if (reason == DISMOUNT_KNOCKED && j != DIR_ERR) {
476 /* we'll check preferred location first; if viable it'll be picked */
477 best_j = j;
478 try[0].x = u.dx, try[0].y = u.dy;
479 /* the two next best locations are checked second and third */
480 i = rn2(2);
481 clockwise_j = DIR_RIGHT(j); /* (j + 1) % 8 */
482 dtoxy(&cc, clockwise_j);
483 try[1 + i].x = cc.x, try[1 + i].y = cc.y; /* [1] or [2] */
484 counterclk_j = DIR_LEFT(j); /* (j + 8 - 1) % 8 */
485 dtoxy(&cc, counterclk_j);
486 try[2 - i].x = cc.x, try[2 - i].y = cc.y; /* [2] or [1] */
487 n = 3;
488 debugpline3("knock from saddle: best %s, next %s or %s",
489 directionname(best_j),
490 directionname(clockwise_j), directionname(counterclk_j));
491 } else {
492 best_j = clockwise_j = counterclk_j = -1;
494 for (j = 0; j < N_DIRS; ++j) {
495 /* fortunately NODIAG() handling isn't needed for DISMOUNT_KNOCKED
496 because hero can only ride when humanoid */
497 if (j == best_j || j == clockwise_j || j == counterclk_j)
498 continue;
499 /* j==0 is W, j==1 NW, j==2 N, j==3 NE, ..., around to j==7 SW;
500 so odd j values are diagonal directions here */
501 if (reason == DISMOUNT_POLY && NODIAG(u.umonnum) && (j % 1) != 0)
502 continue;
503 dtoxy(&cc, j);
504 try[n++] = cc;
508 * Up to three passes;
509 * i==0: voluntary dismount without impairment avoids known traps and
510 * boulders;
511 * i==1: voluntary dismount with impairment or knocked out of saddle
512 * avoids boulders but allows known traps;
513 * i==2: other, allow traps and boulders.
515 * Fallback to i==1 if nothing appropriate was found for i==0 and
516 * to i==2 as last resort.
518 impaird = (Stunned || Confusion || Fumbling);
519 viable = 0;
520 found = FALSE;
521 for (i = (reason == DISMOUNT_BYCHOICE && !impaird) ? 0
522 : ((reason == DISMOUNT_BYCHOICE && impaird)
523 || reason == DISMOUNT_KNOCKED) ? 1
524 : 2;
525 i <= 2 && !found; ++i) {
526 for (j = 0; j < n; ++j) {
527 x = u.ux + try[j].x;
528 y = u.uy + try[j].y;
529 if (!isok(x, y) || u_at(x, y)) /* [note: u_at() can't happen] */
530 continue;
532 if (accessible(x, y) && !MON_AT(x, y)
533 && test_move(u.ux, u.uy, x - u.ux, y - u.uy, TEST_MOVE)) {
534 ++viable;
535 distance = distu(x, y);
536 if (min_distance < 0 /* no viable candidate yet */
537 /* or better than pending candidate (note: orthogonal
538 spots are distance 1 and diagonal ones distance 2;
539 treating one as better than the other is arbitrary
540 and not wanted for DISMOUNT_KNOCKED) */
541 || ((best_j == -1) ? (distance < min_distance) : (j < 3))
542 /* or equally good, maybe substitute this one */
543 || (distance == min_distance && !rn2(viable))) {
544 /* traps avoided on pass 0; boulders avoided on 0 and 1 */
545 kn_trap = i == 0 && ((t = t_at(x, y)) != 0 && t->tseen
546 && t->ttyp != VIBRATING_SQUARE);
547 boulder = i <= 1 && (sobj_at(BOULDER, x, y)
548 && !throws_rocks(gy.youmonst.data));
549 if (!kn_trap && !boulder) {
550 spot->x = x;
551 spot->y = y;
552 min_distance = distance;
553 found = TRUE;
554 if (best_j != -1 && j < 3)
555 /* since best_j is first candidate (j==0), j==1
556 and j==2 can only get here when best_j was
557 not viable; 50:50 chance for clockwise_j to
558 come before counterclk_j so each has same
559 chance to be next after best_j */
560 break;
567 /* If we didn't find a good spot and forceit is on, try enexto(). */
568 if (forceit && !found)
569 found = enexto(spot, u.ux, u.uy, gy.youmonst.data);
571 return found;
574 /* Stop riding the current steed */
575 void
576 dismount_steed(
577 int reason) /* Player was thrown off etc. */
579 struct monst *mtmp;
580 struct obj *otmp;
581 const char *verb;
582 coord cc, steedcc;
583 unsigned save_utrap = u.utrap;
584 boolean ulev, ufly,
585 repair_leg_damage = (Wounded_legs != 0L),
586 have_spot = landing_spot(&cc, reason, 0);
588 mtmp = u.usteed; /* make a copy of steed pointer */
589 /* Sanity check */
590 if (!mtmp) /* Just return silently */
591 return;
592 u.usteed = 0; /* affects Fly test; could hypothetically affect Lev;
593 * also affects u_locomotion() */
594 ufly = Flying ? TRUE : FALSE;
595 ulev = Levitation ? TRUE : FALSE;
596 verb = u_locomotion("fall"); /* only used for _FELL and _KNOCKED */
597 u.usteed = mtmp;
599 /* Check the reason for dismounting */
600 otmp = which_armor(mtmp, W_SADDLE);
601 switch (reason) {
602 case DISMOUNT_THROWN:
603 verb = "are thrown";
604 FALLTHROUGH;
605 /*FALLTHRU*/
606 case DISMOUNT_KNOCKED:
607 case DISMOUNT_FELL:
608 You("%s off of %s!", verb, mon_nam(mtmp));
609 if (!have_spot)
610 have_spot = landing_spot(&cc, reason, 1);
611 if (!ulev && !ufly) {
612 losehp(Maybe_Half_Phys(rn1(10, 10)), "riding accident",
613 KILLED_BY_AN);
614 set_wounded_legs(BOTH_SIDES, (int) HWounded_legs + rn1(5, 5));
615 repair_leg_damage = FALSE;
617 break;
618 case DISMOUNT_POLY:
619 You("can no longer ride %s.", mon_nam(u.usteed));
620 if (!have_spot)
621 have_spot = landing_spot(&cc, reason, 1);
622 break;
623 case DISMOUNT_ENGULFED:
624 /* caller displays message */
625 break;
626 case DISMOUNT_BONES:
627 /* hero has just died... */
628 break;
629 case DISMOUNT_GENERIC:
630 /* no messages, just make it so */
631 break;
632 case DISMOUNT_BYCHOICE:
633 default:
634 if (otmp && otmp->cursed) {
635 You("can't. The saddle %s cursed.",
636 otmp->bknown ? "is" : "seems to be");
637 otmp->bknown = 1; /* ok to skip set_bknown() here */
638 return;
640 if (!have_spot) {
641 You("can't. There isn't anywhere for you to stand.");
642 return;
644 if (!has_mgivenname(mtmp)) {
645 pline("You've been through the dungeon on %s with no name.",
646 an(pmname(mtmp->data, Mgender(mtmp))));
647 if (Hallucination)
648 pline("It felt good to get out of the rain.");
649 } else
650 You("dismount %s.", mon_nam(mtmp));
652 /* While riding, Wounded_legs refers to the steed's legs;
653 after dismounting, it reverts to the hero's legs. */
654 if (repair_leg_damage)
655 heal_legs(1);
657 /* Release the steed */
658 u.usteed = (struct monst *) NULL;
659 u.ugallop = 0L;
661 boolean was_stealthy = Stealth != 0;
663 steed_vs_stealth();
664 if (Stealth && !was_stealthy)
665 You("seem less noisy now.");
668 if (u.utraptype == TT_BEARTRAP
669 || u.utraptype == TT_PIT
670 || u.utraptype == TT_WEB) {
671 mtmp->mtrapped = 1;
675 * rloc(), rloc_to(), and monkilled()->mondead()->m_detach() all
676 * expect mtmp to be on the map or else have mtmp->mx be 0, but
677 * setting the latter to 0 here would interfere with dropping
678 * the saddle. Prior to 3.6.2, being off the map didn't matter.
680 * place_monster() expects mtmp to be alive and not be u.usteed.
682 * Unfortunately, <u.ux,u.uy> (former steed's implicit location)
683 * might now be occupied by an engulfer, so we can't just put mtmp
684 * at that spot. An engulfer's previous spot will be unoccupied
685 * but we don't know where that was and even if we did, it might
686 * be hostile terrain.
688 steedcc.x = u.ux, steedcc.y = u.uy;
689 if (m_at(u.ux, u.uy)) {
690 /* hero's spot has a monster in it; hero must have been plucked
691 from saddle as engulfer moved into his spot--other dismounts
692 shouldn't run into this situation; find nearest viable spot */
693 if (!enexto(&steedcc, u.ux, u.uy, mtmp->data)
694 /* no spot? must have been engulfed by a lurker-above over
695 water or lava; try requesting a location for a flyer */
696 && !enexto(&steedcc, u.ux, u.uy, &mons[PM_BAT]))
697 /* still no spot; last resort is any spot within bounds */
698 (void) enexto(&steedcc, u.ux, u.uy, &mons[PM_GHOST]);
701 if (!DEADMONSTER(mtmp)) {
702 gi.in_steed_dismounting++;
703 place_monster(mtmp, steedcc.x, steedcc.y);
704 gi.in_steed_dismounting--;
706 /* if for bones, there's no reason to place the hero;
707 we want to make room for potential ghost, so move steed */
708 if (reason == DISMOUNT_BONES) {
709 /* move the steed to an adjacent square */
710 if (enexto(&cc, u.ux, u.uy, mtmp->data))
711 rloc_to(mtmp, cc.x, cc.y);
712 else /* evidently no room nearby; move steed elsewhere */
713 (void) rloc(mtmp, RLOC_ERR | RLOC_NOMSG);
714 return;
717 /* Set hero's and/or steed's positions. Usually try moving the
718 hero first. Note: for DISMOUNT_ENGULFED, caller hasn't set
719 u.uswallow yet but has set u.ustuck. */
720 if (!u.uswallow && !u.ustuck && have_spot) {
721 struct permonst *mdat = mtmp->data;
723 /* The steed may drop into water/lava */
724 if (grounded(mdat)) {
725 if (is_pool(u.ux, u.uy)) {
726 if (!Underwater)
727 pline("%s falls into the %s!", Monnam(mtmp),
728 surface(u.ux, u.uy));
729 if (!cant_drown(mdat)) {
730 killed(mtmp);
731 adjalign(-1);
733 } else if (is_lava(u.ux, u.uy)) {
734 pline("%s is pulled into the %s!", Monnam(mtmp),
735 hliquid("lava"));
736 if (!likes_lava(mdat)) {
737 killed(mtmp);
738 adjalign(-1);
742 /* Steed dismounting consists of two steps: being moved to another
743 * square, and descending to the floor. We have functions to do
744 * each of these activities, but they're normally called
745 * individually and include an attempt to look at or pick up the
746 * objects on the floor:
747 * teleds() --> spoteffects() --> pickup()
748 * float_down() --> pickup()
749 * We use this kludge to make sure there is only one such attempt.
751 * Clearly this is not the best way to do it. A full fix would
752 * involve having these functions not call pickup() at all,
753 * instead calling them first and calling pickup() afterwards.
754 * But it would take a lot of work to keep this change from
755 * having any unforeseen side effects (for instance, you would
756 * no longer be able to walk onto a square with a hole, and
757 * autopickup before falling into the hole).
759 /* [ALI] No need to move the player if the steed died. */
760 if (!DEADMONSTER(mtmp)) {
761 /* Keep steed here, move the player to cc;
762 * teleds() clears u.utrap
764 gi.in_steed_dismounting = TRUE;
765 teleds(cc.x, cc.y, TELEDS_ALLOW_DRAG);
766 if (sobj_at(BOULDER, cc.x, cc.y))
767 sokoban_guilt();
768 gi.in_steed_dismounting = FALSE;
770 /* Put your steed in your trap */
771 if (save_utrap)
772 (void) mintrap(mtmp, NO_TRAP_FLAGS);
775 /* Couldn't move hero... try moving the steed. */
776 } else if (enexto(&cc, u.ux, u.uy, mtmp->data)) {
777 /* Keep player here, move the steed to cc */
778 rloc_to(mtmp, cc.x, cc.y);
779 /* Player stays put */
781 /* Otherwise, steed goes bye-bye. */
782 } else {
783 #if 1 /* original there's-no-room handling */
784 if (reason == DISMOUNT_BYCHOICE) {
785 /* [un]#ride: hero gets credit/blame for killing steed */
786 killed(mtmp);
787 adjalign(-1);
788 } else {
789 /* other dismount: kill former steed with no penalty;
790 damage type is just "neither AD_DGST nor -AD_RBRE" */
791 monkilled(mtmp, "", -AD_PHYS);
793 #else
794 /* Can't use this [yet?] because it violates monmove()'s
795 * assumption that a moving monster (engulfer) can't cause
796 * another monster (steed) to be removed from the fmon list.
797 * That other monster (steed) might be cached as the next one
798 * to move.
800 /* migrate back to this level if hero leaves and returns
801 or to next level if it is happening in the endgame */
802 mdrop_special_objs(mtmp);
803 deal_with_overcrowding(mtmp);
804 #endif
806 } /* !DEADMONST(mtmp) */
808 /* usually return the hero to the surface */
809 if (reason != DISMOUNT_ENGULFED && reason != DISMOUNT_BONES) {
810 gi.in_steed_dismounting = TRUE;
811 (void) float_down(0L, W_SADDLE);
812 gi.in_steed_dismounting = FALSE;
813 disp.botl = TRUE;
814 (void) encumber_msg();
815 gv.vision_full_recalc = 1;
816 } else
817 disp.botl = TRUE;
818 /* polearms behave differently when not mounted */
819 if (uwep && is_pole(uwep))
820 gu.unweapon = TRUE;
821 return;
824 /* when attempting to saddle or mount a sleeping steed, try to wake it up
825 (for the saddling case, it won't be u.usteed yet) */
826 staticfn void
827 maybewakesteed(struct monst *steed)
829 int frozen = (int) steed->mfrozen;
830 boolean wasimmobile = helpless(steed);
832 steed->msleeping = 0;
833 if (frozen) {
834 frozen = (frozen + 1) / 2; /* half */
835 /* might break out of timed sleep or paralysis */
836 if (!rn2(frozen)) {
837 steed->mfrozen = 0;
838 steed->mcanmove = 1;
839 } else {
840 /* didn't awake, but remaining duration is halved */
841 steed->mfrozen = frozen;
844 if (wasimmobile && !helpless(steed))
845 pline("%s wakes up.", Monnam(steed));
846 /* regardless of waking, terminate any meal in progress */
847 finish_meating(steed);
850 /* steed has taken on a new shape */
851 void
852 poly_steed(
853 struct monst *steed,
854 struct permonst *oldshape)
856 if (!can_saddle(steed) || !can_ride(steed)) {
857 /* can't get here; newcham() -> mon_break_armor() -> m_lose_armor()
858 removes saddle and/or forces hero to dismount, if applicable,
859 before newcham() calls us */
860 dismount_steed(DISMOUNT_FELL);
861 } else {
862 char buf[BUFSZ];
864 Strcpy(buf, x_monnam(steed, ARTICLE_YOUR, (char *) 0,
865 SUPPRESS_SADDLE, FALSE));
866 if (oldshape != steed->data)
867 (void) strsubst(buf, "your ", "your new ");
868 You("adjust yourself in the saddle on %s.", buf);
870 /* riding blocks stealth unless hero+steed fly */
871 steed_vs_stealth();
875 /* decide whether hero's steed is able to move;
876 doesn't check for holding traps--those affect the hero directly */
877 boolean
878 stucksteed(boolean checkfeeding)
880 struct monst *steed = u.usteed;
882 if (steed) {
883 /* check whether steed can move */
884 if (helpless(steed)) {
885 pline("%s won't move!", YMonnam(steed));
886 return TRUE;
888 /* optionally check whether steed is in the midst of a meal */
889 if (checkfeeding && steed->meating) {
890 pline("%s is still eating.", YMonnam(steed));
891 return TRUE;
894 return FALSE;
897 void
898 place_monster(struct monst *mon, coordxy x, coordxy y)
900 struct monst *othermon;
901 const char *monnm, *othnm;
902 char buf[QBUFSZ];
904 buf[0] = '\0';
905 /* normal map bounds are <1..COLNO-1,0..ROWNO-1> but sometimes
906 vault guards (either living or dead) are parked at <0,0> */
907 if (!isok(x, y) && (x != 0 || y != 0 || !mon->isgd)) {
908 describe_level(buf, 0);
909 impossible("trying to place %s at <%d,%d> mstate:%lx on %s",
910 minimal_monnam(mon, TRUE), x, y, mon->mstate, buf);
911 x = y = 0;
913 if ((mon == u.usteed && !gi.in_steed_dismounting)
914 /* special case is for convoluted vault guard handling */
915 || (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) {
916 describe_level(buf, 0);
917 impossible("placing %s onto map, mstate:%lx, on %s?",
918 (mon == u.usteed) ? "steed" : "defunct monster",
919 mon->mstate, buf);
920 return;
922 if ((othermon = svl.level.monsters[x][y]) != 0) {
923 describe_level(buf, 0);
924 monnm = minimal_monnam(mon, FALSE);
925 othnm = (mon != othermon) ? minimal_monnam(othermon, TRUE) : "itself";
926 impossible("placing %s over %s at <%d,%d>, mstates:%lx %lx on %s?",
927 monnm, othnm, x, y, othermon->mstate, mon->mstate, buf);
929 mon->mx = x, mon->my = y;
930 svl.level.monsters[x][y] = mon;
931 mon->mstate = MON_FLOOR;
934 /*steed.c*/