1    | /*	SCCS Id: @(#)dokick.c	3.3	2000/04/21	*/
2    | /* Copyright (c) Izchak Miller, Mike Stephenson, Steve Linhart, 1989. */
3    | /* NetHack may be freely redistributed.  See license for details. */
4    | 
5    | #include "hack.h"
6    | #include "eshk.h"
7    | 
8    | #define is_bigfoot(x)	((x) == &mons[PM_SASQUATCH])
9    | #define martial()	(martial_bonus() || is_bigfoot(youmonst.data) || \
10   | 		(uarmf && uarmf->otyp == KICKING_BOOTS))
11   | 
12   | static NEARDATA struct rm *maploc;
13   | static NEARDATA const char *gate_str;
14   | 
15   | extern boolean notonhead;	/* for long worms */
16   | 
17   | STATIC_DCL void FDECL(kickdmg, (struct monst *, BOOLEAN_P));
18   | STATIC_DCL void FDECL(kick_monster, (XCHAR_P, XCHAR_P));
19   | STATIC_DCL int FDECL(kick_object, (XCHAR_P, XCHAR_P));
20   | STATIC_DCL char *FDECL(kickstr, (char *));
21   | STATIC_DCL void FDECL(otransit_msg, (struct obj *, BOOLEAN_P, long));
22   | STATIC_DCL void FDECL(drop_to, (coord *,SCHAR_P));
23   | 
24   | static NEARDATA struct obj *kickobj;
25   | 
26   | #define IS_SHOP(x)	(rooms[x].rtype >= SHOPBASE)
27   | 
28   | static const char kick_passes_thru[] = "kick passes harmlessly through";
29   | 
30   | STATIC_OVL void
31   | kickdmg(mon, clumsy)
32   | register struct monst *mon;
33   | register boolean clumsy;
34   | {
35   | 	register int mdx, mdy;
36   | 	register int dmg = ( ACURRSTR + ACURR(A_DEX) + ACURR(A_CON) )/ 15;
37   | 	int kick_skill = P_NONE;
38   | 	int blessed_foot_damage = 0;
39   | 
40   | 	if (uarmf && uarmf->otyp == KICKING_BOOTS)
41   | 	    dmg += 5;
42   | 
43   | 	/* excessive wt affects dex, so it affects dmg */
44   | 	if (clumsy) dmg /= 2;
45   | 
46   | 	/* kicking a dragon or an elephant will not harm it */
47   | 	if (thick_skinned(mon->data)) dmg = 0;
48   | 
49   | 	/* attacking a shade is useless */
50   | 	if (mon->data == &mons[PM_SHADE])
51   | 	    dmg = 0;
52   | 
53   | 	if ((is_undead(mon->data) || is_demon(mon->data)) && uarmf &&
54   | 		uarmf->blessed)
55   | 	    blessed_foot_damage = 1;
56   | 
57   | 	if (mon->data == &mons[PM_SHADE] && !blessed_foot_damage) {
58   | 	    pline_The("%s.", kick_passes_thru);
59   | 	    /* doesn't exercise skill or abuse alignment or frighten pet,
60   | 	       and shades have no passive counterattack */
61   | 	    return;
62   | 	}
63   | 
64   | 	if(mon->m_ap_type) seemimic(mon);
65   | 
66   | 	/* it is unchivalrous to attack the defenseless or from behind */
67   | 	if (Role_if(PM_KNIGHT) &&
68   | 		u.ualign.type == A_LAWFUL && u.ualign.record > -10 &&
69   | 		(!mon->mcanmove || mon->msleeping || mon->mflee)) {
70   | 	    You_feel("like a caitiff!");
71   | 	    adjalign(-1);
72   | 	}
73   | 
74   | 	/* squeeze some guilt feelings... */
75   | 	if(mon->mtame) {
76   | 	    abuse_dog(mon);
77   | 	    mon->mflee = mon->mtame ? 1 : 0;
78   | #ifdef HISX
79   | 	    mon->mfleetim = mon->mfleetim + (dmg ? rnd(dmg) : 1);
80   | #else
81   | 	    mon->mfleetim += (dmg ? rnd(dmg) : 1);
82   | #endif
83   | 	}
84   | 
85   | 	if (dmg > 0) {
86   | 		/* convert potential damage to actual damage */
87   | 		dmg = rnd(dmg);
88   | 		if (martial()) {
89   | 		    if (dmg > 1) kick_skill = P_MARTIAL_ARTS;
90   | 		    dmg += rn2(ACURR(A_DEX)/2 + 1);
91   | 		}
92   | 		/* a good kick exercises your dex */
93   | 		exercise(A_DEX, TRUE);
94   | 	}
95   | 	if (blessed_foot_damage) dmg += rnd(4);
96   | 	if (uarmf) dmg += uarmf->spe;
97   | 	dmg += u.udaminc;	/* add ring(s) of increase damage */
98   | 	if (dmg > 0)
99   | 		mon->mhp -= dmg;
100  | 	if(mon->mhp > 0 && martial() && !bigmonst(mon->data) && !rn2(3)
101  | 			&& mon->mcanmove && mon != u.ustuck) {
102  | 		/* see if the monster has a place to move into */
103  | 		mdx = mon->mx + u.dx;
104  | 		mdy = mon->my + u.dy;
105  | 		if(goodpos(mdx, mdy, mon)) {
106  | 			pline("%s reels from the blow.", Monnam(mon));
107  | 			if (!m_in_out_region(mon, mdx, mdy)) {
108  | 			    remove_monster(mon->mx, mon->my);
109  | 			    newsym(mon->mx, mon->my);
110  | 			    place_monster(mon, mdx, mdy);
111  | 			    newsym(mon->mx, mon->my);
112  | 			    set_apparxy(mon);
113  | 			}
114  | 		}
115  | 	}
116  | 
117  | 	(void) passive(mon, TRUE, mon->mhp > 0, AT_KICK);
118  | 	if (mon->mhp <= 0) killed(mon);
119  | 
120  | 	/* may bring up a dialog, so put this after all messages */
121  | 	if (kick_skill != P_NONE)	/* exercise proficiency */
122  | 	    use_skill(kick_skill, 1);
123  | }
124  | 
125  | STATIC_OVL void
126  | kick_monster(x, y)
127  | register xchar x, y;
128  | {
129  | 	register boolean clumsy = FALSE;
130  | 	register struct monst *mon = m_at(x, y);
131  | 	register int i, j;
132  | 
133  | 	bhitpos.x = x;
134  | 	bhitpos.y = y;
135  | 	if (attack_checks(mon, (struct obj *)0)) return;
136  | 	setmangry(mon);
137  | 
138  | 	/* Kick attacks by kicking monsters are normal attacks, not special.
139  | 	 * This is almost always worthless, since you can either take one turn
140  | 	 * and do all your kicks, or else take one turn and attack the monster
141  | 	 * normally, getting all your attacks _including_ all your kicks.
142  | 	 * If you have >1 kick attack, you get all of them.
143  | 	 */
144  | 	if (Upolyd && attacktype(youmonst.data, AT_KICK)) {
145  | 	    struct attack *uattk;
146  | 	    int sum;
147  | 	    schar tmp = find_roll_to_hit(mon);
148  | 
149  | 	    for (i = 0; i < NATTK; i++) {
150  | 		/* first of two kicks might have provoked counterattack
151  | 		   that has incapacitated the hero (ie, floating eye) */
152  | 		if (multi < 0) break;
153  | 
154  | 		uattk = &youmonst.data->mattk[i];
155  | 		/* we only care about kicking attacks here */
156  | 		if (uattk->aatyp != AT_KICK) continue;
157  | 
158  | 		if (mon->data == &mons[PM_SHADE] &&
159  | 			(!uarmf || !uarmf->blessed)) {
160  | 		    /* doesn't matter whether it would have hit or missed,
161  | 		       and shades have no passive counterattack */
162  | 		    Your("%s %s.", kick_passes_thru, mon_nam(mon));
163  | 		    break;	/* skip any additional kicks */
164  | 		} else if (tmp > rnd(20)) {
165  | 		    You("kick %s.", mon_nam(mon));
166  | 		    sum = damageum(mon, uattk);
167  | 		    (void)passive(mon, (boolean)(sum > 0), (sum != 2), AT_KICK);
168  | 		} else {
169  | 		    missum(mon, uattk);
170  | 		    (void)passive(mon, 0, 1, AT_KICK);
171  | 		}
172  | 	    }
173  | 	    return;
174  | 	}
175  | 
176  | 	if(Levitation && !rn2(3) && verysmall(mon->data) &&
177  | 	   !is_flyer(mon->data)) {
178  | 		pline("Floating in the air, you miss wildly!");
179  | 		exercise(A_DEX, FALSE);
180  | 		(void) passive(mon, FALSE, 1, AT_KICK);
181  | 		return;
182  | 	}
183  | 
184  | 	i = -inv_weight();
185  | 	j = weight_cap();
186  | 
187  | 	if(i < (j*3)/10) {
188  | 		if(!rn2((i < j/10) ? 2 : (i < j/5) ? 3 : 4)) {
189  | 			if(martial() && !rn2(2)) goto doit;
190  | 			Your("clumsy kick does no damage.");
191  | 			(void) passive(mon, FALSE, 1, AT_KICK);
192  | 			return;
193  | 		}
194  | 		if(i < j/10) clumsy = TRUE;
195  | 		else if(!rn2((i < j/5) ? 2 : 3)) clumsy = TRUE;
196  | 	}
197  | 
198  | 	if(Fumbling) clumsy = TRUE;
199  | 
200  | 	else if(uarm && objects[uarm->otyp].oc_bulky && ACURR(A_DEX) < rnd(25))
201  | 		clumsy = TRUE;
202  | doit:
203  | 	You("kick %s.", mon_nam(mon));
204  | 	if(!rn2(clumsy ? 3 : 4) && (clumsy || !bigmonst(mon->data)) &&
205  | 	   mon->mcansee && !mon->mtrapped && !thick_skinned(mon->data) &&
206  | 	   mon->data->mlet != S_EEL && haseyes(mon->data) && mon->mcanmove &&
207  | 	   !mon->mstun && !mon->mconf && !mon->msleeping &&
208  | 	   mon->data->mmove >= 12) {
209  | 		if(!nohands(mon->data) && !rn2(martial() ? 5 : 3)) {
210  | 		    pline("%s blocks your %skick.", Monnam(mon),
211  | 				clumsy ? "clumsy " : "");
212  | 		    (void) passive(mon, FALSE, 1, AT_KICK);
213  | 		    return;
214  | 		} else {
215  | 		    mnexto(mon);
216  | 		    if(mon->mx != x || mon->my != y) {
217  | 			pline("%s %s, %s evading your %skick.", Monnam(mon),
218  | 				(can_teleport(mon->data) ? "teleports" :
219  | 				 is_floater(mon->data) ? "floats" :
220  | 				 is_flyer(mon->data) ? "flutters" :
221  | 				 (nolimbs(mon->data) || slithy(mon->data)) ?
222  | 					"slides" : "jumps"),
223  | 				clumsy ? "easily" : "nimbly",
224  | 				clumsy ? "clumsy " : "");
225  | 			(void) passive(mon, FALSE, 1, AT_KICK);
226  | 			return;
227  | 		    }
228  | 		}
229  | 	}
230  | 	kickdmg(mon, clumsy);
231  | }
232  | 
233  | /*
234  |  *  Return TRUE if caught (the gold taken care of), FALSE otherwise.
235  |  *  The gold object is *not* attached to the fobj chain!
236  |  */
237  | boolean
238  | ghitm(mtmp, gold)
239  | register struct monst *mtmp;
240  | register struct obj *gold;
241  | {
242  | 	if(!likes_gold(mtmp->data) && !mtmp->isshk && !mtmp->ispriest
243  | 			&& !is_mercenary(mtmp->data)) {
244  | 		wakeup(mtmp);
245  | 	} else if (!mtmp->mcanmove) {
246  | 		/* too light to do real damage */
247  | 		if (canseemon(mtmp))
248  | 		    pline_The("gold hits %s.", mon_nam(mtmp));
249  | 	} else {
250  | 		mtmp->msleeping = 0;
251  | 		mtmp->meating = 0;
252  | 		if(!rn2(4)) setmangry(mtmp); /* not always pleasing */
253  | 
254  | 		/* greedy monsters catch gold */
255  | 		if (cansee(mtmp->mx, mtmp->my))
256  | 		    pline("%s catches the gold.", Monnam(mtmp));
257  | 		mtmp->mgold += gold->quan;
258  | 		if (mtmp->isshk) {
259  | 			long robbed = ESHK(mtmp)->robbed;
260  | 
261  | 			if (robbed) {
262  | 				robbed -= gold->quan;
263  | 				if (robbed < 0) robbed = 0;
264  | 				pline_The("amount %scovers %s recent losses.",
265  | 				      !robbed ? "" : "partially ",
266  | 				      his[mtmp->female]);
267  | 				ESHK(mtmp)->robbed = robbed;
268  | 				if(!robbed)
269  | 					make_happy_shk(mtmp, FALSE);
270  | 			} else {
271  | 				if(mtmp->mpeaceful) {
272  | 				    ESHK(mtmp)->credit += gold->quan;
273  | 				    You("have %ld zorkmid%s in credit.",
274  | 					ESHK(mtmp)->credit,
275  | 					plur(ESHK(mtmp)->credit));
276  | 				} else verbalize("Thanks, scum!");
277  | 			}
278  | 		} else if (mtmp->ispriest) {
279  | 			if (mtmp->mpeaceful)
280  | 			    verbalize("Thank you for your contribution.");
281  | 			else verbalize("Thanks, scum!");
282  | 		} else if (is_mercenary(mtmp->data)) {
283  | 		    long goldreqd = 0L;
284  | 
285  | 		    if (rn2(3)) {
286  | 			if (mtmp->data == &mons[PM_SOLDIER])
287  | 			   goldreqd = 100L;
288  | 			else if (mtmp->data == &mons[PM_SERGEANT])
289  | 			   goldreqd = 250L;
290  | 			else if (mtmp->data == &mons[PM_LIEUTENANT])
291  | 			   goldreqd = 500L;
292  | 			else if (mtmp->data == &mons[PM_CAPTAIN])
293  | 			   goldreqd = 750L;
294  | 
295  | 			if (goldreqd) {
296  | 			   if (gold->quan > goldreqd +
297  | 				(u.ugold + u.ulevel*rn2(5))/ACURR(A_CHA))
298  | 			    mtmp->mpeaceful = TRUE;
299  | 			}
300  | 		     }
301  | 		     if (mtmp->mpeaceful)
302  | 			    verbalize("That should do.  Now beat it!");
303  | 		     else verbalize("That's not enough, coward!");
304  | 		 }
305  | 
306  | 		dealloc_obj(gold);
307  | 		return(1);
308  | 	}
309  | 	return(0);
310  | }
311  | 
312  | STATIC_OVL int
313  | kick_object(x, y)
314  | xchar x, y;
315  | {
316  | 	int range;
317  | 	register struct monst *mon, *shkp;
318  | 	register struct obj *otmp;
319  | 	struct trap *trap;
320  | 	char bhitroom;
321  | 	boolean costly, insider, isgold, slide = FALSE;
322  | 
323  | 	/* if a pile, the "top" object gets kicked */
324  | 	kickobj = level.objects[x][y];
325  | 
326  | 	/* kickobj should always be set due to conditions of call */
327  | 	if(!kickobj || kickobj->otyp == BOULDER
328  | 			|| kickobj == uball || kickobj == uchain)
329  | 		return(0);
330  | 
331  | 	if ((trap = t_at(x,y)) != 0 &&
332  | 			(((trap->ttyp == PIT ||
333  | 			   trap->ttyp == SPIKED_PIT) && !Passes_walls) ||
334  | 			 trap->ttyp == WEB)) {
335  | 		if (!trap->tseen) find_trap(trap);
336  | 		You_cant("kick %s that's in a %s!", something,
337  | 			 Hallucination ? "tizzy" :
338  | 			 (trap->ttyp == WEB) ? "web" : "pit");
339  | 		return 1;
340  | 	}
341  | 
342  | 	if(Fumbling && !rn2(3)) {
343  | 		Your("clumsy kick missed.");
344  | 		return(1);
345  | 	}
346  | 
347  | 	if(kickobj->otyp == CORPSE && touch_petrifies(&mons[kickobj->corpsenm])
348  | 			&& !Stone_resistance && !uarmf) {
349  | 		char kbuf[BUFSZ];
350  | 
351  | 		You("kick the %s corpse with your bare %s.",
352  | 				mons[kickobj->corpsenm].mname, makeplural(body_part(FOOT)));
353  | 	    if (!(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) {
354  | 		You("turn to stone...");
355  | 		killer_format = KILLED_BY;
356  | 		/* KMH -- otmp should be kickobj */
357  | 		Sprintf(kbuf, "kicking a %s corpse without boots",
358  | 			mons[kickobj->corpsenm].mname);
359  | 		killer = kbuf;
360  | 		done(STONING);
361  | 	    }
362  | 	}
363  | 
364  | 	/* range < 2 means the object will not move.	*/
365  | 	/* maybe dexterity should also figure here.     */
366  | 	range = (int)((ACURRSTR)/2 - kickobj->owt/40);
367  | 
368  | 	if(martial()) range += rnd(3);
369  | 
370  | 	if (is_pool(x, y)) {
371  | 	    /* you're in the water too; significantly reduce range */
372  | 	    range = range / 3 + 1;	/* {1,2}=>1, {3,4,5}=>2, {6,7,8}=>3 */
373  | 	} else {
374  | 	    if (is_ice(x, y)) range += rnd(3),  slide = TRUE;
375  | 	    if (kickobj->greased) range += rnd(3),  slide = TRUE;
376  | 	}
377  | 
378  | 	/* Mjollnir is magically too heavy to kick */
379  | 	if(kickobj->oartifact == ART_MJOLLNIR) range = 1;
380  | 
381  | 	/* see if the object has a place to move into */
382  | 	if(!ZAP_POS(levl[x+u.dx][y+u.dy].typ) || closed_door(x+u.dx, y+u.dy))
383  | 		range = 1;
384  | 
385  | 	costly = ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) &&
386  | 				    costly_spot(x, y));
387  | 	insider = (*u.ushops && inside_shop(u.ux, u.uy) &&
388  | 				    *in_rooms(x, y, SHOPBASE) == *u.ushops);
389  | 
390  | 	/* a box gets a chance of breaking open here */
391  | 	if(Is_box(kickobj)) {
392  | 		boolean otrp = kickobj->otrapped;
393  | 		struct obj *otmp2;
394  | 		long loss = 0L;
395  | 
396  | 		if(range < 2) pline("THUD!");
397  | 
398  | 		for(otmp = kickobj->cobj; otmp; otmp = otmp2) {
399  | 			const char *result = (char *)0;
400  | 
401  | 			otmp2 = otmp->nobj;
402  | 			if (objects[otmp->otyp].oc_material == GLASS
403  | 			    && otmp->oclass != GEM_CLASS
404  | 			    && !obj_resists(otmp, 33, 100)) {
405  | 				result = "shatter";
406  | 			} else if (otmp->otyp == EGG && !rn2(3)) {
407  | 				result = "cracking";
408  | 			}
409  | 			if (result) {
410  | 				You_hear("a muffled %s.",result);
411  | 				if(costly) loss += stolen_value(otmp, x, y,
412  | 					    (boolean)shkp->mpeaceful, TRUE);
413  | 				if (otmp->quan > 1L)
414  | 					useup(otmp);
415  | 				else {
416  | 					obj_extract_self(otmp);
417  | 					obfree(otmp, (struct obj *) 0);
418  | 				}
419  | 			}
420  | 		}
421  | 		if(costly && loss) {
422  | 		    if(!insider) {
423  | 			You("caused %ld zorkmids worth of damage!", loss);
424  | 			make_angry_shk(shkp, x, y);
425  | 		    } else {
426  | 			You("owe %s %ld zorkmids for objects destroyed.",
427  | 			    mon_nam(shkp), loss);
428  | 		    }
429  | 		}
430  | 
431  | 		if (kickobj->olocked) {
432  | 		    if (!rn2(5) || (martial() && !rn2(2))) {
433  | 			You("break open the lock!");
434  | 			kickobj->olocked = 0;
435  | 			kickobj->obroken = 1;
436  | 			if (otrp) (void) chest_trap(kickobj, LEG, FALSE);
437  | 			return(1);
438  | 		    }
439  | 		} else {
440  | 		    if (!rn2(3) || (martial() && !rn2(2))) {
441  | 			pline_The("lid slams open, then falls shut.");
442  | 			if (otrp) (void) chest_trap(kickobj, LEG, FALSE);
443  | 			return(1);
444  | 		    }
445  | 		}
446  | 		if(range < 2) return(1);
447  | 		/* else let it fall through to the next cases... */
448  | 	}
449  | 
450  | 	/* fragile objects should not be kicked */
451  | 	if (hero_breaks(kickobj, kickobj->ox, kickobj->oy, FALSE)) return 1;
452  | 
453  | 	if (IS_ROCK(levl[x][y].typ) || closed_door(x, y)) {
454  | 		if ((!martial() && rn2(20) > ACURR(A_DEX))
455  | 				 || IS_ROCK(levl[u.ux][u.uy].typ)
456  | 				 || closed_door(u.ux, u.uy)) {
457  | 			if (Blind) pline("It doesn't come loose.");
458  | 			else pline("%s do%sn't come loose.",
459  | 				The(distant_name(kickobj, xname)),
460  | 				(kickobj->quan == 1L) ? "es" : "");
461  | 			return(!rn2(3) || martial());
462  | 		}
463  | 		if (Blind) pline("It comes loose.");
464  | 		else pline("%s come%s loose.",
465  | 			   The(distant_name(kickobj, xname)),
466  | 			   (kickobj->quan == 1L) ? "s" : "");
467  | 		obj_extract_self(kickobj);
468  | 		newsym(x, y);
469  | 		if (costly && (!costly_spot(u.ux, u.uy)
470  | 			       || !index(u.urooms, *in_rooms(x, y, SHOPBASE))))
471  | 			addtobill(kickobj, FALSE, FALSE, FALSE);
472  | 		if(!flooreffects(kickobj,u.ux,u.uy,"fall")) {
473  | 		    place_object(kickobj, u.ux, u.uy);
474  | 		    stackobj(kickobj);
475  | 		    newsym(u.ux, u.uy);
476  | 		}
477  | 		return(1);
478  | 	}
479  | 
480  | 	isgold = (kickobj->oclass == GOLD_CLASS);
481  | 
482  | 	/* too heavy to move.  range is calculated as potential distance from
483  | 	 * player, so range == 2 means the object may move up to one square
484  | 	 * from its current position
485  | 	 */
486  | 	if(range < 2 || (isgold && kickobj->quan > 300L)) {
487  | 	    if(!Is_box(kickobj)) pline("Thump!");
488  | 	    return(!rn2(3) || martial());
489  | 	}
490  | 
491  | 	if (kickobj->quan > 1L && !isgold) (void) splitobj(kickobj, 1L);
492  | 
493  | 	if (slide && !Blind)
494  | 	    pline("Whee!  %s slide%s across the %s.", Doname2(kickobj),
495  | 		kickobj->quan > 1L ? "" : "s",
496  | 		surface(x,y));
497  | 
498  | 	obj_extract_self(kickobj);
499  | 	(void) snuff_candle(kickobj);
500  | 	newsym(x, y);
501  | 	mon = bhit(u.dx, u.dy, range, KICKED_WEAPON,
502  | 		   (int FDECL((*),(MONST_P,OBJ_P)))0,
503  | 		   (int FDECL((*),(OBJ_P,OBJ_P)))0,
504  | 		   kickobj);
505  | 
506  | 	if(mon) {
507  | 	    if (mon->isshk &&
508  | 		    kickobj->where == OBJ_MINVENT && kickobj->ocarry == mon)
509  | 		return 1;	/* alert shk caught it */
510  | 	    notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y);
511  | 	    if (isgold ? ghitm(mon, kickobj) :	/* caught? */
512  | 		    thitmonst(mon, kickobj))	/* hit && used up? */
513  | 		return(1);
514  | 	}
515  | 
516  | 	/* the object might have fallen down a hole */
517  | 	if (kickobj->where == OBJ_MIGRATING)
518  | 	    return 1;
519  | 
520  | 	bhitroom = *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE);
521  | 	if (costly && (!costly_spot(bhitpos.x, bhitpos.y) ||
522  | 			*in_rooms(x, y, SHOPBASE) != bhitroom)) {
523  | 	    if(isgold)
524  | 		costly_gold(x, y, kickobj->quan);
525  | 	    else (void)stolen_value(kickobj, x, y,
526  | 				    (boolean)shkp->mpeaceful, FALSE);
527  | 	}
528  | 
529  | 	if(flooreffects(kickobj,bhitpos.x,bhitpos.y,"fall")) return(1);
530  | 	place_object(kickobj, bhitpos.x, bhitpos.y);
531  | 	stackobj(kickobj);
532  | 	newsym(kickobj->ox, kickobj->oy);
533  | 	return(1);
534  | }
535  | 
536  | STATIC_OVL char *
537  | kickstr(buf)
538  | char *buf;
539  | {
540  | 	const char *what;
541  | 
542  | 	if (kickobj) what = distant_name(kickobj,doname);
543  | 	else if (IS_DOOR(maploc->typ)) what = "a door";
544  | 	else if (IS_STWALL(maploc->typ)) what = "a wall";
545  | 	else if (IS_ROCK(maploc->typ)) what = "a rock";
546  | 	else if (IS_THRONE(maploc->typ)) what = "a throne";
547  | 	else if (IS_FOUNTAIN(maploc->typ)) what = "a fountain";
548  | 	else if (IS_GRAVE(maploc->typ)) what = "a headstone";
549  | 	else if (IS_TREE(maploc->typ)) what = "a tree";
550  | #ifdef SINKS
551  | 	else if (IS_SINK(maploc->typ)) what = "a sink";
552  | #endif
553  | 	else if (IS_ALTAR(maploc->typ)) what = "an altar";
554  | 	else if (IS_DRAWBRIDGE(maploc->typ)) what = "the drawbridge";
555  | 	else if (maploc->typ == STAIRS) what = "the stairs";
556  | 	else if (maploc->typ == LADDER) what = "a ladder";
557  | 	else if (maploc->typ == IRONBARS) what = "an iron bar";
558  | 	else what = "something weird";
559  | 	return strcat(strcpy(buf, "kicking "), what);
560  | }
561  | 
562  | int
563  | dokick()
564  | {
565  | 	register int x, y;
566  | 	int avrg_attrib;
567  | 	register struct monst *mtmp;
568  | 	s_level *slev;
569  | 	boolean no_kick = FALSE;
570  | 	char buf[BUFSZ];
571  | 
572  | 	if (nolimbs(youmonst.data) || slithy(youmonst.data)) {
573  | 		You("have no legs to kick with.");
574  | 		no_kick = TRUE;
575  | 	} else if (verysmall(youmonst.data)) {
576  | 		You("are too small to do any kicking.");
577  | 		no_kick = TRUE;
578  | #ifdef STEED
579  | 	} else if (u.usteed) {
580  | 		if (yn_function("Kick your steed?", ynchars, 'y') == 'y') {
581  | 		    You("kick %s.", mon_nam(u.usteed));
582  | 		    kick_steed();
583  | 		    return 1;
584  | 		} else {
585  | 		    return 0;
586  | 		}
587  | #endif
588  | 	} else if (Wounded_legs) {
589  | 		/* note: jump() has similar code */
590  | 		long wl = (EWounded_legs & BOTH_SIDES);
591  | 		const char *bp = body_part(LEG);
592  | 
593  | 		if (wl == BOTH_SIDES) bp = makeplural(bp);
594  | 		Your("%s%s %s in no shape for kicking.",
595  | 		     (wl == LEFT_SIDE) ? "left " :
596  | 			(wl == RIGHT_SIDE) ? "right " : "",
597  | 		     bp, (wl == BOTH_SIDES) ? "are" : "is");
598  | 		no_kick = TRUE;
599  | 	} else if (near_capacity() > SLT_ENCUMBER) {
600  | 		Your("load is too heavy to balance yourself for a kick.");
601  | 		no_kick = TRUE;
602  | 	} else if (u.uinwater && !rn2(2)) {
603  | 		Your("slow motion kick doesn't hit anything.");
604  | 		no_kick = TRUE;
605  | 	} else if (u.utrap) {
606  | 		switch (u.utraptype) {
607  | 		    case TT_PIT:
608  | 			pline("There's not enough room to kick down here.");
609  | 			break;
610  | 		    case TT_WEB:
611  | 		    case TT_BEARTRAP:
612  | 			You_cant("move your %s!", body_part(LEG));
613  | 			break;
614  | 		    default:
615  | 			break;
616  | 		}
617  | 		no_kick = TRUE;
618  | 	}
619  | 
620  | 	if (no_kick) {
621  | 		/* ignore direction typed before player notices kick failed */
622  | 		display_nhwindow(WIN_MESSAGE, TRUE);	/* --More-- */
623  | 		return 0;
624  | 	}
625  | 
626  | 	if(!getdir((char *)0)) return(0);
627  | 	if(!u.dx && !u.dy) return(0);
628  | 
629  | 	x = u.ux + u.dx;
630  | 	y = u.uy + u.dy;
631  | 
632  | 	/* KMH -- Kicking boots always succeed */
633  | 	if (uarmf && uarmf->otyp == KICKING_BOOTS)
634  | 	    avrg_attrib = 99;
635  | 	else
636  | 	    avrg_attrib = (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3;
637  | 
638  | 	if(u.uswallow) {
639  | 		switch(rn2(3)) {
640  | 		case 0:  You_cant("move your %s!", body_part(LEG));
641  | 			 break;
642  | 		case 1:  if (is_animal(u.ustuck->data)) {
643  | 				pline("%s burps loudly.", Monnam(u.ustuck));
644  | 				break;
645  | 			 }
646  | 		default: Your("feeble kick has no effect."); break;
647  | 		}
648  | 		return(1);
649  | 	}
650  | 	if (Levitation) {
651  | 		int xx, yy;
652  | 
653  | 		xx = u.ux - u.dx;
654  | 		yy = u.uy - u.dy;
655  | 		/* doors can be opened while levitating, so they must be
656  | 		 * reachable for bracing purposes
657  | 		 * Possible extension: allow bracing against stuff on the side?
658  | 		 */
659  | 		if (isok(xx,yy) && !IS_ROCK(levl[xx][yy].typ) &&
660  | 			!IS_DOOR(levl[xx][yy].typ) &&
661  | 			(!Is_airlevel(&u.uz) || !OBJ_AT(xx,yy))) {
662  | 		    You("have nothing to brace yourself against.");
663  | 		    return(0);
664  | 		}
665  | 	}
666  | 
667  | 	wake_nearby();
668  | 	u_wipe_engr(2);
669  | 
670  | 	maploc = &levl[x][y];
671  | 
672  | 	/* The next five tests should stay in    */
673  | 	/* their present order: monsters, pools, */
674  | 	/* objects, non-doors, doors.		 */
675  | 
676  | 	if(MON_AT(x, y)) {
677  | 		struct permonst *mdat;
678  | 
679  | 		mtmp = m_at(x, y);
680  | 		mdat = mtmp->data;
681  | 		if (!mtmp->mpeaceful || !canspotmon(mtmp))
682  | 		    flags.forcefight = TRUE; /* attack even if invisible */
683  | 		kick_monster(x, y);
684  | 		flags.forcefight = FALSE;
685  | 		/* see comment in attack_checks() */
686  | 		if (!canspotmon(mtmp) &&
687  | 		    /* check x and y; a monster that evades your kick by
688  | 		       jumping to an unseen square doesn't leave an I behind */
689  | 		    mtmp->mx == x && mtmp->my == y &&
690  | 		    !glyph_is_invisible(levl[x][y].glyph) &&
691  | 		    !(u.uswallow && mtmp == u.ustuck))
692  | 			map_invisible(x, y);
693  | 		if((Is_airlevel(&u.uz) || Levitation) && flags.move) {
694  | 		    int range;
695  | 
696  | 		    range = ((int)youmonst.data->cwt + (weight_cap() + inv_weight()));
697  | 		    if (range < 1) range = 1; /* divide by zero avoidance */
698  | 		    range = (3*(int)mdat->cwt) / range;
699  | 
700  | 		    if(range < 1) range = 1;
701  | 		    hurtle(-u.dx, -u.dy, range, TRUE);
702  | 		}
703  | 		return(1);
704  | 	}
705  | 	if (glyph_is_invisible(levl[x][y].glyph)) {
706  | 		unmap_object(x, y);
707  | 		newsym(x, y);
708  | 	}
709  | 	if (is_pool(x, y) ^ !!u.uinwater) {
710  | 		/* objects normally can't be removed from water by kicking */
711  | 		You("splash some water around.");
712  | 		return 1;
713  | 	}
714  | 
715  | 	kickobj = (struct obj *)0;
716  | 	if (OBJ_AT(x, y) &&
717  | 	    (!Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)
718  | 	     || sobj_at(BOULDER,x,y))) {
719  | 		if(kick_object(x, y)) {
720  | 		    if(Is_airlevel(&u.uz))
721  | 			hurtle(-u.dx, -u.dy, 1, TRUE); /* assume it's light */
722  | 		    return(1);
723  | 		}
724  | 		goto ouch;
725  | 	}
726  | 
727  | 	if(!IS_DOOR(maploc->typ)) {
728  | 		if(maploc->typ == SDOOR) {
729  | 		    if(!Levitation && rn2(30) < avrg_attrib) {
730  | 			cvt_sdoor_to_door(maploc);	/* ->typ = DOOR */
731  | 			pline("Crash!  %s a secret door!",
732  | 			      /* don't "kick open" when it's locked
733  | 				 unless it also happens to be trapped */
734  | 			(maploc->doormask & (D_LOCKED|D_TRAPPED)) == D_LOCKED ?
735  | 			      "Your kick uncovers" : "You kick open");
736  | 			exercise(A_DEX, TRUE);
737  | 			if(maploc->doormask & D_TRAPPED) {
738  | 			    maploc->doormask = D_NODOOR;
739  | 			    b_trapped("door", FOOT);
740  | 			} else if (maploc->doormask != D_NODOOR &&
741  | 				   !(maploc->doormask & D_LOCKED))
742  | 			    maploc->doormask = D_ISOPEN;
743  | 			if (Blind)
744  | 			    feel_location(x,y);	/* we know it's gone */
745  | 			else
746  | 			    newsym(x,y);
747  | 			if (maploc->doormask == D_ISOPEN ||
748  | 			    maploc->doormask == D_NODOOR)
749  | 			    unblock_point(x,y);	/* vision */
750  | 			return(1);
751  | 		    } else goto ouch;
752  | 		}
753  | 		if(maploc->typ == SCORR) {
754  | 		    if(!Levitation && rn2(30) < avrg_attrib) {
755  | 			pline("Crash!  You kick open a secret passage!");
756  | 			exercise(A_DEX, TRUE);
757  | 			maploc->typ = CORR;
758  | 			if (Blind)
759  | 			    feel_location(x,y);	/* we know it's gone */
760  | 			else
761  | 			    newsym(x,y);
762  | 			unblock_point(x,y);	/* vision */
763  | 			return(1);
764  | 		    } else goto ouch;
765  | 		}
766  | 		if(IS_THRONE(maploc->typ)) {
767  | 		    register int i;
768  | 		    if(Levitation) goto dumb;
769  | 		    if((Luck < 0 || maploc->doormask) && !rn2(3)) {
770  | 			maploc->typ = ROOM;
771  | 			maploc->doormask = 0; /* don't leave loose ends.. */
772  | 			(void) mkgold((long)rnd(200), x, y);
773  | 			if (Blind)
774  | 			    pline("CRASH!  You destroy it.");
775  | 			else {
776  | 			    pline("CRASH!  You destroy the throne.");
777  | 			    newsym(x, y);
778  | 			}
779  | 			exercise(A_DEX, TRUE);
780  | 			return(1);
781  | 		    } else if(Luck > 0 && !rn2(3) && !maploc->looted) {
782  | 			(void) mkgold((long) rn1(201, 300), x, y);
783  | 			i = Luck + 1;
784  | 			if(i > 6) i = 6;
785  | 			while(i--) (void) mkobj_at(GEM_CLASS, x, y, TRUE);
786  | 			if (Blind)
787  | 			    You("kick %s loose!", something);
788  | 			else {
789  | 			    You("kick loose some ornamental coins and gems!");
790  | 			    newsym(x, y);
791  | 			}
792  | 			/* prevent endless milking */
793  | 			maploc->looted = T_LOOTED;
794  | 			return(1);
795  | 		    } else if (!rn2(4)) {
796  | 			if(dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)) {
797  | 			    fall_through(FALSE);
798  | 			    return(1);
799  | 			} else goto ouch;
800  | 		    }
801  | 		    goto ouch;
802  | 		}
803  | 		if(IS_ALTAR(maploc->typ)) {
804  | 		    if(Levitation) goto dumb;
805  | 		    You("kick %s.",(Blind ? something : "the altar"));
806  | 		    if(!rn2(3)) goto ouch;
807  | 		    altar_wrath(x, y);
808  | 		    exercise(A_DEX, TRUE);
809  | 		    return(1);
810  | 		}
811  | 		if(IS_FOUNTAIN(maploc->typ)) {
812  | 		    if(Levitation) goto dumb;
813  | 		    You("kick %s.",(Blind ? something : "the fountain"));
814  | 		    if(!rn2(3)) goto ouch;
815  | 		    /* make metal boots rust */
816  | 		    if(uarmf && rn2(3))
817  | 			if (!rust_dmg(uarmf, "metal boots", 1, FALSE, &youmonst)) {
818  | 				Your("boots get wet.");
819  | 				/* could cause short-lived fumbling here */
820  | 			}
821  | 		    exercise(A_DEX, TRUE);
822  | 		    return(1);
823  | 		}
824  | 		if(IS_GRAVE(maploc->typ))
825  | 		    goto ouch;
826  | 		if(IS_TREE(maploc->typ)) {
827  | 		    struct obj *treefruit;
828  | 		    if (rn2(8)) goto ouch;
829  | 		    /* fruit or trouble ? */
830  | 		    if (!rn2(2) && !(maploc->looted & TREE_LOOTED) &&
831  | 			  (treefruit = rnd_treefruit_at(x, y))) {
832  | 			treefruit->quan = (long)(8 - rnl(8));
833  | 			if (treefruit->quan > 1L)
834  | 				pline("Some %s fall from the tree!", xname(treefruit));
835  | 			else
836  | 				pline("%s falls from the tree!", An(xname(treefruit)));
837  | 			scatter(x,y,2,MAY_HIT,treefruit);
838  | 			exercise(A_DEX, TRUE);
839  | 			exercise(A_WIS, TRUE);	/* discovered a new food source! */
840  | 			newsym(x, y);
841  | 			maploc->looted |= TREE_LOOTED;
842  | 			return(1);
843  | 		    } else if (!rn2(15) && !(maploc->looted & TREE_SWARM)){
844  | 		    	int cnt = rnl(5);
845  | 		    	coord mm;
846  | 		    	mm.x = x; mm.y = y;
847  | 			pline("You've disturbed the occupants!");
848  | 			while (cnt--)
849  | 			    if (enexto(&mm, mm.x, mm.y, &mons[PM_KILLER_BEE]))
850  | 				(void) makemon(&mons[PM_KILLER_BEE],
851  | 					       mm.x, mm.y, MM_ANGRY);
852  | 			maploc->looted |= TREE_SWARM;
853  | 			return(1);
854  | 		    }
855  | 		    goto ouch;
856  | 		}
857  | #ifdef SINKS
858  | 		if(IS_SINK(maploc->typ)) {
859  | 		    int gend = poly_gender();
860  | 		    short washerndx = (gend == 1 || (gend == 2 && rn2(2))) ?
861  | 					PM_INCUBUS : PM_SUCCUBUS;
862  | 
863  | 		    if(Levitation) goto dumb;
864  | 		    if(rn2(5)) {
865  | 			if(flags.soundok)
866  | 			    pline("Klunk!  The pipes vibrate noisily.");
867  | 			else pline("Klunk!");
868  | 			exercise(A_DEX, TRUE);
869  | 			return(1);
870  | 		    } else if(!(maploc->looted & S_LPUDDING) && !rn2(3) &&
871  | 			  !(mvitals[PM_BLACK_PUDDING].mvflags & G_GONE)) {
872  | 			if (Blind)
873  | 			    You_hear("a gushing sound.");
874  | 			else
875  | 			    pline("A %s ooze gushes up from the drain!",
876  | 					 hcolor(Black));
877  | 			(void) makemon(&mons[PM_BLACK_PUDDING],
878  | 					 x, y, NO_MM_FLAGS);
879  | 			exercise(A_DEX, TRUE);
880  | 			newsym(x,y);
881  | 			maploc->looted |= S_LPUDDING;
882  | 			return(1);
883  | 		    } else if(!(maploc->looted & S_LDWASHER) && !rn2(3) &&
884  | 			      !(mvitals[washerndx].mvflags & G_GONE)) {
885  | 			/* can't resist... */
886  | 			pline("%s returns!", (Blind ? Something :
887  | 							"The dish washer"));
888  | 			if (makemon(&mons[washerndx], x, y, NO_MM_FLAGS))
889  | 			    newsym(x,y);
890  | 			maploc->looted |= S_LDWASHER;
891  | 			exercise(A_DEX, TRUE);
892  | 			return(1);
893  | 		    } else if(!rn2(3)) {
894  | 			pline("Flupp!  %s.", (Blind ?
895  | 				      "You hear a sloshing sound" :
896  | 				      "Muddy waste pops up from the drain"));
897  | 			if(!(maploc->looted & S_LRING)) { /* once per sink */
898  | 			    if (!Blind)
899  | 				You("see a ring shining in its midst.");
900  | 			    (void) mkobj_at(RING_CLASS, x, y, TRUE);
901  | 			    newsym(x, y);
902  | 			    exercise(A_DEX, TRUE);
903  | 			    exercise(A_WIS, TRUE);	/* a discovery! */
904  | 			    maploc->looted |= S_LRING;
905  | 			}
906  | 			return(1);
907  | 		    }
908  | 		    goto ouch;
909  | 		}
910  | #endif
911  | 		if (maploc->typ == STAIRS || maploc->typ == LADDER ||
912  | 						    IS_STWALL(maploc->typ)) {
913  | 		    if(!IS_STWALL(maploc->typ) && maploc->ladder == LA_DOWN)
914  | 			goto dumb;
915  | ouch:
916  | 		    pline("Ouch!  That hurts!");
917  | 		    exercise(A_DEX, FALSE);
918  | 		    exercise(A_STR, FALSE);
919  | 		    if (Blind) feel_location(x,y); /* we know we hit it */
920  | 		    if(!rn2(3)) set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
921  | 		    losehp(rnd(ACURR(A_CON) > 15 ? 3 : 5), kickstr(buf),
922  | 			KILLED_BY);
923  | 		    if(Is_airlevel(&u.uz) || Levitation)
924  | 			hurtle(-u.dx, -u.dy, rn1(2,4), TRUE); /* assume it's heavy */
925  | 		    return(1);
926  | 		}
927  | 		if (is_drawbridge_wall(x,y) >= 0) {
928  | 		    pline_The("drawbridge is unaffected.");
929  | 		    if(Levitation)
930  | 			hurtle(-u.dx, -u.dy, rn1(2,4), TRUE); /* it's heavy */
931  | 		    return(1);
932  | 		}
933  | 		goto dumb;
934  | 	}
935  | 
936  | 	if(maploc->doormask == D_ISOPEN ||
937  | 	   maploc->doormask == D_BROKEN ||
938  | 	   maploc->doormask == D_NODOOR) {
939  | dumb:
940  | 		exercise(A_DEX, FALSE);
941  | 		if (martial() || ACURR(A_DEX) >= 16 || rn2(3)) {
942  | 			You("kick at empty space.");
943  | 			if (Blind) feel_location(x,y);
944  | 		} else {
945  | 			pline("Dumb move!  You strain a muscle.");
946  | 			exercise(A_STR, FALSE);
947  | 			set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
948  | 		}
949  | 		if ((Is_airlevel(&u.uz) || Levitation) && rn2(2)) {
950  | 		    hurtle(-u.dx, -u.dy, 1, TRUE);
951  | 		    return 1;		/* you moved, so use up a turn */
952  | 		}
953  | 		return(0);
954  | 	}
955  | 
956  | 	/* not enough leverage to kick open doors while levitating */
957  | 	if(Levitation) goto ouch;
958  | 
959  | 	exercise(A_DEX, TRUE);
960  | 	/* door is known to be CLOSED or LOCKED */
961  | 	if(rnl(35) < avrg_attrib + (!martial() ? 0 : ACURR(A_DEX))) {
962  | 		boolean shopdoor = *in_rooms(x, y, SHOPBASE) ? TRUE : FALSE;
963  | 		/* break the door */
964  | 		if(maploc->doormask & D_TRAPPED) {
965  | 		    if (flags.verbose) You("kick the door.");
966  | 		    exercise(A_STR, FALSE);
967  | 		    maploc->doormask = D_NODOOR;
968  | 		    b_trapped("door", FOOT);
969  | 		} else if(ACURR(A_STR) > 18 && !rn2(5) && !shopdoor) {
970  | 		    pline("As you kick the door, it shatters to pieces!");
971  | 		    exercise(A_STR, TRUE);
972  | 		    maploc->doormask = D_NODOOR;
973  | 		} else {
974  | 		    pline("As you kick the door, it crashes open!");
975  | 		    exercise(A_STR, TRUE);
976  | 		    maploc->doormask = D_BROKEN;
977  | 		}
978  | 		if (Blind)
979  | 		    feel_location(x,y);		/* we know we broke it */
980  | 		else
981  | 		    newsym(x,y);
982  | 		unblock_point(x,y);		/* vision */
983  | 		if (shopdoor) {
984  | 		    add_damage(x, y, 400L);
985  | 		    pay_for_damage("break");
986  | 		}
987  | 		if ((slev = Is_special(&u.uz)) && slev->flags.town)
988  | 		  for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
989  | 		    if (DEADMONSTER(mtmp)) continue;
990  | 		    if((mtmp->data == &mons[PM_WATCHMAN] ||
991  | 			mtmp->data == &mons[PM_WATCH_CAPTAIN]) &&
992  | 			couldsee(mtmp->mx, mtmp->my) &&
993  | 			mtmp->mpeaceful) {
994  | 			pline("%s yells:", Amonnam(mtmp));
995  | 			verbalize("Halt, thief!  You're under arrest!");
996  | 			(void) angry_guards(FALSE);
997  | 			break;
998  | 		    }
999  | 		  }
1000 | 	} else {
1001 | 	    if (Blind) feel_location(x,y);	/* we know we hit it */
1002 | 	    exercise(A_STR, TRUE);
1003 | 	    pline("WHAMMM!!!");
1004 | 	    if ((slev = Is_special(&u.uz)) && slev->flags.town)
1005 | 		for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1006 | 		    if (DEADMONSTER(mtmp)) continue;
1007 | 		    if ((mtmp->data == &mons[PM_WATCHMAN] ||
1008 | 				mtmp->data == &mons[PM_WATCH_CAPTAIN]) &&
1009 | 			    mtmp->mpeaceful && couldsee(mtmp->mx, mtmp->my)) {
1010 | 			pline("%s yells:", Amonnam(mtmp));
1011 | 			if(levl[x][y].looted & D_WARNED) {
1012 | 			    verbalize("Halt, vandal!  You're under arrest!");
1013 | 			    (void) angry_guards(FALSE);
1014 | 			} else {
1015 | 			    verbalize("Hey, stop damaging that door!");
1016 | 			    levl[x][y].looted |= D_WARNED;
1017 | 			}
1018 | 			break;
1019 | 		    }
1020 | 		}
1021 | 	}
1022 | 	return(1);
1023 | }
1024 | 
1025 | STATIC_OVL void
1026 | drop_to(cc, loc)
1027 | coord *cc;
1028 | schar loc;
1029 | {
1030 | 	/* cover all the MIGR_xxx choices generated by down_gate() */
1031 | 	switch (loc) {
1032 | 	 case MIGR_RANDOM:	/* trap door or hole */
1033 | 		    if (Is_stronghold(&u.uz)) {
1034 | 			cc->x = valley_level.dnum;
1035 | 			cc->y = valley_level.dlevel;
1036 | 			break;
1037 | 		    } else if (In_endgame(&u.uz) || Is_botlevel(&u.uz)) {
1038 | 			cc->y = cc->x = 0;
1039 | 			break;
1040 | 		    } /* else fall to the next cases */
1041 | 	 case MIGR_STAIRS_UP:
1042 | 	 case MIGR_LADDER_UP:
1043 | 		    cc->x = u.uz.dnum;
1044 | 		    cc->y = u.uz.dlevel + 1;
1045 | 		    break;
1046 | 	 case MIGR_SSTAIRS:
1047 | 		    cc->x = sstairs.tolev.dnum;
1048 | 		    cc->y = sstairs.tolev.dlevel;
1049 | 		    break;
1050 | 	 default:
1051 | 	 case MIGR_NOWHERE:
1052 | 		    /* y==0 means "nowhere", in which case x doesn't matter */
1053 | 		    cc->y = cc->x = 0;
1054 | 		    break;
1055 | 	}
1056 | }
1057 | 
1058 | void
1059 | impact_drop(missile, x, y, dlev)
1060 | struct obj *missile;
1061 | xchar x, y, dlev;
1062 | {
1063 | 	schar toloc;
1064 | 	register struct obj *obj, *obj2;
1065 | 	register struct monst *shkp;
1066 | 	long oct, dct, price, debit, robbed;
1067 | 	boolean angry, costly, isrock;
1068 | 	coord cc;
1069 | 
1070 | 	if(!OBJ_AT(x, y)) return;
1071 | 
1072 | 	toloc = down_gate(x, y);
1073 | 	drop_to(&cc, toloc);
1074 | 	if (!cc.y) return;
1075 | 
1076 | 	if (dlev) {
1077 | 		/* send objects next to player falling through trap door.
1078 | 		 * checked in obj_delivery().
1079 | 		 */
1080 | 		toloc = MIGR_NEAR_PLAYER;
1081 | 		cc.y = dlev;
1082 | 	}
1083 | 
1084 | 	costly = costly_spot(x, y);
1085 | 	price = debit = robbed = 0L;
1086 | 	angry = FALSE;
1087 | 	shkp = (struct monst *) 0;
1088 | 	/* if 'costly', we must keep a record of ESHK(shkp) before
1089 | 	 * it undergoes changes through the calls to stolen_value.
1090 | 	 * the angry bit must be reset, if needed, in this fn, since
1091 | 	 * stolen_value is called under the 'silent' flag to avoid
1092 | 	 * unsavory pline repetitions.
1093 | 	 */
1094 | 	if(costly) {
1095 | 	    if ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) != 0) {
1096 | 		debit	= ESHK(shkp)->debit;
1097 | 		robbed	= ESHK(shkp)->robbed;
1098 | 		angry	= !shkp->mpeaceful;
1099 | 	    }
1100 | 	}
1101 | 
1102 | 	isrock = (missile && missile->otyp == ROCK);
1103 | 	oct = dct = 0L;
1104 | 	for(obj = level.objects[x][y]; obj; obj = obj2) {
1105 | 		obj2 = obj->nexthere;
1106 | 		if(obj == missile) continue;
1107 | 		/* number of objects in the pile */
1108 | 		oct += obj->quan;
1109 | 		if(obj == uball || obj == uchain) continue;
1110 | 		/* boulders can fall too, but rarely & never due to rocks */
1111 | 		if((isrock && obj->otyp == BOULDER) ||
1112 | 		   rn2(obj->otyp == BOULDER ? 30 : 3)) continue;
1113 | 		obj_extract_self(obj);
1114 | 
1115 | 		if(costly) {
1116 | 		    price += stolen_value(obj, x, y,
1117 | 				(costly_spot(u.ux, u.uy) &&
1118 | 				 index(u.urooms, *in_rooms(x, y, SHOPBASE))),
1119 | 				TRUE);
1120 | 		    /* set obj->no_charge to 0 */
1121 | 		    if (Has_contents(obj))
1122 | 			picked_container(obj);	/* does the right thing */
1123 | 		    if (obj->oclass != GOLD_CLASS)
1124 | 			obj->no_charge = 0;
1125 | 		}
1126 | 
1127 | 		add_to_migration(obj);
1128 | 		obj->ox = cc.x;
1129 | 		obj->oy = cc.y;
1130 | 		obj->owornmask = (long)toloc;
1131 | 
1132 | 		/* number of fallen objects */
1133 | 		dct += obj->quan;
1134 | 	}
1135 | 
1136 | 	if (dct && cansee(x,y)) {	/* at least one object fell */
1137 | 	    const char *what = (dct == 1L ? "object falls" : "objects fall");
1138 | 
1139 | 	    if (missile)
1140 | 		pline("From the impact, %sother %s.",
1141 | 		      dct == oct ? "the " : dct == 1L ? "an" : "", what);
1142 | 	    else if (oct == dct)
1143 | 		pline("%s adjacent %s %s.",
1144 | 		      dct == 1L ? "The" : "All the", what, gate_str);
1145 | 	    else
1146 | 		pline("%s adjacent %s %s.",
1147 | 		      dct == 1L ? "One of the" : "Some of the",
1148 | 		      dct == 1L ? "objects falls" : what, gate_str);
1149 | 	}
1150 | 
1151 | 	if(costly && shkp && price) {
1152 | 		if(ESHK(shkp)->robbed > robbed) {
1153 | 		    You("removed %ld zorkmids worth of goods!", price);
1154 | 		    if(cansee(shkp->mx, shkp->my)) {
1155 | 			if(ESHK(shkp)->customer[0] == 0)
1156 | 			    (void) strncpy(ESHK(shkp)->customer,
1157 | 					   plname, PL_NSIZ);
1158 | 			if(angry)
1159 | 			    pline("%s is infuriated!", Monnam(shkp));
1160 | 			else pline("\"%s, you are a thief!\"", plname);
1161 | 		    } else  You_hear("a scream, \"Thief!\"");
1162 | 		    hot_pursuit(shkp);
1163 | 		    (void) angry_guards(FALSE);
1164 | 		    return;
1165 | 		}
1166 | 		if(ESHK(shkp)->debit > debit)
1167 | 		    You("owe %s %ld zorkmids for goods lost.",
1168 | 			Monnam(shkp),
1169 | 			(ESHK(shkp)->debit - debit));
1170 | 	}
1171 | 
1172 | }
1173 | 
1174 | /* NOTE: ship_object assumes otmp was FREED from fobj or invent.
1175 |  * <x,y> is the point of drop.  otmp is _not_ an <x,y> resident:
1176 |  * otmp is either a kicked, dropped, or thrown object.
1177 |  */
1178 | boolean
1179 | ship_object(otmp, x, y, shop_floor_obj)
1180 | xchar  x, y;
1181 | struct obj *otmp;
1182 | boolean shop_floor_obj;
1183 | {
1184 | 	schar toloc;
1185 | 	xchar ox, oy;
1186 | 	coord cc;
1187 | 	struct obj *obj;
1188 | 	struct trap *t;
1189 | 	boolean nodrop, unpaid, container, impact = FALSE;
1190 | 	long n = 0L;
1191 | 
1192 | 	if (!otmp) return(FALSE);
1193 | 	if ((toloc = down_gate(x, y)) == MIGR_NOWHERE) return(FALSE);
1194 | 	drop_to(&cc, toloc);
1195 | 	if (!cc.y) return(FALSE);
1196 | 
1197 | 	/* objects other than attached iron ball always fall down ladder,
1198 | 	   but have a chance of staying otherwise */
1199 | 	nodrop = (otmp == uball) || (otmp == uchain) ||
1200 | 		(toloc != MIGR_LADDER_UP && rn2(3));
1201 | 
1202 | 	container = Has_contents(otmp);
1203 | 	unpaid = (otmp->unpaid || (container && count_unpaid(otmp->cobj)));
1204 | 
1205 | 	if(OBJ_AT(x, y)) {
1206 | 	    for(obj = level.objects[x][y]; obj; obj = obj->nexthere)
1207 | 		if(obj != otmp) n += obj->quan;
1208 | 	    if(n) impact = TRUE;
1209 | 	}
1210 | 	/* boulders never fall through trap doors, but they might knock
1211 | 	   other things down before plugging the hole */
1212 | 	if (otmp->otyp == BOULDER &&
1213 | 		((t = t_at(x, y)) != 0) &&
1214 | 		(t->ttyp == TRAPDOOR || t->ttyp == HOLE)) {
1215 | 	    if (impact) impact_drop(otmp, x, y, 0);
1216 | 	    return FALSE;		/* let caller finish the drop */
1217 | 	}
1218 | 
1219 | 	if (cansee(x, y))
1220 | 	    otransit_msg(otmp, nodrop, n);
1221 | 
1222 | 	if (nodrop) {
1223 | 	    if (impact) impact_drop(otmp, x, y, 0);
1224 | 	    return(FALSE);
1225 | 	}
1226 | 
1227 | 	if(unpaid || shop_floor_obj) {
1228 | 	    if(unpaid) {
1229 | 		subfrombill(otmp, shop_keeper(*u.ushops));
1230 | 		(void)stolen_value(otmp, u.ux, u.uy, TRUE, FALSE);
1231 | 	    } else {
1232 | 		ox = otmp->ox;
1233 | 		oy = otmp->oy;
1234 | 		(void)stolen_value(otmp, ox, oy,
1235 | 			  (costly_spot(u.ux, u.uy) &&
1236 | 			      index(u.urooms, *in_rooms(ox, oy, SHOPBASE))),
1237 | 			  FALSE);
1238 | 	    }
1239 | 	    /* set otmp->no_charge to 0 */
1240 | 	    if(container)
1241 | 		picked_container(otmp); /* happens to do the right thing */
1242 | 	    if(otmp->oclass != GOLD_CLASS)
1243 | 		otmp->no_charge = 0;
1244 | 	}
1245 | 
1246 | 	add_to_migration(otmp);
1247 | 	otmp->ox = cc.x;
1248 | 	otmp->oy = cc.y;
1249 | 	otmp->owornmask = (long)toloc;
1250 | 
1251 | 	if(impact) {
1252 | 	    /* the objs impacted may be in a shop other than
1253 | 	     * the one in which the hero is located.  another
1254 | 	     * check for a shk is made in impact_drop.  it is, e.g.,
1255 | 	     * possible to kick/throw an object belonging to one
1256 | 	     * shop into another shop through a gap in the wall,
1257 | 	     * and cause objects belonging to the other shop to
1258 | 	     * fall down a trap door--thereby getting two shopkeepers
1259 | 	     * angry at the hero in one shot.
1260 | 	     */
1261 | 	    impact_drop(otmp, x, y, 0);
1262 | 	    newsym(x,y);
1263 | 	}
1264 | 	return(TRUE);
1265 | }
1266 | 
1267 | void
1268 | obj_delivery()
1269 | {
1270 | 	register struct obj *otmp, *otmp2;
1271 | 	register int nx, ny;
1272 | 	long where;
1273 | 
1274 | 	for (otmp = migrating_objs; otmp; otmp = otmp2) {
1275 | 	    otmp2 = otmp->nobj;
1276 | 	    if (otmp->ox != u.uz.dnum || otmp->oy != u.uz.dlevel) continue;
1277 | 
1278 | 	    obj_extract_self(otmp);
1279 | 	    where = otmp->owornmask;		/* destination code */
1280 | 	    otmp->owornmask = 0L;
1281 | 
1282 | 	    switch ((int)where) {
1283 | 	     case MIGR_STAIRS_UP:   nx = xupstair,  ny = yupstair;
1284 | 				break;
1285 | 	     case MIGR_LADDER_UP:   nx = xupladder,  ny = yupladder;
1286 | 				break;
1287 | 	     case MIGR_SSTAIRS:	    nx = sstairs.sx,  ny = sstairs.sy;
1288 | 				break;
1289 | 	     case MIGR_NEAR_PLAYER: nx = u.ux,  ny = u.uy;
1290 | 				break;
1291 | 	     default:
1292 | 	     case MIGR_RANDOM:	    nx = ny = 0;
1293 | 				break;
1294 | 	    }
1295 | 	    if (nx > 0) {
1296 | 		place_object(otmp, nx, ny);
1297 | 		stackobj(otmp);
1298 | 		scatter(nx, ny, rnd(2), 0, otmp);
1299 | 	    } else {		/* random location */
1300 | 		/* set dummy coordinates because there's no
1301 | 		   current position for rloco() to update */
1302 | 		otmp->ox = otmp->oy = 0;
1303 | 		rloco(otmp);
1304 | 	    }
1305 | 	}
1306 | }
1307 | 
1308 | STATIC_OVL void
1309 | otransit_msg(otmp, nodrop, num)
1310 | register struct obj *otmp;
1311 | register boolean nodrop;
1312 | long num;
1313 | {
1314 | 	char obuf[BUFSZ];
1315 | 
1316 | 	Sprintf(obuf, "%s%s",
1317 | 		 (otmp->otyp == CORPSE &&
1318 | 			type_is_pname(&mons[otmp->corpsenm])) ? "" : "The ",
1319 | 		 xname(otmp));
1320 | 
1321 | 	if(num) { /* means: other objects are impacted */
1322 | 	    Sprintf(eos(obuf), " hit%s %s object%s",
1323 | 		      otmp->quan == 1L ? "s" : "",
1324 | 		      num == 1L ? "another" : "other",
1325 | 		      num > 1L ? "s" : "");
1326 | 	    if(nodrop)
1327 | 		Sprintf(eos(obuf), ".");
1328 | 	    else
1329 | 		Sprintf(eos(obuf), " and fall%s %s.",
1330 | 			otmp->quan == 1L ? "s" : "", gate_str);
1331 | 	    pline("%s", obuf);
1332 | 	} else if(!nodrop)
1333 | 	    pline("%s fall%s %s.", obuf,
1334 | 		  otmp->quan == 1L ? "s" : "", gate_str);
1335 | }
1336 | 
1337 | /* migration destination for objects which fall down to next level */
1338 | schar
1339 | down_gate(x, y)
1340 | xchar x, y;
1341 | {
1342 | 	struct trap *ttmp;
1343 | 
1344 | 	gate_str = 0;
1345 | 	/* this matches the player restriction in goto_level() */
1346 | 	if (on_level(&u.uz, &qstart_level) && !ok_to_quest())
1347 | 	    return MIGR_NOWHERE;
1348 | 
1349 | 	if ((xdnstair == x && ydnstair == y) ||
1350 | 		(sstairs.sx == x && sstairs.sy == y && !sstairs.up)) {
1351 | 	    gate_str = "down the stairs";
1352 | 	    return (xdnstair == x && ydnstair == y) ?
1353 | 		    MIGR_STAIRS_UP : MIGR_SSTAIRS;
1354 | 	}
1355 | 	if (xdnladder == x && ydnladder == y) {
1356 | 	    gate_str = "down the ladder";
1357 | 	    return MIGR_LADDER_UP;
1358 | 	}
1359 | 
1360 | 	if (((ttmp = t_at(x, y)) != 0 && ttmp->tseen) &&
1361 | 		(ttmp->ttyp == TRAPDOOR || ttmp->ttyp == HOLE)) {
1362 | 	    gate_str = (ttmp->ttyp == TRAPDOOR) ?
1363 | 		    "through the trap door" : "through the hole";
1364 | 	    return MIGR_RANDOM;
1365 | 	}
1366 | 	return MIGR_NOWHERE;
1367 | }
1368 | 
1369 | /*dokick.c*/