Commit | Line | Data |
---|---|---|
12458000 WJ |
1 | /* |
2 | * movem.c (move monster) Larn is copyrighted 1986 by Noah Morgan. | |
3 | * | |
4 | * Here are the functions in this file: | |
5 | * | |
6 | * movemonst() Routine to move the monsters toward the player | |
7 | * movemt(x,y) Function to move a monster at (x,y) -- must determine where | |
8 | * mmove(x,y,xd,yd) Function to actually perform the monster movement | |
9 | * movsphere() Function to look for and move spheres of annihilation | |
10 | */ | |
11 | #include "header.h" | |
12 | ||
13 | /* | |
14 | * movemonst() Routine to move the monsters toward the player | |
15 | * | |
16 | * This routine has the responsibility to determine which monsters are to | |
17 | * move, and call movemt() to do the move. | |
18 | * Returns no value. | |
19 | */ | |
20 | static short w1[9],w1x[9],w1y[9]; | |
21 | static int tmp1,tmp2,tmp3,tmp4,distance; | |
22 | movemonst() | |
23 | { | |
24 | register int i,j; | |
25 | if (c[TIMESTOP]) return; /* no action if time is stopped */ | |
26 | if (c[HASTESELF]) if ((c[HASTESELF]&1)==0) return; | |
27 | if (spheres) movsphere(); /* move the spheres of annihilation if any */ | |
28 | if (c[HOLDMONST]) return; /* no action if monsters are held */ | |
29 | ||
30 | if (c[AGGRAVATE]) /* determine window of monsters to move */ | |
31 | { | |
32 | tmp1=playery-5; tmp2=playery+6; tmp3=playerx-10; tmp4=playerx+11; | |
33 | distance=40; /* depth of intelligent monster movement */ | |
34 | } | |
35 | else | |
36 | { | |
37 | tmp1=playery-3; tmp2=playery+4; tmp3=playerx-5; tmp4=playerx+6; | |
38 | distance=17; /* depth of intelligent monster movement */ | |
39 | } | |
40 | ||
41 | if (level == 0) /* if on outside level monsters can move in perimeter */ | |
42 | { | |
43 | if (tmp1 < 0) tmp1=0; if (tmp2 > MAXY) tmp2=MAXY; | |
44 | if (tmp3 < 0) tmp3=0; if (tmp4 > MAXX) tmp4=MAXX; | |
45 | } | |
46 | else /* if in a dungeon monsters can't be on the perimeter (wall there) */ | |
47 | { | |
48 | if (tmp1 < 1) tmp1=1; if (tmp2 > MAXY-1) tmp2=MAXY-1; | |
49 | if (tmp3 < 1) tmp3=1; if (tmp4 > MAXX-1) tmp4=MAXX-1; | |
50 | } | |
51 | ||
52 | for (j=tmp1; j<tmp2; j++) /* now reset monster moved flags */ | |
53 | for (i=tmp3; i<tmp4; i++) | |
54 | moved[i][j] = 0; | |
55 | moved[lasthx][lasthy]=0; | |
56 | ||
57 | if (c[AGGRAVATE] || !c[STEALTH]) /* who gets moved? split for efficiency */ | |
58 | { | |
59 | for (j=tmp1; j<tmp2; j++) /* look thru all locations in window */ | |
60 | for (i=tmp3; i<tmp4; i++) | |
61 | if (mitem[i][j]) /* if there is a monster to move */ | |
62 | if (moved[i][j]==0) /* if it has not already been moved */ | |
63 | movemt(i,j); /* go and move the monster */ | |
64 | } | |
65 | else /* not aggravated and not stealth */ | |
66 | { | |
67 | for (j=tmp1; j<tmp2; j++) /* look thru all locations in window */ | |
68 | for (i=tmp3; i<tmp4; i++) | |
69 | if (mitem[i][j]) /* if there is a monster to move */ | |
70 | if (moved[i][j]==0) /* if it has not already been moved */ | |
71 | if (stealth[i][j]) /* if it is asleep due to stealth */ | |
72 | movemt(i,j); /* go and move the monster */ | |
73 | } | |
74 | ||
75 | if (mitem[lasthx][lasthy]) /* now move monster last hit by player if not already moved */ | |
76 | { | |
77 | if (moved[lasthx][lasthy]==0) /* if it has not already been moved */ | |
78 | { | |
79 | movemt(lasthx,lasthy); | |
80 | lasthx = w1x[0]; lasthy = w1y[0]; | |
81 | } | |
82 | } | |
83 | } | |
84 | ||
85 | /* | |
86 | * movemt(x,y) Function to move a monster at (x,y) -- must determine where | |
87 | * int x,y; | |
88 | * | |
89 | * This routine is responsible for determining where one monster at (x,y) will | |
90 | * move to. Enter with the monsters coordinates in (x,y). | |
91 | * Returns no value. | |
92 | */ | |
93 | static int tmpitem,xl,xh,yl,yh; | |
94 | movemt(i,j) | |
95 | int i,j; | |
96 | { | |
97 | register int k,m,z,tmp,xtmp,ytmp,monst; | |
98 | switch(monst=mitem[i][j]) /* for half speed monsters */ | |
99 | { | |
100 | case TROGLODYTE: case HOBGOBLIN: case METAMORPH: case XVART: | |
101 | case INVISIBLESTALKER: case ICELIZARD: if ((gtime & 1) == 1) return; | |
102 | }; | |
103 | ||
104 | if (c[SCAREMONST]) /* choose destination randomly if scared */ | |
105 | { | |
106 | if ((xl = i+rnd(3)-2) < 0) xl=0; if (xl >= MAXX) xl=MAXX-1; | |
107 | if ((yl = j+rnd(3)-2) < 0) yl=0; if (yl >= MAXY) yl=MAXY-1; | |
108 | if ((tmp=item[xl][yl]) != OWALL) | |
109 | if (mitem[xl][yl] == 0) | |
110 | if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR)) | |
111 | if (tmp != OCLOSEDDOOR) mmove(i,j,xl,yl); | |
112 | return; | |
113 | } | |
114 | ||
115 | if (monster[monst].intelligence > 10-c[HARDGAME]) /* if smart monster */ | |
116 | /* intelligent movement here -- first setup screen array */ | |
117 | { | |
118 | xl=tmp3-2; yl=tmp1-2; xh=tmp4+2; yh=tmp2+2; | |
119 | vxy(&xl,&yl); vxy(&xh,&yh); | |
120 | for (k=yl; k<yh; k++) | |
121 | for (m=xl; m<xh; m++) | |
122 | { | |
123 | switch(item[m][k]) | |
124 | { | |
125 | case OWALL: case OPIT: case OTRAPARROW: case ODARTRAP: | |
126 | case OCLOSEDDOOR: case OTRAPDOOR: case OTELEPORTER: | |
127 | smm: screen[m][k]=127; break; | |
128 | case OMIRROR: if (mitem[m][k]==VAMPIRE) goto smm; | |
129 | default: screen[m][k]= 0; break; | |
130 | }; | |
131 | } | |
132 | screen[playerx][playery]=1; | |
133 | ||
134 | /* now perform proximity ripple from playerx,playery to monster */ | |
135 | xl=tmp3-1; yl=tmp1-1; xh=tmp4+1; yh=tmp2+1; | |
136 | vxy(&xl,&yl); vxy(&xh,&yh); | |
137 | for (tmp=1; tmp<distance; tmp++) /* only up to 20 squares away */ | |
138 | for (k=yl; k<yh; k++) | |
139 | for (m=xl; m<xh; m++) | |
140 | if (screen[m][k]==tmp) /* if find proximity n advance it */ | |
141 | for (z=1; z<9; z++) /* go around in a circle */ | |
142 | { | |
143 | if (screen[xtmp=m+diroffx[z]][ytmp=k+diroffy[z]]==0) | |
144 | screen[xtmp][ytmp]=tmp+1; | |
145 | if (xtmp==i && ytmp==j) goto out; | |
146 | } | |
147 | ||
148 | out: if (tmp<distance) /* did find connectivity */ | |
149 | /* now select lowest value around playerx,playery */ | |
150 | for (z=1; z<9; z++) /* go around in a circle */ | |
151 | if (screen[xl=i+diroffx[z]][yl=j+diroffy[z]]==tmp) | |
152 | if (!mitem[xl][yl]) { mmove(i,j,w1x[0]=xl,w1y[0]=yl); return; } | |
153 | } | |
154 | ||
155 | /* dumb monsters move here */ | |
156 | xl=i-1; yl=j-1; xh=i+2; yh=j+2; | |
157 | if (i<playerx) xl++; else if (i>playerx) --xh; | |
158 | if (j<playery) yl++; else if (j>playery) --yh; | |
159 | for (k=0; k<9; k++) w1[k] = 10000; | |
160 | ||
161 | for (k=xl; k<xh; k++) | |
162 | for (m=yl; m<yh; m++) /* for each square compute distance to player */ | |
163 | { | |
164 | tmp = k-i+4+3*(m-j); | |
165 | tmpitem = item[k][m]; | |
166 | if (tmpitem!=OWALL || (k==playerx && m==playery)) | |
167 | if (mitem[k][m]==0) | |
168 | if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR)) | |
169 | if (tmpitem!=OCLOSEDDOOR) | |
170 | { | |
171 | w1[tmp] = (playerx-k)*(playerx-k)+(playery-m)*(playery-m); | |
172 | w1x[tmp] = k; w1y[tmp] = m; | |
173 | } | |
174 | } | |
175 | ||
176 | tmp = 0; | |
177 | for (k=1; k<9; k++) if (w1[tmp] > w1[k]) tmp=k; | |
178 | ||
179 | if (w1[tmp] < 10000) | |
180 | if ((i!=w1x[tmp]) || (j!=w1y[tmp])) | |
181 | mmove(i,j,w1x[tmp],w1y[tmp]); | |
182 | } | |
183 | ||
184 | /* | |
185 | * mmove(x,y,xd,yd) Function to actually perform the monster movement | |
186 | * int x,y,xd,yd; | |
187 | * | |
188 | * Enter with the from coordinates in (x,y) and the destination coordinates | |
189 | * in (xd,yd). | |
190 | */ | |
191 | mmove(aa,bb,cc,dd) | |
192 | int aa,bb,cc,dd; | |
193 | { | |
194 | register int tmp,i,flag; | |
195 | char *who,*p; | |
196 | flag=0; /* set to 1 if monster hit by arrow trap */ | |
197 | if ((cc==playerx) && (dd==playery)) | |
198 | { | |
199 | hitplayer(aa,bb); moved[aa][bb] = 1; return; | |
200 | } | |
201 | i=item[cc][dd]; | |
202 | if ((i==OPIT) || (i==OTRAPDOOR)) | |
203 | switch(mitem[aa][bb]) | |
204 | { | |
205 | case SPIRITNAGA: case PLATINUMDRAGON: case WRAITH: | |
206 | case VAMPIRE: case SILVERDRAGON: case POLTERGEIST: | |
207 | case DEMONLORD: case DEMONLORD+1: case DEMONLORD+2: | |
208 | case DEMONLORD+3: case DEMONLORD+4: case DEMONLORD+5: | |
209 | case DEMONLORD+6: case DEMONPRINCE: break; | |
210 | ||
211 | default: mitem[aa][bb]=0; /* fell in a pit or trapdoor */ | |
212 | }; | |
213 | tmp = mitem[cc][dd] = mitem[aa][bb]; | |
214 | if (i==OANNIHILATION) | |
215 | { | |
216 | if (tmp>=DEMONLORD+3) /* demons dispel spheres */ | |
217 | { | |
218 | cursors(); | |
219 | lprintf("\nThe %s dispels the sphere!",monster[tmp].name); | |
220 | rmsphere(cc,dd); /* delete the sphere */ | |
221 | } | |
222 | else i=tmp=mitem[cc][dd]=0; | |
223 | } | |
224 | stealth[cc][dd]=1; | |
225 | if ((hitp[cc][dd] = hitp[aa][bb]) < 0) hitp[cc][dd]=1; | |
226 | mitem[aa][bb] = 0; moved[cc][dd] = 1; | |
227 | if (tmp == LEPRECHAUN) | |
228 | switch(i) | |
229 | { | |
230 | case OGOLDPILE: case OMAXGOLD: case OKGOLD: case ODGOLD: | |
231 | case ODIAMOND: case ORUBY: case OEMERALD: case OSAPPHIRE: | |
232 | item[cc][dd] = 0; /* leprechaun takes gold */ | |
233 | }; | |
234 | ||
235 | if (tmp == TROLL) /* if a troll regenerate him */ | |
236 | if ((gtime & 1) == 0) | |
237 | if (monster[tmp].hitpoints > hitp[cc][dd]) hitp[cc][dd]++; | |
238 | ||
239 | if (i==OTRAPARROW) /* arrow hits monster */ | |
240 | { who = "An arrow"; if ((hitp[cc][dd] -= rnd(10)+level) <= 0) | |
241 | { mitem[cc][dd]=0; flag=2; } else flag=1; } | |
242 | if (i==ODARTRAP) /* dart hits monster */ | |
243 | { who = "A dart"; if ((hitp[cc][dd] -= rnd(6)) <= 0) | |
244 | { mitem[cc][dd]=0; flag=2; } else flag=1; } | |
245 | if (i==OTELEPORTER) /* monster hits teleport trap */ | |
246 | { flag=3; fillmonst(mitem[cc][dd]); mitem[cc][dd]=0; } | |
247 | if (c[BLINDCOUNT]) return; /* if blind don't show where monsters are */ | |
248 | if (know[cc][dd] & 1) | |
249 | { | |
250 | p=0; | |
251 | if (flag) cursors(); | |
252 | switch(flag) | |
253 | { | |
254 | case 1: p="\n%s hits the %s"; break; | |
255 | case 2: p="\n%s hits and kills the %s"; break; | |
256 | case 3: p="\nThe %s%s gets teleported"; who=""; break; | |
257 | }; | |
258 | if (p) { lprintf(p,who,monster[tmp].name); beep(); } | |
259 | } | |
260 | /* if (yrepcount>1) { know[aa][bb] &= 2; know[cc][dd] &= 2; return; } */ | |
261 | if (know[aa][bb] & 1) show1cell(aa,bb); | |
262 | if (know[cc][dd] & 1) show1cell(cc,dd); | |
263 | } | |
264 | ||
265 | /* | |
266 | * movsphere() Function to look for and move spheres of annihilation | |
267 | * | |
268 | * This function works on the sphere linked list, first duplicating the list | |
269 | * (the act of moving changes the list), then processing each sphere in order | |
270 | * to move it. They eat anything in their way, including stairs, volcanic | |
271 | * shafts, potions, etc, except for upper level demons, who can dispel | |
272 | * spheres. | |
273 | * No value is returned. | |
274 | */ | |
275 | #define SPHMAX 20 /* maximum number of spheres movsphere can handle */ | |
276 | movsphere() | |
277 | { | |
278 | register int x,y,dir,len; | |
279 | register struct sphere *sp,*sp2; | |
280 | struct sphere sph[SPHMAX]; | |
281 | ||
282 | /* first duplicate sphere list */ | |
283 | for (sp=0,x=0,sp2=spheres; sp2; sp2=sp2->p) /* look through sphere list */ | |
284 | if (sp2->lev == level) /* only if this level */ | |
285 | { | |
286 | sph[x] = *sp2; sph[x++].p = 0; /* copy the struct */ | |
287 | if (x>1) sph[x-2].p = &sph[x-1]; /* link pointers */ | |
288 | } | |
289 | if (x) sp= sph; /* if any spheres, point to them */ | |
290 | else return; /* no spheres */ | |
291 | ||
292 | for (sp=sph; sp; sp=sp->p) /* look through sphere list */ | |
293 | { | |
294 | x = sp->x; y = sp->y; | |
295 | if (item[x][y]!=OANNIHILATION) continue; /* not really there */ | |
296 | if (--(sp->lifetime) < 0) /* has sphere run out of gas? */ | |
297 | { | |
298 | rmsphere(x,y); /* delete sphere */ | |
299 | continue; | |
300 | } | |
301 | switch(rnd((int)max(7,c[INTELLIGENCE]>>1))) /* time to move the sphere */ | |
302 | { | |
303 | case 1: | |
304 | case 2: /* change direction to a random one */ | |
305 | sp->dir = rnd(8); | |
306 | default: /* move in normal direction */ | |
307 | dir = sp->dir; len = sp->lifetime; | |
308 | rmsphere(x,y); | |
309 | newsphere(x+diroffx[dir],y+diroffy[dir],dir,len); | |
310 | }; | |
311 | } | |
312 | } |