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. */
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
,
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 */
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? */
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
));
48 /* Select an animal */
49 if (u
.uswallow
|| Underwater
|| !getdir((char *) 0)) {
54 pline("Saddle yourself? Very funny...");
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.");
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
));
69 if (touch_petrifies(ptr
) && !uarmg
&& !Stone_resistance
) {
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
));
78 if (ptr
== &mons
[PM_INCUBUS
] || ptr
== &mons
[PM_SUCCUBUS
]) {
79 pline("Shame on you!");
80 exercise(A_WIS
, FALSE
);
83 if (mtmp
->isminion
|| mtmp
->isshk
|| mtmp
->ispriest
|| mtmp
->isgd
85 pline("I think %s would mind.", mon_nam(mtmp
));
88 if (!can_saddle(mtmp
)) {
89 You_cant("saddle such a creature.");
93 /* Calculate your chance */
94 chance
= ACURR(A_DEX
) + ACURR(A_CHA
) / 2 + 2 * mtmp
->mtame
;
95 chance
+= u
.ulevel
* (mtmp
->mtame
? 20 : 5);
97 chance
-= 10 * mtmp
->m_lev
;
98 if (Role_if(PM_KNIGHT
))
100 switch (P_SKILL(P_RIDING
)) {
115 if (Confusion
|| Fumbling
|| Glib
)
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 */
121 else if (uarmf
&& (s
= OBJ_DESCR(objects
[uarmf
->otyp
])) != (char *) 0
122 && !strncmp(s
, "riding ", 7))
123 /* ... or for "riding boots" */
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
));
135 remove_worn_item(otmp
, FALSE
);
137 put_saddle_on_mon(otmp
, mtmp
);
139 pline("%s resists!", Monnam(mtmp
));
144 put_saddle_on_mon(saddle
, mtmp
)
148 if (!can_saddle(mtmp
) || which_armor(mtmp
, W_SADDLE
))
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() */
165 return (mtmp
->mtame
&& humanoid(youmonst
.data
)
166 && !verysmall(youmonst
.data
) && !bigmonst(youmonst
.data
)
167 && (!Underwater
|| is_swimmer(mtmp
->data
)));
173 boolean forcemount
= FALSE
;
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')
180 return (mount_steed(m_at(u
.ux
+ u
.dx
, u
.uy
+ u
.dy
), forcemount
));
187 /* Start riding, with the given monster */
189 mount_steed(mtmp
, force
)
190 struct monst
*mtmp
; /* The animal */
191 boolean force
; /* Quietly force this animal */
195 struct permonst
*ptr
;
199 You("are already riding %s.", mon_nam(u
.usteed
));
203 /* Is the player in the right form? */
204 if (Hallucination
&& !force
) {
205 pline("Maybe you should find a designated driver.");
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.]
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;
229 if (Upolyd
&& (!humanoid(youmonst
.data
) || verysmall(youmonst
.data
)
230 || bigmonst(youmonst
.data
) || slithy(youmonst
.data
))) {
231 You("won't fit on a saddle.");
234 if (!force
&& (near_capacity() > SLT_ENCUMBER
)) {
235 You_cant("do that while carrying so much stuff.");
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.");
246 if (u
.uswallow
|| u
.ustuck
|| u
.utrap
|| Punished
247 || !test_move(u
.ux
, u
.uy
, mtmp
->mx
- u
.ux
, mtmp
->my
- u
.uy
,
249 if (Punished
|| !(u
.uswallow
|| u
.ustuck
|| u
.utrap
))
250 You("are unable to swing your %s over.", body_part(LEG
));
252 You("are stuck here for now.");
256 /* Is this a valid monster? */
257 otmp
= which_armor(mtmp
, W_SADDLE
);
259 pline("%s is not saddled.", Monnam(mtmp
));
263 if (touch_petrifies(ptr
) && !Stone_resistance
) {
266 You("touch %s.", mon_nam(mtmp
));
267 Sprintf(kbuf
, "attempting to ride %s", an(mtmp
->data
->mname
));
270 if (!mtmp
->mtame
|| mtmp
->isminion
) {
271 pline("I think %s would mind.", mon_nam(mtmp
));
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
));
282 if (!force
&& !Role_if(PM_KNIGHT
) && !(--mtmp
->mtame
)) {
284 newsym(mtmp
->mx
, mtmp
->my
);
285 pline("%s resists%s!", Monnam(mtmp
),
286 mtmp
->mleashed
? " and its leash comes off" : "");
288 m_unleash(mtmp
, FALSE
);
291 if (!force
&& Underwater
&& !is_swimmer(ptr
)) {
292 You_cant("ride that creature while under %s.",
296 if (!can_saddle(mtmp
) || !can_ride(mtmp
)) {
297 You_cant("ride such a creature.");
301 /* Is the player impaired? */
302 if (!force
&& !is_floater(ptr
) && !is_flyer(ptr
) && Levitation
304 You("cannot reach %s.", mon_nam(mtmp
));
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
));
313 && (Confusion
|| Fumbling
|| Glib
|| Wounded_legs
|| otmp
->cursed
314 || (u
.ulevel
+ mtmp
->mtame
< rnd(MAXULEV
/ 2 + 5)))) {
316 pline("%s slips away from you.", Monnam(mtmp
));
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
,
327 losehp(Maybe_Half_Phys(rn1(5, 10)), buf
, NO_KILLER_PREFIX
);
332 maybewakesteed(mtmp
);
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
))
343 remove_monster(mtmp
->mx
, mtmp
->my
);
344 teleds(mtmp
->mx
, mtmp
->my
, TRUE
);
349 /* You and your steed have moved */
356 /* It takes many turns of riding to exercise skill */
357 if (u
.urideturns
++ >= 100) {
359 use_skill(P_RIDING
, 1);
364 /* The player kicks or whips the steed */
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
));
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;
385 u
.usteed
->mfrozen
= 0;
386 u
.usteed
->mcanmove
= 1;
388 if (u
.usteed
->msleeping
|| !u
.usteed
->mcanmove
)
389 pline("%s stirs.", He
);
391 pline("%s rouses %sself!", He
, mhim(u
.usteed
));
393 pline("%s does not respond.", He
);
397 /* Make the steed less tame and check if it resists */
400 if (!u
.usteed
->mtame
&& u
.usteed
->mleashed
)
401 m_unleash(u
.usteed
, TRUE
);
403 || (u
.ulevel
+ u
.usteed
->mtame
< rnd(MAXULEV
/ 2 + 5))) {
404 newsym(u
.usteed
->mx
, u
.usteed
->my
);
405 dismount_steed(DISMOUNT_THROWN
);
409 pline("%s gallops!", Monnam(u
.usteed
));
410 u
.ugallop
+= rn1(20, 30);
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.
422 landing_spot(spot
, reason
, forceit
)
423 coord
*spot
; /* landing position (we fill it in) */
427 int i
= 0, x
, y
, distance
, min_distance
= -1;
428 boolean found
= FALSE
;
431 /* avoid known traps (i == 0) and boulders, but allow them as a backup */
432 if (reason
!= DISMOUNT_BYCHOICE
|| Stunned
|| Confusion
|| Fumbling
)
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
))
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
)))) {
449 min_distance
= distance
;
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
))
465 /* Stop riding the current steed */
467 dismount_steed(reason
)
468 int reason
; /* Player was thrown off etc. */
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 */
480 if (!mtmp
) /* Just return silently */
483 /* Check the reason for dismounting */
484 otmp
= which_armor(mtmp
, W_SADDLE
);
486 case DISMOUNT_THROWN
:
489 You("%s off of %s!", verb
, mon_nam(mtmp
));
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
;
497 You("can no longer ride %s.", mon_nam(u
.usteed
));
499 have_spot
= landing_spot(&cc
, reason
, 1);
501 case DISMOUNT_ENGULFED
:
502 /* caller displays message */
505 /* hero has just died... */
507 case DISMOUNT_GENERIC
:
508 /* no messages, just make it so */
510 case DISMOUNT_BYCHOICE
:
512 if (otmp
&& otmp
->cursed
) {
513 You("can't. The saddle %s cursed.",
514 otmp
->bknown
? "is" : "seems to be");
519 You("can't. There isn't anywhere for you to stand.");
522 if (!has_mname(mtmp
)) {
523 pline("You've been through the dungeon on %s with no name.",
524 an(mtmp
->data
->mname
));
526 pline("It felt good to get out of the rain.");
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
;
536 in_steed_dismounting
= FALSE
;
539 /* Release the steed and saddle */
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
);
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
)) {
562 pline("%s falls into the %s!", Monnam(mtmp
),
563 surface(u
.ux
, u
.uy
));
564 if (!is_swimmer(mdat
) && !amphibious(mdat
)) {
568 } else if (is_lava(u
.ux
, u
.uy
)) {
569 pline("%s is pulled into the %s!", Monnam(mtmp
),
571 if (!likes_lava(mdat
)) {
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,
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. */
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 */
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 */
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
;
626 (void) encumber_msg();
627 vision_full_recalc
= 1;
630 /* polearms behave differently when not mounted */
631 if (uwep
&& is_pole(uwep
))
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) */
639 maybewakesteed(steed
)
642 int frozen
= (int) steed
->mfrozen
;
643 boolean wasimmobile
= steed
->msleeping
|| !steed
->mcanmove
;
645 steed
->msleeping
= 0;
647 frozen
= (frozen
+ 1) / 2; /* half */
648 /* might break out of timed sleep or paralysis */
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 */
666 stucksteed(checkfeeding
)
667 boolean checkfeeding
;
669 struct monst
*steed
= u
.usteed
;
672 /* check whether steed can move */
673 if (steed
->msleeping
|| !steed
->mcanmove
) {
674 pline("%s won't move!", upstart(y_monnam(steed
)));
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
)));
687 place_monster(mon
, x
, y
)
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");
698 mon
->mx
= x
, mon
->my
= y
;
699 level
.monsters
[x
][y
] = mon
;