1 /*
2   FOP: Fight or Perish
3 
4   Based on Jack Pavelich's "Dandy" ('Thesis of Terror')
5   and Atari Games' "Gauntlet"
6 
7   by Bill Kendrick <bill@newbreedsoftware.com>
8   http://www.newbreedsoftware.com/fop/
9 
10   February 25, 2009 - March 15, 2009
11 */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <math.h>
16 #include "SDL.h"
17 #include "SDL_image.h"
18 
19 #define SCREENW 640
20 #define SCREENH 480
21 
22 #define TILEW 32
23 #define TILEH 32
24 
25 #define MAXHEALTH 1200
26 
27 #define FPS 10
28 
29 typedef struct arrow_s {
30   int alive;
31   int x, y, xm, ym;
32 } arrow_t;
33 
34 #define MAXARROWS 3
35 
36 typedef struct map_s {
37   int w, h;
38   char * m;
39   int startx[4], starty[4];
40 } map_t;
41 
42 #define setmap(M, y, x, c) (M)->m[((y) * (M)->w) + (x)] = (c)
43 #define getmap(M, y, x) ((M)->m[((y) * (M)->w) + (x)])
44 
45 /* Characters:
46   1 - Fast with weak weapon
47   2 - Slow but strong
48   3 - Evenly balanced
49   4 - Weak but with powerful weapon
50 */
51 
52 int char_speeds[4] = {3, 1, 2, 2};
53 int char_healths[4] = {2, 3, 2, 1};
54 int char_weapons[4] = {1, 2, 2, 3};
55 Uint32 char_colors[4] = {0xffff00, 0xff00ff, 0x00ff00, 0x0000ff};
56 
57 enum {
58   UPGRADE_NONE = -1,
59   UPGRADE_HEALTH,
60   UPGRADE_WEAPON,
61   UPGRADE_SPEED,
62   NUM_UPGRADES
63 };
64 #define UPGRADE_INIT_TIME 200
65 
66 enum {
67   KEY_DOWN,
68   KEY_UP,
69   KEY_LEFT,
70   KEY_RIGHT,
71   KEY_FIRE,
72   KEY_BOMB,
73   NUM_KEYS
74 };
75 
76 int key_codes[4][NUM_KEYS];
77 
78 typedef struct stick_code_s {
79   int joy, axis, axis_sign, btn;
80 } stick_code_t;
81 
82 stick_code_t stick_codes[4][NUM_KEYS];
83 
84 #define speed_check(toggle, speed) (((toggle) % (4 - (((speed) <= 3 ? (speed) : 3)))) == 0)
85 
86 SDL_Surface * screen;
87 SDL_Surface * img_title, * img_playerselect, * img_stats, * img_numbers;
88 SDL_Surface * img_generators, * img_collectibles, * img_blockers, * img_badguys;
89 SDL_Surface * img_player[4], * img_arrows[4];
90 int num_players;
91 int plytype[4];
92 int level;
93 int plyx[4], plyy[4], score[4], oldplyx[4], oldplyy[4], health[4], maxhealth[4], bombs[4], keys[4], oldhealth[4], oldscore[4], hurting[4], plymovetoggle[4], upgrade[4], upgradetime[4], exited[4];
94 
95 enum {
96   MENU_START,
97   MENU_OPTIONS,
98   MENU_QUIT
99 };
100 
101 enum {
102   STARTGAME_START,
103   STARTGAME_BACK,
104   STARTGAME_QUIT
105 };
106 
107 enum {
108   GAME_NEXTLEVEL,
109   GAME_FINISHED,
110   GAME_BACK,
111   GAME_QUIT
112 };
113 
114 #define sign(x) (((x) < 0) ? -1 : (((x) > 0) ? 1 : 0))
115 void setup(int argc, char * argv[]);
116 map_t * loadmap(int level);
117 void freemap(map_t * * map);
118 int safe(map_t * map, int x, int y, int toggle);
119 int bomb(map_t * map, int plyx, int plyy);
120 void opendoor(map_t * map, int x, int y);
121 SDL_Surface * loadimage(char * fname);
122 void drawimage(SDL_Surface * screen, SDL_Surface * sheet, int tilew, int tileh, int tilex, int tiley, int x, int y);
123 int closestplayer(map_t * map, int plyx[4], int plyy[4], int health[4], int x, int y);
124 int menu(void);
125 int startgame(void);
126 int game(int level);
127 
main(int argc,char * argv[])128 int main(int argc, char * argv[])
129 {
130   int option, quit, i;
131 
132   setup(argc, argv);
133 
134   /* Initialize start-game settings */
135 
136   level = 1;
137   num_players = 1;
138   for (i = 0; i < 4; i++)
139     plytype[i] = i;
140 
141   quit = 0;
142 
143   do
144   {
145     option = menu();
146 
147     if (option == MENU_QUIT)
148       quit = 1;
149     else if (option == MENU_START)
150     {
151       i = startgame();
152       if (i == STARTGAME_START)
153       {
154         do
155         {
156           i = game(level);
157           if (i == GAME_NEXTLEVEL)
158             level++;
159         }
160         while (i == GAME_NEXTLEVEL);
161         if (i == GAME_QUIT)
162           quit = 1;
163       }
164       else if (i == STARTGAME_QUIT)
165         quit = 1;
166     }
167   }
168   while (!quit);
169 
170   SDL_FreeSurface(img_title);
171   SDL_FreeSurface(img_playerselect);
172   SDL_FreeSurface(img_stats);
173   SDL_FreeSurface(img_numbers);
174   SDL_FreeSurface(img_generators);
175   SDL_FreeSurface(img_collectibles);
176   SDL_FreeSurface(img_blockers);
177   SDL_FreeSurface(img_badguys);
178   for (i = 0; i < 4; i++)
179   {
180     SDL_FreeSurface(img_player[i]);
181     SDL_FreeSurface(img_arrows[i]);
182   }
183 
184   SDL_Quit();
185 
186   return(0);
187 }
188 
game(int level)189 int game(int level)
190 {
191   int done, results;
192   int i, j, k, x, y, x2, y2, found, want, anyalive, allexited;
193   int scrollx, scrolly;
194   int finescrollx, finescrolly, finescrollwantx, finescrollwanty;
195   map_t * map;
196   char c, c2;
197   int got;
198   SDL_Rect dest;
199   SDL_Event event;
200   SDLKey key;
201   Uint32 timestamp, nowtimestamp;
202   int toggle;
203   int key_up[4], key_down[4], key_left[4], key_right[4], key_fire[4];
204   int oldkey_up[4], oldkey_down[4], oldkey_left[4], oldkey_right[4];
205   int plydirx[4], plydiry[4];
206   int minx, maxx, miny, maxy;
207   arrow_t arrows[4][MAXARROWS];
208   char str[16];
209 
210   results = -1;
211 
212   map = loadmap(level);
213   if (map == NULL)
214     return(GAME_FINISHED);
215 
216   scrollx = scrolly = 0;
217   finescrollx = finescrolly = finescrollwantx = finescrollwanty = 0;
218 
219   for (i = 0; i < 4; i++)
220   {
221     key_up[i] = key_down[i] = key_left[i] = key_right[i] = key_fire[i] = 0;
222 
223     exited[i] = 0;
224 
225     /* Start out facing down */
226     plydirx[i] = 2;
227     plydiry[i] = 3;
228     plyx[i] = map->startx[i];
229     plyy[i] = map->starty[i];
230 
231     setmap(map, plyy[i], plyx[i], ' ');
232     plymovetoggle[i] = 0;
233 
234     for (j = 0; j < MAXARROWS; j++)
235       arrows[i][j].alive = 0;
236   }
237 
238   done = 0;
239   toggle = 0;
240 
241 
242   /* Main loop: */
243 
244   do
245   {
246     timestamp = SDL_GetTicks();
247     toggle++;
248 
249     /* Determine center of all live player's positions, to center scrolling: */
250 
251     maxx = 0;
252     maxy = 0;
253     minx = map->w;
254     miny = map->h;
255     j = 0;
256     for (i = 0; i < 4; i++)
257     {
258       if (health[i] > 0 && exited[i] == 0)
259       {
260         if (plyx[i] < minx)
261           minx = plyx[i];
262         if (plyx[i] > maxx)
263           maxx = plyx[i];
264 
265         if (plyy[i] < miny)
266           miny = plyy[i];
267         if (plyy[i] > maxy)
268           maxy = plyy[i];
269 
270         j++;
271       }
272     }
273     if (j > 0)
274     {
275       scrollx = ((maxx - minx) / 2) + minx;
276       scrolly = ((maxy - miny) / 2) + miny;
277     }
278 
279     if (scrollx < (SCREENW / TILEW) / 2)
280       scrollx = (SCREENW / TILEW) / 2;
281     else if (scrollx > map->w - (SCREENW / TILEW) / 2)
282       scrollx = map->w - (SCREENW / TILEW) / 2;
283 
284     if (scrolly < (SCREENH / TILEH) / 2)
285       scrolly = (SCREENH / TILEH) / 2;
286     else if (scrolly > map->h - (SCREENH / TILEH) / 2)
287       scrolly = map->h - (SCREENH / TILEH) / 2;
288 
289     finescrollwantx = scrollx * TILEW;
290     finescrollwanty = scrolly * TILEH;
291 
292     if (finescrollx != finescrollwantx)
293     {
294       finescrollx += ((finescrollwantx - finescrollx) / (TILEW / 4)) + sign(finescrollwantx - finescrollx);
295     }
296     if (finescrolly != finescrollwanty)
297     {
298       finescrolly += ((finescrollwanty - finescrolly) / (TILEH / 4)) + sign(finescrollwanty - finescrolly);
299     }
300 
301     for (i = 0; i < 4; i++)
302     {
303       /* Temporarily keep track of old state for comparison or rollback: */
304 
305       oldhealth[i] = health[i];
306 
307       oldplyx[i] = plyx[i];
308       oldplyy[i] = plyy[i];
309 
310       oldkey_up[i] = key_up[i];
311       oldkey_down[i] = key_down[i];
312       oldkey_left[i] = key_left[i];
313       oldkey_right[i] = key_right[i];
314 
315       oldscore[i] = score[i];
316     }
317 
318 
319     /* Handle events: */
320 
321     while (SDL_PollEvent(&event) > 0)
322     {
323       if (event.type == SDL_QUIT)
324       {
325         done = 1;
326         results = GAME_QUIT;
327       }
328       else if (event.type == SDL_KEYDOWN)
329       {
330         key = event.key.keysym.sym;
331         if (key == SDLK_ESCAPE)
332         {
333           done = 1;
334           results = GAME_BACK;
335         }
336         else
337         {
338           for (i = 0; i < 4; i++)
339           {
340             if (key == key_codes[i][KEY_DOWN])
341               key_down[i] = 1;
342             else if (key == key_codes[i][KEY_UP])
343               key_up[i] = 1;
344             else if (key == key_codes[i][KEY_LEFT])
345               key_left[i] = 1;
346             else if (key == key_codes[i][KEY_RIGHT])
347               key_right[i] = 1;
348             else if (key == key_codes[i][KEY_FIRE])
349               key_fire[i] = 1;
350             else if (key == key_codes[i][KEY_BOMB])
351             {
352               if (bombs[i] > 0 && health[i] > 0 && exited[i] == 0)
353               {
354                 score[i] += bomb(map, scrollx, scrolly);
355                 bombs[i]--;
356               }
357             }
358           }
359         }
360       }
361       else if (event.type == SDL_KEYUP)
362       {
363         key = event.key.keysym.sym;
364         for (i = 0; i < 4; i++)
365         {
366           if (key == key_codes[i][KEY_DOWN])
367             key_down[i] = 0;
368           else if (key == key_codes[i][KEY_UP])
369             key_up[i] = 0;
370           else if (key == key_codes[i][KEY_LEFT])
371             key_left[i] = 0;
372           else if (key == key_codes[i][KEY_RIGHT])
373             key_right[i] = 0;
374           else if (key == key_codes[i][KEY_FIRE])
375             key_fire[i] = 0;
376         }
377       }
378       else if (event.type == SDL_JOYAXISMOTION)
379       {
380         for (i = 0; i < 4; i++)
381         {
382           for (j = 0; j < NUM_KEYS; j++)
383           {
384             if (event.jaxis.which == stick_codes[i][j].joy &&
385                 event.jaxis.axis == stick_codes[i][j].axis)
386             {
387               if ((event.jaxis.value > 0 && stick_codes[i][j].axis_sign > 0) ||
388                   (event.jaxis.value < 0 && stick_codes[i][j].axis_sign < 0))
389               {
390                 if (j == KEY_UP)
391                 {
392                   key_up[i] = 1;
393                   key_down[i] = 0;
394                 }
395                 else if (j == KEY_DOWN)
396                 {
397                   key_down[i] = 1;
398                   key_up[i] = 0;
399                 }
400                 else if (j == KEY_LEFT)
401                 {
402                   key_left[i] = 1;
403                   key_right[i] = 0;
404                 }
405                 else if (j == KEY_RIGHT)
406                 {
407                   key_right[i] = 1;
408                   key_left[i] = 0;
409                 }
410               }
411               else if (event.jaxis.value == 0)
412               {
413                 if (j == KEY_UP)
414                   key_up[i] = 0;
415                 else if (j == KEY_DOWN)
416                   key_down[i] = 0;
417                 else if (j == KEY_LEFT)
418                   key_left[i] = 0;
419                 else if (j == KEY_RIGHT)
420                   key_right[i] = 0;
421               }
422             }
423           }
424         }
425       }
426       else if (event.type == SDL_JOYBUTTONDOWN)
427       {
428         for (i = 0; i < 4; i++)
429         {
430           for (j = 0; j < NUM_KEYS; j++)
431           {
432             if (event.jbutton.which == stick_codes[i][j].joy &&
433                 event.jbutton.button == stick_codes[i][j].btn)
434             {
435               if (j == KEY_UP)
436                 key_up[i] = 1;
437               else if (j == KEY_DOWN)
438                 key_down[i] = 1;
439               else if (j == KEY_LEFT)
440                 key_left[i] = 1;
441               else if (j == KEY_RIGHT)
442                 key_right[i] = 1;
443               else if (j == KEY_FIRE)
444                 key_fire[i] = 1;
445               else if (j == KEY_BOMB)
446               {
447                 if (bombs[i] > 0 && health[i] > 0 && exited[i] == 0)
448                 {
449                   score[i] += bomb(map, scrollx, scrolly);
450                   bombs[i]--;
451                 }
452               }
453             }
454           }
455         }
456       }
457       else if (event.type == SDL_JOYBUTTONUP)
458       {
459         for (i = 0; i < 4; i++)
460         {
461           for (j = 0; j < NUM_KEYS; j++)
462           {
463             if (event.jbutton.which == stick_codes[i][j].joy &&
464                 event.jbutton.button == stick_codes[i][j].btn)
465             {
466               if (j == KEY_UP)
467                 key_up[i] = 0;
468               else if (j == KEY_DOWN)
469                 key_down[i] = 0;
470               else if (j == KEY_LEFT)
471                 key_left[i] = 0;
472               else if (j == KEY_RIGHT)
473                 key_right[i] = 0;
474               else if (j == KEY_FIRE)
475                 key_fire[i] = 0;
476             }
477           }
478         }
479       }
480     }
481 
482     /* Based on keys, which direction is the player facing? */
483 
484     for (i = 0; i < 4; i++)
485     {
486       if (key_up[i])
487         plydiry[i] = 1;
488       else if (key_down[i])
489         plydiry[i] = 3;
490       else if (key_right[i] || key_left[i])
491         plydiry[i] = 2;
492 
493       if (key_left[i])
494         plydirx[i] = 1;
495       else if (key_right[i])
496         plydirx[i] = 3;
497       else if (key_up[i] || key_down[i])
498         plydirx[i] = 2;
499 
500       if (speed_check(toggle, char_speeds[plytype[i]] + (upgrade[i] == UPGRADE_SPEED ? 1 : 0)))
501         plymovetoggle[i] = !plymovetoggle[i];
502 
503       got = -1;
504 
505       if (health[i] > 0 && exited[i] == 0)
506       {
507         if (key_fire[i])
508         {
509           /* If pressing [Fire], and a direction, try to shoot: */
510 
511           found = -1;
512           if ((key_up[i] && !oldkey_up[i]) ||
513               (key_down[i] && !oldkey_down[i]) ||
514               (key_left[i] && !oldkey_left[i]) ||
515               (key_right[i] && !oldkey_right[i]))
516           {
517             for (j = 0; j < MAXARROWS && found == -1; j++)
518             {
519               if (arrows[i][j].alive == 0)
520                 found = j;
521             }
522           }
523 
524           if (found != -1)
525           {
526             /* Add an arrow: */
527 
528             arrows[i][found].alive = 1;
529             arrows[i][found].x = plyx[i];
530             arrows[i][found].y = plyy[i];
531 
532             if (key_up[i])
533               arrows[i][found].ym = -1;
534             else if (key_down[i])
535               arrows[i][found].ym = 1;
536             else
537               arrows[i][found].ym = 0;
538 
539             if (key_left[i])
540               arrows[i][found].xm = -1;
541             else if (key_right[i])
542               arrows[i][found].xm = 1;
543             else
544               arrows[i][found].xm = 0;
545 
546             /* Directional handicap for weak weapons: */
547             if (char_weapons[plytype[i]] == 1 && upgrade[i] != UPGRADE_WEAPON)
548             {
549               if (arrows[i][found].xm != 0 && arrows[i][found].ym != 0)
550               {
551                 if (toggle % 2)
552                   arrows[i][found].xm = 0;
553                 else
554                   arrows[i][found].xm = 0;
555               }
556             }
557 
558             /* FIXME: Sound effect */
559           }
560         }
561         else if (speed_check(toggle, char_speeds[plytype[i]] + (upgrade[i] == UPGRADE_SPEED ? 1 : 0)))
562         {
563           /* Not pressing [Fire]; move the player, if they're pressing any arrow key(s): */
564 
565           /* Note: We cannot move diagonally in a single frame, so we use 'toggle % 2'
566              determine which direction to go if the user is pressing two arrow keys at once. */
567 
568           if (key_up[i] && plyy[i] > scrolly - ((SCREENH / TILEH) / 2))
569           {
570             if ((key_left[i] == 0 && key_right[i] == 0) || plymovetoggle[i] == 0)
571             {
572               got = safe(map, plyx[i], plyy[i] - 1, toggle);
573               if (got != -1)
574                 plyy[i]--;
575             }
576           }
577           else if (key_down[i] && plyy[i] < scrolly + ((SCREENH / TILEH) / 2))
578           {
579             if ((key_left[i] == 0 && key_right[i] == 0) || plymovetoggle[i] == 0)
580             {
581               got = safe(map, plyx[i], plyy[i] + 1, toggle);
582               if (got != -1)
583                 plyy[i]++;
584             }
585           }
586 
587           if (key_left[i] && plyx[i] > scrollx - ((SCREENW / TILEW) / 2))
588           {
589             if ((key_up[i] == 0 && key_down[i] == 0) || plymovetoggle[i] == 1)
590             {
591               got = safe(map, plyx[i] - 1, plyy[i], toggle);
592               if (got != -1)
593                 plyx[i]--;
594             }
595           }
596           else if (key_right[i] && plyx[i] < scrollx + ((SCREENW / TILEW) / 2) - 1)
597           {
598             if ((key_up[i] == 0 && key_down[i] == 0) || plymovetoggle[i] == 1)
599             {
600               got = safe(map, plyx[i] + 1, plyy[i], toggle);
601               if (got != -1)
602                 plyx[i]++;
603             }
604           }
605         }
606       }
607 
608       /* If they player bumped into something that we can act upon... */
609 
610       if (got == '$')
611       {
612         /* Money */
613 
614         setmap(map, plyy[i], plyx[i], ' ');
615         score[i] += 10;
616         /* FIXME: Sound effect */
617       }
618       else if (got == '?')
619       {
620         /* Random bonus */
621 
622         setmap(map, plyy[i], plyx[i], ' ');
623         upgrade[i] = rand() % NUM_UPGRADES;
624         upgradetime[i] = UPGRADE_INIT_TIME;
625         score[i] += 10;
626         /* FIXME: Sound effect */
627       }
628       else if (got == 'b')
629       {
630         /* A bomb */
631 
632         setmap(map, plyy[i], plyx[i], ' ');
633         bombs[i]++;
634         /* FIXME: Sound effect */
635       }
636       else if (got == 'k')
637       {
638         /* A key */
639 
640         setmap(map, plyy[i], plyx[i], ' ');
641         keys[i]++;
642         /* FIXME: Sound effect */
643       }
644       else if (got == '=')
645       {
646         /* A door */
647 
648         if (keys[i] > 0)
649         {
650           /* We have a key; remove the door: */
651 
652           keys[i]--;
653           opendoor(map, plyx[i], plyy[i]);
654           /* FIXME: Sound effect */
655         }
656         else
657         {
658           /* No keys; user cannot go through the door! */
659 
660           plyx[i] = oldplyx[i];
661           plyy[i] = oldplyy[i];
662           /* FIXME: Sound effect */
663         }
664       }
665       else if (got == '%')
666       {
667         exited[i] = 1;
668       }
669       else if (got == 'F')
670       {
671         /* Food */
672 
673         setmap(map, plyy[i], plyx[i], ' ');
674         health[i] += (MAXHEALTH / 30);
675         if (health[i] > maxhealth[i])
676           health[i] = maxhealth[i];
677         /* FIXME: Sound effect */
678       }
679       else if (got == 'C' || got == 'B' || got == '3' || got == '2')
680       {
681         /* A bad guy or generator, higher than level 1:
682            Reduce the bad guy or generator, bounce player back, reduce player's health */
683 
684         setmap(map, plyy[i], plyx[i], getmap(map, plyy[i], plyy[i]) - 1);
685         plyx[i] = oldplyx[i];
686         plyy[i] = oldplyy[i];
687         if (upgrade[i] != UPGRADE_HEALTH)
688           health[i] -= 10;
689         score[i] += 10;
690         /* FIXME: Sound effect */
691       }
692       else if (got == 'A' || got == '1')
693       {
694         /* A bad guy or generator, at level 1:
695            Remove the bad guy or generator, reduce player's health */
696 
697         setmap(map, plyy[i], plyx[i], ' ');
698         if (upgrade[i] != UPGRADE_HEALTH)
699           health[i] -= 10;
700         score[i] += 10;
701         /* FIXME: Sound effect */
702       }
703       else if (got == 'w' || got == 'x' || got == 'y' || got == 'z')
704       {
705         /* A dead player's treasure */
706 
707         score[i] += 100;
708         keys[i] += keys[got - 'w'];
709         bombs[i] += bombs[got - 'w'];
710         setmap(map, plyy[i], plyx[i], ' ');
711         keys[got - 'w'] = 0;
712         bombs[got - 'w'] = 0;
713       }
714 
715 
716       /* Handle arrows: */
717       /* FIXME: These can be handled as part of the map's cellular automata -bjk 2009.02.26 */
718 
719       for (j = 0; j < MAXARROWS; j++)
720       {
721         /* Since bad guys can move into the arrow's position as the arrow
722            is about to go by, we check whether the arrow hit anything before and after
723            moving it. */
724 
725         for (k = 0; k < 2; k++)
726         {
727           if (k == 1 && arrows[i][j].alive)
728           {
729             /* Arrow exists;
730                If we've already checked for bad guys at the original position,
731                move it (and see if it's still on the map) */
732 
733             arrows[i][j].x += arrows[i][j].xm;
734             arrows[i][j].y += arrows[i][j].ym;
735             if (arrows[i][j].y < 0 || arrows[i][j].y >= map->h || arrows[i][j].x < 0 || arrows[i][j].x >= map->w ||
736                 arrows[i][j].y < scrolly - ((SCREENH / TILEH) / 2) ||
737                 arrows[i][j].y > scrolly + ((SCREENH / TILEH) / 2) ||
738                 arrows[i][j].x < scrollx - ((SCREENW / TILEW) / 2) ||
739                 arrows[i][j].x > scrollx + ((SCREENW / TILEW) / 2))
740               arrows[i][j].alive = 0;
741           }
742 
743           if (arrows[i][j].alive)
744           {
745             /* Arrow still exists; see what it hit into */
746 
747             c = getmap(map, arrows[i][j].y, arrows[i][j].x);
748             if (c == 'B' || c == 'C' || c == '2' || c == '3')
749             {
750               /* A bad guy or generator, higher than level 1:
751                  Reduce the bad guy or generator */
752 
753               setmap(map, arrows[i][j].y, arrows[i][j].x, (c - 1));
754               score[i] += 10;
755               /* FIXME: Sound effect */
756             }
757             else if (c == 'A' || c == '1')
758             {
759               /* A bad guy or generator, at level 1:
760                  Remove the bad guy or generator */
761 
762               setmap(map, arrows[i][j].y, arrows[i][j].x, ' ');
763               score[i] += 10;
764               /* FIXME: Sound effect */
765 
766               /* Some players' arrows keep going after a kill */
767               if (char_weapons[plytype[i]] + (upgrade[i] == UPGRADE_WEAPON ? 1 : 0) == 3)
768                 c = ' ';
769             }
770             else if (c == 'b')
771             {
772               /* A bomb; activate it now! */
773 
774               setmap(map, arrows[i][j].y, arrows[i][j].x, ' ');
775               score[i] += (bomb(map, scrollx, scrolly) / 2);
776               /* FIXME: Sound effect */
777             }
778             else if (c == 'F')
779             {
780               /* Food; remove it (don't shoot food!!!) */
781 
782               setmap(map, arrows[i][j].y, arrows[i][j].x, ' ');
783               /* FIXME: Sound effect */
784             }
785 
786 
787             /* Arrows go away once they hit _anything_ */
788 
789             if (c != ' ')
790               arrows[i][j].alive = 0;
791           }
792         }
793       }
794     }
795 
796 
797     /* Handle objects on the map, as a cellular automata: */
798 
799     for (y = 0; y < map->h; y++)
800     {
801       for (x = 0; x < map->w; x++)
802       {
803         want = closestplayer(map, plyx, plyy, health, x, y);
804 
805         /* For those objects interested, which way do we go to
806            head towards the player */
807 
808         if (plyy[want] > y)
809           y2 = y + 1;
810         else if (plyy[want] < y)
811           y2 = y - 1;
812         else
813           y2 = y;
814 
815         if (plyx[want] > x)
816           x2 = x + 1;
817         else if (plyx[want] < x)
818           x2 = x - 1;
819         else
820           x2 = x;
821 
822         /* Note: We cannot move diagonally in a single frame,
823            so use (toggle % 2) to determine which way to go
824            (up/down, or left/right), if we're headed diagonally */
825 
826         if (y2 != y && x2 != x)
827         {
828           if (toggle % 2 == 0)
829             x2 = x;
830           else
831             y2 = y;
832         }
833 
834         /* What's at our current position (and the one we want to
835            go to, if we're a bad-guy) */
836 
837         c = getmap(map, y, x);
838         c2 = getmap(map, y2, x2);
839 
840         /* FIXME: Here, we could decide bad guys move semi-randomly
841            towards the player they're chasing, if they cannot move into
842            an empty space */
843 
844         if (c == 'A' || c == 'B' || c == 'C')
845         {
846           /* Bad guys (who haven't moved already) */
847 
848           /* See if we're walking into a player */
849 
850           k = -1;
851           for (i = 0; i < 4; i++)
852           {
853             if (health[i] > 0 && exited[i] == 0)
854             {
855               if (x2 == plyx[i] && y2 == plyy[i])
856               {
857                 k = i;
858               }
859             }
860           }
861 
862           if (k != -1)
863           {
864             /* If we're adjacent to the player, try to do a melee attack */
865 
866             if (key_fire[k] == 0)
867             {
868               /* If player is not trying to attack us,
869                  try to attack them.
870                  (Our chance of succeeding in the melee attack depends
871                  on what level we are) */
872 
873               if (upgrade[k] != UPGRADE_HEALTH)
874               {
875                 if (c == 'A' && (rand() % 10) < 3)
876                   health[k] -= 10;
877                 else if (c == 'B' && (rand() % 10) < 5)
878                   health[k] -= 10;
879                 else if (c == 'C' && (rand() % 10) < 7)
880                   health[k] -= 10;
881 
882                 if (health[k] < oldhealth[k])
883                 {
884                   /* FIXME: Sound effect */
885                 }
886               }
887             }
888           }
889           else
890           {
891             /* Head toward's the player we're chasing */
892             /* Note: We place a modified bad guy onto the map, so that
893                they can't move right or down more than once per frame,
894                as we traverse the map.  They get replaced with normal
895                bad guys after the entire map has been processed */
896 
897             if (c2 == ' ' && (rand() % 10) < 3)
898             {
899               setmap(map, y, x, ' ');
900               setmap(map, y2, x2, (c - 'A' + 'X'));
901             }
902           }
903         }
904         else if (c == '1' || c == '2' || c == '3')
905         {
906           /* Generators */
907 
908           /* Pick a random adjacent spot */
909           x2 = x + (rand() % 3) - 1;
910           y2 = y + (rand() % 3) - 1;
911 
912           /* If the spot is empty, generate a bad guy of
913              our level into the spot (but not one who
914              can move this frame...) */
915 
916           if (getmap(map, y2, x2) == ' ')
917             setmap(map, y2, x2, (c - '1' + 'X'));
918         }
919       }
920     }
921 
922     /* Convert modified ('has already moved this frame') bad guys
923        back to normal */
924     for (y = 0; y < map->h; y++)
925     {
926       for (x = 0; x < map->w; x++)
927       {
928         c = getmap(map, y, x);
929         if (c == 'X' || c == 'Y' || c == 'Z')
930           setmap(map, y, x, (c - 'X' + 'A'));
931       }
932     }
933 
934 
935     /* Handle players' heath- and timer-related things: */
936 
937     for (i = 0; i < 4; i++)
938     {
939       /* See if a player has earned enough points for a health upgrade */
940 
941       if (health[i] > 0 && (oldscore[i] / 1000) < (score[i] / 1000))
942       {
943         health[i] = maxhealth[i];
944         maxhealth[i] += (MAXHEALTH / 4);
945         if (maxhealth[i] > MAXHEALTH)
946           maxhealth[i] = MAXHEALTH;
947         /* FIXME: Sound effect */
948       }
949 
950 
951       /* Constantly reduce player health: */
952 
953       if (toggle % 2 == 0 && exited[i] == 0)
954         health[i]--;
955 
956 
957       /* Are we still alive?! */
958 
959       if (health[i] <= 0)
960       {
961         health[i] = 0;
962         if (oldhealth[i] > 0)
963         {
964           setmap(map, plyy[i], plyx[i], ('w' + i));
965           /* FIXME: Sound effect */
966         }
967       }
968 
969       if (health[i] < oldhealth[i] - 1)
970         hurting[i] = 1;
971       else
972         hurting[i] = 0;
973 
974 
975       /* Reduce upgrade time: */
976       if (upgradetime[i] > 0)
977       {
978         upgradetime[i]--;
979         if (upgradetime[i] <= 0)
980           upgrade[i] = UPGRADE_NONE;
981       }
982     }
983 
984     /* Determine if anyone is alive, and who has exited */
985 
986     anyalive = 0;
987     allexited = 1;
988     for (i = 0; i < num_players; i++)
989     {
990       anyalive += health[i];
991       if (health[i] > 0 && exited[i] == 0)
992         allexited = 0;
993     }
994 
995     if (allexited)
996     {
997       done = 1;
998       results = GAME_NEXTLEVEL;
999     }
1000 
1001 
1002     /* Clear the screen */
1003 
1004     if (anyalive > 0)
1005       SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
1006     else
1007       SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 32, 0, 0));
1008 
1009 
1010     /* Draw the objects we can see from here */
1011 
1012     for (y = (finescrolly - (SCREENH / 2)) / TILEH; y <= (finescrolly + (SCREENH / 2)) / TILEH; y++)
1013     {
1014       for (x = (finescrollx - (SCREENW / 2)) / TILEW; x <= (finescrollx + (SCREENW / 2)) / TILEW; x++)
1015       {
1016         if (y >= 0 && y < map->h && x >= 0 && x < map->w)
1017         {
1018           /* Draw map objects: */
1019 
1020           c = getmap(map, y, x);
1021           //dest.x = (x - scrollx + (SCREENW / TILEW) / 2) * TILEW;
1022           //dest.y = (y - scrolly + (SCREENH / TILEH) / 2) * TILEH;
1023           dest.x = ((x + (SCREENW / TILEW) / 2) * TILEW) - finescrollx;
1024           dest.y = ((y + (SCREENH / TILEH) / 2) * TILEH) - finescrolly;
1025           dest.w = TILEW;
1026           dest.h = TILEH;
1027 
1028           if (c == '#')
1029             drawimage(screen, img_blockers, 2, 1, 1, 1, dest.x, dest.y);
1030           else if (c == '$')
1031             drawimage(screen, img_collectibles, 5, 1, 4, 1, dest.x, dest.y);
1032           else if (c == '?')
1033             drawimage(screen, img_collectibles, 5, 1, 5, 1, dest.x, dest.y);
1034           else if (c == '=')
1035             drawimage(screen, img_blockers, 2, 1, 2, 1, dest.x, dest.y);
1036           else if (c == '%')
1037             SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format,
1038                                                    rand() % 255,
1039                                                    rand() % 255,
1040                                                    rand() % 255));
1041           else if (c == 'F')
1042             drawimage(screen, img_collectibles, 5, 1, 1, 1, dest.x, dest.y);
1043           else if (c == '1')
1044             drawimage(screen, img_generators, 3, 1, 1, 1, dest.x, dest.y);
1045           else if (c == '2')
1046             drawimage(screen, img_generators, 3, 1, 2, 1, dest.x, dest.y);
1047           else if (c == '3')
1048             drawimage(screen, img_generators, 3, 1, 3, 1, dest.x, dest.y);
1049           else if (c == 'A' || c == 'B' || c == 'C')
1050           {
1051             want = closestplayer(map, plyx, plyy, health, x, y);
1052             drawimage(screen, img_badguys, 6, 9,
1053               (plyx[want] < x ? 1 : (plyx[want] == x ? 2 : 3)) + (3 * (rand() % 2)),
1054               (plyy[want] < y ? 1 : (plyy[want] == y ? 2 : 3)) + (3 * (c - 'A')),
1055               dest.x, dest.y);
1056           }
1057           else if (c == 'k')
1058             drawimage(screen, img_collectibles, 5, 1, 2, 1, dest.x, dest.y);
1059           else if (c == 'b')
1060             drawimage(screen, img_collectibles, 5, 1, 3, 1, dest.x, dest.y);
1061           else if (c == 'w' || c == 'x' || c == 'y' || c == 'z')
1062             drawimage(screen, img_player[0], 6, 12,
1063               2, 2 + ((c - 'w') * 3),
1064               dest.x, dest.y);
1065 
1066           /* Draw the players */
1067 
1068           for (i = 0; i < 4; i++)
1069           {
1070             if (x == plyx[i] && y == plyy[i] && health[i] > 0 && exited[i] == 0)
1071             {
1072               /* Flash around player if they just got hurt */
1073 
1074               if (hurting[i])
1075                 SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 255, 255));
1076 
1077               drawimage(screen, img_player[plytype[i]], 6, 12,
1078                 plydirx[i] + (((key_up[i] || key_down[i] || key_left[i] || key_right[i]) && !key_fire[i]) ? (3 * (toggle % 2)) : 0),
1079                 plydiry[i] + (i * 3),
1080                 dest.x, dest.y);
1081             }
1082 
1083             /* Draw any arrows we can still see: */
1084 
1085             for (j = 0; j < MAXARROWS; j++)
1086             {
1087               if (arrows[i][j].alive && x == arrows[i][j].x && y == arrows[i][j].y)
1088               {
1089                 drawimage(screen, img_arrows[plytype[i]], 3, 3, 2 + arrows[i][j].xm, 2 + arrows[i][j].ym, dest.x, dest.y);
1090               }
1091             }
1092           }
1093         }
1094       }
1095     }
1096 
1097     for (i = 0; i < 4; i++)
1098     {
1099       /* FIXME: Better HUD display */
1100 
1101       if (health[i] > 0)
1102       {
1103         /* Draw our health meter */
1104 
1105         y = ((TILEH * 4) * i) + TILEH / 4;
1106 
1107         drawimage(screen, img_stats, 4, 1, 1, 1, 0, y);
1108 
1109         /* Max health */
1110 
1111         dest.x = img_stats->w / 4;
1112         dest.y = y;
1113         dest.w = (maxhealth[i] * (TILEW * 4)) / MAXHEALTH;
1114         dest.h = img_stats->h;
1115 
1116         SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format,
1117                                                  ((char_colors[i] & 0xff0000) >> 16) / 2,
1118                                                  ((char_colors[i] & 0x00ff00) >> 8) / 2,
1119                                                  ((char_colors[i] & 0x0000ff) / 2)));
1120 
1121         /* Current meter: */
1122 
1123         dest.x = img_stats->w / 4;
1124         dest.y = y;
1125         dest.w = (health[i] * (TILEW * 4)) / MAXHEALTH;
1126         dest.h = img_stats->h;
1127 
1128         /* Flash if it's low! */
1129 
1130         if (health[i] > MAXHEALTH / 10 || (toggle % 2) == 0)
1131           SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format,
1132                                                  (char_colors[i] & 0xff0000) >> 16,
1133                                                  (char_colors[i] & 0x00ff00) >> 8,
1134                                                  (char_colors[i] & 0x0000ff)));
1135         else
1136           SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 255, 0, 0));
1137 
1138 
1139         /* Draw our collected objects */
1140 
1141 
1142         /* Treasure */
1143 
1144         dest.x = img_stats->w / 4;
1145         dest.y = y + img_stats->h;
1146 
1147         drawimage(screen, img_stats, 4, 1, 4, 1, 0, dest.y);
1148 
1149         snprintf(str, sizeof(str), "%06d", score[i]);
1150         for (j = 0; j < strlen(str); j++)
1151         {
1152           x = (img_stats->w / 4) + (img_numbers->w / 10) * j;
1153           drawimage(screen, img_numbers, 10, 1, (str[j] - '0') + 1, 1, x, y + img_stats->h);
1154         }
1155 
1156         /* Keys */
1157 
1158         for (j = 0; j < keys[i]; j++)
1159         {
1160           dest.x = (j * TILEW);
1161           dest.y = y + img_stats->h * 2;
1162           drawimage(screen, img_collectibles, 5, 1, 2, 1, dest.x, dest.y);
1163         }
1164 
1165         /* Bombs */
1166 
1167         for (j = 0; j < bombs[i]; j++)
1168         {
1169           dest.x = (j * TILEW);
1170           dest.y = y + img_stats->h * 2 + TILEH;
1171           drawimage(screen, img_collectibles, 5, 1, 3, 1, dest.x, dest.y);
1172         }
1173 
1174         /* Any temporary upgrade */
1175         if (upgrade[i] != UPGRADE_NONE)
1176         {
1177           dest.x = 0;
1178           dest.y = y + img_stats->h * 2 + TILEH * 2;
1179           drawimage(screen, img_stats, 4, 1, upgrade[i] + 1, 1, dest.x, dest.y);
1180 
1181           dest.x = img_stats->w / 4;
1182           dest.w = (upgradetime[i] * (TILEW * 4)) / UPGRADE_INIT_TIME;
1183           dest.h = img_stats->h;
1184           SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0xff, 0xff, 0xff));
1185         }
1186       }
1187     }
1188 
1189 
1190     /* Update the screen, and keep framerate throttled: */
1191 
1192     SDL_Flip(screen);
1193 
1194     nowtimestamp = SDL_GetTicks();
1195     if (nowtimestamp - timestamp < (1000 / FPS))
1196       SDL_Delay(timestamp + (1000 / FPS) - nowtimestamp);
1197   }
1198   while (!done);
1199 
1200   freemap(&map);
1201 
1202   return(results);
1203 }
1204 
1205 
1206 /* Determine whether player can move into a particular spot */
1207 
safe(map_t * map,int x,int y,int toggle)1208 int safe(map_t * map, int x, int y, int toggle)
1209 {
1210   char c;
1211 
1212   if (x < 0 || y < 0 || x >= map->w || y >= map->h)
1213     return -1;
1214 
1215   c = getmap(map, y, x);
1216 
1217   if (c == '#')
1218   {
1219     /* Cannot walk through walls: */
1220     return -1;
1221   }
1222   else if (c == 'A' || c == 'B' || c == 'C' ||
1223            c == '1' || c == '2' || c == '3')
1224   {
1225     /* Can only melee attack bad guys occassionally */
1226     if ((toggle % 4) >= 2)
1227       return -1;
1228   }
1229 
1230   /* Otherwise, go ahead and try moving there */
1231   /* Note: We may be bounced back (e.g., if you try to walk
1232      through a door without any keys, or you're melee-attacking
1233      a bad guy, you'll get bounced back, even though we report
1234      it as being 'safe' here). */
1235 
1236   return (int) c;
1237 }
1238 
1239 
1240 /* Explode a bomb - hurt all bad things visible on the screen */
1241 
bomb(map_t * map,int plyx,int plyy)1242 int bomb(map_t * map, int plyx, int plyy)
1243 {
1244   int x, y;
1245   char c;
1246   int score;
1247 
1248   score = 0;
1249 
1250   for (y = plyy - (SCREENH / TILEH) / 2; y <= plyy + (SCREENH / TILEH) / 2; y++)
1251   {
1252     for (x = plyx - (SCREENW / TILEW) / 2; x <= plyx + (SCREENW / TILEW) / 2; x++)
1253     {
1254       if (y >= 0 && y < map->h && x >= 0 && x < map->w)
1255       {
1256         c = getmap(map, y, x);
1257         if (c == 'B' || c == 'C' || c == '2' || c == '3')
1258         {
1259           /* A bad guy or generator, higher than level 1:
1260              Reduce the bad guy or generator */
1261 
1262           setmap(map, y, x, c - 1);
1263           score += 10;
1264         }
1265         else if (c == 'A' || c == '1')
1266         {
1267           /* A bad guy or generator, at level 1:
1268              Remove the bad guy or generator */
1269 
1270           setmap(map, y, x, ' ');
1271           score += 10;
1272         }
1273       }
1274     }
1275   }
1276 
1277   return(score);
1278 }
1279 
1280 
1281 /* 'Open' a door, by removing all door pieces that touch.
1282    Note: a recursive function, but uses two x/y coordinates
1283    and does up/down/left/right adjacency tests to crawl
1284    along the door and remove it.  This means no more than
1285    two door pieces should touch on the map, or the door
1286    won't open right! */
1287 
opendoor(map_t * map,int x,int y)1288 void opendoor(map_t * map, int x, int y)
1289 {
1290   int x1, y1, x2, y2;
1291   char c1l, c1r, c1u, c1d;
1292   char c2l, c2r, c2u, c2d;
1293   int done;
1294 
1295   /* Start at the spot on the door that the player touched */
1296 
1297   x1 = x2 = x;
1298   y1 = y2 = y;
1299 
1300   do
1301   {
1302     done = 1;
1303 
1304     /* Look around x/y coordinate #1 */
1305 
1306     c1l = getmap(map, y1, x1 - 1);
1307     c1r = getmap(map, y1, x1 + 1);
1308     c1u = getmap(map, y1 - 1, x1);
1309     c1d = getmap(map, y1 + 1, x1);
1310 
1311 
1312     /* Remove door at x/y coordinates #1 and #2 */
1313 
1314     setmap(map, y1, x1, ' ');
1315     setmap(map, y2, x2, ' ');
1316 
1317     /* Crawl x/y coordinate #1 towards any door piece
1318        that's adjacent */
1319 
1320     if (c1l == '=')
1321     {
1322       done = 0;
1323       x1--;
1324     }
1325     else if (c1r == '=')
1326     {
1327       done = 0;
1328       x1++;
1329     }
1330     else if (c1u == '=')
1331     {
1332       done = 0;
1333       y1--;
1334     }
1335     else if (c1d == '=')
1336     {
1337       done = 0;
1338       y1++;
1339     }
1340 
1341     /* If we found a piece, remove it
1342        (so that x/y coordinate #2 doesn't see it) */
1343 
1344     if (!done)
1345       setmap(map, y1, x1, ' ');
1346 
1347 
1348     /* Look around x/y coordinate #1 */
1349 
1350     c2l = getmap(map, y2, x2 - 1);
1351     c2r = getmap(map, y2, x2 + 1);
1352     c2u = getmap(map, y2 - 1, x2);
1353     c2d = getmap(map, y2 + 1, x2);
1354 
1355     /* Crawl x/y coordinate #1 towards any door piece
1356        that's adjacent */
1357 
1358     if (c2l == '=')
1359     {
1360       done = 0;
1361       x2--;
1362     }
1363     else if (c2r == '=')
1364     {
1365       done = 0;
1366       x2++;
1367     }
1368     else if (c2u == '=')
1369     {
1370       done = 0;
1371       y2--;
1372     }
1373     else if (c2d == '=')
1374     {
1375       done = 0;
1376       y2++;
1377     }
1378 
1379     /* Note: The piece at x/y coordinate #2 will be removed
1380        when this loop repeats. */
1381   }
1382   while (!done);
1383 
1384   /* Note: Loop stops repeating once all adjacent door pieces have
1385      been found. */
1386 }
1387 
loadimage(char * fname)1388 SDL_Surface * loadimage(char * fname)
1389 {
1390   char fullfname[1024];
1391   SDL_Surface * tmp1, * tmp2;
1392 
1393   snprintf(fullfname, sizeof(fullfname), "/usr/local/share/fightorperish/data/images/%s", fname);
1394   tmp1 = IMG_Load(fullfname);
1395   if (tmp1 == NULL)
1396   {
1397     fprintf(stderr, "Couldn't load %s: %s\n", fullfname, SDL_GetError());
1398     SDL_Quit();
1399     exit(1);
1400   }
1401 
1402   tmp2 = SDL_DisplayFormatAlpha(tmp1);
1403   SDL_FreeSurface(tmp1);
1404   if (tmp2 == NULL)
1405   {
1406     fprintf(stderr, "Couldn't convert %s: %s\n", fullfname, SDL_GetError());
1407     SDL_Quit();
1408     exit(1);
1409   }
1410 
1411   return(tmp2);
1412 }
1413 
drawimage(SDL_Surface * screen,SDL_Surface * sheet,int tilew,int tileh,int tilex,int tiley,int x,int y)1414 void drawimage(SDL_Surface * screen, SDL_Surface * sheet, int tilew, int tileh, int tilex, int tiley, int x, int y)
1415 {
1416   SDL_Rect src, dest;
1417 
1418   src.x = (sheet->w / tilew) * (tilex - 1);
1419   src.y = (sheet->h / tileh) * (tiley - 1);
1420   src.w = (sheet->w / tilew);
1421   src.h = (sheet->h / tileh);
1422 
1423   dest.x = x;
1424   dest.y = y;
1425 
1426   SDL_BlitSurface(sheet, &src, screen, &dest);
1427 }
1428 
closestplayer(map_t * map,int plyx[4],int plyy[4],int health[4],int x,int y)1429 int closestplayer(map_t * map, int plyx[4], int plyy[4], int health[4], int x, int y)
1430 {
1431   int i, want, dx, dy, thisdist, dist;
1432 
1433   /* Default (in case everyone's dead) */
1434 
1435   want = ((x + y) % 4);
1436   dist = map->w * map->h;
1437 
1438   for (i = 0; i < 4; i++)
1439   {
1440     if (health[i] > 0)
1441     {
1442       dx = plyx[i] - x;
1443       dy = plyy[i] - y;
1444       thisdist = sqrt((dx * dx) + (dy * dy));
1445       if (thisdist < dist)
1446       {
1447         want = i;
1448         dist = thisdist;
1449       }
1450     }
1451   }
1452 
1453   return(want);
1454 }
1455 
1456 /* Free a map */
1457 
freemap(map_t ** map)1458 void freemap(map_t * * map)
1459 {
1460   if ((*map) != NULL)
1461   {
1462     if ((*map)->m != NULL)
1463       free((*map)->m);
1464 
1465     free((*map));
1466     *map = NULL;
1467   }
1468 }
1469 
1470 
1471 /* Load a map */
1472 
loadmap(int level)1473 map_t * loadmap(int level)
1474 {
1475   char fname[1024], line[1024];
1476   FILE * fi;
1477   int x, y, i, j;
1478   map_t * map;
1479   void * dummy;
1480 
1481   snprintf(fname, sizeof(fname), "/usr/local/share/fightorperish/data/maps/%d.txt", level);
1482   fi = fopen(fname, "r");
1483   if (fi == NULL)
1484     return(NULL);
1485 
1486   map = (map_t *) malloc(sizeof(map_t));
1487 
1488   dummy = fgets(line, 1024, fi);
1489   map->w = atoi(line);
1490   dummy = fgets(line, 1024, fi);
1491   map->h = atoi(line);
1492 
1493   for (i = 0; i < 4; i++)
1494   {
1495     map->startx[i] = i + 1;
1496     map->starty[i] = 1;
1497   }
1498 
1499   map->m = (char *) malloc(sizeof(char) * (map->w * map->h));
1500 
1501   for (y = 0; y < map->h; y++)
1502   {
1503     dummy = fgets(line, 1024, fi);
1504 
1505     for (x = 0; x < map->w; x++)
1506     {
1507       map->m[(y * map->w) + x] = line[x];
1508 
1509       if (line[x] >= '5' && line[x] <= '8')
1510       {
1511         j = line[x] - '5';
1512         line[x] = ' ';
1513 
1514         map->startx[j] = x;
1515         map->starty[j] = y;
1516       }
1517     }
1518   }
1519 
1520   return(map);
1521 }
1522 
menu(void)1523 int menu(void)
1524 {
1525   int option, done;
1526   Uint32 timestamp, nowtimestamp;
1527   SDLKey key;
1528   SDL_Event event;
1529   SDL_Rect dest;
1530 
1531   /* FIXME: Provide access to an options screen */
1532 
1533   option = MENU_START;
1534   done = 0;
1535 
1536   do
1537   {
1538     timestamp = SDL_GetTicks();
1539 
1540     while (SDL_PollEvent(&event) > 0)
1541     {
1542       if (event.type == SDL_QUIT)
1543       {
1544         option = MENU_QUIT;
1545         done = 1;
1546       }
1547       else if (event.type == SDL_KEYDOWN)
1548       {
1549         key = event.key.keysym.sym;
1550         if (key == SDLK_ESCAPE)
1551         {
1552           option = MENU_QUIT;
1553           done = 1;
1554         }
1555         else if (key == SDLK_RETURN || key == SDLK_SPACE)
1556         {
1557           done = 1;
1558         }
1559       }
1560     }
1561 
1562     SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
1563 
1564     dest.x = (screen->w - img_title->w) / 2;
1565     dest.y = 0;
1566     SDL_BlitSurface(img_title, NULL, screen, &dest);
1567 
1568     SDL_Flip(screen);
1569 
1570     nowtimestamp = SDL_GetTicks();
1571     if (nowtimestamp - timestamp < (1000 / FPS))
1572       SDL_Delay(timestamp + (1000 / FPS) - nowtimestamp);
1573   }
1574   while (!done);
1575 
1576   return(option);
1577 }
1578 
1579 
startgame(void)1580 int startgame(void)
1581 {
1582   int option, done, i, j, x, y;
1583   Uint32 timestamp, nowtimestamp;
1584   SDLKey key;
1585   SDL_Event event;
1586   SDL_Rect dest;
1587   int key_left[4], key_right[4];
1588   int oldkey_left[4], oldkey_right[4];
1589   char str[16];
1590 
1591   done = 0;
1592   option = STARTGAME_START;
1593 
1594   for (i = 0; i < 4; i++)
1595     key_left[i] = key_right[i] = 0;
1596 
1597   do
1598   {
1599     timestamp = SDL_GetTicks();
1600 
1601     for (i = 0; i < 4; i++)
1602     {
1603       oldkey_left[i] = key_left[i];
1604       oldkey_right[i] = key_right[i];
1605     }
1606 
1607     while (SDL_PollEvent(&event) > 0)
1608     {
1609       if (event.type == SDL_QUIT)
1610       {
1611         option = STARTGAME_QUIT;
1612         done = 1;
1613       }
1614       else if (event.type == SDL_KEYDOWN)
1615       {
1616         key = event.key.keysym.sym;
1617         if (key == SDLK_ESCAPE)
1618         {
1619           /* [Esc] back to main menu */
1620 
1621           option = STARTGAME_BACK;
1622           done = 1;
1623         }
1624         else if (key >= SDLK_1 && key <= SDLK_4)
1625         {
1626           /* [1]-[4] to choose number of players */
1627 
1628           num_players = (key - SDLK_1) + 1;
1629         }
1630         else if (key == SDLK_RETURN || key == SDLK_SPACE)
1631         {
1632           /* [Enter/Return] or [Space] to begin */
1633 
1634           option = STARTGAME_START;
1635           done = 1;
1636         }
1637         else if (key == SDLK_PAGEUP)
1638         {
1639           level++;
1640           if (level > 99)
1641             level = 1;
1642         }
1643         else if (key == SDLK_PAGEDOWN)
1644         {
1645           level--;
1646           if (level < 1)
1647             level = 99;
1648         }
1649         else
1650         {
1651           /* Allow players to change their character: */
1652 
1653           for (i = 0; i < 4; i++)
1654           {
1655             if (key == key_codes[i][KEY_RIGHT])
1656             {
1657               plytype[i]++;
1658               if (plytype[i] > 3)
1659                 plytype[i] = 0;
1660             }
1661             else if (key == key_codes[i][KEY_LEFT])
1662             {
1663               plytype[i]--;
1664               if (plytype[i] < 0)
1665                 plytype[i] = 3;
1666             }
1667           }
1668         }
1669       }
1670       else if (event.type == SDL_JOYAXISMOTION)
1671       {
1672         for (i = 0; i < 4; i++)
1673         {
1674           for (j = 0; j < NUM_KEYS; j++)
1675           {
1676             if (event.jaxis.which == stick_codes[i][j].joy &&
1677                 event.jaxis.axis == stick_codes[i][j].axis)
1678             {
1679               if ((event.jaxis.value > 0 && stick_codes[i][j].axis_sign > 0) ||
1680                   (event.jaxis.value < 0 && stick_codes[i][j].axis_sign < 0))
1681               {
1682                 if (j == KEY_LEFT)
1683                 {
1684                   key_left[i] = 1;
1685                   key_right[i] = 0;
1686                 }
1687                 else if (j == KEY_RIGHT)
1688                 {
1689                   key_right[i] = 1;
1690                   key_left[i] = 0;
1691                 }
1692               }
1693               else if (event.jaxis.value == 0)
1694               {
1695                 if (j == KEY_LEFT)
1696                   key_left[i] = 0;
1697                 else if (j == KEY_RIGHT)
1698                   key_right[i] = 0;
1699               }
1700             }
1701           }
1702         }
1703       }
1704       else if (event.type == SDL_JOYBUTTONDOWN)
1705       {
1706         for (i = 0; i < 4; i++)
1707         {
1708           for (j = 0; j < NUM_KEYS; j++)
1709           {
1710             if (event.jbutton.which == stick_codes[i][j].joy &&
1711                 event.jbutton.button == stick_codes[i][j].btn)
1712             {
1713               if (j == KEY_LEFT)
1714                 key_left[i] = 1;
1715               else if (j == KEY_RIGHT)
1716                 key_right[i] = 1;
1717             }
1718           }
1719         }
1720       }
1721       else if (event.type == SDL_JOYBUTTONUP)
1722       {
1723         for (i = 0; i < 4; i++)
1724         {
1725           for (j = 0; j < NUM_KEYS; j++)
1726           {
1727             if (event.jbutton.which == stick_codes[i][j].joy &&
1728                 event.jbutton.button == stick_codes[i][j].btn)
1729             {
1730               if (j == KEY_LEFT)
1731                 key_left[i] = 0;
1732               else if (j == KEY_RIGHT)
1733                 key_right[i] = 0;
1734             }
1735           }
1736         }
1737       }
1738     }
1739 
1740 
1741     for (i = 0; i < 4; i++)
1742     {
1743       if (key_right[i] && oldkey_right[i] == 0)
1744       {
1745         plytype[i]++;
1746         if (plytype[i] > 3)
1747           plytype[i] = 0;
1748       }
1749       else if (key_left[i] && oldkey_left[i] == 0)
1750       {
1751         plytype[i]--;
1752         if (plytype[i] < 0)
1753           plytype[i] = 3;
1754       }
1755     }
1756 
1757     /* Draw screen */
1758 
1759     SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
1760 
1761     /* Title: */
1762     dest.x = (screen->w - img_title->w) / 2;
1763     dest.y = 0;
1764     SDL_BlitSurface(img_title, NULL, screen, &dest);
1765 
1766     /* Instructions: */
1767     dest.x = (screen->w - img_playerselect->w) / 2;
1768     dest.y = img_title->h;
1769     SDL_BlitSurface(img_playerselect, NULL, screen, &dest);
1770 
1771     /* Players: */
1772     for (i = 0; i < 4; i++)
1773     {
1774       x = ((screen->w - TILEW * 8) / 2) + (i * (TILEW * 2));
1775       y = img_title->h + img_playerselect->h + TILEH;
1776       dest.x = x;
1777       dest.y = y;
1778 
1779       drawimage(screen, img_player[plytype[i]], 6, 12,
1780               3, 2 + (i * 3),
1781               dest.x, dest.y);
1782 
1783       if (i >= num_players)
1784       {
1785         /* Fade-out effect over disabled players */
1786 
1787         for (j = 0; j < TILEH; j += 2)
1788         {
1789           dest.x = x;
1790           dest.y = y + j;
1791           dest.w = TILEW;
1792           dest.h = 1;
1793           SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
1794         }
1795       } else {
1796         /* Show player's character stats: */
1797 
1798         for (j = 0; j < char_healths[plytype[i]]; j++)
1799           drawimage(screen, img_stats, 4, 1, 1, 1, x + (img_stats->w / 4) * j, y + TILEH);
1800         for (j = 0; j < char_weapons[plytype[i]]; j++)
1801           drawimage(screen, img_stats, 4, 1, 2, 1, x + (img_stats->w / 4) * j, y + TILEH + img_stats->h);
1802         for (j = 0; j < char_speeds[plytype[i]]; j++)
1803           drawimage(screen, img_stats, 4, 1, 3, 1, x + (img_stats->w / 4) * j, y + TILEH + img_stats->h * 2);
1804       }
1805     }
1806 
1807     /* Level selection */
1808     snprintf(str, sizeof(str), "%d", level);
1809     y = screen->h - img_numbers->h;
1810     x = (screen->w - (img_numbers->w / 10) * strlen(str)) / 2;
1811     for (i = 0; i < strlen(str); i++)
1812       drawimage(screen, img_numbers, 10, 1, str[i] - '0' + 1, 1, x + (i * (img_numbers->w / 10)), y);
1813 
1814     SDL_Flip(screen);
1815 
1816     nowtimestamp = SDL_GetTicks();
1817     if (nowtimestamp - timestamp < (1000 / FPS))
1818       SDL_Delay(timestamp + (1000 / FPS) - nowtimestamp);
1819   }
1820   while (!done);
1821 
1822   /* Set initial game state: */
1823 
1824   if (option == STARTGAME_START)
1825   {
1826     for (i = 0; i < 4; i++)
1827     {
1828       score[i] = 0;
1829       bombs[i] = 0;
1830       upgrade[i] = UPGRADE_NONE;
1831       upgradetime[i] = 0;
1832       keys[i] = 0;
1833 
1834       maxhealth[i] = (MAXHEALTH / 3) * char_healths[plytype[i]];
1835 
1836       if (i < num_players)
1837         health[i] = maxhealth[i];
1838       else
1839         health[i] = 0;
1840     }
1841   }
1842 
1843   return(option);
1844 }
1845 
setup(int argc,char * argv[])1846 void setup(int argc, char * argv[])
1847 {
1848   FILE * fi;
1849   char line[1024], fname[1024];
1850   void * dummy;
1851   int ply, want, joy, axis, btn, i, j;
1852   char * valstr;
1853   int num_joysticks;
1854   SDL_Joystick * js;
1855 
1856   /* Set defaults: */
1857 
1858   for (i = 0; i < 4; i++)
1859     for (j = 0; j < NUM_KEYS; j++)
1860       stick_codes[i][j].joy = -1;
1861 
1862   key_codes[0][KEY_DOWN] = SDLK_DOWN;
1863   key_codes[0][KEY_UP] = SDLK_UP;
1864   key_codes[0][KEY_LEFT] = SDLK_LEFT;
1865   key_codes[0][KEY_RIGHT] = SDLK_RIGHT;
1866   key_codes[0][KEY_FIRE] = SDLK_RCTRL;
1867   key_codes[0][KEY_BOMB] = SDLK_1;
1868 
1869   key_codes[1][KEY_DOWN] = SDLK_s;
1870   key_codes[1][KEY_UP] = SDLK_w;
1871   key_codes[1][KEY_LEFT] = SDLK_a;
1872   key_codes[1][KEY_RIGHT] = SDLK_d;
1873   key_codes[1][KEY_FIRE] = SDLK_q;
1874   key_codes[1][KEY_BOMB] = SDLK_2;
1875 
1876   key_codes[2][KEY_DOWN] = SDLK_KP2;
1877   key_codes[2][KEY_UP] = SDLK_KP8;
1878   key_codes[2][KEY_LEFT] = SDLK_KP4;
1879   key_codes[2][KEY_RIGHT] = SDLK_KP6;
1880   key_codes[2][KEY_FIRE] = SDLK_KP0;
1881   key_codes[2][KEY_BOMB] = SDLK_3;
1882 
1883   key_codes[3][KEY_DOWN] = SDLK_g;
1884   key_codes[3][KEY_UP] = SDLK_t;
1885   key_codes[3][KEY_LEFT] = SDLK_f;
1886   key_codes[3][KEY_RIGHT] = SDLK_h;
1887   key_codes[3][KEY_FIRE] = SDLK_r;
1888   key_codes[3][KEY_BOMB] = SDLK_4;
1889 
1890   /* FIXME: Read config-file arguments */
1891   if (getenv("HOME") != NULL)
1892   {
1893     snprintf(fname, sizeof(fname), "%s/.foprc", getenv("HOME"));
1894     fi = fopen(fname, "r");
1895     if (fi != NULL)
1896     {
1897       do
1898       {
1899         dummy = fgets(line, sizeof(line), fi);
1900         if (!feof(fi))
1901         {
1902           line[strlen(line) - 1] = '\0';
1903           if (line[0] == 'P' && line[1] >= '1' && line[1] <= '4' && line[2] == '_')
1904           {
1905             ply = line[1] - '1';
1906 
1907             if (strstr(line, "FIRE = ") == line + 3)
1908               want = KEY_FIRE;
1909             else if (strstr(line, "BOMB = ") == line + 3)
1910               want = KEY_BOMB;
1911             else if (strstr(line, "UP = ") == line + 3)
1912               want = KEY_UP;
1913             else if (strstr(line, "DOWN = ") == line + 3)
1914               want = KEY_DOWN;
1915             else if (strstr(line, "LEFT = ") == line + 3)
1916               want = KEY_LEFT;
1917             else if (strstr(line, "RIGHT = ") == line + 3)
1918               want = KEY_RIGHT;
1919             else
1920             {
1921               fprintf(stderr, "Unknown option: %s\n", line);
1922               want = -1;
1923             }
1924 
1925             if (want != -1)
1926             {
1927               valstr = strstr(line, " = ") + 3;
1928               if (strstr(valstr, "KEY_") == valstr)
1929               {
1930                 key_codes[ply][want] = atoi(valstr + 4);
1931                 stick_codes[ply][want].joy = -1;
1932               }
1933               else if (strstr(valstr, "JOY") == valstr)
1934               {
1935                 joy = atoi(valstr + 3);
1936 
1937                 if (strstr(valstr, "_AXIS") == valstr + 4)
1938                 {
1939                   axis = atoi(valstr + 9);
1940 
1941                   if (strstr(valstr, "_POS") >= valstr + 10)
1942                   {
1943                     key_codes[ply][want] = -1;
1944                     stick_codes[ply][want].joy = joy;
1945                     stick_codes[ply][want].axis = axis;
1946                     stick_codes[ply][want].axis_sign = 1;
1947                     stick_codes[ply][want].btn = -1;
1948                   }
1949                   else if (strstr(valstr, "_NEG") >= valstr + 10)
1950                   {
1951                     key_codes[ply][want] = -1;
1952                     stick_codes[ply][want].joy = joy;
1953                     stick_codes[ply][want].axis = axis;
1954                     stick_codes[ply][want].axis_sign = -1;
1955                     stick_codes[ply][want].btn = -1;
1956                   }
1957                   else
1958                     fprintf(stderr, "Unknown option: %s\n", line);
1959                 }
1960                 else if (strstr(valstr, "_BTN") == valstr + 4)
1961                 {
1962                   btn = atoi(valstr + 8);
1963 
1964                   key_codes[ply][want] = -1;
1965                   stick_codes[ply][want].joy = joy;
1966                   stick_codes[ply][want].axis = -1;
1967                   stick_codes[ply][want].btn = btn;
1968                 }
1969                 else
1970                 {
1971                   fprintf(stderr, "Unknown option: %s\n", line);
1972                 }
1973               }
1974             }
1975           }
1976         }
1977       }
1978       while (!feof(fi));
1979       fclose(fi);
1980     }
1981   }
1982 
1983 /*
1984   for (i = 0; i < 4; i++)
1985     for (j = 0; j < NUM_KEYS; j++)
1986       printf("ply%d key%d : joy=%d axis=%d(%d) btn=%d\n", i+1, j,
1987         stick_codes[i][j].joy,
1988         stick_codes[i][j].axis,
1989         stick_codes[i][j].axis_sign,
1990         stick_codes[i][j].btn);
1991 */
1992 
1993   /* FIXME: Parse command-line arguments */
1994 
1995   /* Initialize SDL */
1996 
1997   if (SDL_Init(SDL_INIT_VIDEO) < 0)
1998   {
1999     fprintf(stderr, "Error init'ing SDL: %s\n", SDL_GetError());
2000     exit(1);
2001   }
2002 
2003   if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
2004   {
2005     fprintf(stderr, "Warning: Error init'ing SDL: %s\n", SDL_GetError());
2006   }
2007   else
2008   {
2009     num_joysticks = SDL_NumJoysticks();
2010 
2011     for (i = 0; i < num_joysticks; i++)
2012       js = SDL_JoystickOpen(i);
2013   }
2014 
2015   /* Open a window / screen */
2016 
2017   screen = SDL_SetVideoMode(SCREENW, SCREENH, 0, 0);
2018   if (screen == NULL)
2019   {
2020     fprintf(stderr, "Error opening video: %s\n", SDL_GetError());
2021     SDL_Quit();
2022     exit(1);
2023   }
2024 
2025   SDL_WM_SetCaption("Fight or Perish (v" VERSION ")", "FOP");
2026 
2027   /* Load imagery */
2028 
2029   img_title = loadimage("title.png");
2030   img_playerselect = loadimage("playerselect.png");
2031   img_stats = loadimage("stats.png");
2032   img_numbers = loadimage("numbers.png");
2033   img_generators = loadimage("generators.png");
2034   img_collectibles = loadimage("collectibles.png");
2035   img_blockers = loadimage("blockers.png");
2036   img_badguys = loadimage("badguys.png");
2037   img_player[0] = loadimage("playera.png");
2038   img_arrows[0] = loadimage("arrowsa.png");
2039   img_player[1] = loadimage("playerb.png");
2040   img_arrows[1] = loadimage("arrowsb.png");
2041   img_player[2] = loadimage("playerc.png");
2042   img_arrows[2] = loadimage("arrowsc.png");
2043   img_player[3] = loadimage("playerd.png");
2044   img_arrows[3] = loadimage("arrowsd.png");
2045 }
2046 
2047