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