1 /* SCCS Id: @(#)steed.c 3.4 2003/01/10 */
2 /* Copyright (c) Kevin Hugo, 1998-1999. */
3 /* NetHack may be freely redistributed. See license for details. */
10 /* Monsters that might be ridden */
11 static NEARDATA const char steeds[] = {
12 S_QUADRUPED, S_UNICORN, S_ANGEL, S_CENTAUR, S_DRAGON, S_JABBERWOCK, '\0'
15 STATIC_DCL boolean FDECL(landing_spot, (coord *, int, int));
17 /* caller has decided that hero can't reach something while mounted */
21 You("aren't skilled enough to reach from %s.", y_monnam(u.usteed));
24 /*** Putting the saddle on ***/
26 /* Can this monster wear a saddle? */
31 struct permonst *ptr = mtmp->data;
33 return (index(steeds, ptr->mlet) && (ptr->msize >= MZ_MEDIUM) &&
34 (!humanoid(ptr) || ptr->mlet == S_CENTAUR) &&
35 !amorphous(ptr) && !noncorporeal(ptr) &&
36 !is_whirly(ptr) && !unsolid(ptr));
51 if (nohands(youmonst.data)) {
52 You("have no hands!"); /* not `body_part(HAND)' */
54 } else if (!freehand()) {
55 You("have no free %s.", body_part(HAND));
59 /* Select an animal */
60 if (u.uswallow || Underwater || !getdir((char *)0)) {
65 pline("Saddle yourself? Very funny...");
68 if (!isok(u.ux+u.dx, u.uy+u.dy) ||
69 !(mtmp = m_at(u.ux+u.dx, u.uy+u.dy)) ||
71 pline("I see nobody there.");
75 /* Is this a valid monster? */
76 if (mtmp->misc_worn_check & W_SADDLE ||
77 which_armor(mtmp, W_SADDLE)) {
78 pline("%s doesn't need another one.", Monnam(mtmp));
82 if (touch_petrifies(ptr) && !uarmg && !Stone_resistance) {
85 You("touch %s.", mon_nam(mtmp));
86 if (!(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) {
87 Sprintf(kbuf, "attempting to saddle %s", an(mtmp->data->mname));
91 if (ptr == &mons[PM_INCUBUS] || ptr == &mons[PM_SUCCUBUS]) {
92 pline("Shame on you!");
93 exercise(A_WIS, FALSE);
96 if (mtmp->isminion || mtmp->isshk || mtmp->ispriest ||
97 mtmp->isgd || mtmp->iswiz) {
98 pline("I think %s would mind.", mon_nam(mtmp));
101 if (!can_saddle(mtmp)) {
102 You_cant("saddle such a creature.");
106 /* Calculate your chance */
107 chance = ACURR(A_DEX) + ACURR(A_CHA)/2 + 2*mtmp->mtame;
108 chance += u.ulevel * (mtmp->mtame ? 20 : 5);
109 if (!mtmp->mtame) chance -= 10*mtmp->m_lev;
110 if (Role_if(PM_KNIGHT))
112 switch (P_SKILL(P_RIDING)) {
124 if (Confusion || Fumbling || Glib)
127 (s = OBJ_DESCR(objects[uarmg->otyp])) != (char *)0 &&
128 !strncmp(s, "riding ", 7))
129 /* Bonus for wearing "riding" (but not fumbling) gloves */
132 (s = OBJ_DESCR(objects[uarmf->otyp])) != (char *)0 &&
133 !strncmp(s, "riding ", 7))
134 /* ... or for "riding boots" */
139 /* Make the attempt */
140 if (rn2(100) < chance) {
141 You("put the saddle on %s.", mon_nam(mtmp));
142 if (otmp->owornmask) remove_worn_item(otmp, FALSE);
144 /* mpickobj may free otmp it if merges, but we have already
145 checked for a saddle above, so no merger should happen */
146 (void) mpickobj(mtmp, otmp);
147 mtmp->misc_worn_check |= W_SADDLE;
148 otmp->owornmask = W_SADDLE;
149 otmp->leashmon = mtmp->m_id;
150 update_mon_intrinsics(mtmp, otmp, TRUE, FALSE);
152 pline("%s resists!", Monnam(mtmp));
157 /*** Riding the monster ***/
159 /* Can we ride this monster? Caller should also check can_saddle() */
164 return (mtmp->mtame && humanoid(youmonst.data) &&
165 !verysmall(youmonst.data) && !bigmonst(youmonst.data) &&
166 (!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)) {
179 if (wizard && yn("Force the mount to succeed?") == 'y')
182 return (mount_steed(m_at(u.ux+u.dx, u.uy+u.dy), forcemount));
189 /* Start riding, with the given monster */
191 mount_steed(mtmp, force)
192 struct monst *mtmp; /* The animal */
193 boolean force; /* Quietly force this animal */
197 struct permonst *ptr;
201 You("are already riding %s.", mon_nam(u.usteed));
205 /* Is the player in the right form? */
206 if (Hallucination && !force) {
207 pline("Maybe you should find a designated driver.");
210 /* While riding Wounded_legs refers to the steed's,
211 * not the hero's legs.
212 * That opens up a potential abuse where the player
213 * can mount a steed, then dismount immediately to
214 * heal leg damage, because leg damage is always
215 * healed upon dismount (Wounded_legs context switch).
216 * By preventing a hero with Wounded_legs from
217 * mounting a steed, the potential for abuse is
218 * minimized, if not eliminated altogether.
221 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;
230 if (Upolyd && (!humanoid(youmonst.data) || verysmall(youmonst.data) ||
231 bigmonst(youmonst.data) || slithy(youmonst.data))) {
232 You("won't fit on a saddle.");
235 if(!force && (near_capacity() > SLT_ENCUMBER)) {
236 You_cant("do that while carrying so much stuff.");
240 /* Can the player reach and see the monster? */
241 if (!mtmp || (!force && ((Blind && !Blind_telepat) ||
243 mtmp->m_ap_type == M_AP_FURNITURE ||
244 mtmp->m_ap_type == M_AP_OBJECT))) {
245 pline("I see nobody there.");
248 if (u.uswallow || u.ustuck || u.utrap || Punished ||
249 !test_move(u.ux, u.uy, mtmp->mx-u.ux, mtmp->my-u.uy, TEST_MOVE)) {
250 if (Punished || !(u.uswallow || u.ustuck || u.utrap))
251 You("are unable to swing your %s over.", body_part(LEG));
253 You("are stuck here for now.");
257 /* Is this a valid monster? */
258 otmp = which_armor(mtmp, W_SADDLE);
260 pline("%s is not saddled.", Monnam(mtmp));
264 if (touch_petrifies(ptr) && !Stone_resistance) {
267 You("touch %s.", mon_nam(mtmp));
268 Sprintf(kbuf, "attempting to ride %s", an(mtmp->data->mname));
271 if (!mtmp->mtame || mtmp->isminion) {
272 pline("I think %s would mind.", mon_nam(mtmp));
275 if (mtmp->mtrapped) {
276 struct trap *t = t_at(mtmp->mx, mtmp->my);
278 You_cant("mount %s while %s's trapped in %s.",
279 mon_nam(mtmp), mhe(mtmp),
280 an(defsyms[trap_to_defsym(t->ttyp)].explanation));
284 if (!force && !Role_if(PM_KNIGHT) && !(--mtmp->mtame)) {
286 newsym(mtmp->mx, mtmp->my);
287 pline("%s resists%s!", Monnam(mtmp),
288 mtmp->mleashed ? " and its leash comes off" : "");
289 if (mtmp->mleashed) m_unleash(mtmp, FALSE);
292 if (!force && Underwater && !is_swimmer(ptr)) {
293 You_cant("ride that creature while under water.");
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) &&
303 Levitation && !Lev_at_will) {
304 You("cannot reach %s.", mon_nam(mtmp));
307 if (!force && uarm && is_metallic(uarm) &&
308 greatest_erosion(uarm)) {
309 Your("%s armor is too stiff to be able to mount %s.",
310 uarm->oeroded ? "rusty" : "corroded",
314 if (!force && (Confusion || Fumbling || Glib || Wounded_legs ||
315 otmp->cursed || (u.ulevel+mtmp->mtame < rnd(MAXULEV/2+5)))) {
317 pline("%s slips away from you.", Monnam(mtmp));
320 You("slip while trying to get on %s.", mon_nam(mtmp));
322 Sprintf(buf, "slipped while mounting %s",
323 /* "a saddled mumak" or "a saddled pony called Dobbin" */
324 x_monnam(mtmp, ARTICLE_A, (char *)0,
325 SUPPRESS_IT|SUPPRESS_INVISIBLE|SUPPRESS_HALLUCINATION,
327 losehp(rn1(5,10), buf, NO_KILLER_PREFIX);
333 if (Levitation && !is_floater(ptr) && !is_flyer(ptr))
334 /* Must have Lev_at_will at this point */
335 pline("%s magically floats up!", Monnam(mtmp));
336 You("mount %s.", mon_nam(mtmp));
338 /* setuwep handles polearms differently when you're mounted */
339 if (uwep && is_pole(uwep)) unweapon = FALSE;
341 remove_monster(mtmp->mx, mtmp->my);
342 teleds(mtmp->mx, mtmp->my, TRUE);
347 /* You and your steed have moved */
354 /* It takes many turns of riding to exercise skill */
355 if (u.urideturns++ >= 100) {
357 use_skill(P_RIDING, 1);
363 /* The player kicks or whips the steed */
371 /* [ALI] Various effects of kicking sleeping/paralyzed steeds */
372 if (u.usteed->msleeping || !u.usteed->mcanmove) {
373 /* We assume a message has just been output of the form
374 * "You kick <steed>."
376 Strcpy(He, mhe(u.usteed));
378 if ((u.usteed->mcanmove || u.usteed->mfrozen) && !rn2(2)) {
379 if (u.usteed->mcanmove)
380 u.usteed->msleeping = 0;
381 else if (u.usteed->mfrozen > 2)
382 u.usteed->mfrozen -= 2;
384 u.usteed->mfrozen = 0;
385 u.usteed->mcanmove = 1;
387 if (u.usteed->msleeping || !u.usteed->mcanmove)
388 pline("%s stirs.", He);
390 pline("%s rouses %sself!", He, mhim(u.usteed));
392 pline("%s does not respond.", He);
396 /* Make the steed less tame and check if it resists */
397 if (u.usteed->mtame) u.usteed->mtame--;
398 if (!u.usteed->mtame && u.usteed->mleashed) m_unleash(u.usteed, TRUE);
399 if (!u.usteed->mtame || (u.ulevel+u.usteed->mtame < rnd(MAXULEV/2+5))) {
400 newsym(u.usteed->mx, u.usteed->my);
401 dismount_steed(DISMOUNT_THROWN);
405 pline("%s gallops!", Monnam(u.usteed));
406 u.ugallop += rn1(20, 30);
411 * Try to find a dismount point adjacent to the steed's location.
412 * If all else fails, try enexto(). Use enexto() as a last resort because
413 * enexto() chooses its point randomly, possibly even outside the
414 * room's walls, which is not what we want.
415 * Adapted from mail daemon code.
418 landing_spot(spot, reason, forceit)
419 coord *spot; /* landing position (we fill it in) */
423 int i = 0, x, y, distance, min_distance = -1;
424 boolean found = FALSE;
427 /* avoid known traps (i == 0) and boulders, but allow them as a backup */
428 if (reason != DISMOUNT_BYCHOICE || Stunned || Confusion || Fumbling) i = 1;
429 for (; !found && i < 2; ++i) {
430 for (x = u.ux-1; x <= u.ux+1; x++)
431 for (y = u.uy-1; y <= u.uy+1; y++) {
432 if (!isok(x, y) || (x == u.ux && y == u.uy)) continue;
434 if (ACCESSIBLE(levl[x][y].typ) &&
435 !MON_AT(x,y) && !closed_door(x,y)) {
436 distance = distu(x,y);
437 if (min_distance < 0 || distance < min_distance ||
438 (distance == min_distance && rn2(2))) {
439 if (i > 0 || (((t = t_at(x, y)) == 0 || !t->tseen) &&
440 (!sobj_at(BOULDER, x, y) ||
441 throws_rocks(youmonst.data)))) {
444 min_distance = distance;
452 /* If we didn't find a good spot and forceit is on, try enexto(). */
453 if (forceit && min_distance < 0 &&
454 !enexto(spot, u.ux, u.uy, youmonst.data))
460 /* Stop riding the current steed */
462 dismount_steed(reason)
463 int reason; /* Player was thrown off etc. */
468 const char *verb = "fall";
469 boolean repair_leg_damage = TRUE;
470 unsigned save_utrap = u.utrap;
471 boolean have_spot = landing_spot(&cc,reason,0);
473 mtmp = u.usteed; /* make a copy of steed pointer */
475 if (!mtmp) /* Just return silently */
478 /* Check the reason for dismounting */
479 otmp = which_armor(mtmp, W_SADDLE);
481 case DISMOUNT_THROWN:
484 You("%s off of %s!", verb, mon_nam(mtmp));
485 if (!have_spot) have_spot = landing_spot(&cc,reason,1);
486 losehp(rn1(10,10), "riding accident", KILLED_BY_AN);
487 set_wounded_legs(BOTH_SIDES, (int)HWounded_legs + rn1(5,5));
488 repair_leg_damage = FALSE;
491 You("can no longer ride %s.", mon_nam(u.usteed));
492 if (!have_spot) have_spot = landing_spot(&cc,reason,1);
494 case DISMOUNT_ENGULFED:
495 /* caller displays message */
498 /* hero has just died... */
500 case DISMOUNT_GENERIC:
501 /* no messages, just make it so */
503 case DISMOUNT_BYCHOICE:
505 if (otmp && otmp->cursed) {
506 You("can't. The saddle %s cursed.",
507 otmp->bknown ? "is" : "seems to be");
512 You("can't. There isn't anywhere for you to stand.");
515 if (!mtmp->mnamelth) {
516 pline("You've been through the dungeon on %s with no name.",
517 an(mtmp->data->mname));
519 pline("It felt good to get out of the rain.");
521 You("dismount %s.", mon_nam(mtmp));
523 /* While riding these refer to the steed's legs
524 * so after dismounting they refer to the player's
527 if (repair_leg_damage) HWounded_legs = EWounded_legs = 0;
529 /* Release the steed and saddle */
533 /* Set player and steed's position. Try moving the player first
534 unless we're in the midst of creating a bones file. */
535 if (reason == DISMOUNT_BONES) {
536 /* move the steed to an adjacent square */
537 if (enexto(&cc, u.ux, u.uy, mtmp->data))
538 rloc_to(mtmp, cc.x, cc.y);
539 else /* evidently no room nearby; move steed elsewhere */
540 (void) rloc(mtmp, FALSE);
543 if (!DEADMONSTER(mtmp)) {
544 place_monster(mtmp, u.ux, u.uy);
545 if (!u.uswallow && !u.ustuck && have_spot) {
546 struct permonst *mdat = mtmp->data;
548 /* The steed may drop into water/lava */
549 if (!is_flyer(mdat) && !is_floater(mdat) && !is_clinger(mdat)) {
550 if (is_pool(u.ux, u.uy)) {
552 pline("%s falls into the %s!", Monnam(mtmp),
553 surface(u.ux, u.uy));
554 if (!is_swimmer(mdat) && !amphibious(mdat)) {
558 } else if (is_lava(u.ux, u.uy)) {
559 pline("%s is pulled into the lava!", Monnam(mtmp));
560 if (!likes_lava(mdat)) {
566 /* Steed dismounting consists of two steps: being moved to another
567 * square, and descending to the floor. We have functions to do
568 * each of these activities, but they're normally called
569 * individually and include an attempt to look at or pick up the
570 * objects on the floor:
571 * teleds() --> spoteffects() --> pickup()
572 * float_down() --> pickup()
573 * We use this kludge to make sure there is only one such attempt.
575 * Clearly this is not the best way to do it. A full fix would
576 * involve having these functions not call pickup() at all, instead
577 * calling them first and calling pickup() afterwards. But it
578 * would take a lot of work to keep this change from having any
579 * unforseen side effects (for instance, you would no longer be
580 * able to walk onto a square with a hole, and autopickup before
581 * falling into the hole).
583 /* [ALI] No need to move the player if the steed died. */
584 if (!DEADMONSTER(mtmp)) {
585 /* Keep steed here, move the player to cc;
586 * teleds() clears u.utrap
588 in_steed_dismounting = TRUE;
589 teleds(cc.x, cc.y, TRUE);
590 in_steed_dismounting = FALSE;
592 /* Put your steed in your trap */
594 (void) mintrap(mtmp);
596 /* Couldn't... try placing the steed */
597 } else if (enexto(&cc, u.ux, u.uy, mtmp->data)) {
598 /* Keep player here, move the steed to cc */
599 rloc_to(mtmp, cc.x, cc.y);
600 /* Player stays put */
601 /* Otherwise, kill the steed */
608 /* Return the player to the floor */
609 if (reason != DISMOUNT_ENGULFED) {
610 in_steed_dismounting = TRUE;
611 (void) float_down(0L, W_SADDLE);
612 in_steed_dismounting = FALSE;
614 (void)encumber_msg();
615 vision_full_recalc = 1;
618 /* polearms behave differently when not mounted */
619 if (uwep && is_pole(uwep)) unweapon = TRUE;
624 place_monster(mon, x, y)
628 if (mon == u.usteed ||
629 /* special case is for convoluted vault guard handling */
630 (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) {
631 impossible("placing %s onto map?",
632 (mon == u.usteed) ? "steed" : "defunct monster");
635 mon->mx = x, mon->my = y;
636 level.monsters[x][y] = mon;