OSDN Git Service

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