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