1 /**************************************************************
2  *         _____    __                       _____            *
3  *        /  _  \  |  |    ____  ___  ___   /  |  |           *
4  *       /  /_\  \ |  |  _/ __ \ \  \/  /  /   |  |_          *
5  *      /    |    \|  |__\  ___/  >    <  /    ^   /          *
6  *      \____|__  /|____/ \___  >/__/\_ \ \____   |           *
7  *              \/            \/       \/      |__|           *
8  *                                                            *
9  **************************************************************
10  *    (c) Free Lunch Design 2003                              *
11  *    Written by Johan Peitz                                  *
12  *    http://www.freelunchdesign.com                          *
13  **************************************************************
14  *    This source code is released under the The GNU          *
15  *    General Public License (GPL). Please refer to the       *
16  *    document license.txt in the source directory or         *
17  *    http://www.gnu.org for license information.             *
18  **************************************************************/
19 
20 
21 
22 
23 #include "allegro.h"
24 #include "actor.h"
25 #include "map.h"
26 #include "timer.h"
27 #include "player.h"
28 #include "bullet.h"
29 #include "particle.h"
30 #include "main.h"
31 
32 
33 #include "../data/data.h"
34 
35 // pointer to datafile declared in main.c
36 extern DATAFILE* data;
37 
38 // draw an actor at specified position
draw_actor(BITMAP * bmp,Tactor * a,int x,int y)39 void draw_actor(BITMAP *bmp, Tactor *a, int x, int y) {
40 	// check if the actor has any frames and quit otherwise
41 	if (!a->num_frames) return;
42 
43 	if (a->status == AC_NORM) {
44 		// normal drawing, just select direction
45 		if (a->direction)
46 			draw_sprite_h_flip(bmp, a->data[a->frames[a->frame] + (a->is_hit ? a->hit_offset : 0)].dat, x, y - a->h - a->oy);
47 		else
48 			draw_sprite(bmp, a->data[a->frames[a->frame] + (a->is_hit ? a->hit_offset : 0)].dat, x, y - a->h - a->oy);
49 	}
50 	else if (a->status == AC_DEAD) {
51 		// draw dead frame, check for direction
52 		if (a->direction)
53 			draw_sprite_vh_flip(bmp, a->data[a->frames[a->frame]].dat, x, y - a->h - a->oy);
54 		else
55 			draw_sprite_v_flip(bmp, a->data[a->frames[a->frame]].dat, x, y - a->h - a->oy);
56 	}
57 	else if (a->status == AC_FLAT) {
58 		// draw flat frame, check for direction
59 		if (a->direction)
60 			draw_sprite_h_flip(bmp, a->data[a->flat_frame].dat, x, y - a->h - a->oy);
61 		else
62 			draw_sprite(bmp, a->data[a->flat_frame].dat, x, y - a->h - a->oy);
63 	}
64 }
65 
66 
67 // animates the actor one tick
animate_actor(Tactor * a)68 void animate_actor(Tactor *a) {
69 	a->anim_counter ++;
70 	if (a->anim_counter > a->anim_max) {
71 		a->anim_counter = 0;
72 		a->frame ++;
73 		if (a->frame >= a->num_frames)
74 			a->frame = 0;
75 	}
76 }
77 
78 // gets an unused actor from the array a
79 // a = array to look in
make_actor(Tactor * a,int x,int y,DATAFILE * data)80 Tactor *make_actor(Tactor *a, int x, int y, DATAFILE *data) {
81 	int i = 0;
82 	// find free slot
83 	while(a[i].active && i < MAX_ACTORS) i ++;
84 
85 	// set values
86 	if (i < MAX_ACTORS) {
87 		a[i].active = 1;
88 		a[i].status = AC_NORM;
89 		a[i].x = x;
90 		a[i].y = y;
91 		a[i].direction = 0;
92 		a[i].data = data;
93 
94 		a[i].frame = 0;
95 		a[i].anim_counter = 0;
96 		a[i].anim_max = 4;
97 
98 		a[i].dy = a[i].dx = a[i].tx = 0;
99 		a[i].mode = a[i].toggle = 0;
100 		a[i].counter = 0;
101 
102 		a[i].energy = 1;
103 		a[i].is_hit = 0;
104 		a[i].sound = -1;
105 
106 		return &a[i];
107 	}
108 
109 	// failed
110 	return NULL;
111 }
112 
113 // special handler for jumping the spike crusher guardian
_crush_jump(Tactor * a,Tmap * m)114 void _crush_jump(Tactor *a, Tmap *m) {
115 	a->y += a->dy >> 2;
116 	a->dy ++;
117 	if (a->x < a->tx) a->x ++;
118 	if (a->x > a->tx) a->x --;
119 
120 	// check left and right
121 	if (is_ground(m, a->x, a->y-1)) a->x ++;
122 	if (is_ground(m, a->x+31, a->y-1)) a->x --;
123 
124 	// check ground
125 	if (is_ground(m, a->x+1, a->y)) {
126 		a->y += adjust_ypos(m, a->x+1, a->y - 1, 1, -1);
127 		create_burst(particle, a->x +  8, a->y, 8, 10, 25, PARTICLE_DUST);
128 		create_burst(particle, a->x + 16, a->y, 8, 10, 25, PARTICLE_DUST);
129 		create_burst(particle, a->x + 24, a->y, 8, 10, 25, PARTICLE_DUST);
130 		play_sound_id(SMPL_CRUSH_LAND);
131 		a->mode ++;
132 	}
133 	else if (is_ground(m, a->x+15, a->y)) {
134 		a->y += adjust_ypos(m, a->x+15, a->y - 1, 1, -1);
135 		create_burst(particle, a->x +  8, a->y, 8, 10, 25, PARTICLE_DUST);
136 		create_burst(particle, a->x + 16, a->y, 8, 10, 25, PARTICLE_DUST);
137 		create_burst(particle, a->x + 24, a->y, 8, 10, 25, PARTICLE_DUST);
138 		play_sound_id(SMPL_CRUSH_LAND);
139 		a->mode ++;
140 	}
141 	else if (is_ground(m, a->x+30, a->y)) {
142 		a->y += adjust_ypos(m, a->x+30, a->y - 1, 1, -1);
143 		create_burst(particle, a->x +  8, a->y, 8, 10, 25, PARTICLE_DUST);
144 		create_burst(particle, a->x + 16, a->y, 8, 10, 25, PARTICLE_DUST);
145 		create_burst(particle, a->x + 24, a->y, 8, 10, 25, PARTICLE_DUST);
146 		play_sound_id(SMPL_CRUSH_LAND);
147 		a->mode ++;
148 	}
149 }
150 
151 // special handler for jumping the spike crusher guardian
_spike_jump(Tactor * a,Tmap * m)152 void _spike_jump(Tactor *a, Tmap *m) {
153 	a->y += a->dy >> 2;
154 	a->dy ++;
155 	if (a->x < a->tx) a->x ++;
156 	if (a->x > a->tx) a->x --;
157 
158 	// check left and right
159 	if (is_ground(m, a->x, a->y-1)) a->x ++;
160 	if (is_ground(m, a->x+31, a->y-1)) a->x --;
161 
162 	// check ground
163 	if (is_ground(m, a->x+1, a->y)) {
164 		int tx, ty;
165 		Tmappos *mp;
166 
167 		tx = (a->x+1)>>4;
168 		ty = (a->y)>>4;
169 		mp = get_mappos(m, tx, ty);
170 		if (mp->type == MAP_BRK) {
171 			mp->tile = mp->type = mp->mask = 0;
172 			create_burst(particle, (tx << 4) + 7, (ty << 4) + 7, 32, 64, 0, -1);
173 			create_burst(particle, (tx << 4) + 7, (ty << 4) + 7, 32, 64, 0, -1);
174 			play_sound_id(SMPL_CRUSH);
175 		}
176 
177 		create_burst(particle, a->x + 16, a->y, 8, 6, 25, PARTICLE_DUST);
178 		play_sound_id(SMPL_CRUSH_LAND);
179 		a->toggle = 1;  // don't crush any more , fall and land
180 	}
181 
182 	// check ground
183 	if (is_ground(m, a->x+15, a->y)) {
184 		int tx, ty;
185 		Tmappos *mp;
186 
187 		tx = (a->x+15)>>4;
188 		ty = (a->y)>>4;
189 		mp = get_mappos(m, tx, ty);
190 		if (mp->type == MAP_BRK) {
191 			mp->tile = mp->type = mp->mask = 0;
192 			create_burst(particle, (tx << 4) + 7, (ty << 4) + 7, 32, 64, 0, -1);
193 			create_burst(particle, (tx << 4) + 7, (ty << 4) + 7, 32, 64, 0, -1);
194 			play_sound_id(SMPL_CRUSH);
195 		}
196 
197 		create_burst(particle, a->x + 16, a->y, 8, 6, 25, PARTICLE_DUST);
198 		play_sound_id(SMPL_CRUSH_LAND);
199 		a->toggle = 1;  // don't crush any more , fall and land
200 	}
201 
202 	// check ground
203 	if (is_ground(m, a->x+30, a->y)) {
204 		int tx, ty;
205 		Tmappos *mp;
206 
207 		tx = (a->x+30)>>4;
208 		ty = (a->y)>>4;
209 		mp = get_mappos(m, tx, ty);
210 		if (mp->type == MAP_BRK) {
211 			mp->tile = mp->type = mp->mask = 0;
212 			create_burst(particle, (tx << 4) + 7, (ty << 4) + 7, 32, 64, 0, -1);
213 			create_burst(particle, (tx << 4) + 7, (ty << 4) + 7, 32, 64, 0, -1);
214 			play_sound_id(SMPL_CRUSH);
215 		}
216 
217 		create_burst(particle, a->x + 16, a->y, 8, 6, 25, PARTICLE_DUST);
218 		play_sound_id(SMPL_CRUSH_LAND);
219 		a->toggle = 1;  // don't crush any more , fall and land
220 	}
221 
222 }
223 
224 
225 // checks the actor with the tile map
226 // this is an ugly function with an if clause
227 // for each enemy type
update_actor_with_map(Tactor * a,Tmap * m)228 void update_actor_with_map(Tactor *a, Tmap *m) {
229 	if (a->energy <= 0 && a->status == AC_NORM) {
230 		a->status = AC_DEAD;
231 		play_sound_id(SMPL_E_DIE);
232 		a->dy = -8;
233 		if (a->type == MAP_GUARD1) a->dy =- 16;
234 
235 	}
236 
237 	if (a->status == AC_DEAD) {
238 		if (a->direction) a->x -= 1;
239 		else a->x += 1;
240 		a->dy ++;
241 		a->y += a->dy >> 2;
242 
243 		// add smoke to dying actor
244 		if (a->type == MAP_GUARD1) {
245 			Tparticle *p;
246 			p = get_free_particle(particle, MAX_PARTICLES);
247 			if (p != NULL) {
248 				set_particle(p, a->x + 8 + rand()%16, a->y - 8 - rand()%16, 0, 0, -1, 16, SMOKE1);
249 			}
250 		}
251 	}
252 	else if (a->status == AC_FLAT) {
253 		if (a->counter > 200) {
254 			a->x += (a->counter % 3) - 1;
255 		}
256 		if (++ a->counter > 250) a->status = AC_NORM;
257 
258 		if (!is_ground(m, (int)a->x + 2, (int)a->y + 0)) {
259 			if (!is_ground(m, (int)a->x + 13, (int)a->y + 0)) {
260 				a->dy ++;
261 				a->y += a->dy >> 2;
262 				while(is_ground(m, (int)a->x + 13, (int)a->y - 2)) { a->y --; a->dy = 0; }
263 				while(is_ground(m, (int)a->x + 2, (int)a->y - 2)) { a->y --; a->dy = 0; }
264 			}
265 		}
266 
267 	}
268 	else if (a->type == MAP_ENEMY6) {		// cannon
269 		a->mode ++;
270 		if (a->mode >= 125) {
271 			Tbullet *b = get_free_bullet(bullet, MAX_BULLETS);
272 			if (b != NULL) {
273 				int diff = (player.actor->x + 8) - (a->x + 8);
274 				set_bullet(b, a->x + (diff > 0 ? 8 : -12), a->y - 16, (diff > 0 ? 3 : -3), 0, data[ENEMY6].dat, 1);
275 				create_burst(particle, a->x + (diff > 0 ? 18 : -2), a->y - 8, 8, 8, 25, PARTICLE_DUST);
276 				a->mode = 0;
277 				play_sound_id(SMPL_SHOOT);
278 			}
279 		}
280 	}
281 	else if (a->type == MAP_ENEMY4 || a->type == MAP_ENEMY5) {	// fish
282 		if (a->mode == 0) {
283 			int b[] = {8, 14, 18, 21, 23, 26, 28};
284 			a->ymax = a->y;
285 			a->y = 120;
286 			a->mode ++;
287 			a->dy = -b[MIN(MAX(ABS(a->ymax - a->y) / 16, 0), 7)];
288 			a->ymax = a->dy;
289 		}
290 		else if (a->mode == 1) {
291 			a->dy ++;
292 			a->y += a->dy >> 2;
293 			if (a->y >= 120) {
294 				a->y = 130;
295 				a->mode = 2;
296 			}
297 
298 			if (a->type == MAP_ENEMY5) {
299 				a->frames[0] = (a->dy < 0 ? ENEMY5_02 : ENEMY5_01);
300 			}
301 		}
302 		else if (a->mode >= 2) {
303 			a->mode ++;
304 			if (a->mode > 40) {
305 				a->y = 120;
306 				a->dy = a->ymax;
307 				a->mode = 1;
308 			}
309 		}
310 	}
311 	else if (a->type == MAP_ENEMY3) {		// stomper/crusher
312 		if (a->mode == 0) {
313 			a->ymax = a->y;
314 			a->mode ++;
315 			a->dy = 0;
316 		}
317 		else if (a->mode == 1) {
318 			int diff = ABS((player.actor->x + 8) - (a->x + 16));
319 			if (diff < 32) {
320 				a->mode = 2;
321 				a->dy = 0;
322 				a->dy = -4;
323 			}
324 		}
325 		else if (a->mode == 2) {
326 			int hit = 0;
327 
328 			a->dy ++;
329 			a->y += a->dy >> 2;
330 
331 			while(is_ground(m, (int)a->x + 8, (int)a->y - 1)) { a->y --; hit = 1; }
332 			while(is_ground(m, (int)a->x + 24, (int)a->y - 1)) { a->y --; hit = 1; }
333 
334 			if (hit) {
335 				a->dy = -1;
336 				a->mode = 3;
337 				play_sound_id(SMPL_CRUSH_LAND);
338 				create_burst(particle, a->x + 16, a->y, 8, 6, 25, PARTICLE_DUST);
339 			}
340 		}
341 		else if (a->mode >= 3) {
342 			a->mode ++;
343 			if (a->mode > 25) {
344 				if (game_count & 1) a->y += a->dy;
345 				if (a->y <=  a->ymax) a->mode = 1;
346 			}
347 		}
348 	}
349 	else if (a->type == MAP_GUARD2) {		// ground pounder
350 		/* MODES:
351 			0 long wait
352 			    if jumped on, mode = 4
353 			1 get alex pos
354 			2 jump
355 			3 mode = 0
356 			4 spikes on, wait
357 			5 get alex pos
358 			6 jump
359 			7 small wait
360 			8 spikes off
361 			9 small wait
362 		   10 fire
363 		   11 small wait
364 		   12 mode = 0
365 		*/
366 
367 		Tactor *alex = get_alex();
368 		switch(a->mode) {
369 			case 0:		// wait
370 				a->frames[0] = GUARD2_1a;
371 				a->frames[1] = GUARD2_1b;
372 				a->counter ++;
373 				if (a->counter > 60) {
374 					a->mode ++;
375 					a->counter = 0;
376 				}
377 				break;
378 			case 1:		// aim
379 				a->tx = alex->x - 8;
380 				a->dy = - MIN(MAX(ABS(a->x - a->tx) / 2, 10), 30);
381 				a->mode ++;
382 				break;
383 			case 2:		// jump
384 				a->frames[0] = a->frames[1] = GUARD2_2;
385 				_crush_jump(a, m);
386 				break;
387 			case 3:		// land, goto 0
388 				a->mode = 0;
389 				break;
390 			case 4:		// turn on spikes, wait
391 				a->frames[0] = a->frames[1] = GUARD2_3;
392 				a->counter ++;
393 				if (a->counter > 50) {
394 					a->mode ++;
395 					a->counter = 0;
396 				}
397 				break;
398 			case 5:		// aim
399 				a->tx = alex->x - 8;
400 				a->dy = - MAX(ABS(a->x - a->tx) / 2, 10);
401 				a->mode ++;
402 				a->toggle = 0;  // crush
403 				break;
404 			case 6:		// jump
405 				a->frames[0] = a->frames[1] = GUARD2_4;
406 				if (a->toggle) _crush_jump(a, m);
407 				else _spike_jump(a, m);
408 				break;
409 			case 7:		// land, short wait
410 				a->frames[0] = a->frames[1] = GUARD2_5;
411 				a->counter ++;
412 				if (a->counter > 50) {
413 					a->mode ++;
414 					a->counter = 0;
415 				}
416 				break;
417 			case 8: {		// fire
418 				Tbullet *b;
419 				int i;
420 
421 				play_sound_id(SMPL_SHOOT);
422 				for(i = 0; i < 3; i ++) {
423 					b = get_free_bullet(bullet, MAX_BULLETS);
424 					if (b != NULL) {
425 						set_bullet(b, a->x + 16, a->y - 32, rand()%5 - 2, -(rand()%5 + 2), data[BULLET_1].dat, 1);
426 						b->animate = b->gravity = 1;
427 						b->bmp2 = data[BULLET_2].dat;
428 					}
429 				}
430 				a->mode ++;
431 				break;
432 			}
433 			case 9:		// wait
434 				a->counter ++;
435 				if (a->counter > 10) {
436 					a->mode ++;
437 					a->counter = 0;
438 				}
439 				break;
440 			case 10:		// spikes off
441 				a->frames[0] = a->frames[1] = GUARD2_6;
442 				a->mode ++;
443 				break;
444 			case 11:		// wait
445 				a->counter ++;
446 				if (a->counter > 10) {
447 					a->mode ++;
448 					a->counter = 0;
449 				}
450 				break;
451 			case 12:		// return
452 				a->frames[0] = GUARD2_1a;
453 				a->frames[1] = GUARD2_1b;
454 				a->mode = 0;
455 				break;
456 		}
457 		animate_actor(a);
458 	}
459 	else if (a->status == AC_NORM) {  // other ppl
460 		if (game_count & 1) {
461 			if (a->direction) a->x += 1;
462 			else a->x -= 1;
463 		}
464 		animate_actor(a);
465 
466 		if (a->type == MAP_ENEMY2 || a->type == MAP_GUARD1) {
467 			if (!is_ground(m, (int)a->x + (a->direction ? a->w - 1 : 0), (int)a->y + 2))
468 				a->direction = (a->direction ? 0 : 1);
469 		}
470 		else {
471 			if (!is_ground(m, (int)a->x + 2, (int)a->y + 0)) {
472 				if (!is_ground(m, (int)a->x + 13, (int)a->y + 0)) {
473 					a->dy ++;
474 					a->y += a->dy >> 2;
475 					while(is_ground(m, (int)a->x + 13, (int)a->y - 1)) { a->y --; a->dy = 0; }
476 					while(is_ground(m, (int)a->x + 2, (int)a->y - 1)) { a->y --; a->dy = 0; }
477 				}
478 			}
479 		}
480 
481 		if (is_ground(m, (int)a->x + (a->direction ? a->w : -1), (int)a->y - 2)) a->direction = (a->direction ? 0 : 1);
482 
483 	}
484 
485 	// play/adjust sound
486 	if (a->sound != -1) {
487 		adjust_sound_id_ex(a->sound, a->x);
488 	}
489 
490 
491 
492 	if (a->y > 160 + (a->type == MAP_GUARD2 || a->type == MAP_GUARD1 ? 600 : 0)) {
493 		// change win conditions if needed
494 		if (a->type == MAP_GUARD2 || a->type == MAP_GUARD1)  {
495 			m->win_conditions_fullfilled |= m->win_conditions & MAP_WIN_KILL_GUARDIAN;
496 		}
497 
498 		a->active = 0;
499 
500 		// stop sample if available
501 		if (a->sound != -1) stop_sound_id(a->sound);
502 
503 		// if not already doen, decrease number of enemies left
504 		if (a->status != AC_DEAD) {
505 			m->num_enemies --;
506 		}
507 	}
508 }
509 
510 
511 // puts the actor into dead-mode
kill_actor(Tactor * a)512 void kill_actor(Tactor *a) {
513 	a->status = AC_DEAD;
514 	a->dy = -10;
515 }
516 
517