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*/