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