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