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