Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ |
2 | /* hack.mon.c - version 1.0.3 */ | |
3 | ||
4 | #include "hack.h" | |
5 | #include "hack.mfndpos.h" | |
6 | ||
7 | #ifndef NULL | |
8 | #define NULL (char *) 0 | |
9 | #endif | |
10 | ||
11 | extern struct monst *makemon(); | |
12 | extern struct obj *mkobj_at(); | |
13 | ||
14 | int warnlevel; /* used by movemon and dochugw */ | |
15 | long lastwarntime; | |
16 | int lastwarnlev; | |
17 | char *warnings[] = { | |
18 | "white", "pink", "red", "ruby", "purple", "black" | |
19 | }; | |
20 | ||
21 | movemon() | |
22 | { | |
23 | register struct monst *mtmp; | |
24 | register int fr; | |
25 | ||
26 | warnlevel = 0; | |
27 | ||
28 | while(1) { | |
29 | /* find a monster that we haven't treated yet */ | |
30 | /* note that mtmp or mtmp->nmon might get killed | |
31 | while mtmp moves, so we cannot just walk down the | |
32 | chain (even new monsters might get created!) */ | |
33 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) | |
34 | if(mtmp->mlstmv < moves) goto next_mon; | |
35 | /* treated all monsters */ | |
36 | break; | |
37 | ||
38 | next_mon: | |
39 | mtmp->mlstmv = moves; | |
40 | ||
41 | /* most monsters drown in pools */ | |
42 | { boolean inpool, iseel; | |
43 | ||
44 | inpool = (levl[mtmp->mx][mtmp->my].typ == POOL); | |
45 | iseel = (mtmp->data->mlet == ';'); | |
46 | if(inpool && !iseel) { | |
47 | if(cansee(mtmp->mx,mtmp->my)) | |
48 | pline("%s drowns.", Monnam(mtmp)); | |
49 | mondead(mtmp); | |
50 | continue; | |
51 | } | |
52 | /* but eels have a difficult time outside */ | |
53 | if(iseel && !inpool) { | |
54 | if(mtmp->mhp > 1) mtmp->mhp--; | |
55 | mtmp->mflee = 1; | |
56 | mtmp->mfleetim += 2; | |
57 | } | |
58 | } | |
59 | if(mtmp->mblinded && !--mtmp->mblinded) | |
60 | mtmp->mcansee = 1; | |
61 | if(mtmp->mfleetim && !--mtmp->mfleetim) | |
62 | mtmp->mflee = 0; | |
63 | if(mtmp->mimic) continue; | |
64 | if(mtmp->mspeed != MSLOW || !(moves%2)){ | |
65 | /* continue if the monster died fighting */ | |
66 | fr = -1; | |
67 | if(Conflict && cansee(mtmp->mx,mtmp->my) | |
68 | && (fr = fightm(mtmp)) == 2) | |
69 | continue; | |
70 | if(fr<0 && dochugw(mtmp)) | |
71 | continue; | |
72 | } | |
73 | if(mtmp->mspeed == MFAST && dochugw(mtmp)) | |
74 | continue; | |
75 | } | |
76 | ||
77 | warnlevel -= u.ulevel; | |
78 | if(warnlevel >= SIZE(warnings)) | |
79 | warnlevel = SIZE(warnings)-1; | |
80 | if(warnlevel >= 0) | |
81 | if(warnlevel > lastwarnlev || moves > lastwarntime + 5){ | |
82 | register char *rr; | |
83 | switch(Warning & (LEFT_RING | RIGHT_RING)){ | |
84 | case LEFT_RING: | |
85 | rr = "Your left ring glows"; | |
86 | break; | |
87 | case RIGHT_RING: | |
88 | rr = "Your right ring glows"; | |
89 | break; | |
90 | case LEFT_RING | RIGHT_RING: | |
91 | rr = "Both your rings glow"; | |
92 | break; | |
93 | default: | |
94 | rr = "Your fingertips glow"; | |
95 | break; | |
96 | } | |
97 | pline("%s %s!", rr, warnings[warnlevel]); | |
98 | lastwarntime = moves; | |
99 | lastwarnlev = warnlevel; | |
100 | } | |
101 | ||
102 | dmonsfree(); /* remove all dead monsters */ | |
103 | } | |
104 | ||
105 | justswld(mtmp,name) | |
106 | register struct monst *mtmp; | |
107 | char *name; | |
108 | { | |
109 | ||
110 | mtmp->mx = u.ux; | |
111 | mtmp->my = u.uy; | |
112 | u.ustuck = mtmp; | |
113 | pmon(mtmp); | |
114 | kludge("%s swallows you!",name); | |
115 | more(); | |
116 | seeoff(1); | |
117 | u.uswallow = 1; | |
118 | u.uswldtim = 0; | |
119 | swallowed(); | |
120 | } | |
121 | ||
122 | youswld(mtmp,dam,die,name) | |
123 | register struct monst *mtmp; | |
124 | register dam,die; | |
125 | char *name; | |
126 | { | |
127 | if(mtmp != u.ustuck) return; | |
128 | kludge("%s digests you!",name); | |
129 | u.uhp -= dam; | |
130 | if(u.uswldtim++ >= die){ /* a3 */ | |
131 | pline("It totally digests you!"); | |
132 | u.uhp = -1; | |
133 | } | |
134 | if(u.uhp < 1) done_in_by(mtmp); | |
135 | /* flags.botlx = 1; /* should we show status line ? */ | |
136 | } | |
137 | ||
138 | dochugw(mtmp) register struct monst *mtmp; { | |
139 | register x = mtmp->mx; | |
140 | register y = mtmp->my; | |
141 | register d = dochug(mtmp); | |
142 | register dd; | |
143 | if(!d) /* monster still alive */ | |
144 | if(Warning) | |
145 | if(!mtmp->mpeaceful) | |
146 | if(mtmp->data->mlevel > warnlevel) | |
147 | if((dd = dist(mtmp->mx,mtmp->my)) < dist(x,y)) | |
148 | if(dd < 100) | |
149 | if(!canseemon(mtmp)) | |
150 | warnlevel = mtmp->data->mlevel; | |
151 | return(d); | |
152 | } | |
153 | ||
154 | /* returns 1 if monster died moving, 0 otherwise */ | |
155 | dochug(mtmp) | |
156 | register struct monst *mtmp; | |
157 | { | |
158 | register struct permonst *mdat; | |
159 | register tmp, nearby, scared; | |
160 | ||
161 | if(mtmp->cham && !rn2(6)) | |
162 | (void) newcham(mtmp, &mons[dlevel+14+rn2(CMNUM-14-dlevel)]); | |
163 | mdat = mtmp->data; | |
164 | if(mdat->mlevel < 0) | |
165 | panic("bad monster %c (%d)",mdat->mlet,mdat->mlevel); | |
166 | ||
167 | /* regenerate monsters */ | |
168 | if((!(moves%20) || index(MREGEN, mdat->mlet)) && | |
169 | mtmp->mhp < mtmp->mhpmax) | |
170 | mtmp->mhp++; | |
171 | ||
172 | if(mtmp->mfroz) return(0); /* frozen monsters don't do anything */ | |
173 | ||
174 | if(mtmp->msleep) { | |
175 | /* wake up, or get out of here. */ | |
176 | /* ettins are hard to surprise */ | |
177 | /* Nymphs and Leprechauns do not easily wake up */ | |
178 | if(cansee(mtmp->mx,mtmp->my) && | |
179 | (!Stealth || (mdat->mlet == 'e' && rn2(10))) && | |
180 | (!index("NL",mdat->mlet) || !rn2(50)) && | |
181 | (Aggravate_monster || index("d1", mdat->mlet) | |
182 | || (!rn2(7) && !mtmp->mimic))) | |
183 | mtmp->msleep = 0; | |
184 | else return(0); | |
185 | } | |
186 | ||
187 | /* not frozen or sleeping: wipe out texts written in the dust */ | |
188 | wipe_engr_at(mtmp->mx, mtmp->my, 1); | |
189 | ||
190 | /* confused monsters get unconfused with small probability */ | |
191 | if(mtmp->mconf && !rn2(50)) mtmp->mconf = 0; | |
192 | ||
193 | /* some monsters teleport */ | |
194 | if(mtmp->mflee && index("tNL", mdat->mlet) && !rn2(40)){ | |
195 | rloc(mtmp); | |
196 | return(0); | |
197 | } | |
198 | if(mdat->mmove < rnd(6)) return(0); | |
199 | ||
200 | /* fleeing monsters might regain courage */ | |
201 | if(mtmp->mflee && !mtmp->mfleetim | |
202 | && mtmp->mhp == mtmp->mhpmax && !rn2(25)) | |
203 | mtmp->mflee = 0; | |
204 | ||
205 | nearby = (dist(mtmp->mx, mtmp->my) < 3); | |
206 | scared = (nearby && (sengr_at("Elbereth", u.ux, u.uy) || | |
207 | sobj_at(SCR_SCARE_MONSTER, u.ux, u.uy))); | |
208 | if(scared && !mtmp->mflee) { | |
209 | mtmp->mflee = 1; | |
210 | mtmp->mfleetim = (rn2(7) ? rnd(10) : rnd(100)); | |
211 | } | |
212 | ||
213 | if(!nearby || | |
214 | mtmp->mflee || | |
215 | mtmp->mconf || | |
216 | (mtmp->minvis && !rn2(3)) || | |
217 | (index("BIuy", mdat->mlet) && !rn2(4)) || | |
218 | (mdat->mlet == 'L' && !u.ugold && (mtmp->mgold || rn2(2))) || | |
219 | (!mtmp->mcansee && !rn2(4)) || | |
220 | mtmp->mpeaceful | |
221 | ) { | |
222 | tmp = m_move(mtmp,0); /* 2: monster died moving */ | |
223 | if(tmp == 2 || (tmp && mdat->mmove <= 12)) | |
224 | return(tmp == 2); | |
225 | } | |
226 | ||
227 | if(!index("Ea", mdat->mlet) && nearby && | |
228 | !mtmp->mpeaceful && u.uhp > 0 && !scared) { | |
229 | if(mhitu(mtmp)) | |
230 | return(1); /* monster died (e.g. 'y' or 'F') */ | |
231 | } | |
232 | /* extra movement for fast monsters */ | |
233 | if(mdat->mmove-12 > rnd(12)) tmp = m_move(mtmp,1); | |
234 | return(tmp == 2); | |
235 | } | |
236 | ||
237 | m_move(mtmp,after) | |
238 | register struct monst *mtmp; | |
239 | { | |
240 | register struct monst *mtmp2; | |
241 | register nx,ny,omx,omy,appr,nearer,cnt,i,j; | |
242 | xchar gx,gy,nix,niy,chcnt; | |
243 | schar chi; | |
244 | boolean likegold, likegems, likeobjs; | |
245 | char msym = mtmp->data->mlet; | |
246 | schar mmoved = 0; /* not strictly nec.: chi >= 0 will do */ | |
247 | coord poss[9]; | |
248 | int info[9]; | |
249 | ||
250 | if(mtmp->mfroz || mtmp->msleep) | |
251 | return(0); | |
252 | if(mtmp->mtrapped) { | |
253 | i = mintrap(mtmp); | |
254 | if(i == 2) return(2); /* he died */ | |
255 | if(i == 1) return(0); /* still in trap, so didnt move */ | |
256 | } | |
257 | if(mtmp->mhide && o_at(mtmp->mx,mtmp->my) && rn2(10)) | |
258 | return(0); /* do not leave hiding place */ | |
259 | ||
260 | #ifndef NOWORM | |
261 | if(mtmp->wormno) | |
262 | goto not_special; | |
263 | #endif NOWORM | |
264 | ||
265 | /* my dog gets a special treatment */ | |
266 | if(mtmp->mtame) { | |
267 | return( dog_move(mtmp, after) ); | |
268 | } | |
269 | ||
270 | /* likewise for shopkeeper */ | |
271 | if(mtmp->isshk) { | |
272 | mmoved = shk_move(mtmp); | |
273 | if(mmoved >= 0) | |
274 | goto postmov; | |
275 | mmoved = 0; /* follow player outside shop */ | |
276 | } | |
277 | ||
278 | /* and for the guard */ | |
279 | if(mtmp->isgd) { | |
280 | mmoved = gd_move(); | |
281 | goto postmov; | |
282 | } | |
283 | ||
284 | /* teleport if that lies in our nature ('t') or when badly wounded ('1') */ | |
285 | if((msym == 't' && !rn2(5)) | |
286 | || (msym == '1' && (mtmp->mhp < 7 || (!xdnstair && !rn2(5)) | |
287 | || levl[u.ux][u.uy].typ == STAIRS))) { | |
288 | if(mtmp->mhp < 7 || (msym == 't' && rn2(2))) | |
289 | rloc(mtmp); | |
290 | else | |
291 | mnexto(mtmp); | |
292 | mmoved = 1; | |
293 | goto postmov; | |
294 | } | |
295 | ||
296 | /* spit fire ('D') or use a wand ('1') when appropriate */ | |
297 | if(index("D1", msym)) | |
298 | inrange(mtmp); | |
299 | ||
300 | if(msym == 'U' && !mtmp->mcan && canseemon(mtmp) && | |
301 | mtmp->mcansee && rn2(5)) { | |
302 | if(!Confusion) | |
303 | pline("%s's gaze has confused you!", Monnam(mtmp)); | |
304 | else | |
305 | pline("You are getting more and more confused."); | |
306 | if(rn2(3)) mtmp->mcan = 1; | |
307 | Confusion += d(3,4); /* timeout */ | |
308 | } | |
309 | not_special: | |
310 | if(!mtmp->mflee && u.uswallow && u.ustuck != mtmp) return(1); | |
311 | appr = 1; | |
312 | if(mtmp->mflee) appr = -1; | |
313 | if(mtmp->mconf || Invis || !mtmp->mcansee || | |
314 | (index("BIy", msym) && !rn2(3))) | |
315 | appr = 0; | |
316 | omx = mtmp->mx; | |
317 | omy = mtmp->my; | |
318 | gx = u.ux; | |
319 | gy = u.uy; | |
320 | if(msym == 'L' && appr == 1 && mtmp->mgold > u.ugold) | |
321 | appr = -1; | |
322 | ||
323 | /* random criterion for 'smell' or track finding ability | |
324 | should use mtmp->msmell or sth | |
325 | */ | |
326 | if(msym == '@' || | |
327 | ('a' <= msym && msym <= 'z')) { | |
328 | extern coord *gettrack(); | |
329 | register coord *cp; | |
330 | schar mroom; | |
331 | mroom = inroom(omx,omy); | |
332 | if(mroom < 0 || mroom != inroom(u.ux,u.uy)){ | |
333 | cp = gettrack(omx,omy); | |
334 | if(cp){ | |
335 | gx = cp->x; | |
336 | gy = cp->y; | |
337 | } | |
338 | } | |
339 | } | |
340 | ||
341 | /* look for gold or jewels nearby */ | |
342 | likegold = (index("LOD", msym) != NULL); | |
343 | likegems = (index("ODu", msym) != NULL); | |
344 | likeobjs = mtmp->mhide; | |
345 | #define SRCHRADIUS 25 | |
346 | { xchar mind = SRCHRADIUS; /* not too far away */ | |
347 | register int dd; | |
348 | if(likegold){ | |
349 | register struct gold *gold; | |
350 | for(gold = fgold; gold; gold = gold->ngold) | |
351 | if((dd = DIST(omx,omy,gold->gx,gold->gy)) < mind){ | |
352 | mind = dd; | |
353 | gx = gold->gx; | |
354 | gy = gold->gy; | |
355 | } | |
356 | } | |
357 | if(likegems || likeobjs){ | |
358 | register struct obj *otmp; | |
359 | for(otmp = fobj; otmp; otmp = otmp->nobj) | |
360 | if(likeobjs || otmp->olet == GEM_SYM) | |
361 | if(msym != 'u' || | |
362 | objects[otmp->otyp].g_val != 0) | |
363 | if((dd = DIST(omx,omy,otmp->ox,otmp->oy)) < mind){ | |
364 | mind = dd; | |
365 | gx = otmp->ox; | |
366 | gy = otmp->oy; | |
367 | } | |
368 | } | |
369 | if(mind < SRCHRADIUS && appr == -1) { | |
370 | if(dist(omx,omy) < 10) { | |
371 | gx = u.ux; | |
372 | gy = u.uy; | |
373 | } else | |
374 | appr = 1; | |
375 | } | |
376 | } | |
377 | nix = omx; | |
378 | niy = omy; | |
379 | cnt = mfndpos(mtmp,poss,info, | |
380 | msym == 'u' ? NOTONL : | |
381 | (msym == '@' || msym == '1') ? (ALLOW_SSM | ALLOW_TRAPS) : | |
382 | index(UNDEAD, msym) ? NOGARLIC : ALLOW_TRAPS); | |
383 | /* ALLOW_ROCK for some monsters ? */ | |
384 | chcnt = 0; | |
385 | chi = -1; | |
386 | for(i=0; i<cnt; i++) { | |
387 | nx = poss[i].x; | |
388 | ny = poss[i].y; | |
389 | for(j=0; j<MTSZ && j<cnt-1; j++) | |
390 | if(nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) | |
391 | if(rn2(4*(cnt-j))) goto nxti; | |
392 | #ifdef STUPID | |
393 | /* some stupid compilers think that this is too complicated */ | |
394 | { int d1 = DIST(nx,ny,gx,gy); | |
395 | int d2 = DIST(nix,niy,gx,gy); | |
396 | nearer = (d1 < d2); | |
397 | } | |
398 | #else | |
399 | nearer = (DIST(nx,ny,gx,gy) < DIST(nix,niy,gx,gy)); | |
400 | #endif STUPID | |
401 | if((appr == 1 && nearer) || (appr == -1 && !nearer) || | |
402 | !mmoved || | |
403 | (!appr && !rn2(++chcnt))){ | |
404 | nix = nx; | |
405 | niy = ny; | |
406 | chi = i; | |
407 | mmoved = 1; | |
408 | } | |
409 | nxti: ; | |
410 | } | |
411 | if(mmoved){ | |
412 | if(info[chi] & ALLOW_M){ | |
413 | mtmp2 = m_at(nix,niy); | |
414 | if(hitmm(mtmp,mtmp2) == 1 && rn2(4) && | |
415 | hitmm(mtmp2,mtmp) == 2) return(2); | |
416 | return(0); | |
417 | } | |
418 | if(info[chi] & ALLOW_U){ | |
419 | (void) hitu(mtmp, d(mtmp->data->damn, mtmp->data->damd)+1); | |
420 | return(0); | |
421 | } | |
422 | mtmp->mx = nix; | |
423 | mtmp->my = niy; | |
424 | for(j=MTSZ-1; j>0; j--) mtmp->mtrack[j] = mtmp->mtrack[j-1]; | |
425 | mtmp->mtrack[0].x = omx; | |
426 | mtmp->mtrack[0].y = omy; | |
427 | #ifndef NOWORM | |
428 | if(mtmp->wormno) worm_move(mtmp); | |
429 | #endif NOWORM | |
430 | } else { | |
431 | if(msym == 'u' && rn2(2)){ | |
432 | rloc(mtmp); | |
433 | return(0); | |
434 | } | |
435 | #ifndef NOWORM | |
436 | if(mtmp->wormno) worm_nomove(mtmp); | |
437 | #endif NOWORM | |
438 | } | |
439 | postmov: | |
440 | if(mmoved == 1) { | |
441 | if(mintrap(mtmp) == 2) /* he died */ | |
442 | return(2); | |
443 | if(likegold) mpickgold(mtmp); | |
444 | if(likegems) mpickgems(mtmp); | |
445 | if(mtmp->mhide) mtmp->mundetected = 1; | |
446 | } | |
447 | pmon(mtmp); | |
448 | return(mmoved); | |
449 | } | |
450 | ||
451 | mpickgold(mtmp) register struct monst *mtmp; { | |
452 | register struct gold *gold; | |
453 | while(gold = g_at(mtmp->mx, mtmp->my)){ | |
454 | mtmp->mgold += gold->amount; | |
455 | freegold(gold); | |
456 | if(levl[mtmp->mx][mtmp->my].scrsym == '$') | |
457 | newsym(mtmp->mx, mtmp->my); | |
458 | } | |
459 | } | |
460 | ||
461 | mpickgems(mtmp) register struct monst *mtmp; { | |
462 | register struct obj *otmp; | |
463 | for(otmp = fobj; otmp; otmp = otmp->nobj) | |
464 | if(otmp->olet == GEM_SYM) | |
465 | if(otmp->ox == mtmp->mx && otmp->oy == mtmp->my) | |
466 | if(mtmp->data->mlet != 'u' || objects[otmp->otyp].g_val != 0){ | |
467 | freeobj(otmp); | |
468 | mpickobj(mtmp, otmp); | |
469 | if(levl[mtmp->mx][mtmp->my].scrsym == GEM_SYM) | |
470 | newsym(mtmp->mx, mtmp->my); /* %% */ | |
471 | return; /* pick only one object */ | |
472 | } | |
473 | } | |
474 | ||
475 | /* return number of acceptable neighbour positions */ | |
476 | mfndpos(mon,poss,info,flag) | |
477 | register struct monst *mon; | |
478 | coord poss[9]; | |
479 | int info[9], flag; | |
480 | { | |
481 | register int x,y,nx,ny,cnt = 0,ntyp; | |
482 | register struct monst *mtmp; | |
483 | int nowtyp; | |
484 | boolean pool; | |
485 | ||
486 | x = mon->mx; | |
487 | y = mon->my; | |
488 | nowtyp = levl[x][y].typ; | |
489 | ||
490 | pool = (mon->data->mlet == ';'); | |
491 | nexttry: /* eels prefer the water, but if there is no water nearby, | |
492 | they will crawl over land */ | |
493 | if(mon->mconf) { | |
494 | flag |= ALLOW_ALL; | |
495 | flag &= ~NOTONL; | |
496 | } | |
497 | for(nx = x-1; nx <= x+1; nx++) for(ny = y-1; ny <= y+1; ny++) | |
498 | if(nx != x || ny != y) if(isok(nx,ny)) | |
499 | if(!IS_ROCK(ntyp = levl[nx][ny].typ)) | |
500 | if(!(nx != x && ny != y && (nowtyp == DOOR || ntyp == DOOR))) | |
501 | if((ntyp == POOL) == pool) { | |
502 | info[cnt] = 0; | |
503 | if(nx == u.ux && ny == u.uy){ | |
504 | if(!(flag & ALLOW_U)) continue; | |
505 | info[cnt] = ALLOW_U; | |
506 | } else if(mtmp = m_at(nx,ny)){ | |
507 | if(!(flag & ALLOW_M)) continue; | |
508 | info[cnt] = ALLOW_M; | |
509 | if(mtmp->mtame){ | |
510 | if(!(flag & ALLOW_TM)) continue; | |
511 | info[cnt] |= ALLOW_TM; | |
512 | } | |
513 | } | |
514 | if(sobj_at(CLOVE_OF_GARLIC, nx, ny)) { | |
515 | if(flag & NOGARLIC) continue; | |
516 | info[cnt] |= NOGARLIC; | |
517 | } | |
518 | if(sobj_at(SCR_SCARE_MONSTER, nx, ny) || | |
519 | (!mon->mpeaceful && sengr_at("Elbereth", nx, ny))) { | |
520 | if(!(flag & ALLOW_SSM)) continue; | |
521 | info[cnt] |= ALLOW_SSM; | |
522 | } | |
523 | if(sobj_at(ENORMOUS_ROCK, nx, ny)) { | |
524 | if(!(flag & ALLOW_ROCK)) continue; | |
525 | info[cnt] |= ALLOW_ROCK; | |
526 | } | |
527 | if(!Invis && online(nx,ny)){ | |
528 | if(flag & NOTONL) continue; | |
529 | info[cnt] |= NOTONL; | |
530 | } | |
531 | /* we cannot avoid traps of an unknown kind */ | |
532 | { register struct trap *ttmp = t_at(nx, ny); | |
533 | register int tt; | |
534 | if(ttmp) { | |
535 | tt = 1 << ttmp->ttyp; | |
536 | if(mon->mtrapseen & tt){ | |
537 | if(!(flag & tt)) continue; | |
538 | info[cnt] |= tt; | |
539 | } | |
540 | } | |
541 | } | |
542 | poss[cnt].x = nx; | |
543 | poss[cnt].y = ny; | |
544 | cnt++; | |
545 | } | |
546 | if(!cnt && pool && nowtyp != POOL) { | |
547 | pool = FALSE; | |
548 | goto nexttry; | |
549 | } | |
550 | return(cnt); | |
551 | } | |
552 | ||
553 | dist(x,y) int x,y; { | |
554 | return((x-u.ux)*(x-u.ux) + (y-u.uy)*(y-u.uy)); | |
555 | } | |
556 | ||
557 | poisoned(string, pname) | |
558 | register char *string, *pname; | |
559 | { | |
560 | register int i; | |
561 | ||
562 | if(Blind) pline("It was poisoned."); | |
563 | else pline("The %s was poisoned!",string); | |
564 | if(Poison_resistance) { | |
565 | pline("The poison doesn't seem to affect you."); | |
566 | return; | |
567 | } | |
568 | i = rn2(10); | |
569 | if(i == 0) { | |
570 | u.uhp = -1; | |
571 | pline("I am afraid the poison was deadly ..."); | |
572 | } else if(i <= 5) { | |
573 | losestr(rn1(3,3)); | |
574 | } else { | |
575 | losehp(rn1(10,6), pname); | |
576 | } | |
577 | if(u.uhp < 1) { | |
578 | killer = pname; | |
579 | done("died"); | |
580 | } | |
581 | } | |
582 | ||
583 | mondead(mtmp) | |
584 | register struct monst *mtmp; | |
585 | { | |
586 | relobj(mtmp,1); | |
587 | unpmon(mtmp); | |
588 | relmon(mtmp); | |
589 | unstuck(mtmp); | |
590 | if(mtmp->isshk) shkdead(mtmp); | |
591 | if(mtmp->isgd) gddead(); | |
592 | #ifndef NOWORM | |
593 | if(mtmp->wormno) wormdead(mtmp); | |
594 | #endif NOWORM | |
595 | monfree(mtmp); | |
596 | } | |
597 | ||
598 | /* called when monster is moved to larger structure */ | |
599 | replmon(mtmp,mtmp2) | |
600 | register struct monst *mtmp, *mtmp2; | |
601 | { | |
602 | relmon(mtmp); | |
603 | monfree(mtmp); | |
604 | mtmp2->nmon = fmon; | |
605 | fmon = mtmp2; | |
606 | if(u.ustuck == mtmp) u.ustuck = mtmp2; | |
607 | if(mtmp2->isshk) replshk(mtmp,mtmp2); | |
608 | if(mtmp2->isgd) replgd(mtmp,mtmp2); | |
609 | } | |
610 | ||
611 | relmon(mon) | |
612 | register struct monst *mon; | |
613 | { | |
614 | register struct monst *mtmp; | |
615 | ||
616 | if(mon == fmon) fmon = fmon->nmon; | |
617 | else { | |
618 | for(mtmp = fmon; mtmp->nmon != mon; mtmp = mtmp->nmon) ; | |
619 | mtmp->nmon = mon->nmon; | |
620 | } | |
621 | } | |
622 | ||
623 | /* we do not free monsters immediately, in order to have their name | |
624 | available shortly after their demise */ | |
625 | struct monst *fdmon; /* chain of dead monsters, need not to be saved */ | |
626 | ||
627 | monfree(mtmp) register struct monst *mtmp; { | |
628 | mtmp->nmon = fdmon; | |
629 | fdmon = mtmp; | |
630 | } | |
631 | ||
632 | dmonsfree(){ | |
633 | register struct monst *mtmp; | |
634 | while(mtmp = fdmon){ | |
635 | fdmon = mtmp->nmon; | |
636 | free((char *) mtmp); | |
637 | } | |
638 | } | |
639 | ||
640 | unstuck(mtmp) | |
641 | register struct monst *mtmp; | |
642 | { | |
643 | if(u.ustuck == mtmp) { | |
644 | if(u.uswallow){ | |
645 | u.ux = mtmp->mx; | |
646 | u.uy = mtmp->my; | |
647 | u.uswallow = 0; | |
648 | setsee(); | |
649 | docrt(); | |
650 | } | |
651 | u.ustuck = 0; | |
652 | } | |
653 | } | |
654 | ||
655 | killed(mtmp) | |
656 | register struct monst *mtmp; | |
657 | { | |
658 | #ifdef lint | |
659 | #define NEW_SCORING | |
660 | #endif lint | |
661 | register int tmp,tmp2,nk,x,y; | |
662 | register struct permonst *mdat; | |
663 | extern long newuexp(); | |
664 | ||
665 | if(mtmp->cham) mtmp->data = PM_CHAMELEON; | |
666 | mdat = mtmp->data; | |
667 | if(Blind) pline("You destroy it!"); | |
668 | else { | |
669 | pline("You destroy %s!", | |
670 | mtmp->mtame ? amonnam(mtmp, "poor") : monnam(mtmp)); | |
671 | } | |
672 | if(u.umconf) { | |
673 | if(!Blind) pline("Your hands stop glowing blue."); | |
674 | u.umconf = 0; | |
675 | } | |
676 | ||
677 | /* count killed monsters */ | |
678 | #define MAXMONNO 100 | |
679 | nk = 1; /* in case we cannot find it in mons */ | |
680 | tmp = mdat - mons; /* index in mons array (if not 'd', '@', ...) */ | |
681 | if(tmp >= 0 && tmp < CMNUM+2) { | |
682 | extern char fut_geno[]; | |
683 | u.nr_killed[tmp]++; | |
684 | if((nk = u.nr_killed[tmp]) > MAXMONNO && | |
685 | !index(fut_geno, mdat->mlet)) | |
686 | charcat(fut_geno, mdat->mlet); | |
687 | } | |
688 | ||
689 | /* punish bad behaviour */ | |
690 | if(mdat->mlet == '@') Telepat = 0, u.uluck -= 2; | |
691 | if(mtmp->mpeaceful || mtmp->mtame) u.uluck--; | |
692 | if(mdat->mlet == 'u') u.uluck -= 5; | |
693 | if((int)u.uluck < LUCKMIN) u.uluck = LUCKMIN; | |
694 | ||
695 | /* give experience points */ | |
696 | tmp = 1 + mdat->mlevel * mdat->mlevel; | |
697 | if(mdat->ac < 3) tmp += 2*(7 - mdat->ac); | |
698 | if(index("AcsSDXaeRTVWU&In:P", mdat->mlet)) | |
699 | tmp += 2*mdat->mlevel; | |
700 | if(index("DeV&P",mdat->mlet)) tmp += (7*mdat->mlevel); | |
701 | if(mdat->mlevel > 6) tmp += 50; | |
702 | if(mdat->mlet == ';') tmp += 1000; | |
703 | ||
704 | #ifdef NEW_SCORING | |
705 | /* ------- recent addition: make nr of points decrease | |
706 | when this is not the first of this kind */ | |
707 | { int ul = u.ulevel; | |
708 | int ml = mdat->mlevel; | |
709 | ||
710 | if(ul < 14) /* points are given based on present and future level */ | |
711 | for(tmp2 = 0; !tmp2 || ul + tmp2 <= ml; tmp2++) | |
712 | if(u.uexp + 1 + (tmp + ((tmp2 <= 0) ? 0 : 4<<(tmp2-1)))/nk | |
713 | >= 10*pow((unsigned)(ul-1))) | |
714 | if(++ul == 14) break; | |
715 | ||
716 | tmp2 = ml - ul -1; | |
717 | tmp = (tmp + ((tmp2 < 0) ? 0 : 4<<tmp2))/nk; | |
718 | if(!tmp) tmp = 1; | |
719 | } | |
720 | /* note: ul is not necessarily the future value of u.ulevel */ | |
721 | /* ------- end of recent valuation change ------- */ | |
722 | #endif NEW_SCORING | |
723 | ||
724 | more_experienced(tmp,0); | |
725 | flags.botl = 1; | |
726 | while(u.ulevel < 14 && u.uexp >= newuexp()){ | |
727 | pline("Welcome to experience level %u.", ++u.ulevel); | |
728 | tmp = rnd(10); | |
729 | if(tmp < 3) tmp = rnd(10); | |
730 | u.uhpmax += tmp; | |
731 | u.uhp += tmp; | |
732 | flags.botl = 1; | |
733 | } | |
734 | ||
735 | /* dispose of monster and make cadaver */ | |
736 | x = mtmp->mx; y = mtmp->my; | |
737 | mondead(mtmp); | |
738 | tmp = mdat->mlet; | |
739 | if(tmp == 'm') { /* he killed a minotaur, give him a wand of digging */ | |
740 | /* note: the dead minotaur will be on top of it! */ | |
741 | mksobj_at(WAN_DIGGING, x, y); | |
742 | /* if(cansee(x,y)) atl(x,y,fobj->olet); */ | |
743 | stackobj(fobj); | |
744 | } else | |
745 | #ifndef NOWORM | |
746 | if(tmp == 'w') { | |
747 | mksobj_at(WORM_TOOTH, x, y); | |
748 | stackobj(fobj); | |
749 | } else | |
750 | #endif NOWORM | |
751 | if(!letter(tmp) || (!index("mw", tmp) && !rn2(3))) tmp = 0; | |
752 | ||
753 | if(ACCESSIBLE(levl[x][y].typ)) /* might be mimic in wall or dead eel*/ | |
754 | if(x != u.ux || y != u.uy) /* might be here after swallowed */ | |
755 | if(index("NTVm&",mdat->mlet) || rn2(5)) { | |
756 | register struct obj *obj2 = mkobj_at(tmp,x,y); | |
757 | if(cansee(x,y)) | |
758 | atl(x,y,obj2->olet); | |
759 | stackobj(obj2); | |
760 | } | |
761 | } | |
762 | ||
763 | kludge(str,arg) | |
764 | register char *str,*arg; | |
765 | { | |
766 | if(Blind) { | |
767 | if(*str == '%') pline(str,"It"); | |
768 | else pline(str,"it"); | |
769 | } else pline(str,arg); | |
770 | } | |
771 | ||
772 | rescham() /* force all chameleons to become normal */ | |
773 | { | |
774 | register struct monst *mtmp; | |
775 | ||
776 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) | |
777 | if(mtmp->cham) { | |
778 | mtmp->cham = 0; | |
779 | (void) newcham(mtmp, PM_CHAMELEON); | |
780 | } | |
781 | } | |
782 | ||
783 | newcham(mtmp,mdat) /* make a chameleon look like a new monster */ | |
784 | /* returns 1 if the monster actually changed */ | |
785 | register struct monst *mtmp; | |
786 | register struct permonst *mdat; | |
787 | { | |
788 | register mhp, hpn, hpd; | |
789 | ||
790 | if(mdat == mtmp->data) return(0); /* still the same monster */ | |
791 | #ifndef NOWORM | |
792 | if(mtmp->wormno) wormdead(mtmp); /* throw tail away */ | |
793 | #endif NOWORM | |
794 | if (u.ustuck == mtmp) { | |
795 | if (u.uswallow) { | |
796 | u.uswallow = 0; | |
797 | u.uswldtim = 0; | |
798 | mnexto (mtmp); | |
799 | docrt (); | |
800 | prme (); | |
801 | } | |
802 | u.ustuck = 0; | |
803 | } | |
804 | hpn = mtmp->mhp; | |
805 | hpd = (mtmp->data->mlevel)*8; | |
806 | if(!hpd) hpd = 4; | |
807 | mtmp->data = mdat; | |
808 | mhp = (mdat->mlevel)*8; | |
809 | /* new hp: same fraction of max as before */ | |
810 | mtmp->mhp = 2 + (hpn*mhp)/hpd; | |
811 | hpn = mtmp->mhpmax; | |
812 | mtmp->mhpmax = 2 + (hpn*mhp)/hpd; | |
813 | mtmp->minvis = (mdat->mlet == 'I') ? 1 : 0; | |
814 | #ifndef NOWORM | |
815 | if(mdat->mlet == 'w' && getwn(mtmp)) initworm(mtmp); | |
816 | /* perhaps we should clear mtmp->mtame here? */ | |
817 | #endif NOWORM | |
818 | unpmon(mtmp); /* necessary for 'I' and to force pmon */ | |
819 | pmon(mtmp); | |
820 | return(1); | |
821 | } | |
822 | ||
823 | mnexto(mtmp) /* Make monster mtmp next to you (if possible) */ | |
824 | struct monst *mtmp; | |
825 | { | |
826 | extern coord enexto(); | |
827 | coord mm; | |
828 | mm = enexto(u.ux, u.uy); | |
829 | mtmp->mx = mm.x; | |
830 | mtmp->my = mm.y; | |
831 | pmon(mtmp); | |
832 | } | |
833 | ||
834 | ishuman(mtmp) register struct monst *mtmp; { | |
835 | return(mtmp->data->mlet == '@'); | |
836 | } | |
837 | ||
838 | setmangry(mtmp) register struct monst *mtmp; { | |
839 | if(!mtmp->mpeaceful) return; | |
840 | if(mtmp->mtame) return; | |
841 | mtmp->mpeaceful = 0; | |
842 | if(ishuman(mtmp)) pline("%s gets angry!", Monnam(mtmp)); | |
843 | } | |
844 | ||
845 | /* not one hundred procent correct: now a snake may hide under an | |
846 | invisible object */ | |
847 | canseemon(mtmp) | |
848 | register struct monst *mtmp; | |
849 | { | |
850 | return((!mtmp->minvis || See_invisible) | |
851 | && (!mtmp->mhide || !o_at(mtmp->mx,mtmp->my)) | |
852 | && cansee(mtmp->mx, mtmp->my)); | |
853 | } |