1 /* $NetBSD: hack.fight.c,v 1.7 2006/03/29 01:18:39 jnemeth Exp $ */ 2 3 /* 4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, 5 * Amsterdam 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are 10 * met: 11 * 12 * - Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * - Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * - Neither the name of the Stichting Centrum voor Wiskunde en 20 * Informatica, nor the names of its contributors may be used to endorse or 21 * promote products derived from this software without specific prior 22 * written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 /* 38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org> 39 * All rights reserved. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. The name of the author may not be used to endorse or promote products 50 * derived from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 */ 63 64 #include <sys/cdefs.h> 65 #ifndef lint 66 __RCSID("$NetBSD: hack.fight.c,v 1.7 2006/03/29 01:18:39 jnemeth Exp $"); 67 #endif /* not lint */ 68 69 #include "hack.h" 70 #include "extern.h" 71 72 static boolean far_noise; 73 static long noisetime; 74 75 /* hitmm returns 0 (miss), 1 (hit), or 2 (kill) */ 76 int 77 hitmm(magr, mdef) 78 struct monst *magr, *mdef; 79 { 80 const struct permonst *pa = magr->data, *pd = mdef->data; 81 int hit; 82 schar tmp; 83 boolean vis; 84 if (strchr("Eauy", pa->mlet)) 85 return (0); 86 if (magr->mfroz) 87 return (0); /* riv05!a3 */ 88 tmp = pd->ac + pa->mlevel; 89 if (mdef->mconf || mdef->mfroz || mdef->msleep) { 90 tmp += 4; 91 if (mdef->msleep) 92 mdef->msleep = 0; 93 } 94 hit = (tmp > rnd(20)); 95 if (hit) 96 mdef->msleep = 0; 97 vis = (cansee(magr->mx, magr->my) && cansee(mdef->mx, mdef->my)); 98 if (vis) { 99 char buf[BUFSZ]; 100 if (mdef->mimic) 101 seemimic(mdef); 102 if (magr->mimic) 103 seemimic(magr); 104 (void) sprintf(buf, "%s %s", Monnam(magr), 105 hit ? "hits" : "misses"); 106 pline("%s %s.", buf, monnam(mdef)); 107 } else { 108 boolean far = (dist(magr->mx, magr->my) > 15); 109 if (far != far_noise || moves - noisetime > 10) { 110 far_noise = far; 111 noisetime = moves; 112 pline("You hear some noises%s.", 113 far ? " in the distance" : ""); 114 } 115 } 116 if (hit) { 117 if (magr->data->mlet == 'c' && !magr->cham) { 118 magr->mhpmax += 3; 119 if (vis) 120 pline("%s is turned to stone!", Monnam(mdef)); 121 else if (mdef->mtame) 122 pline("You have a peculiarly sad feeling for a moment, then it passes."); 123 monstone(mdef); 124 hit = 2; 125 } else if ((mdef->mhp -= d(pa->damn, pa->damd)) < 1) { 126 magr->mhpmax += 1 + rn2(pd->mlevel + 1); 127 if (magr->mtame && magr->mhpmax > 8 * pa->mlevel) { 128 if (pa == &li_dog) 129 magr->data = pa = &dog; 130 else if (pa == &dog) 131 magr->data = pa = &la_dog; 132 } 133 if (vis) 134 pline("%s is killed!", Monnam(mdef)); 135 else if (mdef->mtame) 136 pline("You have a sad feeling for a moment, then it passes."); 137 mondied(mdef); 138 hit = 2; 139 } 140 } 141 return (hit); 142 } 143 144 /* drop (perhaps) a cadaver and remove monster */ 145 void 146 mondied(mdef) 147 struct monst *mdef; 148 { 149 const struct permonst *pd = mdef->data; 150 if (letter(pd->mlet) && rn2(3)) { 151 (void) mkobj_at(pd->mlet, mdef->mx, mdef->my); 152 if (cansee(mdef->mx, mdef->my)) { 153 unpmon(mdef); 154 atl(mdef->mx, mdef->my, fobj->olet); 155 } 156 stackobj(fobj); 157 } 158 mondead(mdef); 159 } 160 161 /* drop a rock and remove monster */ 162 void 163 monstone(mdef) 164 struct monst *mdef; 165 { 166 if (strchr(mlarge, mdef->data->mlet)) 167 mksobj_at(ENORMOUS_ROCK, mdef->mx, mdef->my); 168 else 169 mksobj_at(ROCK, mdef->mx, mdef->my); 170 if (cansee(mdef->mx, mdef->my)) { 171 unpmon(mdef); 172 atl(mdef->mx, mdef->my, fobj->olet); 173 } 174 mondead(mdef); 175 } 176 177 178 int 179 fightm(mtmp) 180 struct monst *mtmp; 181 { 182 struct monst *mon; 183 for (mon = fmon; mon; mon = mon->nmon) 184 if (mon != mtmp) { 185 if (DIST(mon->mx, mon->my, mtmp->mx, mtmp->my) < 3) 186 if (rn2(4)) 187 return (hitmm(mtmp, mon)); 188 } 189 return (-1); 190 } 191 192 /* u is hit by sth, but not a monster */ 193 int 194 thitu(tlev, dam, name) 195 int tlev, dam; 196 const char *name; 197 { 198 char buf[BUFSZ]; 199 setan(name, buf); 200 if (u.uac + tlev <= rnd(20)) { 201 if (Blind) 202 pline("It misses."); 203 else 204 pline("You are almost hit by %s!", buf); 205 return (0); 206 } else { 207 if (Blind) 208 pline("You are hit!"); 209 else 210 pline("You are hit by %s!", buf); 211 losehp(dam, name); 212 return (1); 213 } 214 } 215 216 char mlarge[] = "bCDdegIlmnoPSsTUwY',&"; 217 218 boolean 219 hmon(mon, obj, thrown) /* return TRUE if mon still alive */ 220 struct monst *mon; 221 struct obj *obj; 222 int thrown; 223 { 224 int tmp; 225 boolean hittxt = FALSE; 226 227 if (!obj) { 228 tmp = rnd(2); /* attack with bare hands */ 229 if (mon->data->mlet == 'c' && !uarmg) { 230 pline("You hit the cockatrice with your bare hands."); 231 pline("You turn to stone ..."); 232 done_in_by(mon); 233 } 234 } else if (obj->olet == WEAPON_SYM || obj->otyp == PICK_AXE) { 235 if (obj == uwep && (obj->otyp > SPEAR || obj->otyp < BOOMERANG)) 236 tmp = rnd(2); 237 else { 238 if (strchr(mlarge, mon->data->mlet)) { 239 tmp = rnd(objects[obj->otyp].wldam); 240 if (obj->otyp == TWO_HANDED_SWORD) 241 tmp += d(2, 6); 242 else if (obj->otyp == FLAIL) 243 tmp += rnd(4); 244 } else { 245 tmp = rnd(objects[obj->otyp].wsdam); 246 } 247 tmp += obj->spe; 248 if (!thrown && obj == uwep && obj->otyp == BOOMERANG 249 && !rn2(3)) { 250 pline("As you hit %s, the boomerang breaks into splinters.", 251 monnam(mon)); 252 freeinv(obj); 253 setworn((struct obj *) 0, obj->owornmask); 254 obfree(obj, (struct obj *) 0); 255 obj = NULL; 256 tmp++; 257 } 258 } 259 if (mon->data->mlet == 'O' && obj != NULL && 260 obj->otyp == TWO_HANDED_SWORD && 261 !strcmp(ONAME(obj), "Orcrist")) 262 tmp += rnd(10); 263 } else 264 switch (obj->otyp) { 265 case HEAVY_IRON_BALL: 266 tmp = rnd(25); 267 break; 268 case EXPENSIVE_CAMERA: 269 pline("You succeed in destroying your camera. Congratulations!"); 270 freeinv(obj); 271 if (obj->owornmask) 272 setworn((struct obj *) 0, obj->owornmask); 273 obfree(obj, (struct obj *) 0); 274 return (TRUE); 275 case DEAD_COCKATRICE: 276 pline("You hit %s with the cockatrice corpse.", 277 monnam(mon)); 278 if (mon->data->mlet == 'c') { 279 tmp = 1; 280 hittxt = TRUE; 281 break; 282 } 283 pline("%s is turned to stone!", Monnam(mon)); 284 killed(mon); 285 return (FALSE); 286 case CLOVE_OF_GARLIC: /* no effect against demons */ 287 if (strchr(UNDEAD, mon->data->mlet)) 288 mon->mflee = 1; 289 tmp = 1; 290 break; 291 default: 292 /* non-weapons can damage because of their weight */ 293 /* (but not too much) */ 294 tmp = obj->owt / 10; 295 if (tmp < 1) 296 tmp = 1; 297 else 298 tmp = rnd(tmp); 299 if (tmp > 6) 300 tmp = 6; 301 } 302 303 /****** NOTE: perhaps obj is undefined!! (if !thrown && BOOMERANG) */ 304 305 tmp += u.udaminc + dbon(); 306 if (u.uswallow) { 307 if ((tmp -= u.uswldtim) <= 0) { 308 pline("Your arms are no longer able to hit."); 309 return (TRUE); 310 } 311 } 312 if (tmp < 1) 313 tmp = 1; 314 mon->mhp -= tmp; 315 if (mon->mhp < 1) { 316 killed(mon); 317 return (FALSE); 318 } 319 if (mon->mtame && (!mon->mflee || mon->mfleetim)) { 320 mon->mflee = 1; /* Rick Richardson */ 321 mon->mfleetim += 10 * rnd(tmp); 322 } 323 if (!hittxt) { 324 if (thrown) { 325 /* this assumes that we cannot throw plural things */ 326 if (obj == NULL) 327 panic("thrown non-object"); 328 hit(xname(obj) /* or: objects[obj->otyp].oc_name */ , 329 mon, exclam(tmp)); 330 } else if (Blind) 331 pline("You hit it."); 332 else 333 pline("You hit %s%s", monnam(mon), exclam(tmp)); 334 } 335 if (u.umconf && !thrown) { 336 if (!Blind) { 337 pline("Your hands stop glowing blue."); 338 if (!mon->mfroz && !mon->msleep) 339 pline("%s appears confused.", Monnam(mon)); 340 } 341 mon->mconf = 1; 342 u.umconf = 0; 343 } 344 return (TRUE); /* mon still alive */ 345 } 346 347 /* try to attack; return FALSE if monster evaded */ 348 /* u.dx and u.dy must be set */ 349 int 350 attack(mtmp) 351 struct monst *mtmp; 352 { 353 schar tmp; 354 boolean malive = TRUE; 355 const struct permonst *mdat; 356 mdat = mtmp->data; 357 358 u_wipe_engr(3); /* andrew@orca: prevent unlimited pick-axe 359 * attacks */ 360 361 if (mdat->mlet == 'L' && !mtmp->mfroz && !mtmp->msleep && 362 !mtmp->mconf && mtmp->mcansee && !rn2(7) && 363 (m_move(mtmp, 0) == 2 /* he died */ || /* he moved: */ 364 mtmp->mx != u.ux + u.dx || mtmp->my != u.uy + u.dy)) 365 return (FALSE); 366 367 if (mtmp->mimic) { 368 if (!u.ustuck && !mtmp->mflee) 369 u.ustuck = mtmp; 370 switch (levl[u.ux + u.dx][u.uy + u.dy].scrsym) { 371 case '+': 372 pline("The door actually was a Mimic."); 373 break; 374 case '$': 375 pline("The chest was a Mimic!"); 376 break; 377 default: 378 pline("Wait! That's a Mimic!"); 379 } 380 wakeup(mtmp); /* clears mtmp->mimic */ 381 return (TRUE); 382 } 383 wakeup(mtmp); 384 385 if (mtmp->mhide && mtmp->mundetected) { 386 struct obj *obj; 387 388 mtmp->mundetected = 0; 389 if ((obj = o_at(mtmp->mx, mtmp->my)) && !Blind) 390 pline("Wait! There's a %s hiding under %s!", 391 mdat->mname, doname(obj)); 392 return (TRUE); 393 } 394 tmp = u.uluck + u.ulevel + mdat->ac + abon(); 395 if (uwep) { 396 if (uwep->olet == WEAPON_SYM || uwep->otyp == PICK_AXE) 397 tmp += uwep->spe; 398 if (uwep->otyp == TWO_HANDED_SWORD) 399 tmp -= 1; 400 else if (uwep->otyp == DAGGER) 401 tmp += 2; 402 else if (uwep->otyp == CRYSKNIFE) 403 tmp += 3; 404 else if (uwep->otyp == SPEAR && 405 strchr("XDne", mdat->mlet)) 406 tmp += 2; 407 } 408 if (mtmp->msleep) { 409 mtmp->msleep = 0; 410 tmp += 2; 411 } 412 if (mtmp->mfroz) { 413 tmp += 4; 414 if (!rn2(10)) 415 mtmp->mfroz = 0; 416 } 417 if (mtmp->mflee) 418 tmp += 2; 419 if (u.utrap) 420 tmp -= 3; 421 422 /* with a lot of luggage, your agility diminishes */ 423 tmp -= (inv_weight() + 40) / 20; 424 425 if (tmp <= rnd(20) && !u.uswallow) { 426 if (Blind) 427 pline("You miss it."); 428 else 429 pline("You miss %s.", monnam(mtmp)); 430 } else { 431 /* we hit the monster; be careful: it might die! */ 432 433 if ((malive = hmon(mtmp, uwep, 0)) == TRUE) { 434 /* monster still alive */ 435 if (!rn2(25) && mtmp->mhp < mtmp->mhpmax / 2) { 436 mtmp->mflee = 1; 437 if (!rn2(3)) 438 mtmp->mfleetim = rnd(100); 439 if (u.ustuck == mtmp && !u.uswallow) 440 u.ustuck = 0; 441 } 442 #ifndef NOWORM 443 if (mtmp->wormno) 444 cutworm(mtmp, u.ux + u.dx, u.uy + u.dy, 445 uwep ? uwep->otyp : 0); 446 #endif /* NOWORM */ 447 } 448 if (mdat->mlet == 'a') { 449 if (rn2(2)) { 450 pline("You are splashed by the blob's acid!"); 451 losehp_m(rnd(6), mtmp); 452 if (!rn2(30)) 453 corrode_armor(); 454 } 455 if (!rn2(6)) 456 corrode_weapon(); 457 } 458 } 459 if (malive && mdat->mlet == 'E' && canseemon(mtmp) 460 && !mtmp->mcan && rn2(3)) { 461 if (mtmp->mcansee) { 462 pline("You are frozen by the floating eye's gaze!"); 463 nomul((u.ulevel > 6 || rn2(4)) ? rn1(20, -21) : -200); 464 } else { 465 pline("The blinded floating eye cannot defend itself."); 466 if (!rn2(500)) 467 if ((int) u.uluck > LUCKMIN) 468 u.uluck--; 469 } 470 } 471 return (TRUE); 472 } 473