OSDN Git Service

Initial Import
[nethackexpress/trunk.git] / src / steed.c
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. */
4
5 #include "hack.h"
6
7
8 #ifdef STEED
9
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'
13 };
14
15 STATIC_DCL boolean FDECL(landing_spot, (coord *, int, int));
16
17 /* caller has decided that hero can't reach something while mounted */
18 void
19 rider_cant_reach()
20 {
21      You("aren't skilled enough to reach from %s.", y_monnam(u.usteed));
22 }
23
24 /*** Putting the saddle on ***/
25
26 /* Can this monster wear a saddle? */
27 boolean
28 can_saddle(mtmp)
29         struct monst *mtmp;
30 {
31         struct permonst *ptr = mtmp->data;
32
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));
37 }
38
39
40 int
41 use_saddle(otmp)
42         struct obj *otmp;
43 {
44         struct monst *mtmp;
45         struct permonst *ptr;
46         int chance;
47         const char *s;
48
49
50         /* Can you use it? */
51         if (nohands(youmonst.data)) {
52                 You("have no hands!");  /* not `body_part(HAND)' */
53                 return 0;
54         } else if (!freehand()) {
55                 You("have no free %s.", body_part(HAND));
56                 return 0;
57         }
58
59         /* Select an animal */
60         if (u.uswallow || Underwater || !getdir((char *)0)) {
61             pline(Never_mind);
62             return 0;
63         }
64         if (!u.dx && !u.dy) {
65             pline("Saddle yourself?  Very funny...");
66             return 0;
67         }
68         if (!isok(u.ux+u.dx, u.uy+u.dy) ||
69                         !(mtmp = m_at(u.ux+u.dx, u.uy+u.dy)) ||
70                         !canspotmon(mtmp)) {
71             pline("I see nobody there.");
72             return 1;
73         }
74
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));
79             return 1;
80         }
81         ptr = mtmp->data;
82         if (touch_petrifies(ptr) && !uarmg && !Stone_resistance) {
83             char kbuf[BUFSZ];
84
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));
88                 instapetrify(kbuf);
89             }
90         }
91         if (ptr == &mons[PM_INCUBUS] || ptr == &mons[PM_SUCCUBUS]) {
92             pline("Shame on you!");
93             exercise(A_WIS, FALSE);
94             return 1;
95         }
96         if (mtmp->isminion || mtmp->isshk || mtmp->ispriest ||
97                         mtmp->isgd || mtmp->iswiz) {
98             pline("I think %s would mind.", mon_nam(mtmp));
99             return 1;
100         }
101         if (!can_saddle(mtmp)) {
102                 You_cant("saddle such a creature.");
103                 return 1;
104         }
105
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))
111             chance += 20;
112         switch (P_SKILL(P_RIDING)) {
113         case P_ISRESTRICTED:
114         case P_UNSKILLED:
115         default:
116             chance -= 20;       break;
117         case P_BASIC:
118             break;
119         case P_SKILLED:
120             chance += 15;       break;
121         case P_EXPERT:
122             chance += 30;       break;
123         }
124         if (Confusion || Fumbling || Glib)
125             chance -= 20;
126         else if (uarmg &&
127                 (s = OBJ_DESCR(objects[uarmg->otyp])) != (char *)0 &&
128                 !strncmp(s, "riding ", 7))
129             /* Bonus for wearing "riding" (but not fumbling) gloves */
130             chance += 10;
131         else if (uarmf &&
132                 (s = OBJ_DESCR(objects[uarmf->otyp])) != (char *)0 &&
133                 !strncmp(s, "riding ", 7))
134             /* ... or for "riding boots" */
135             chance += 10;
136         if (otmp->cursed)
137             chance -= 50;
138
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);
143             freeinv(otmp);
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);
151         } else
152             pline("%s resists!", Monnam(mtmp));
153         return 1;
154 }
155
156
157 /*** Riding the monster ***/
158
159 /* Can we ride this monster?  Caller should also check can_saddle() */
160 boolean
161 can_ride(mtmp)
162         struct monst *mtmp;
163 {
164         return (mtmp->mtame && humanoid(youmonst.data) &&
165                         !verysmall(youmonst.data) && !bigmonst(youmonst.data) &&
166                         (!Underwater || is_swimmer(mtmp->data)));
167 }
168
169
170 int
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 #ifdef WIZARD
179         if (wizard && yn("Force the mount to succeed?") == 'y')
180                 forcemount = TRUE;
181 #endif
182             return (mount_steed(m_at(u.ux+u.dx, u.uy+u.dy), forcemount));
183         } else
184             return 0;
185         return 1;
186 }
187
188
189 /* Start riding, with the given monster */
190 boolean
191 mount_steed(mtmp, force)
192         struct monst *mtmp;     /* The animal */
193         boolean force;          /* Quietly force this animal */
194 {
195         struct obj *otmp;
196         char buf[BUFSZ];
197         struct permonst *ptr;
198
199         /* Sanity checks */
200         if (u.usteed) {
201             You("are already riding %s.", mon_nam(u.usteed));
202             return (FALSE);
203         }
204
205         /* Is the player in the right form? */
206         if (Hallucination && !force) {
207             pline("Maybe you should find a designated driver.");
208             return (FALSE);
209         }
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.
219          */
220         if (Wounded_legs) {
221             Your("%s are in no shape for riding.", makeplural(body_part(LEG)));
222 #ifdef WIZARD
223             if (force && wizard && yn("Heal your legs?") == 'y')
224                 HWounded_legs = EWounded_legs = 0;
225             else
226 #endif
227             return (FALSE);
228         }
229
230         if (Upolyd && (!humanoid(youmonst.data) || verysmall(youmonst.data) ||
231                         bigmonst(youmonst.data) || slithy(youmonst.data))) {
232             You("won't fit on a saddle.");
233             return (FALSE);
234         }
235         if(!force && (near_capacity() > SLT_ENCUMBER)) {
236             You_cant("do that while carrying so much stuff.");
237             return (FALSE);
238         }
239
240         /* Can the player reach and see the monster? */
241         if (!mtmp || (!force && ((Blind && !Blind_telepat) ||
242                 mtmp->mundetected ||
243                 mtmp->m_ap_type == M_AP_FURNITURE ||
244                 mtmp->m_ap_type == M_AP_OBJECT))) {
245             pline("I see nobody there.");
246             return (FALSE);
247         }
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)); 
252             else
253                 You("are stuck here for now.");
254             return (FALSE);
255         }
256
257         /* Is this a valid monster? */
258         otmp = which_armor(mtmp, W_SADDLE);
259         if (!otmp) {
260             pline("%s is not saddled.", Monnam(mtmp));
261             return (FALSE);
262         }
263         ptr = mtmp->data;
264         if (touch_petrifies(ptr) && !Stone_resistance) {
265             char kbuf[BUFSZ];
266
267             You("touch %s.", mon_nam(mtmp));
268             Sprintf(kbuf, "attempting to ride %s", an(mtmp->data->mname));
269             instapetrify(kbuf);
270         }
271         if (!mtmp->mtame || mtmp->isminion) {
272             pline("I think %s would mind.", mon_nam(mtmp));
273             return (FALSE);
274         }
275         if (mtmp->mtrapped) {
276             struct trap *t = t_at(mtmp->mx, mtmp->my);
277
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));
281             return (FALSE);
282         }
283
284         if (!force && !Role_if(PM_KNIGHT) && !(--mtmp->mtame)) {
285             /* no longer tame */
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);
290             return (FALSE);
291         }
292         if (!force && Underwater && !is_swimmer(ptr)) {
293             You_cant("ride that creature while under water.");
294             return (FALSE);
295         }
296         if (!can_saddle(mtmp) || !can_ride(mtmp)) {
297             You_cant("ride such a creature.");
298             return (0);
299         }
300
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));
305             return (FALSE);
306         }
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",
311                         mon_nam(mtmp));
312             return (FALSE);
313         }
314         if (!force && (Confusion || Fumbling || Glib || Wounded_legs ||
315                 otmp->cursed || (u.ulevel+mtmp->mtame < rnd(MAXULEV/2+5)))) {
316             if (Levitation) {
317                 pline("%s slips away from you.", Monnam(mtmp));
318                 return FALSE;
319             }
320             You("slip while trying to get on %s.", mon_nam(mtmp));
321
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,
326                              TRUE));
327             losehp(rn1(5,10), buf, NO_KILLER_PREFIX);
328             return (FALSE);
329         }
330
331         /* Success */
332         if (!force) {
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));
337         }
338         /* setuwep handles polearms differently when you're mounted */
339         if (uwep && is_pole(uwep)) unweapon = FALSE;
340         u.usteed = mtmp;
341         remove_monster(mtmp->mx, mtmp->my);
342         teleds(mtmp->mx, mtmp->my, TRUE);
343         return (TRUE);
344 }
345
346
347 /* You and your steed have moved */
348 void
349 exercise_steed()
350 {
351         if (!u.usteed)
352                 return;
353
354         /* It takes many turns of riding to exercise skill */
355         if (u.urideturns++ >= 100) {
356             u.urideturns = 0;
357             use_skill(P_RIDING, 1);
358         }
359         return;
360 }
361
362
363 /* The player kicks or whips the steed */
364 void
365 kick_steed()
366 {
367         char He[4];
368         if (!u.usteed)
369             return;
370
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>."
375              */
376             Strcpy(He, mhe(u.usteed));
377             *He = highc(*He);
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;
383                 else {
384                     u.usteed->mfrozen = 0;
385                     u.usteed->mcanmove = 1;
386                 }
387                 if (u.usteed->msleeping || !u.usteed->mcanmove)
388                     pline("%s stirs.", He);
389                 else
390                     pline("%s rouses %sself!", He, mhim(u.usteed));
391             } else
392                 pline("%s does not respond.", He);
393             return;
394         }
395
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);
402             return;
403         }
404
405         pline("%s gallops!", Monnam(u.usteed));
406         u.ugallop += rn1(20, 30);
407         return;
408 }
409
410 /*
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.
416  */
417 STATIC_OVL boolean
418 landing_spot(spot, reason, forceit)
419 coord *spot;    /* landing position (we fill it in) */
420 int reason;
421 int forceit;
422 {
423     int i = 0, x, y, distance, min_distance = -1;
424     boolean found = FALSE;
425     struct trap *t;
426
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;
433
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)))) {
442                             spot->x = x;
443                             spot->y = y;
444                             min_distance = distance;
445                             found = TRUE;
446                         }
447                     }
448                 }
449             }
450     }
451
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))
455         return FALSE;
456
457     return found;
458 }
459
460 /* Stop riding the current steed */
461 void
462 dismount_steed(reason)
463         int reason;             /* Player was thrown off etc. */
464 {
465         struct monst *mtmp;
466         struct obj *otmp;
467         coord cc;
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);
472         
473         mtmp = u.usteed;                /* make a copy of steed pointer */
474         /* Sanity check */
475         if (!mtmp)              /* Just return silently */
476             return;
477
478         /* Check the reason for dismounting */
479         otmp = which_armor(mtmp, W_SADDLE);
480         switch (reason) {
481             case DISMOUNT_THROWN:
482                 verb = "are thrown";
483             case DISMOUNT_FELL:
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;
489                 break;
490             case DISMOUNT_POLY:
491                 You("can no longer ride %s.", mon_nam(u.usteed));
492                 if (!have_spot) have_spot = landing_spot(&cc,reason,1);
493                 break;
494             case DISMOUNT_ENGULFED:
495                 /* caller displays message */
496                 break;
497             case DISMOUNT_BONES:
498                 /* hero has just died... */
499                 break;
500             case DISMOUNT_GENERIC:
501                 /* no messages, just make it so */
502                 break;
503             case DISMOUNT_BYCHOICE:
504             default:
505                 if (otmp && otmp->cursed) {
506                     You("can't.  The saddle %s cursed.",
507                         otmp->bknown ? "is" : "seems to be");
508                     otmp->bknown = TRUE;
509                     return;
510                 }
511                 if (!have_spot) {
512                     You("can't. There isn't anywhere for you to stand.");
513                     return;
514                 }
515                 if (!mtmp->mnamelth) {
516                         pline("You've been through the dungeon on %s with no name.",
517                                 an(mtmp->data->mname));
518                         if (Hallucination)
519                                 pline("It felt good to get out of the rain.");
520                 } else
521                         You("dismount %s.", mon_nam(mtmp));
522         }
523         /* While riding these refer to the steed's legs
524          * so after dismounting they refer to the player's
525          * legs once again.
526          */
527         if (repair_leg_damage) HWounded_legs = EWounded_legs = 0;
528
529         /* Release the steed and saddle */
530         u.usteed = 0;
531         u.ugallop = 0L;
532
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);
541             return;
542         }
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;
547
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)) {
551                         if (!Underwater)
552                             pline("%s falls into the %s!", Monnam(mtmp),
553                                                         surface(u.ux, u.uy));
554                         if (!is_swimmer(mdat) && !amphibious(mdat)) {
555                             killed(mtmp);
556                             adjalign(-1);
557                         }
558                     } else if (is_lava(u.ux, u.uy)) {
559                         pline("%s is pulled into the lava!", Monnam(mtmp));
560                         if (!likes_lava(mdat)) {
561                             killed(mtmp);
562                             adjalign(-1);
563                         }
564                     }
565                 }
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.
574              *
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).
582              */
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
587                      */
588                     in_steed_dismounting = TRUE;
589                     teleds(cc.x, cc.y, TRUE);
590                     in_steed_dismounting = FALSE;
591
592                     /* Put your steed in your trap */
593                     if (save_utrap)
594                         (void) mintrap(mtmp);
595                 }
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 */
602             } else {
603                 killed(mtmp);
604                 adjalign(-1);
605             }
606         }
607
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;
613             flags.botl = 1;
614             (void)encumber_msg();
615             vision_full_recalc = 1;
616         } else
617             flags.botl = 1;
618         /* polearms behave differently when not mounted */
619         if (uwep && is_pole(uwep)) unweapon = TRUE;
620         return;
621 }
622
623 void
624 place_monster(mon, x, y)
625 struct monst *mon;
626 int x, y;
627 {
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");
633         return;
634     }
635     mon->mx = x, mon->my = y;
636     level.monsters[x][y] = mon;
637 }
638
639 #endif /* STEED */
640
641 /*steed.c*/