1 /* $NetBSD: hack.fight.c,v 1.5 2001/03/25 20:44:00 jsm Exp $ */ 2 3 /* 4 * Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. 5 */ 6 7 #include <sys/cdefs.h> 8 #ifndef lint 9 __RCSID("$NetBSD: hack.fight.c,v 1.5 2001/03/25 20:44:00 jsm Exp $"); 10 #endif /* not lint */ 11 12 #include "hack.h" 13 #include "extern.h" 14 15 static boolean far_noise; 16 static long noisetime; 17 18 /* hitmm returns 0 (miss), 1 (hit), or 2 (kill) */ 19 int 20 hitmm(magr, mdef) 21 struct monst *magr, *mdef; 22 { 23 const struct permonst *pa = magr->data, *pd = mdef->data; 24 int hit; 25 schar tmp; 26 boolean vis; 27 if (strchr("Eauy", pa->mlet)) 28 return (0); 29 if (magr->mfroz) 30 return (0); /* riv05!a3 */ 31 tmp = pd->ac + pa->mlevel; 32 if (mdef->mconf || mdef->mfroz || mdef->msleep) { 33 tmp += 4; 34 if (mdef->msleep) 35 mdef->msleep = 0; 36 } 37 hit = (tmp > rnd(20)); 38 if (hit) 39 mdef->msleep = 0; 40 vis = (cansee(magr->mx, magr->my) && cansee(mdef->mx, mdef->my)); 41 if (vis) { 42 char buf[BUFSZ]; 43 if (mdef->mimic) 44 seemimic(mdef); 45 if (magr->mimic) 46 seemimic(magr); 47 (void) sprintf(buf, "%s %s", Monnam(magr), 48 hit ? "hits" : "misses"); 49 pline("%s %s.", buf, monnam(mdef)); 50 } else { 51 boolean far = (dist(magr->mx, magr->my) > 15); 52 if (far != far_noise || moves - noisetime > 10) { 53 far_noise = far; 54 noisetime = moves; 55 pline("You hear some noises%s.", 56 far ? " in the distance" : ""); 57 } 58 } 59 if (hit) { 60 if (magr->data->mlet == 'c' && !magr->cham) { 61 magr->mhpmax += 3; 62 if (vis) 63 pline("%s is turned to stone!", Monnam(mdef)); 64 else if (mdef->mtame) 65 pline("You have a peculiarly sad feeling for a moment, then it passes."); 66 monstone(mdef); 67 hit = 2; 68 } else if ((mdef->mhp -= d(pa->damn, pa->damd)) < 1) { 69 magr->mhpmax += 1 + rn2(pd->mlevel + 1); 70 if (magr->mtame && magr->mhpmax > 8 * pa->mlevel) { 71 if (pa == &li_dog) 72 magr->data = pa = &dog; 73 else if (pa == &dog) 74 magr->data = pa = &la_dog; 75 } 76 if (vis) 77 pline("%s is killed!", Monnam(mdef)); 78 else if (mdef->mtame) 79 pline("You have a sad feeling for a moment, then it passes."); 80 mondied(mdef); 81 hit = 2; 82 } 83 } 84 return (hit); 85 } 86 87 /* drop (perhaps) a cadaver and remove monster */ 88 void 89 mondied(mdef) 90 struct monst *mdef; 91 { 92 const struct permonst *pd = mdef->data; 93 if (letter(pd->mlet) && rn2(3)) { 94 (void) mkobj_at(pd->mlet, mdef->mx, mdef->my); 95 if (cansee(mdef->mx, mdef->my)) { 96 unpmon(mdef); 97 atl(mdef->mx, mdef->my, fobj->olet); 98 } 99 stackobj(fobj); 100 } 101 mondead(mdef); 102 } 103 104 /* drop a rock and remove monster */ 105 void 106 monstone(mdef) 107 struct monst *mdef; 108 { 109 if (strchr(mlarge, mdef->data->mlet)) 110 mksobj_at(ENORMOUS_ROCK, mdef->mx, mdef->my); 111 else 112 mksobj_at(ROCK, mdef->mx, mdef->my); 113 if (cansee(mdef->mx, mdef->my)) { 114 unpmon(mdef); 115 atl(mdef->mx, mdef->my, fobj->olet); 116 } 117 mondead(mdef); 118 } 119 120 121 int 122 fightm(mtmp) 123 struct monst *mtmp; 124 { 125 struct monst *mon; 126 for (mon = fmon; mon; mon = mon->nmon) 127 if (mon != mtmp) { 128 if (DIST(mon->mx, mon->my, mtmp->mx, mtmp->my) < 3) 129 if (rn2(4)) 130 return (hitmm(mtmp, mon)); 131 } 132 return (-1); 133 } 134 135 /* u is hit by sth, but not a monster */ 136 int 137 thitu(tlev, dam, name) 138 int tlev, dam; 139 const char *name; 140 { 141 char buf[BUFSZ]; 142 setan(name, buf); 143 if (u.uac + tlev <= rnd(20)) { 144 if (Blind) 145 pline("It misses."); 146 else 147 pline("You are almost hit by %s!", buf); 148 return (0); 149 } else { 150 if (Blind) 151 pline("You are hit!"); 152 else 153 pline("You are hit by %s!", buf); 154 losehp(dam, name); 155 return (1); 156 } 157 } 158 159 char mlarge[] = "bCDdegIlmnoPSsTUwY',&"; 160 161 boolean 162 hmon(mon, obj, thrown) /* return TRUE if mon still alive */ 163 struct monst *mon; 164 struct obj *obj; 165 int thrown; 166 { 167 int tmp; 168 boolean hittxt = FALSE; 169 170 if (!obj) { 171 tmp = rnd(2); /* attack with bare hands */ 172 if (mon->data->mlet == 'c' && !uarmg) { 173 pline("You hit the cockatrice with your bare hands."); 174 pline("You turn to stone ..."); 175 done_in_by(mon); 176 } 177 } else if (obj->olet == WEAPON_SYM || obj->otyp == PICK_AXE) { 178 if (obj == uwep && (obj->otyp > SPEAR || obj->otyp < BOOMERANG)) 179 tmp = rnd(2); 180 else { 181 if (strchr(mlarge, mon->data->mlet)) { 182 tmp = rnd(objects[obj->otyp].wldam); 183 if (obj->otyp == TWO_HANDED_SWORD) 184 tmp += d(2, 6); 185 else if (obj->otyp == FLAIL) 186 tmp += rnd(4); 187 } else { 188 tmp = rnd(objects[obj->otyp].wsdam); 189 } 190 tmp += obj->spe; 191 if (!thrown && obj == uwep && obj->otyp == BOOMERANG 192 && !rn2(3)) { 193 pline("As you hit %s, the boomerang breaks into splinters.", 194 monnam(mon)); 195 freeinv(obj); 196 setworn((struct obj *) 0, obj->owornmask); 197 obfree(obj, (struct obj *) 0); 198 tmp++; 199 } 200 } 201 if (mon->data->mlet == 'O' && obj->otyp == TWO_HANDED_SWORD && 202 !strcmp(ONAME(obj), "Orcrist")) 203 tmp += rnd(10); 204 } else 205 switch (obj->otyp) { 206 case HEAVY_IRON_BALL: 207 tmp = rnd(25); 208 break; 209 case EXPENSIVE_CAMERA: 210 pline("You succeed in destroying your camera. Congratulations!"); 211 freeinv(obj); 212 if (obj->owornmask) 213 setworn((struct obj *) 0, obj->owornmask); 214 obfree(obj, (struct obj *) 0); 215 return (TRUE); 216 case DEAD_COCKATRICE: 217 pline("You hit %s with the cockatrice corpse.", 218 monnam(mon)); 219 if (mon->data->mlet == 'c') { 220 tmp = 1; 221 hittxt = TRUE; 222 break; 223 } 224 pline("%s is turned to stone!", Monnam(mon)); 225 killed(mon); 226 return (FALSE); 227 case CLOVE_OF_GARLIC: /* no effect against demons */ 228 if (strchr(UNDEAD, mon->data->mlet)) 229 mon->mflee = 1; 230 tmp = 1; 231 break; 232 default: 233 /* non-weapons can damage because of their weight */ 234 /* (but not too much) */ 235 tmp = obj->owt / 10; 236 if (tmp < 1) 237 tmp = 1; 238 else 239 tmp = rnd(tmp); 240 if (tmp > 6) 241 tmp = 6; 242 } 243 244 /****** NOTE: perhaps obj is undefined!! (if !thrown && BOOMERANG) */ 245 246 tmp += u.udaminc + dbon(); 247 if (u.uswallow) { 248 if ((tmp -= u.uswldtim) <= 0) { 249 pline("Your arms are no longer able to hit."); 250 return (TRUE); 251 } 252 } 253 if (tmp < 1) 254 tmp = 1; 255 mon->mhp -= tmp; 256 if (mon->mhp < 1) { 257 killed(mon); 258 return (FALSE); 259 } 260 if (mon->mtame && (!mon->mflee || mon->mfleetim)) { 261 mon->mflee = 1; /* Rick Richardson */ 262 mon->mfleetim += 10 * rnd(tmp); 263 } 264 if (!hittxt) { 265 if (thrown) 266 /* this assumes that we cannot throw plural things */ 267 hit(xname(obj) /* or: objects[obj->otyp].oc_name */ , 268 mon, exclam(tmp)); 269 else if (Blind) 270 pline("You hit it."); 271 else 272 pline("You hit %s%s", monnam(mon), exclam(tmp)); 273 } 274 if (u.umconf && !thrown) { 275 if (!Blind) { 276 pline("Your hands stop glowing blue."); 277 if (!mon->mfroz && !mon->msleep) 278 pline("%s appears confused.", Monnam(mon)); 279 } 280 mon->mconf = 1; 281 u.umconf = 0; 282 } 283 return (TRUE); /* mon still alive */ 284 } 285 286 /* try to attack; return FALSE if monster evaded */ 287 /* u.dx and u.dy must be set */ 288 int 289 attack(mtmp) 290 struct monst *mtmp; 291 { 292 schar tmp; 293 boolean malive = TRUE; 294 const struct permonst *mdat; 295 mdat = mtmp->data; 296 297 u_wipe_engr(3); /* andrew@orca: prevent unlimited pick-axe 298 * attacks */ 299 300 if (mdat->mlet == 'L' && !mtmp->mfroz && !mtmp->msleep && 301 !mtmp->mconf && mtmp->mcansee && !rn2(7) && 302 (m_move(mtmp, 0) == 2 /* he died */ || /* he moved: */ 303 mtmp->mx != u.ux + u.dx || mtmp->my != u.uy + u.dy)) 304 return (FALSE); 305 306 if (mtmp->mimic) { 307 if (!u.ustuck && !mtmp->mflee) 308 u.ustuck = mtmp; 309 switch (levl[u.ux + u.dx][u.uy + u.dy].scrsym) { 310 case '+': 311 pline("The door actually was a Mimic."); 312 break; 313 case '$': 314 pline("The chest was a Mimic!"); 315 break; 316 default: 317 pline("Wait! That's a Mimic!"); 318 } 319 wakeup(mtmp); /* clears mtmp->mimic */ 320 return (TRUE); 321 } 322 wakeup(mtmp); 323 324 if (mtmp->mhide && mtmp->mundetected) { 325 struct obj *obj; 326 327 mtmp->mundetected = 0; 328 if ((obj = o_at(mtmp->mx, mtmp->my)) && !Blind) 329 pline("Wait! There's a %s hiding under %s!", 330 mdat->mname, doname(obj)); 331 return (TRUE); 332 } 333 tmp = u.uluck + u.ulevel + mdat->ac + abon(); 334 if (uwep) { 335 if (uwep->olet == WEAPON_SYM || uwep->otyp == PICK_AXE) 336 tmp += uwep->spe; 337 if (uwep->otyp == TWO_HANDED_SWORD) 338 tmp -= 1; 339 else if (uwep->otyp == DAGGER) 340 tmp += 2; 341 else if (uwep->otyp == CRYSKNIFE) 342 tmp += 3; 343 else if (uwep->otyp == SPEAR && 344 strchr("XDne", mdat->mlet)) 345 tmp += 2; 346 } 347 if (mtmp->msleep) { 348 mtmp->msleep = 0; 349 tmp += 2; 350 } 351 if (mtmp->mfroz) { 352 tmp += 4; 353 if (!rn2(10)) 354 mtmp->mfroz = 0; 355 } 356 if (mtmp->mflee) 357 tmp += 2; 358 if (u.utrap) 359 tmp -= 3; 360 361 /* with a lot of luggage, your agility diminishes */ 362 tmp -= (inv_weight() + 40) / 20; 363 364 if (tmp <= rnd(20) && !u.uswallow) { 365 if (Blind) 366 pline("You miss it."); 367 else 368 pline("You miss %s.", monnam(mtmp)); 369 } else { 370 /* we hit the monster; be careful: it might die! */ 371 372 if ((malive = hmon(mtmp, uwep, 0)) == TRUE) { 373 /* monster still alive */ 374 if (!rn2(25) && mtmp->mhp < mtmp->mhpmax / 2) { 375 mtmp->mflee = 1; 376 if (!rn2(3)) 377 mtmp->mfleetim = rnd(100); 378 if (u.ustuck == mtmp && !u.uswallow) 379 u.ustuck = 0; 380 } 381 #ifndef NOWORM 382 if (mtmp->wormno) 383 cutworm(mtmp, u.ux + u.dx, u.uy + u.dy, 384 uwep ? uwep->otyp : 0); 385 #endif /* NOWORM */ 386 } 387 if (mdat->mlet == 'a') { 388 if (rn2(2)) { 389 pline("You are splashed by the blob's acid!"); 390 losehp_m(rnd(6), mtmp); 391 if (!rn2(30)) 392 corrode_armor(); 393 } 394 if (!rn2(6)) 395 corrode_weapon(); 396 } 397 } 398 if (malive && mdat->mlet == 'E' && canseemon(mtmp) 399 && !mtmp->mcan && rn2(3)) { 400 if (mtmp->mcansee) { 401 pline("You are frozen by the floating eye's gaze!"); 402 nomul((u.ulevel > 6 || rn2(4)) ? rn1(20, -21) : -200); 403 } else { 404 pline("The blinded floating eye cannot defend itself."); 405 if (!rn2(500)) 406 if ((int) u.uluck > LUCKMIN) 407 u.uluck--; 408 } 409 } 410 return (TRUE); 411 } 412