dumplog message history groundwork
[aNetHack.git] / src / steed.c
blobf5cd50bc5bd264ece0459b4a38988d23259c9d6e
1 /* NetHack 3.6 steed.c $NHDT-Date: 1445906867 2015/10/27 00:47:47 $ $NHDT-Branch: master $:$NHDT-Revision: 1.47 $ */
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 STATIC_DCL boolean FDECL(landing_spot, (coord *, int, int));
13 STATIC_DCL void FDECL(maybewakesteed, (struct monst *));
15 /* caller has decided that hero can't reach something while mounted */
16 void
17 rider_cant_reach()
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(mtmp)
27 struct monst *mtmp;
29 struct permonst *ptr = mtmp->data;
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));
36 int
37 use_saddle(otmp)
38 struct obj *otmp;
40 struct monst *mtmp;
41 struct permonst *ptr;
42 int chance;
43 const char *s;
45 if (!u_handsy())
46 return 0;
48 /* Select an animal */
49 if (u.uswallow || Underwater || !getdir((char *) 0)) {
50 pline1(Never_mind);
51 return 0;
53 if (!u.dx && !u.dy) {
54 pline("Saddle yourself? Very funny...");
55 return 0;
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;
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;
68 ptr = mtmp->data;
69 if (touch_petrifies(ptr) && !uarmg && !Stone_resistance) {
70 char kbuf[BUFSZ];
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);
78 if (ptr == &mons[PM_INCUBUS] || ptr == &mons[PM_SUCCUBUS]) {
79 pline("Shame on you!");
80 exercise(A_WIS, FALSE);
81 return 1;
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;
88 if (!can_saddle(mtmp)) {
89 You_cant("saddle such a creature.");
90 return 1;
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;
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;
128 /* [intended] steed becomes alert if possible */
129 maybewakesteed(mtmp);
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;
143 void
144 put_saddle_on_mon(saddle, mtmp)
145 struct obj *saddle;
146 struct monst *mtmp;
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);
158 /*** Riding the monster ***/
160 /* Can we ride this monster? Caller should also check can_saddle() */
161 boolean
162 can_ride(mtmp)
163 struct monst *mtmp;
165 return (mtmp->mtame && humanoid(youmonst.data)
166 && !verysmall(youmonst.data) && !bigmonst(youmonst.data)
167 && (!Underwater || is_swimmer(mtmp->data)));
171 doride()
173 boolean forcemount = FALSE;
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;
184 return 1;
187 /* Start riding, with the given monster */
188 boolean
189 mount_steed(mtmp, force)
190 struct monst *mtmp; /* The animal */
191 boolean force; /* Quietly force this animal */
193 struct obj *otmp;
194 char buf[BUFSZ];
195 struct permonst *ptr;
197 /* Sanity checks */
198 if (u.usteed) {
199 You("are already riding %s.", mon_nam(u.usteed));
200 return (FALSE);
203 /* Is the player in the right form? */
204 if (Hallucination && !force) {
205 pline("Maybe you should find a designated driver.");
206 return (FALSE);
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.]
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 = 0;
225 else
226 return (FALSE);
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);
234 if (!force && (near_capacity() > SLT_ENCUMBER)) {
235 You_cant("do that while carrying so much stuff.");
236 return (FALSE);
239 /* Can the player reach and see the monster? */
240 if (!mtmp || (!force && ((Blind && !Blind_telepat) || mtmp->mundetected
241 || mtmp->m_ap_type == M_AP_FURNITURE
242 || mtmp->m_ap_type == M_AP_OBJECT))) {
243 pline("I see nobody there.");
244 return (FALSE);
246 if (u.uswallow || u.ustuck || u.utrap || Punished
247 || !test_move(u.ux, u.uy, mtmp->mx - u.ux, mtmp->my - u.uy,
248 TEST_MOVE)) {
249 if (Punished || !(u.uswallow || u.ustuck || u.utrap))
250 You("are unable to swing your %s over.", body_part(LEG));
251 else
252 You("are stuck here for now.");
253 return (FALSE);
256 /* Is this a valid monster? */
257 otmp = which_armor(mtmp, W_SADDLE);
258 if (!otmp) {
259 pline("%s is not saddled.", Monnam(mtmp));
260 return (FALSE);
262 ptr = mtmp->data;
263 if (touch_petrifies(ptr) && !Stone_resistance) {
264 char kbuf[BUFSZ];
266 You("touch %s.", mon_nam(mtmp));
267 Sprintf(kbuf, "attempting to ride %s", an(mtmp->data->mname));
268 instapetrify(kbuf);
270 if (!mtmp->mtame || mtmp->isminion) {
271 pline("I think %s would mind.", mon_nam(mtmp));
272 return (FALSE);
274 if (mtmp->mtrapped) {
275 struct trap *t = t_at(mtmp->mx, mtmp->my);
277 You_cant("mount %s while %s's trapped in %s.", mon_nam(mtmp),
278 mhe(mtmp), an(defsyms[trap_to_defsym(t->ttyp)].explanation));
279 return (FALSE);
282 if (!force && !Role_if(PM_KNIGHT) && !(--mtmp->mtame)) {
283 /* no longer tame */
284 newsym(mtmp->mx, mtmp->my);
285 pline("%s resists%s!", Monnam(mtmp),
286 mtmp->mleashed ? " and its leash comes off" : "");
287 if (mtmp->mleashed)
288 m_unleash(mtmp, FALSE);
289 return (FALSE);
291 if (!force && Underwater && !is_swimmer(ptr)) {
292 You_cant("ride that creature while under %s.",
293 hliquid("water"));
294 return (FALSE);
296 if (!can_saddle(mtmp) || !can_ride(mtmp)) {
297 You_cant("ride such a creature.");
298 return FALSE;
301 /* Is the player impaired? */
302 if (!force && !is_floater(ptr) && !is_flyer(ptr) && Levitation
303 && !Lev_at_will) {
304 You("cannot reach %s.", mon_nam(mtmp));
305 return (FALSE);
307 if (!force && uarm && is_metallic(uarm) && greatest_erosion(uarm)) {
308 Your("%s armor is too stiff to be able to mount %s.",
309 uarm->oeroded ? "rusty" : "corroded", mon_nam(mtmp));
310 return (FALSE);
312 if (!force
313 && (Confusion || Fumbling || Glib || Wounded_legs || otmp->cursed
314 || (u.ulevel + mtmp->mtame < rnd(MAXULEV / 2 + 5)))) {
315 if (Levitation) {
316 pline("%s slips away from you.", Monnam(mtmp));
317 return FALSE;
319 You("slip while trying to get on %s.", mon_nam(mtmp));
321 Sprintf(buf, "slipped while mounting %s",
322 /* "a saddled mumak" or "a saddled pony called Dobbin" */
323 x_monnam(mtmp, ARTICLE_A, (char *) 0,
324 SUPPRESS_IT | SUPPRESS_INVISIBLE
325 | SUPPRESS_HALLUCINATION,
326 TRUE));
327 losehp(Maybe_Half_Phys(rn1(5, 10)), buf, NO_KILLER_PREFIX);
328 return (FALSE);
331 /* Success */
332 maybewakesteed(mtmp);
333 if (!force) {
334 if (Levitation && !is_floater(ptr) && !is_flyer(ptr))
335 /* Must have Lev_at_will at this point */
336 pline("%s magically floats up!", Monnam(mtmp));
337 You("mount %s.", mon_nam(mtmp));
339 /* setuwep handles polearms differently when you're mounted */
340 if (uwep && is_pole(uwep))
341 unweapon = FALSE;
342 u.usteed = mtmp;
343 remove_monster(mtmp->mx, mtmp->my);
344 teleds(mtmp->mx, mtmp->my, TRUE);
345 context.botl = TRUE;
346 return TRUE;
349 /* You and your steed have moved */
350 void
351 exercise_steed()
353 if (!u.usteed)
354 return;
356 /* It takes many turns of riding to exercise skill */
357 if (u.urideturns++ >= 100) {
358 u.urideturns = 0;
359 use_skill(P_RIDING, 1);
361 return;
364 /* The player kicks or whips the steed */
365 void
366 kick_steed()
368 char He[4];
369 if (!u.usteed)
370 return;
372 /* [ALI] Various effects of kicking sleeping/paralyzed steeds */
373 if (u.usteed->msleeping || !u.usteed->mcanmove) {
374 /* We assume a message has just been output of the form
375 * "You kick <steed>."
377 Strcpy(He, mhe(u.usteed));
378 *He = highc(*He);
379 if ((u.usteed->mcanmove || u.usteed->mfrozen) && !rn2(2)) {
380 if (u.usteed->mcanmove)
381 u.usteed->msleeping = 0;
382 else if (u.usteed->mfrozen > 2)
383 u.usteed->mfrozen -= 2;
384 else {
385 u.usteed->mfrozen = 0;
386 u.usteed->mcanmove = 1;
388 if (u.usteed->msleeping || !u.usteed->mcanmove)
389 pline("%s stirs.", He);
390 else
391 pline("%s rouses %sself!", He, mhim(u.usteed));
392 } else
393 pline("%s does not respond.", He);
394 return;
397 /* Make the steed less tame and check if it resists */
398 if (u.usteed->mtame)
399 u.usteed->mtame--;
400 if (!u.usteed->mtame && u.usteed->mleashed)
401 m_unleash(u.usteed, TRUE);
402 if (!u.usteed->mtame
403 || (u.ulevel + u.usteed->mtame < rnd(MAXULEV / 2 + 5))) {
404 newsym(u.usteed->mx, u.usteed->my);
405 dismount_steed(DISMOUNT_THROWN);
406 return;
409 pline("%s gallops!", Monnam(u.usteed));
410 u.ugallop += rn1(20, 30);
411 return;
415 * Try to find a dismount point adjacent to the steed's location.
416 * If all else fails, try enexto(). Use enexto() as a last resort because
417 * enexto() chooses its point randomly, possibly even outside the
418 * room's walls, which is not what we want.
419 * Adapted from mail daemon code.
421 STATIC_OVL boolean
422 landing_spot(spot, reason, forceit)
423 coord *spot; /* landing position (we fill it in) */
424 int reason;
425 int forceit;
427 int i = 0, x, y, distance, min_distance = -1;
428 boolean found = FALSE;
429 struct trap *t;
431 /* avoid known traps (i == 0) and boulders, but allow them as a backup */
432 if (reason != DISMOUNT_BYCHOICE || Stunned || Confusion || Fumbling)
433 i = 1;
434 for (; !found && i < 2; ++i) {
435 for (x = u.ux - 1; x <= u.ux + 1; x++)
436 for (y = u.uy - 1; y <= u.uy + 1; y++) {
437 if (!isok(x, y) || (x == u.ux && y == u.uy))
438 continue;
440 if (accessible(x, y) && !MON_AT(x, y)) {
441 distance = distu(x, y);
442 if (min_distance < 0 || distance < min_distance
443 || (distance == min_distance && rn2(2))) {
444 if (i > 0 || (((t = t_at(x, y)) == 0 || !t->tseen)
445 && (!sobj_at(BOULDER, x, y)
446 || throws_rocks(youmonst.data)))) {
447 spot->x = x;
448 spot->y = y;
449 min_distance = distance;
450 found = TRUE;
457 /* If we didn't find a good spot and forceit is on, try enexto(). */
458 if (forceit && min_distance < 0
459 && !enexto(spot, u.ux, u.uy, youmonst.data))
460 return FALSE;
462 return found;
465 /* Stop riding the current steed */
466 void
467 dismount_steed(reason)
468 int reason; /* Player was thrown off etc. */
470 struct monst *mtmp;
471 struct obj *otmp;
472 coord cc;
473 const char *verb = "fall";
474 boolean repair_leg_damage = (Wounded_legs != 0L);
475 unsigned save_utrap = u.utrap;
476 boolean have_spot = landing_spot(&cc, reason, 0);
478 mtmp = u.usteed; /* make a copy of steed pointer */
479 /* Sanity check */
480 if (!mtmp) /* Just return silently */
481 return;
483 /* Check the reason for dismounting */
484 otmp = which_armor(mtmp, W_SADDLE);
485 switch (reason) {
486 case DISMOUNT_THROWN:
487 verb = "are thrown";
488 case DISMOUNT_FELL:
489 You("%s off of %s!", verb, mon_nam(mtmp));
490 if (!have_spot)
491 have_spot = landing_spot(&cc, reason, 1);
492 losehp(Maybe_Half_Phys(rn1(10, 10)), "riding accident", KILLED_BY_AN);
493 set_wounded_legs(BOTH_SIDES, (int) HWounded_legs + rn1(5, 5));
494 repair_leg_damage = FALSE;
495 break;
496 case DISMOUNT_POLY:
497 You("can no longer ride %s.", mon_nam(u.usteed));
498 if (!have_spot)
499 have_spot = landing_spot(&cc, reason, 1);
500 break;
501 case DISMOUNT_ENGULFED:
502 /* caller displays message */
503 break;
504 case DISMOUNT_BONES:
505 /* hero has just died... */
506 break;
507 case DISMOUNT_GENERIC:
508 /* no messages, just make it so */
509 break;
510 case DISMOUNT_BYCHOICE:
511 default:
512 if (otmp && otmp->cursed) {
513 You("can't. The saddle %s cursed.",
514 otmp->bknown ? "is" : "seems to be");
515 otmp->bknown = TRUE;
516 return;
518 if (!have_spot) {
519 You("can't. There isn't anywhere for you to stand.");
520 return;
522 if (!has_mname(mtmp)) {
523 pline("You've been through the dungeon on %s with no name.",
524 an(mtmp->data->mname));
525 if (Hallucination)
526 pline("It felt good to get out of the rain.");
527 } else
528 You("dismount %s.", mon_nam(mtmp));
530 /* While riding, Wounded_legs refers to the steed's legs;
531 after dismounting, it reverts to the hero's legs. */
532 if (repair_leg_damage) {
533 /* [TODO: make heal_legs() take a parameter to handle this] */
534 in_steed_dismounting = TRUE;
535 heal_legs();
536 in_steed_dismounting = FALSE;
539 /* Release the steed and saddle */
540 u.usteed = 0;
541 u.ugallop = 0L;
543 /* Set player and steed's position. Try moving the player first
544 unless we're in the midst of creating a bones file. */
545 if (reason == DISMOUNT_BONES) {
546 /* move the steed to an adjacent square */
547 if (enexto(&cc, u.ux, u.uy, mtmp->data))
548 rloc_to(mtmp, cc.x, cc.y);
549 else /* evidently no room nearby; move steed elsewhere */
550 (void) rloc(mtmp, FALSE);
551 return;
553 if (mtmp->mhp > 0) {
554 place_monster(mtmp, u.ux, u.uy);
555 if (!u.uswallow && !u.ustuck && have_spot) {
556 struct permonst *mdat = mtmp->data;
558 /* The steed may drop into water/lava */
559 if (!is_flyer(mdat) && !is_floater(mdat) && !is_clinger(mdat)) {
560 if (is_pool(u.ux, u.uy)) {
561 if (!Underwater)
562 pline("%s falls into the %s!", Monnam(mtmp),
563 surface(u.ux, u.uy));
564 if (!is_swimmer(mdat) && !amphibious(mdat)) {
565 killed(mtmp);
566 adjalign(-1);
568 } else if (is_lava(u.ux, u.uy)) {
569 pline("%s is pulled into the %s!", Monnam(mtmp),
570 hliquid("lava"));
571 if (!likes_lava(mdat)) {
572 killed(mtmp);
573 adjalign(-1);
577 /* Steed dismounting consists of two steps: being moved to another
578 * square, and descending to the floor. We have functions to do
579 * each of these activities, but they're normally called
580 * individually and include an attempt to look at or pick up the
581 * objects on the floor:
582 * teleds() --> spoteffects() --> pickup()
583 * float_down() --> pickup()
584 * We use this kludge to make sure there is only one such attempt.
586 * Clearly this is not the best way to do it. A full fix would
587 * involve having these functions not call pickup() at all,
588 * instead
589 * calling them first and calling pickup() afterwards. But it
590 * would take a lot of work to keep this change from having any
591 * unforeseen side effects (for instance, you would no longer be
592 * able to walk onto a square with a hole, and autopickup before
593 * falling into the hole).
595 /* [ALI] No need to move the player if the steed died. */
596 if (mtmp->mhp > 0) {
597 /* Keep steed here, move the player to cc;
598 * teleds() clears u.utrap
600 in_steed_dismounting = TRUE;
601 teleds(cc.x, cc.y, TRUE);
602 in_steed_dismounting = FALSE;
604 /* Put your steed in your trap */
605 if (save_utrap)
606 (void) mintrap(mtmp);
608 /* Couldn't... try placing the steed */
609 } else if (enexto(&cc, u.ux, u.uy, mtmp->data)) {
610 /* Keep player here, move the steed to cc */
611 rloc_to(mtmp, cc.x, cc.y);
612 /* Player stays put */
613 /* Otherwise, kill the steed */
614 } else {
615 killed(mtmp);
616 adjalign(-1);
620 /* Return the player to the floor */
621 if (reason != DISMOUNT_ENGULFED) {
622 in_steed_dismounting = TRUE;
623 (void) float_down(0L, W_SADDLE);
624 in_steed_dismounting = FALSE;
625 context.botl = TRUE;
626 (void) encumber_msg();
627 vision_full_recalc = 1;
628 } else
629 context.botl = TRUE;
630 /* polearms behave differently when not mounted */
631 if (uwep && is_pole(uwep))
632 unweapon = TRUE;
633 return;
636 /* when attempting to saddle or mount a sleeping steed, try to wake it up
637 (for the saddling case, it won't be u.usteed yet) */
638 STATIC_OVL void
639 maybewakesteed(steed)
640 struct monst *steed;
642 int frozen = (int) steed->mfrozen;
643 boolean wasimmobile = steed->msleeping || !steed->mcanmove;
645 steed->msleeping = 0;
646 if (frozen) {
647 frozen = (frozen + 1) / 2; /* half */
648 /* might break out of timed sleep or paralysis */
649 if (!rn2(frozen)) {
650 steed->mfrozen = 0;
651 steed->mcanmove = 1;
652 } else {
653 /* didn't awake, but remaining duration is halved */
654 steed->mfrozen = frozen;
657 if (wasimmobile && !steed->msleeping && steed->mcanmove)
658 pline("%s wakes up.", Monnam(steed));
659 /* regardless of waking, terminate any meal in progress */
660 finish_meating(steed);
663 /* decide whether hero's steed is able to move;
664 doesn't check for holding traps--those affect the hero directly */
665 boolean
666 stucksteed(checkfeeding)
667 boolean checkfeeding;
669 struct monst *steed = u.usteed;
671 if (steed) {
672 /* check whether steed can move */
673 if (steed->msleeping || !steed->mcanmove) {
674 pline("%s won't move!", upstart(y_monnam(steed)));
675 return TRUE;
677 /* optionally check whether steed is in the midst of a meal */
678 if (checkfeeding && steed->meating) {
679 pline("%s is still eating.", upstart(y_monnam(steed)));
680 return TRUE;
683 return FALSE;
686 void
687 place_monster(mon, x, y)
688 struct monst *mon;
689 int x, y;
691 if (mon == u.usteed
692 /* special case is for convoluted vault guard handling */
693 || (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) {
694 impossible("placing %s onto map?",
695 (mon == u.usteed) ? "steed" : "defunct monster");
696 return;
698 mon->mx = x, mon->my = y;
699 level.monsters[x][y] = mon;
702 /*steed.c*/