dumplog message history groundwork
[aNetHack.git] / src / dokick.c
blob2cb485472d4beaa98e865edbeffab19fe021f021
1 /* NetHack 3.6 dokick.c $NHDT-Date: 1446955295 2015/11/08 04:01:35 $ $NHDT-Branch: master $:$NHDT-Revision: 1.104 $ */
2 /* Copyright (c) Izchak Miller, Mike Stephenson, Steve Linhart, 1989. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
7 #define is_bigfoot(x) ((x) == &mons[PM_SASQUATCH])
8 #define martial() \
9 (martial_bonus() || is_bigfoot(youmonst.data) \
10 || (uarmf && uarmf->otyp == KICKING_BOOTS))
12 static NEARDATA struct rm *maploc, nowhere;
13 static NEARDATA const char *gate_str;
15 /* kickedobj (decl.c) tracks a kicked object until placed or destroyed */
17 extern boolean notonhead; /* for long worms */
19 STATIC_DCL void FDECL(kickdmg, (struct monst *, BOOLEAN_P));
20 STATIC_DCL boolean FDECL(maybe_kick_monster, (struct monst *,
21 XCHAR_P, XCHAR_P));
22 STATIC_DCL void FDECL(kick_monster, (struct monst *, XCHAR_P, XCHAR_P));
23 STATIC_DCL int FDECL(kick_object, (XCHAR_P, XCHAR_P, char *));
24 STATIC_DCL int FDECL(really_kick_object, (XCHAR_P, XCHAR_P));
25 STATIC_DCL char *FDECL(kickstr, (char *, const char *));
26 STATIC_DCL void FDECL(otransit_msg, (struct obj *, BOOLEAN_P, long));
27 STATIC_DCL void FDECL(drop_to, (coord *, SCHAR_P));
29 static const char kick_passes_thru[] = "kick passes harmlessly through";
31 STATIC_OVL void
32 kickdmg(mon, clumsy)
33 register struct monst *mon;
34 register boolean clumsy;
36 register int mdx, mdy;
37 register int dmg = (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 15;
38 int kick_skill = P_NONE;
39 int blessed_foot_damage = 0;
40 boolean trapkilled = FALSE;
42 if (uarmf && uarmf->otyp == KICKING_BOOTS)
43 dmg += 5;
45 /* excessive wt affects dex, so it affects dmg */
46 if (clumsy)
47 dmg /= 2;
49 /* kicking a dragon or an elephant will not harm it */
50 if (thick_skinned(mon->data))
51 dmg = 0;
53 /* attacking a shade is useless */
54 if (mon->data == &mons[PM_SHADE])
55 dmg = 0;
57 if ((is_undead(mon->data) || is_demon(mon->data) || is_vampshifter(mon))
58 && uarmf && uarmf->blessed)
59 blessed_foot_damage = 1;
61 if (mon->data == &mons[PM_SHADE] && !blessed_foot_damage) {
62 pline_The("%s.", kick_passes_thru);
63 /* doesn't exercise skill or abuse alignment or frighten pet,
64 and shades have no passive counterattack */
65 return;
68 if (mon->m_ap_type)
69 seemimic(mon);
71 check_caitiff(mon);
73 /* squeeze some guilt feelings... */
74 if (mon->mtame) {
75 abuse_dog(mon);
76 if (mon->mtame)
77 monflee(mon, (dmg ? rnd(dmg) : 1), FALSE, FALSE);
78 else
79 mon->mflee = 0;
82 if (dmg > 0) {
83 /* convert potential damage to actual damage */
84 dmg = rnd(dmg);
85 if (martial()) {
86 if (dmg > 1)
87 kick_skill = P_MARTIAL_ARTS;
88 dmg += rn2(ACURR(A_DEX) / 2 + 1);
90 /* a good kick exercises your dex */
91 exercise(A_DEX, TRUE);
93 if (blessed_foot_damage)
94 dmg += rnd(4);
95 if (uarmf)
96 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 && !mon->mtrapped) {
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, 0)) {
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 if (mintrap(mon) == 2)
114 trapkilled = TRUE;
119 (void) passive(mon, TRUE, mon->mhp > 0, AT_KICK, FALSE);
120 if (mon->mhp <= 0 && !trapkilled)
121 killed(mon);
123 /* may bring up a dialog, so put this after all messages */
124 if (kick_skill != P_NONE) /* exercise proficiency */
125 use_skill(kick_skill, 1);
128 STATIC_OVL boolean
129 maybe_kick_monster(mon, x, y)
130 struct monst *mon;
131 xchar x, y;
133 if (mon) {
134 boolean save_forcefight = context.forcefight;
136 bhitpos.x = x;
137 bhitpos.y = y;
138 if (!mon->mpeaceful || !canspotmon(mon))
139 context.forcefight = TRUE; /* attack even if invisible */
140 /* kicking might be halted by discovery of hidden monster,
141 by player declining to attack peaceful monster,
142 or by passing out due to encumbrance */
143 if (attack_checks(mon, (struct obj *) 0) || overexertion())
144 mon = 0; /* don't kick after all */
145 context.forcefight = save_forcefight;
147 return (boolean) (mon != 0);
150 STATIC_OVL void
151 kick_monster(mon, x, y)
152 struct monst *mon;
153 xchar x, y;
155 boolean clumsy = FALSE;
156 int i, j;
158 /* anger target even if wild miss will occur */
159 setmangry(mon, TRUE);
161 if (Levitation && !rn2(3) && verysmall(mon->data)
162 && !is_flyer(mon->data)) {
163 pline("Floating in the air, you miss wildly!");
164 exercise(A_DEX, FALSE);
165 (void) passive(mon, FALSE, 1, AT_KICK, FALSE);
166 return;
169 /* reveal hidden target even if kick ends up missing (note: being
170 hidden doesn't affect chance to hit so neither does this reveal) */
171 if (mon->mundetected
172 || (mon->m_ap_type && mon->m_ap_type != M_AP_MONSTER)) {
173 if (mon->m_ap_type)
174 seemimic(mon);
175 mon->mundetected = 0;
176 if (!canspotmon(mon))
177 map_invisible(x, y);
178 else
179 newsym(x, y);
180 There("is %s here.",
181 canspotmon(mon) ? a_monnam(mon) : "something hidden");
184 /* Kick attacks by kicking monsters are normal attacks, not special.
185 * This is almost always worthless, since you can either take one turn
186 * and do all your kicks, or else take one turn and attack the monster
187 * normally, getting all your attacks _including_ all your kicks.
188 * If you have >1 kick attack, you get all of them.
190 if (Upolyd && attacktype(youmonst.data, AT_KICK)) {
191 struct attack *uattk;
192 int sum, kickdieroll, armorpenalty,
193 attknum = 0,
194 tmp = find_roll_to_hit(mon, AT_KICK, (struct obj *) 0, &attknum,
195 &armorpenalty);
197 for (i = 0; i < NATTK; i++) {
198 /* first of two kicks might have provoked counterattack
199 that has incapacitated the hero (ie, floating eye) */
200 if (multi < 0)
201 break;
203 uattk = &youmonst.data->mattk[i];
204 /* we only care about kicking attacks here */
205 if (uattk->aatyp != AT_KICK)
206 continue;
208 if (mon->data == &mons[PM_SHADE] && (!uarmf || !uarmf->blessed)) {
209 /* doesn't matter whether it would have hit or missed,
210 and shades have no passive counterattack */
211 Your("%s %s.", kick_passes_thru, mon_nam(mon));
212 break; /* skip any additional kicks */
213 } else if (tmp > (kickdieroll = rnd(20))) {
214 You("kick %s.", mon_nam(mon));
215 sum = damageum(mon, uattk);
216 (void) passive(mon, (boolean) (sum > 0), (sum != 2), AT_KICK,
217 FALSE);
218 if (sum == 2)
219 break; /* Defender died */
220 } else {
221 missum(mon, uattk, (tmp + armorpenalty > kickdieroll));
222 (void) passive(mon, FALSE, 1, AT_KICK, FALSE);
225 return;
228 i = -inv_weight();
229 j = weight_cap();
231 if (i < (j * 3) / 10) {
232 if (!rn2((i < j / 10) ? 2 : (i < j / 5) ? 3 : 4)) {
233 if (martial() && !rn2(2))
234 goto doit;
235 Your("clumsy kick does no damage.");
236 (void) passive(mon, FALSE, 1, AT_KICK, FALSE);
237 return;
239 if (i < j / 10)
240 clumsy = TRUE;
241 else if (!rn2((i < j / 5) ? 2 : 3))
242 clumsy = TRUE;
245 if (Fumbling)
246 clumsy = TRUE;
248 else if (uarm && objects[uarm->otyp].oc_bulky && ACURR(A_DEX) < rnd(25))
249 clumsy = TRUE;
250 doit:
251 You("kick %s.", mon_nam(mon));
252 if (!rn2(clumsy ? 3 : 4) && (clumsy || !bigmonst(mon->data))
253 && mon->mcansee && !mon->mtrapped && !thick_skinned(mon->data)
254 && mon->data->mlet != S_EEL && haseyes(mon->data) && mon->mcanmove
255 && !mon->mstun && !mon->mconf && !mon->msleeping
256 && mon->data->mmove >= 12) {
257 if (!nohands(mon->data) && !rn2(martial() ? 5 : 3)) {
258 pline("%s blocks your %skick.", Monnam(mon),
259 clumsy ? "clumsy " : "");
260 (void) passive(mon, FALSE, 1, AT_KICK, FALSE);
261 return;
262 } else {
263 maybe_mnexto(mon);
264 if (mon->mx != x || mon->my != y) {
265 if (glyph_is_invisible(levl[x][y].glyph)) {
266 unmap_object(x, y);
267 newsym(x, y);
269 pline("%s %s, %s evading your %skick.", Monnam(mon),
270 (!level.flags.noteleport && can_teleport(mon->data))
271 ? "teleports"
272 : is_floater(mon->data)
273 ? "floats"
274 : is_flyer(mon->data) ? "swoops"
275 : (nolimbs(mon->data)
276 || slithy(mon->data))
277 ? "slides"
278 : "jumps",
279 clumsy ? "easily" : "nimbly", clumsy ? "clumsy " : "");
280 (void) passive(mon, FALSE, 1, AT_KICK, FALSE);
281 return;
285 kickdmg(mon, clumsy);
289 * Return TRUE if caught (the gold taken care of), FALSE otherwise.
290 * The gold object is *not* attached to the fobj chain!
292 boolean
293 ghitm(mtmp, gold)
294 register struct monst *mtmp;
295 register struct obj *gold;
297 boolean msg_given = FALSE;
299 if (!likes_gold(mtmp->data) && !mtmp->isshk && !mtmp->ispriest
300 && !mtmp->isgd && !is_mercenary(mtmp->data)) {
301 wakeup(mtmp, TRUE);
302 } else if (!mtmp->mcanmove) {
303 /* too light to do real damage */
304 if (canseemon(mtmp)) {
305 pline_The("%s harmlessly %s %s.", xname(gold),
306 otense(gold, "hit"), mon_nam(mtmp));
307 msg_given = TRUE;
309 } else {
310 long umoney, value = gold->quan * objects[gold->otyp].oc_cost;
312 mtmp->msleeping = 0;
313 finish_meating(mtmp);
314 if (!mtmp->isgd && !rn2(4)) /* not always pleasing */
315 setmangry(mtmp, TRUE);
316 /* greedy monsters catch gold */
317 if (cansee(mtmp->mx, mtmp->my))
318 pline("%s catches the gold.", Monnam(mtmp));
319 (void) mpickobj(mtmp, gold);
320 gold = (struct obj *) 0; /* obj has been freed */
321 if (mtmp->isshk) {
322 long robbed = ESHK(mtmp)->robbed;
324 if (robbed) {
325 robbed -= value;
326 if (robbed < 0L)
327 robbed = 0L;
328 pline_The("amount %scovers %s recent losses.",
329 !robbed ? "" : "partially ", mhis(mtmp));
330 ESHK(mtmp)->robbed = robbed;
331 if (!robbed)
332 make_happy_shk(mtmp, FALSE);
333 } else {
334 if (mtmp->mpeaceful) {
335 ESHK(mtmp)->credit += value;
336 You("have %ld %s in credit.", ESHK(mtmp)->credit,
337 currency(ESHK(mtmp)->credit));
338 } else
339 verbalize("Thanks, scum!");
341 } else if (mtmp->ispriest) {
342 if (mtmp->mpeaceful)
343 verbalize("Thank you for your contribution.");
344 else
345 verbalize("Thanks, scum!");
346 } else if (mtmp->isgd) {
347 umoney = money_cnt(invent);
348 /* Some of these are iffy, because a hostile guard
349 won't become peaceful and resume leading hero
350 out of the vault. If he did do that, player
351 could try fighting, then weasle out of being
352 killed by throwing his/her gold when losing. */
353 verbalize(
354 umoney
355 ? "Drop the rest and follow me."
356 : hidden_gold()
357 ? "You still have hidden gold. Drop it now."
358 : mtmp->mpeaceful
359 ? "I'll take care of that; please move along."
360 : "I'll take that; now get moving.");
361 } else if (is_mercenary(mtmp->data)) {
362 long goldreqd = 0L;
364 if (rn2(3)) {
365 if (mtmp->data == &mons[PM_SOLDIER])
366 goldreqd = 100L;
367 else if (mtmp->data == &mons[PM_SERGEANT])
368 goldreqd = 250L;
369 else if (mtmp->data == &mons[PM_LIEUTENANT])
370 goldreqd = 500L;
371 else if (mtmp->data == &mons[PM_CAPTAIN])
372 goldreqd = 750L;
374 if (goldreqd) {
375 umoney = money_cnt(invent);
376 if (value
377 > goldreqd
378 + (umoney + u.ulevel * rn2(5)) / ACURR(A_CHA))
379 mtmp->mpeaceful = TRUE;
382 if (mtmp->mpeaceful)
383 verbalize("That should do. Now beat it!");
384 else
385 verbalize("That's not enough, coward!");
387 return TRUE;
390 if (!msg_given)
391 miss(xname(gold), mtmp);
392 return FALSE;
395 /* container is kicked, dropped, thrown or otherwise impacted by player.
396 * Assumes container is on floor. Checks contents for possible damage. */
397 void
398 container_impact_dmg(obj, x, y)
399 struct obj *obj;
400 xchar x, y; /* coordinates where object was before the impact, not after */
402 struct monst *shkp;
403 struct obj *otmp, *otmp2;
404 long loss = 0L;
405 boolean costly, insider, frominv;
407 /* only consider normal containers */
408 if (!Is_container(obj) || !Has_contents(obj) || Is_mbag(obj))
409 return;
411 costly = ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)))
412 && costly_spot(x, y));
413 insider = (*u.ushops && inside_shop(u.ux, u.uy)
414 && *in_rooms(x, y, SHOPBASE) == *u.ushops);
415 /* if dropped or thrown, shop ownership flags are set on this obj */
416 frominv = (obj != kickedobj);
418 for (otmp = obj->cobj; otmp; otmp = otmp2) {
419 const char *result = (char *) 0;
421 otmp2 = otmp->nobj;
422 if (objects[otmp->otyp].oc_material == GLASS
423 && otmp->oclass != GEM_CLASS && !obj_resists(otmp, 33, 100)) {
424 result = "shatter";
425 } else if (otmp->otyp == EGG && !rn2(3)) {
426 result = "cracking";
428 if (result) {
429 if (otmp->otyp == MIRROR)
430 change_luck(-2);
432 /* eggs laid by you. penalty is -1 per egg, max 5,
433 * but it's always exactly 1 that breaks */
434 if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM)
435 change_luck(-1);
436 You_hear("a muffled %s.", result);
437 if (costly) {
438 if (frominv && !otmp->unpaid)
439 otmp->no_charge = 1;
440 loss +=
441 stolen_value(otmp, x, y, (boolean) shkp->mpeaceful, TRUE);
443 if (otmp->quan > 1L) {
444 useup(otmp);
445 } else {
446 obj_extract_self(otmp);
447 obfree(otmp, (struct obj *) 0);
449 /* contents of this container are no longer known */
450 obj->cknown = 0;
453 if (costly && loss) {
454 if (!insider) {
455 You("caused %ld %s worth of damage!", loss, currency(loss));
456 make_angry_shk(shkp, x, y);
457 } else {
458 You("owe %s %ld %s for objects destroyed.", mon_nam(shkp), loss,
459 currency(loss));
464 /* jacket around really_kick_object */
465 STATIC_OVL int
466 kick_object(x, y, kickobjnam)
467 xchar x, y;
468 char *kickobjnam;
470 int res = 0;
472 *kickobjnam = '\0';
473 /* if a pile, the "top" object gets kicked */
474 kickedobj = level.objects[x][y];
475 if (kickedobj) {
476 /* kick object; if doing is fatal, done() will clean up kickedobj */
477 Strcpy(kickobjnam, killer_xname(kickedobj)); /* matters iff res==0 */
478 res = really_kick_object(x, y);
479 kickedobj = (struct obj *) 0;
481 return res;
484 /* guts of kick_object */
485 STATIC_OVL int
486 really_kick_object(x, y)
487 xchar x, y;
489 int range;
490 struct monst *mon, *shkp = 0;
491 struct trap *trap;
492 char bhitroom;
493 boolean costly, isgold, slide = FALSE;
495 /* kickedobj should always be set due to conditions of call */
496 if (!kickedobj || kickedobj->otyp == BOULDER || kickedobj == uball
497 || kickedobj == uchain)
498 return 0;
500 if ((trap = t_at(x, y)) != 0) {
501 if (((trap->ttyp == PIT || trap->ttyp == SPIKED_PIT) && !Passes_walls)
502 || trap->ttyp == WEB) {
503 if (!trap->tseen)
504 find_trap(trap);
505 You_cant("kick %s that's in a %s!", something,
506 Hallucination ? "tizzy" :
507 (trap->ttyp == WEB) ? "web" : "pit");
508 return 1;
510 if (trap->ttyp == STATUE_TRAP) {
511 activate_statue_trap(trap, x,y, FALSE);
512 return 1;
516 if (Fumbling && !rn2(3)) {
517 Your("clumsy kick missed.");
518 return 1;
521 if (!uarmf && kickedobj->otyp == CORPSE
522 && touch_petrifies(&mons[kickedobj->corpsenm]) && !Stone_resistance) {
523 You("kick %s with your bare %s.",
524 corpse_xname(kickedobj, (const char *) 0, CXN_PFX_THE),
525 makeplural(body_part(FOOT)));
526 if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) {
527 ; /* hero has been transformed but kick continues */
528 } else {
529 /* normalize body shape here; foot, not body_part(FOOT) */
530 Sprintf(killer.name, "kicking %s barefoot",
531 killer_xname(kickedobj));
532 instapetrify(killer.name);
536 /* range < 2 means the object will not move. */
537 /* maybe dexterity should also figure here. */
538 range = (int) ((ACURRSTR) / 2 - kickedobj->owt / 40);
540 if (martial())
541 range += rnd(3);
543 if (is_pool(x, y)) {
544 /* you're in the water too; significantly reduce range */
545 range = range / 3 + 1; /* {1,2}=>1, {3,4,5}=>2, {6,7,8}=>3 */
546 } else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) {
547 /* you're in air, since is_pool did not match */
548 range += rnd(3);
549 } else {
550 if (is_ice(x, y))
551 range += rnd(3), slide = TRUE;
552 if (kickedobj->greased)
553 range += rnd(3), slide = TRUE;
556 /* Mjollnir is magically too heavy to kick */
557 if (kickedobj->oartifact == ART_MJOLLNIR)
558 range = 1;
560 /* see if the object has a place to move into */
561 if (!ZAP_POS(levl[x + u.dx][y + u.dy].typ)
562 || closed_door(x + u.dx, y + u.dy))
563 range = 1;
565 costly = (!(kickedobj->no_charge && !Has_contents(kickedobj))
566 && (shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) != 0
567 && costly_spot(x, y));
568 isgold = (kickedobj->oclass == COIN_CLASS);
570 if (IS_ROCK(levl[x][y].typ) || closed_door(x, y)) {
571 if ((!martial() && rn2(20) > ACURR(A_DEX))
572 || IS_ROCK(levl[u.ux][u.uy].typ) || closed_door(u.ux, u.uy)) {
573 if (Blind)
574 pline("It doesn't come loose.");
575 else
576 pline("%s %sn't come loose.",
577 The(distant_name(kickedobj, xname)),
578 otense(kickedobj, "do"));
579 return (!rn2(3) || martial());
581 if (Blind)
582 pline("It comes loose.");
583 else
584 pline("%s %s loose.", The(distant_name(kickedobj, xname)),
585 otense(kickedobj, "come"));
586 obj_extract_self(kickedobj);
587 newsym(x, y);
588 if (costly && (!costly_spot(u.ux, u.uy)
589 || !index(u.urooms, *in_rooms(x, y, SHOPBASE))))
590 addtobill(kickedobj, FALSE, FALSE, FALSE);
591 if (!flooreffects(kickedobj, u.ux, u.uy, "fall")) {
592 place_object(kickedobj, u.ux, u.uy);
593 stackobj(kickedobj);
594 newsym(u.ux, u.uy);
596 return 1;
599 /* a box gets a chance of breaking open here */
600 if (Is_box(kickedobj)) {
601 boolean otrp = kickedobj->otrapped;
603 if (range < 2)
604 pline("THUD!");
605 container_impact_dmg(kickedobj, x, y);
606 if (kickedobj->olocked) {
607 if (!rn2(5) || (martial() && !rn2(2))) {
608 You("break open the lock!");
609 breakchestlock(kickedobj, FALSE);
610 if (otrp)
611 (void) chest_trap(kickedobj, LEG, FALSE);
612 return 1;
614 } else {
615 if (!rn2(3) || (martial() && !rn2(2))) {
616 pline_The("lid slams open, then falls shut.");
617 kickedobj->lknown = 1;
618 if (otrp)
619 (void) chest_trap(kickedobj, LEG, FALSE);
620 return 1;
623 if (range < 2)
624 return 1;
625 /* else let it fall through to the next cases... */
628 /* fragile objects should not be kicked */
629 if (hero_breaks(kickedobj, kickedobj->ox, kickedobj->oy, FALSE))
630 return 1;
632 /* too heavy to move. range is calculated as potential distance from
633 * player, so range == 2 means the object may move up to one square
634 * from its current position
636 if (range < 2) {
637 if (!Is_box(kickedobj))
638 pline("Thump!");
639 return (!rn2(3) || martial());
642 if (kickedobj->quan > 1L) {
643 if (!isgold) {
644 kickedobj = splitobj(kickedobj, 1L);
645 } else {
646 if (rn2(20)) {
647 static NEARDATA const char *const flyingcoinmsg[] = {
648 "scatter the coins", "knock coins all over the place",
649 "send coins flying in all directions",
652 pline("Thwwpingg!");
653 You("%s!", flyingcoinmsg[rn2(SIZE(flyingcoinmsg))]);
654 (void) scatter(x, y, rn2(3) + 1, VIS_EFFECTS | MAY_HIT,
655 kickedobj);
656 newsym(x, y);
657 return 1;
659 if (kickedobj->quan > 300L) {
660 pline("Thump!");
661 return (!rn2(3) || martial());
666 if (slide && !Blind)
667 pline("Whee! %s %s across the %s.", Doname2(kickedobj),
668 otense(kickedobj, "slide"), surface(x, y));
670 if (costly && !isgold)
671 addtobill(kickedobj, FALSE, FALSE, TRUE);
672 obj_extract_self(kickedobj);
673 (void) snuff_candle(kickedobj);
674 newsym(x, y);
675 mon = bhit(u.dx, u.dy, range, KICKED_WEAPON,
676 (int FDECL((*), (MONST_P, OBJ_P))) 0,
677 (int FDECL((*), (OBJ_P, OBJ_P))) 0, &kickedobj);
678 if (!kickedobj)
679 return 1; /* object broken */
681 if (mon) {
682 if (mon->isshk && kickedobj->where == OBJ_MINVENT
683 && kickedobj->ocarry == mon)
684 return 1; /* alert shk caught it */
685 notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y);
686 if (isgold ? ghitm(mon, kickedobj) /* caught? */
687 : thitmonst(mon, kickedobj)) /* hit && used up? */
688 return 1;
691 /* the object might have fallen down a hole;
692 ship_object() will have taken care of shop billing */
693 if (kickedobj->where == OBJ_MIGRATING)
694 return 1;
696 bhitroom = *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE);
697 if (costly && (!costly_spot(bhitpos.x, bhitpos.y)
698 || *in_rooms(x, y, SHOPBASE) != bhitroom)) {
699 if (isgold)
700 costly_gold(x, y, kickedobj->quan);
701 else
702 (void) stolen_value(kickedobj, x, y, (boolean) shkp->mpeaceful,
703 FALSE);
706 if (flooreffects(kickedobj, bhitpos.x, bhitpos.y, "fall"))
707 return 1;
708 if (kickedobj->unpaid)
709 subfrombill(kickedobj, shkp);
710 place_object(kickedobj, bhitpos.x, bhitpos.y);
711 stackobj(kickedobj);
712 newsym(kickedobj->ox, kickedobj->oy);
713 return 1;
716 /* cause of death if kicking kills kicker */
717 STATIC_OVL char *
718 kickstr(buf, kickobjnam)
719 char *buf;
720 const char *kickobjnam;
722 const char *what;
724 if (*kickobjnam)
725 what = kickobjnam;
726 else if (maploc == &nowhere)
727 what = "nothing";
728 else if (IS_DOOR(maploc->typ))
729 what = "a door";
730 else if (IS_TREE(maploc->typ))
731 what = "a tree";
732 else if (IS_STWALL(maploc->typ))
733 what = "a wall";
734 else if (IS_ROCK(maploc->typ))
735 what = "a rock";
736 else if (IS_THRONE(maploc->typ))
737 what = "a throne";
738 else if (IS_FOUNTAIN(maploc->typ))
739 what = "a fountain";
740 else if (IS_GRAVE(maploc->typ))
741 what = "a headstone";
742 else if (IS_SINK(maploc->typ))
743 what = "a sink";
744 else if (IS_ALTAR(maploc->typ))
745 what = "an altar";
746 else if (IS_DRAWBRIDGE(maploc->typ))
747 what = "a drawbridge";
748 else if (maploc->typ == STAIRS)
749 what = "the stairs";
750 else if (maploc->typ == LADDER)
751 what = "a ladder";
752 else if (maploc->typ == IRONBARS)
753 what = "an iron bar";
754 else
755 what = "something weird";
756 return strcat(strcpy(buf, "kicking "), what);
760 dokick()
762 int x, y;
763 int avrg_attrib;
764 int dmg = 0, glyph, oldglyph = -1;
765 register struct monst *mtmp;
766 boolean no_kick = FALSE;
767 char buf[BUFSZ], kickobjnam[BUFSZ];
769 kickobjnam[0] = '\0';
770 if (nolimbs(youmonst.data) || slithy(youmonst.data)) {
771 You("have no legs to kick with.");
772 no_kick = TRUE;
773 } else if (verysmall(youmonst.data)) {
774 You("are too small to do any kicking.");
775 no_kick = TRUE;
776 } else if (u.usteed) {
777 if (yn_function("Kick your steed?", ynchars, 'y') == 'y') {
778 You("kick %s.", mon_nam(u.usteed));
779 kick_steed();
780 return 1;
781 } else {
782 return 0;
784 } else if (Wounded_legs) {
785 /* note: jump() has similar code */
786 long wl = (EWounded_legs & BOTH_SIDES);
787 const char *bp = body_part(LEG);
789 if (wl == BOTH_SIDES)
790 bp = makeplural(bp);
791 Your("%s%s %s in no shape for kicking.",
792 (wl == LEFT_SIDE) ? "left " : (wl == RIGHT_SIDE) ? "right " : "",
793 bp, (wl == BOTH_SIDES) ? "are" : "is");
794 no_kick = TRUE;
795 } else if (near_capacity() > SLT_ENCUMBER) {
796 Your("load is too heavy to balance yourself for a kick.");
797 no_kick = TRUE;
798 } else if (youmonst.data->mlet == S_LIZARD) {
799 Your("legs cannot kick effectively.");
800 no_kick = TRUE;
801 } else if (u.uinwater && !rn2(2)) {
802 Your("slow motion kick doesn't hit anything.");
803 no_kick = TRUE;
804 } else if (u.utrap) {
805 no_kick = TRUE;
806 switch (u.utraptype) {
807 case TT_PIT:
808 if (!Passes_walls)
809 pline("There's not enough room to kick down here.");
810 else
811 no_kick = FALSE;
812 break;
813 case TT_WEB:
814 case TT_BEARTRAP:
815 You_cant("move your %s!", body_part(LEG));
816 break;
817 default:
818 break;
822 if (no_kick) {
823 /* ignore direction typed before player notices kick failed */
824 display_nhwindow(WIN_MESSAGE, TRUE); /* --More-- */
825 return 0;
828 if (!getdir((char *) 0))
829 return 0;
830 if (!u.dx && !u.dy)
831 return 0;
833 x = u.ux + u.dx;
834 y = u.uy + u.dy;
836 /* KMH -- Kicking boots always succeed */
837 if (uarmf && uarmf->otyp == KICKING_BOOTS)
838 avrg_attrib = 99;
839 else
840 avrg_attrib = (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3;
842 if (u.uswallow) {
843 switch (rn2(3)) {
844 case 0:
845 You_cant("move your %s!", body_part(LEG));
846 break;
847 case 1:
848 if (is_animal(u.ustuck->data)) {
849 pline("%s burps loudly.", Monnam(u.ustuck));
850 break;
852 default:
853 Your("feeble kick has no effect.");
854 break;
856 return 1;
857 } else if (u.utrap && u.utraptype == TT_PIT) {
858 /* must be Passes_walls */
859 You("kick at the side of the pit.");
860 return 1;
862 if (Levitation) {
863 int xx, yy;
865 xx = u.ux - u.dx;
866 yy = u.uy - u.dy;
867 /* doors can be opened while levitating, so they must be
868 * reachable for bracing purposes
869 * Possible extension: allow bracing against stuff on the side?
871 if (isok(xx, yy) && !IS_ROCK(levl[xx][yy].typ)
872 && !IS_DOOR(levl[xx][yy].typ)
873 && (!Is_airlevel(&u.uz) || !OBJ_AT(xx, yy))) {
874 You("have nothing to brace yourself against.");
875 return 0;
879 mtmp = isok(x, y) ? m_at(x, y) : 0;
880 /* might not kick monster if it is hidden and becomes revealed,
881 if it is peaceful and player declines to attack, or if the
882 hero passes out due to encumbrance with low hp; context.move
883 will be 1 unless player declines to kick peaceful monster */
884 if (mtmp) {
885 oldglyph = glyph_at(x, y);
886 if (!maybe_kick_monster(mtmp, x, y))
887 return context.move;
890 wake_nearby();
891 u_wipe_engr(2);
893 if (!isok(x, y)) {
894 maploc = &nowhere;
895 goto ouch;
897 maploc = &levl[x][y];
900 * The next five tests should stay in their present order:
901 * monsters, pools, objects, non-doors, doors.
903 * [FIXME: Monsters who are hidden underneath objects or
904 * in pools should lead to hero kicking the concealment
905 * rather than the monster, probably exposing the hidden
906 * monster in the process. And monsters who are hidden on
907 * ceiling shouldn't be kickable (unless hero is flying?);
908 * kicking toward them should just target whatever is on
909 * the floor at that spot.]
912 if (mtmp) {
913 /* save mtmp->data (for recoil) in case mtmp gets killed */
914 struct permonst *mdat = mtmp->data;
916 kick_monster(mtmp, x, y);
917 glyph = glyph_at(x, y);
918 /* see comment in attack_checks() */
919 if (mtmp->mhp <= 0) { /* DEADMONSTER() */
920 /* if we mapped an invisible monster and immediately
921 killed it, we don't want to forget what we thought
922 was there before the kick */
923 if (glyph != oldglyph && glyph_is_invisible(glyph))
924 show_glyph(x, y, oldglyph);
925 } else if (!canspotmon(mtmp)
926 /* check <x,y>; monster that evades kick by jumping
927 to an unseen square doesn't leave an I behind */
928 && mtmp->mx == x && mtmp->my == y
929 && !glyph_is_invisible(glyph)
930 && !(u.uswallow && mtmp == u.ustuck)) {
931 map_invisible(x, y);
933 /* recoil if floating */
934 if ((Is_airlevel(&u.uz) || Levitation) && context.move) {
935 int range;
937 range =
938 ((int) youmonst.data->cwt + (weight_cap() + inv_weight()));
939 if (range < 1)
940 range = 1; /* divide by zero avoidance */
941 range = (3 * (int) mdat->cwt) / range;
943 if (range < 1)
944 range = 1;
945 hurtle(-u.dx, -u.dy, range, TRUE);
947 return 1;
949 if (glyph_is_invisible(levl[x][y].glyph)) {
950 unmap_object(x, y);
951 newsym(x, y);
953 if (is_pool(x, y) ^ !!u.uinwater) {
954 /* objects normally can't be removed from water by kicking */
955 You("splash some %s around.", hliquid("water"));
956 return 1;
959 if (OBJ_AT(x, y) && (!Levitation || Is_airlevel(&u.uz)
960 || Is_waterlevel(&u.uz) || sobj_at(BOULDER, x, y))) {
961 if (kick_object(x, y, kickobjnam)) {
962 if (Is_airlevel(&u.uz))
963 hurtle(-u.dx, -u.dy, 1, TRUE); /* assume it's light */
964 return 1;
966 goto ouch;
969 if (!IS_DOOR(maploc->typ)) {
970 if (maploc->typ == SDOOR) {
971 if (!Levitation && rn2(30) < avrg_attrib) {
972 cvt_sdoor_to_door(maploc); /* ->typ = DOOR */
973 pline("Crash! %s a secret door!",
974 /* don't "kick open" when it's locked
975 unless it also happens to be trapped */
976 (maploc->doormask & (D_LOCKED | D_TRAPPED)) == D_LOCKED
977 ? "Your kick uncovers"
978 : "You kick open");
979 exercise(A_DEX, TRUE);
980 if (maploc->doormask & D_TRAPPED) {
981 maploc->doormask = D_NODOOR;
982 b_trapped("door", FOOT);
983 } else if (maploc->doormask != D_NODOOR
984 && !(maploc->doormask & D_LOCKED))
985 maploc->doormask = D_ISOPEN;
986 feel_newsym(x, y); /* we know it's gone */
987 if (maploc->doormask == D_ISOPEN
988 || maploc->doormask == D_NODOOR)
989 unblock_point(x, y); /* vision */
990 return 1;
991 } else
992 goto ouch;
994 if (maploc->typ == SCORR) {
995 if (!Levitation && rn2(30) < avrg_attrib) {
996 pline("Crash! You kick open a secret passage!");
997 exercise(A_DEX, TRUE);
998 maploc->typ = CORR;
999 feel_newsym(x, y); /* we know it's gone */
1000 unblock_point(x, y); /* vision */
1001 return 1;
1002 } else
1003 goto ouch;
1005 if (IS_THRONE(maploc->typ)) {
1006 register int i;
1007 if (Levitation)
1008 goto dumb;
1009 if ((Luck < 0 || maploc->doormask) && !rn2(3)) {
1010 maploc->typ = ROOM;
1011 maploc->doormask = 0; /* don't leave loose ends.. */
1012 (void) mkgold((long) rnd(200), x, y);
1013 if (Blind)
1014 pline("CRASH! You destroy it.");
1015 else {
1016 pline("CRASH! You destroy the throne.");
1017 newsym(x, y);
1019 exercise(A_DEX, TRUE);
1020 return 1;
1021 } else if (Luck > 0 && !rn2(3) && !maploc->looted) {
1022 (void) mkgold((long) rn1(201, 300), x, y);
1023 i = Luck + 1;
1024 if (i > 6)
1025 i = 6;
1026 while (i--)
1027 (void) mksobj_at(
1028 rnd_class(DILITHIUM_CRYSTAL, LUCKSTONE - 1), x, y,
1029 FALSE, TRUE);
1030 if (Blind)
1031 You("kick %s loose!", something);
1032 else {
1033 You("kick loose some ornamental coins and gems!");
1034 newsym(x, y);
1036 /* prevent endless milking */
1037 maploc->looted = T_LOOTED;
1038 return 1;
1039 } else if (!rn2(4)) {
1040 if (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)) {
1041 fall_through(FALSE);
1042 return 1;
1043 } else
1044 goto ouch;
1046 goto ouch;
1048 if (IS_ALTAR(maploc->typ)) {
1049 if (Levitation)
1050 goto dumb;
1051 You("kick %s.", (Blind ? something : "the altar"));
1052 if (!rn2(3))
1053 goto ouch;
1054 altar_wrath(x, y);
1055 exercise(A_DEX, TRUE);
1056 return 1;
1058 if (IS_FOUNTAIN(maploc->typ)) {
1059 if (Levitation)
1060 goto dumb;
1061 You("kick %s.", (Blind ? something : "the fountain"));
1062 if (!rn2(3))
1063 goto ouch;
1064 /* make metal boots rust */
1065 if (uarmf && rn2(3))
1066 if (water_damage(uarmf, "metal boots", TRUE) == ER_NOTHING) {
1067 Your("boots get wet.");
1068 /* could cause short-lived fumbling here */
1070 exercise(A_DEX, TRUE);
1071 return 1;
1073 if (IS_GRAVE(maploc->typ)) {
1074 if (Levitation)
1075 goto dumb;
1076 if (rn2(4))
1077 goto ouch;
1078 exercise(A_WIS, FALSE);
1079 if (Role_if(PM_ARCHEOLOGIST) || Role_if(PM_SAMURAI)
1080 || ((u.ualign.type == A_LAWFUL) && (u.ualign.record > -10))) {
1081 adjalign(-sgn(u.ualign.type));
1083 maploc->typ = ROOM;
1084 maploc->doormask = 0;
1085 (void) mksobj_at(ROCK, x, y, TRUE, FALSE);
1086 del_engr_at(x, y);
1087 if (Blind)
1088 pline("Crack! %s broke!", Something);
1089 else {
1090 pline_The("headstone topples over and breaks!");
1091 newsym(x, y);
1093 return 1;
1095 if (maploc->typ == IRONBARS)
1096 goto ouch;
1097 if (IS_TREE(maploc->typ)) {
1098 struct obj *treefruit;
1100 /* nothing, fruit or trouble? 75:23.5:1.5% */
1101 if (rn2(3)) {
1102 if (!rn2(6) && !(mvitals[PM_KILLER_BEE].mvflags & G_GONE))
1103 You_hear("a low buzzing."); /* a warning */
1104 goto ouch;
1106 if (rn2(15) && !(maploc->looted & TREE_LOOTED)
1107 && (treefruit = rnd_treefruit_at(x, y))) {
1108 long nfruit = 8L - rnl(7), nfall;
1109 short frtype = treefruit->otyp;
1111 treefruit->quan = nfruit;
1112 treefruit->owt = weight(treefruit);
1113 if (is_plural(treefruit))
1114 pline("Some %s fall from the tree!", xname(treefruit));
1115 else
1116 pline("%s falls from the tree!", An(xname(treefruit)));
1117 nfall = scatter(x, y, 2, MAY_HIT, treefruit);
1118 if (nfall != nfruit) {
1119 /* scatter left some in the tree, but treefruit
1120 * may not refer to the correct object */
1121 treefruit = mksobj(frtype, TRUE, FALSE);
1122 treefruit->quan = nfruit - nfall;
1123 pline("%ld %s got caught in the branches.",
1124 nfruit - nfall, xname(treefruit));
1125 dealloc_obj(treefruit);
1127 exercise(A_DEX, TRUE);
1128 exercise(A_WIS, TRUE); /* discovered a new food source! */
1129 newsym(x, y);
1130 maploc->looted |= TREE_LOOTED;
1131 return 1;
1132 } else if (!(maploc->looted & TREE_SWARM)) {
1133 int cnt = rnl(4) + 2;
1134 int made = 0;
1135 coord mm;
1137 mm.x = x;
1138 mm.y = y;
1139 while (cnt--) {
1140 if (enexto(&mm, mm.x, mm.y, &mons[PM_KILLER_BEE])
1141 && makemon(&mons[PM_KILLER_BEE], mm.x, mm.y,
1142 MM_ANGRY))
1143 made++;
1145 if (made)
1146 pline("You've attracted the tree's former occupants!");
1147 else
1148 You("smell stale honey.");
1149 maploc->looted |= TREE_SWARM;
1150 return 1;
1152 goto ouch;
1154 if (IS_SINK(maploc->typ)) {
1155 int gend = poly_gender();
1156 short washerndx = (gend == 1 || (gend == 2 && rn2(2)))
1157 ? PM_INCUBUS
1158 : PM_SUCCUBUS;
1160 if (Levitation)
1161 goto dumb;
1162 if (rn2(5)) {
1163 if (!Deaf)
1164 pline("Klunk! The pipes vibrate noisily.");
1165 else
1166 pline("Klunk!");
1167 exercise(A_DEX, TRUE);
1168 return 1;
1169 } else if (!(maploc->looted & S_LPUDDING) && !rn2(3)
1170 && !(mvitals[PM_BLACK_PUDDING].mvflags & G_GONE)) {
1171 if (Blind)
1172 You_hear("a gushing sound.");
1173 else
1174 pline("A %s ooze gushes up from the drain!",
1175 hcolor(NH_BLACK));
1176 (void) makemon(&mons[PM_BLACK_PUDDING], x, y, NO_MM_FLAGS);
1177 exercise(A_DEX, TRUE);
1178 newsym(x, y);
1179 maploc->looted |= S_LPUDDING;
1180 return 1;
1181 } else if (!(maploc->looted & S_LDWASHER) && !rn2(3)
1182 && !(mvitals[washerndx].mvflags & G_GONE)) {
1183 /* can't resist... */
1184 pline("%s returns!", (Blind ? Something : "The dish washer"));
1185 if (makemon(&mons[washerndx], x, y, NO_MM_FLAGS))
1186 newsym(x, y);
1187 maploc->looted |= S_LDWASHER;
1188 exercise(A_DEX, TRUE);
1189 return 1;
1190 } else if (!rn2(3)) {
1191 pline("Flupp! %s.",
1192 (Blind ? "You hear a sloshing sound"
1193 : "Muddy waste pops up from the drain"));
1194 if (!(maploc->looted & S_LRING)) { /* once per sink */
1195 if (!Blind)
1196 You_see("a ring shining in its midst.");
1197 (void) mkobj_at(RING_CLASS, x, y, TRUE);
1198 newsym(x, y);
1199 exercise(A_DEX, TRUE);
1200 exercise(A_WIS, TRUE); /* a discovery! */
1201 maploc->looted |= S_LRING;
1203 return 1;
1205 goto ouch;
1207 if (maploc->typ == STAIRS || maploc->typ == LADDER
1208 || IS_STWALL(maploc->typ)) {
1209 if (!IS_STWALL(maploc->typ) && maploc->ladder == LA_DOWN)
1210 goto dumb;
1211 ouch:
1212 pline("Ouch! That hurts!");
1213 exercise(A_DEX, FALSE);
1214 exercise(A_STR, FALSE);
1215 if (isok(x, y)) {
1216 if (Blind)
1217 feel_location(x, y); /* we know we hit it */
1218 if (is_drawbridge_wall(x, y) >= 0) {
1219 pline_The("drawbridge is unaffected.");
1220 /* update maploc to refer to the drawbridge */
1221 (void) find_drawbridge(&x, &y);
1222 maploc = &levl[x][y];
1225 if (!rn2(3))
1226 set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
1227 dmg = rnd(ACURR(A_CON) > 15 ? 3 : 5);
1228 losehp(Maybe_Half_Phys(dmg), kickstr(buf, kickobjnam), KILLED_BY);
1229 if (Is_airlevel(&u.uz) || Levitation)
1230 hurtle(-u.dx, -u.dy, rn1(2, 4), TRUE); /* assume it's heavy */
1231 return 1;
1233 goto dumb;
1236 if (maploc->doormask == D_ISOPEN || maploc->doormask == D_BROKEN
1237 || maploc->doormask == D_NODOOR) {
1238 dumb:
1239 exercise(A_DEX, FALSE);
1240 if (martial() || ACURR(A_DEX) >= 16 || rn2(3)) {
1241 You("kick at empty space.");
1242 if (Blind)
1243 feel_location(x, y);
1244 } else {
1245 pline("Dumb move! You strain a muscle.");
1246 exercise(A_STR, FALSE);
1247 set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
1249 if ((Is_airlevel(&u.uz) || Levitation) && rn2(2))
1250 hurtle(-u.dx, -u.dy, 1, TRUE);
1251 return 1; /* uses a turn */
1254 /* not enough leverage to kick open doors while levitating */
1255 if (Levitation)
1256 goto ouch;
1258 exercise(A_DEX, TRUE);
1259 /* door is known to be CLOSED or LOCKED */
1260 if (rnl(35) < avrg_attrib + (!martial() ? 0 : ACURR(A_DEX))) {
1261 boolean shopdoor = *in_rooms(x, y, SHOPBASE) ? TRUE : FALSE;
1262 /* break the door */
1263 if (maploc->doormask & D_TRAPPED) {
1264 if (flags.verbose)
1265 You("kick the door.");
1266 exercise(A_STR, FALSE);
1267 maploc->doormask = D_NODOOR;
1268 b_trapped("door", FOOT);
1269 } else if (ACURR(A_STR) > 18 && !rn2(5) && !shopdoor) {
1270 pline("As you kick the door, it shatters to pieces!");
1271 exercise(A_STR, TRUE);
1272 maploc->doormask = D_NODOOR;
1273 } else {
1274 pline("As you kick the door, it crashes open!");
1275 exercise(A_STR, TRUE);
1276 maploc->doormask = D_BROKEN;
1278 feel_newsym(x, y); /* we know we broke it */
1279 unblock_point(x, y); /* vision */
1280 if (shopdoor) {
1281 add_damage(x, y, 400L);
1282 pay_for_damage("break", FALSE);
1284 if (in_town(x, y))
1285 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1286 if (DEADMONSTER(mtmp))
1287 continue;
1288 if (is_watch(mtmp->data) && couldsee(mtmp->mx, mtmp->my)
1289 && mtmp->mpeaceful) {
1290 mon_yells(mtmp, "Halt, thief! You're under arrest!");
1291 (void) angry_guards(FALSE);
1292 break;
1295 } else {
1296 if (Blind)
1297 feel_location(x, y); /* we know we hit it */
1298 exercise(A_STR, TRUE);
1299 pline("WHAMMM!!!");
1300 if (in_town(x, y))
1301 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1302 if (DEADMONSTER(mtmp))
1303 continue;
1304 if (is_watch(mtmp->data) && mtmp->mpeaceful
1305 && couldsee(mtmp->mx, mtmp->my)) {
1306 if (levl[x][y].looted & D_WARNED) {
1307 mon_yells(mtmp,
1308 "Halt, vandal! You're under arrest!");
1309 (void) angry_guards(FALSE);
1310 } else {
1311 mon_yells(mtmp, "Hey, stop damaging that door!");
1312 levl[x][y].looted |= D_WARNED;
1314 break;
1318 return 1;
1321 STATIC_OVL void
1322 drop_to(cc, loc)
1323 coord *cc;
1324 schar loc;
1326 /* cover all the MIGR_xxx choices generated by down_gate() */
1327 switch (loc) {
1328 case MIGR_RANDOM: /* trap door or hole */
1329 if (Is_stronghold(&u.uz)) {
1330 cc->x = valley_level.dnum;
1331 cc->y = valley_level.dlevel;
1332 break;
1333 } else if (In_endgame(&u.uz) || Is_botlevel(&u.uz)) {
1334 cc->y = cc->x = 0;
1335 break;
1336 } /* else fall to the next cases */
1337 case MIGR_STAIRS_UP:
1338 case MIGR_LADDER_UP:
1339 cc->x = u.uz.dnum;
1340 cc->y = u.uz.dlevel + 1;
1341 break;
1342 case MIGR_SSTAIRS:
1343 cc->x = sstairs.tolev.dnum;
1344 cc->y = sstairs.tolev.dlevel;
1345 break;
1346 default:
1347 case MIGR_NOWHERE:
1348 /* y==0 means "nowhere", in which case x doesn't matter */
1349 cc->y = cc->x = 0;
1350 break;
1354 /* player or missile impacts location, causing objects to fall down */
1355 void
1356 impact_drop(missile, x, y, dlev)
1357 struct obj *missile; /* caused impact, won't drop itself */
1358 xchar x, y; /* location affected */
1359 xchar dlev; /* if !0 send to dlev near player */
1361 schar toloc;
1362 register struct obj *obj, *obj2;
1363 register struct monst *shkp;
1364 long oct, dct, price, debit, robbed;
1365 boolean angry, costly, isrock;
1366 coord cc;
1368 if (!OBJ_AT(x, y))
1369 return;
1371 toloc = down_gate(x, y);
1372 drop_to(&cc, toloc);
1373 if (!cc.y)
1374 return;
1376 if (dlev) {
1377 /* send objects next to player falling through trap door.
1378 * checked in obj_delivery().
1380 toloc = MIGR_WITH_HERO;
1381 cc.y = dlev;
1384 costly = costly_spot(x, y);
1385 price = debit = robbed = 0L;
1386 angry = FALSE;
1387 shkp = (struct monst *) 0;
1388 /* if 'costly', we must keep a record of ESHK(shkp) before
1389 * it undergoes changes through the calls to stolen_value.
1390 * the angry bit must be reset, if needed, in this fn, since
1391 * stolen_value is called under the 'silent' flag to avoid
1392 * unsavory pline repetitions.
1394 if (costly) {
1395 if ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) != 0) {
1396 debit = ESHK(shkp)->debit;
1397 robbed = ESHK(shkp)->robbed;
1398 angry = !shkp->mpeaceful;
1402 isrock = (missile && missile->otyp == ROCK);
1403 oct = dct = 0L;
1404 for (obj = level.objects[x][y]; obj; obj = obj2) {
1405 obj2 = obj->nexthere;
1406 if (obj == missile)
1407 continue;
1408 /* number of objects in the pile */
1409 oct += obj->quan;
1410 if (obj == uball || obj == uchain)
1411 continue;
1412 /* boulders can fall too, but rarely & never due to rocks */
1413 if ((isrock && obj->otyp == BOULDER)
1414 || rn2(obj->otyp == BOULDER ? 30 : 3))
1415 continue;
1416 obj_extract_self(obj);
1418 if (costly) {
1419 price += stolen_value(
1420 obj, x, y, (costly_spot(u.ux, u.uy)
1421 && index(u.urooms, *in_rooms(x, y, SHOPBASE))),
1422 TRUE);
1423 /* set obj->no_charge to 0 */
1424 if (Has_contents(obj))
1425 picked_container(obj); /* does the right thing */
1426 if (obj->oclass != COIN_CLASS)
1427 obj->no_charge = 0;
1430 add_to_migration(obj);
1431 obj->ox = cc.x;
1432 obj->oy = cc.y;
1433 obj->owornmask = (long) toloc;
1435 /* number of fallen objects */
1436 dct += obj->quan;
1439 if (dct && cansee(x, y)) { /* at least one object fell */
1440 const char *what = (dct == 1L ? "object falls" : "objects fall");
1442 if (missile)
1443 pline("From the impact, %sother %s.",
1444 dct == oct ? "the " : dct == 1L ? "an" : "", what);
1445 else if (oct == dct)
1446 pline("%s adjacent %s %s.", dct == 1L ? "The" : "All the", what,
1447 gate_str);
1448 else
1449 pline("%s adjacent %s %s.",
1450 dct == 1L ? "One of the" : "Some of the",
1451 dct == 1L ? "objects falls" : what, gate_str);
1454 if (costly && shkp && price) {
1455 if (ESHK(shkp)->robbed > robbed) {
1456 You("removed %ld %s worth of goods!", price, currency(price));
1457 if (cansee(shkp->mx, shkp->my)) {
1458 if (ESHK(shkp)->customer[0] == 0)
1459 (void) strncpy(ESHK(shkp)->customer, plname, PL_NSIZ);
1460 if (angry)
1461 pline("%s is infuriated!", Monnam(shkp));
1462 else
1463 pline("\"%s, you are a thief!\"", plname);
1464 } else
1465 You_hear("a scream, \"Thief!\"");
1466 hot_pursuit(shkp);
1467 (void) angry_guards(FALSE);
1468 return;
1470 if (ESHK(shkp)->debit > debit) {
1471 long amt = (ESHK(shkp)->debit - debit);
1472 You("owe %s %ld %s for goods lost.", Monnam(shkp), amt,
1473 currency(amt));
1478 /* NOTE: ship_object assumes otmp was FREED from fobj or invent.
1479 * <x,y> is the point of drop. otmp is _not_ an <x,y> resident:
1480 * otmp is either a kicked, dropped, or thrown object.
1482 boolean
1483 ship_object(otmp, x, y, shop_floor_obj)
1484 xchar x, y;
1485 struct obj *otmp;
1486 boolean shop_floor_obj;
1488 schar toloc;
1489 xchar ox, oy;
1490 coord cc;
1491 struct obj *obj;
1492 struct trap *t;
1493 boolean nodrop, unpaid, container, impact = FALSE;
1494 long n = 0L;
1496 if (!otmp)
1497 return FALSE;
1498 if ((toloc = down_gate(x, y)) == MIGR_NOWHERE)
1499 return FALSE;
1500 drop_to(&cc, toloc);
1501 if (!cc.y)
1502 return FALSE;
1504 /* objects other than attached iron ball always fall down ladder,
1505 but have a chance of staying otherwise */
1506 nodrop = (otmp == uball) || (otmp == uchain)
1507 || (toloc != MIGR_LADDER_UP && rn2(3));
1509 container = Has_contents(otmp);
1510 unpaid = is_unpaid(otmp);
1512 if (OBJ_AT(x, y)) {
1513 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
1514 if (obj != otmp)
1515 n += obj->quan;
1516 if (n)
1517 impact = TRUE;
1519 /* boulders never fall through trap doors, but they might knock
1520 other things down before plugging the hole */
1521 if (otmp->otyp == BOULDER && ((t = t_at(x, y)) != 0)
1522 && (t->ttyp == TRAPDOOR || t->ttyp == HOLE)) {
1523 if (impact)
1524 impact_drop(otmp, x, y, 0);
1525 return FALSE; /* let caller finish the drop */
1528 if (cansee(x, y))
1529 otransit_msg(otmp, nodrop, n);
1531 if (nodrop) {
1532 if (impact)
1533 impact_drop(otmp, x, y, 0);
1534 return FALSE;
1537 if (unpaid || shop_floor_obj) {
1538 if (unpaid) {
1539 (void) stolen_value(otmp, u.ux, u.uy, TRUE, FALSE);
1540 } else {
1541 ox = otmp->ox;
1542 oy = otmp->oy;
1543 (void) stolen_value(
1544 otmp, ox, oy,
1545 (costly_spot(u.ux, u.uy)
1546 && index(u.urooms, *in_rooms(ox, oy, SHOPBASE))),
1547 FALSE);
1549 /* set otmp->no_charge to 0 */
1550 if (container)
1551 picked_container(otmp); /* happens to do the right thing */
1552 if (otmp->oclass != COIN_CLASS)
1553 otmp->no_charge = 0;
1556 if (otmp->owornmask)
1557 remove_worn_item(otmp, TRUE);
1559 /* some things break rather than ship */
1560 if (breaktest(otmp)) {
1561 const char *result;
1563 if (objects[otmp->otyp].oc_material == GLASS
1564 || otmp->otyp == EXPENSIVE_CAMERA) {
1565 if (otmp->otyp == MIRROR)
1566 change_luck(-2);
1567 result = "crash";
1568 } else {
1569 /* penalty for breaking eggs laid by you */
1570 if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM)
1571 change_luck((schar) -min(otmp->quan, 5L));
1572 result = "splat";
1574 You_hear("a muffled %s.", result);
1575 obj_extract_self(otmp);
1576 obfree(otmp, (struct obj *) 0);
1577 return TRUE;
1580 add_to_migration(otmp);
1581 otmp->ox = cc.x;
1582 otmp->oy = cc.y;
1583 otmp->owornmask = (long) toloc;
1584 /* boulder from rolling boulder trap, no longer part of the trap */
1585 if (otmp->otyp == BOULDER)
1586 otmp->otrapped = 0;
1588 if (impact) {
1589 /* the objs impacted may be in a shop other than
1590 * the one in which the hero is located. another
1591 * check for a shk is made in impact_drop. it is, e.g.,
1592 * possible to kick/throw an object belonging to one
1593 * shop into another shop through a gap in the wall,
1594 * and cause objects belonging to the other shop to
1595 * fall down a trap door--thereby getting two shopkeepers
1596 * angry at the hero in one shot.
1598 impact_drop(otmp, x, y, 0);
1599 newsym(x, y);
1601 return TRUE;
1604 void
1605 obj_delivery(near_hero)
1606 boolean near_hero;
1608 register struct obj *otmp, *otmp2;
1609 register int nx, ny;
1610 int where;
1611 boolean nobreak, noscatter;
1613 for (otmp = migrating_objs; otmp; otmp = otmp2) {
1614 otmp2 = otmp->nobj;
1615 if (otmp->ox != u.uz.dnum || otmp->oy != u.uz.dlevel)
1616 continue;
1618 where = (int) (otmp->owornmask & 0x7fffL); /* destination code */
1619 nobreak = (where & MIGR_NOBREAK) != 0;
1620 noscatter = (where & MIGR_WITH_HERO) != 0;
1621 where &= ~(MIGR_NOBREAK | MIGR_NOSCATTER);
1623 if (!near_hero ^ (where == MIGR_WITH_HERO))
1624 continue;
1626 obj_extract_self(otmp);
1627 otmp->owornmask = 0L;
1629 switch (where) {
1630 case MIGR_STAIRS_UP:
1631 nx = xupstair, ny = yupstair;
1632 break;
1633 case MIGR_LADDER_UP:
1634 nx = xupladder, ny = yupladder;
1635 break;
1636 case MIGR_SSTAIRS:
1637 nx = sstairs.sx, ny = sstairs.sy;
1638 break;
1639 case MIGR_WITH_HERO:
1640 nx = u.ux, ny = u.uy;
1641 break;
1642 default:
1643 case MIGR_RANDOM:
1644 nx = ny = 0;
1645 break;
1647 if (nx > 0) {
1648 place_object(otmp, nx, ny);
1649 if (!nobreak && !IS_SOFT(levl[nx][ny].typ)) {
1650 if (where == MIGR_WITH_HERO) {
1651 if (breaks(otmp, nx, ny))
1652 continue;
1653 } else if (breaktest(otmp)) {
1654 /* assume it broke before player arrived, no messages */
1655 delobj(otmp);
1656 continue;
1659 stackobj(otmp);
1660 if (!noscatter)
1661 (void) scatter(nx, ny, rnd(2), 0, otmp);
1662 } else { /* random location */
1663 /* set dummy coordinates because there's no
1664 current position for rloco() to update */
1665 otmp->ox = otmp->oy = 0;
1666 if (rloco(otmp) && !nobreak && breaktest(otmp)) {
1667 /* assume it broke before player arrived, no messages */
1668 delobj(otmp);
1674 STATIC_OVL void
1675 otransit_msg(otmp, nodrop, num)
1676 register struct obj *otmp;
1677 register boolean nodrop;
1678 long num;
1680 char obuf[BUFSZ];
1682 Sprintf(obuf, "%s%s",
1683 (otmp->otyp == CORPSE && type_is_pname(&mons[otmp->corpsenm]))
1684 ? ""
1685 : "The ",
1686 cxname(otmp));
1688 if (num) { /* means: other objects are impacted */
1689 Sprintf(eos(obuf), " %s %s object%s", otense(otmp, "hit"),
1690 num == 1L ? "another" : "other", num > 1L ? "s" : "");
1691 if (nodrop)
1692 Sprintf(eos(obuf), ".");
1693 else
1694 Sprintf(eos(obuf), " and %s %s.", otense(otmp, "fall"), gate_str);
1695 pline1(obuf);
1696 } else if (!nodrop)
1697 pline("%s %s %s.", obuf, otense(otmp, "fall"), gate_str);
1700 /* migration destination for objects which fall down to next level */
1701 schar
1702 down_gate(x, y)
1703 xchar x, y;
1705 struct trap *ttmp;
1707 gate_str = 0;
1708 /* this matches the player restriction in goto_level() */
1709 if (on_level(&u.uz, &qstart_level) && !ok_to_quest())
1710 return MIGR_NOWHERE;
1712 if ((xdnstair == x && ydnstair == y)
1713 || (sstairs.sx == x && sstairs.sy == y && !sstairs.up)) {
1714 gate_str = "down the stairs";
1715 return (xdnstair == x && ydnstair == y) ? MIGR_STAIRS_UP
1716 : MIGR_SSTAIRS;
1718 if (xdnladder == x && ydnladder == y) {
1719 gate_str = "down the ladder";
1720 return MIGR_LADDER_UP;
1723 if (((ttmp = t_at(x, y)) != 0 && ttmp->tseen)
1724 && (ttmp->ttyp == TRAPDOOR || ttmp->ttyp == HOLE)) {
1725 gate_str = (ttmp->ttyp == TRAPDOOR) ? "through the trap door"
1726 : "through the hole";
1727 return MIGR_RANDOM;
1729 return MIGR_NOWHERE;
1732 /*dokick.c*/