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