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