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