xref: /dragonfly/games/larn/movem.c (revision d4ef6694)
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 				default:
184 					screen[m][k] = 0;
185 					break;
186 				}
187 			}
188 		screen[playerx][playery] = 1;
189 
190 		/* now perform proximity ripple from playerx,playery to monster */
191 		xl = tmp3 - 1;
192 		yl = tmp1 - 1;
193 		xh = tmp4 + 1;
194 		yh = tmp2 + 1;
195 		vxy(&xl, &yl);
196 		vxy(&xh, &yh);
197 		for (tmp = 1; tmp < distance; tmp++)	/* only up to 20 squares away */
198 			for (k = yl; k < yh; k++)
199 				for (m = xl; m < xh; m++)
200 					/* if find proximity n advance it */
201 					if (screen[m][k] == tmp)
202 						/* go around in a circle */
203 						for (z = 1; z < 9; z++) {
204 							if (screen[xtmp = m + diroffx[z]][ytmp = k + diroffy[z]] == 0)
205 								screen[xtmp][ytmp] = tmp + 1;
206 							if (xtmp == i && ytmp == j)
207 								goto out;
208 						}
209 
210 out:		if (tmp < distance)	/* did find connectivity */
211 			/* now select lowest value around playerx,playery */
212 			for (z = 1; z < 9; z++)	/* go around in a circle */
213 				if (screen[xl = i + diroffx[z]][yl = j + diroffy[z]] == tmp)
214 					if (!mitem[xl][yl]) {
215 						mmove(i, j, w1x[0] = xl, w1y[0] = yl);
216 						return;
217 					}
218 	}
219 
220 	/* dumb monsters move here */
221 	xl = i - 1;
222 	yl = j - 1;
223 	xh = i + 2;
224 	yh = j + 2;
225 	if (i < playerx)
226 		xl++;
227 	else if (i > playerx)
228 		--xh;
229 	if (j < playery)
230 		yl++;
231 	else if (j > playery)
232 		--yh;
233 	for (k = 0; k < 9; k++)
234 		w1[k] = 10000;
235 
236 	for (k = xl; k < xh; k++)
237 		/* for each square compute distance to player */
238 		for (m = yl; m < yh; m++) {
239 			tmp = k - i + 4 + 3 * (m - j);
240 			tmpitem = item[k][m];
241 			if (tmpitem != OWALL || (k == playerx && m == playery))
242 				if (mitem[k][m] == 0)
243 					if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR))
244 						if (tmpitem != OCLOSEDDOOR) {
245 							w1[tmp] = (playerx - k) * (playerx - k) + (playery - m) * (playery - m);
246 							w1x[tmp] = k;
247 							w1y[tmp] = m;
248 						}
249 		}
250 
251 	tmp = 0;
252 	for (k = 1; k < 9; k++)
253 		if (w1[tmp] > w1[k])
254 			tmp = k;
255 
256 	if (w1[tmp] < 10000)
257 		if ((i != w1x[tmp]) || (j != w1y[tmp]))
258 			mmove(i, j, w1x[tmp], w1y[tmp]);
259 }
260 
261 /*
262  *	mmove(x,y,xd,yd)	Function to actually perform the monster movement
263  *		int x,y,xd,yd;
264  *
265  *	Enter with the from coordinates in (x,y) and the destination coordinates
266  *	in (xd,yd).
267  */
268 static void
269 mmove(int aa, int bb, int cc, int dd)
270 {
271 	int tmp, i, flag;
272 	const char *who = NULL, *p;
273 	flag = 0;		/* set to 1 if monster hit by arrow trap */
274 	if ((cc == playerx) && (dd == playery)) {
275 		hitplayer(aa, bb);
276 		moved[aa][bb] = 1;
277 		return;
278 	}
279 	i = item[cc][dd];
280 	if ((i == OPIT) || (i == OTRAPDOOR))
281 		switch (mitem[aa][bb]) {
282 		case SPIRITNAGA:
283 		case PLATINUMDRAGON:
284 		case WRAITH:
285 		case VAMPIRE:
286 		case SILVERDRAGON:
287 		case POLTERGEIST:
288 		case DEMONLORD:
289 		case DEMONLORD + 1:
290 		case DEMONLORD + 2:
291 		case DEMONLORD + 3:
292 		case DEMONLORD + 4:
293 		case DEMONLORD + 5:
294 		case DEMONLORD + 6:
295 		case DEMONPRINCE:
296 			break;
297 
298 		default:
299 			mitem[aa][bb] = 0;	/* fell in a pit or trapdoor */
300 		}
301 	tmp = mitem[cc][dd] = mitem[aa][bb];
302 	if (i == OANNIHILATION) {
303 		if (tmp >= DEMONLORD + 3) {	/* demons dispel spheres */
304 			cursors();
305 			lprintf("\nThe %s dispels the sphere!", monster[tmp].name);
306 			rmsphere(cc, dd);	/* delete the sphere */
307 		} else
308 			i = tmp = mitem[cc][dd] = 0;
309 	}
310 	stealth[cc][dd] = 1;
311 	if ((hitp[cc][dd] = hitp[aa][bb]) < 0)
312 		hitp[cc][dd] = 1;
313 	mitem[aa][bb] = 0;
314 	moved[cc][dd] = 1;
315 	if (tmp == LEPRECHAUN)
316 		switch (i) {
317 		case OGOLDPILE:
318 		case OMAXGOLD:
319 		case OKGOLD:
320 		case ODGOLD:
321 		case ODIAMOND:
322 		case ORUBY:
323 		case OEMERALD:
324 		case OSAPPHIRE:
325 			item[cc][dd] = 0;	/* leprechaun takes gold */
326 		}
327 
328 	if (tmp == TROLL)	/* if a troll regenerate him */
329 		if ((gtime & 1) == 0)
330 			if (monster[tmp].hitpoints > hitp[cc][dd])
331 				hitp[cc][dd]++;
332 
333 	if (i == OTRAPARROW) {	/* arrow hits monster */
334 		who = "An arrow";
335 		if ((hitp[cc][dd] -= rnd(10) + level) <= 0) {
336 			mitem[cc][dd] = 0;
337 			flag = 2;
338 		} else
339 			flag = 1;
340 	}
341 	if (i == ODARTRAP) {	/* dart hits monster */
342 		who = "A dart";
343 		if ((hitp[cc][dd] -= rnd(6)) <= 0) {
344 			mitem[cc][dd] = 0;
345 			flag = 2;
346 		} else
347 			flag = 1;
348 	}
349 	if (i == OTELEPORTER) {	/* monster hits teleport trap */
350 		flag = 3;
351 		fillmonst(mitem[cc][dd]);
352 		mitem[cc][dd] = 0;
353 	}
354 	if (c[BLINDCOUNT])	/* if blind don't show where monsters are */
355 		return;
356 	if (know[cc][dd] & 1) {
357 		p = NULL;
358 		if (flag)
359 			cursors();
360 		switch (flag) {
361 		case 1:
362 			p = "\n%s hits the %s";
363 			break;
364 		case 2:
365 			p = "\n%s hits and kills the %s";
366 			break;
367 		case 3:
368 			p = "\nThe %s%s gets teleported";
369 			who = "";
370 			break;
371 		}
372 		if (p) {
373 			lprintf(p, who, monster[tmp].name);
374 			beep();
375 		}
376 	}
377 	if (know[aa][bb] & 1)
378 		show1cell(aa, bb);
379 	if (know[cc][dd] & 1)
380 		show1cell(cc, dd);
381 }
382 
383 /*
384  *	movsphere() 	Function to look for and move spheres of annihilation
385  *
386  *	This function works on the sphere linked list, first duplicating the list
387  *	(the act of moving changes the list), then processing each sphere in order
388  *	to move it.  They eat anything in their way, including stairs, volcanic
389  *	shafts, potions, etc, except for upper level demons, who can dispel
390  *	spheres.
391  *	No value is returned.
392  */
393 #define SPHMAX 20	/* maximum number of spheres movsphere can handle */
394 static void
395 movsphere(void)
396 {
397 	int x, y, dir, len;
398 	struct sphere *sp, *sp2;
399 	struct sphere sph[SPHMAX];
400 
401 	/* first duplicate sphere list */
402 	/* look through sphere list */
403 	for (sp = NULL, x = 0, sp2 = spheres; sp2; sp2 = sp2->p)
404 		if (sp2->lev == level) {	/* only if this level */
405 			sph[x] = *sp2;
406 			sph[x++].p = 0;		/* copy the struct */
407 			if (x > 1)
408 				sph[x - 2].p = &sph[x - 1];	/* link pointers */
409 		}
410 	if (x)		/* if any spheres, point to them */
411 		sp = sph;
412 	else		/* no spheres */
413 		return;
414 
415 	for (sp = sph; sp; sp = sp->p) {	/* look through sphere list */
416 		x = sp->x;
417 		y = sp->y;
418 		if (item[x][y] != OANNIHILATION)	/* not really there */
419 			continue;
420 		if (--(sp->lifetime) < 0) {	/* has sphere run out of gas? */
421 			rmsphere(x, y);		/* delete sphere */
422 			continue;
423 		}
424 		/* time to move the sphere */
425 		switch (rnd((int)max(7, c[INTELLIGENCE] >> 1))) {
426 		case 1:
427 		case 2:		/* change direction to a random one */
428 			sp->dir = rnd(8);
429 		default:	/* move in normal direction */
430 			dir = sp->dir;
431 			len = sp->lifetime;
432 			rmsphere(x, y);
433 			newsphere(x + diroffx[dir], y + diroffy[dir], dir, len);
434 		}
435 	}
436 }
437