1 /* factoroids.c
2
3 The main game loop for a factoring game resembling the
4 arcade classic "Asteroids".
5
6 Some code adapted from the GPL-licensed game "vectoroids"
7 by Bill Kendrick (http://www.newbreedsoftware.com).
8
9 Copyright 2001, 2002, 2008, 2009, 2010.
10 Authors: Bill Kendrick, Jesus M. Mager H., Tim Holy, David Bruce
11 Project email: <tuxmath-devel@lists.sourceforge.net>
12 Project website: http://tux4kids.alioth.debian.org
13
14
15 factoroids.c is part of "Tux, of Math Command", a.k.a. "tuxmath".
16
17 Tuxmath is free software: you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation; either version 3 of the License, or
20 (at your option) any later version.
21
22 Tuxmath is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with this program. If not, see <http://www.gnu.org/licenses/>. */
29
30
31
32 #include "tuxmath.h"
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include "SDL.h"
39 #ifndef NOSOUND
40 #include "SDL_mixer.h"
41 #endif
42 #include "SDL_image.h"
43 #include "SDL_rotozoom.h"
44
45 #include "credits.h"
46 #include "game.h"
47 #include "fileops.h"
48 #include "setup.h"
49 #include "mathcards.h"
50 #include "titlescreen.h"
51 #include "options.h"
52
53 #define FPS 15 /* 15 frames per second */
54 #define MS_PER_FRAME (1000 / FPS)
55 #define BASE_RES_X 1280
56 #define ASTEROID_NUM_SIZE 48
57
58 #define MAX_LASER 5
59 #define MAX_ASTEROIDS 50
60 #define NUM_TUXSHIPS 2
61 #define NUM_SPRITES 11
62 #define TUXSHIP_LIVES 3
63 #define DEG_PER_ROTATION 2
64 #define NUM_OF_ROTO_IMGS 360/DEG_PER_ROTATION
65 /* TUXSHIP_DECEL controls "friction" - 1 means ship glides infinitely, 0 stops it instantly */
66 #define TUXSHIP_DECEL 0.95
67 #define DEG_TO_RAD 0.0174532925
68 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
69 //the prime set keeps increasing till its size reaches this value
70 #define PRIME_MAX_LIMIT 6
71
72 #define CTRL_NEXT SDLK_f
73 #define CTRL_PREV SDLK_d
74
75 /* definitions of level message */
76 #define MAX_CHAR_MSG 256
77 #define LVL_WIDTH_MSG 350
78 #define LVL_HEIGHT_MSG 200
79 #define LVL_OBJ_X_OFFSET 20
80 #define LVL_OBJ_Y_OFFSET 20
81 #define LVL_HINT_X_OFFSET 20
82 #define LVL_HINT_Y_OFFSET 130
83
84 /* definitions for cockpit buttons */
85 #define BUTTONW 24
86 #define BUTTONH 24
87 #define BUTTON2_X 65
88 #define BUTTON2_Y 45
89 #define BUTTON3_X 53
90 #define BUTTON3_Y 65
91 #define BUTTON5_X 30
92 #define BUTTON5_Y 77
93 #define BUTTON7_X 8
94 #define BUTTON7_Y 77
95 #define BUTTON11_X 30
96 #define BUTTON11_Y 65
97 #define BUTTON13_X 45
98 #define BUTTON13_Y 45
99 #define NUMBUTTONS 6
100
101 //a value (float) indicating the sensitivity of the mouse
102 //0 = disable mouse; 0.1 high ... 1 low, 2 lower, so on
103 //FIXME - this is a very quick/dirty response to my observation
104 //that the mouse sensitivity is too high in my initial tests
105 //of the win32 build. We ought to figure out if we can get
106 //some info from the OS under SDL to set this better, and
107 //also provide a way for users to adjust this setting.
108 #if defined BUILD_MINGW32 || defined __APPLE__
109 #define MOUSE_SENSITIVITY 2
110 #else
111 #define MOUSE_SENSITIVITY 0.5
112 #endif
113
114 //a special value indicating that a bonus hasn't been used yet
115 #define BONUS_NOTUSED -1
116
117 /********* Enumerations ***********/
118
119 enum{
120 FACTOROIDS_GAME,
121 FRACTIONS_GAME
122 };
123
124 /* Enumerations for Button Types in Cockpit */
125 enum BUTTON_TYPE
126 {
127 ACTIVE,
128 SELECTED,
129 PRESSED,
130 DISABLED
131 };
132
133 enum FF_STATUS
134 {
135 FF_OVER_SDL_QUIT,
136 FF_OVER_ESCAPE,
137 FF_OVER_LOST,
138 FF_OVER_WON,
139 FF_OVER_ERROR,
140 FF_OVER_OTHER,
141 FF_IN_PROGRESS
142 };
143
144 /********* Structures *********/
145
146 typedef struct colorRGBA_type {
147 Uint8 r;
148 Uint8 g;
149 Uint8 b;
150 Uint8 a;
151 } ColorRGBA_type;
152
153 typedef struct asteroid_type {
154 int alive, size;
155 int angle, angle_speed;
156 int xspeed, yspeed;
157 int x, y;
158 int rx, ry;
159 int centerx, centery;
160 int radius;
161 int fact_number;
162 int isprime;
163 int a, b; /* a / b */
164 int count;
165 int xdead, ydead, isdead, countdead;
166 } asteroid_type;
167
168
169 typedef struct tuxship_type {
170 int lives, size;
171 int xspeed, yspeed;
172 int x, y;
173 int rx, ry;
174 int x1,y1,x2,y2,x3,y3;
175 int radius;
176 int centerx, centery;
177 int angle;
178 int hurt, hurt_count;
179 int count;
180 bool thrust;
181 } tuxship_type;
182
183
184 typedef struct FF_laser_type{
185 int alive;
186 int x, y;
187 int destx,desty;
188 int r, g, b;
189 int count;
190 int angle;
191 int m;
192 int n;
193 } FF_laser_type;
194
195
196 typedef struct {
197 int x_is_blinking;
198 int extra_life_is_blinking;
199 int laser_enabled;
200 } help_controls_type;
201
202 /* Structures for Buttons on Cockpit */
203 struct ButtonType
204 {
205 int img_id;
206 int x;
207 int y;
208 int prime;
209 };
210
211 /********* Enums ******************/
212
213 typedef enum _TuxBonus {
214 TB_CLOAKING, TB_FORCEFIELD, TB_POWERBOMB, TB_SIZE
215 } TuxBonus;
216
217 int bonus_img_ids[] = {
218 IMG_BONUS_CLOAKING, IMG_BONUS_FORCEFIELD, IMG_BONUS_POWERBOMB
219 };
220
221 /********* Global vars ************/
222
223 /* Trig junk: (thanks to Atari BASIC for this) */
224
225 static int trig[12] = {
226 1024,
227 1014,
228 984,
229 935,
230 868,
231 784,
232 685,
233 572,
234 448,
235 316,
236 117,
237 0
238 };
239
240 static int bonus = -1;
241 static int bonus_time = BONUS_NOTUSED;
242
243 static const int prime_numbers[] = {2, 3, 5, 7, 11, 13};
244 static const int prime_power_limit[] = {7, 4, 3, 3, 2, 2}; //custom calibrated power limits for extra "goodness"
245
246 static const int prime_next[] = {2, 2, 3, 5, 5, 7, 7, 11, 11, 11, 11, 13, 13, 2};
247 static const int prime_prev[] = {13, 13, 13, 2, 3, 3, 3, 5, 5, 7, 7, 7, 7, 11, 11};
248
249 static char* game_music_filenames[NUM_MUSICS] = {
250 "01_rush.ogg",
251 "02_on_the_edge_of_the_universe.ogg",
252 "03_gravity.ogg",
253 "game.mod",
254 "game2.mod",
255 "game3.mod",
256 };
257
258 static int laser_coeffs[][3] = {
259 {0, 0, 0}, // 0
260 {0, 0, 0}, // 1
261 {18, 0, 0}, // 2
262 {0, 18, 0}, // 3
263 {0, 0, 0}, // 4
264 {0, 0, 18}, // 5
265 {0, 0, 0}, // 6
266 {18, 18, 0},// 7
267 {0, 0, 0}, // 8
268 {0, 0, 0}, // 9
269 {0, 0, 0}, // 10
270 {0, 18, 18},// 11
271 {0, 0, 0}, // 12
272 {18, 0, 18} // 13
273 };
274
275 // ControlKeys
276 static int mouseroto;
277 static int left_pressed;
278 static int right_pressed;
279 static int up_pressed;
280 static int shift_pressed;
281 static int shoot_pressed;
282 static int button_pressed;
283
284 // GameControl
285 static int game_status;
286 //static int gameover_counter;
287 static int escape_received;
288
289 //SDL_Surfaces:
290 static SDL_Surface* IMG_lives_ship = NULL;
291 static SDL_Surface* IMG_tuxship[NUM_OF_ROTO_IMGS];
292 static SDL_Surface* IMG_tuxship_cloaked[NUM_OF_ROTO_IMGS];
293 static SDL_Surface* IMG_tuxship_thrust[NUM_OF_ROTO_IMGS];
294 static SDL_Surface* IMG_tuxship_thrust_cloaked[NUM_OF_ROTO_IMGS];
295 static SDL_Surface* IMG_asteroids1[NUM_OF_ROTO_IMGS];
296 static SDL_Surface* IMG_asteroids2[NUM_OF_ROTO_IMGS];
297 static SDL_Surface* bkgd = NULL; //640x480 background (windowed)
298 static SDL_Surface* scaled_bkgd = NULL; //native resolution (fullscreen)
299
300 //Scale factor for window size/resolution
301 static float zoom;
302
303 // Game type
304 static int FF_game;
305
306 // Game vars
307 static int score;
308 static int wave;
309 static int paused;
310 static int escape_received;
311 static int game_status;
312 static int SDL_quit_received;
313 static int quit;
314 static int digits[3];
315 static int num;
316 static int mouse_reset;
317
318 static int neg_answer_picked;
319 static int tux_pressing;
320 static int doing_answer;
321 static int tux_img;
322 //static int FF_level;
323
324 static asteroid_type* asteroid = NULL;
325 static tuxship_type tuxship;
326 static FF_laser_type laser[MAX_LASER];
327
328 static int NUM_ASTEROIDS;
329 static int counter;
330 static int roto_speed;
331
332 static struct ButtonType buttons[NUMBUTTONS];
333
334 /*************** The Factor and Fraction Activity Game Functions ***************/
335
336 /* Local function prototypes: */
337
338 static int FF_init(void);
339 static void FF_intro(void);
340
341 static void FF_handle_ship(void);
342 static void FF_handle_asteroids(void);
343 static void FF_handle_answer(void);
344 static int check_exit_conditions(void);
345 static void FF_draw(void);
346 static void FF_draw_bkgr(void);
347 static void FF_draw_led_console(void);
348 static void draw_console_image(int i);
349 void draw_nums(const char* str, int x, int y, SDL_Color* col);
350
351 static void FF_DrawButton(int img_id, enum BUTTON_TYPE type, int x, int y);
352 static void FF_DrawButtonLayout(void);
353 static void FF_ButtonInit(void);
354 static int FF_CockpitTux(int prime);
355
current_bkgd()356 static SDL_Surface* current_bkgd()
357 { return screen->flags & SDL_FULLSCREEN ? scaled_bkgd : bkgd; }
358
359 static void FF_add_level(void);
360 static int FF_over(int game_status);
361 static void FF_exit_free(void);
362
363 static int FF_add_laser(void);
364 static int FF_add_asteroid(int x, int y, int xspeed, int yspeed, int size, int angle, int angle_speed, int fact_num, int a, int b, int new_wave);
365 static int FF_destroy_asteroid(int i, float xspeed, float yspeed);
366
367 static void FF_ShowMessage(char* str);
368 static void FF_LevelMessage(void);
369 static void FF_LevelObjsHints(char *label, char *contents, int x, int y);
370
371 static SDL_Surface* get_asteroid_image(int size,int angle);
372 static int AsteroidColl(int astW,int astH,int astX,int astY,
373 int x, int y);
374 static int is_prime(int num);
375 static int fast_cos(int angle);
376 static int fast_sin(int angle);
377 static int generatenumber(int wave);
378 static int validate_number(int num, int wave);
379 static void game_handle_user_events(void);
380 static int game_mouse_event(SDL_Event event);
game_mouseroto(SDL_Event event)381 static int game_mouseroto(SDL_Event event) {return event.motion.xrel;}
382 static void _tb_PowerBomb(int n);
383
384 /************** factors(): The factor main function ********************/
factors(void)385 void factors(void)
386 {
387 Uint32 timer = 0;
388
389 quit = 0;
390 counter = 0;
391
392 DEBUGMSG(debug_factoroids, "Entering factors():\n");
393
394 FF_game = FACTOROIDS_GAME;
395
396 if (!FF_init())
397 {
398 fprintf(stderr, "FF_init() failed!\n");
399 FF_exit_free();
400 return;
401 }
402 FF_LevelMessage();
403
404 while (game_status == FF_IN_PROGRESS)
405 {
406 counter++;
407
408 game_handle_user_events();
409 if(SDL_GetTicks() > bonus_time && bonus_time != -1) {bonus_time = 0;}
410
411 FF_handle_ship();
412 FF_handle_asteroids();
413 FF_handle_answer();
414
415 tux_img = FF_CockpitTux(num);
416
417 FF_draw();
418 SDL_Flip(screen);
419
420 game_status = check_exit_conditions();
421
422 if (paused)
423 {
424 pause_game();
425 paused = 0;
426 }
427
428
429 #ifndef NOSOUND
430 if (Opts_UsingSound())
431 {
432 //...when the music's over, turn out the lights!
433 //...oops, wrong song! Actually, we just pick next music at random:
434 if (!Mix_PlayingMusic())
435 {
436 T4K_AudioMusicLoad(game_music_filenames[(rand() % NUM_MUSICS)], T4K_AUDIO_PLAY_ONCE);
437 }
438 }
439 #endif
440
441 /* Pause (keep frame-rate event) */
442 T4K_Throttle(MS_PER_FRAME, &timer);
443 }
444 FF_over(game_status);
445 }
446
447
448 /************** fractions(): The fractions main function ********************/
fractions(void)449 void fractions(void)
450 {
451 Uint32 timer = 0;
452 quit = 0;
453 counter = 0;
454 tux_img = IMG_TUX_CONSOLE1;
455
456 DEBUGMSG(debug_factoroids, "Entering factors():\n");
457 /*****Initalizing the Factor activiy *****/
458 FF_game = FRACTIONS_GAME;
459
460 if (!FF_init())
461 {
462 fprintf(stderr, "FF_init() failed!\n");
463 FF_exit_free();
464 return;
465 }
466
467 /************ Main Loop **************/
468 while (game_status == FF_IN_PROGRESS)
469 {
470 counter++;
471
472 if(counter%15 == 0)
473 {
474 if(tux_img < IMG_TUX_CONSOLE4)
475 tux_img++;
476 else
477 tux_img = IMG_TUX_CONSOLE1;
478 }
479
480 game_handle_user_events();
481
482 FF_handle_ship();
483 FF_handle_asteroids();
484 FF_handle_answer();
485 FF_draw();
486 SDL_Flip(screen);
487
488 game_status = check_exit_conditions();
489
490 if (paused)
491 {
492 pause_game();
493 paused = 0;
494 }
495
496
497 #ifndef NOSOUND
498 if (Opts_UsingSound())
499 {
500 if (!Mix_PlayingMusic())
501 {
502 T4K_AudioMusicLoad(game_music_filenames[(rand() % 3)], T4K_AUDIO_PLAY_ONCE);
503 }
504 }
505 #endif
506
507 /* Pause (keep frame-rate event) */
508 T4K_Throttle(MS_PER_FRAME, &timer);
509 }
510 FF_over(game_status);
511 }
512
FF_LevelMessage(void)513 static void FF_LevelMessage(void)
514 {
515 SDL_Event event;
516 SDL_Rect rect;
517 SDL_Surface *bgsurf=NULL;
518 int nwave;
519 Uint32 timer =0;
520 int waiting = 1;
521
522 char objs_str[PRIME_MAX_LIMIT][MAX_CHAR_MSG] =
523 {
524 N_("Powers of 2"),
525 N_("Products of 2 and 3"),
526 N_("Products of 2, 3 and 5"),
527 N_("Products of 2, 3, 5 and 7"),
528 N_("Products of 2, 3, 5, 7, and 11"),
529 N_("Products of 2, 3, 5, 7, 11 and 13")
530 };
531
532 char hints_str[PRIME_MAX_LIMIT][MAX_CHAR_MSG] =
533 {
534 N_("All multiples of 2 end in 2, 4, 6, 8, or 0"),
535 N_("The digits of a multiple of 3 add up to a multiple of 3"),
536 N_("All multiples of 5 end in 0 or 5"),
537 N_("Sorry - there is no simple rule to identify multiples of 7."),
538 N_("Under 100, multiples of 11 have equal digits, such as 55 or 88."),
539 N_("Sorry - there is no simple rule to identify multiples of 13."),
540 };
541
542 rect.x = (screen->w/2)-(LVL_WIDTH_MSG/2);
543 rect.y = (screen->h/2)-(LVL_HEIGHT_MSG/2);
544
545 FF_draw();
546 bgsurf = T4K_CreateButton(LVL_WIDTH_MSG,LVL_HEIGHT_MSG,12,19,19,96,96);
547
548 if(bgsurf)
549 {
550 SDL_BlitSurface(bgsurf, NULL, screen, &rect );
551 SDL_FreeSurface(bgsurf);
552 }
553
554 nwave = (wave > PRIME_MAX_LIMIT) ? PRIME_MAX_LIMIT : wave;
555
556 FF_LevelObjsHints(_("Objectives:"), _(objs_str[nwave-1]), rect.x+LVL_OBJ_X_OFFSET, rect.y+LVL_OBJ_Y_OFFSET);
557 FF_LevelObjsHints(_("Hints:"), _(hints_str[nwave-1]), rect.x+LVL_HINT_X_OFFSET, rect.y+LVL_HINT_Y_OFFSET);
558
559 SDL_Flip(screen);
560
561 /* wait for user events */
562 while(waiting)
563 {
564 while(SDL_PollEvent(&event))
565 {
566 if (event.type == SDL_QUIT)
567 {
568 SDL_quit_received = 1;
569 quit = 1;
570 waiting = 0;
571 break;
572 }
573 else if (event.type == SDL_MOUSEBUTTONDOWN)
574 {
575 waiting = 0;
576 break;
577 }
578 else if (event.type == SDL_KEYDOWN)
579 {
580 if (event.key.keysym.sym == SDLK_ESCAPE)
581 escape_received = 1;
582 waiting = 0;
583 break;
584 }
585 }
586 /* keep from eating all CPU: */
587 T4K_Throttle(MS_PER_FRAME, &timer);
588 }
589 }
590
591 /************ Initialize all vars... ****************/
FF_init(void)592 static int FF_init(void)
593 {
594 Uint32 timer = 0;
595 int i;
596 mouse_reset = 0;
597
598 SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
599 SDL_Flip(screen);
600 SDL_ShowCursor(0);
601
602 /* Settings to let us track mouse movement even beyond edge of screen
603 * for control of ship rotation. Note that SDL reportedly supports
604 * this only on "Windows and Unix-alikes", i.e. maybe not OS-X
605 */
606 SDL_WM_GrabInput(SDL_GRAB_ON);
607
608 /******** Set up properly scaled and optimized background surfaces: *********/
609 /* NOTE - optimization code moved into LoadBothBkgds() so rest of program */
610 /* can take advantage of it - DSB */
611
612 T4K_LoadBothBkgds("factoroids/gbstars.png", &scaled_bkgd, &bkgd);
613
614 if (bkgd == NULL || scaled_bkgd == NULL)
615 {
616 fprintf(stderr,
617 "\nError: could not scale background\n");
618 return 0;
619 }
620
621 FF_intro();
622
623 if(screen->h < 600 && screen->w < 800)
624 zoom = 0.65;
625 else
626 zoom = (float)screen->w/(float)BASE_RES_X;
627
628 DEBUGCODE(debug_factoroids)
629 fprintf(stderr, "The zoom factor is: %f\n", zoom);
630
631 /*************** Precalculating software rotation ***************/
632
633 for(i = 0; i < NUM_OF_ROTO_IMGS; i++)
634 {
635 //rotozoomSurface (SDL_Surface *src, double angle, double zoom, int smooth);
636 IMG_tuxship[i] = rotozoomSurface(images[IMG_SHIP01], i * DEG_PER_ROTATION, zoom, 1);
637 IMG_tuxship_cloaked[i] = rotozoomSurface(images[IMG_SHIP_CLOAKED], i * DEG_PER_ROTATION, zoom, 1);
638 IMG_tuxship_thrust[i] = rotozoomSurface(images[IMG_SHIP_THRUST], i * DEG_PER_ROTATION, zoom, 1);
639 IMG_tuxship_thrust_cloaked[i] = rotozoomSurface(images[IMG_SHIP_THRUST_CLOAKED], i * DEG_PER_ROTATION, zoom, 1);
640
641 if (IMG_tuxship[i] == NULL)
642 {
643 fprintf(stderr,
644 "\nError: rotozoomSurface() of images[IMG_SHIP01] for i = %d returned NULL\n", i);
645 return 0;
646 }
647
648 IMG_asteroids1[i] = rotozoomSurface(images[IMG_ASTEROID1], i * DEG_PER_ROTATION, zoom, 1);
649
650 if (IMG_asteroids1[i] == NULL)
651 {
652 fprintf(stderr,
653 "\nError: rotozoomSurface() of images[IMG_ASTEROID1] for i = %d returned NULL\n", i);
654 return 0;
655 }
656
657 IMG_asteroids2[i] = rotozoomSurface(images[IMG_ASTEROID2], i*DEG_PER_ROTATION, zoom, 1);
658
659 if (IMG_asteroids2[i] == NULL)
660 {
661 fprintf(stderr,
662 "\nError: rotozoomSurface() of images[IMG_ASTEROID2] for i = %d returned NULL\n", i);
663 return 0;
664 }
665 }
666
667
668 /* Create zoomed and scaled ship image for "lives" counter */
669 IMG_lives_ship = rotozoomSurface(images[IMG_SHIP_CLOAKED], 90, zoom * 0.7, 1);
670
671
672 /******** Set up properly scaled and optimized background surfaces: *********/
673 /* NOTE - optimization code moved into LoadBothBkgds() so rest of program */
674 /* can take advantage of it - DSB */
675
676 T4K_LoadBothBkgds("factoroids/gbstars.png", &scaled_bkgd, &bkgd);
677
678 if (bkgd == NULL || scaled_bkgd == NULL)
679 {
680 fprintf(stderr,
681 "\nError: could not scale background\n");
682 return 0;
683 }
684
685
686 // Allocate memory
687 asteroid = NULL; // set in case allocation fails partway through
688 asteroid = (asteroid_type *) malloc(MAX_ASTEROIDS * sizeof(asteroid_type));
689
690 if (asteroid == NULL)
691 {
692 fprintf(stderr, "Allocation of asteroids failed");
693 return 0;
694 }
695
696 memset(asteroid, 0, MAX_ASTEROIDS * sizeof(asteroid_type));
697
698 NUM_ASTEROIDS = 4;
699
700 /**************Setting up the ship values! **************/
701 tuxship.x = ((screen->w)/2) - 20;
702 tuxship.y = ((screen->h)/2) - 20;
703 tuxship.lives = TUXSHIP_LIVES;
704 tuxship.hurt = 0;
705 tuxship.hurt_count = 0;
706 tuxship.angle = 90;
707 tuxship.xspeed = 0;
708 tuxship.yspeed = 0;
709 tuxship.radius = (images[IMG_SHIP01]->h)/2;
710 tuxship.thrust = 0;
711
712 tuxship.x1 = images[IMG_SHIP01]->w-(images[IMG_SHIP01]->w/8);
713 tuxship.y1 = images[IMG_SHIP01]->h/2;
714 tuxship.x2 = images[IMG_SHIP01]->w/8;
715 tuxship.y2 = images[IMG_SHIP01]->h/8;
716 tuxship.x3 = images[IMG_SHIP01]->w/8;
717 tuxship.y3 = images[IMG_SHIP01]->h-(images[IMG_SHIP01]->h/8);
718
719 /* --- reset all controls: --- */
720 left_pressed = 0;
721 right_pressed = 0;
722 up_pressed = 0;
723 shift_pressed = 0;
724 shoot_pressed = 0;
725 button_pressed = 0;
726 SDL_quit_received = 0;
727 score = 0;
728 wave = 0;
729 escape_received = 0;
730 game_status = FF_IN_PROGRESS;
731
732 FF_add_level();
733
734 for (i = 0; i < MAX_LASER; i++)
735 laser[i].alive = 0;
736
737 // Wait for click or keypress to start (get out if user presses Esc) :
738 while(1)
739 {
740 SDL_PollEvent(&event);
741 if (event.type == SDL_QUIT)
742 {
743 SDL_quit_received = 1;
744 quit = 1;
745 return 1;
746 }
747 else if (event.type == SDL_MOUSEBUTTONDOWN)
748 {
749 return 1;
750 }
751 else if (event.type == SDL_KEYDOWN)
752 {
753 if (event.key.keysym.sym == SDLK_ESCAPE)
754 escape_received = 1;
755 return 1;
756 }
757 /* Don't eat all available CPU: */
758 T4K_Throttle(MS_PER_FRAME, &timer);
759 }
760 T4K_Throttle(MS_PER_FRAME, &timer);
761 }
762
763
FF_intro(void)764 static void FF_intro(void)
765 {
766 static SDL_Surface* IMG_factors;
767 static SDL_Surface* IMG_fractions;
768
769 SDL_Rect rect;
770
771 float zoom;
772
773 if(screen->h < 600 && screen->w < 800)
774 zoom = 0.65;
775 else
776 zoom=(float)screen->w/(float)BASE_RES_X;
777
778 IMG_factors = rotozoomSurface(images[IMG_FACTOROIDS], 0, zoom, 1);
779 IMG_fractions = rotozoomSurface(images[IMG_FACTORS], 0, zoom, 1);
780
781 FF_draw_bkgr();
782 if(FF_game == FACTOROIDS_GAME)
783 {
784
785 rect.x = (screen->w/2) - (IMG_factors->w/2);
786 rect.y = (screen->h)/7;
787 SDL_BlitSurface(IMG_factors, NULL, screen, &rect);
788 FF_ShowMessage(_("To win, you must destroy all the asteroids.\n"
789 "Turn: arrow keys or mouse movement.\n"
790 "Thrust: up arrow or right mouse button.\n"
791 "Shoot: [Enter], [Space], or left mouse button.\n"
792 "Switch Prime Number Gun: [D], [F], or mouse scroll wheel.\n"
793 "Activate Powerup: [Shift].\n"
794 "Shoot the rocks with their prime factors until they are all destroyed."));
795 SDL_BlitSurface(IMG_asteroids1[3],NULL,screen,&rect);
796 }
797 else if (FF_game == FRACTIONS_GAME)
798 {
799 rect.x = (screen->w/2)-(IMG_fractions->w/2);
800 rect.y = (screen->h)/7;
801 SDL_BlitSurface(IMG_fractions,NULL,screen,&rect);
802 FF_ShowMessage(_("FRACTIONS: to win, you need destroy all the asteroids. "
803 "Use the arrow keys to turn or go forward. Aim at an asteroid, "
804 "type a number that can simplify the fraction, and press space or return "
805 "to split it. Destroy fractions that can not be further simplified in a single shot!"));
806 }
807
808 SDL_FreeSurface(IMG_factors);
809 SDL_FreeSurface(IMG_fractions);
810 }
811
FF_handle_ship(void)812 static void FF_handle_ship(void)
813 {
814 //FIXME - am I missing something -- doesn't this just reduce to
815 //"tuxship.centerx = tuxship.x" and likewise for y???
816 /****************** Ship center... ******************/
817
818 tuxship.centerx = ((IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->w)/2) +
819 (tuxship.x - (IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->w/2));
820 tuxship.centery = ((IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->h)/2) +
821 (tuxship.y - (IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->h/2));
822
823 /******************* Ship live *********************/
824
825 if(tuxship.hurt)
826 {
827 tuxship.hurt_count--;
828 if(tuxship.hurt_count <= 0)
829 tuxship.hurt = 0;
830 }
831 /****************** Rotate Ship *********************/
832
833 if(right_pressed || left_pressed)
834 {
835 if(roto_speed < 10)
836 {
837 roto_speed = roto_speed + 2;
838 }
839 }
840 else
841 {
842 roto_speed = 1;
843 }
844
845 if (right_pressed)
846 {
847 tuxship.angle = tuxship.angle - DEG_PER_ROTATION * roto_speed;
848 if (tuxship.angle < 0)
849 tuxship.angle = tuxship.angle + 360;
850
851 tuxship.x1= fast_cos(DEG_PER_ROTATION*-roto_speed) * tuxship.centerx
852 -fast_sin(DEG_PER_ROTATION*-roto_speed) * tuxship.centery;
853 tuxship.y1= fast_sin(DEG_PER_ROTATION*-roto_speed) * tuxship.centerx
854 +fast_cos(DEG_PER_ROTATION*-roto_speed) * tuxship.centery;
855
856 }
857 else if (left_pressed)
858 {
859 tuxship.angle=tuxship.angle + DEG_PER_ROTATION * roto_speed;
860 if (tuxship.angle >= 360)
861 tuxship.angle = tuxship.angle - 360;
862
863 tuxship.x1= fast_cos(DEG_PER_ROTATION*roto_speed) * tuxship.centerx
864 -fast_sin(DEG_PER_ROTATION*roto_speed) * tuxship.centery;
865 tuxship.y1= fast_sin(DEG_PER_ROTATION*roto_speed * tuxship.centerx
866 +fast_cos(DEG_PER_ROTATION*roto_speed)) * tuxship.centery;
867
868 }
869
870 /**************** Mouse Rotation ************************/
871 tuxship.angle = (tuxship.angle + DEG_PER_ROTATION * -mouseroto) % 360;
872 tuxship.angle += tuxship.angle < 0 ? 360 : 0;
873
874 tuxship.x1= fast_cos(DEG_PER_ROTATION*roto_speed) * tuxship.centerx
875 -fast_sin(DEG_PER_ROTATION*roto_speed) * tuxship.centery;
876 tuxship.y1= fast_sin(DEG_PER_ROTATION*roto_speed * tuxship.centerx
877 +fast_cos(DEG_PER_ROTATION*roto_speed)) * tuxship.centery;
878
879 /**************** Move, and increse speed ***************/
880
881
882 if (up_pressed && (tuxship.lives > 0))
883 {
884 tuxship.xspeed = tuxship.xspeed + ((fast_cos(tuxship.angle >> 3) * 3) >> 10);
885 tuxship.yspeed = tuxship.yspeed - ((fast_sin(tuxship.angle >> 3) * 3) >> 10);
886
887 //Google Code-In 2010 Task: Add sound for ship's thrust
888 //Sound taken from http://www.freesound.org 20/12/2010
889 playsound(SND_ENGINE);
890 tuxship.thrust = 1;
891 }
892 else
893 {
894 if ((counter % 2) == 0)
895 {
896 tuxship.xspeed = tuxship.xspeed * TUXSHIP_DECEL;
897 tuxship.yspeed = tuxship.yspeed * TUXSHIP_DECEL;
898 tuxship.thrust = 0;
899 }
900 }
901
902 tuxship.x = tuxship.x + tuxship.xspeed;
903 tuxship.y = tuxship.y + tuxship.yspeed;
904
905 /*************** Wrap ship around edges of screen ****************/
906
907 if(tuxship.x >= (screen->w))
908 tuxship.x = tuxship.x - (screen->w);
909 else if (tuxship.x < -60)
910 tuxship.x = tuxship.x + (screen->w);
911
912 if(tuxship.y >= (screen->h))
913 tuxship.y = tuxship.y - (screen->h);
914 else if (tuxship.y < -60)
915 tuxship.y = tuxship.y + (screen->h);
916
917 /**************** Shoot ***************/
918 if(shoot_pressed)
919 {
920 FF_add_laser();
921 shoot_pressed=0;
922 }
923 }
924
925
FF_handle_asteroids(void)926 static void FF_handle_asteroids(void){
927
928 SDL_Surface* surf;
929 int i, found=0;
930 for (i = 0; i < MAX_ASTEROIDS; i++){
931 if (asteroid[i].alive)
932 {
933
934 found=1;
935
936 /*************** Rotate asteroid ****************/
937
938 asteroid[i].angle = (asteroid[i].angle + asteroid[i].angle_speed);
939
940 // Wrap rotation angle...
941
942 if (asteroid[i].angle < 0)
943 asteroid[i].angle = asteroid[i].angle + 360;
944 else if (asteroid[i].angle >= 360)
945 asteroid[i].angle = asteroid[i].angle - 360;
946
947 /**************Move the astroids ****************/
948 surf=get_asteroid_image(asteroid[i].size,asteroid[i].angle);
949
950 asteroid[i].rx = asteroid[i].rx + asteroid[i].xspeed;
951 asteroid[i].ry = asteroid[i].ry + asteroid[i].yspeed;
952
953 asteroid[i].x = (asteroid[i].rx - (surf->w/2));
954 asteroid[i].y = (asteroid[i].ry - (surf->h/2));
955
956 // Wrap asteroid around edges of screen:
957
958 if (asteroid[i].x >= (screen->w))
959 asteroid[i].rx = asteroid[i].rx - (screen->w);
960 else if (asteroid[i].x < 0)
961 asteroid[i].rx = asteroid[i].rx + (screen->w);
962
963 if (asteroid[i].y >= (screen->h))
964 asteroid[i].ry = asteroid[i].ry - (screen->h);
965 else if (asteroid[i].ry < 0)
966 asteroid[i].ry = asteroid[i].ry + (screen->h);
967 /**************Center Asteroids**************/
968
969 asteroid[i].centerx=((surf->w)/2)+(asteroid[i].x-5);
970 asteroid[i].centery=((surf->h)/2)+(asteroid[i].y-5);
971
972 /*************** Collisions! ****************/
973
974 if(AsteroidColl(surf->w, surf->h, asteroid[i].x, asteroid[i].y, tuxship.centerx, tuxship.centery) &&
975 !(bonus == TB_CLOAKING && bonus_time > 0))
976 {
977 if(!tuxship.hurt)
978 {
979 asteroid[i].xdead=asteroid[i].centerx;
980 asteroid[i].ydead=asteroid[i].centery;
981
982 if(!(bonus == TB_FORCEFIELD && bonus_time > 0)) {
983 tuxship.lives--;
984 tuxship.hurt=1;
985 tuxship.hurt_count=50;
986 }
987 FF_destroy_asteroid(i, tuxship.xspeed, tuxship.yspeed);
988 playsound(SND_EXPLOSION);
989
990 }
991 }
992 }
993 }
994 if(!found)
995 FF_add_level();
996 }
997
FF_handle_answer(void)998 static void FF_handle_answer(void)
999 {
1000
1001 num = (digits[0] * 100 +
1002 digits[1] * 10 +
1003 digits[2]);
1004 /* negative answer support DSB */
1005 if (neg_answer_picked)
1006 {
1007 num = -num;
1008 }
1009
1010 if (!doing_answer)
1011 {
1012 return;
1013 }
1014
1015 doing_answer = 0;
1016
1017 neg_answer_picked = 0;
1018
1019 }
1020
get_asteroid_image(int size,int angle)1021 static SDL_Surface* get_asteroid_image(int size,int angle)
1022 {
1023 if (size == 0)
1024 return IMG_asteroids1[angle/DEG_PER_ROTATION];
1025 else
1026 return IMG_asteroids2[angle/DEG_PER_ROTATION];
1027 }
1028
FF_draw(void)1029 static void FF_draw(void){
1030
1031 int i, offset;
1032 int xnum, ynum;
1033 char str[64];
1034 SDL_Surface* surf;
1035 SDL_Rect dest;
1036
1037 SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
1038
1039 /************ Draw Background ***************/
1040
1041 FF_draw_bkgr();
1042
1043 /******************* Draw laser *************************/
1044 for (i=0;i<MAX_LASER;i++){
1045 if(laser[i].alive)
1046 {
1047 if(laser[i].count>0)
1048 {
1049 laser[i].count--;
1050 laser[i].x=laser[i].x+tuxship.xspeed;
1051 laser[i].y=laser[i].y+tuxship.yspeed;
1052 laser[i].destx=laser[i].destx+tuxship.xspeed;
1053 laser[i].desty=laser[i].desty+tuxship.yspeed;
1054 draw_line(laser[i].x, laser[i].y, laser[i].destx, laser[i].desty,
1055 laser[i].count*laser_coeffs[laser[i].n][0], laser[i].count*laser_coeffs[laser[i].n][1], laser[i].count*laser_coeffs[laser[i].n][2]);
1056 } else if (laser[i].count <= 0)
1057 {
1058 laser[i].alive=0;
1059 }
1060 }
1061 }
1062 /*************** Draw Ship ******************/
1063
1064 if(!tuxship.hurt || (tuxship.hurt && tuxship.hurt_count%2==0)){
1065 dest.x = (tuxship.x - (IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->w/2));
1066 dest.y = (tuxship.y - (IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->h/2));
1067 dest.w = IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->w;
1068 dest.h = IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->h;
1069
1070 //Change the image based on if the rocket is thrusting
1071 //Google code in task
1072
1073 if(!tuxship.thrust) {
1074 SDL_Surface **_IMG_ship = bonus == TB_CLOAKING && bonus_time>0 ? IMG_tuxship_cloaked : IMG_tuxship;
1075 SDL_BlitSurface(_IMG_ship[tuxship.angle/DEG_PER_ROTATION], NULL, screen, &dest);
1076 } else {
1077 SDL_Surface **_IMG_ship = bonus == TB_CLOAKING && bonus_time>0 ? IMG_tuxship_thrust_cloaked : IMG_tuxship_thrust;
1078 SDL_BlitSurface(_IMG_ship[tuxship.angle/DEG_PER_ROTATION], NULL, screen, &dest);
1079 }
1080
1081
1082
1083 if(bonus == TB_FORCEFIELD && bonus_time > 0) {
1084 SDL_Rect tmp = {tuxship.x - images[IMG_FORCEFIELD]->w/2, tuxship.y - images[IMG_FORCEFIELD]->h/2};
1085 SDL_BlitSurface(images[IMG_FORCEFIELD], NULL, screen, &tmp);
1086 }
1087 }
1088
1089 /************* Draw Asteroids ***************/
1090 for(i=0; i<MAX_ASTEROIDS; i++){
1091 if(asteroid[i].alive>0){
1092
1093 xnum=0;
1094 ynum=0;
1095
1096 dest.x = asteroid[i].x;
1097 dest.y = asteroid[i].y;
1098
1099 surf=get_asteroid_image(asteroid[i].size,asteroid[i].angle);
1100
1101 dest.w = surf->w;
1102 dest.h = surf->h;
1103
1104 SDL_BlitSurface(surf, NULL, screen, &dest);
1105
1106 // Wrap the numbers of the asteroids
1107 if((asteroid[i].centery)>23 && (asteroid[i].centery)<screen->h)
1108 {
1109 if((asteroid[i].centerx)>0 && (asteroid[i].centerx)<screen->w)
1110 {
1111 xnum=asteroid[i].centerx-3;
1112 ynum=asteroid[i].centery;
1113 }
1114 else if((asteroid[i].centerx)<=0){
1115 xnum=20;
1116 ynum=asteroid[i].centery;
1117 }
1118 else if((asteroid[i].centerx)<=screen->w){
1119 xnum=screen->w-20;
1120 ynum=asteroid[i].centery;
1121 }
1122 }
1123 else if((asteroid[i].centery)<=23)
1124 {
1125 xnum=asteroid[i].centerx;
1126 ynum=23;
1127 }
1128 else if((asteroid[i].centery)>=screen->h)
1129 {
1130 xnum=asteroid[i].centerx;
1131 ynum=screen->h-7;
1132 }
1133
1134 //Draw Numbers
1135 if(FF_game==FACTOROIDS_GAME)
1136 {
1137 sprintf(str, "%.1d", asteroid[i].fact_number);
1138 draw_nums(str, xnum, ynum, &white);
1139 }
1140 else if (FF_game==FRACTIONS_GAME)
1141 {
1142 sprintf(str, "%d", asteroid[i].a);
1143 draw_nums(str, xnum, ynum, &white);
1144 draw_line(xnum, ynum + 4, xnum + 30, ynum + 4,
1145 255, 255, 255);
1146 sprintf(str, "%d", asteroid[i].b);
1147 draw_nums(str, xnum, ynum + 35, &white);
1148 }
1149 }
1150 }
1151 /*************** Draw Steam ***************/
1152 for(i=0; i<MAX_ASTEROIDS; i++)
1153 {
1154 if(asteroid[i].isdead) {
1155 dest.x = asteroid[i].xdead;
1156 dest.y = asteroid[i].ydead;
1157 SDL_BlitSurface(images[IMG_STEAM1+asteroid[i].countdead], NULL, screen, &dest);
1158 if(bonus == TB_POWERBOMB && bonus_time > 0)
1159 draw_line(asteroid[i].x, asteroid[i].y, tuxship.x, tuxship.y,
1160 (5 - asteroid[i].countdead)*4*laser_coeffs[digits[1]*10+digits[2]][0],
1161 (5 - asteroid[i].countdead)*4*laser_coeffs[digits[1]*10+digits[2]][1],
1162 (5 - asteroid[i].countdead)*4*laser_coeffs[digits[1]*10+digits[2]][2]);
1163 }
1164
1165
1166 if(asteroid[i].isdead) {
1167 dest.x = asteroid[i].xdead;
1168 dest.y = asteroid[i].ydead;
1169 SDL_BlitSurface(images[IMG_STEAM1+asteroid[i].countdead], NULL, screen, &dest);
1170 asteroid[i].countdead++;
1171 if(asteroid[i].countdead > 5)
1172 {
1173 asteroid[i].isdead = 0;
1174 asteroid[i].countdead = 0;
1175 }
1176 }
1177 }
1178
1179 /* Draw wave: */
1180 if (1)//Opts_BonusCometInterval())
1181 offset = images[IMG_EXTRA_LIFE]->w + 5;
1182 else
1183 offset = 0;
1184
1185 dest.x = offset;
1186
1187 dest.y = 0;
1188 dest.w = images[IMG_WAVE]->w;
1189 dest.h = images[IMG_WAVE]->h;
1190
1191 SDL_BlitSurface(images[IMG_WAVE], NULL, screen, &dest);
1192
1193 sprintf(str, "%d", wave);
1194 draw_numbers(str, offset+images[IMG_WAVE]->w + (images[IMG_NUMBERS]->w / 10), 0);
1195
1196 /* Draw "score" label: */
1197 dest.x = (screen->w - ((images[IMG_NUMBERS]->w/10) * 7) -
1198 images[IMG_SCORE]->w -
1199 images[IMG_STOP]->w - 5);
1200 dest.y = 0;
1201 dest.w = images[IMG_SCORE]->w;
1202 dest.h = images[IMG_SCORE]->h;
1203
1204 SDL_BlitSurface(images[IMG_SCORE], NULL, screen, &dest);
1205
1206 sprintf(str, "%.6d", score);
1207 draw_numbers(str,
1208 screen->w - ((images[IMG_NUMBERS]->w / 10) * 6) - images[IMG_STOP]->w - 5,
1209 0);
1210
1211 /* Draw stop button: */
1212 // if (!help_controls.x_is_blinking || (frame % 10 < 5)) {
1213 dest.x = (screen->w - images[IMG_STOP]->w);
1214 dest.y = 0;
1215 dest.w = images[IMG_STOP]->w;
1216 dest.h = images[IMG_STOP]->h;
1217
1218 SDL_BlitSurface(images[IMG_STOP], NULL, screen, &dest);
1219 // }
1220
1221 /************* Draw pre answer ************/
1222
1223
1224 if(screen->w < 800 && screen->h < 600)
1225 {
1226 sprintf(str, "%.3d", num);
1227 draw_numbers(str, ((screen->w)/2) - 50, (screen->h) - 30);
1228 }
1229 else
1230 {
1231 FF_draw_led_console();
1232 draw_console_image(tux_img);
1233 }
1234
1235 /************** Draw lives ***************/
1236 dest.y = screen->h;
1237 dest.x = 0;
1238
1239 for(i = 1; i <= tuxship.lives; i++)
1240 {
1241 if(tuxship.lives <= 5)
1242 {
1243 dest.y = dest.y - (IMG_lives_ship->h);
1244 SDL_BlitSurface(IMG_lives_ship, NULL, screen, &dest);
1245 }
1246 else if(tuxship.lives > 4)
1247 {
1248 dest.y = screen->h - (IMG_lives_ship->h);
1249 SDL_BlitSurface(IMG_lives_ship, NULL, screen, &dest);
1250 sprintf(str, "%d", tuxship.lives);
1251 draw_numbers(str, 10, (screen->h) - 30);
1252 }
1253 }
1254
1255 /*** Draw Bonus Indicator ***/
1256 static int blink = 0;
1257 if(bonus_time == 0)
1258 blink = 0;
1259 else if(bonus_time - SDL_GetTicks() > 3000)
1260 blink = 5;
1261 else
1262 blink = (blink + 1) % 10;
1263 if(bonus != -1 && blink>4) {
1264 SDL_Surface *indicator = images[bonus_img_ids[bonus]];
1265 SDL_Rect pos = {screen->w - indicator->w, screen->h - indicator->h};
1266 SDL_BlitSurface(indicator, NULL, screen, &pos);
1267 }
1268 }
1269
FF_DrawButton(int img_id,enum BUTTON_TYPE type,int x,int y)1270 static void FF_DrawButton(int img_id, enum BUTTON_TYPE type, int x, int y)
1271 {
1272 SDL_Rect rect, scr;
1273 rect.y = 0;
1274 rect.w = BUTTONW;
1275 rect.h = BUTTONH;
1276
1277 scr.x = x;
1278 scr.y = y;
1279
1280 if(type == ACTIVE)
1281 {
1282 rect.x = 0;
1283 SDL_BlitSurface(images[img_id], &rect, screen, &scr);
1284 }
1285 else if(type == SELECTED)
1286 {
1287 rect.x = BUTTONW;
1288 SDL_BlitSurface(images[img_id], &rect, screen, &scr);
1289 }
1290 else if(type == PRESSED)
1291 {
1292 rect.x = BUTTONW * 2;
1293 SDL_BlitSurface(images[img_id], &rect, screen, &scr);
1294 }
1295 else if(type == DISABLED)
1296 {
1297 rect.x = BUTTONW * 3;
1298 SDL_BlitSurface(images[img_id], &rect, screen, &scr);
1299 }
1300 }
1301
FF_ButtonInit(void)1302 static void FF_ButtonInit(void)
1303 {
1304
1305 buttons[0].img_id = IMG_BUTTON2;
1306 buttons[0].x = screen->w/2 - BUTTON2_X;
1307 buttons[0].y = screen->h - BUTTON2_Y;
1308 buttons[0].prime = 2;
1309
1310 buttons[1].img_id = IMG_BUTTON3;
1311 buttons[1].x = screen->w/2 - BUTTON3_X;
1312 buttons[1].y = screen->h - BUTTON3_Y;
1313 buttons[1].prime = 3;
1314
1315 buttons[2].img_id = IMG_BUTTON5;
1316 buttons[2].x = screen->w/2 - BUTTON5_X;
1317 buttons[2].y = screen->h - BUTTON5_Y;
1318 buttons[2].prime = 5;
1319
1320 buttons[3].img_id = IMG_BUTTON7;
1321 buttons[3].x = screen->w/2 + BUTTON7_X;
1322 buttons[3].y = screen->h - BUTTON7_Y;
1323 buttons[3].prime = 7;
1324
1325 buttons[4].img_id = IMG_BUTTON11;
1326 buttons[4].x = screen->w/2 + BUTTON11_X;
1327 buttons[4].y = screen->h - BUTTON11_Y;
1328 buttons[4].prime = 11;
1329
1330 buttons[5].img_id = IMG_BUTTON13;
1331 buttons[5].x = screen->w/2 + BUTTON13_X;
1332 buttons[5].y = screen->h - BUTTON13_Y;
1333 buttons[5].prime = 13;
1334
1335 }
1336
FF_DrawButtonLayout(void)1337 static void FF_DrawButtonLayout(void)
1338 {
1339 int i;
1340 enum BUTTON_TYPE type;
1341
1342 FF_ButtonInit();
1343
1344 for(i=0; i<6; i++)
1345 {
1346 if(i < wave)
1347 {
1348 if(button_pressed && num==buttons[i].prime)
1349 {
1350 type=PRESSED;
1351 }
1352 else if(num==buttons[i].prime)
1353 {
1354 type=SELECTED;
1355 }
1356 else
1357 {
1358 type = ACTIVE;
1359 }
1360 }
1361 else
1362 {
1363 type = DISABLED;
1364 }
1365 FF_DrawButton(buttons[i].img_id,type,buttons[i].x,buttons[i].y);
1366 }
1367 }
1368
FF_CockpitTux(int prime)1369 static int FF_CockpitTux( int prime )
1370 {
1371 switch( prime )
1372 {
1373 case 2:
1374 return IMG_TUX1;
1375 case 3:
1376 return IMG_TUX2;
1377 case 5:
1378 return IMG_TUX3;
1379 case 7:
1380 return IMG_TUX4;
1381 case 11:
1382 return IMG_TUX5;
1383 case 13:
1384 return IMG_TUX6;
1385 default:
1386 return IMG_TUX1;
1387 }
1388 }
1389
1390 /*Modified from game.c*/
FF_draw_led_console(void)1391 void FF_draw_led_console(void)
1392 {
1393 int i;
1394 SDL_Rect src, dest;
1395 int y;
1396
1397
1398 if(FF_game == FACTOROIDS_GAME)
1399 {
1400 draw_console_image(IMG_COCKPIT);
1401 FF_DrawButtonLayout();
1402 }
1403 else
1404 {
1405 /* draw new console image with "monitor" for LED numbers: */
1406 draw_console_image(IMG_CONSOLE_LED);
1407
1408 /* set y to draw LED numbers into Tux's "monitor": */
1409 y = (screen->h
1410 - images[IMG_CONSOLE_LED]->h
1411 + 4); /* "monitor" has 4 pixel margin */
1412
1413 /* begin drawing so as to center display depending on whether minus */
1414 /* sign needed (4 digit slots) or not (3 digit slots) DSB */
1415 if (MC_GetOpt(ALLOW_NEGATIVES) )
1416 dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 4) / 2);
1417 else
1418 dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 3) / 2);
1419
1420 for (i = -1; i < MC_MAX_DIGITS; i++) /* -1 is special case to allow minus sign */
1421 /* with minimal modification of existing code DSB */
1422 {
1423 if (-1 == i)
1424 {
1425 if (MC_GetOpt(ALLOW_NEGATIVES))
1426 {
1427 if (neg_answer_picked)
1428 src.x = (images[IMG_LED_NEG_SIGN]->w) / 2;
1429 else
1430 src.x = 0;
1431 src.y = 0;
1432 src.w = (images[IMG_LED_NEG_SIGN]->w) / 2;
1433 src.h = images[IMG_LED_NEG_SIGN]->h;
1434
1435 dest.y = y;
1436 dest.w = src.w;
1437 dest.h = src.h;
1438
1439 SDL_BlitSurface(images[IMG_LED_NEG_SIGN], &src, screen, &dest);
1440 /* move "cursor" */
1441 dest.x += src.w;
1442 }
1443 }
1444 else
1445 {
1446 src.x = digits[i] * ((images[IMG_LEDNUMS]->w) / 10);
1447 src.y = 0;
1448 src.w = (images[IMG_LEDNUMS]->w) / 10;
1449 src.h = images[IMG_LEDNUMS]->h;
1450
1451 /* dest.x already set */
1452 dest.y = y;
1453 dest.w = src.w;
1454 dest.h = src.h;
1455
1456 SDL_BlitSurface(images[IMG_LEDNUMS], &src, screen, &dest);
1457 /* move "cursor" */
1458 dest.x += src.w;
1459 }
1460 }
1461 }
1462 }
1463
1464 /* Draw image at lower center of screen: */
draw_console_image(int i)1465 void draw_console_image(int i)
1466 {
1467 SDL_Rect dest;
1468
1469 dest.x = (screen->w - images[i]->w) / 2;
1470 dest.y = (screen->h - images[i]->h);
1471 dest.w = images[i]->w;
1472 dest.h = images[i]->h;
1473
1474 SDL_BlitSurface(images[i], NULL, screen, &dest);
1475 }
1476
FF_draw_bkgr(void)1477 static void FF_draw_bkgr(void)
1478 {
1479
1480 SDL_BlitSurface(current_bkgd(), NULL, screen, NULL);
1481 //if(bgSrc.y>bkg_h)
1482 // SDL_BlitSurface(images[BG_STARS], NULL, screen, &bgScreen);
1483
1484 }
1485
1486 /*Tree rectangle vs a point collitions
1487 returns 1 if the collitions is detected
1488 and 0 if not*/
1489
AsteroidColl(int astW,int astH,int astX,int astY,int x,int y)1490 int AsteroidColl(int astW,int astH,int astX,int astY,
1491 int x, int y)
1492 {
1493 int astWq=astW/8;
1494 int astHq=astH/8;
1495 int x1, y1, x2, y2;
1496
1497 x1=astX+astWq*3;
1498 y1=astY;
1499
1500 x2=astX+astWq*6;
1501 y2=astY+astH;
1502
1503 if(x>x1 && x<x2 && y>y1 && y<y2)
1504 return 1;
1505
1506 x1=astX;
1507 y1=astY+astHq*3;
1508
1509 x2=astW;
1510 y2=astY+astHq*6;
1511
1512 if(x>x1 && x<x2 && y>y1 && y<y2)
1513 return 1;
1514
1515 x1=astX+astWq;
1516 y1=astY+astHq;
1517
1518 x2=astX+astWq*7;
1519 y2=astY+astHq*7;
1520
1521 if(x>x1 && x<x2 && y>y1 && y<y2)
1522 return 1;
1523
1524 return 0;
1525 }
1526
1527 // Returns x % w but in the range [-w/2, w/2]
modwrap(int x,int w)1528 static int modwrap(int x,int w)
1529 {
1530 x = x % w;
1531 if (x > (w/2))
1532 x -= w;
1533 else if (x < -(w/2))
1534 x += w;
1535 return x;
1536 }
1537
FF_add_level(void)1538 static void FF_add_level(void)
1539 {
1540 int i = 0;
1541 int x, y, xvel, yvel, dx, dy;
1542 int ok;
1543 int width;
1544 int safety_radius2, speed2;
1545 int max_speed;
1546 Uint32 timer = 0;
1547 SDL_Rect rect;
1548
1549 wave++;
1550
1551 // New lives per wave!
1552 if (wave%5==0)
1553 {
1554 tuxship.lives++;
1555 }
1556
1557 // Clear all bonuses obtained in a wave
1558 bonus_time = BONUS_NOTUSED; // Reset the timer for the bonus
1559 // And now reward a new bonus
1560 bonus = rand()%TB_SIZE;
1561
1562 // Set active number to newly added prime
1563 int wave_i = wave - 1;
1564 if(wave>=PRIME_MAX_LIMIT) {
1565 wave_i = PRIME_MAX_LIMIT - 1;
1566 }
1567 int c_prime = prime_numbers[wave_i];
1568 digits[2] = c_prime % 10;
1569 digits[1] = c_prime / 10;
1570
1571 //Limit the new asteroids
1572 if(NUM_ASTEROIDS<MAX_ASTEROIDS)
1573 NUM_ASTEROIDS=NUM_ASTEROIDS+wave;
1574 else
1575 NUM_ASTEROIDS=MAX_ASTEROIDS;
1576
1577 width = screen->w;
1578 if (screen->h < width)
1579 width = screen->h;
1580
1581 // Define the "safety radius" as one third of the screen width
1582 safety_radius2 = width/3;
1583 safety_radius2 = safety_radius2*safety_radius2; // the square distance
1584
1585 // Define the max speed in terms of the screen width
1586 max_speed = width/100;
1587 if (max_speed == 0)
1588 max_speed = 1;
1589
1590 for (i=0; i<MAX_ASTEROIDS; i++)
1591 asteroid[i].alive=0;
1592 for (i=0; i<NUM_ASTEROIDS && NUM_ASTEROIDS<MAX_ASTEROIDS; i++){
1593 // Generate the new position, avoiding the location of the ship
1594 ok = 0;
1595 while (!ok) {
1596 x = rand()%(screen->w);
1597 y = rand()%(screen->h);
1598 dx = modwrap(x - tuxship.x,screen->w);
1599 dy = modwrap(y - tuxship.y,screen->h);
1600 if (dx*dx + dy*dy > safety_radius2)
1601 ok = 1;
1602 }
1603 // Generate the new speed, making none of them stationary but none
1604 // of them too fast
1605 ok = 0;
1606 while (!ok) {
1607 xvel = rand()%(2*max_speed+1) - max_speed;
1608 yvel = rand()%(2*max_speed+1) - max_speed;
1609 speed2 = xvel*xvel + yvel*yvel;
1610 if (speed2 != 0 && speed2 < max_speed*max_speed)
1611 ok = 1;
1612 }
1613 if(FF_game == FACTOROIDS_GAME){
1614 FF_add_asteroid(x,y,
1615 xvel,yvel,
1616 rand()%2,
1617 rand()%360, rand()%3,
1618 generatenumber(wave),
1619 0, 0,
1620 1);
1621 }
1622 else if(FF_game==FRACTIONS_GAME){
1623 FF_add_asteroid(x,y,
1624 xvel,yvel,
1625 rand()%2,
1626 rand()%360, rand()%3,
1627 0,
1628 (rand()%(31+(wave*2))), (rand()%(80+(wave*wave))),
1629 1);
1630 }
1631 }
1632
1633 if(wave != 1)
1634 {
1635 while(i < 35)
1636 {
1637 i++;
1638 rect.x=(screen->w/2)-(images[IMG_GOOD]->w/2);
1639 rect.y=(screen->h/2)-(images[IMG_GOOD]->h/2);
1640 FF_draw();
1641 SDL_BlitSurface(images[IMG_GOOD],NULL,screen,&rect);
1642 SDL_Flip(screen);
1643 T4K_Throttle(MS_PER_FRAME, &timer);
1644 }
1645 FF_LevelMessage();
1646 }
1647 }
1648
FF_over(int game_status)1649 static int FF_over(int game_status)
1650 {
1651 Uint32 timer = 0;
1652 SDL_Rect dest_message;
1653 SDL_Event event;
1654
1655
1656 /* TODO: need better "victory" screen with animation, special music, etc., */
1657 /* as well as options to review missed questions, play again using missed */
1658 /* questions as question list, etc. */
1659 /* TODO: also, some of these cases just redraw the background on every */
1660 /* frame with nothing else - just copy-and-pasted code without much */
1661 /* further attention. */
1662
1663 /* Turn mouse cursor back on before we go back to menus: */
1664 SDL_ShowCursor(1);
1665 SDL_WM_GrabInput(SDL_GRAB_OFF);
1666
1667
1668 switch (game_status)
1669 {
1670 case FF_OVER_WON:
1671 {
1672 int looping = 1;
1673
1674 DEBUGMSG(debug_factoroids, "Loop exited with GAME_OVER_WON\n");
1675
1676 /* set up victory message: */
1677 dest_message.x = (screen->w - images[IMG_GAMEOVER_WON]->w) / 2;
1678 dest_message.y = (screen->h - images[IMG_GAMEOVER_WON]->h) / 2;
1679 dest_message.w = images[IMG_GAMEOVER_WON]->w;
1680 dest_message.h = images[IMG_GAMEOVER_WON]->h;
1681
1682 do
1683 {
1684 //frame++;
1685
1686 /* draw flashing victory message: */
1687 //if (((frame / 2) % 4))
1688 //{
1689 SDL_BlitSurface(images[IMG_GAMEOVER_WON], NULL, screen, &dest_message);
1690 //}
1691
1692
1693 SDL_Flip(screen);
1694
1695 while (1)
1696 {
1697 SDL_PollEvent(&event);
1698 if (event.type == SDL_QUIT
1699 || event.type == SDL_KEYDOWN
1700 || event.type == SDL_MOUSEBUTTONDOWN)
1701 {
1702 looping = 0;
1703 break;
1704 }
1705 T4K_Throttle(MS_PER_FRAME, &timer);
1706 }
1707 T4K_Throttle(MS_PER_FRAME, &timer);
1708 }
1709 while (looping);
1710 break;
1711 }
1712
1713 case FF_OVER_ERROR:
1714 {
1715 DEBUGMSG(debug_factoroids, "Loop exited with FF_OVER_ERROR\n");
1716 }
1717 case FF_OVER_LOST:
1718 case FF_OVER_OTHER:
1719 {
1720 int looping = 1;
1721
1722 DEBUGMSG(debug_factoroids, "Loop exited with FF_OVER_LOST or FF_OVER_OTHER\n");
1723
1724 /* set up GAMEOVER message: */
1725 dest_message.x = (screen->w - images[IMG_GAMEOVER]->w) / 2;
1726 dest_message.y = (screen->h - images[IMG_GAMEOVER]->h) / 2;
1727 dest_message.w = images[IMG_GAMEOVER]->w;
1728 dest_message.h = images[IMG_GAMEOVER]->h;
1729
1730 do
1731 {
1732 //frame++;
1733 SDL_BlitSurface(images[IMG_GAMEOVER], NULL, screen, &dest_message);
1734 SDL_Flip(screen);
1735
1736 while (1)
1737 {
1738 SDL_PollEvent(&event);
1739 if (event.type == SDL_QUIT
1740 || event.type == SDL_KEYDOWN
1741 || event.type == SDL_MOUSEBUTTONDOWN)
1742 {
1743 looping = 0;
1744 break;
1745 }
1746 T4K_Throttle(MS_PER_FRAME, &timer);
1747 }
1748 T4K_Throttle(MS_PER_FRAME, &timer);
1749 }
1750 while (looping);
1751
1752 break;
1753 }
1754
1755 case FF_OVER_ESCAPE:
1756 {
1757 DEBUGMSG(debug_factoroids, "Loop exited with FF_OVER_ESCAPE\n");
1758 break;
1759 }
1760
1761 case FF_OVER_SDL_QUIT:
1762 {
1763 DEBUGMSG(debug_factoroids, "Loop exited with FF_OVER_SDL_QUIT\n");
1764 break;
1765 }
1766
1767 default:
1768 {
1769 DEBUGMSG(debug_factoroids, "Loop exited with unrecognized status value: %dn", game_status);
1770 }
1771 }
1772
1773 FF_exit_free();
1774
1775 /* Save score in case needed for high score table: */
1776 Opts_SetLastScore(score);
1777
1778 /* Return the chosen command: */
1779 if (GAME_OVER_WINDOW_CLOSE == game_status)
1780 {
1781 /* program exits: */
1782 FF_exit_free();;
1783 return 1;
1784 }
1785 else
1786 {
1787 /* return to title() screen: */
1788 return 0;
1789 }
1790 }
1791
1792
FF_exit_free()1793 static void FF_exit_free()
1794 {
1795 int i = 0;
1796
1797 free(asteroid);
1798
1799 for(i = 0; i < NUM_OF_ROTO_IMGS; i++)
1800 {
1801 if (IMG_tuxship[i])
1802 {
1803 SDL_FreeSurface(IMG_tuxship[i]);
1804 IMG_tuxship[i] = NULL;
1805 }
1806 if (IMG_tuxship_thrust[i])
1807 {
1808 SDL_FreeSurface(IMG_tuxship_thrust[i]);
1809 IMG_tuxship_thrust[i] = NULL;
1810 }
1811 if (IMG_tuxship_cloaked[i])
1812 {
1813 SDL_FreeSurface(IMG_tuxship_cloaked[i]);
1814 IMG_tuxship_cloaked[i] = NULL;
1815 }
1816 if (IMG_tuxship_thrust_cloaked[i])
1817 {
1818 SDL_FreeSurface(IMG_tuxship_thrust_cloaked[i]);
1819 IMG_tuxship_thrust_cloaked[i] = NULL;
1820 }
1821 if (IMG_asteroids1[i])
1822 {
1823 SDL_FreeSurface(IMG_asteroids1[i]);
1824 IMG_asteroids1[i] = NULL;
1825 }
1826 if (IMG_asteroids2[i])
1827 {
1828 SDL_FreeSurface(IMG_asteroids2[i]);
1829 IMG_asteroids2[i] = NULL;
1830 }
1831 }
1832
1833 if (IMG_lives_ship)
1834 {
1835 SDL_FreeSurface(IMG_lives_ship);
1836 IMG_lives_ship = NULL;
1837 }
1838
1839 // SDL_FreeSurface(*IMG_asteroids1);
1840 // SDL_FreeSurface(*IMG_asteroids2);
1841 // SDL_FreeSurface(*IMG_tuxship);
1842
1843 if (bkgd)
1844 {
1845 SDL_FreeSurface(bkgd);
1846 bkgd = NULL;
1847 }
1848 if (scaled_bkgd)
1849 {
1850 SDL_FreeSurface(scaled_bkgd);
1851 scaled_bkgd = NULL;
1852 }
1853
1854 /* Resume "normal" settings when we leave:
1855 */
1856 SDL_ShowCursor(1);
1857 SDL_WM_GrabInput(SDL_GRAB_OFF);
1858 }
1859
1860 /******************* Math Funcs ***********************/
1861
1862 /* Return 1 if the number is prime and 0 if its not */
is_prime(int num)1863 int is_prime(int num)
1864 {
1865 int i;
1866 if (num==0 || num==1 || num==-1) return 1;
1867 else if (num > 0)
1868 {
1869
1870 for(i = 2; i < num; i++)
1871 {
1872 if(num%i == 0) return 0;
1873 }
1874 }
1875 else if (num < 0)
1876 {
1877 for(i = 2; i > num; i--)
1878 {
1879 if(num%i == 0) return 0;
1880 }
1881 }
1882 return 1;
1883 }
1884
is_simplified(int a,int b)1885 int is_simplified(int a, int b)
1886 {
1887 int i;
1888 for(i=2; i<1000; i++)
1889 if(((a%i)==0)&&((b%i)==0))
1890 return 0;
1891 return 1;
1892 }
1893 /*** Fast cos by Bill***/
1894
fast_cos(int angle)1895 int fast_cos(int angle)
1896 {
1897 angle = (angle % 45);
1898
1899 if (angle < 12)
1900 return(trig[angle]);
1901 else if (angle < 23)
1902 return(-trig[10 - (angle - 12)]);
1903 else if (angle < 34)
1904 return(-trig[angle - 22]);
1905 else
1906 return(trig[45 - angle]);
1907 }
1908
1909
1910 /*** Sine based on fast cosine..., by Bill ***/
1911
fast_sin(int angle)1912 int fast_sin(int angle)
1913 {
1914 return(- fast_cos((angle + 11) % 45));
1915 }
1916
1917 /*** fact_number generator by aviraldg ***/
1918
generatenumber(int wave)1919 static int generatenumber(int wave) {
1920 if(wave > PRIME_MAX_LIMIT) wave = PRIME_MAX_LIMIT;
1921 int n=1, i;
1922 for(i=0; i<wave; i++)
1923 n *= pow(prime_numbers[i], rand()%prime_power_limit[i]);
1924 /* If we somehow got a bogus number, try again: */
1925 if(validate_number(n, wave))
1926 return n;
1927 else
1928 {
1929 if (n > 1) /* 1 can be generated without bugs and is innocuous */
1930 DEBUGMSG(debug_factoroids, "generatenumber() - wrn - invalid number: %d\n", n);
1931 return generatenumber(wave);
1932 }
1933 }
1934
1935 /*** For some reason, we have sometimes seen rocks with numbers */
1936 /*** that are not multiples of the desired primes. Here we */
1937 /*** factor those primes out and see what's left. */
1938 /*** Returns 0 (false) if number is invalid. DSB */
validate_number(int num,int wave)1939 static int validate_number(int num, int wave)
1940 {
1941 int i = 0;
1942 if(num < 2)
1943 return 0;
1944 if(wave > PRIME_MAX_LIMIT)
1945 wave = PRIME_MAX_LIMIT;
1946 for(i = 0; i < wave; i++)
1947 {
1948 while(num % prime_numbers[i] == 0)
1949 num /= prime_numbers[i];
1950 }
1951
1952 /* If we aren't left with 1, the number is invalid: */
1953 if(num == 1)
1954 return 1;
1955 else
1956 return 0;
1957 }
1958
1959 //implementation of the powerbomb powerup
_tb_PowerBomb(int num)1960 void _tb_PowerBomb (int num) {
1961 int i;
1962
1963 for(i=0; i<MAX_ASTEROIDS; i++) {
1964 if(asteroid[i].alive == 1) {
1965 if((FF_game==FACTOROIDS_GAME && (asteroid[i].isprime && ((num==asteroid[i].fact_number)||(num==0)))) ||
1966 (FF_game==FRACTIONS_GAME && (asteroid[i].isprime && num==0))) {
1967 FF_destroy_asteroid(i, 0, 0);
1968 } else if((FF_game==FACTOROIDS_GAME && num > 1 && ((asteroid[i].fact_number%num)==0) && (num!=asteroid[i].fact_number)) ||
1969 (FF_game==FRACTIONS_GAME && num > 1 && ((asteroid[i].a%num)==0) && ((asteroid[i].b%num)==0) && (num!=asteroid[i].fact_number))) {
1970 FF_destroy_asteroid(i, 0, 0);
1971 }
1972 }
1973 }
1974 }
1975
1976 /******************* LASER FUNCTIONS *********************/
1977 /*Return -1 if no laser is available*/
FF_add_laser(void)1978 int FF_add_laser(void)
1979 {
1980 int i, k, zapIndex, zapScore;
1981 float ux, uy, s, smin,dx,dy,dx2, dy2, d2, thresh;
1982 int screensize;
1983 SDL_Surface *asteroid_image;
1984
1985 const float inside_factor = 0.9*0.9;
1986
1987 screensize = screen->w;
1988 if (screensize < screen->h)
1989 screensize = screen->h;
1990
1991 for(i=0; i<=MAX_LASER; i++)
1992 {
1993 if(laser[i].alive==0)
1994 {
1995 // Fire the laser
1996 laser[i].alive=1;
1997 laser[i].x=tuxship.centerx;
1998 laser[i].y=tuxship.centery;
1999 laser[i].angle=tuxship.angle;
2000 laser[i].count=15;
2001 laser[i].n = num;
2002
2003 ux = cos((float)laser[i].angle * DEG_TO_RAD);
2004 uy = -sin((float)laser[i].angle * DEG_TO_RAD);
2005 laser[i].destx = laser[i].x + (int)(ux * screensize);
2006 laser[i].desty = laser[i].y + (int)(uy * screensize);
2007
2008 // Check to see if it hits asteroids---we only check when it
2009 // just starts firing, "drift" later doesn't count!
2010 // We describe the laser path as p = p0 + s*u, where
2011 // p0 = (x0,y0) is the initial position vector (i.e., the ship)
2012 // u = (ux,uy) is the unit vector of the laser's direction
2013 // s (a scalar) is the distance along the laser (s >= 0)
2014 // With this parametrization, it's easy to calculate the
2015 // closest approach to the asteroid center, etc.
2016 zapIndex = -1; // keep track of the closest "hit" asteroid
2017 zapScore = 0;
2018 smin = 10*screensize;
2019
2020
2021 for (k=0; k<MAX_ASTEROIDS; k++)
2022 {
2023 if (!asteroid[k].alive)
2024 continue;
2025 asteroid_image = get_asteroid_image(asteroid[k].size,asteroid[k].angle);
2026 dx = asteroid[k].x + asteroid_image->w/2 - laser[i].x;
2027 dy = asteroid[k].y + asteroid_image->h/2 - laser[i].y;
2028 // Find distance along laser of closest approach to asteroid center
2029 s = dx*ux + dy*uy;
2030 if (s >= 0) // don't worry about it if it's in the opposite direction! (i.e., behind the ship)
2031 {
2032 // Find the distance to the asteroid center at closest approach
2033 dx2 = dx - s*ux;
2034 dy2 = dy - s*uy;
2035 d2 = dx2*dx2 + dy2*dy2;
2036 thresh = (asteroid_image->h)/2;
2037 thresh = thresh*thresh*inside_factor;
2038 if (d2 < thresh)
2039 {
2040 // The laser intersects the asteroid. Check to see if
2041 // the answer works
2042
2043 if( (FF_game==FACTOROIDS_GAME && (asteroid[k].isprime && ((num==asteroid[k].fact_number)||(num==0)))) ||
2044 (FF_game==FRACTIONS_GAME && (asteroid[k].isprime && num==0))
2045 )
2046 {
2047 // It's valid, check to see if it's closest
2048 if (s < smin)
2049 {
2050 // It's the closest yet examined but has not score
2051 smin = s;
2052 zapIndex = k;
2053 zapScore = 0;
2054 }
2055 }
2056 else if((FF_game==FACTOROIDS_GAME && num > 1 && ((asteroid[k].fact_number%num)==0) && (num!=asteroid[k].fact_number)) ||
2057 (FF_game==FRACTIONS_GAME && num > 1 && ((asteroid[k].a%num)==0) && ((asteroid[k].b%num)==0) && (num!=asteroid[k].fact_number)))
2058 {
2059 // It's valid, check to see if it's closest
2060 if (s < smin)
2061 {
2062 // It's the closest yet examined and has socre
2063 smin = s;
2064 zapIndex = k;
2065 zapScore = 1;
2066 }
2067 }
2068 }
2069 }
2070 }
2071
2072 // Handle the destruction, score, and extra lives
2073 if (zapIndex >= 0) // did we zap one?
2074 {
2075 asteroid[zapIndex].isdead = 1;
2076 laser[i].destx = laser[i].x + (int)(ux * smin);
2077 laser[i].desty = laser[i].y + (int)(uy * smin);
2078 FF_destroy_asteroid(zapIndex,2*ux,2*uy);
2079 playsound(SND_SIZZLE);
2080
2081 if (floor((float)score/100) < floor((float)(score+num)/100))
2082 tuxship.lives++;
2083 if(zapScore)
2084 {
2085 score += num;
2086 }
2087 }
2088 return 1;
2089 }
2090 }
2091 DEBUGMSG(debug_factoroids, "Laser could't be created!\n");
2092 return -1;
2093 }
2094
2095 /******************* ASTEROIDS FUNCTIONS *******************/
2096
2097
2098
FF_add_asteroid(int x,int y,int xspeed,int yspeed,int size,int angle,int angle_speed,int fact_number,int a,int b,int new_wave)2099 static int FF_add_asteroid(int x, int y, int xspeed, int yspeed, int size, int angle, int angle_speed, int fact_number, int a, int b, int new_wave)
2100 {
2101 int i;
2102 for(i=0; i<MAX_ASTEROIDS; i++){
2103 if(asteroid[i].alive==0)
2104 {
2105 asteroid[i].alive=1;
2106 asteroid[i].rx=x;
2107 asteroid[i].ry=y;
2108 asteroid[i].angle=angle;
2109 asteroid[i].angle_speed=angle_speed;
2110 asteroid[i].y=(asteroid[i].ry - (IMG_tuxship[asteroid[i].angle/DEG_PER_ROTATION]->h/2));
2111 asteroid[i].x=(asteroid[i].rx - (IMG_tuxship[asteroid[i].angle/DEG_PER_ROTATION]->w/2));
2112 asteroid[i].yspeed=yspeed;
2113 asteroid[i].xspeed=xspeed;
2114 asteroid[i].xdead = 0;
2115 asteroid[i].ydead = 0;
2116 asteroid[i].isdead = 0;
2117 asteroid[i].countdead = 0;
2118
2119 if(FF_game==FACTOROIDS_GAME){
2120
2121 if(!validate_number(fact_number, wave))
2122 {
2123 DEBUGMSG(debug_factoroids, "Invalid asteroid number: %d\n", fact_number);
2124 return -1;
2125 }
2126 //while(!asteroid[i].fact_number)
2127 // asteroid[i].fact_number=rand()%80;
2128
2129 asteroid[i].fact_number=fact_number;
2130 asteroid[i].isprime=is_prime(asteroid[i].fact_number);
2131
2132 }else if(FF_game==FRACTIONS_GAME){
2133
2134 asteroid[i].a=a;
2135 asteroid[i].b=b;
2136
2137 while(!asteroid[i].a)
2138 asteroid[i].a=rand()%80;
2139 while(!asteroid[i].b)
2140 asteroid[i].b=rand()%80;
2141
2142 asteroid[i].isprime=is_simplified(asteroid[i].a,asteroid[i].b);
2143 }
2144
2145 if(new_wave){
2146 if(tuxship.x-50<asteroid[i].x+80 &&
2147 tuxship.x+50>asteroid[i].x &&
2148 tuxship.y-50<asteroid[i].y+80 &&
2149 tuxship.y+50>asteroid[i].y &&
2150 tuxship.lives>0 &&
2151 asteroid[i].alive){
2152 asteroid[i].rx=asteroid[i].rx+300;
2153 asteroid[i].ry=asteroid[i].ry+300;
2154 }
2155 }
2156
2157 if(asteroid[i].isprime)
2158 {
2159 asteroid[i].size=0;
2160 asteroid[i].centerx=(images[IMG_ASTEROID1]->w/2)+asteroid[i].x;
2161 asteroid[i].centery=(images[IMG_ASTEROID1]->h/2)+asteroid[i].y;
2162 asteroid[i].radius=(images[IMG_ASTEROID1]->h/2);
2163
2164 }
2165 else if(!asteroid[i].isprime)
2166 {
2167 asteroid[i].size=1;
2168 asteroid[i].centerx=(images[IMG_ASTEROID2]->w/2)+asteroid[i].x;
2169 asteroid[i].centery=(images[IMG_ASTEROID2]->h/2)+asteroid[i].y;
2170 asteroid[i].radius=(images[IMG_ASTEROID1]->h/2);
2171 }
2172
2173 while (asteroid[i].xspeed==0)
2174 {
2175 asteroid[i].xspeed = ((rand() % 3) - 1)*2;
2176 }
2177 return 1;
2178 }
2179 }
2180 fprintf(stderr, "Asteroid could't be created!\n");
2181 return -1;
2182 }
2183
FF_destroy_asteroid(int i,float xspeed,float yspeed)2184 int FF_destroy_asteroid(int i, float xspeed, float yspeed)
2185 {
2186 if(asteroid[i].alive==1){
2187 asteroid[i].isdead=1;
2188 asteroid[i].xdead=asteroid[i].x;
2189 asteroid[i].ydead=asteroid[i].y;
2190 if(asteroid[i].size>0){
2191 /* Break the rock into two smaller ones! */
2192 if(num!=0){
2193
2194
2195
2196 if(FF_game==FACTOROIDS_GAME){
2197 FF_add_asteroid(asteroid[i].rx,
2198 asteroid[i].ry,
2199 asteroid[i].xspeed + (xspeed - yspeed)/2,
2200 asteroid[i].yspeed + (yspeed + xspeed)/2,
2201 0,
2202 rand()%360, rand()%3, (int)(asteroid[i].fact_number/num),
2203 0, 0,
2204 0);
2205
2206 FF_add_asteroid(asteroid[i].rx,
2207 asteroid[i].ry,
2208 asteroid[i].xspeed + (xspeed + yspeed)/2,
2209 asteroid[i].yspeed + (yspeed - xspeed)/2,
2210 0,
2211 rand()%360, rand()%3, num,
2212 0, 0,
2213 0);
2214 }
2215 else if(FF_game==FRACTIONS_GAME){
2216 FF_add_asteroid(asteroid[i].rx,
2217 asteroid[i].ry,
2218 ((asteroid[i].xspeed + xspeed) / 2),
2219 (asteroid[i].yspeed + yspeed),
2220 0,
2221 rand()%360, rand()%3, 0,
2222 (int)(asteroid[i].a/num), (int)(asteroid[i].b/num),
2223 0);
2224
2225 FF_add_asteroid(asteroid[i].rx,
2226 asteroid[i].ry,
2227 (asteroid[i].xspeed + xspeed),
2228 ((asteroid[i].yspeed + yspeed) / 2),
2229 0,
2230 rand()%360, rand()%3, 0,
2231 (int)(asteroid[i].b/num), (int)(asteroid[i].a/num),
2232 0);
2233 }
2234 }
2235 }
2236
2237 /* Destroy the old asteroid */
2238
2239 asteroid[i].alive=0;
2240 return 1;
2241 }
2242 return 0;
2243 }
2244
2245 /************** MODIFIED FUNCS FROM game.c and titlescreen.c ******************/
2246
FF_ShowMessage(char * str)2247 void FF_ShowMessage(char* str)
2248 {
2249 SDL_Surface* s1 = NULL;
2250 SDL_Rect loc;
2251 char wrapped_str[1024];
2252 int char_width;
2253
2254 if(str == NULL)
2255 return;
2256
2257 char_width = T4K_CharsForWidth(DEFAULT_MENU_FONT_SIZE, screen->w * 0.75);
2258 T4K_LineWrapInsBreaks(str, wrapped_str, char_width, 64, 64);
2259 s1 = T4K_BlackOutline(wrapped_str, DEFAULT_MENU_FONT_SIZE, &yellow);
2260 if (s1)
2261 {
2262 loc.x = screen->w/2 - s1->w/2;
2263 loc.y = screen->h/4;
2264 SDL_BlitSurface(s1, NULL, screen, &loc);
2265 SDL_FreeSurface(s1);
2266 }
2267 SDL_UpdateRect(screen, 0, 0, 0, 0);
2268 }
2269
FF_LevelObjsHints(char * label,char * contents,int x,int y)2270 static void FF_LevelObjsHints(char *label, char *contents, int x, int y )
2271 {
2272 SDL_Surface *s1 = NULL, *s2 = NULL;
2273 SDL_Rect loc;
2274 char wrapped_label[256];
2275 char wrapped_contents[256];
2276 int char_width;
2277
2278 if(label == NULL || contents == NULL)
2279 return;
2280
2281 char_width = T4K_CharsForWidth(DEFAULT_MENU_FONT_SIZE, LVL_WIDTH_MSG);
2282 T4K_LineWrapInsBreaks(label, wrapped_label, char_width, 64, 64);
2283 T4K_LineWrapInsBreaks(contents, wrapped_contents, char_width, 64, 64);
2284
2285 s1 = T4K_BlackOutline(wrapped_label, DEFAULT_MENU_FONT_SIZE, &white);
2286 s2 = T4K_BlackOutline(wrapped_contents, DEFAULT_MENU_FONT_SIZE, &white);
2287
2288 if(s1)
2289 {
2290 loc.x = x;
2291 loc.y = y;
2292 SDL_BlitSurface(s1, NULL, screen, &loc);
2293 }
2294 if(s2)
2295 {
2296 loc.x = x;
2297 loc.y = s1->h + loc.y ;
2298 SDL_BlitSurface(s2, NULL, screen, &loc);
2299 }
2300
2301 SDL_UpdateRect(screen, 0, 0, 0, 0);
2302
2303 SDL_FreeSurface(s1);
2304 SDL_FreeSurface(s2);
2305 }
2306
game_handle_user_events(void)2307 void game_handle_user_events(void)
2308 {
2309 SDL_Event event;
2310 SDLKey key;
2311 int roto = 0; //rotation flag
2312
2313 while (SDL_PollEvent(&event) > 0)
2314 {
2315 T4K_HandleStdEvents(&event);
2316 if (event.type == SDL_QUIT)
2317 {
2318 SDL_quit_received = 1;
2319 quit = 1;
2320 }
2321 if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP)
2322 {
2323 key = game_mouse_event(event);
2324 //the code transforms a mouse event into a keyboard event,
2325 //so the same modification should be made with the event structure
2326 // -- aviraldg 14/12/10
2327 int state = event.button.state;
2328 event.key.keysym.sym = key;
2329 event.type = state == SDL_PRESSED ? SDL_KEYDOWN : SDL_KEYUP;
2330 }
2331 if(event.type == SDL_MOUSEMOTION) {
2332 //NOTE: in SDL 1.2 this repositioning is only needed for
2333 //OS-X. Not sure if it will be needed at all in SDL 1.3
2334
2335 //If we are near edge of the screen, reset mouse to center
2336 if(event.motion.x <= 10
2337 || event.motion.x >= (screen->w - 10)
2338 || event.motion.y <= 10
2339 || event.motion.y >= (screen->h - 10))
2340 {
2341 mouse_reset = 1;
2342 SDL_WarpMouse(screen->w/2, screen->h/2);
2343 continue;
2344 }
2345 //If this was an event generated by our reset-to-center,
2346 //ignore it:
2347 if(mouse_reset)
2348 {
2349 mouse_reset = 0;
2350 continue;
2351 }
2352 //otherwise, valid event - handle mouse rotation
2353 roto = 1;
2354 mouseroto = game_mouseroto(event) / MOUSE_SENSITIVITY;
2355 }
2356 if (event.type == SDL_KEYDOWN ||
2357 event.type == SDL_KEYUP)
2358 {
2359 key = event.key.keysym.sym;
2360
2361 if (event.type == SDL_KEYDOWN)
2362 {
2363 if (key == SDLK_ESCAPE)
2364 {
2365 // Return to menu!
2366 escape_received = 1;
2367
2368 }
2369
2370 // Key press...
2371
2372 if (key == SDLK_RIGHT)
2373 {
2374 // Rotate CW
2375
2376 left_pressed = 0;
2377 right_pressed = 1;
2378 }
2379 else if (key == SDLK_LEFT)
2380 {
2381 // Rotate CCW
2382
2383 left_pressed = 1;
2384 right_pressed = 0;
2385 }
2386 else if (key == SDLK_UP)
2387 {
2388 // Thrust!
2389
2390 up_pressed = 1;
2391 }
2392
2393 if (key == SDLK_LSHIFT || key == SDLK_RSHIFT)
2394 {
2395 // Respawn now (if applicable)
2396 shift_pressed = 1;
2397 }
2398
2399 if (key == SDLK_TAB || key == SDLK_p)
2400 {
2401 /* [TAB] or [P]: Pause! (if settings allow) */
2402 if (Opts_AllowPause())
2403 {
2404 paused = 1;
2405 }
2406 }
2407
2408 if (key == CTRL_NEXT) {
2409 int n = prime_next[digits[1]*10 + digits[2]];
2410 if(n <= prime_numbers[wave - 1]) {
2411 digits[2] = n % 10;
2412 digits[1] = n / 10;
2413 tux_pressing = 1;
2414 playsound(SND_SHIELDSDOWN);
2415 } else {
2416 digits[2] = 2;
2417 digits[1] = 0;
2418 tux_pressing = 1;
2419 playsound(SND_SHIELDSDOWN);
2420 }
2421 }
2422
2423 if (key == CTRL_PREV) {
2424 int n = prime_prev[digits[1]*10 + digits[2]];
2425 if(n <= prime_numbers[wave - 1]) {
2426 digits[2] = n % 10;
2427 digits[1] = n / 10;
2428 tux_pressing = 1;
2429 playsound(SND_SHIELDSDOWN);
2430 } else {
2431 digits[2] = prime_numbers[wave - 1] % 10;
2432 digits[1] = prime_numbers[wave - 1] / 10;
2433 tux_pressing = 1;
2434 playsound(SND_SHIELDSDOWN);
2435 }
2436 }
2437 // TODO this code only deals with the first 6 primes, we'd probably want a
2438 // more generic solution
2439 int _tmp = digits[0]*100 + digits[1]*10 + digits[2];
2440 int digit, exec_digits = 0;
2441 if(key >= SDLK_0 && key <= SDLK_9) {
2442 digit = key - SDLK_0;
2443 tux_pressing = 1;
2444 exec_digits = 1;
2445 playsound(SND_SHIELDSDOWN);
2446 } else if (key >= SDLK_KP0 && key <= SDLK_KP9) {
2447 digit = key - SDLK_KP0;
2448 tux_pressing = 1;
2449 exec_digits = 1;
2450 playsound(SND_SHIELDSDOWN);
2451 }
2452 if(exec_digits == 1) {
2453 if(digits[1] == 1 && (digit == 2 || digit == 5 || digit == 7)) {
2454 digits[1] = 0;
2455 digits[2] = digit;
2456 } else if(digits[1] == 1 && digit == 1) {
2457 digits[2] = 1;
2458 } else if(digit==1) {
2459 digits[1] = 1;
2460 digits[2] = 0;
2461 }
2462 if(digit == 2 || digit == 3 || digit == 5 || digit == 7) {
2463 digits[2] = digit;
2464 }
2465 }
2466 //cancel if requested digit forms larger prime than allowed
2467 if((digits[1]*10 + digits[2]) > prime_numbers[wave - 1]) {
2468 digits[2] = _tmp % 10;
2469 digits[1] = _tmp / 10;
2470 }
2471
2472 /* activate bonus/powerup */
2473 if((key == SDLK_LSHIFT || key == SDLK_RSHIFT) && bonus_time == -1) {
2474 playsound(SND_HARP);
2475 bonus_time = SDL_GetTicks() + 10000; //10sec bonus
2476
2477 //special handling for the powerbomb, since it happens "at once"
2478 if(bonus == TB_POWERBOMB) {
2479 _tb_PowerBomb(digits[1]*10 + digits[2]);
2480 bonus_time = SDL_GetTicks() + 1000;
2481 /* FIXME ugly hack to allow multiple lasers to display */
2482 }
2483 }
2484 /* support for negative answer input DSB */
2485 else if ((key == SDLK_MINUS || key == SDLK_KP_MINUS))
2486 //&& MC_AllowNegatives()) /* do nothing unless neg answers allowed */
2487 {
2488 /* allow player to make answer negative: */
2489 neg_answer_picked = 1;
2490 tux_pressing = 1;
2491 playsound(SND_SHIELDSDOWN);
2492 }
2493 else if ((key == SDLK_PLUS || key == SDLK_KP_PLUS))
2494 //&& MC_AllowNegatives()) /* do nothing unless neg answers allowed */
2495 {
2496 /* allow player to make answer positive: */
2497 neg_answer_picked = 0;
2498 tux_pressing = 1;
2499 playsound(SND_SHIELDSDOWN);
2500 }
2501 else if (key == SDLK_RETURN ||
2502 key == SDLK_KP_ENTER ||
2503 key == SDLK_SPACE)
2504 {
2505 shoot_pressed = 1;
2506 doing_answer = 1;
2507 button_pressed = 1;
2508 playsound(SND_LASER);
2509 }
2510
2511
2512 }
2513 else if (event.type == SDL_KEYUP)
2514 {
2515 // Key release...
2516
2517 if (key == SDLK_RIGHT)
2518 {
2519 right_pressed = 0;
2520 }
2521 else if (key == SDLK_LEFT)
2522 {
2523 left_pressed = 0;
2524 }
2525 else if (key == SDLK_UP)
2526 {
2527 up_pressed = 0;
2528 }
2529 if (key == SDLK_LSHIFT ||
2530 key == SDLK_RSHIFT)
2531 {
2532 // Respawn now (if applicable)
2533 shift_pressed = 0;
2534 }
2535 if ( key == SDLK_RETURN || key == SDLK_KP_ENTER || key == SDLK_SPACE )
2536 {
2537 button_pressed = 0;
2538 }
2539 }
2540 }
2541
2542 #ifdef JOY_YES
2543 else if (event.type == SDL_JOYBUTTONDOWN &&
2544 player_alive)
2545 {
2546 if (event.jbutton.button == JOY_B)
2547 {
2548 shoot_pressed = 1;
2549 }
2550 else if (event.jbutton.button == JOY_A)
2551 {
2552 // Thrust:
2553
2554 up_pressed = 1;
2555 }
2556 else
2557 {
2558 shift_pressed = 1;
2559 }
2560 }
2561 else if (event.type == SDL_JOYBUTTONUP)
2562 {
2563 if (event.jbutton.button == JOY_A)
2564 {
2565 // Stop thrust:
2566
2567 up_pressed = 0;
2568 }
2569 else if (event.jbutton.button != JOY_B)
2570 {
2571 shift_pressed = 0;
2572 }
2573 }
2574 else if (event.type == SDL_JOYAXISMOTION)
2575 {
2576 if (event.jaxis.axis == JOY_X)
2577 {
2578 if (event.jaxis.value < -256)
2579 {
2580 left_pressed = 1;
2581 right_pressed = 0;
2582 }
2583 else if (event.jaxis.value > 256)
2584 {
2585 left_pressed = 0;
2586 right_pressed = 1;
2587 }
2588 else
2589 {
2590 left_pressed = 0;
2591 right_pressed = 0;
2592 }
2593 }
2594 }
2595 #endif
2596
2597 }
2598 if(roto == 0) mouseroto = 0;
2599 }
2600
2601
game_mouse_event(SDL_Event event)2602 static int game_mouse_event(SDL_Event event)
2603 {
2604 if(event.button.button == SDL_BUTTON_LEFT) return SDLK_RETURN;
2605 else if(event.button.button == SDL_BUTTON_MIDDLE) return SDLK_LSHIFT;
2606 else if(event.button.button == SDL_BUTTON_RIGHT) return SDLK_UP;
2607 else if(event.button.button == SDL_BUTTON_WHEELUP) return CTRL_NEXT;
2608 else if(event.button.button == SDL_BUTTON_WHEELDOWN) return CTRL_PREV;
2609 else return SDLK_UNKNOWN;
2610 }
2611
2612
check_exit_conditions(void)2613 static int check_exit_conditions(void)
2614 {
2615 if(SDL_quit_received)
2616 {
2617 return FF_OVER_SDL_QUIT;
2618 }
2619
2620 if(escape_received)
2621 {
2622 return FF_OVER_ESCAPE;
2623 }
2624 if(tuxship.lives<=0)
2625 {
2626 return FF_OVER_LOST;
2627 }
2628 if(wave > 6 )
2629 {
2630 return FF_OVER_WON;
2631 }
2632
2633 /* if we made it to here, the game goes on! */
2634 return FF_IN_PROGRESS;
2635 }
2636
2637
2638 /* Draw numbers/symbols over the attacker: */
draw_nums(const char * str,int x,int y,SDL_Color * col)2639 void draw_nums(const char* str, int x, int y, SDL_Color* col)
2640 {
2641 if(!str || !col)
2642 return;
2643
2644 SDL_Surface* surf = NULL;
2645 surf = T4K_BlackOutline(str, ASTEROID_NUM_SIZE * zoom, col);
2646 if(surf)
2647 {
2648 int w = T4K_GetScreen()->w;
2649 x -= surf->w/2;
2650 // Keep formula at least 8 pixels inside screen:
2651 if(surf->w + x > (w - 8))
2652 x -= (surf->w + x - (w - 8));
2653 if(x < 8)
2654 x = 8;
2655
2656 SDL_Rect pos = {x, y};
2657 SDL_BlitSurface(surf, NULL, T4K_GetScreen(), &pos);
2658 SDL_FreeSurface(surf);
2659 }
2660 }
2661