Show keys in travel help wrapped in single quotes
[aNetHack.git] / src / dokick.c
blob4737a27a0b469faeb1994e5e42add76ec99d4180
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);
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);
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);
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 && (((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" : (trap->ttyp == WEB) ? "web"
507 : "pit");
508 return 1;
511 if (Fumbling && !rn2(3)) {
512 Your("clumsy kick missed.");
513 return 1;
516 if (!uarmf && kickedobj->otyp == CORPSE
517 && touch_petrifies(&mons[kickedobj->corpsenm]) && !Stone_resistance) {
518 You("kick %s with your bare %s.",
519 corpse_xname(kickedobj, (const char *) 0, CXN_PFX_THE),
520 makeplural(body_part(FOOT)));
521 if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) {
522 ; /* hero has been transformed but kick continues */
523 } else {
524 /* normalize body shape here; foot, not body_part(FOOT) */
525 Sprintf(killer.name, "kicking %s barefoot",
526 killer_xname(kickedobj));
527 instapetrify(killer.name);
531 /* range < 2 means the object will not move. */
532 /* maybe dexterity should also figure here. */
533 range = (int) ((ACURRSTR) / 2 - kickedobj->owt / 40);
535 if (martial())
536 range += rnd(3);
538 if (is_pool(x, y)) {
539 /* you're in the water too; significantly reduce range */
540 range = range / 3 + 1; /* {1,2}=>1, {3,4,5}=>2, {6,7,8}=>3 */
541 } else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) {
542 /* you're in air, since is_pool did not match */
543 range += rnd(3);
544 } else {
545 if (is_ice(x, y))
546 range += rnd(3), slide = TRUE;
547 if (kickedobj->greased)
548 range += rnd(3), slide = TRUE;
551 /* Mjollnir is magically too heavy to kick */
552 if (kickedobj->oartifact == ART_MJOLLNIR)
553 range = 1;
555 /* see if the object has a place to move into */
556 if (!ZAP_POS(levl[x + u.dx][y + u.dy].typ)
557 || closed_door(x + u.dx, y + u.dy))
558 range = 1;
560 costly = (!(kickedobj->no_charge && !Has_contents(kickedobj))
561 && (shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) != 0
562 && costly_spot(x, y));
563 isgold = (kickedobj->oclass == COIN_CLASS);
565 if (IS_ROCK(levl[x][y].typ) || closed_door(x, y)) {
566 if ((!martial() && rn2(20) > ACURR(A_DEX))
567 || IS_ROCK(levl[u.ux][u.uy].typ) || closed_door(u.ux, u.uy)) {
568 if (Blind)
569 pline("It doesn't come loose.");
570 else
571 pline("%s %sn't come loose.",
572 The(distant_name(kickedobj, xname)),
573 otense(kickedobj, "do"));
574 return (!rn2(3) || martial());
576 if (Blind)
577 pline("It comes loose.");
578 else
579 pline("%s %s loose.", The(distant_name(kickedobj, xname)),
580 otense(kickedobj, "come"));
581 obj_extract_self(kickedobj);
582 newsym(x, y);
583 if (costly && (!costly_spot(u.ux, u.uy)
584 || !index(u.urooms, *in_rooms(x, y, SHOPBASE))))
585 addtobill(kickedobj, FALSE, FALSE, FALSE);
586 if (!flooreffects(kickedobj, u.ux, u.uy, "fall")) {
587 place_object(kickedobj, u.ux, u.uy);
588 stackobj(kickedobj);
589 newsym(u.ux, u.uy);
591 return 1;
594 /* a box gets a chance of breaking open here */
595 if (Is_box(kickedobj)) {
596 boolean otrp = kickedobj->otrapped;
598 if (range < 2)
599 pline("THUD!");
600 container_impact_dmg(kickedobj, x, y);
601 if (kickedobj->olocked) {
602 if (!rn2(5) || (martial() && !rn2(2))) {
603 You("break open the lock!");
604 breakchestlock(kickedobj, FALSE);
605 if (otrp)
606 (void) chest_trap(kickedobj, LEG, FALSE);
607 return 1;
609 } else {
610 if (!rn2(3) || (martial() && !rn2(2))) {
611 pline_The("lid slams open, then falls shut.");
612 kickedobj->lknown = 1;
613 if (otrp)
614 (void) chest_trap(kickedobj, LEG, FALSE);
615 return 1;
618 if (range < 2)
619 return 1;
620 /* else let it fall through to the next cases... */
623 /* fragile objects should not be kicked */
624 if (hero_breaks(kickedobj, kickedobj->ox, kickedobj->oy, FALSE))
625 return 1;
627 /* too heavy to move. range is calculated as potential distance from
628 * player, so range == 2 means the object may move up to one square
629 * from its current position
631 if (range < 2) {
632 if (!Is_box(kickedobj))
633 pline("Thump!");
634 return (!rn2(3) || martial());
637 if (kickedobj->quan > 1L) {
638 if (!isgold) {
639 kickedobj = splitobj(kickedobj, 1L);
640 } else {
641 if (rn2(20)) {
642 static NEARDATA const char *const flyingcoinmsg[] = {
643 "scatter the coins", "knock coins all over the place",
644 "send coins flying in all directions",
647 pline("Thwwpingg!");
648 You("%s!", flyingcoinmsg[rn2(SIZE(flyingcoinmsg))]);
649 (void) scatter(x, y, rn2(3) + 1, VIS_EFFECTS | MAY_HIT,
650 kickedobj);
651 newsym(x, y);
652 return 1;
654 if (kickedobj->quan > 300L) {
655 pline("Thump!");
656 return (!rn2(3) || martial());
661 if (slide && !Blind)
662 pline("Whee! %s %s across the %s.", Doname2(kickedobj),
663 otense(kickedobj, "slide"), surface(x, y));
665 if (costly && !isgold)
666 addtobill(kickedobj, FALSE, FALSE, TRUE);
667 obj_extract_self(kickedobj);
668 (void) snuff_candle(kickedobj);
669 newsym(x, y);
670 mon = bhit(u.dx, u.dy, range, KICKED_WEAPON,
671 (int FDECL((*), (MONST_P, OBJ_P))) 0,
672 (int FDECL((*), (OBJ_P, OBJ_P))) 0, &kickedobj);
673 if (!kickedobj)
674 return 1; /* object broken */
676 if (mon) {
677 if (mon->isshk && kickedobj->where == OBJ_MINVENT
678 && kickedobj->ocarry == mon)
679 return 1; /* alert shk caught it */
680 notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y);
681 if (isgold ? ghitm(mon, kickedobj) /* caught? */
682 : thitmonst(mon, kickedobj)) /* hit && used up? */
683 return 1;
686 /* the object might have fallen down a hole;
687 ship_object() will have taken care of shop billing */
688 if (kickedobj->where == OBJ_MIGRATING)
689 return 1;
691 bhitroom = *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE);
692 if (costly && (!costly_spot(bhitpos.x, bhitpos.y)
693 || *in_rooms(x, y, SHOPBASE) != bhitroom)) {
694 if (isgold)
695 costly_gold(x, y, kickedobj->quan);
696 else
697 (void) stolen_value(kickedobj, x, y, (boolean) shkp->mpeaceful,
698 FALSE);
701 if (flooreffects(kickedobj, bhitpos.x, bhitpos.y, "fall"))
702 return 1;
703 if (kickedobj->unpaid)
704 subfrombill(kickedobj, shkp);
705 place_object(kickedobj, bhitpos.x, bhitpos.y);
706 stackobj(kickedobj);
707 newsym(kickedobj->ox, kickedobj->oy);
708 return 1;
711 /* cause of death if kicking kills kicker */
712 STATIC_OVL char *
713 kickstr(buf, kickobjnam)
714 char *buf;
715 const char *kickobjnam;
717 const char *what;
719 if (*kickobjnam)
720 what = kickobjnam;
721 else if (maploc == &nowhere)
722 what = "nothing";
723 else if (IS_DOOR(maploc->typ))
724 what = "a door";
725 else if (IS_TREE(maploc->typ))
726 what = "a tree";
727 else if (IS_STWALL(maploc->typ))
728 what = "a wall";
729 else if (IS_ROCK(maploc->typ))
730 what = "a rock";
731 else if (IS_THRONE(maploc->typ))
732 what = "a throne";
733 else if (IS_FOUNTAIN(maploc->typ))
734 what = "a fountain";
735 else if (IS_GRAVE(maploc->typ))
736 what = "a headstone";
737 else if (IS_SINK(maploc->typ))
738 what = "a sink";
739 else if (IS_ALTAR(maploc->typ))
740 what = "an altar";
741 else if (IS_DRAWBRIDGE(maploc->typ))
742 what = "a drawbridge";
743 else if (maploc->typ == STAIRS)
744 what = "the stairs";
745 else if (maploc->typ == LADDER)
746 what = "a ladder";
747 else if (maploc->typ == IRONBARS)
748 what = "an iron bar";
749 else
750 what = "something weird";
751 return strcat(strcpy(buf, "kicking "), what);
755 dokick()
757 int x, y;
758 int avrg_attrib;
759 int dmg = 0, glyph, oldglyph = -1;
760 register struct monst *mtmp;
761 boolean no_kick = FALSE;
762 char buf[BUFSZ], kickobjnam[BUFSZ];
764 kickobjnam[0] = '\0';
765 if (nolimbs(youmonst.data) || slithy(youmonst.data)) {
766 You("have no legs to kick with.");
767 no_kick = TRUE;
768 } else if (verysmall(youmonst.data)) {
769 You("are too small to do any kicking.");
770 no_kick = TRUE;
771 } else if (u.usteed) {
772 if (yn_function("Kick your steed?", ynchars, 'y') == 'y') {
773 You("kick %s.", mon_nam(u.usteed));
774 kick_steed();
775 return 1;
776 } else {
777 return 0;
779 } else if (Wounded_legs) {
780 /* note: jump() has similar code */
781 long wl = (EWounded_legs & BOTH_SIDES);
782 const char *bp = body_part(LEG);
784 if (wl == BOTH_SIDES)
785 bp = makeplural(bp);
786 Your("%s%s %s in no shape for kicking.",
787 (wl == LEFT_SIDE) ? "left " : (wl == RIGHT_SIDE) ? "right " : "",
788 bp, (wl == BOTH_SIDES) ? "are" : "is");
789 no_kick = TRUE;
790 } else if (near_capacity() > SLT_ENCUMBER) {
791 Your("load is too heavy to balance yourself for a kick.");
792 no_kick = TRUE;
793 } else if (youmonst.data->mlet == S_LIZARD) {
794 Your("legs cannot kick effectively.");
795 no_kick = TRUE;
796 } else if (u.uinwater && !rn2(2)) {
797 Your("slow motion kick doesn't hit anything.");
798 no_kick = TRUE;
799 } else if (u.utrap) {
800 no_kick = TRUE;
801 switch (u.utraptype) {
802 case TT_PIT:
803 if (!Passes_walls)
804 pline("There's not enough room to kick down here.");
805 else
806 no_kick = FALSE;
807 break;
808 case TT_WEB:
809 case TT_BEARTRAP:
810 You_cant("move your %s!", body_part(LEG));
811 break;
812 default:
813 break;
817 if (no_kick) {
818 /* ignore direction typed before player notices kick failed */
819 display_nhwindow(WIN_MESSAGE, TRUE); /* --More-- */
820 return 0;
823 if (!getdir((char *) 0))
824 return 0;
825 if (!u.dx && !u.dy)
826 return 0;
828 x = u.ux + u.dx;
829 y = u.uy + u.dy;
831 /* KMH -- Kicking boots always succeed */
832 if (uarmf && uarmf->otyp == KICKING_BOOTS)
833 avrg_attrib = 99;
834 else
835 avrg_attrib = (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3;
837 if (u.uswallow) {
838 switch (rn2(3)) {
839 case 0:
840 You_cant("move your %s!", body_part(LEG));
841 break;
842 case 1:
843 if (is_animal(u.ustuck->data)) {
844 pline("%s burps loudly.", Monnam(u.ustuck));
845 break;
847 default:
848 Your("feeble kick has no effect.");
849 break;
851 return 1;
852 } else if (u.utrap && u.utraptype == TT_PIT) {
853 /* must be Passes_walls */
854 You("kick at the side of the pit.");
855 return 1;
857 if (Levitation) {
858 int xx, yy;
860 xx = u.ux - u.dx;
861 yy = u.uy - u.dy;
862 /* doors can be opened while levitating, so they must be
863 * reachable for bracing purposes
864 * Possible extension: allow bracing against stuff on the side?
866 if (isok(xx, yy) && !IS_ROCK(levl[xx][yy].typ)
867 && !IS_DOOR(levl[xx][yy].typ)
868 && (!Is_airlevel(&u.uz) || !OBJ_AT(xx, yy))) {
869 You("have nothing to brace yourself against.");
870 return 0;
874 mtmp = isok(x, y) ? m_at(x, y) : 0;
875 /* might not kick monster if it is hidden and becomes revealed,
876 if it is peaceful and player declines to attack, or if the
877 hero passes out due to encumbrance with low hp; context.move
878 will be 1 unless player declines to kick peaceful monster */
879 if (mtmp) {
880 oldglyph = glyph_at(x, y);
881 if (!maybe_kick_monster(mtmp, x, y))
882 return context.move;
885 wake_nearby();
886 u_wipe_engr(2);
888 if (!isok(x, y)) {
889 maploc = &nowhere;
890 goto ouch;
892 maploc = &levl[x][y];
895 * The next five tests should stay in their present order:
896 * monsters, pools, objects, non-doors, doors.
898 * [FIXME: Monsters who are hidden underneath objects or
899 * in pools should lead to hero kicking the concealment
900 * rather than the monster, probably exposing the hidden
901 * monster in the process. And monsters who are hidden on
902 * ceiling shouldn't be kickable (unless hero is flying?);
903 * kicking toward them should just target whatever is on
904 * the floor at that spot.]
907 if (mtmp) {
908 /* save mtmp->data (for recoil) in case mtmp gets killed */
909 struct permonst *mdat = mtmp->data;
911 kick_monster(mtmp, x, y);
912 glyph = glyph_at(x, y);
913 /* see comment in attack_checks() */
914 if (mtmp->mhp <= 0) { /* DEADMONSTER() */
915 /* if we mapped an invisible monster and immediately
916 killed it, we don't want to forget what we thought
917 was there before the kick */
918 if (glyph != oldglyph && glyph_is_invisible(glyph))
919 show_glyph(x, y, oldglyph);
920 } else if (!canspotmon(mtmp)
921 /* check <x,y>; monster that evades kick by jumping
922 to an unseen square doesn't leave an I behind */
923 && mtmp->mx == x && mtmp->my == y
924 && !glyph_is_invisible(glyph)
925 && !(u.uswallow && mtmp == u.ustuck)) {
926 map_invisible(x, y);
928 /* recoil if floating */
929 if ((Is_airlevel(&u.uz) || Levitation) && context.move) {
930 int range;
932 range =
933 ((int) youmonst.data->cwt + (weight_cap() + inv_weight()));
934 if (range < 1)
935 range = 1; /* divide by zero avoidance */
936 range = (3 * (int) mdat->cwt) / range;
938 if (range < 1)
939 range = 1;
940 hurtle(-u.dx, -u.dy, range, TRUE);
942 return 1;
944 if (glyph_is_invisible(levl[x][y].glyph)) {
945 unmap_object(x, y);
946 newsym(x, y);
948 if (is_pool(x, y) ^ !!u.uinwater) {
949 /* objects normally can't be removed from water by kicking */
950 You("splash some %s around.", hliquid("water"));
951 return 1;
954 if (OBJ_AT(x, y) && (!Levitation || Is_airlevel(&u.uz)
955 || Is_waterlevel(&u.uz) || sobj_at(BOULDER, x, y))) {
956 if (kick_object(x, y, kickobjnam)) {
957 if (Is_airlevel(&u.uz))
958 hurtle(-u.dx, -u.dy, 1, TRUE); /* assume it's light */
959 return 1;
961 goto ouch;
964 if (!IS_DOOR(maploc->typ)) {
965 if (maploc->typ == SDOOR) {
966 if (!Levitation && rn2(30) < avrg_attrib) {
967 cvt_sdoor_to_door(maploc); /* ->typ = DOOR */
968 pline("Crash! %s a secret door!",
969 /* don't "kick open" when it's locked
970 unless it also happens to be trapped */
971 (maploc->doormask & (D_LOCKED | D_TRAPPED)) == D_LOCKED
972 ? "Your kick uncovers"
973 : "You kick open");
974 exercise(A_DEX, TRUE);
975 if (maploc->doormask & D_TRAPPED) {
976 maploc->doormask = D_NODOOR;
977 b_trapped("door", FOOT);
978 } else if (maploc->doormask != D_NODOOR
979 && !(maploc->doormask & D_LOCKED))
980 maploc->doormask = D_ISOPEN;
981 feel_newsym(x, y); /* we know it's gone */
982 if (maploc->doormask == D_ISOPEN
983 || maploc->doormask == D_NODOOR)
984 unblock_point(x, y); /* vision */
985 return 1;
986 } else
987 goto ouch;
989 if (maploc->typ == SCORR) {
990 if (!Levitation && rn2(30) < avrg_attrib) {
991 pline("Crash! You kick open a secret passage!");
992 exercise(A_DEX, TRUE);
993 maploc->typ = CORR;
994 feel_newsym(x, y); /* we know it's gone */
995 unblock_point(x, y); /* vision */
996 return 1;
997 } else
998 goto ouch;
1000 if (IS_THRONE(maploc->typ)) {
1001 register int i;
1002 if (Levitation)
1003 goto dumb;
1004 if ((Luck < 0 || maploc->doormask) && !rn2(3)) {
1005 maploc->typ = ROOM;
1006 maploc->doormask = 0; /* don't leave loose ends.. */
1007 (void) mkgold((long) rnd(200), x, y);
1008 if (Blind)
1009 pline("CRASH! You destroy it.");
1010 else {
1011 pline("CRASH! You destroy the throne.");
1012 newsym(x, y);
1014 exercise(A_DEX, TRUE);
1015 return 1;
1016 } else if (Luck > 0 && !rn2(3) && !maploc->looted) {
1017 (void) mkgold((long) rn1(201, 300), x, y);
1018 i = Luck + 1;
1019 if (i > 6)
1020 i = 6;
1021 while (i--)
1022 (void) mksobj_at(
1023 rnd_class(DILITHIUM_CRYSTAL, LUCKSTONE - 1), x, y,
1024 FALSE, TRUE);
1025 if (Blind)
1026 You("kick %s loose!", something);
1027 else {
1028 You("kick loose some ornamental coins and gems!");
1029 newsym(x, y);
1031 /* prevent endless milking */
1032 maploc->looted = T_LOOTED;
1033 return 1;
1034 } else if (!rn2(4)) {
1035 if (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)) {
1036 fall_through(FALSE);
1037 return 1;
1038 } else
1039 goto ouch;
1041 goto ouch;
1043 if (IS_ALTAR(maploc->typ)) {
1044 if (Levitation)
1045 goto dumb;
1046 You("kick %s.", (Blind ? something : "the altar"));
1047 if (!rn2(3))
1048 goto ouch;
1049 altar_wrath(x, y);
1050 exercise(A_DEX, TRUE);
1051 return 1;
1053 if (IS_FOUNTAIN(maploc->typ)) {
1054 if (Levitation)
1055 goto dumb;
1056 You("kick %s.", (Blind ? something : "the fountain"));
1057 if (!rn2(3))
1058 goto ouch;
1059 /* make metal boots rust */
1060 if (uarmf && rn2(3))
1061 if (water_damage(uarmf, "metal boots", TRUE) == ER_NOTHING) {
1062 Your("boots get wet.");
1063 /* could cause short-lived fumbling here */
1065 exercise(A_DEX, TRUE);
1066 return 1;
1068 if (IS_GRAVE(maploc->typ)) {
1069 if (Levitation)
1070 goto dumb;
1071 if (rn2(4))
1072 goto ouch;
1073 exercise(A_WIS, FALSE);
1074 if (Role_if(PM_ARCHEOLOGIST) || Role_if(PM_SAMURAI)
1075 || ((u.ualign.type == A_LAWFUL) && (u.ualign.record > -10))) {
1076 adjalign(-sgn(u.ualign.type));
1078 maploc->typ = ROOM;
1079 maploc->doormask = 0;
1080 (void) mksobj_at(ROCK, x, y, TRUE, FALSE);
1081 del_engr_at(x, y);
1082 if (Blind)
1083 pline("Crack! %s broke!", Something);
1084 else {
1085 pline_The("headstone topples over and breaks!");
1086 newsym(x, y);
1088 return 1;
1090 if (maploc->typ == IRONBARS)
1091 goto ouch;
1092 if (IS_TREE(maploc->typ)) {
1093 struct obj *treefruit;
1095 /* nothing, fruit or trouble? 75:23.5:1.5% */
1096 if (rn2(3)) {
1097 if (!rn2(6) && !(mvitals[PM_KILLER_BEE].mvflags & G_GONE))
1098 You_hear("a low buzzing."); /* a warning */
1099 goto ouch;
1101 if (rn2(15) && !(maploc->looted & TREE_LOOTED)
1102 && (treefruit = rnd_treefruit_at(x, y))) {
1103 long nfruit = 8L - rnl(7), nfall;
1104 short frtype = treefruit->otyp;
1106 treefruit->quan = nfruit;
1107 treefruit->owt = weight(treefruit);
1108 if (is_plural(treefruit))
1109 pline("Some %s fall from the tree!", xname(treefruit));
1110 else
1111 pline("%s falls from the tree!", An(xname(treefruit)));
1112 nfall = scatter(x, y, 2, MAY_HIT, treefruit);
1113 if (nfall != nfruit) {
1114 /* scatter left some in the tree, but treefruit
1115 * may not refer to the correct object */
1116 treefruit = mksobj(frtype, TRUE, FALSE);
1117 treefruit->quan = nfruit - nfall;
1118 pline("%ld %s got caught in the branches.",
1119 nfruit - nfall, xname(treefruit));
1120 dealloc_obj(treefruit);
1122 exercise(A_DEX, TRUE);
1123 exercise(A_WIS, TRUE); /* discovered a new food source! */
1124 newsym(x, y);
1125 maploc->looted |= TREE_LOOTED;
1126 return 1;
1127 } else if (!(maploc->looted & TREE_SWARM)) {
1128 int cnt = rnl(4) + 2;
1129 int made = 0;
1130 coord mm;
1132 mm.x = x;
1133 mm.y = y;
1134 while (cnt--) {
1135 if (enexto(&mm, mm.x, mm.y, &mons[PM_KILLER_BEE])
1136 && makemon(&mons[PM_KILLER_BEE], mm.x, mm.y,
1137 MM_ANGRY))
1138 made++;
1140 if (made)
1141 pline("You've attracted the tree's former occupants!");
1142 else
1143 You("smell stale honey.");
1144 maploc->looted |= TREE_SWARM;
1145 return 1;
1147 goto ouch;
1149 if (IS_SINK(maploc->typ)) {
1150 int gend = poly_gender();
1151 short washerndx = (gend == 1 || (gend == 2 && rn2(2)))
1152 ? PM_INCUBUS
1153 : PM_SUCCUBUS;
1155 if (Levitation)
1156 goto dumb;
1157 if (rn2(5)) {
1158 if (!Deaf)
1159 pline("Klunk! The pipes vibrate noisily.");
1160 else
1161 pline("Klunk!");
1162 exercise(A_DEX, TRUE);
1163 return 1;
1164 } else if (!(maploc->looted & S_LPUDDING) && !rn2(3)
1165 && !(mvitals[PM_BLACK_PUDDING].mvflags & G_GONE)) {
1166 if (Blind)
1167 You_hear("a gushing sound.");
1168 else
1169 pline("A %s ooze gushes up from the drain!",
1170 hcolor(NH_BLACK));
1171 (void) makemon(&mons[PM_BLACK_PUDDING], x, y, NO_MM_FLAGS);
1172 exercise(A_DEX, TRUE);
1173 newsym(x, y);
1174 maploc->looted |= S_LPUDDING;
1175 return 1;
1176 } else if (!(maploc->looted & S_LDWASHER) && !rn2(3)
1177 && !(mvitals[washerndx].mvflags & G_GONE)) {
1178 /* can't resist... */
1179 pline("%s returns!", (Blind ? Something : "The dish washer"));
1180 if (makemon(&mons[washerndx], x, y, NO_MM_FLAGS))
1181 newsym(x, y);
1182 maploc->looted |= S_LDWASHER;
1183 exercise(A_DEX, TRUE);
1184 return 1;
1185 } else if (!rn2(3)) {
1186 pline("Flupp! %s.",
1187 (Blind ? "You hear a sloshing sound"
1188 : "Muddy waste pops up from the drain"));
1189 if (!(maploc->looted & S_LRING)) { /* once per sink */
1190 if (!Blind)
1191 You_see("a ring shining in its midst.");
1192 (void) mkobj_at(RING_CLASS, x, y, TRUE);
1193 newsym(x, y);
1194 exercise(A_DEX, TRUE);
1195 exercise(A_WIS, TRUE); /* a discovery! */
1196 maploc->looted |= S_LRING;
1198 return 1;
1200 goto ouch;
1202 if (maploc->typ == STAIRS || maploc->typ == LADDER
1203 || IS_STWALL(maploc->typ)) {
1204 if (!IS_STWALL(maploc->typ) && maploc->ladder == LA_DOWN)
1205 goto dumb;
1206 ouch:
1207 pline("Ouch! That hurts!");
1208 exercise(A_DEX, FALSE);
1209 exercise(A_STR, FALSE);
1210 if (isok(x, y)) {
1211 if (Blind)
1212 feel_location(x, y); /* we know we hit it */
1213 if (is_drawbridge_wall(x, y) >= 0) {
1214 pline_The("drawbridge is unaffected.");
1215 /* update maploc to refer to the drawbridge */
1216 (void) find_drawbridge(&x, &y);
1217 maploc = &levl[x][y];
1220 if (!rn2(3))
1221 set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
1222 dmg = rnd(ACURR(A_CON) > 15 ? 3 : 5);
1223 losehp(Maybe_Half_Phys(dmg), kickstr(buf, kickobjnam), KILLED_BY);
1224 if (Is_airlevel(&u.uz) || Levitation)
1225 hurtle(-u.dx, -u.dy, rn1(2, 4), TRUE); /* assume it's heavy */
1226 return 1;
1228 goto dumb;
1231 if (maploc->doormask == D_ISOPEN || maploc->doormask == D_BROKEN
1232 || maploc->doormask == D_NODOOR) {
1233 dumb:
1234 exercise(A_DEX, FALSE);
1235 if (martial() || ACURR(A_DEX) >= 16 || rn2(3)) {
1236 You("kick at empty space.");
1237 if (Blind)
1238 feel_location(x, y);
1239 } else {
1240 pline("Dumb move! You strain a muscle.");
1241 exercise(A_STR, FALSE);
1242 set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
1244 if ((Is_airlevel(&u.uz) || Levitation) && rn2(2))
1245 hurtle(-u.dx, -u.dy, 1, TRUE);
1246 return 1; /* uses a turn */
1249 /* not enough leverage to kick open doors while levitating */
1250 if (Levitation)
1251 goto ouch;
1253 exercise(A_DEX, TRUE);
1254 /* door is known to be CLOSED or LOCKED */
1255 if (rnl(35) < avrg_attrib + (!martial() ? 0 : ACURR(A_DEX))) {
1256 boolean shopdoor = *in_rooms(x, y, SHOPBASE) ? TRUE : FALSE;
1257 /* break the door */
1258 if (maploc->doormask & D_TRAPPED) {
1259 if (flags.verbose)
1260 You("kick the door.");
1261 exercise(A_STR, FALSE);
1262 maploc->doormask = D_NODOOR;
1263 b_trapped("door", FOOT);
1264 } else if (ACURR(A_STR) > 18 && !rn2(5) && !shopdoor) {
1265 pline("As you kick the door, it shatters to pieces!");
1266 exercise(A_STR, TRUE);
1267 maploc->doormask = D_NODOOR;
1268 } else {
1269 pline("As you kick the door, it crashes open!");
1270 exercise(A_STR, TRUE);
1271 maploc->doormask = D_BROKEN;
1273 feel_newsym(x, y); /* we know we broke it */
1274 unblock_point(x, y); /* vision */
1275 if (shopdoor) {
1276 add_damage(x, y, 400L);
1277 pay_for_damage("break", FALSE);
1279 if (in_town(x, y))
1280 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1281 if (DEADMONSTER(mtmp))
1282 continue;
1283 if (is_watch(mtmp->data) && couldsee(mtmp->mx, mtmp->my)
1284 && mtmp->mpeaceful) {
1285 mon_yells(mtmp, "Halt, thief! You're under arrest!");
1286 (void) angry_guards(FALSE);
1287 break;
1290 } else {
1291 if (Blind)
1292 feel_location(x, y); /* we know we hit it */
1293 exercise(A_STR, TRUE);
1294 pline("WHAMMM!!!");
1295 if (in_town(x, y))
1296 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1297 if (DEADMONSTER(mtmp))
1298 continue;
1299 if (is_watch(mtmp->data) && mtmp->mpeaceful
1300 && couldsee(mtmp->mx, mtmp->my)) {
1301 if (levl[x][y].looted & D_WARNED) {
1302 mon_yells(mtmp,
1303 "Halt, vandal! You're under arrest!");
1304 (void) angry_guards(FALSE);
1305 } else {
1306 mon_yells(mtmp, "Hey, stop damaging that door!");
1307 levl[x][y].looted |= D_WARNED;
1309 break;
1313 return 1;
1316 STATIC_OVL void
1317 drop_to(cc, loc)
1318 coord *cc;
1319 schar loc;
1321 /* cover all the MIGR_xxx choices generated by down_gate() */
1322 switch (loc) {
1323 case MIGR_RANDOM: /* trap door or hole */
1324 if (Is_stronghold(&u.uz)) {
1325 cc->x = valley_level.dnum;
1326 cc->y = valley_level.dlevel;
1327 break;
1328 } else if (In_endgame(&u.uz) || Is_botlevel(&u.uz)) {
1329 cc->y = cc->x = 0;
1330 break;
1331 } /* else fall to the next cases */
1332 case MIGR_STAIRS_UP:
1333 case MIGR_LADDER_UP:
1334 cc->x = u.uz.dnum;
1335 cc->y = u.uz.dlevel + 1;
1336 break;
1337 case MIGR_SSTAIRS:
1338 cc->x = sstairs.tolev.dnum;
1339 cc->y = sstairs.tolev.dlevel;
1340 break;
1341 default:
1342 case MIGR_NOWHERE:
1343 /* y==0 means "nowhere", in which case x doesn't matter */
1344 cc->y = cc->x = 0;
1345 break;
1349 /* player or missile impacts location, causing objects to fall down */
1350 void
1351 impact_drop(missile, x, y, dlev)
1352 struct obj *missile; /* caused impact, won't drop itself */
1353 xchar x, y; /* location affected */
1354 xchar dlev; /* if !0 send to dlev near player */
1356 schar toloc;
1357 register struct obj *obj, *obj2;
1358 register struct monst *shkp;
1359 long oct, dct, price, debit, robbed;
1360 boolean angry, costly, isrock;
1361 coord cc;
1363 if (!OBJ_AT(x, y))
1364 return;
1366 toloc = down_gate(x, y);
1367 drop_to(&cc, toloc);
1368 if (!cc.y)
1369 return;
1371 if (dlev) {
1372 /* send objects next to player falling through trap door.
1373 * checked in obj_delivery().
1375 toloc = MIGR_WITH_HERO;
1376 cc.y = dlev;
1379 costly = costly_spot(x, y);
1380 price = debit = robbed = 0L;
1381 angry = FALSE;
1382 shkp = (struct monst *) 0;
1383 /* if 'costly', we must keep a record of ESHK(shkp) before
1384 * it undergoes changes through the calls to stolen_value.
1385 * the angry bit must be reset, if needed, in this fn, since
1386 * stolen_value is called under the 'silent' flag to avoid
1387 * unsavory pline repetitions.
1389 if (costly) {
1390 if ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) != 0) {
1391 debit = ESHK(shkp)->debit;
1392 robbed = ESHK(shkp)->robbed;
1393 angry = !shkp->mpeaceful;
1397 isrock = (missile && missile->otyp == ROCK);
1398 oct = dct = 0L;
1399 for (obj = level.objects[x][y]; obj; obj = obj2) {
1400 obj2 = obj->nexthere;
1401 if (obj == missile)
1402 continue;
1403 /* number of objects in the pile */
1404 oct += obj->quan;
1405 if (obj == uball || obj == uchain)
1406 continue;
1407 /* boulders can fall too, but rarely & never due to rocks */
1408 if ((isrock && obj->otyp == BOULDER)
1409 || rn2(obj->otyp == BOULDER ? 30 : 3))
1410 continue;
1411 obj_extract_self(obj);
1413 if (costly) {
1414 price += stolen_value(
1415 obj, x, y, (costly_spot(u.ux, u.uy)
1416 && index(u.urooms, *in_rooms(x, y, SHOPBASE))),
1417 TRUE);
1418 /* set obj->no_charge to 0 */
1419 if (Has_contents(obj))
1420 picked_container(obj); /* does the right thing */
1421 if (obj->oclass != COIN_CLASS)
1422 obj->no_charge = 0;
1425 add_to_migration(obj);
1426 obj->ox = cc.x;
1427 obj->oy = cc.y;
1428 obj->owornmask = (long) toloc;
1430 /* number of fallen objects */
1431 dct += obj->quan;
1434 if (dct && cansee(x, y)) { /* at least one object fell */
1435 const char *what = (dct == 1L ? "object falls" : "objects fall");
1437 if (missile)
1438 pline("From the impact, %sother %s.",
1439 dct == oct ? "the " : dct == 1L ? "an" : "", what);
1440 else if (oct == dct)
1441 pline("%s adjacent %s %s.", dct == 1L ? "The" : "All the", what,
1442 gate_str);
1443 else
1444 pline("%s adjacent %s %s.",
1445 dct == 1L ? "One of the" : "Some of the",
1446 dct == 1L ? "objects falls" : what, gate_str);
1449 if (costly && shkp && price) {
1450 if (ESHK(shkp)->robbed > robbed) {
1451 You("removed %ld %s worth of goods!", price, currency(price));
1452 if (cansee(shkp->mx, shkp->my)) {
1453 if (ESHK(shkp)->customer[0] == 0)
1454 (void) strncpy(ESHK(shkp)->customer, plname, PL_NSIZ);
1455 if (angry)
1456 pline("%s is infuriated!", Monnam(shkp));
1457 else
1458 pline("\"%s, you are a thief!\"", plname);
1459 } else
1460 You_hear("a scream, \"Thief!\"");
1461 hot_pursuit(shkp);
1462 (void) angry_guards(FALSE);
1463 return;
1465 if (ESHK(shkp)->debit > debit) {
1466 long amt = (ESHK(shkp)->debit - debit);
1467 You("owe %s %ld %s for goods lost.", Monnam(shkp), amt,
1468 currency(amt));
1473 /* NOTE: ship_object assumes otmp was FREED from fobj or invent.
1474 * <x,y> is the point of drop. otmp is _not_ an <x,y> resident:
1475 * otmp is either a kicked, dropped, or thrown object.
1477 boolean
1478 ship_object(otmp, x, y, shop_floor_obj)
1479 xchar x, y;
1480 struct obj *otmp;
1481 boolean shop_floor_obj;
1483 schar toloc;
1484 xchar ox, oy;
1485 coord cc;
1486 struct obj *obj;
1487 struct trap *t;
1488 boolean nodrop, unpaid, container, impact = FALSE;
1489 long n = 0L;
1491 if (!otmp)
1492 return FALSE;
1493 if ((toloc = down_gate(x, y)) == MIGR_NOWHERE)
1494 return FALSE;
1495 drop_to(&cc, toloc);
1496 if (!cc.y)
1497 return FALSE;
1499 /* objects other than attached iron ball always fall down ladder,
1500 but have a chance of staying otherwise */
1501 nodrop = (otmp == uball) || (otmp == uchain)
1502 || (toloc != MIGR_LADDER_UP && rn2(3));
1504 container = Has_contents(otmp);
1505 unpaid = is_unpaid(otmp);
1507 if (OBJ_AT(x, y)) {
1508 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
1509 if (obj != otmp)
1510 n += obj->quan;
1511 if (n)
1512 impact = TRUE;
1514 /* boulders never fall through trap doors, but they might knock
1515 other things down before plugging the hole */
1516 if (otmp->otyp == BOULDER && ((t = t_at(x, y)) != 0)
1517 && (t->ttyp == TRAPDOOR || t->ttyp == HOLE)) {
1518 if (impact)
1519 impact_drop(otmp, x, y, 0);
1520 return FALSE; /* let caller finish the drop */
1523 if (cansee(x, y))
1524 otransit_msg(otmp, nodrop, n);
1526 if (nodrop) {
1527 if (impact)
1528 impact_drop(otmp, x, y, 0);
1529 return FALSE;
1532 if (unpaid || shop_floor_obj) {
1533 if (unpaid) {
1534 (void) stolen_value(otmp, u.ux, u.uy, TRUE, FALSE);
1535 } else {
1536 ox = otmp->ox;
1537 oy = otmp->oy;
1538 (void) stolen_value(
1539 otmp, ox, oy,
1540 (costly_spot(u.ux, u.uy)
1541 && index(u.urooms, *in_rooms(ox, oy, SHOPBASE))),
1542 FALSE);
1544 /* set otmp->no_charge to 0 */
1545 if (container)
1546 picked_container(otmp); /* happens to do the right thing */
1547 if (otmp->oclass != COIN_CLASS)
1548 otmp->no_charge = 0;
1551 if (otmp->owornmask)
1552 remove_worn_item(otmp, TRUE);
1554 /* some things break rather than ship */
1555 if (breaktest(otmp)) {
1556 const char *result;
1558 if (objects[otmp->otyp].oc_material == GLASS
1559 || otmp->otyp == EXPENSIVE_CAMERA) {
1560 if (otmp->otyp == MIRROR)
1561 change_luck(-2);
1562 result = "crash";
1563 } else {
1564 /* penalty for breaking eggs laid by you */
1565 if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM)
1566 change_luck((schar) -min(otmp->quan, 5L));
1567 result = "splat";
1569 You_hear("a muffled %s.", result);
1570 obj_extract_self(otmp);
1571 obfree(otmp, (struct obj *) 0);
1572 return TRUE;
1575 add_to_migration(otmp);
1576 otmp->ox = cc.x;
1577 otmp->oy = cc.y;
1578 otmp->owornmask = (long) toloc;
1579 /* boulder from rolling boulder trap, no longer part of the trap */
1580 if (otmp->otyp == BOULDER)
1581 otmp->otrapped = 0;
1583 if (impact) {
1584 /* the objs impacted may be in a shop other than
1585 * the one in which the hero is located. another
1586 * check for a shk is made in impact_drop. it is, e.g.,
1587 * possible to kick/throw an object belonging to one
1588 * shop into another shop through a gap in the wall,
1589 * and cause objects belonging to the other shop to
1590 * fall down a trap door--thereby getting two shopkeepers
1591 * angry at the hero in one shot.
1593 impact_drop(otmp, x, y, 0);
1594 newsym(x, y);
1596 return TRUE;
1599 void
1600 obj_delivery(near_hero)
1601 boolean near_hero;
1603 register struct obj *otmp, *otmp2;
1604 register int nx, ny;
1605 int where;
1606 boolean nobreak, noscatter;
1608 for (otmp = migrating_objs; otmp; otmp = otmp2) {
1609 otmp2 = otmp->nobj;
1610 if (otmp->ox != u.uz.dnum || otmp->oy != u.uz.dlevel)
1611 continue;
1613 where = (int) (otmp->owornmask & 0x7fffL); /* destination code */
1614 nobreak = (where & MIGR_NOBREAK) != 0;
1615 noscatter = (where & MIGR_WITH_HERO) != 0;
1616 where &= ~(MIGR_NOBREAK | MIGR_NOSCATTER);
1618 if (!near_hero ^ (where == MIGR_WITH_HERO))
1619 continue;
1621 obj_extract_self(otmp);
1622 otmp->owornmask = 0L;
1624 switch (where) {
1625 case MIGR_STAIRS_UP:
1626 nx = xupstair, ny = yupstair;
1627 break;
1628 case MIGR_LADDER_UP:
1629 nx = xupladder, ny = yupladder;
1630 break;
1631 case MIGR_SSTAIRS:
1632 nx = sstairs.sx, ny = sstairs.sy;
1633 break;
1634 case MIGR_WITH_HERO:
1635 nx = u.ux, ny = u.uy;
1636 break;
1637 default:
1638 case MIGR_RANDOM:
1639 nx = ny = 0;
1640 break;
1642 if (nx > 0) {
1643 place_object(otmp, nx, ny);
1644 if (!nobreak && !IS_SOFT(levl[nx][ny].typ)) {
1645 if (where == MIGR_WITH_HERO) {
1646 if (breaks(otmp, nx, ny))
1647 continue;
1648 } else if (breaktest(otmp)) {
1649 /* assume it broke before player arrived, no messages */
1650 delobj(otmp);
1651 continue;
1654 stackobj(otmp);
1655 if (!noscatter)
1656 (void) scatter(nx, ny, rnd(2), 0, otmp);
1657 } else { /* random location */
1658 /* set dummy coordinates because there's no
1659 current position for rloco() to update */
1660 otmp->ox = otmp->oy = 0;
1661 if (rloco(otmp) && !nobreak && breaktest(otmp)) {
1662 /* assume it broke before player arrived, no messages */
1663 delobj(otmp);
1669 STATIC_OVL void
1670 otransit_msg(otmp, nodrop, num)
1671 register struct obj *otmp;
1672 register boolean nodrop;
1673 long num;
1675 char obuf[BUFSZ];
1677 Sprintf(obuf, "%s%s",
1678 (otmp->otyp == CORPSE && type_is_pname(&mons[otmp->corpsenm]))
1679 ? ""
1680 : "The ",
1681 cxname(otmp));
1683 if (num) { /* means: other objects are impacted */
1684 Sprintf(eos(obuf), " %s %s object%s", otense(otmp, "hit"),
1685 num == 1L ? "another" : "other", num > 1L ? "s" : "");
1686 if (nodrop)
1687 Sprintf(eos(obuf), ".");
1688 else
1689 Sprintf(eos(obuf), " and %s %s.", otense(otmp, "fall"), gate_str);
1690 pline1(obuf);
1691 } else if (!nodrop)
1692 pline("%s %s %s.", obuf, otense(otmp, "fall"), gate_str);
1695 /* migration destination for objects which fall down to next level */
1696 schar
1697 down_gate(x, y)
1698 xchar x, y;
1700 struct trap *ttmp;
1702 gate_str = 0;
1703 /* this matches the player restriction in goto_level() */
1704 if (on_level(&u.uz, &qstart_level) && !ok_to_quest())
1705 return MIGR_NOWHERE;
1707 if ((xdnstair == x && ydnstair == y)
1708 || (sstairs.sx == x && sstairs.sy == y && !sstairs.up)) {
1709 gate_str = "down the stairs";
1710 return (xdnstair == x && ydnstair == y) ? MIGR_STAIRS_UP
1711 : MIGR_SSTAIRS;
1713 if (xdnladder == x && ydnladder == y) {
1714 gate_str = "down the ladder";
1715 return MIGR_LADDER_UP;
1718 if (((ttmp = t_at(x, y)) != 0 && ttmp->tseen)
1719 && (ttmp->ttyp == TRAPDOOR || ttmp->ttyp == HOLE)) {
1720 gate_str = (ttmp->ttyp == TRAPDOOR) ? "through the trap door"
1721 : "through the hole";
1722 return MIGR_RANDOM;
1724 return MIGR_NOWHERE;
1727 /*dokick.c*/