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
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
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
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
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
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
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
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
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
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*/