1 /* SCCS Id: @(#)steed.c 3.3 2000/07/29 */
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
16 /*** Putting the saddle on ***/
17
18 /* Can this monster wear a saddle? */
19 boolean
can_saddle(mtmp)20 can_saddle(mtmp)
21 struct monst *mtmp;
22 {
23 struct permonst *ptr = mtmp->data;
24
25 return (index(steeds, ptr->mlet) && (ptr->msize >= MZ_MEDIUM) &&
26 (!humanoid(ptr) || ptr->mlet == S_CENTAUR) &&
27 !amorphous(ptr) && !noncorporeal(ptr) &&
28 !is_whirly(ptr) && !unsolid(ptr));
29 }
30
31
32 int
use_saddle(otmp)33 use_saddle(otmp)
34 struct obj *otmp;
35 {
36 struct monst *mtmp;
37 struct permonst *ptr;
38 int chance;
39 const char *s;
40
41
42 /* Can you use it? */
43 if (nohands(youmonst.data)) {
44 You("have no hands!"); /* not `body_part(HAND)' */
45 return 0;
46 } else if (!freehand()) {
47 You("have no free %s.", body_part(HAND));
48 return 0;
49 }
50
51 /* Select an animal */
52 if (u.uswallow || Underwater || !getdir((char *)0)) {
53 pline(Never_mind);
54 return 0;
55 }
56 if (!u.dx && !u.dy) {
57 pline("Saddle yourself? Very funny...");
58 return 0;
59 }
60 if (!isok(u.ux+u.dx, u.uy+u.dy) ||
61 !(mtmp = m_at(u.ux+u.dx, u.uy+u.dy)) ||
62 !canspotmon(mtmp)) {
63 pline("I see nobody there.");
64 return 1;
65 }
66
67 /* Is this a valid monster? */
68 if (mtmp->misc_worn_check & W_SADDLE ||
69 which_armor(mtmp, W_SADDLE)) {
70 pline("%s doesn't need another one.", Monnam(mtmp));
71 return 1;
72 }
73 ptr = mtmp->data;
74 if (touch_petrifies(ptr) && !Stone_resistance) {
75 char kbuf[BUFSZ];
76
77 You("touch %s.", mon_nam(mtmp));
78 if (!(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) {
79 Sprintf(kbuf, "attempting to saddle %s", a_monnam(mtmp));
80 instapetrify(kbuf);
81 }
82 }
83 if (ptr == &mons[PM_INCUBUS] || ptr == &mons[PM_SUCCUBUS]) {
84 pline("Shame on you!");
85 exercise(A_WIS, FALSE);
86 return 1;
87 }
88 if (mtmp->isminion || mtmp->isshk || mtmp->ispriest ||
89 mtmp->isgd || mtmp->iswiz) {
90 pline("I think %s would mind.", mon_nam(mtmp));
91 return 1;
92 }
93 if (!can_saddle(mtmp)) {
94 You_cant("saddle such a creature.");
95 return 1;
96 }
97
98 /* Calculate your chance */
99 chance = ACURR(A_DEX) + ACURR(A_CHA)/2 + 2*mtmp->mtame;
100 chance += u.ulevel * (mtmp->mtame ? 20 : 5);
101 if (!mtmp->mtame) chance -= 10*mtmp->m_lev;
102 if (Role_if(PM_KNIGHT))
103 chance += 20;
104 switch (P_SKILL(P_RIDING)) {
105 case P_ISRESTRICTED:
106 case P_UNSKILLED:
107 default:
108 chance -= 20; break;
109 case P_BASIC:
110 break;
111 case P_SKILLED:
112 chance += 15; break;
113 case P_EXPERT:
114 chance += 30; break;
115 }
116 if (Confusion || Fumbling || Glib)
117 chance -= 20;
118 else if (uarmg &&
119 (s = OBJ_DESCR(objects[uarmg->otyp])) != (char *)0 &&
120 !strncmp(s, "riding ", 7))
121 /* Bonus for wearing "riding" (but not fumbling) gloves */
122 chance += 10;
123 else if (uarmf &&
124 (s = OBJ_DESCR(objects[uarmf->otyp])) != (char *)0 &&
125 !strncmp(s, "riding ", 7))
126 /* ... or for "riding boots" */
127 chance += 10;
128 if (otmp->cursed)
129 chance -= 50;
130
131 /* Make the attempt */
132 if (rn2(100) < chance) {
133 You("put the saddle on %s.", mon_nam(mtmp));
134 freeinv(otmp);
135 /* mpickobj may free otmp it if merges, but we have already
136 checked for a saddle above, so no merger should happen */
137 (void) mpickobj(mtmp, otmp);
138 mtmp->misc_worn_check |= W_SADDLE;
139 otmp->owornmask = W_SADDLE;
140 otmp->leashmon = mtmp->m_id;
141 update_mon_intrinsics(mtmp, otmp, TRUE);
142 } else
143 pline("%s resists!", Monnam(mtmp));
144 return 1;
145 }
146
147
148 /*** Riding the monster ***/
149
150 /* Can we ride this monster? Caller should also check can_saddle() */
151 boolean
can_ride(mtmp)152 can_ride(mtmp)
153 struct monst *mtmp;
154 {
155 return (mtmp->mtame && humanoid(youmonst.data) &&
156 !verysmall(youmonst.data) && !bigmonst(youmonst.data) &&
157 (!Underwater || is_swimmer(mtmp->data)));
158 }
159
160
161 int
doride()162 doride()
163 {
164 boolean forcemount = FALSE;
165
166 if (u.usteed)
167 dismount_steed(DISMOUNT_BYCHOICE);
168 else if (getdir((char *)0) && isok(u.ux+u.dx, u.uy+u.dy)) {
169 #ifdef WIZARD
170 if (wizard && yn("Force the mount to succeed?") == 'y')
171 forcemount = TRUE;
172 #endif
173 return (mount_steed(m_at(u.ux+u.dx, u.uy+u.dy), forcemount));
174 } else
175 return 0;
176 return 1;
177 }
178
179
180 /* Start riding, with the given monster */
181 boolean
mount_steed(mtmp,force)182 mount_steed(mtmp, force)
183 struct monst *mtmp; /* The animal */
184 boolean force; /* Quietly force this animal */
185 {
186 struct obj *otmp;
187 char buf[BUFSZ];
188 struct permonst *ptr;
189
190
191 /* Sanity checks */
192 if (u.usteed) {
193 if (!force)
194 You("are already riding %s.", mon_nam(u.usteed));
195 return (FALSE);
196 }
197
198 /* Is the player in the right form? */
199 if (Hallucination && !force) {
200 pline("Maybe you should find a designated driver.");
201 return (FALSE);
202 }
203 if (Upolyd && (!humanoid(youmonst.data) || verysmall(youmonst.data) ||
204 bigmonst(youmonst.data))) {
205 if (!force)
206 You("won't fit on a saddle.");
207 return (FALSE);
208 }
209 if(!force && (near_capacity() > SLT_ENCUMBER)) {
210 You_cant("do that while carrying so much stuff.");
211 return (FALSE);
212 }
213
214 /* Can the player reach and see the monster? */
215 if (u.uswallow || u.ustuck || u.utrap || Punished) {
216 if (!force) {
217 if (Punished)
218 You("are unable to swing your %s over.",
219 body_part(LEG));
220 else
221 You("are stuck here for now.");
222 }
223 return (FALSE);
224 }
225 if (!mtmp || (!force && ((Blind && !Blind_telepat) ||
226 mtmp->mundetected ||
227 mtmp->m_ap_type == M_AP_FURNITURE ||
228 mtmp->m_ap_type == M_AP_OBJECT))) {
229 if (!force)
230 pline("I see nobody there.");
231 return (FALSE);
232 }
233
234 /* Is this a valid monster? */
235 otmp = which_armor(mtmp, W_SADDLE);
236 if (!otmp) {
237 pline("%s is not saddled.", Monnam(mtmp));
238 return (FALSE);
239 }
240 ptr = mtmp->data;
241 if (touch_petrifies(ptr) && !Stone_resistance) {
242 char kbuf[BUFSZ];
243
244 You("touch %s.", mon_nam(mtmp));
245 Sprintf(kbuf, "attempting to ride %s", an(mtmp->data->mname));
246 instapetrify(kbuf);
247 }
248 if (!mtmp->mtame || mtmp->isminion) {
249 if (!force)
250 pline("I think %s would mind.", mon_nam(mtmp));
251 return (FALSE);
252 }
253 if (!force && !Role_if(PM_KNIGHT) && !(--mtmp->mtame)) {
254 pline("%s resists!", Monnam(mtmp));
255 return (FALSE);
256 }
257 if (!force && Underwater && !is_swimmer(ptr)) {
258 You_cant("ride that creature while under water.");
259 return (FALSE);
260 }
261 if (!can_saddle(mtmp) || !can_ride(mtmp)) {
262 if (!force)
263 You_cant("ride such a creature.");
264 return (0);
265 }
266
267 /* Is the player impaired? */
268 if (!force && !is_floater(ptr) && !is_flyer(ptr) &&
269 Levitation && !Lev_at_will) {
270 You("cannot reach %s.", mon_nam(mtmp));
271 return (FALSE);
272 }
273 if (!force && uarm && is_metallic(uarm) &&
274 greatest_erosion(uarm)) {
275 Your("%s armor is too stiff to be able to mount %s.",
276 uarm->oeroded ? "rusty" : "corroded",
277 mon_nam(mtmp));
278 return (FALSE);
279 }
280 if (!force && (Confusion || Fumbling || Glib || Wounded_legs ||
281 otmp->cursed || (u.ulevel+mtmp->mtame < rnd(MAXULEV/2+5)))) {
282 You("slip while trying to get on %s.", mon_nam(mtmp));
283 /* Unfortunately we don't have a version of the monster-naming
284 * function that works well with "a" and "the" but ignores
285 * hallucination. Fortunately, we know the monster must be saddled
286 * at this point, and that it can't have type_is_pname(), so we
287 * don't need to worry about the special cases such a function
288 * would have to consider.
289 */
290 Sprintf(buf, "slipped while mounting a saddled %s",
291 m_monnam(mtmp));
292 losehp(rn1(5,10), buf, NO_KILLER_PREFIX);
293 return (FALSE);
294 }
295
296 /* Success */
297 if (!force) {
298 if (Levitation && !is_floater(ptr) && !is_flyer(ptr))
299 /* Must have Lev_at_will at this point */
300 pline("%s magically floats up!", Monnam(mtmp));
301 You("mount %s.", mon_nam(mtmp));
302 }
303 u.usteed = mtmp;
304 remove_monster(mtmp->mx, mtmp->my);
305 teleds(mtmp->mx, mtmp->my);
306 return (TRUE);
307 }
308
309
310 /* You and your steed have moved */
311 void
exercise_steed()312 exercise_steed()
313 {
314 if (!u.usteed)
315 return;
316
317 /* It takes many turns of riding to exercise skill */
318 if (u.urideturns++ >= 100) {
319 u.urideturns = 0;
320 use_skill(P_RIDING, 1);
321 }
322 return;
323 }
324
325
326 /* The player kicks or whips the steed */
327 void
kick_steed()328 kick_steed()
329 {
330 if (!u.usteed)
331 return;
332
333 /* Make the steed less tame and check if it resists */
334 if (u.usteed->mtame) u.usteed->mtame--;
335 if (!u.usteed->mtame || (u.ulevel+u.usteed->mtame < rnd(MAXULEV/2+5))) {
336 dismount_steed(DISMOUNT_THROWN);
337 return;
338 }
339
340 pline("%s gallops!", Monnam(u.usteed));
341 u.ugallop += rn1(20, 30);
342 return;
343 }
344
345
346 /* Stop riding the current steed */
347 void
dismount_steed(reason)348 dismount_steed(reason)
349 int reason; /* Player was thrown off etc. */
350 {
351 struct monst *mtmp;
352 struct obj *otmp;
353 coord cc;
354 const char *verb = "fall";
355 boolean repair_leg_damage = TRUE;
356 unsigned save_utrap = u.utrap;
357
358 /* Sanity checks */
359 if (!(mtmp = u.usteed))
360 /* Just return silently */
361 return;
362
363 /* Check the reason for dismounting */
364 otmp = which_armor(mtmp, W_SADDLE);
365 switch (reason) {
366 case DISMOUNT_THROWN:
367 verb = "are thrown";
368 case DISMOUNT_FELL:
369 You("%s off of %s!", verb, mon_nam(mtmp));
370 losehp(rn1(10,10), "riding accident", KILLED_BY_AN);
371 HWounded_legs += rn1(5, 5);
372 EWounded_legs |= BOTH_SIDES;
373 repair_leg_damage = FALSE;
374 break;
375 case DISMOUNT_POLY:
376 You("can no longer ride %s.", mon_nam(u.usteed));
377 break;
378 case DISMOUNT_ENGULFED:
379 /* caller displays message */
380 break;
381 case DISMOUNT_GENERIC:
382 /* no messages, just make it so */
383 break;
384 case DISMOUNT_BYCHOICE:
385 default:
386 if (otmp && otmp->cursed) {
387 You("can't. The saddle seems to be cursed.");
388 otmp->bknown = TRUE;
389 return;
390 }
391 if (!mtmp->mnamelth) {
392 pline("You've been through the dungeon on %s with no name.",
393 an(mtmp->data->mname));
394 if (Hallucination)
395 pline("It felt good to get out of the rain.");
396 } else
397 You("dismount %s.", mon_nam(mtmp));
398 }
399 /* While riding these refer to the steed's legs
400 * so after dismounting they refer to the player's
401 * legs once again.
402 */
403 if (repair_leg_damage) HWounded_legs = EWounded_legs = 0;
404
405 /* Release the steed and saddle */
406 u.usteed = 0;
407 u.ugallop = 0L;
408
409 /* Set player and steed's position. Try moving the player first */
410 if (!DEADMONSTER(mtmp)) {
411 place_monster(mtmp, u.ux, u.uy);
412 if (!u.uswallow && !u.ustuck && enexto(&cc, u.ux, u.uy, youmonst.data)) {
413 struct permonst *mdat = mtmp->data;
414
415 /* The steed may drop into water/lava */
416 if (!is_flyer(mdat) && !is_floater(mdat) && !is_clinger(mdat)) {
417 if (is_pool(u.ux, u.uy)) {
418 if (!Underwater)
419 pline("%s falls into the %s!", Monnam(mtmp),
420 surface(u.ux, u.uy));
421 if (!is_swimmer(mdat) && !amphibious(mdat)) {
422 killed(mtmp);
423 adjalign(-1);
424 }
425 } else if (is_lava(u.ux, u.uy)) {
426 pline("%s is pulled into the lava!", Monnam(mtmp));
427 if (!likes_lava(mdat)) {
428 killed(mtmp);
429 adjalign(-1);
430 }
431 }
432 }
433 /* Steed dismounting consists of two steps: being moved to another
434 * square, and descending to the floor. We have functions to do
435 * each of these activities, but they're normally called
436 * individually and include an attempt to look at or pick up the
437 * objects on the floor:
438 * teleds() --> spoteffects() --> pickup()
439 * float_down() --> pickup()
440 * We use this kludge to make sure there is only one such attempt.
441 *
442 * Clearly this is not the best way to do it. A full fix would
443 * involve having these functions not call pickup() at all, instead
444 * calling them first and calling pickup() afterwards. But it
445 * would take a lot of work to keep this change from having any
446 * unforseen side effects (for instance, you would no longer be
447 * able to walk onto a square with a hole, and autopickup before
448 * falling into the hole).
449 */
450 /* Keep steed here, move the player to cc; teleds() clears u.utrap */
451 in_steed_dismounting = TRUE;
452 teleds(cc.x, cc.y);
453 in_steed_dismounting = FALSE;
454
455 /* Put your steed in your trap */
456 if (save_utrap)
457 (void) mintrap(mtmp);
458
459 /* Couldn't... try placing the steed */
460 } else if (enexto(&cc, u.ux, u.uy, mtmp->data)) {
461 /* Keep player here, move the steed to cc */
462 rloc_to(mtmp, cc.x, cc.y);
463 /* Player stays put */
464 /* Otherwise, kill the steed */
465 } else {
466 killed(mtmp);
467 adjalign(-1);
468 }
469 }
470
471 /* Return the player to the floor */
472 (void) float_down(0L, W_SADDLE);
473 flags.botl = 1;
474 if (reason != DISMOUNT_ENGULFED) {
475 (void)encumber_msg();
476 vision_full_recalc = 1;
477 }
478 return;
479 }
480
481 void
place_monster(mon,x,y)482 place_monster(mon, x, y)
483 struct monst *mon;
484 int x, y;
485 {
486 if (mon == u.usteed ||
487 /* special case is for convoluted vault guard handling */
488 (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) {
489 impossible("placing %s onto map?",
490 (mon == u.usteed) ? "steed" : "defunct monster");
491 return;
492 }
493 mon->mx = x, mon->my = y;
494 level.monsters[x][y] = mon;
495 }
496
497 #endif /* STEED */
498
499 /*steed.c*/
500