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