Reinitialize console text / font on resolution change
[attac-man.git] / ghost.c
blob54cee9bd670c7b329837b8ac51671f373224ba00
1 /*
2 Pacman Arena
3 Copyright (C) 2003 Nuno Subtil
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 static const char cvsid[] =
21 "$Id: ghost.c,v 1.21 2003/11/22 17:07:42 nsubtil Exp $";
23 #ifdef _WIN32
24 #include <windows.h>
25 #endif
27 #include <GL/gl.h>
28 #include <SDL.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #include "m_math.h"
34 #include "object.h"
35 #include "game.h"
36 #include "map.h"
37 #include "ghost.h"
38 #include "player.h"
39 #include "render.h"
40 #include "audio.h"
42 GLfloat ghost_colors[4][4] = {
43 {GHOST_GREEN}, {GHOST_RED}, {GHOST_MAGENTA}, {GHOST_ORANGE}
45 int num_ghost_colors = 4;
47 void ghost_taint_all(struct game *game)
49 int c;
51 for(c = 0; c < game->n_ghosts; c++)
52 if(!game->ghosts[c].tainted && game->ghosts[c].state == GHOST_STATE_ACTIVE)
54 game->ghosts[c].tainted = 1;
55 game->ghosts[c].speed /= 2.0;
59 void ghost_untaint_all(struct game *game)
61 int c;
63 for(c = 0; c < game->n_ghosts; c++)
64 if(game->ghosts[c].tainted)
66 game->ghosts[c].tainted = 0;
67 game->ghosts[c].speed *= 2.0;
71 void ghost_reset_all(struct game *game)
73 int cx, cy;
74 struct map *map;
76 map = game->map;
78 if(game->ghosts)
79 free(game->ghosts);
81 game->ghosts = NULL;
82 game->n_ghosts = 0;
84 for(cx = 0; cx < map->width; cx++)
85 for(cy = 0; cy < map->height; cy++)
86 if(MAP(map, cx, cy).flags & MAP_FLAG_GHOST_START_POSITION)
88 struct ghost *new;
90 game->n_ghosts++;
91 game->ghosts = realloc(game->ghosts,
92 sizeof(struct ghost) * game->n_ghosts);
94 new = &game->ghosts[game->n_ghosts - 1];
95 new->state = GHOST_STATE_ACTIVE;
97 new->position[X] = (float)cx + 0.5;
98 new->position[Y] = -0.5;
99 new->position[Z] = (float)cy + 0.5;
101 new->direction = DIRECTION_UP;
102 new->speed = GHOST_SPEED;
104 new->model_active =
105 object_read_file("gfx/ghost-green-moving.3d",
106 &new->frames_active);
108 new->model_dying =
109 object_read_file("gfx/ghost-green-dying.3d",
110 &new->frames_dying);
112 new->model_dead = &new->model_dying[new->frames_dying - 1];
113 new->frames_dead = 1;
115 new->model_returning =
116 object_read_file("gfx/ghost-green-returning.3d",
117 &new->frames_returning);
119 new->current_frame = (float)(rand() % new->frames_active);
120 new->time = 0.0;
121 new->view_distance = 10;
123 new->color = ghost_colors[(game->n_ghosts - 1) % num_ghost_colors];
124 new->tainted = 0;
128 void ghost_kill(struct game *game, struct ghost *g)
130 g->state = GHOST_STATE_DYING;
131 g->current_frame = 0.0;
132 audio_play_sample("sfx/eat-ghost.wav");
135 void ghost_update(struct game *game, int ghost_no, float delta)
137 float new_position[3], vec[2];
138 float frac_x, frac_z, dest_x, dest_z;
139 struct ghost *g;
140 struct map *map;
141 int keep_moving = 1, verify = 0;
143 g = &game->ghosts[ghost_no];
144 map = game->map;
146 switch(g->state)
148 case GHOST_STATE_DYING:
149 g->current_frame += delta * ANIM_FPS;
151 if(g->current_frame >= (float)g->frames_dying)
153 g->state = GHOST_STATE_DEAD;
154 g->current_frame = 0.0;
155 if(g->tainted)
157 g->tainted = 0;
158 g->speed *= 2.0;
163 return;
165 case GHOST_STATE_RETURNING:
166 g->current_frame += delta * ANIM_FPS;
168 if(g->current_frame >= (float)g->frames_returning)
170 g->state = GHOST_STATE_ACTIVE;
171 g->current_frame = 0.0;
174 return;
177 g->time -= delta;
178 if(g->time <= 0.0)
179 keep_moving = 0;
181 new_position[X] = g->position[X];
182 new_position[Y] = g->position[Y];
183 new_position[Z] = g->position[Z];
185 frac_x = g->position[X] - (float)((int)g->position[X]);
186 frac_z = g->position[Z] - (float)((int)g->position[Z]);
188 dest_x = (int)g->position[X] + 0.5;
189 dest_z = (int)g->position[Z] + 0.5;
191 switch(g->direction)
193 case DIRECTION_UP:
194 new_position[Z] += MIN(delta * g->speed, 0.5);
196 if(frac_z > 0.5)
197 dest_z += 1.0;
199 if(new_position[Z] > dest_z)
201 verify = 1;
203 if(keep_moving)
205 g->position[X] = new_position[X];
206 g->position[Z] = new_position[Z];
207 } else {
208 g->position[X] = dest_x;
209 g->position[Z] = dest_z;
210 ghost_new_direction(game, g);
212 } else {
213 if(MAP_CAN_ENTER_GHOST(map, (int)dest_x, (int)dest_z))
215 g->position[X] = new_position[X];
216 g->position[Z] = new_position[Z];
217 } else {
219 g->position[X] = dest_x;
220 g->position[Z] = dest_z - 1.0;
221 do {
222 ghost_new_direction(game, g);
223 } while(g->direction == DIRECTION_UP &&
224 g->state != GHOST_STATE_DEAD);
228 break;
230 case DIRECTION_DOWN:
231 new_position[Z] -= MIN(delta * g->speed, 0.5);
233 if(frac_z < 0.5)
234 dest_z -= 1.0;
236 if(new_position[Z] < dest_z)
238 verify = 1;
240 if(keep_moving)
242 g->position[X] = new_position[X];
243 g->position[Z] = new_position[Z];
244 } else {
245 g->position[X] = dest_x;
246 g->position[Z] = dest_z;
247 ghost_new_direction(game, g);
249 } else {
250 if(MAP_CAN_ENTER_GHOST(map, (int)dest_x, (int)dest_z))
252 g->position[X] = new_position[X];
253 g->position[Z] = new_position[Z];
254 } else {
256 g->position[X] = dest_x;
257 g->position[Z] = dest_z + 1.0;
258 do {
259 ghost_new_direction(game, g);
260 } while(g->direction == DIRECTION_DOWN &&
261 g->state != GHOST_STATE_DEAD);
265 break;
267 case DIRECTION_LEFT:
268 new_position[X] -= MIN(delta * g->speed, 0.5);
270 if(frac_x < 0.5)
271 dest_x -= 1.0;
273 if(new_position[X] < dest_x)
275 verify = 1;
277 if(keep_moving)
279 g->position[X] = new_position[X];
280 g->position[Z] = new_position[Z];
281 } else {
282 g->position[X] = dest_x;
283 g->position[Z] = dest_z;
284 ghost_new_direction(game, g);
286 } else {
287 if(MAP_CAN_ENTER_GHOST(map, (int)dest_x, (int)dest_z))
289 g->position[X] = new_position[X];
290 g->position[Z] = new_position[Z];
291 } else {
293 g->position[X] = dest_x + 1.0;
294 g->position[Z] = dest_z;
295 do {
296 ghost_new_direction(game, g);
297 } while(g->direction == DIRECTION_LEFT &&
298 g->state != GHOST_STATE_DEAD);
302 break;
304 case DIRECTION_RIGHT:
305 new_position[X] += MIN(delta * g->speed, 0.5);
307 if(frac_x > 0.5)
308 dest_x += 1.0;
310 if(new_position[X] > dest_x)
312 verify = 1;
314 if(keep_moving)
316 g->position[X] = new_position[X];
317 g->position[Z] = new_position[Z];
318 } else {
319 g->position[X] = dest_x;
320 g->position[Z] = dest_z;
321 ghost_new_direction(game, g);
323 } else {
324 if(MAP_CAN_ENTER_GHOST(map, (int)dest_x, (int)dest_z))
326 g->position[X] = new_position[X];
327 g->position[Z] = new_position[Z];
328 } else {
330 g->position[X] = dest_x - 1.0;
331 g->position[Z] = dest_z;
332 do {
333 ghost_new_direction(game, g);
334 } while(g->direction == DIRECTION_RIGHT &&
335 g->state != GHOST_STATE_DEAD);
339 break;
342 if(g->time <= 0.0)
343 ghost_new_direction(game, g);
345 vec[X] = g->position[X] - (float)((int)g->position[X]);
346 vec[Y] = g->position[Z] - (float)((int)g->position[Z]);
347 if(verify || math_norm_vec2(vec) <= 0.1)
349 if(MAP(map, (int)g->position[X], (int)g->position[Z]).flags & MAP_FLAG_GHOST_START_POSITION)
350 if(g->state == GHOST_STATE_DEAD)
352 g->state = GHOST_STATE_RETURNING;
353 g->current_frame = 0.0;
354 audio_play_sample("sfx/ghost-return.wav");
355 return;
358 switch(MAP(map, (int)g->position[X], (int)g->position[Z]).ghost_dir_alive)
360 case DIRECTION_UP:
361 if(g->state == GHOST_STATE_ACTIVE)
363 g->direction = DIRECTION_UP;
364 g->time = 1.0 / g->speed;
367 break;
369 default:
371 int dir, dist;
373 dir = ghost_check_visibility(game, g, &dist);
374 if(dir != -1)
376 /* jogador por perto */
377 g->direction = dir;
378 g->time = dist / g->speed;
384 /* animação */
385 g->current_frame = g->current_frame + delta * ANIM_FPS;
387 ghost_detect_player_collisions(game, g);
388 ghost_detect_ghost_collisions(game, g);
391 void ghost_detect_ghost_collisions(struct game *game, struct ghost *g)
393 int c;
394 float vec[3];
395 struct map *map;
397 map = game->map;
399 for(c = 0; c < game->n_ghosts; c++)
401 math_sub_vec3(vec, g->position, game->ghosts[c].position);
403 if(math_norm_vec3(vec) < 0.7)
405 int gx, gy, ox, oy;
407 if(math_norm_vec3(vec) == 0.0)
408 /* sou eu */
409 continue;
411 if(g->direction == DIRECTION_STOPPED)
412 /* já colidiu */
413 continue;
415 gx = (int)g->position[X];
416 gy = (int)g->position[Z];
418 ox = (int)game->ghosts[c].position[X];
419 oy = (int)game->ghosts[c].position[Z];
421 if(rand() % 2)
423 /* escapatória para g */
424 if(MAP_CAN_ENTER_GHOST(map, gx, gy + 1) &&
425 !ghost_in_position(game, gx, gy + 1))
427 g->direction = DIRECTION_UP;
428 g->time = 1.0;
430 // ghosts[c].direction = DIRECTION_STOPPED;
431 // ghosts[c].time = 0.25;
432 continue;
435 if(MAP_CAN_ENTER_GHOST(map, gx, gy - 1) &&
436 !ghost_in_position(game, gx, gy - 1))
438 g->direction = DIRECTION_DOWN;
439 g->time = 1.0;
441 // ghosts[c].direction = DIRECTION_STOPPED;
442 // ghosts[c].time = 0.25;
443 continue;
446 if(MAP_CAN_ENTER_GHOST(map, gx - 1, gy) &&
447 !ghost_in_position(game, gx - 1, gy))
449 g->direction = DIRECTION_LEFT;
450 g->time = 1.0;
452 // ghosts[c].direction = DIRECTION_STOPPED;
453 // ghosts[c].time = 0.25;
454 continue;
457 if(MAP_CAN_ENTER_GHOST(map, gx + 1, gy) &&
458 !ghost_in_position(game, gx + 1, gy))
460 g->direction = DIRECTION_RIGHT;
461 g->time = 1.0;
463 // ghosts[c].direction = DIRECTION_STOPPED;
464 // ghosts[c].time = 0.25;
465 continue;
468 /* escapatória para ghosts[c] */
469 g->direction = DIRECTION_STOPPED;
470 g->time = 0.0;
472 if(MAP_CAN_ENTER_GHOST(map, ox, oy + 1) &&
473 !ghost_in_position(game, ox, oy + 1))
475 game->ghosts[c].direction = DIRECTION_UP;
476 game->ghosts[c].time = 1.0;
477 continue;
480 if(MAP_CAN_ENTER_GHOST(map, ox, oy - 1) &&
481 !ghost_in_position(game, ox, oy - 1))
483 game->ghosts[c].direction = DIRECTION_DOWN;
484 game->ghosts[c].time = 1.0;
485 continue;
488 if(MAP_CAN_ENTER_GHOST(map, ox - 1, oy) &&
489 !ghost_in_position(game, ox - 1, oy))
491 game->ghosts[c].direction = DIRECTION_LEFT;
492 game->ghosts[c].time = 1.0;
493 continue;
496 if(MAP_CAN_ENTER_GHOST(map, ox + 1, oy) &&
497 !ghost_in_position(game, ox + 1, oy))
499 game->ghosts[c].direction = DIRECTION_RIGHT;
500 game->ghosts[c].time = 1.0;
501 continue;
504 game->ghosts[c].direction = DIRECTION_STOPPED;
505 game->ghosts[c].time = 0.25;
506 } else {
507 /* escapatória para ghosts[c] */
508 if(MAP_CAN_ENTER_GHOST(map, ox, oy + 1) &&
509 !ghost_in_position(game, ox, oy + 1))
511 game->ghosts[c].direction = DIRECTION_UP;
512 game->ghosts[c].time = 1.0;
514 // g->direction = DIRECTION_STOPPED;
515 // g->time = 0.25;
516 continue;
519 if(MAP_CAN_ENTER_GHOST(map, ox, oy - 1) &&
520 !ghost_in_position(game, ox, oy - 1))
522 game->ghosts[c].direction = DIRECTION_DOWN;
523 game->ghosts[c].time = 1.0;
525 // g->direction = DIRECTION_STOPPED;
526 // g->time = 0.25;
527 continue;
530 if(MAP_CAN_ENTER_GHOST(map, ox - 1, oy) &&
531 !ghost_in_position(game, ox - 1, oy))
533 game->ghosts[c].direction = DIRECTION_LEFT;
534 game->ghosts[c].time = 1.0;
536 // g->direction = DIRECTION_STOPPED;
537 // g->time = 0.25;
538 continue;
541 if(MAP_CAN_ENTER_GHOST(map, ox + 1, oy) &&
542 !ghost_in_position(game, ox + 1, oy))
544 game->ghosts[c].direction = DIRECTION_RIGHT;
545 game->ghosts[c].time = 1.0;
547 // g->direction = DIRECTION_STOPPED;
548 // g->time = 0.25;
549 continue;
552 game->ghosts[c].direction = DIRECTION_STOPPED;
553 game->ghosts[c].time = 0.0;
555 /* escapatória para g */
556 if(MAP_CAN_ENTER_GHOST(map, gx, gy + 1) &&
557 !ghost_in_position(game, gx, gy + 1))
559 g->direction = DIRECTION_UP;
560 g->time = 1.0;
561 continue;
564 if(MAP_CAN_ENTER_GHOST(map, gx, gy - 1) &&
565 !ghost_in_position(game, gx, gy - 1))
567 g->direction = DIRECTION_DOWN;
568 g->time = 1.0;
569 continue;
572 if(MAP_CAN_ENTER_GHOST(map, gx - 1, gy) &&
573 !ghost_in_position(game, gx - 1, gy))
575 g->direction = DIRECTION_LEFT;
576 g->time = 1.0;
577 continue;
580 if(MAP_CAN_ENTER_GHOST(map, gx + 1, gy) &&
581 !ghost_in_position(game, gx + 1, gy))
583 g->direction = DIRECTION_RIGHT;
584 g->time = 1.0;
585 continue;
588 g->direction = DIRECTION_STOPPED;
589 g->time = 0.25;
595 int ghost_in_position(struct game *game, int x, int y)
597 int c;
599 for(c = 0; c < game->n_ghosts; c++)
600 if((int)game->ghosts[c].position[X] == x &&
601 (int)game->ghosts[c].position[Z] == y)
602 return 1;
604 return 0;
607 void ghost_detect_player_collisions(struct game *game, struct ghost *g)
609 int c;
610 float vec[3];
612 if(g->state != GHOST_STATE_ACTIVE)
613 return;
615 for(c = 0; c < game->n_players; c++)
617 if(game->players[c].state == PLAYER_STATE_DEAD)
618 continue;
620 math_sub_vec3(vec, g->position, game->players[c].position);
621 if(math_norm_vec3(vec) < 0.7)
623 /* fantasma bateu num jogador */
624 if(game->players[c].pill_time > 0.0)
626 /* uh oh, fantasma comido */
627 game->players[c].score += (100 * game->players[c].multiplier);
628 game->players[c].multiplier <<= 1;
629 ghost_kill(game, g);
630 return;
633 switch(g->direction)
635 case DIRECTION_UP:
636 g->direction = DIRECTION_DOWN;
637 break;
639 case DIRECTION_DOWN:
640 g->direction = DIRECTION_UP;
641 break;
643 case DIRECTION_LEFT:
644 g->direction = DIRECTION_RIGHT;
645 break;
647 case DIRECTION_RIGHT:
648 g->direction = DIRECTION_LEFT;
649 break;
652 g->time = 1.0 / g->speed;
654 player_kill(game, c);
655 return;
661 devolve a direcção de um jogador próximo (distância em dist)
662 ou -1 se não houver
664 int ghost_check_visibility(struct game *game, struct ghost *g, int *r_dist)
666 int c, px, py, gx, gy, dist, player_no;
667 struct map *map;
669 map = game->map;
670 gx = (int)g->position[X];
671 gy = (int)g->position[Z];
673 if(g->state == GHOST_STATE_DEAD)
674 return -1;
676 /* determinar visibilidade de jogadores próximos */
677 for(player_no = 0; player_no < game->n_players; player_no++)
679 if(game->players[player_no].state == PLAYER_STATE_DEAD)
680 continue;
682 px = (int)game->players[player_no].position[X];
683 py = (int)game->players[player_no].position[Z];
685 if(py == gy)
687 dist = gx - px;
688 if(dist < 0)
689 dist = -dist;
691 if(dist > g->view_distance)
692 /* muito longe */
693 continue;
695 if(px < gx)
697 for(c = gx - 1; c > px; c--)
698 if(!MAP_CAN_ENTER_GHOST(map, c, py))
699 /* não chega lá */
700 break;
702 if(c == px)
704 /* jogador à esquerda */
705 *r_dist = dist;
707 if(game->players[player_no].pill_time > 0.0)
709 /* fugir! */
710 *r_dist = 1;
712 if(MAP_CAN_ENTER_GHOST(map,
713 (int)g->position[X] + 1,
714 (int)g->position[Z]))
715 return DIRECTION_RIGHT;
717 if(MAP_CAN_ENTER_GHOST(map,
718 (int)g->position[X],
719 (int)g->position[Z] + 1))
720 return DIRECTION_UP;
722 if(MAP_CAN_ENTER_GHOST(map,
723 (int)g->position[X],
724 (int)g->position[Z] - 1))
725 return DIRECTION_DOWN;
727 /* sem hipótese de fugir */
730 return DIRECTION_LEFT;
732 } else {
733 for(c = gx + 1; c < px; c++)
734 if(!MAP_CAN_ENTER_GHOST(map, c, py))
735 break;
737 if(c == px)
739 /* jogador à direita */
740 *r_dist = dist;
742 if(game->players[player_no].pill_time > 0.0)
744 /* fugir! */
745 *r_dist = 1;
747 if(MAP_CAN_ENTER_GHOST(map,
748 (int)g->position[X] - 1,
749 (int)g->position[Z]))
750 return DIRECTION_LEFT;
752 if(MAP_CAN_ENTER_GHOST(map,
753 (int)g->position[X],
754 (int)g->position[Z] + 1))
755 return DIRECTION_UP;
757 if(MAP_CAN_ENTER_GHOST(map,
758 (int)g->position[X],
759 (int)g->position[Z] - 1))
760 return DIRECTION_DOWN;
763 return DIRECTION_RIGHT;
768 if(px == gx)
770 dist = gy - py;
771 if(dist < 0)
772 dist = -dist;
774 if(dist > g->view_distance)
775 continue;
777 if(py < gy)
779 for(c = gy - 1; c > py; c--)
780 if(!MAP_CAN_ENTER_GHOST(map, px, c))
781 break;
783 if(c == py)
785 /* jogador abaixo */
786 *r_dist = dist;
788 if(game->players[player_no].pill_time > 0.0)
790 *r_dist = 1;
792 if(MAP_CAN_ENTER_GHOST(map,
793 (int)g->position[X],
794 (int)g->position[Z] + 1))
795 return DIRECTION_UP;
797 if(MAP_CAN_ENTER_GHOST(map,
798 (int)g->position[X] - 1,
799 (int)g->position[Z]))
800 return DIRECTION_LEFT;
802 if(MAP_CAN_ENTER_GHOST(map,
803 (int)g->position[X] + 1,
804 (int)g->position[Z]))
805 return DIRECTION_RIGHT;
808 return DIRECTION_DOWN;
810 } else {
811 for(c = gy + 1; c < py; c++)
812 if(!MAP_CAN_ENTER_GHOST(map, px, c))
813 break;
815 if(c == py)
817 /* jogador acima */
818 *r_dist = dist;
820 if(game->players[player_no].pill_time > 0.0)
822 *r_dist = 1;
824 if(MAP_CAN_ENTER_GHOST(map,
825 (int)g->position[X],
826 (int)g->position[Z] - 1))
827 return DIRECTION_DOWN;
829 if(MAP_CAN_ENTER_GHOST(map,
830 (int)g->position[X] - 1,
831 (int)g->position[Z]))
832 return DIRECTION_LEFT;
834 if(MAP_CAN_ENTER_GHOST(map,
835 (int)g->position[X] + 1,
836 (int)g->position[Z]))
837 return DIRECTION_RIGHT;
840 return DIRECTION_UP;
846 return -1;
849 void ghost_new_direction(struct game *game, struct ghost *g)
851 int dir, dist;
852 struct map *map;
854 map = game->map;
856 /* XXX - eek! */
857 g->position[X] = (float)((int)g->position[X]) + 0.5;
858 g->position[Z] = (float)((int)g->position[Z]) + 0.5;
860 if(g->state == GHOST_STATE_DEAD)
862 g->time = 1.0 / g->speed;
864 switch(MAP(map, (int)g->position[X], (int)g->position[Z]).ghost_dir)
866 case DIRECTION_UP:
867 g->direction = DIRECTION_UP;
868 break;
870 case DIRECTION_DOWN:
871 g->direction = DIRECTION_DOWN;
872 break;
874 case DIRECTION_LEFT:
875 g->direction = DIRECTION_LEFT;
876 break;
878 case DIRECTION_RIGHT:
879 g->direction = DIRECTION_RIGHT;
880 break;
882 default:
883 if(MAP(map, (int)g->position[X], (int)g->position[Z]).flags & MAP_FLAG_GHOST_START_POSITION)
885 g->time = 0.0;
886 break;
889 printf("ghost_new_direction: que raio está um (hmm...) a fazer no mapa ? (%d %d)\n", (int)g->position[X], (int)g->position[Z]);
890 fflush(stdout);
891 SDL_Quit();
892 exit(1);
894 break;
897 return;
900 dir = ghost_check_visibility(game, g, &dist);
901 if(dir == -1)
903 /* não há jogadores visíveis, direcção aleatória */
904 g->direction = rand() % DIRECTION_COUNT;
905 g->time = (float)(rand() % 5) + 1.0;
906 } else {
907 g->direction = dir;
908 g->time = dist / g->speed;