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