1 /*
2  *  Twin Distress
3  *  Copyright (C) 2003-04 Keith Frampton
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Library General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19 
20 #include <SDL.h>
21 #include <SDL_image.h>
22 #ifndef NOAUDIO
23 #include <SDL_mixer.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <math.h>
29 #include <time.h>
30 
31 /* used for bmp vs. png support */
32 #ifndef EXTENSION
33 #define EXTENSION ".png"
34 #endif
35 #ifndef DATA_PREFIX
36 #define DATA_PREFIX ""
37 #endif
38 #ifndef HIGH_SCORE_PREFIX
39 #define HIGH_SCORE_PREFIX ""
40 #endif
41 #define GRIDSTARTX 238
42 #define GRIDSTARTY 138
43 /* left & right x coordinates for the menu system */
44 #define MENULX 222
45 #define MENURX 578
46 #define MUSIC_LOOPS 6
47 /* used for the change volume function */
48 #define MUSIC 1
49 #define SOUND 2
50 /* maximum number of lines in a menu / options in a submenu */
51 #define MAX_IN_MENU 7
52 #define MAX_OPTIONS 6
53 #define EXIT_GAME 2
54 #define HIGHSCORES 1
55 /* numbers of options remembered in the .twind.opts/options.dat */
56 #define NUM_OPTS 11
57 enum fade_types {FADE_OUT, FADE_IN};
58 enum block_status {BLOCK_NORM, BLOCK_HIGH, BLOCK_GONE, BLOCK_GOING};
59 enum directions {DIR_NONE, DIR_UP, DIR_RIGHT, DIR_DOWN, DIR_LEFT};
60 /* lines used for drawing the path
61    UL - upper left, UR - upper right, LR - lower right, LL - lower left */
62 enum lines {LINE_VER, LINE_HOR, LINE_UL, LINE_UR, LINE_LR, LINE_LL, LINE_NONE};
63 enum menus {MAIN, NEW_GAME, OPTIONS};
64 enum sub_menu_items {NO_OPTIONS = -1, SKILL_LEVEL, GRAVITY, BLOCK_SET,
65     CORNER_STYLE, PAUSE_MODE, DISPLAY_MODE, FADING_EFFECT, REMEMBER_OPTIONS};
66 enum skill_levels {KIDS, EASY, NORMAL, HARD, VERYHARD, INSANE, NUM_SKILLS};
67 enum block_sets {SQUARE_BRICKS, BEVELED_BLOCKS, SNAKE_SKIN_BLOCKS,
68     PYRAMID_BLOCKS, CIRCLES, BRUSHED_METAL, NUM_BLOCK_SETS};
69 enum corner_styles {SQUARE, ROUNDED};
70 enum sounds {MENU_OPTION, OPTION_CHOICE, KEYBOARD_CLICK, ELIMINATE_BLOCKS,
71     WRONG_MOVE, APPLY_BONUS, NUM_SOUNDS};
72 enum music_types {REGULAR, LAST_TEN_SECONDS};
73 /* used when we are changing volume with either the toggle, or the slider */
74 enum toggles_used {TOGGLE_YES, TOGGLE_NO};
75 enum adjust_sliders {MOVE_SLIDER, NO_SLIDER};
76 enum surfaces {BACKGROUND, BLOCKS, HIGHLIGHT1, HIGHLIGHT2, HIGHLIGHT3,
77     HIGHLIGHT4, BLACK_L, WHITE_L, BLACK_R, WHITE_R, PATH_BLOCKS, TIMER_FULL,
78     TIMER_FULL_WHITE, TIMER_EMPTY, TIMER_EMPTY_WHITE, TITLE_SMALL, TITLE_BIG,
79     VOLUME_BAR, VOLUME_SLIDER, NUM_SURFACES};
80 enum fonts {SERIFG20, SERIFW20, SERIFG24, SERIFW24, NUM_FONTS};
81 enum colors {DARKBLUE, GREY, GREEN, RED, BROWN, PURPLE, ORANGE, PINK, LIGHTBLUE,
82              YELLOW};
83 enum in_game_menu_options {NEWGAME, PAUSE, HELP, EXIT, NUM_IN_GAME_MO};
84 enum pause_modes {PAUSE_ONLY, MINIMIZE};
85 enum display_modes {WINDOW, FULLSCREEN};
86 
87 struct path_info {
88     int x;
89     int y;
90     int direction;
91     /* current time for 1st, +40ms for next, +20ms for highlighted and destination */
92     Uint32 blit_time;
93     int block_num;
94     int blit_done;
95 };
96 
97 struct block {
98     int status;
99     int color;
100     int left_or_right;
101 };
102 
103 struct removed_blocks {
104     int x, y, x2, y2;
105 };
106 
107 struct existing_blocks {
108     int x, y;
109     int color;
110     int left_or_right;
111 };
112 
113 struct font_info {
114     SDL_Surface *name;
115     int pos[95];
116     int width[95];
117     int height;
118 };
119 
120 struct option_info {
121     int opt_group[MAX_OPTIONS];
122     int opt_num[MAX_OPTIONS];
123 };
124 
125 struct highscore_info {
126     char name_score[21];
127     int score;
128     int level;
129     char name_time[21];
130     int time;
131 };
132 
133 /* used for the activation of in game menu options, yes/no questions, etc */
134 typedef struct highlight_info HIGHLIGHT_INFO;
135 struct highlight_info {
136     int x1, x2, y1, y2;
137     char name[9];
138 };
139 
140 struct game_info {
141     char *score_file, *options_file;
142     struct block grid[12][12];
143     /* used for the Insane game mode to keep track of removed blocks */
144     struct removed_blocks eliminated[50];
145     /* currently highlighted block and destination block */
146     int highx, highy, destx, desty;
147     /* number of turns made to go from one block to the next */
148     int turns;
149     /* maximum turns we are allowed to make */
150     int max_turns;
151     /* number of elements in the list of paths to be highlighted */
152     int path_count;
153     /* if we don't want to add the first path to the list, but
154        we want to know that the path was found, set this to 1 */
155     int first_block;
156     /* next two are used for the last ten seconds of a level */
157     int blink;
158     Uint32 blink_time;
159     /* various timers used during the game */
160     Uint32 start_timer, elapsed_time, paused_time, stop_timer, best_time;
161     Uint32 temp_time, prev_time, added_time, skipped_time, random_block_time;
162     /* all of the graphics + the main screen surface where everything is blitted */
163     SDL_Surface *screen;
164     SDL_Surface *surface[NUM_SURFACES];
165     /* fonts used in the game */
166     struct font_info font[NUM_FONTS];
167     char player_name[21];
168     /* used only when the user ends the game before it's over */
169     int exit_game;
170     /* if (blit_done) update the screen */
171     int blit_done;
172     /* should the corners of the bricks be a rounded style */
173     int corner_style;
174     /* number of groups of skill levels / game modes for the highscore file */
175     int num_groups;
176     /* used in-game to indicate whether or not the option is highlighted */
177     int new_game_highlighted, exit_highlighted, help_highlighted;
178     int highlight[4];
179     /* used to determine which of the four highlight blocks are to be blitted */
180     int block_highlight_num;
181     /* current block highlighted time (used in the animation of block highlight) */
182     Uint32 block_highlight_time;
183     int unpause_msg;
184     /* bonus scoring related vars */
185     int bonus_score, last_color_removed, current_combos_removed;
186     int current_sets_removed, no_matching_colors, no_wrong_moves;
187     float highest_combo_removed;
188     /* toggle indicating whether or not to use the fading effect */
189     int fading_effect;
190     /* used to temporarily turn off fading (ie: when changing the block set or
191        corner style in the help screen, the fade isn't appropriate) */
192     int fading_off_temp;
193     /* toggle indicating whether or not to use the gravity */
194     int gravity;
195     /* when playing the Insane mode, the L & R can either be black or white &
196        cal be changed by pressing the number keys */
197     char color_l_and_r[11];
198     /* window/full screen toggle */
199     int score, score_changed;
200     int timer_height, block_num, erased;
201     int level_start, current_level, not_over, level_time, paused;
202     int skill_level, block_set, pause_mode, display_mode, remember_options;
203     struct option_info options;
204     int menu, menu_option, next_option;
205     int menu_item_cnt, sub_menu_item_cnt, last_menu_high;
206     /* used for converting a number to a string */
207     char *temp_string;
208     /* when changing to full screen or window mode;
209        set this so we don't hear any sound effect */
210     int option_toggle;
211     #ifndef NOAUDIO
212     Mix_Music *music;
213     #endif
214     int regular_music, music_loop[MUSIC_LOOPS], sound_volume, music_volume;
215     /* used when muting sound/music */
216     int old_sound_volume, old_music_volume;
217     /* toggle indicating whether or not sound/music is enabled */
218     int audio_enabled, music_enabled;
219     /* used for switching between the two different music loops for the menu */
220     int menu_music;
221     #ifndef NOAUDIO
222     Mix_Chunk *sound[NUM_SOUNDS];
223     #endif
224 } game;
225 
226 int rnd(float max);
227 void initialize_and_load();
228 void create_random_grid();
229 void randomize_blocks();
230 void randomize_music();
231 void draw_grid_on_screen();
232 void draw_blocks();
233 void display_menu(char *menu_items[][MAX_IN_MENU], int menu);
234 void blank_options();
235 void create_menu(char *menu_items[][MAX_IN_MENU], char *sub_menu_items[][MAX_OPTIONS]);
236 int change_option(char *menu_items[][MAX_IN_MENU], char *sub_menu_items[][MAX_OPTIONS], int sub_menu, int option);
237 int get_input_for_menu(char *menu_items[][MAX_IN_MENU], char *sub_menu_items[][MAX_OPTIONS]);
238 int move_in_menu(char *menu_items[][MAX_IN_MENU], int current_menu_high, int direction);
239 int get_input_for_screen(int *screen_num, int max_screens, int screen);
240 void display_high_scores(int skill_level);
241 void show_scores_for_skill_level(struct highscore_info highscores[][10], int skill_level);
242 void display_help();
243 void show_help_page_number(int page_num);
244 void blit(SDL_Surface *source, Sint16 srcx, Sint16 srcy, Uint16 srcw, Uint16 srch, Sint16 destx, Sint16 desty);
245 void fill(Sint16 destx, Sint16 desty, Uint16 destw, Uint16 desth, Uint8 red, Uint8 green, Uint8 blue);
246 int word_width(char *word, int font);
247 void blank_screen();
248 void play_game();
249 void play_level();
250 void update_highlighted_block();
251 void update_score_and_bonus();
252 void print_score_and_bonus();
253 void apply_bonus_score();
254 void update_timer();
255 struct path_info *get_input_for_level(struct path_info *path);
256 void change_volume(int volume_type, int volume, int x, int y, int toggle, int adjust_slider);
257 void change_block_set();
258 void change_display_mode();
259 void setup_yes_no_xy(char *word, int destx, int desty, int font, HIGHLIGHT_INFO *highlight);
260 int get_input_for_yes_no(HIGHLIGHT_INFO *yes_no);
261 void exit_game(struct path_info *path, int exit_type);
262 void read_highscore_file(struct highscore_info highscores[][10]);
263 void write_highscore_file(struct highscore_info highscores[][10]);
264 void update_highscores();
265 int game_over();
266 SDL_Surface *load_image(char *filename, int *loaded);
267 #ifndef NOAUDIO
268 Mix_Chunk *load_wav(char *filename, int *loaded);
269 #endif
270 void unhighlight_block(int x, int y);
271 void put_block_back_on_screen(int x, int y);
272 void eliminate_blocks(int x, int y);
273 void keep_track_of_removed_blocks(int x, int y, int x2, int y2);
274 void add_gravity_effect(int x, int *y, int x2, int *y2);
275 void add_gravity_effect_for_blocks_put_back(int x, int *y, int x2, int *y2);
276 void setup_array(int dir1, int dir2, int dir3, int dir4, int directions[]);
277 struct path_info *change_grid(int x, int y, struct path_info *path, int button);
278 void wrong_move_penalty ();
279 struct path_info *legal_move(int x, int y, int cur_dir, int prev_dir, struct path_info *path);
280 struct path_info *add_path_to_list(int x, int y, int direction, struct path_info *path);
281 int show_path(struct path_info *path);
282 struct path_info *create_path(struct path_info *path, int cnt);
283 struct path_info *erase_path(struct path_info *path, int block_num);
284 void cleanup_and_exit();
285 void read_options_file();
286 void write_options_file();
287 void pause_game(char *message);
288 void unpause_game();
289 void print_word(char *word, int destx, int desty, int font);
290 void print_char(int character, int destx, int desty, int font);
291 void num_to_str(int num, int max_length);
292 void print_number(int destx, int desty, int font, int number, int len);
293 void print_time(int destx, int desty, int font, int number);
294 void change_corner_style(int x, int y);
295 Uint32 getpixel(SDL_Surface *surface, int x);
296 void fade_screen(int fade_type, int time, Uint32 color);
297 
main(int argc,char * argv[])298 int main(int argc, char *argv[]) {
299 
300     char *menu_items[3][MAX_IN_MENU] = {{"New Game","Options","High Scores","Help","Exit"},
301                                         {"Start Game", "", "Skill Level:","Gravity:", "Player Name: ", "", "Back"},
302                                         {"Block Set:", "Corner Style:", "Pause Mode:", "Display Mode:", "Fading Effect:", "Remember Options:", "Back"}};
303 
304     /* code below was only used for a friend while building the game ... it may
305        go back in later on when I implement command line options */
306     /*
307     if (argc > 1) {
308         char *temp = argv[1];
309         game.score_file = (char *)malloc(sizeof(char) * (strlen(temp) + 12));
310         game.score_file[0] = '\0';
311         strcat(game.score_file, temp);*/
312         /* add a backslash character if one isn't there already */
313         /*if (temp[strlen(temp) - 1] != '\\')
314             strcat(game.score_file, "\\");
315     }
316     else {*/
317     game.score_file = (char *)malloc(strlen(HIGH_SCORE_PREFIX) + sizeof(char) * (11));
318     game.score_file[0] = '\0';
319     /*}*/
320     #ifndef LINUX
321     strcat(game.score_file, "scores.dat");
322     #else
323     strcat(game.score_file, HIGH_SCORE_PREFIX "twind.hscr");
324     #endif
325     /*fprintf(stdout, "Highscore file location: %s\n", game.score_file);*/
326     initialize_and_load();
327     display_menu(menu_items, MAIN);
328     return 0;
329 
330 }
331 
display_menu(char * menu_items[][MAX_IN_MENU],int menu)332 void display_menu(char *menu_items[][MAX_IN_MENU], int menu) {
333 
334     int destx, desty, font, hifont;
335     char *sub_menu_items[8][MAX_OPTIONS] = {{"Kids", "Easy", "Normal", "Hard", "Very Hard", "Insane"},
336                                             {"Off", "On"},
337                                             {"Bricks", "Beveled", "Snake Skin", "Pyramids", "Circles", "Brushed Metal"},
338                                             {"Square", "Rounded"},
339                                             {"Pause Only", "Minimize"},
340                                             {"Window", "Full Screen"},
341                                             {"Off", "On"},
342                                             {"No", "Yes"}};
343     #ifndef NOAUDIO
344     char *menu_music[2] = {DATA_PREFIX "music/0-0.ogg",
345                            DATA_PREFIX "music/0-1.ogg"};
346     #endif
347 
348     font = SERIFG24;
349     hifont = SERIFW24;
350     game.menu = menu;
351     blank_options();
352     /* start up the background music */
353     #ifndef NOAUDIO
354     if (game.music == NULL && game.audio_enabled) {
355         /* switch the music loop */
356         game.menu_music = !game.menu_music;
357         if (game.music_enabled) {
358             game.music = Mix_LoadMUS(menu_music[game.menu_music]);
359             Mix_FadeInMusic(game.music, -1, 1000);
360         }
361     }
362     #endif
363 
364     /* for each menu in the if statements below, the number of items must be set
365        with the game.menu_item_cnt & as well as the options with the
366        game.options.opt_group[i] & game.options.opt_num[i]. 'i' must be the
367        index of where the options are located in the menu. ie: skill level is
368        the third option, so it has an index of 2. */
369     if (menu == MAIN) {
370         game.menu_item_cnt = 5;
371         create_menu(menu_items, sub_menu_items);
372         /* check for input */
373         while (1) {
374             game.menu_option = get_input_for_menu(menu_items, sub_menu_items);
375             switch (game.menu_option) {
376                 /* New Game */
377                 case 0:
378                     display_menu(menu_items, NEW_GAME);
379                     break;
380                 /* Options */
381                 case 1:
382                     display_menu(menu_items, OPTIONS);
383                     break;
384                 /* High Scores */
385                 case 2:
386                     display_high_scores(NORMAL);
387                     break;
388                 /* Help */
389                 case 3:
390                     display_help();
391                     break;
392                 /* Exit */
393                 case 4:
394                     cleanup_and_exit(game);
395                     break;
396             }
397             game.menu = menu;
398             blank_options();
399             game.menu_item_cnt = 5;
400             create_menu(menu_items, sub_menu_items);
401         }
402     }
403     else if (menu == NEW_GAME) {
404         game.options.opt_group[2] = SKILL_LEVEL;
405         game.options.opt_num[2] = game.skill_level;
406         game.options.opt_group[3] = GRAVITY;
407         game.options.opt_num[3] = game.gravity;
408         game.menu_item_cnt = 7;
409         create_menu(menu_items, sub_menu_items);
410         while (1) {
411             game.options.opt_num[2] = game.skill_level;
412             game.options.opt_num[3] = game.gravity;
413             game.menu_option = get_input_for_menu(menu_items, sub_menu_items);
414             switch (game.menu_option) {
415                 /* Start Game */
416                 case 0:
417                     if (strlen(game.player_name) > 0) {
418                         /* stop our background music & play the game */
419                         #ifndef NOAUDIO
420                         if (game.music_enabled) {
421                             Mix_HaltMusic();
422                             Mix_FreeMusic(game.music);
423                         }
424                         game.music = NULL;
425                         #endif
426                         if (game.fading_effect)
427                             fade_screen(FADE_OUT, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
428                         play_game();
429                         return;
430                     }
431                     else {
432                         destx = MENULX + word_width("Player Name: ", font);
433                         desty = (600 - game.font[font].height * game.menu_item_cnt) / 2 + 4 * game.font[font].height;
434                         print_word("Please enter a name.", destx, desty, font);
435                     }
436                     break;
437                 /* Skill Level */
438                 case 2:
439                     game.sub_menu_item_cnt = NUM_SKILLS;
440                     game.skill_level = change_option(menu_items, sub_menu_items, SKILL_LEVEL, game.skill_level);
441                     if (game.skill_level == EASY)
442                         game.max_turns = 3;
443                     else
444                         game.max_turns = 2;
445                     break;
446                 /* Gravity */
447                 case 3:
448                     game.sub_menu_item_cnt = 2;
449                     game.gravity = change_option(menu_items, sub_menu_items, GRAVITY, game.gravity);
450                     break;
451                 /* Player Name */
452                 case 4:
453                     break;
454                 /* Back */
455                 case 6:
456                     return;
457                     break;
458             }
459         }
460     }
461     else if (menu == OPTIONS) {
462         game.options.opt_group[0] = BLOCK_SET;
463         game.options.opt_num[0] = game.block_set;
464         game.options.opt_group[1] = CORNER_STYLE;
465         game.options.opt_num[1] = game.corner_style;
466         game.options.opt_group[2] = PAUSE_MODE;
467         game.options.opt_num[2] = game.pause_mode;
468         game.options.opt_group[3] = DISPLAY_MODE;
469         game.options.opt_num[3] = game.display_mode;
470         game.options.opt_group[4] = FADING_EFFECT;
471         game.options.opt_num[4] = game.fading_effect;
472         game.options.opt_group[5] = REMEMBER_OPTIONS;
473         game.options.opt_num[5] = game.remember_options;
474         game.menu_item_cnt = 7;
475         create_menu(menu_items, sub_menu_items);
476         while (1) {
477             game.options.opt_num[0] = game.block_set;
478             game.options.opt_num[1] = game.corner_style;
479             game.options.opt_num[2] = game.pause_mode;
480             game.options.opt_num[3] = game.display_mode;
481             game.options.opt_num[4] = game.fading_effect;
482             game.options.opt_num[5] = game.remember_options;
483             game.menu_option = get_input_for_menu(menu_items, sub_menu_items);
484             switch (game.menu_option) {
485                 /* Block Set */
486                 case 0: {
487                     game.sub_menu_item_cnt = NUM_BLOCK_SETS;
488                     game.block_set = change_option(menu_items, sub_menu_items, BLOCK_SET, game.block_set);
489                     change_block_set();
490                     break;
491                 }
492                 /* Corner Style */
493                 case 1: {
494                     game.sub_menu_item_cnt = 2;
495                     game.corner_style = change_option(menu_items, sub_menu_items, CORNER_STYLE, game.corner_style);
496                     break;
497                 }
498                 /* Pause Mode */
499                 case 2: {
500                     game.sub_menu_item_cnt = 2;
501                     game.pause_mode = change_option(menu_items, sub_menu_items, PAUSE_MODE, game.pause_mode);
502                     break;
503                 }
504                 /* Display Mode */
505                 case 3: {
506                     game.sub_menu_item_cnt = 2;
507                     game.display_mode = change_option(menu_items, sub_menu_items, DISPLAY_MODE, game.display_mode);
508                     /* change to window mode */
509                     if (game.display_mode == WINDOW)
510                         game.screen = SDL_SetVideoMode(800, 600, 0, SDL_SWSURFACE);
511                     /* change to full screen mode (if possible) */
512                     else {
513                         game.screen = SDL_SetVideoMode(800, 600, 0, SDL_FULLSCREEN);
514                         if (game.screen == NULL) {
515                             game.screen = SDL_SetVideoMode(800, 600, 0, SDL_SWSURFACE);
516                             game.display_mode = WINDOW;
517                         }
518                     }
519                     game.options.opt_num[3] = game.display_mode;
520                     create_menu(menu_items, sub_menu_items);
521                     break;
522                 }
523                 /* Fading Effect */
524                 case 4: {
525                     game.sub_menu_item_cnt = 2;
526                     game.fading_effect = change_option(menu_items, sub_menu_items, FADING_EFFECT, game.fading_effect);
527                     break;
528                 }
529                 /* Remember Options */
530                 case 5: {
531                     game.sub_menu_item_cnt = 2;
532                     game.remember_options = change_option(menu_items, sub_menu_items, REMEMBER_OPTIONS, game.remember_options);
533                     break;
534                 }
535                 /* Back */
536                 case 6:
537                     return;
538             }
539         }
540     }
541 
542 }
543 
blank_options()544 void blank_options() {
545 
546     int i;
547 
548     for (i = 0; i < MAX_OPTIONS; i++) {
549         game.options.opt_group[i] = NO_OPTIONS;
550         game.options.opt_num[i] = NO_OPTIONS;
551     }
552 
553 }
554 
change_option(char * menu_items[][MAX_IN_MENU],char * sub_menu_items[][MAX_OPTIONS],int sub_menu,int option)555 int change_option(char *menu_items[][MAX_IN_MENU], char *sub_menu_items[][MAX_OPTIONS], int sub_menu, int option) {
556 
557     int new_option, destx, desty, destw, desth, font;
558 
559     font = SERIFW24;
560     destx = MENULX + word_width(menu_items[game.menu][game.menu_option], font);
561     desty = (600 - game.font[font].height * game.menu_item_cnt) / 2 + game.font[font].height * (game.menu_option);
562     destw = MENURX - destx;
563     desth = game.font[font].height;
564     fill(destx, desty, destw, desth, 0, 0, 0);
565     /* change to the next option */
566     if (game.next_option) {
567         new_option = option + 1;
568         if (new_option == game.sub_menu_item_cnt)
569             new_option = 0;
570     }
571     /* change to the previous option */
572     else {
573         new_option = option - 1;
574         if (new_option < 0)
575             new_option = game.sub_menu_item_cnt - 1;
576     }
577     destx = MENURX - word_width(sub_menu_items[sub_menu][new_option], font);
578     print_word(sub_menu_items[sub_menu][new_option], destx, desty, font);
579     if (game.blit_done) {
580         #ifndef NOAUDIO
581         if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
582         #endif
583         SDL_UpdateRect(game.screen, 0, 0, 0, 0);
584         game.blit_done = 0;
585     }
586     return new_option;
587 
588 }
589 
create_menu(char * menu_items[][MAX_IN_MENU],char * sub_menu_items[][MAX_OPTIONS])590 void create_menu(char *menu_items[][MAX_IN_MENU], char *sub_menu_items[][MAX_OPTIONS]) {
591 
592     int i, destx, desty, font, hifont;
593 
594     if (game.fading_effect)
595         fade_screen(FADE_OUT, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
596     font = SERIFG24;
597     hifont = SERIFW24;
598     blank_screen();
599     blit(game.surface[TITLE_BIG], 0, 0, game.surface[TITLE_BIG]->w, game.surface[TITLE_BIG]->h, 166, 0);
600     /* draw the volume meters */
601     if (game.audio_enabled) {
602         print_word("Sound:", 185, 456, SERIFG24);
603         blit(game.surface[VOLUME_BAR], 0, 0, 133, 32, 252, 456);
604         blit(game.surface[VOLUME_SLIDER], 0, 0, 5, 32, game.sound_volume + 252, 456);
605         if (game.music_enabled) {
606             print_word("Music:", 416, 456, SERIFG24);
607             blit(game.surface[VOLUME_BAR], 0, 0, 133, 32, 482, 456);
608             blit(game.surface[VOLUME_SLIDER], 0, 0, 5, 32, game.music_volume + 482, 456);
609         }
610     }
611     print_word("http://twind.sourceforge.net", 5, 575, SERIFG20);
612     print_word("v1.1.0", -800, 575, SERIFG20);
613     print_word("(c) 2003-04 Keith Frampton", 800 - word_width("(c) 2003-04 Keith Frampton", SERIFG20) - 5, 575, SERIFG20);
614     desty = (600 - game.font[font].height * game.menu_item_cnt) / 2;
615     for (i = 0; i < game.menu_item_cnt; i++) {
616         if (strchr(menu_items[game.menu][i],':') == NULL) {
617             if (i == 0)
618                 print_word(menu_items[game.menu][i], -800, desty, hifont);
619             else
620                 print_word(menu_items[game.menu][i], -800, desty, font);
621         }
622         /* special case for player name */
623         else if (game.menu == NEW_GAME && i == 4) {
624             print_word(menu_items[game.menu][i], MENULX, desty, font);
625             print_word(game.player_name, MENULX + word_width("Player Name: ", font), desty, font);
626         }
627         else {
628             if (i == 0) {
629                 print_word(menu_items[game.menu][i], MENULX, desty, hifont);
630                 destx = MENURX - word_width(sub_menu_items[game.options.opt_group[i]][game.options.opt_num[i]], font);
631                 print_word(sub_menu_items[game.options.opt_group[i]][game.options.opt_num[i]], destx, desty, hifont);
632             }
633             else {
634                 print_word(menu_items[game.menu][i], MENULX, desty, font);
635                 destx = MENURX - word_width(sub_menu_items[game.options.opt_group[i]][game.options.opt_num[i]], font);
636                 print_word(sub_menu_items[game.options.opt_group[i]][game.options.opt_num[i]], destx, desty, font);
637             }
638         }
639         desty += game.font[font].height;
640     }
641     game.last_menu_high = 0;
642     if (game.fading_effect)
643         fade_screen(FADE_IN, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
644     SDL_UpdateRect(game.screen, 0, 0, 0, 0);
645 
646 }
647 
get_input_for_menu(char * menu_items[][MAX_IN_MENU],char * sub_menu_items[][MAX_OPTIONS])648 int get_input_for_menu(char *menu_items[][MAX_IN_MENU], char *sub_menu_items[][MAX_OPTIONS]) {
649 
650     int current_menu_high, starty, destx, height;
651     int destxpn, destxc, destypn, max_word_len;
652     int cursor, cursor_blink_time, location;
653     int font, hifont;
654     SDL_Event event;
655     SDL_keysym keysym;
656 
657     font = SERIFG24;
658     hifont = SERIFW24;
659     location = strlen(game.player_name);
660     cursor = cursor_blink_time = 0;
661     max_word_len = 202;
662     height = game.font[font].height * game.menu_item_cnt;
663     starty = (600 - height) / 2;
664     destxpn = MENULX + word_width("Player Name: ", font);
665     destxc = destxpn + word_width(game.player_name, font);
666     destypn = (600 - game.font[font].height * game.menu_item_cnt) / 2 + 4 * game.font[font].height;
667     current_menu_high = game.last_menu_high;
668     game.next_option = 1;
669     while (1) {
670         if (SDL_PollEvent(&event)) {
671             switch (event.type) {
672                 case SDL_MOUSEMOTION:
673                     /* are we in the menu? */
674                     if (event.button.x >= MENULX && event.button.x < MENURX && event.button.y >= starty && event.button.y < (starty + height)) {
675                         current_menu_high = (event.button.y - starty) / game.font[font].height;
676                         /* re-assign current_menu_high if blank option */
677                         if (*menu_items[game.menu][current_menu_high] == '\0')
678                             current_menu_high = game.last_menu_high;
679                         if (game.last_menu_high != current_menu_high) {
680                             if (*menu_items[game.menu][current_menu_high] != '\0') {
681                                 #ifndef NOAUDIO
682                                 if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
683                                 #endif
684                             }
685                         }
686                     }
687                     /* change the sound volume */
688                     else if (event.motion.x >= 254 && event.motion.x < 254 + 129 && event.motion.y >= 456 && event.motion.y < 488 && event.motion.state == SDL_BUTTON(1))
689                         change_volume(SOUND, event.motion.x - 254, 252, 456, TOGGLE_NO, MOVE_SLIDER);
690                     /* change the music volume */
691                     else if (event.button.x >= 484 && event.motion.x < 613 && event.motion.y >= 456 && event.motion.y < 488 && event.motion.state == SDL_BUTTON(1) && game.music_enabled)
692                         change_volume(MUSIC, event.motion.x - 484, 482, 456, TOGGLE_NO, MOVE_SLIDER);
693                     break;
694                 case SDL_MOUSEBUTTONDOWN:
695                     if (event.button.button == SDL_BUTTON_LEFT) {
696                         /* see if we are in the menu */
697                         if (event.button.x >= MENULX && event.button.x < MENURX && event.button.y >= starty && event.button.y < (starty + height)) {
698                             current_menu_high = (event.button.y - starty) / game.font[font].height;
699                             /* make sure the option isn't blank */
700                             if (*menu_items[game.menu][current_menu_high] != '\0') {
701                                 #ifndef NOAUDIO
702                                 if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
703                                 #endif
704                                 return current_menu_high;
705                             }
706                         }
707                     }
708                     /* select the menu option with the middle mouse button */
709                     else if (event.button.button == 2) {
710                         #ifndef NOAUDIO
711                         if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
712                         #endif
713                         return current_menu_high;
714                     }
715                     /* exit the menu / game with the right mouse button */
716                     if (event.button.button == 3) {
717                         #ifndef NOAUDIO
718                         if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
719                         #endif
720                         return game.menu_item_cnt - 1;
721                     }
722                     /* move up in the menu */
723                     else if (event.button.button == 4)
724                         current_menu_high = move_in_menu(menu_items, current_menu_high, DIR_UP);
725                     /* move down in the menu */
726                     else if (event.button.button == 5)
727                         current_menu_high = move_in_menu(menu_items, current_menu_high, DIR_DOWN);
728                     /* change the sound volume */
729                     else if (event.button.x >= 254 && event.button.x < 254 + 129 && event.button.y >= 456 && event.button.y < 488)
730                         change_volume(SOUND, event.button.x - 254, 252, 456, TOGGLE_NO, MOVE_SLIDER);
731                     /* change the music volume */
732                     else if (event.button.x >= 484 && event.button.x < 613 && event.button.y >= 456 && event.button.y < 488 && game.music_enabled)
733                         change_volume(MUSIC, event.button.x - 484, 482, 456, TOGGLE_NO, MOVE_SLIDER);
734                     break;
735                 case SDL_KEYDOWN:
736                     keysym = event.key.keysym;
737                     if (game.menu == NEW_GAME && game.last_menu_high == 4) {
738                         /* adding a character to the player name */
739                         if (keysym.unicode > 31 && keysym.unicode < 127 && location < 20) {
740                             if (word_width(game.player_name, hifont) + game.font[font].width[keysym.unicode - 32] <= max_word_len) {
741                                 #ifndef NOAUDIO
742                                 if (game.audio_enabled) Mix_PlayChannel(KEYBOARD_CLICK, game.sound[KEYBOARD_CLICK], 0);
743                                 #endif
744                                 game.player_name[location] = keysym.unicode;
745                                 game.player_name[location+1] = '\0';
746                                 fill(destxpn, destypn, MENURX - destxpn, game.font[font].height, 0, 0, 0);
747                                 print_word(game.player_name, destxpn, destypn, hifont);
748                                 location++;
749                                 destxc += game.font[hifont].width[keysym.unicode - 32];
750                             }
751                         }
752                         /* erase one character from the player name */
753                         else if (keysym.sym == SDLK_BACKSPACE && location > 0) {
754                             location--;
755                             destxc -= game.font[hifont].width[game.player_name[location] - 32];
756                             game.player_name[location] = '\0';
757                             fill(destxpn, destypn, MENURX - destxpn, game.font[font].height, 0, 0, 0);
758                             print_word(game.player_name, destxpn, destypn, hifont);
759                             #ifndef NOAUDIO
760                             if (game.audio_enabled) Mix_PlayChannel(KEYBOARD_CLICK, game.sound[KEYBOARD_CLICK], 0);
761                             #endif
762                         }
763                         /* highlight the Start Game option */
764                         else if (keysym.sym == SDLK_RETURN || keysym.sym == SDLK_KP_ENTER) {
765                             current_menu_high = 0;
766                             #ifndef NOAUDIO
767                             if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
768                             #endif
769                             break;
770                         }
771                     }
772                     /* don't only allow the keys f, m, s & w to be used when
773                        entering a player's name */
774                     else {
775                         /* needed under Windows to be able to use Alt-F4 to quit */
776                         if (keysym.sym == SDLK_F4 && (keysym.mod & KMOD_ALT))
777                             cleanup_and_exit();
778                         /* toggle between full screen/window mode */
779                         else if (keysym.sym == SDLK_f || keysym.sym == SDLK_F4) {
780                             change_display_mode();
781                             if (game.menu == OPTIONS)
782                                 game.options.opt_num[3] = game.display_mode;
783                             create_menu(menu_items, sub_menu_items);
784                         }
785                         /* turn the background music on/off */
786                         else if (keysym.sym == SDLK_m && game.music_enabled) {
787                             if (game.music_volume == 0)
788                                 change_volume(MUSIC, game.old_music_volume, 482, 456, TOGGLE_YES, MOVE_SLIDER);
789                             else {
790                                 game.old_music_volume = game.music_volume;
791                                 change_volume(MUSIC, 0, 482, 456, TOGGLE_YES, MOVE_SLIDER);
792                             }
793                         }
794                         /* turn the sound effects on/off */
795                         else if (keysym.sym == SDLK_s) {
796                             if (game.sound_volume == 0)
797                                 change_volume(SOUND, game.old_sound_volume, 252, 456, TOGGLE_YES, MOVE_SLIDER);
798                             else {
799                                 game.old_sound_volume = game.sound_volume;
800                                 change_volume(SOUND, 0, 252, 456, TOGGLE_YES, MOVE_SLIDER);
801                             }
802                         }
803                     }
804                     /* exit the menu / game */
805                     if (keysym.sym == SDLK_ESCAPE) {
806                         #ifndef NOAUDIO
807                         if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
808                         #endif
809                         return game.menu_item_cnt - 1;
810                     }
811                     /* select the menu option */
812                     else if (keysym.sym == SDLK_RETURN || keysym.sym == SDLK_KP_ENTER) {
813                         #ifndef NOAUDIO
814                         if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
815                         #endif
816                         return current_menu_high;
817                     }
818                     /* change to next option or move down in menu */
819                     else if (keysym.sym == SDLK_RIGHT) {
820                         if (strchr(menu_items[game.menu][current_menu_high],':') == NULL)
821                             current_menu_high = move_in_menu(menu_items, current_menu_high, DIR_DOWN);
822                         else if (game.menu == NEW_GAME && current_menu_high == 3) {
823                         }
824                         else {
825                             #ifndef NOAUDIO
826                             if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
827                             #endif
828                             return current_menu_high;
829                         }
830                     }
831                     /* change to previous option or move up in menu */
832                     else if (keysym.sym == SDLK_LEFT) {
833                         if (strchr(menu_items[game.menu][current_menu_high],':') == NULL)
834                             current_menu_high = move_in_menu(menu_items, current_menu_high, DIR_UP);
835                         else if (game.menu == NEW_GAME && current_menu_high == 3) {
836                         }
837                         else {
838                             #ifndef NOAUDIO
839                             if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
840                             #endif
841                             game.next_option = 0;
842                             return current_menu_high;
843                         }
844                     }
845                     /* move up in the menu */
846                     else if (keysym.sym == SDLK_UP || keysym.sym == SDLK_KP8 || (keysym.sym == SDLK_TAB && (keysym.mod & KMOD_SHIFT)))
847                         current_menu_high = move_in_menu(menu_items, current_menu_high, DIR_UP);
848                     /* move down in the menu */
849                     else if (keysym.sym == SDLK_DOWN || keysym.sym == SDLK_KP2 || (keysym.sym == SDLK_TAB && !(keysym.mod & KMOD_SHIFT)))
850                         current_menu_high = move_in_menu(menu_items, current_menu_high, DIR_DOWN);
851                     break;
852                 case SDL_QUIT:
853                     cleanup_and_exit();
854             }
855         }
856         if (game.last_menu_high != current_menu_high) {
857             /* unhighlight the last menu item */
858             if (strchr(menu_items[game.menu][game.last_menu_high],':') == NULL) {
859                 print_word(menu_items[game.menu][game.last_menu_high], -800, starty + game.last_menu_high * game.font[font].height, font);
860             }
861             else {
862                 print_word(menu_items[game.menu][game.last_menu_high], MENULX, starty + game.last_menu_high * game.font[font].height, font);
863                 /* special case for player name */
864                 if (game.menu == NEW_GAME && game.last_menu_high == 4) {
865                     fill(destxpn, destypn, MENURX - destxpn, game.font[font].height, 0, 0, 0);
866                     print_word(game.player_name, destxpn, destypn, font);
867                 }
868                 else {
869                     destx = MENURX - word_width(sub_menu_items[game.options.opt_group[game.last_menu_high]][game.options.opt_num[game.last_menu_high]], font);
870                     print_word(sub_menu_items[game.options.opt_group[game.last_menu_high]][game.options.opt_num[game.last_menu_high]], destx, starty + game.last_menu_high * game.font[font].height, font);
871                 }
872             }
873             /* highlight the curent menu item */
874             if (strchr(menu_items[game.menu][current_menu_high],':') == NULL)
875                 print_word(menu_items[game.menu][current_menu_high], -800, starty + current_menu_high * game.font[font].height, hifont);
876             else {
877                 print_word(menu_items[game.menu][current_menu_high], MENULX, starty + current_menu_high * game.font[font].height, hifont);
878                 /* special case for player name */
879                 if (game.menu == NEW_GAME && current_menu_high == 4) {
880                     fill(destxpn, destypn, MENURX - destxpn, game.font[font].height, 0, 0, 0);
881                     print_word(game.player_name, destxpn, destypn, hifont);
882                 }
883                 else {
884                     destx = MENURX - word_width(sub_menu_items[game.options.opt_group[current_menu_high]][game.options.opt_num[current_menu_high]], font);
885                     print_word(sub_menu_items[game.options.opt_group[current_menu_high]][game.options.opt_num[current_menu_high]], destx, starty + current_menu_high * game.font[font].height, hifont);
886                 }
887             }
888             game.last_menu_high = current_menu_high;
889         }
890         /* blink the cursor */
891         if (game.menu == NEW_GAME && game.last_menu_high == 4) {
892             if (!cursor && SDL_GetTicks() - cursor_blink_time > 200) {
893                 cursor_blink_time = SDL_GetTicks();
894                 cursor = 1;
895                 print_char('_', destxc, destypn, hifont);
896             }
897             else if (cursor && SDL_GetTicks() - cursor_blink_time > 200) {
898                 cursor_blink_time = SDL_GetTicks();
899                 cursor = 0;
900                 fill(destxc, destypn, word_width("_", font), game.font[font].height, 0, 0, 0);
901             }
902         }
903         if (game.blit_done) {
904             SDL_UpdateRect(game.screen, 0, 0, 0, 0);
905             game.blit_done = 0;
906         }
907         SDL_Delay(10);
908     }
909 
910 }
911 
move_in_menu(char * menu_items[][MAX_IN_MENU],int current_menu_high,int direction)912 int move_in_menu(char *menu_items[][MAX_IN_MENU], int current_menu_high, int direction) {
913 
914     if (direction == DIR_UP) {
915         #ifndef NOAUDIO
916         if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
917         #endif
918         current_menu_high--;
919         /* if option blank, go to previous */
920         while (*menu_items[game.menu][current_menu_high] == '\0')
921             current_menu_high--;
922         /* wrap around to the bottom entry */
923         if (current_menu_high == -1)
924             current_menu_high = game.menu_item_cnt - 1;
925     }
926     /* move down in the menu */
927     else if (direction == DIR_DOWN) {
928         #ifndef NOAUDIO
929         if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
930         #endif
931         current_menu_high++;
932         /* if option blank, go to next */
933         while (*menu_items[game.menu][current_menu_high] == '\0')
934             current_menu_high++;
935         /* wrap around to the top entry */
936         if (current_menu_high == game.menu_item_cnt)
937             current_menu_high = 0;
938     }
939     return current_menu_high;
940 
941 }
942 
get_input_for_screen(int * screen_num,int max_screens,int screen)943 int get_input_for_screen(int *screen_num, int max_screens, int screen) {
944 
945     SDL_Event event;
946     SDL_keysym keysym;
947 
948     while (1) {
949         if (SDL_PollEvent(&event)) {
950             switch (event.type) {
951                 case SDL_MOUSEBUTTONDOWN:
952                     /* show the next screen */
953                     if (event.button.button == SDL_BUTTON_LEFT || event.button.button == 5) {
954                         (*screen_num)++;
955                         if (*screen_num == max_screens) *screen_num = 0;
956                         return 0;
957                     }
958                     /* return to the main menu with the right mouse button */
959                     else if (event.button.button == 3)
960                         return 1;
961                     else if (event.button.button == 4) {
962                         (*screen_num)--;
963                         if (*screen_num < 0) *screen_num = max_screens - 1;
964                         return 0;
965                     }
966                     break;
967                 case SDL_KEYDOWN:
968                     keysym = event.key.keysym;
969                     /* needed under Windows to be able to use Alt-F4 to quit */
970                     if (keysym.sym == SDLK_F4 && (keysym.mod & KMOD_ALT))
971                         cleanup_and_exit();
972                     /* toggle between full screen/window mode */
973                     else if (keysym.sym == SDLK_f || keysym.sym == SDLK_F4) {
974                         change_display_mode();
975                         game.option_toggle = 1;
976                         return 0;
977                     }
978                     /* change the color of the l & r */
979                     if (keysym.sym >= SDLK_0 && keysym.sym <= SDLK_9 && game.skill_level == INSANE) {
980                         game.color_l_and_r[keysym.sym - 48] = !(game.color_l_and_r[keysym.sym - 48] - 48) + 48;
981                         game.fading_off_temp = 1;
982                         return 0;
983                     }
984                     /* change the block set */
985                     else if (keysym.sym == SDLK_b && *screen_num == 4 && screen == HELP) {
986                         game.block_set++;
987                         if (game.block_set == NUM_BLOCK_SETS)
988                             game.block_set = SQUARE_BRICKS;
989                         game.fading_off_temp = 1;
990                         change_block_set();
991                         return 0;
992                     }
993                     /* change the style of the corners of the blocks */
994                     else if (keysym.sym == SDLK_c && *screen_num == 4 && screen == HELP) {
995                         if (game.corner_style == SQUARE)
996                             game.corner_style = ROUNDED;
997                         else
998                             game.corner_style = SQUARE;
999                         game.fading_off_temp = 1;
1000                         return 0;
1001                     }
1002                     /* turn the background music on/off */
1003                     else if (keysym.sym == SDLK_m && game.music_enabled) {
1004                         if (game.music_volume == 0)
1005                             change_volume(MUSIC, game.old_music_volume, 0, 0, TOGGLE_YES, NO_SLIDER);
1006                         else {
1007                             game.old_music_volume = game.music_volume;
1008                             change_volume(MUSIC, 0, 0, 0, TOGGLE_YES, NO_SLIDER);
1009                         }
1010                     }
1011                     /* turn the sound effects on/off */
1012                     else if (keysym.sym == SDLK_s) {
1013                         if (game.sound_volume == 0)
1014                             change_volume(SOUND, game.old_sound_volume, 0, 0, TOGGLE_YES, NO_SLIDER);
1015                         else {
1016                             game.old_sound_volume = game.sound_volume;
1017                             change_volume(SOUND, 0, 0, 0, TOGGLE_YES, NO_SLIDER);
1018                         }
1019                     }
1020                     /* return to the main menu */
1021                     else if (keysym.sym == SDLK_ESCAPE)
1022                         return 1;
1023                     /* show the previous screen */
1024                     else if (keysym.sym == SDLK_LEFT || keysym.sym == SDLK_UP || keysym.sym == SDLK_KP8 || (keysym.sym == SDLK_TAB && (keysym.mod & KMOD_SHIFT))) {
1025                         (*screen_num)--;
1026                         if (*screen_num < 0) *screen_num = max_screens - 1;
1027                         return 0;
1028                     }
1029                     /* show the next screen */
1030                     else if (keysym.sym == SDLK_RIGHT || keysym.sym == SDLK_DOWN || keysym.sym == SDLK_KP2 || (keysym.sym == SDLK_TAB && !(keysym.mod & KMOD_SHIFT))) {
1031                         (*screen_num)++;
1032                         if (*screen_num == max_screens) *screen_num = 0;
1033                         return 0;
1034                     }
1035                     break;
1036                 case SDL_QUIT:
1037                     cleanup_and_exit();
1038             }
1039         }
1040         SDL_Delay(10);
1041     }
1042 
1043 }
1044 
display_high_scores(int skill_level)1045 void display_high_scores(int skill_level) {
1046 
1047     struct highscore_info highscores[NUM_SKILLS * 2][10];
1048 
1049     read_highscore_file(highscores);
1050     show_scores_for_skill_level(highscores, skill_level);
1051     while (get_input_for_screen(&skill_level, NUM_SKILLS * 2, HIGHSCORES) == 0)
1052         show_scores_for_skill_level(highscores, skill_level);
1053     #ifndef NOAUDIO
1054     if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
1055     #endif
1056 
1057 }
1058 
show_scores_for_skill_level(struct highscore_info highscores[][10],int skill_level)1059 void show_scores_for_skill_level(struct highscore_info highscores[][10], int skill_level) {
1060 
1061     int destx, desty, font, x;
1062     char *skill_levels[NUM_SKILLS * 2] = {"Kids", "Easy", "Normal", "Hard",
1063                                           "Very Hard", "Insane", "Kids", "Easy",
1064                                           "Normal", "Hard", "Very Hard", "Insane"};
1065 
1066     if (game.audio_enabled && !game.option_toggle) {
1067         #ifndef NOAUDIO
1068         Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
1069         #endif
1070     }
1071     else
1072         game.option_toggle = 0;
1073     if (game.fading_effect)
1074         fade_screen(FADE_OUT, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
1075     font = SERIFG20;
1076     blank_screen();
1077     print_word("High Scores", -800, 5, SERIFG24);
1078     print_word("Skill Level:", 313, 35, SERIFW20);
1079     print_word("Gravity:", 338, 60, SERIFW20);
1080     /* uncomment next line to see skill level centered */
1081     /*print_word("Skill Level:Very Hard   Gravity: Off", -800, 60, SERIFW20);*/
1082     destx = 313 + word_width("Skill Level:", SERIFW20);
1083     print_word(skill_levels[skill_level], destx, 35, SERIFW20);
1084     destx = 338 + word_width("Gravity:", SERIFW20);
1085     if (skill_level >= NUM_SKILLS)
1086         print_word("On", destx, 60, SERIFW20);
1087     else
1088         print_word("Off", destx, 60, SERIFW20);
1089     print_word("Best Scores", 100, 85, font);
1090     print_word("Best Times", 425, 85, font);
1091     /* horizontal */
1092     fill(85, 110, 630, 2, 0, 255, 0);
1093     /* vertical */
1094     fill(420, 85, 2, 325, 0, 255, 0);
1095     print_word("Name", 110, 125, font);
1096     print_word("Score", 318, 125, font);
1097     print_word("Level", 370, 125, font);
1098     print_word("Name", 425, 125, font);
1099     print_word("Time", 635, 125, font);
1100     desty = 170;
1101     for (x = 0; x < 10; x++) {
1102         /* print the position number */
1103         num_to_str(x + 1, 2);
1104         destx = 25 - word_width(game.temp_string, font) + 80;
1105         print_number(destx, desty, font, x + 1, 2);
1106         print_char('.', 106, desty, font);
1107         /* name for high score */
1108         print_word(highscores[skill_level][x].name_score, 110, desty, font);
1109         /* high score */
1110         if (highscores[skill_level][x].score > 0) {
1111             num_to_str(highscores[skill_level][x].score, 5);
1112             destx = 355 - word_width(game.temp_string, font);
1113             print_word(game.temp_string, destx, desty, font);
1114             free(game.temp_string);
1115             /* level reached for high score */
1116             num_to_str(highscores[skill_level][x].level, 3);
1117             destx = 405 - word_width(game.temp_string, font);
1118             print_word(game.temp_string, destx, desty, font);
1119             free(game.temp_string);
1120         }
1121         /* name for best time */
1122         print_word(highscores[skill_level][x].name_time, 425, desty, font);
1123         /* best time */
1124         if (highscores[skill_level][x].time != 120000) {
1125             print_time(635, desty, font, highscores[skill_level][x].time);
1126         }
1127         desty += game.font[font].height;
1128     }
1129     print_word("Use the mouse wheel, left mouse button or arrow keys to change skill level.", -800, 600 - game.font[SERIFG20].height * 2, SERIFW20);
1130     print_word("Press the right mouse button or Esc to return to the main menu.", -800, 600 - game.font[SERIFG20].height, SERIFW20);
1131     if (game.fading_effect)
1132         fade_screen(FADE_IN, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
1133     SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1134 
1135 }
1136 
display_help()1137 void display_help() {
1138 
1139     int page_num = 0;
1140     show_help_page_number(page_num);
1141     while (get_input_for_screen(&page_num, 6, HELP) == 0)
1142         show_help_page_number(page_num);
1143     #ifndef NOAUDIO
1144     if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
1145     #endif
1146 
1147 }
1148 
show_help_page_number(int page_num)1149 void show_help_page_number(int page_num) {
1150 
1151     int desty, color_l_and_r;
1152     char page_x_of_num[7] = "? of 6";
1153 
1154     if (game.audio_enabled && !game.option_toggle) {
1155         #ifndef NOAUDIO
1156         Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
1157         #endif
1158     }
1159     else
1160         game.option_toggle = 0;
1161     if (game.fading_effect && !game.fading_off_temp)
1162         fade_screen(FADE_OUT, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
1163     /* if we have more than 9 pages, this won't work! */
1164     page_x_of_num[0] = page_num + 49;
1165     desty = 20;
1166     blank_screen();
1167     switch (page_num) {
1168         /* page one */
1169         case 0: {
1170             print_word("How to Play the Game", -800, 0, SERIFW24);
1171             print_word("The object of the game is to remove all of the blocks from the screen before the time runs out.", 20, desty+=game.font[SERIFG20].height, SERIFG20);
1172             print_word("Two blocks are removed at a time, and must be of the same color. After completing a level, you", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1173             print_word("will be rewarded with a bonus point for every tick left on the clock. For each level thereafter, the", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1174             print_word("time to complete the level will be shorter.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1175             desty+=10;
1176             print_word("Shortcut Keys Used During the Game", -800, desty+=game.font[SERIFG20].height, SERIFW24);
1177             desty+=20;
1178             print_word("b -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1179             print_word("change the block set", 5 + word_width("b - ", SERIFG20), desty, SERIFG20);
1180             print_word("c -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1181             print_word("change the corner style of the blocks", 5 + word_width("c - ", SERIFG20), desty, SERIFG20);
1182             print_word("f, F4 -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1183             print_word("toggle between full screen/window mode (can be used anywhere)", 5 + word_width("f, F4 - ", SERIFG20), desty, SERIFG20);
1184             print_word("h, F1 -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1185             print_word("display the help screen", 5 + word_width("h, F1 - ", SERIFG20), desty, SERIFG20);
1186             print_word("m -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1187             print_word("turn background music on/off (can be used anywhere)", 5 + word_width("m - ", SERIFG20), desty, SERIFG20);
1188             print_word("n, F2 -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1189             print_word("start a new game (highscores won't be saved ending a game this way)", 5 + word_width("n, F2 - ", SERIFG20), desty, SERIFG20);
1190             print_word("p, Pause, F3 -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1191             print_word("pause/unpause the game", 5 + word_width("p, Pause, F3 - ", SERIFG20), desty, SERIFG20);
1192             print_word("q, Esc -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1193             print_word("quit the game", 5 + word_width("q, Esc - ", SERIFG20), desty, SERIFG20);
1194             print_word("s -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1195             print_word("turn sound effects on/off (can be used anywhere)", 5 + word_width("s - ", SERIFG20), desty, SERIFG20);
1196             print_word("0 - 9 -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1197             print_word("toggle the L & R colors on Insane mode (can be used anywhere)", 5 + word_width("0 - 9 - ", SERIFG20), desty, SERIFG20);
1198             desty+=10;
1199             print_word("Moving in the Menu", -800, desty+=game.font[SERIFG20].height, SERIFW24);
1200             desty+=20;
1201             print_word("To move in the menu, you have several options besides highlighting with the mouse and clicking", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1202             print_word("the menu/option with the left mouse button.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1203             desty+=20;
1204             print_word("moving up -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1205             print_word("Up arrow, TAB key or the mouse wheel", 5 + word_width("moving up - ", SERIFG20), desty, SERIFG20);
1206             break;
1207         }
1208         /* page two */
1209         case 1: {
1210             print_word("Moving in the Menu", -800, 0, SERIFW24);
1211             print_word("moving down -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1212             print_word("Down arrow, Shift-TAB or the mouse wheel", 5 + word_width("moving down - ", SERIFG20), desty, SERIFG20);
1213             print_word("choosing a menu/option -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1214             print_word("Enter or middle mouse button", 5 + word_width("choosing a menu/option - ", SERIFG20), desty, SERIFG20);
1215             print_word("changing options -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1216             print_word("left or right arrow keys", 5 + word_width("changing options - ", SERIFG20), desty, SERIFG20);
1217             print_word("exit a menu/sub-menu -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1218             print_word("Esc key or right mouse button", 5 + word_width("exit a menu/sub-menu - ", SERIFG20), desty, SERIFG20);
1219             desty+=10;
1220             print_word("Skill Levels", -800, desty+=game.font[SERIFG20].height, SERIFW24);
1221             desty+=20;
1222             print_word("Kids -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1223             print_word("Match two blocks of the same color. Where they are doesn't matter.", 5 + word_width("Kids - ", SERIFG20), desty, SERIFG20);
1224             desty+=20;
1225             print_word("Easy -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1226             print_word("The path used to connect the blocks can only be up, down, left or right (no moving", 5 + word_width("Easy - ", SERIFG20), desty, SERIFG20);
1227             print_word("diagonally). As well, you can't go through other blocks to get to the one you are looking for.", 5 + word_width("Easy - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1228             print_word("The path can turn a maximum of three times in this skill. Examples for this (and other skills)", 5 + word_width("Easy - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1229             print_word("are on page 5.", 5 + word_width("Easy - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1230             desty+=20;
1231             print_word("Normal -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1232             print_word("This is the standard mode of play. Same rules as Easy, except a path can only take two", 5 + word_width("Normal - ", SERIFG20), desty, SERIFG20);
1233             print_word("turns to get to the other block.", 5 + word_width("Normal - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1234             desty+=20;
1235             print_word("Hard -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1236             print_word("Same rules as Normal, except that when you choose two blocks that can't be removed, you", 5 + word_width("Hard - ", SERIFG20), desty, SERIFG20);
1237             print_word("will lose a point from your score & one tick of time from the clock. Two blocks already", 5 + word_width("Hard - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1238             print_word("eliminated will also be put back on the screen. If no blocks have been removed yet, you", 5 + word_width("Hard - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1239             print_word("will lose three points and three ticks on the clock. As well, if you click on a location where", 5 + word_width("Hard - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1240             print_word("a block has been removed, and you currently have a block highlighted, it is considered a", 5 + word_width("Hard - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1241             print_word("wrong move.", 5 + word_width("Hard - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1242             break;
1243         }
1244         /* page three */
1245         case 2: {
1246             print_word("Skill Levels", -800, 0, SERIFW24);
1247             print_word("Very Hard -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1248             print_word("Same rules as Hard, except the blocks will randomize in color several times throughout", 5 + word_width("Very Hard - ", SERIFG20), desty, SERIFG20);
1249             print_word("the game.", 5 + word_width("Very Hard - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1250             desty+=20;
1251             print_word("Insane -", 5, desty+=game.font[SERIFW20].height, SERIFW20);
1252             print_word("Same rules as Very Hard, except you must now use both the left & right mouse buttons to", 5 + word_width("Insane - ", SERIFG20), desty, SERIFG20);
1253             print_word("remove blocks. The blocks will have an L or an R. It isn't necessary to match two L blocks", 5 + word_width("Insane - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1254             print_word("or two R blocks. All that matters is that you must hit the block with the correct mouse", 5 + word_width("Insane - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1255             print_word("button.", 5 + word_width("Insane - ", SERIFG20), desty+=game.font[SERIFG20].height, SERIFG20);
1256             desty+=10;
1257             print_word("Bonus Scoring", -800, desty+=game.font[SERIFG20].height, SERIFW24);
1258             desty+=20;
1259             print_word("In addition to receiving a point for every tick left on the clock at the end of a level, there are also", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1260             print_word("two other ways to receive bonus scoring.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1261             desty+=20;
1262             print_word("If you remove sets of the same color in a row, you will receive bonus points. For the first two sets,", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1263             print_word("you get 5 points. The third set will get you 10 points, the fourth will be 15 points & all five sets will", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1264             print_word("be 25 points. If you do the same thing again with another set of five, the points will be the same as", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1265             print_word("above, plus an extra 10 points. The third set of five will get you 20 points & so on. Up to 1000", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1266             print_word("points can be received for clearing the whole level this way. Making a wrong move won't erase", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1267             print_word("your bonus, but your chain will be broken.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1268             desty+=20;
1269             print_word("If you don't make any wrong moves during the level, you will receive 50 points. Do it again for the", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1270             print_word("next level & you will get 60 points. It will keep increasing by 10 points for every level & will be", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1271             print_word("reset if you make a wrong at any point in a level.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1272             break;
1273         }
1274         /* page four */
1275         case 3: {
1276             print_word("Other Options", -800, 0, SERIFW24);
1277             print_word("There are two unique modes of game play for the game. You can choose to have the gravity either", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1278             print_word("off or on. When two blocks are removed from the screen with gravity on, all the blocks above", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1279             print_word("them will fall into the empty spot. Blocks won't move at all when this is turned off.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1280             desty+=20;
1281             print_word("When pausing the game, you can have it minimize the game by changing it in the Options menu.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1282             desty+=20;
1283             print_word("By default, the menu system & screen changes will have a fading effect. If you don't like the", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1284             print_word("option or it doesn't work, it can be turned off in the Options menu.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1285             desty+=10;
1286             print_word("Tips & Notes", -800, desty+=game.font[SERIFG20].height, SERIFW24);
1287             desty+=20;
1288             print_word("For Hard, Very Hard and Insane; if you highlight a block and realize that a move can't be made;", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1289             print_word("you can un-highlight the block and you will not be penalized in any way.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1290             desty+=20;
1291             print_word("It is possible to have a game that can't be finished in the Normal skill.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1292             desty+=20;
1293             print_word("Even though there is plenty of time in the beginning levels to complete it, you are better off trying", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1294             print_word("to finish it as quickly as possible to obtain a higher score with the bonus points.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1295             desty+=20;
1296             print_word("If you have any ideas for new features for the game, let me know & I'll see if it can be added.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1297             print_word("Several new ideas came from players like you. You'll be given credit for ideas implemented on the", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1298             print_word("last page of the help screen.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1299             break;
1300         }
1301         /* page five */
1302         /* I know that I could've drawn these examples in the Gimp, rather than
1303            have so many blits, but then the user wouldn't have the dyanamic help
1304            screen where they can press 'b' & 'c' to the change the block set &
1305            corner style & see the results! ;-) */
1306         case 4: {
1307             print_word("Examples", -800, 0, SERIFW24);
1308             blit(game.surface[BLOCKS], DARKBLUE, 0, 32, 32, 5, desty+=game.font[SERIFG20].height);
1309             change_corner_style(5, desty);
1310             blit(game.surface[BLOCKS], RED * 32, 0, 32, 32, 37, desty);
1311             change_corner_style(37, desty);
1312             blit(game.surface[BLOCKS], GREEN * 32, 0, 32, 32, 69, desty);
1313             change_corner_style(69, desty);
1314             blit(game.surface[BLOCKS], YELLOW * 32, 0, 32, 32, 5, desty + 32);
1315             change_corner_style(5, desty + 32);
1316             blit(game.surface[BLOCKS], DARKBLUE * 32, 0, 32, 32, 37, desty + 32);
1317             change_corner_style(37, desty + 32);
1318             blit(game.surface[BLOCKS], LIGHTBLUE * 32, 0, 32, 32, 69, desty + 32);
1319             change_corner_style(69, desty + 32);
1320             blit(game.surface[BLOCKS], PURPLE * 32, 0, 32, 32, 5, desty + 64);
1321             change_corner_style(5, desty + 64);
1322             blit(game.surface[BLOCKS], GREY * 32, 0, 32, 32, 37, desty + 64);
1323             change_corner_style(37, desty + 64);
1324             blit(game.surface[BLOCKS], BROWN * 32, 0, 32, 32, 69, desty + 64);
1325             change_corner_style(69, desty + 64);
1326             print_word("The two blue blocks can only be removed on the Kids skill, since the middle blue one", 115, desty, SERIFG20);
1327             print_word("is surrounded by other blocks, and we can't move diagonally.", 115, desty+=game.font[SERIFG20].height, SERIFG20);
1328             desty = 20 + game.font[SERIFG20].height + 96;
1329             blit(game.surface[PATH_BLOCKS], LINE_LR * 32, 0, 32, 32, 5, desty);
1330             blit(game.surface[PATH_BLOCKS], LINE_HOR * 32, 0, 32, 32, 37, desty);
1331             blit(game.surface[PATH_BLOCKS], LINE_LL * 32, 0, 32, 32, 69, desty);
1332             blit(game.surface[BLOCKS], ORANGE * 32, 0, 32, 32, 5, desty + 32);
1333             change_corner_style(5, desty + 32);
1334             blit(game.surface[BLOCKS], PINK * 32, 0, 32, 32, 37, desty + 32);
1335             change_corner_style(37, desty + 32);
1336             blit(game.surface[PATH_BLOCKS], LINE_VER * 32, 0, 32, 32, 69, desty + 32);
1337             blit(game.surface[BLOCKS], RED * 32, 0, 32, 32, 5, desty + 64);
1338             change_corner_style(5, desty + 64);
1339             blit(game.surface[BLOCKS], ORANGE * 32, 0, 32, 32, 37, desty + 64);
1340             change_corner_style(37, desty + 64);
1341             blit(game.surface[PATH_BLOCKS], LINE_UL * 32, 0, 32, 32, 69, desty + 64);
1342             print_word("In addition to the Kids skill, the two orange blocks can be removed on the Easy skill", 115, desty+=32, SERIFG20);
1343             print_word("as well since there are three turns to find the path.", 115, desty+=game.font[SERIFG20].height, SERIFG20);
1344             desty = 20 + game.font[SERIFG20].height + 192;
1345             blit(game.surface[PATH_BLOCKS], LINE_LR * 32, 0, 32, 32, 5, desty);
1346             blit(game.surface[PATH_BLOCKS], LINE_HOR * 32, 0, 32, 32, 37, desty);
1347             blit(game.surface[PATH_BLOCKS], LINE_HOR * 32, 0, 32, 32, 69, desty);
1348             blit(game.surface[PATH_BLOCKS], LINE_LL * 32, 0, 32, 32, 101, desty);
1349             blit(game.surface[BLOCKS], GREEN * 32, 0, 32, 32, 5, desty + 32);
1350             change_corner_style(5, desty + 32);
1351             blit(game.surface[BLOCKS], LIGHTBLUE * 32, 0, 32, 32, 37, desty + 32);
1352             change_corner_style(37, desty + 32);
1353             blit(game.surface[BLOCKS], GREEN * 32, 0, 32, 32, 101, desty + 32);
1354             change_corner_style(101, desty + 32);
1355             blit(game.surface[BLOCKS], RED * 32, 0, 32, 32, 5, desty + 64);
1356             change_corner_style(5, desty + 32);
1357             blit(game.surface[PATH_BLOCKS], LINE_UR * 32, 0, 32, 32, 37, desty + 64);
1358             blit(game.surface[BLOCKS], LIGHTBLUE * 32, 0, 32, 32, 69, desty + 64);
1359             change_corner_style(69, desty + 32);
1360             blit(game.surface[BLOCKS], RED * 32, 0, 32, 32, 5, desty + 96);
1361             change_corner_style(5, desty + 96);
1362             blit(game.surface[BLOCKS], YELLOW * 32, 0, 32, 32, 37, desty + 96);
1363             change_corner_style(37, desty + 96);
1364             blit(game.surface[PATH_BLOCKS], LINE_HOR * 32, 0, 32, 32, 69, desty + 96);
1365             blit(game.surface[BLOCKS], YELLOW * 32, 0, 32, 32, 101, desty + 96);
1366             change_corner_style(101, desty + 96);
1367             print_word("All of the blocks can be removed on any skill level, since they are all between 0", 147, desty+=32, SERIFG20);
1368             print_word("and 2 turns for the path or next to each other like the red ones.", 147, desty+=game.font[SERIFG20].height, SERIFG20);
1369             desty = 20 + game.font[SERIFG20].height + 320;
1370             blit(game.surface[PATH_BLOCKS], LINE_LR * 32, 0, 32, 32, 5, desty);
1371             blit(game.surface[PATH_BLOCKS], LINE_HOR * 32, 0, 32, 32, 37, desty);
1372             blit(game.surface[PATH_BLOCKS], LINE_HOR * 32, 0, 32, 32, 69, desty);
1373             blit(game.surface[PATH_BLOCKS], LINE_LL * 32, 0, 32, 32, 101, desty);
1374             blit(game.surface[BLOCKS], DARKBLUE * 32, 0, 32, 32, 5, desty + 32);
1375             color_l_and_r = game.color_l_and_r[DARKBLUE] - 48;
1376             blit(game.surface[2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, 5, desty + 32);
1377             change_corner_style(5, desty + 32);
1378             blit(game.surface[BLOCKS], LIGHTBLUE * 32, 0, 32, 32, 37, desty + 32);
1379             color_l_and_r = game.color_l_and_r[LIGHTBLUE] - 48;
1380             blit(game.surface[BLACK_L + color_l_and_r], 0, 0, 32, 32, 37, desty + 32);
1381             change_corner_style(37, desty + 32);
1382             blit(game.surface[BLOCKS], GREEN * 32, 0, 32, 32, 69, desty + 32);
1383             color_l_and_r = game.color_l_and_r[GREEN] - 48;
1384             blit(game.surface[2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, 69, desty + 32);
1385             change_corner_style(69, desty + 32);
1386             blit(game.surface[BLOCKS], DARKBLUE * 32, 0, 32, 32, 101, desty + 32);
1387             color_l_and_r = game.color_l_and_r[DARKBLUE] - 48;
1388             blit(game.surface[BLACK_L + color_l_and_r], 0, 0, 32, 32, 101, desty + 32);
1389             change_corner_style(101, desty + 32);
1390             blit(game.surface[BLOCKS], PURPLE * 32, 0, 32, 32, 5, desty + 64);
1391             color_l_and_r = game.color_l_and_r[PURPLE] - 48;
1392             blit(game.surface[BLACK_L + color_l_and_r], 0, 0, 32, 32, 5, desty + 64);
1393             change_corner_style(5, desty + 32);
1394             blit(game.surface[BLOCKS], GREY * 32, 0, 32, 32, 37, desty + 64);
1395             color_l_and_r = game.color_l_and_r[GREY] - 48;
1396             blit(game.surface[BLACK_L + color_l_and_r], 0, 0, 32, 32, 37, desty + 64);
1397             change_corner_style(37, desty + 32);
1398             blit(game.surface[BLOCKS], RED * 32, 0, 32, 32, 69, desty + 64);
1399             color_l_and_r = game.color_l_and_r[RED] - 48;
1400             blit(game.surface[2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, 69, desty + 64);
1401             change_corner_style(69, desty + 96);
1402             blit(game.surface[BLOCKS], BROWN * 32, 0, 32, 32, 101, desty + 64);
1403             color_l_and_r = game.color_l_and_r[BROWN] - 48;
1404             blit(game.surface[2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, 101, desty + 64);
1405             change_corner_style(101, desty + 96);
1406             blit(game.surface[BLOCKS], ORANGE * 32, 0, 32, 32, 5, desty + 96);
1407             color_l_and_r = game.color_l_and_r[ORANGE] - 48;
1408             blit(game.surface[BLACK_L + color_l_and_r], 0, 0, 32, 32, 5, desty + 96);
1409             change_corner_style(5, desty + 96);
1410             blit(game.surface[BLOCKS], YELLOW * 32, 0, 32, 32, 37, desty + 96);
1411             color_l_and_r = game.color_l_and_r[YELLOW] - 48;
1412             blit(game.surface[2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, 37, desty + 96);
1413             change_corner_style(37, desty + 96);
1414             blit(game.surface[BLOCKS], RED * 32, 0, 32, 32, 69, desty + 96);
1415             color_l_and_r = game.color_l_and_r[RED] - 48;
1416             blit(game.surface[2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, 69, desty + 96);
1417             change_corner_style(69, desty + 96);
1418             blit(game.surface[BLOCKS], PINK * 32, 0, 32, 32, 101, desty + 96);
1419             color_l_and_r = game.color_l_and_r[PINK] - 48;
1420             blit(game.surface[BLACK_L + color_l_and_r], 0, 0, 32, 32, 101, desty + 96);
1421             change_corner_style(101, desty + 96);
1422             print_word("You must hit the left blue block with the right mouse button and right blue one", 147, desty+=32, SERIFG20);
1423             print_word("with the left mouse button. The two reds must be removed with the right mouse", 147, desty+=game.font[SERIFG20].height, SERIFG20);
1424             print_word("button.", 147, desty+=game.font[SERIFG20].height, SERIFG20);
1425             desty+=48;
1426             print_word("Press 'b' or 'c' to see the different block & corner styles.", -800, desty, SERIFW20);
1427             print_word("Press the number keys to see the different L & R colors.", -800, desty+=game.font[SERIFG20].height, SERIFW20);
1428             break;
1429         }
1430         /* page six */
1431         case 5: {
1432             print_word("Credits & Thanks", -800, 0, SERIFW24);
1433             print_word("Conrad Rotondella at pocketfuel.com - for letting me use his excellent music loops in my game.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1434             desty += 20;
1435             print_word("All sounds are from flashkit.com. Only those with the Freeware license were used for the game.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1436             desty += 20;
1437             print_word("The Snake Skin blocks came from a tutorial by Marin Laetitia (titix) on the Gimp User Group web", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1438             print_word("site (gug.sunsite.dk).", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1439             desty += 20;
1440             print_word("The fading effect for menu/screen changes came from Bernhard Bliem's Wheep game", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1441             print_word("(wheep.sourceforge.net).", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1442             desty += 20;
1443             print_word("Michael Westerlund for the ideas to use the left & right mouse buttons on the Insane mode as well", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1444             print_word("as bonus scoring for multiple pairs of the same color.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1445             desty += 20;
1446             print_word("Iris Bussey for the idea of having gravity as an option similar to Shisen-Sho.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1447             desty += 20;
1448             print_word("S.C. G.M. K.O. & J.T.  - the first four beta testers who provided me with a lot of feedback while", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1449             print_word("they played the game in its earliest stages.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1450             desty += 20;
1451             print_word("Fellow members in the Orbz gaming community (you know who you are!) who expressed a great", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1452             print_word("deal of interest in seeing me complete this game.", 5, desty+=game.font[SERIFG20].height, SERIFG20);
1453             desty += 30;
1454             print_word("I hope you all have as much fun playing this game as I did writing it. Enjoy!", -800, desty+=game.font[SERIFW20].height, SERIFW20);
1455             break;
1456         }
1457     }
1458     print_word(page_x_of_num, 800 - word_width(page_x_of_num, SERIFG20) - 5, 0, SERIFW20);
1459     print_word("Use the mouse wheel, left mouse button or arrow keys to change help screens.", -800, 600 - game.font[SERIFG20].height * 2, SERIFW20);
1460     print_word("Press the right mouse button or Esc to return to the main menu.", -800, 600 - game.font[SERIFG20].height, SERIFW20);
1461     if (game.fading_effect && !game.fading_off_temp)
1462         fade_screen(FADE_IN, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
1463     SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1464     game.fading_off_temp = 0;
1465 
1466 }
1467 
blit(SDL_Surface * source,Sint16 srcx,Sint16 srcy,Uint16 srcw,Uint16 srch,Sint16 destx,Sint16 desty)1468 void blit(SDL_Surface *source, Sint16 srcx, Sint16 srcy, Uint16 srcw, Uint16 srch, Sint16 destx, Sint16 desty) {
1469 
1470     SDL_Rect src, dest;
1471 
1472     src.x = srcx;
1473     src.y = srcy;
1474     src.w = srcw;
1475     src.h = srch;
1476     /* if destx or desty is negative, center graphic according to the co-ordinates */
1477     if (destx < 0)
1478         dest.x = (abs(destx) - srcw) / 2;
1479     else
1480         dest.x = destx;
1481     if (desty < 0)
1482         dest.y = (abs(desty) - srch) / 2;
1483     else
1484         dest.y = desty;
1485     SDL_BlitSurface(source, &src, game.screen, &dest);
1486     game.blit_done = 1;
1487 
1488 }
1489 
fill(Sint16 destx,Sint16 desty,Uint16 destw,Uint16 desth,Uint8 red,Uint8 green,Uint8 blue)1490 void fill(Sint16 destx, Sint16 desty, Uint16 destw, Uint16 desth, Uint8 red, Uint8 green, Uint8 blue) {
1491 
1492     SDL_Rect dest;
1493 
1494     dest.x = destx;
1495     dest.y = desty;
1496     dest.w = destw;
1497     dest.h = desth;
1498     SDL_FillRect(game.screen, &dest, SDL_MapRGB(game.screen->format, red, green, blue));
1499     game.blit_done = 1;
1500 
1501 }
1502 
blank_screen()1503 void blank_screen() {
1504 
1505     fill(0, 0, 800, 600, 0, 0, 0);
1506 
1507 }
1508 
word_width(char * word,int font)1509 int word_width(char *word, int font) {
1510 
1511     int i, len, chr;
1512 
1513     len = 0;
1514     for (i = 0; i < strlen(word); i++) {
1515         chr = word[i];
1516         len += game.font[font].width[chr - 32];
1517     }
1518     return len;
1519 
1520 }
1521 
play_game()1522 void play_game() {
1523 
1524     int level_time, start_new_game;
1525     int times[11] = {770, 690, 620, 560, 500, 450, 410, 370, 340, 310, 290};
1526 
1527     start_new_game = 1;
1528     while (start_new_game) {
1529         game.best_time = 500000;
1530         game.elapsed_time = level_time = game.score = game.exit_game = 0;
1531         game.score = game.no_wrong_moves = game.highx = game.highy = 0;
1532         game.current_level = game.not_over = 1;
1533         if (game.music_enabled) randomize_music();
1534         while (game.not_over) {
1535             /* save the fastest time for a level */
1536             if (level_time && game.stop_timer + game.added_time < game.best_time)
1537                 game.best_time = game.stop_timer + game.added_time;
1538             /* subtract a set amount of time after level 11 */
1539             if (game.current_level >= 11)
1540                 game.level_time -= 10;
1541             else
1542                 game.level_time = times[level_time];
1543             play_level();
1544             if (game.exit_game == NEW_GAME) {
1545                 start_new_game = 1;
1546                 break;
1547             }
1548             /* increase time for next level */
1549             if (level_time != 12)
1550                 level_time++;
1551         }
1552         if (game.exit_game != NEW_GAME) {
1553             start_new_game = game_over();
1554             #ifndef NOAUDIO
1555             if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
1556             #endif
1557         }
1558     }
1559     game.option_toggle = 1;
1560     display_high_scores(NUM_SKILLS * game.gravity + game.skill_level);
1561 
1562 }
1563 
play_level()1564 void play_level() {
1565 
1566     int erase_it, cur_music, time_to_add;
1567     struct path_info *path;
1568     #ifndef NOAUDIO
1569     char *filename[6] = {DATA_PREFIX "music/1-1.ogg",
1570                          DATA_PREFIX "music/2-1.ogg",
1571                          DATA_PREFIX "music/3-1.ogg",
1572                          DATA_PREFIX "music/4-1.ogg",
1573                          DATA_PREFIX "music/5-1.ogg",
1574                          DATA_PREFIX "music/6-1.ogg"};
1575     #endif
1576 
1577     cur_music = game.music_loop[game.current_level % MUSIC_LOOPS - 1];
1578     create_random_grid();
1579     game.level_start = game.start_timer = game.block_num = game.erased = 0;
1580     game.paused = game.paused_time = game.path_count = game.skipped_time = 0;
1581     game.prev_time = game.current_combos_removed = game.bonus_score = 0;
1582     game.current_sets_removed = game.blink = game.added_time = 0;
1583     game.no_matching_colors = game.random_block_time = 0;
1584     game.no_wrong_moves += 10;
1585     game.blink_time = 10080;
1586     game.last_color_removed = -1;
1587     /* we'll randomize the blocks every 20 - 25 seconds */
1588     time_to_add = 15000;
1589     draw_grid_on_screen();
1590     if (game.current_level == 1) {
1591         if (game.fading_effect)
1592             fade_screen(FADE_IN, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
1593         SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1594     }
1595 	/* main game loop */
1596     while (1) {
1597         game.elapsed_time = SDL_GetTicks() - (game.start_timer + game.paused_time);
1598         /* randomize the blocks for Very Hard & Insane
1599            if the perscribed time has passed */
1600         if (game.elapsed_time > game.random_block_time && game.skill_level > HARD && game.level_start) {
1601             if (game.random_block_time > 0) {
1602                 randomize_blocks();
1603                 draw_grid_on_screen();
1604             }
1605             game.random_block_time += time_to_add + rnd(5000);
1606         }
1607         if (game.level_start && !game.paused) {
1608             /* start playing the music */
1609             #ifndef NOAUDIO
1610             if (game.music == NULL && game.audio_enabled) {
1611                 if (game.music_enabled) {
1612                     game.music = Mix_LoadMUS(filename[cur_music - 1]);
1613                     Mix_PlayMusic(game.music, -1);
1614                 }
1615             }
1616             #endif
1617             /* don't update timer if all 50 blocks have been found */
1618             if (game.block_num != 50) {
1619                 update_timer();
1620             }
1621             /* level over... */
1622             if (game.block_num == 50 && game.stop_timer / game.level_time < 156) {
1623                 /*...but don't exit just yet (the path is still being drawn) */
1624                 if (game.erased != 50)
1625                     game.timer_height = (game.stop_timer + game.skipped_time) / game.level_time + 2;
1626                 /* ...exit, last path has been drawn */
1627                 else {
1628                     game.timer_height = (game.stop_timer + game.skipped_time) / game.level_time + 2;
1629                     break;
1630                 }
1631             }
1632             /* game over */
1633             else if (game.elapsed_time / game.level_time > 155) {
1634                 blit(game.surface[TIMER_EMPTY], 0, 0, 160, 32, GRIDSTARTX + 80, 545);
1635                 break;
1636             }
1637         }
1638         /* see if the user does anything besides look in wonder at the graphics ;-) */
1639         path = get_input_for_level(path);
1640         /* the user chose to end the current game */
1641         if (game.exit_game) {
1642             game.not_over = game.paused = 0;
1643             #ifndef NOAUDIO
1644         	if (game.audio_enabled) {
1645                 if (game.music_enabled) {
1646                     Mix_HaltMusic();
1647                     Mix_FreeMusic(game.music);
1648                 }
1649 	            game.music = NULL;
1650             }
1651             #endif
1652             return;
1653         }
1654         if (game.highx > 0 && !game.paused)
1655             update_highlighted_block();
1656         /* check the list to see if any new path should be drawn */
1657         erase_it = show_path(path);
1658         if (erase_it != 51) {
1659             game.erased++;
1660             path = erase_path(path, erase_it);
1661         }
1662         /* update the screen only if something was blitted */
1663         if (game.blit_done) {
1664             print_score_and_bonus();
1665             SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1666             game.blit_done = 0;
1667         }
1668         /* place a small delay so we don't suck up all of the CPU */
1669         SDL_Delay(10);
1670     }
1671     #ifndef NOAUDIO
1672   	if (game.audio_enabled) {
1673         game.regular_music = 1;
1674         if (game.music_enabled) {
1675             Mix_HaltMusic();
1676             Mix_FreeMusic(game.music);
1677         }
1678 	    game.music = NULL;
1679     }
1680     #endif
1681     /* apply bonus score if time hasn't ran out */
1682     if (game.block_num == 50 && game.stop_timer / game.level_time < 156)
1683         apply_bonus_score();
1684     else
1685         game.not_over = 0;
1686 
1687 }
1688 
update_highlighted_block()1689 void update_highlighted_block() {
1690 
1691     int destx, desty;
1692     if (SDL_GetTicks() - game.block_highlight_time >= 80) {
1693         destx = (game.highx - 1) * 32 + GRIDSTARTX;
1694         desty = (game.highy - 1) * 32 + GRIDSTARTY;
1695         game.block_highlight_num++;
1696         if (game.block_highlight_num > HIGHLIGHT4)
1697             game.block_highlight_num = HIGHLIGHT1;
1698         blit(game.surface[game.block_highlight_num], 0, 0, 32, 32, destx, desty);
1699         game.block_highlight_time = SDL_GetTicks();
1700     }
1701 
1702 }
1703 
update_score_and_bonus()1704 void update_score_and_bonus() {
1705 
1706     int bonus[5] = {0, 5, 10, 15, 25};
1707     Uint32 getticks;
1708 
1709     game.score++;
1710     /* if colors don't match, don't apply any bonus */
1711     if (game.last_color_removed == game.grid[game.highx][game.highy].color) {
1712         game.current_combos_removed++;
1713         /* most combos possible removed, start over */
1714         game.bonus_score = game.bonus_score + bonus[game.current_combos_removed];
1715         if (game.current_combos_removed == 4) {
1716             game.current_combos_removed = 0;
1717             game.no_matching_colors = 0;
1718             game.bonus_score = game.bonus_score + 10 * (game.current_sets_removed);
1719             game.current_sets_removed++;
1720             if (game.block_num != 50)
1721                 game.added_time += game.level_time * 5;
1722             game.paused_time += game.level_time * 5;
1723             /* if the player is quick enough, 5 ticks won't be gone from the
1724                clock when they get a set removed; only put back enough time
1725                back on to bring the timer to full again, not more than full.
1726                The second part of the if is in place for when start_timer is
1727                negative (assumes the game isn't running for more than 40 days
1728                straight!) */
1729             getticks = SDL_GetTicks();
1730             if (getticks < game.start_timer + game.paused_time && game.start_timer < 4 * pow(10, 9)) {
1731                 if (game.block_num != 50)
1732                     game.added_time -= (game.start_timer + game.paused_time) - getticks;
1733                 game.paused_time -= (game.start_timer + game.paused_time) - getticks;
1734             }
1735         }
1736         game.last_color_removed = game.grid[game.highx][game.highy].color;
1737     }
1738     else {
1739         game.no_matching_colors++;
1740         /* if this is the second set in a row that a player doesn't remove the
1741            same color blocks, reset the current sets removed */
1742         if (game.no_matching_colors > 1)
1743             game.current_sets_removed = 0;
1744         game.current_combos_removed = 0;
1745         game.last_color_removed = game.grid[game.highx][game.highy].color;
1746     }
1747 
1748 }
1749 
print_score_and_bonus()1750 void print_score_and_bonus() {
1751 
1752     int x, centerx, rightx;
1753 
1754     num_to_str(game.score, 5);
1755     centerx = GRIDSTARTX + 224 - word_width(game.temp_string, SERIFG24);
1756     free(game.temp_string);
1757     x = GRIDSTARTX + 96 + word_width("Score:", SERIFG24);
1758     fill(x, 505, (GRIDSTARTX + 224) - x, game.font[SERIFG24].height, 0, 0, 0);
1759     print_word("Score:", GRIDSTARTX + 96, 505, SERIFG24);
1760     print_number(centerx, 505, SERIFG24, game.score, 5);
1761     num_to_str(game.bonus_score, 5);
1762     rightx = GRIDSTARTX + 361 - word_width(game.temp_string, SERIFG24);
1763     free(game.temp_string);
1764     x = GRIDSTARTX + 240 + word_width("Bonus:", SERIFG24);
1765     fill(x, 505, (GRIDSTARTX + 361) - x, game.font[SERIFG24].height, 0, 0, 0);
1766     print_word("Bonus:", GRIDSTARTX + 240, 505, SERIFG24);
1767     print_number(rightx, 505, SERIFG24, game.bonus_score, 5);
1768 
1769 }
1770 
apply_bonus_score()1771 void apply_bonus_score() {
1772 
1773     int i, timer_height;
1774     Uint32 capture_time;
1775 
1776     SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
1777     SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
1778     print_word("Applying time bonus...", -800, -600, SERIFG24);
1779     SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1780     capture_time = SDL_GetTicks();
1781     while (SDL_GetTicks() - capture_time < 200) {
1782         SDL_PumpEvents();
1783     }
1784     timer_height = 160 - game.timer_height;
1785     for (i = 1; i <= (156 - (game.stop_timer + game.skipped_time) / game.level_time); i++) {
1786         if (i == 1)
1787             blit(game.surface[TIMER_FULL], 0, 0, 160, 32, GRIDSTARTX + 80, 545);
1788         blit(game.surface[TIMER_EMPTY], timer_height - i, 0, 160 - timer_height + i, 32, GRIDSTARTX + 80 + timer_height - i, 545);
1789         game.score += 1;
1790         print_score_and_bonus();
1791         SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1792         #ifndef NOAUDIO
1793         if (game.audio_enabled) Mix_PlayChannel(APPLY_BONUS, game.sound[APPLY_BONUS], 0);
1794         #endif
1795         capture_time = SDL_GetTicks();
1796         while (SDL_GetTicks() - capture_time < 70) {
1797             SDL_PumpEvents();
1798         }
1799     }
1800     if (game.bonus_score > 0) {
1801         print_word("Applying combo bonus...", -800, -600, SERIFG24);
1802         SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1803         capture_time = SDL_GetTicks();
1804         while (SDL_GetTicks() - capture_time < 200) {
1805             SDL_PumpEvents();
1806         }
1807         while (game.bonus_score != 0) {
1808             if (game.bonus_score > 20) {
1809                 game.score += 20;
1810                 game.bonus_score -= 20;
1811             }
1812             else {
1813                 game.score += game.bonus_score;
1814                 game.bonus_score = 0;
1815             }
1816             print_score_and_bonus();
1817             SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1818             #ifndef NOAUDIO
1819             if (game.audio_enabled) Mix_PlayChannel(APPLY_BONUS, game.sound[APPLY_BONUS], 0);
1820             #endif
1821             capture_time = SDL_GetTicks();
1822             while (SDL_GetTicks() - capture_time < 70) {
1823                 SDL_PumpEvents();
1824             }
1825         }
1826     }
1827     if (game.no_wrong_moves > 0) {
1828         print_word("Applying no wrong moves bonus...", -800, -600, SERIFG24);
1829         /* 50 points for the first time making no wrong moves, an extra 10
1830            points for each thereafter while doing the same */
1831         game.bonus_score = 40 + game.no_wrong_moves;
1832         print_score_and_bonus();
1833         SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1834         capture_time = SDL_GetTicks();
1835         while (SDL_GetTicks() - capture_time < 750) {
1836             SDL_PumpEvents();
1837         }
1838         while (game.bonus_score != 0) {
1839             if (game.bonus_score > 5) {
1840                 game.score += 5;
1841                 game.bonus_score -= 5;
1842             }
1843             else {
1844                 game.score += game.bonus_score;
1845                 game.bonus_score = 0;
1846             }
1847             print_score_and_bonus();
1848             SDL_UpdateRect(game.screen, 0, 0, 0, 0);
1849             #ifndef NOAUDIO
1850             if (game.audio_enabled) Mix_PlayChannel(APPLY_BONUS, game.sound[APPLY_BONUS], 0);
1851             #endif
1852             capture_time = SDL_GetTicks();
1853             while (SDL_GetTicks() - capture_time < 70) {
1854                 SDL_PumpEvents();
1855             }
1856         }
1857     }
1858     capture_time = SDL_GetTicks();
1859     while (SDL_GetTicks() - capture_time < 200) {
1860         SDL_PumpEvents();
1861     }
1862     game.current_level++;
1863     SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
1864     SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_ENABLE);
1865 
1866 }
1867 
update_timer()1868 void update_timer() {
1869 
1870     int cur_music;
1871     Uint32 temp_time;
1872     #ifndef NOAUDIO
1873     char *filename[6] = {DATA_PREFIX "music/1-2.ogg",
1874                          DATA_PREFIX "music/2-2.ogg",
1875                          DATA_PREFIX "music/3-2.ogg",
1876                          DATA_PREFIX "music/4-2.ogg",
1877                          DATA_PREFIX "music/5-2.ogg",
1878                          DATA_PREFIX "music/6-2.ogg"};
1879     #endif
1880 
1881     cur_music = game.music_loop[game.current_level % MUSIC_LOOPS - 1];
1882     /* the game is soon over... blink the timer for the last 10 seconds */
1883     if (game.level_time * 156 - game.elapsed_time < 10000) {
1884         /* change the music to reflect the urgency of the game */
1885         if (game.regular_music && game.audio_enabled) {
1886             game.regular_music = 0;
1887             #ifndef NOAUDIO
1888             if (game.music_enabled) {
1889                 Mix_HaltMusic();
1890                 Mix_FreeMusic(game.music);
1891                 game.music = Mix_LoadMUS(filename[cur_music - 1]);
1892                 Mix_PlayMusic(game.music, -1);
1893             }
1894             #endif
1895         }
1896         if (!game.blink && game.elapsed_time - game.blink_time > 80) {
1897             game.blink_time = game.elapsed_time;
1898             game.blink = 1;
1899             blit(game.surface[TIMER_FULL_WHITE], 0, 0, 160, 32, GRIDSTARTX + 80, 545);
1900             blit(game.surface[TIMER_EMPTY_WHITE], 160 - game.elapsed_time / game.level_time - 2, 0, game.elapsed_time / game.level_time + 2, 32, GRIDSTARTX + 80 + 160 - game.elapsed_time / game.level_time - 2, 545);
1901         }
1902         else if (game.blink && game.elapsed_time - game.blink_time > 80) {
1903             game.blink_time = game.elapsed_time;
1904             game.blink = 0;
1905             blit(game.surface[TIMER_FULL], 0, 0, 160, 32, GRIDSTARTX + 80, 545);
1906             blit(game.surface[TIMER_EMPTY], 160 - game.elapsed_time / game.level_time - 2, 0, game.elapsed_time / game.level_time + 2, 32, GRIDSTARTX + 80 + 160 - game.elapsed_time / game.level_time - 2, 545);
1907         }
1908     }
1909     /* show regular timer */
1910     else {
1911         temp_time = game.elapsed_time / game.level_time + 2;
1912         if (temp_time != game.prev_time) {
1913             game.prev_time = temp_time;
1914             blit(game.surface[TIMER_FULL], 0, 0, 160, 32, GRIDSTARTX + 80, 545);
1915             blit(game.surface[TIMER_EMPTY], 160 - game.elapsed_time / game.level_time - 2, 0, game.elapsed_time / game.level_time + 2, 32, GRIDSTARTX + 80 + 160 - game.elapsed_time / game.level_time - 2, 545);
1916         }
1917     }
1918 
1919 }
1920 
get_input_for_level(struct path_info * path)1921 struct path_info *get_input_for_level(struct path_info *path) {
1922 
1923     int i, x, y, cnt;
1924     SDL_Event event;
1925     SDL_keysym keysym;
1926     HIGHLIGHT_INFO highlight[NUM_IN_GAME_MO] =
1927        {{5, 5 + word_width("New Game", SERIFG24), 5, 5 + game.font[SERIFG24].height, "New Game"},
1928         {5 + word_width("New Game  ", SERIFG24), 5 + word_width("New Game  Pause", SERIFG24), 5, 5 + game.font[SERIFG24].height, "Pause  "},
1929         {795 - word_width("Help  Exit", SERIFG24), 795 - word_width("  Exit", SERIFG24), 5, 5 + game.font[SERIFG24].height, "Help"},
1930         {795 - word_width("Exit", SERIFG24), 795, 5, 5 + game.font[SERIFG24].height, "Exit"}};
1931 
1932     if (game.paused) {
1933         highlight[PAUSE].x2 = 5 + word_width("New Game  Unpause", SERIFG24);
1934         strcpy(highlight[PAUSE].name, "Unpause");
1935     }
1936     if (SDL_PollEvent(&event)) {
1937         switch (event.type) {
1938             case SDL_MOUSEBUTTONDOWN:
1939                 if (event.button.button == SDL_BUTTON_LEFT || event.button.button == SDL_BUTTON_RIGHT) {
1940                     /* see if we are on the grid */
1941                     if (event.button.x >= GRIDSTARTX && event.button.x < GRIDSTARTX + 320 && event.button.y >= GRIDSTARTY && event.button.y < GRIDSTARTY + 320 && !game.paused) {
1942                         /* the game is just starting... */
1943                         if (game.level_start == 0) {
1944                             game.start_timer = SDL_GetTicks();
1945                             game.paused_time = 0;
1946                             game.level_start = 1;
1947                         }
1948                         x = (event.button.x - GRIDSTARTX) / 32 + 1;
1949                         y = (event.button.y - GRIDSTARTY) / 32 + 1;
1950                         cnt = game.path_count;
1951                         path = change_grid(x, y, path, event.button.button);
1952                         /* did we find two blocks that match? */
1953                         if (cnt != game.path_count) {
1954                             update_score_and_bonus();
1955                             game.grid[x][y].status = BLOCK_GOING;
1956                             game.grid[game.highx][game.highy].status = BLOCK_GOING;
1957                             game.highx = game.highy = game.turns = 0;
1958                         }
1959                     }
1960                     /* start a new game */
1961                     else if (event.button.x >= highlight[NEWGAME].x1 && event.button.x < highlight[NEWGAME].x2 && event.button.y >= highlight[NEWGAME].y1 && event.button.y < highlight[NEWGAME].y2) {
1962                         print_word(highlight[NEWGAME].name, highlight[NEWGAME].x1, highlight[NEWGAME].y1, SERIFG24);
1963                         game.highlight[NEWGAME] = 0;
1964                         exit_game(path, NEW_GAME);
1965                     }
1966                     /* quit the game */
1967                     else if (event.button.x >= highlight[EXIT].x1 && event.button.x < highlight[EXIT].x2 && event.button.y >= highlight[EXIT].y1 && event.button.y < highlight[EXIT].y2) {
1968                         print_word(highlight[EXIT].name, highlight[EXIT].x1, highlight[EXIT].y1, SERIFG24);
1969                         game.highlight[EXIT] = 0;
1970                         exit_game(path, EXIT_GAME);
1971                     }
1972                     /* pause or unpause game */
1973                     else if (event.button.x >= highlight[PAUSE].x1 && event.button.x < highlight[PAUSE].x2 && event.button.y >= highlight[PAUSE].y1 && event.button.y < highlight[PAUSE].y2) {
1974                         if (!game.paused) {
1975                             #ifndef NOAUDIO
1976                             if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
1977                             #endif
1978                             pause_game("Game Paused");
1979                             if (game.pause_mode == MINIMIZE)
1980                                 SDL_WM_IconifyWindow();
1981                             print_word("Unpause", highlight[PAUSE].x1, highlight[PAUSE].y1, SERIFG24);
1982                         }
1983                         else {
1984                             #ifndef NOAUDIO
1985                             if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
1986                             #endif
1987                             unpause_game();
1988                             highlight[PAUSE].x2 = 438 + word_width("Pause", SERIFG24);
1989                             fill(highlight[PAUSE].x1, highlight[PAUSE].y1, word_width("Unpause", SERIFG24), game.font[SERIFG24].height, 0, 0, 0);
1990                             print_word("Pause", highlight[PAUSE].x1, highlight[PAUSE].y1, SERIFG24);
1991                         }
1992                         game.highlight[PAUSE] = 0;
1993                     }
1994                     /* display the help screen */
1995                     else if (event.button.x >= highlight[HELP].x1 && event.button.x < highlight[HELP].x2 && event.button.y >= highlight[HELP].y1 && event.button.y < highlight[HELP].y2) {
1996                         print_word(highlight[HELP].name, highlight[HELP].x1, highlight[HELP].y1, SERIFG24);
1997                         game.highlight[HELP] = 0;
1998                         if (game.level_start) pause_game("Game Paused");
1999                         display_help();
2000                         if (game.fading_effect)
2001                             fade_screen(FADE_OUT, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
2002                         draw_grid_on_screen();
2003                         if (game.fading_effect)
2004                             fade_screen(FADE_IN, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
2005                     }
2006                     /* change the sound volume */
2007                     else if (event.button.x >= 182 && event.button.x < 182 + 129 && event.button.y >= 545 && event.button.y < 577)
2008                         change_volume(SOUND, event.button.x - 182, 180, 545, TOGGLE_NO, MOVE_SLIDER);
2009                     /* change the music volume */
2010                     else if (event.button.x >= 485 + word_width("Music: ", SERIFG24) && event.button.x < 485 + word_width("Music: ", SERIFG24) + 129 && event.button.y >= 545 && event.button.y < 577 && game.music_enabled)
2011                         change_volume(MUSIC, event.button.x - (485 + word_width("Music: ", SERIFG24)), 483 + word_width("Music: ", SERIFG24), 545, TOGGLE_NO, MOVE_SLIDER);
2012                 }
2013                 break;
2014             case SDL_MOUSEMOTION:
2015                 /* highlight/unlight in-game menu options */
2016                 for (i = 0; i < 4; i++) {
2017                     if (event.button.x >= highlight[i].x1 && event.button.x < highlight[i].x2 && event.button.y >= highlight[i].y1 && event.button.y < highlight[i].y2) {
2018                         if (!game.highlight[i]) {
2019                             print_word(highlight[i].name, highlight[i].x1, highlight[i].y1, SERIFW24);
2020                             game.highlight[i] = 1;
2021                             #ifndef NOAUDIO
2022                             if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
2023                             #endif
2024                         }
2025                     }
2026                     else {
2027                         if (game.highlight[i]) {
2028                             print_word(highlight[i].name, highlight[i].x1, highlight[i].y1, SERIFG24);
2029                             game.highlight[i] = 0;
2030                             #ifndef NOAUDIO
2031                             if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
2032                             #endif
2033                         }
2034                     }
2035                 }
2036                 /* change the sound volume */
2037                 if (event.motion.x >= 182 && event.motion.x < 182 + 129 && event.motion.y >= 545 && event.motion.y < 577 && event.motion.state == SDL_BUTTON(1))
2038                     change_volume(SOUND, event.motion.x - 182, 180, 545, TOGGLE_NO, MOVE_SLIDER);
2039                 /* change the music volume */
2040                 else if (event.motion.x >= 485 + word_width("Music: ", SERIFG24) && event.motion.x < 485 + word_width("Music: ", SERIFG24) + 129 && event.motion.y >= 545 && event.motion.y < 577 && event.motion.state == SDL_BUTTON(1) && game.music_enabled)
2041                     change_volume(MUSIC, event.motion.x - (485 + word_width("Music: ", SERIFG24)), 483 + word_width("Music: ", SERIFG24), 545, TOGGLE_NO, MOVE_SLIDER);
2042                 break;
2043             case SDL_KEYDOWN:
2044                 keysym = event.key.keysym;
2045                 /* change the color of the l & r for the Insane mode */
2046                 if (keysym.sym >= SDLK_0 && keysym.sym <= SDLK_9 && game.skill_level == INSANE) {
2047                     game.color_l_and_r[keysym.sym - 48] = !(game.color_l_and_r[keysym.sym - 48] - 48) + 48;
2048                     draw_blocks();
2049                 }
2050                 /* change the block set */
2051                 else if (keysym.sym == SDLK_b && !game.paused) {
2052                     game.block_set++;
2053                     if (game.block_set == NUM_BLOCK_SETS)
2054                         game.block_set = SQUARE_BRICKS;
2055                     change_block_set();
2056                     draw_blocks();
2057                 }
2058                 /* change the style of the corners of the blocks */
2059                 else if (keysym.sym == SDLK_c && !game.paused) {
2060                     if (game.corner_style == SQUARE)
2061                         game.corner_style = ROUNDED;
2062                     else
2063                         game.corner_style = SQUARE;
2064                     draw_blocks();
2065                 }
2066                 /* needed under Windows to be able to use Alt-F4 to quit */
2067                 else if (keysym.sym == SDLK_F4 && (keysym.mod & KMOD_ALT)) {
2068                     if (game.path_count > 0)
2069                         free(path);
2070                     cleanup_and_exit();
2071                 }
2072                 /* toggle between full screen/window mode */
2073                 else if (keysym.sym == SDLK_f || keysym.sym == SDLK_F4) {
2074                     change_display_mode();
2075                     draw_grid_on_screen();
2076                 }
2077                 /* pause the game and display the help screen */
2078                 else if (keysym.sym == SDLK_h || keysym.sym == SDLK_F1) {
2079                     if (game.level_start) pause_game("Game Paused");
2080                     display_help();
2081                     if (game.fading_effect)
2082                         fade_screen(FADE_OUT, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
2083                     draw_grid_on_screen();
2084                     if (game.fading_effect)
2085                         fade_screen(FADE_IN, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
2086                 }
2087                 /* turn the background music on/off */
2088                 else if (keysym.sym == SDLK_m && game.music_enabled) {
2089                     if (game.music_volume == 0)
2090                         change_volume(MUSIC, game.old_music_volume, 483 + word_width("Music: ", SERIFG24), 545, TOGGLE_YES, MOVE_SLIDER);
2091                     else {
2092                         game.old_music_volume = game.music_volume;
2093                         change_volume(MUSIC, 0, 483 + word_width("Music: ", SERIFG24), 545, TOGGLE_YES, MOVE_SLIDER);
2094                     }
2095                 }
2096                 /* start a new game */
2097                 else if (keysym.sym == SDLK_n || keysym.sym == SDLK_F2)
2098                     exit_game(path, NEW_GAME);
2099                 /* pause or unpause game */
2100                 else if (keysym.sym == SDLK_p || keysym.sym == SDLK_PAUSE || keysym.sym == SDLK_F3) {
2101                     if (!game.paused) {
2102                         #ifndef NOAUDIO
2103                         if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
2104                         #endif
2105                         pause_game("Game Paused");
2106                         if (game.pause_mode == MINIMIZE)
2107                             SDL_WM_IconifyWindow();
2108                     }
2109                     else {
2110                         #ifndef NOAUDIO
2111                         if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
2112                         #endif
2113                         unpause_game();
2114                     }
2115                 }
2116                 /* quit the game */
2117                 else if (keysym.sym == SDLK_ESCAPE || keysym.sym == SDLK_q)
2118                     exit_game(path, EXIT_GAME);
2119                 /* turn the sound effects on/off */
2120                 else if (keysym.sym == SDLK_s) {
2121                     if (game.sound_volume == 0)
2122                         change_volume(SOUND, game.old_sound_volume, 180, 545, TOGGLE_YES, MOVE_SLIDER);
2123                     else {
2124                         game.old_sound_volume = game.sound_volume;
2125                         change_volume(SOUND, 0, 180, 545, TOGGLE_YES, MOVE_SLIDER);
2126                     }
2127                 }
2128                 break;
2129             case SDL_QUIT:
2130                 if (game.path_count > 0)
2131                     free(path);
2132                 cleanup_and_exit();
2133         }
2134     }
2135     return path;
2136 
2137 }
2138 
change_volume(int volume_type,int volume,int x,int y,int toggle,int adjust_slider)2139 void change_volume(int volume_type, int volume, int x, int y, int toggle, int adjust_slider) {
2140 
2141     if (!game.audio_enabled) return;
2142     #ifndef NOAUDIO
2143     if (volume_type == SOUND) {
2144         game.sound_volume = volume;
2145         if (toggle == TOGGLE_NO) game.old_sound_volume = volume;
2146         Mix_Volume(-1, game.sound_volume);
2147     }
2148     else {
2149         /* music was stopped, start playing it again */
2150         if (game.music_volume == 0 && volume > 0) {
2151             Mix_ResumeMusic();
2152             /* don't play the music if the game is paused! */
2153             if (game.paused)
2154                 Mix_PauseMusic();
2155         }
2156         game.music_volume = volume;
2157         if (toggle == TOGGLE_NO) game.old_music_volume = volume;
2158         Mix_VolumeMusic(game.music_volume);
2159         /* music volume is off, stop playing the music */
2160         if (game.music_volume == 0)
2161             Mix_PauseMusic();
2162     }
2163     if (adjust_slider == MOVE_SLIDER) {
2164         blit(game.surface[VOLUME_BAR], 0, 0, 133, 32, x, y);
2165         blit(game.surface[VOLUME_SLIDER], 0, 0, 5, 32, volume + x, y);
2166     }
2167     #endif
2168 
2169 }
2170 
change_block_set()2171 void change_block_set() {
2172 
2173     int loaded;
2174 
2175     SDL_FreeSurface(game.surface[BLOCKS]);
2176     switch (game.block_set) {
2177         case SQUARE_BRICKS:
2178             game.surface[BLOCKS] = load_image(DATA_PREFIX "graphics/bricks" EXTENSION, &loaded);
2179             break;
2180         case BEVELED_BLOCKS:
2181             game.surface[BLOCKS] = load_image(DATA_PREFIX "graphics/beveled" EXTENSION, &loaded);
2182             break;
2183         case SNAKE_SKIN_BLOCKS:
2184             game.surface[BLOCKS] = load_image(DATA_PREFIX "graphics/snake_skin" EXTENSION, &loaded);
2185             break;
2186         case PYRAMID_BLOCKS:
2187             game.surface[BLOCKS] = load_image(DATA_PREFIX "graphics/pyramids" EXTENSION, &loaded);
2188             break;
2189         case CIRCLES:
2190             game.surface[BLOCKS] = load_image(DATA_PREFIX "graphics/circles" EXTENSION, &loaded);
2191             break;
2192         case BRUSHED_METAL:
2193             game.surface[BLOCKS] = load_image(DATA_PREFIX "graphics/brushed_metal" EXTENSION, &loaded);
2194             break;
2195     }
2196 
2197 }
2198 
change_display_mode()2199 void change_display_mode() {
2200 
2201     /* change to window mode */
2202     if (game.display_mode == FULLSCREEN) {
2203         game.screen = SDL_SetVideoMode(800, 600, 0, SDL_SWSURFACE);
2204         game.display_mode = WINDOW;
2205     }
2206     /* change to full screen mode (if possible) */
2207     else {
2208         game.screen = SDL_SetVideoMode(800, 600, 0, SDL_FULLSCREEN);
2209         if (game.screen == NULL)
2210             game.screen = SDL_SetVideoMode(800, 600, 0, SDL_SWSURFACE);
2211         else
2212             game.display_mode = FULLSCREEN;
2213     }
2214 
2215 }
2216 
setup_yes_no_xy(char * word,int destx,int desty,int font,HIGHLIGHT_INFO * highlight)2217 void setup_yes_no_xy (char *word, int destx, int desty, int font, HIGHLIGHT_INFO *highlight) {
2218 
2219     int newdestx, newdesty;
2220 
2221     /* if destx or desty is negative, center word according to the co-ordinates */
2222     if (destx < 0)
2223         newdestx = (abs(destx) - (word_width(word, font) + word_width("yes/no", font))) / 2;
2224     else
2225         newdestx = destx;
2226     if (desty < 0)
2227         newdesty = (abs(desty) - game.font[font].height) / 2;
2228     else
2229         newdesty = desty;
2230     strcpy(highlight[0].name, "yes");
2231     strcpy(highlight[1].name, "no");
2232     highlight[0].x1 = newdestx + word_width(word, font);
2233     highlight[0].x2 = highlight[0].x1 + word_width("yes", font);
2234     highlight[1].x1 = highlight[0].x2 + word_width("/", font);
2235     highlight[1].x2 = highlight[1].x1 + word_width("no", font);
2236     highlight[0].y1 = highlight[1].y1 = newdesty;
2237     highlight[0].y2 = highlight[1].y2 = newdesty + game.font[font].height;
2238 
2239 }
2240 
get_input_for_yes_no(HIGHLIGHT_INFO * yes_no)2241 int get_input_for_yes_no(HIGHLIGHT_INFO *yes_no) {
2242 
2243     int i, yn_highlight[2] = {0, 0};
2244     SDL_Event event;
2245     SDL_keysym keysym;
2246 
2247     while (1) {
2248         if (SDL_PollEvent(&event)) {
2249             switch (event.type) {
2250                 case SDL_MOUSEBUTTONDOWN:
2251                     if (event.button.button == SDL_BUTTON_LEFT) {
2252                         /* yes */
2253                         if (event.button.x >= yes_no[0].x1 && event.button.x < yes_no[0].x2 && event.button.y >= yes_no[0].y1 && event.button.y < yes_no[0].y2)
2254                             return 1;
2255                         /* no */
2256                         else if (event.button.x >= yes_no[1].x1 && event.button.x < yes_no[1].x2 && event.button.y >= yes_no[1].y1 && event.button.y < yes_no[1].y2)
2257                             return 0;
2258                     }
2259                 break;
2260                 case SDL_MOUSEMOTION:
2261                     /* highlight/unhighlight yes/no */
2262                     for (i = 0; i < 2; i++) {
2263                         if (event.button.x >= yes_no[i].x1 && event.button.x < yes_no[i].x2 && event.button.y >= yes_no[i].y1 && event.button.y < yes_no[i].y2) {
2264                             if (!yn_highlight[i]) {
2265                                 print_word(yes_no[i].name, yes_no[i].x1, yes_no[i].y1, SERIFG24);
2266                                 SDL_UpdateRect(game.screen, 0, 0, 0, 0);
2267                                 yn_highlight[i] = 1;
2268                                 #ifndef NOAUDIO
2269                                 if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
2270                                 #endif
2271                             }
2272                         }
2273                         else {
2274                             if (yn_highlight[i]) {
2275                                 print_word(yes_no[i].name, yes_no[i].x1, yes_no[i].y1, SERIFW24);
2276                                 SDL_UpdateRect(game.screen, 0, 0, 0, 0);
2277                                 yn_highlight[i] = 0;
2278                                 #ifndef NOAUDIO
2279                                 if (game.audio_enabled) Mix_PlayChannel(MENU_OPTION, game.sound[MENU_OPTION], 0);
2280                                 #endif
2281                             }
2282                         }
2283                     }
2284                     break;
2285                 case SDL_KEYDOWN:
2286                     keysym = event.key.keysym;
2287                     if (keysym.sym == SDLK_y)
2288                         return 1;
2289                     else if (keysym.sym == SDLK_n)
2290                         return 0;
2291                     break;
2292                 case SDL_QUIT:
2293                     return -1;
2294             }
2295         }
2296         SDL_Delay(10);
2297     }
2298 
2299 }
2300 
exit_game(struct path_info * path,int exit_type)2301 void exit_game(struct path_info *path, int exit_type) {
2302 
2303     int answer;
2304     HIGHLIGHT_INFO yes_no[2];
2305 
2306     /* the game is already paused, just display the exit game message */
2307     #ifndef NOAUDIO
2308     if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
2309     #endif
2310     if (game.paused) {
2311         if (exit_type == EXIT_GAME) {
2312             print_word("Exit game? yes/no", -800, -600, SERIFW24);
2313             setup_yes_no_xy ("Exit game? ", -800, -600, SERIFW24, yes_no);
2314         }
2315         else {
2316             print_word("Start a new game? yes/no", -800, -600, SERIFW24);
2317             setup_yes_no_xy ("Start a new game? ", -800, -600, SERIFW24, yes_no);
2318         }
2319         SDL_UpdateRect(game.screen, 0, 0, 0, 0);
2320     }
2321     else {
2322         game.unpause_msg = 0;
2323         if (exit_type == EXIT_GAME) {
2324             pause_game("Exit game? yes/no");
2325             setup_yes_no_xy ("Exit game? ", -800, -600, SERIFW24, yes_no);
2326         }
2327         else {
2328             pause_game("Start a new game? yes/no");
2329             setup_yes_no_xy ("Start a new game? ", -800, -600, SERIFW24, yes_no);
2330         }
2331         game.unpause_msg = 1;
2332     }
2333     answer = get_input_for_yes_no(yes_no);
2334     #ifndef NOAUDIO
2335     if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
2336     #endif
2337     /* yes */
2338     if (answer == 1)
2339         game.exit_game = exit_type;
2340     /* no */
2341     else if (answer == 0)
2342         unpause_game();
2343     /* SDL_QUIT */
2344     else {
2345         if (game.path_count > 0)
2346             free(path);
2347         cleanup_and_exit();
2348     }
2349 
2350 }
2351 
read_highscore_file(struct highscore_info highscores[][10])2352 void read_highscore_file(struct highscore_info highscores[][10]) {
2353 
2354     FILE *scorefile;
2355     int i, x, y, pos, corrupt_file, num_groups;
2356     int num_chars[5] = {20, 5, 3, 20, 6};
2357     int lower[5] = {31, 47, 47, 31, 47};
2358     int upper[5] = {127, 58, 58, 127, 58};
2359     char chr, strng[21];
2360 
2361     /* We will read the file one character at a time to ensure each one is
2362        within range. Each field will be separated with a tab character. I'm only
2363        using a very basic form of encryption to keep the text editor cheaters
2364        from changing the file! ;-) */
2365 
2366     /* create an initial blank score file in memory */
2367     for (x = 0; x < game.num_groups; x++) {
2368         for (y = 0; y < 10; y++) {
2369             highscores[x][y].name_score[0] = '\0';
2370             highscores[x][y].score = 0;
2371             highscores[x][y].level = 0;
2372             highscores[x][y].name_time[0] = '\0';
2373             highscores[x][y].time = 120000;
2374         }
2375     }
2376     if ((scorefile = fopen(game.score_file, "r")) == NULL)
2377         fprintf(stderr, "Error reading the highscore file: %s\nCreating new one in memory\n", game.score_file);
2378     else {
2379         corrupt_file = 0;
2380         pos = 0;
2381         /* find out how many groups of high scores there are */
2382         chr = ~fgetc(scorefile);
2383         while (!corrupt_file && !feof(scorefile) && pos < 3 && chr != '\t') {
2384             /* only accept numbers */
2385             if (chr > 47 && chr < 58) {
2386                 strng[pos] = chr;
2387                 pos++;
2388             }
2389             chr = ~fgetc(scorefile);
2390         }
2391         if (chr == '\t')
2392             strng[pos] = '\0';
2393         else
2394             corrupt_file = 1;
2395         num_groups = atoi(strng);
2396         if (num_groups <= 0)
2397             corrupt_file = 1;
2398         for (x = 0; x < num_groups; x++) {
2399             for (y = 0; y < 10; y++) {
2400                 for (i = 0; i < 5; i++) {
2401                     pos = 0;
2402                     chr = ~fgetc(scorefile);
2403                     while (!corrupt_file && !feof(scorefile) && pos < num_chars[i] && chr != '\t') {
2404                         /* only accept characters that are in the font file */
2405                         if (chr > lower[i] && chr < upper[i]) {
2406                             strng[pos] = chr;
2407                             pos++;
2408                         }
2409                         chr = ~fgetc(scorefile);
2410                     }
2411                     if (chr == '\t') {
2412                         strng[pos] = '\0';
2413                         /* place what we have read from the file into the highscore structure */
2414                         switch (i) {
2415                             case 0:
2416                                 strcpy(highscores[x][y].name_score, strng);
2417                                 break;
2418                             case 1:
2419                                 highscores[x][y].score = atoi(strng);
2420                                 break;
2421                             case 2:
2422                                 highscores[x][y].level = atoi(strng);
2423                                 break;
2424                             case 3:
2425                                 strcpy(highscores[x][y].name_time, strng);
2426                                 break;
2427                             case 4:
2428                                 highscores[x][y].time = atoi(strng);
2429                                 break;
2430                         }
2431                     }
2432                     else
2433                         corrupt_file = 1;
2434                 }
2435             }
2436         }
2437         if (corrupt_file) {
2438             /* scores.dat corrupted... erase what's in memory with a new one */
2439             fprintf(stderr, "Your highscore file: %s is corrupted\nCreating new one in memory\n", game.score_file);
2440             for (x = 0; x < game.num_groups; x++) {
2441                 for (y = 0; y < 10; y++) {
2442                     highscores[x][y].name_score[0] = '\0';
2443                     highscores[x][y].score = 0;
2444                     highscores[x][y].level = 0;
2445                     highscores[x][y].name_time[0] = '\0';
2446                     highscores[x][y].time = 120000;
2447                 }
2448             }
2449         }
2450         fclose(scorefile);
2451     }
2452 
2453 }
2454 
write_highscore_file(struct highscore_info highscores[][10])2455 void write_highscore_file(struct highscore_info highscores[][10]) {
2456 
2457     FILE *scorefile;
2458     int i, x, y;
2459 
2460     if ((scorefile = fopen(game.score_file, "w")) == NULL)
2461         fprintf(stderr, "Error writing the highscore file: %s\nDo you have write access to this directory?\n", game.score_file);
2462     else {
2463         num_to_str(game.num_groups, 2);
2464         for (i = 0; i < strlen(game.temp_string); i++)
2465             fputc(~game.temp_string[i], scorefile);
2466         free (game.temp_string);
2467         fputc(~'\t', scorefile);
2468         for (x = 0; x < game.num_groups; x++) {
2469             for (y = 0; y < 10; y++) {
2470                 /* name for score */
2471                 for (i = 0; i < strlen(highscores[x][y].name_score); i++)
2472                     fputc(~highscores[x][y].name_score[i], scorefile);
2473                 fputc(~'\t', scorefile);
2474                 /* score */
2475                 num_to_str(highscores[x][y].score, 5);
2476                 for (i = 0; i < strlen(game.temp_string); i++)
2477                     fputc(~game.temp_string[i], scorefile);
2478                 free (game.temp_string);
2479                 fputc(~'\t', scorefile);
2480                 /* level */
2481                 num_to_str(highscores[x][y].level, 3);
2482                 for (i = 0; i < strlen(game.temp_string); i++)
2483                     fputc(~game.temp_string[i], scorefile);
2484                 free (game.temp_string);
2485                 fputc(~'\t', scorefile);
2486                 /* name for time */
2487                 for (i = 0; i < strlen(highscores[x][y].name_time); i++)
2488                     fputc(~highscores[x][y].name_time[i], scorefile);
2489                 fputc(~'\t', scorefile);
2490                 /* time */
2491                 num_to_str(highscores[x][y].time, 6);
2492                 for (i = 0; i < strlen(game.temp_string); i++)
2493                     fputc(~game.temp_string[i], scorefile);
2494                 free (game.temp_string);
2495                 fputc(~'\t', scorefile);
2496             }
2497         }
2498         fclose(scorefile);
2499     }
2500 
2501 }
2502 
update_highscores()2503 void update_highscores() {
2504 
2505     int x, y, position;
2506     int high_score = 1;
2507     struct highscore_info highscores[NUM_SKILLS * 2][10];
2508 
2509     read_highscore_file(highscores);
2510     position = NUM_SKILLS * game.gravity + game.skill_level;
2511     /* see if we have a high score (scores less than 0 won't be added) */
2512     y = 0;
2513     while (y<10) {
2514         if (game.score > highscores[position][y].score) {
2515             /* we're on the list... move the rest of the scores down */
2516             for (x = 9; x > y; x--) {
2517                 strcpy(highscores[position][x].name_score, highscores[position][x - 1].name_score);
2518                 highscores[position][x].score = highscores[position][x - 1].score;
2519                 highscores[position][x].level = highscores[position][x - 1].level;
2520             }
2521             /* add current game score into position */
2522             strcpy(highscores[position][y].name_score, game.player_name);
2523             highscores[position][y].score = game.score;
2524             highscores[position][y].level = game.current_level;
2525             break;
2526         }
2527         else
2528             y++;
2529     }
2530     if (y==10) high_score = 0;
2531     /* see if we have a best time */
2532     y = 0;
2533     while (y<10) {
2534         if (game.best_time < highscores[position][y].time) {
2535             /* we're on the list... move the rest of the times down */
2536             for (x = 9; x > y; x--) {
2537                 strcpy(highscores[position][x].name_time, highscores[position][x - 1].name_time);
2538                 highscores[position][x].time = highscores[position][x - 1].time;
2539             }
2540             /* add current game time into position */
2541             strcpy(highscores[position][y].name_time, game.player_name);
2542             highscores[position][y].time = game.best_time;
2543             break;
2544         }
2545         else
2546             y++;
2547     }
2548     if (y<10 || high_score)
2549         write_highscore_file(highscores);
2550 }
2551 
game_over()2552 int game_over() {
2553 
2554     int answer, font, start_new;
2555     int x, y;
2556     Uint32 capture_time;
2557     #ifndef NOAUDIO
2558     char *menu_music[2] = {DATA_PREFIX "music/0-0.ogg",
2559                            DATA_PREFIX "music/0-1.ogg"};
2560     #endif
2561     HIGHLIGHT_INFO yes_no[2];
2562 
2563     char *skill_levels[NUM_SKILLS] = {"Kids", "Easy", "Normal", "Hard", "Very Hard", "Insane"};
2564     font = SERIFG24;
2565 
2566     SDL_EventState(SDL_KEYDOWN, SDL_IGNORE);
2567     SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
2568     update_highscores();
2569     start_new = 0;
2570     if (game.fading_effect)
2571         fade_screen(FADE_OUT, 500, SDL_MapRGB(game.screen->format, 0, 0, 0));
2572     else {
2573         for (x = 0; x  <20; x++) {
2574             for (y = 0; y < 600; y += 20)
2575                 fill(0, y + x, 800, 1, 0, 0, 0);
2576             for (y = 0; y < 800; y += 20)
2577                 fill(y + x, 0, 1, 600, 0, 0, 0);
2578             SDL_UpdateRect(game.screen, 0, 0, 0, 0);
2579             capture_time = SDL_GetTicks();
2580             while (SDL_GetTicks() - capture_time < 30) {
2581                 SDL_PumpEvents();
2582             }
2583         }
2584     }
2585     print_word("Game Over", 293, 156, font);
2586     print_word("Level: ", 293, 180, font);
2587     print_number(293 + word_width("Level: ", font), 180, font, game.current_level, 3);
2588     print_word("Score: ", 293, 204, font);
2589     print_number(293 + word_width("Score: ", font), 204, font, game.score, 5);
2590     print_word("Skill Level: ", 293, 228, font);
2591     print_word(skill_levels[game.skill_level], 293 + word_width("Skill Level: ", font), 228, font);
2592     if (game.gravity)
2593         print_word("Gravity: On", 293, 252, font);
2594     else
2595         print_word("Gravity: Off", 293, 252, font);
2596     if (game.current_level > 1) {
2597         print_word("Best Time: ", 293, 276, font);
2598         print_time(293 + word_width("Best Time: ", font), 276, font, game.best_time);
2599     }
2600     print_word("Start a new game? yes/no", -800, 324, SERIFW24);
2601     setup_yes_no_xy("Start a new game? ", -800, 324, SERIFW24, yes_no);
2602     if (!game.fading_effect)
2603         SDL_UpdateRect(game.screen, 0, 0, 0, 0);
2604     else
2605         fade_screen(FADE_IN, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
2606     #ifndef NOAUDIO
2607     if (game.audio_enabled) Mix_PlayChannel(OPTION_CHOICE, game.sound[OPTION_CHOICE], 0);
2608     #endif
2609     SDL_EventState(SDL_KEYDOWN, SDL_ENABLE);
2610     SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_ENABLE);
2611     answer = get_input_for_yes_no(yes_no);
2612     /* yes */
2613     if (answer == 1)
2614         return 1;
2615     /* no */
2616     else if (answer == 0) {
2617         #ifndef NOAUDIO
2618         if (game.music == NULL && game.audio_enabled) {
2619             game.menu_music = !game.menu_music;
2620             if (game.music_enabled) {
2621                 game.music = Mix_LoadMUS(menu_music[game.menu_music]);
2622                 Mix_FadeInMusic(game.music, -1, 1000);
2623             }
2624         }
2625         #endif
2626         return 0;
2627     }
2628     /* SDL_QUIT */
2629     else
2630         cleanup_and_exit();
2631     return 0;
2632 
2633 }
2634 
add_path_to_list(int x,int y,int direction,struct path_info * path)2635 struct path_info *add_path_to_list(int x, int y, int direction, struct path_info *path) {
2636 
2637     /* increase the path dynamic array by one */
2638     if (game.path_count == 0)
2639         path = (struct path_info*)malloc(sizeof(struct path_info) * (game.path_count + 1));
2640     else
2641         path = (struct path_info*)realloc(path, sizeof(struct path_info) * (game.path_count + 1));
2642     /* store the relevant information to the path structure */
2643     path[game.path_count].x = x;
2644     path[game.path_count].y = y;
2645     path[game.path_count].direction = direction;
2646     path[game.path_count].blit_time = 0;
2647     path[game.path_count].block_num = 0;
2648     path[game.path_count].blit_done = 0;
2649     game.path_count++;
2650     return path;
2651 
2652 }
2653 
change_grid(int x,int y,struct path_info * path,int button)2654 struct path_info *change_grid(int x, int y, struct path_info *path, int button) {
2655 
2656     int i, cnt;
2657     Uint32 current_time;
2658 
2659     if (game.grid[x][y].status >= BLOCK_GONE) {
2660         if (game.highx == 0 || game.skill_level < HARD)
2661             return path;
2662         else {
2663             wrong_move_penalty();
2664             if (game.audio_enabled)
2665                 #ifndef NOAUDIO
2666                 Mix_PlayChannel(WRONG_MOVE, game.sound[WRONG_MOVE], 0);
2667                 #endif
2668             unhighlight_block(game.highx, game.highy);
2669             return path;
2670         }
2671     }
2672     if (game.skill_level == INSANE) {
2673         if ((button == SDL_BUTTON_LEFT && game.grid[x][y].left_or_right == 1) || (button == SDL_BUTTON_RIGHT && game.grid[x][y].left_or_right == 0)) {
2674             wrong_move_penalty();
2675             if (game.audio_enabled)
2676                 #ifndef NOAUDIO
2677                 Mix_PlayChannel(WRONG_MOVE, game.sound[WRONG_MOVE], 0);
2678                 #endif
2679             if (game.highx != 0)
2680                 unhighlight_block(game.highx, game.highy);
2681             else {
2682                 game.current_combos_removed = game.current_sets_removed = 0;
2683                 game.last_color_removed = -1;
2684             }
2685             return path;
2686         }
2687     }
2688     switch (game.highx) {
2689         case 0: {
2690             /* highlight current block */
2691             game.grid[x][y].status = BLOCK_HIGH;
2692             game.block_highlight_num = HIGHLIGHT1;
2693             blit(game.surface[HIGHLIGHT1], 0, 0, 32, 32, (x - 1) * 32 + GRIDSTARTX, (y - 1) * 32 + GRIDSTARTY);
2694             game.highx = x;
2695             game.highy = y;
2696             break;
2697         }
2698         default: {
2699             /* if you choose currently highlighted block,
2700                or the colors don't match, unhighlight first block */
2701             if ((x == game.highx && y == game.highy) || (game.grid[x][y].color != game.grid[game.highx][game.highy].color)) {
2702                 if (game.skill_level > NORMAL && game.grid[x][y].color != game.grid[game.highx][game.highy].color)
2703                     wrong_move_penalty();
2704                 unhighlight_block(x, y);
2705             }
2706             /* if the colors match, see if we can make a legal move */
2707             else if (game.grid[x][y].color == game.grid[game.highx][game.highy].color) {
2708                 /* as long as the colors match, we can eliminate the blocks in Kids mode */
2709                 if (game.skill_level == KIDS) {
2710                     update_score_and_bonus();
2711                     if (SDL_GetTicks() < game.start_timer + game.skipped_time + game.paused_time)
2712                         game.stop_timer = 0;
2713                     else
2714                         game.stop_timer = SDL_GetTicks() - (game.start_timer + game.skipped_time + game.paused_time);
2715                     game.block_num++;
2716                     game.erased++;
2717                     eliminate_blocks(x, y);
2718                 }
2719                 /* blocks are next to each other */
2720                 else if (abs(x - game.highx) == 0 && abs(y - game.highy) == 1) {
2721                     update_score_and_bonus();
2722                     if (SDL_GetTicks() < game.start_timer + game.skipped_time + game.paused_time)
2723                         game.stop_timer = 0;
2724                     else
2725                         game.stop_timer = SDL_GetTicks() - (game.start_timer + game.skipped_time + game.paused_time);
2726                     game.block_num++;
2727                     game.erased++;
2728                     eliminate_blocks(x, y);
2729                 }
2730                 else if (abs(x - game.highx) == 1 && abs(y - game.highy) == 0) {
2731                     update_score_and_bonus();
2732                     if (SDL_GetTicks() < game.start_timer + game.skipped_time + game.paused_time)
2733                         game.stop_timer = 0;
2734                     else
2735                         game.stop_timer = SDL_GetTicks() - (game.start_timer + game.skipped_time + game.paused_time);
2736                     game.block_num++;
2737                     game.erased++;
2738                     eliminate_blocks(x, y);
2739                 }
2740                 /* if either block is blocked by all sides unhighlight */
2741                 else if (game.grid[x-1][y].status < BLOCK_GONE && game.grid[x+1][y].status < BLOCK_GONE && game.grid[x][y-1].status < BLOCK_GONE && game.grid[x][y+1].status < BLOCK_GONE) {
2742                     if (game.skill_level > NORMAL)
2743                         wrong_move_penalty();
2744                     unhighlight_block(x, y);
2745                 }
2746                 else if (game.grid[game.highx-1][game.highy].status < BLOCK_GONE && game.grid[game.highx+1][game.highy].status < BLOCK_GONE && game.grid[game.highx][game.highy-1].status < BLOCK_GONE && game.grid[game.highx][game.highy+1].status < BLOCK_GONE) {
2747                     if (game.skill_level > NORMAL)
2748                         wrong_move_penalty();
2749                     unhighlight_block(x, y);
2750                 }
2751                 /* begin the process of checking for a legal move */
2752                 else {
2753                     game.destx = x;
2754                     game.desty = y;
2755                     cnt = game.path_count;
2756                     game.first_block = 0;
2757                     path = legal_move(game.destx, game.desty, DIR_NONE, DIR_NONE, path);
2758                     if (cnt != game.path_count) {
2759                         /* add the last block to the list */
2760                         path = add_path_to_list(game.destx, game.desty, LINE_NONE, path);
2761                         /* create the path */
2762                         path = create_path(path, cnt);
2763                         /* update the list with the correct times for erasing
2764                            the blocks as well as the block number */
2765                         current_time = SDL_GetTicks();
2766                         for (i = cnt + 1; i < game.path_count-1; i++) {
2767                             path[i].blit_time = current_time;
2768                             path[i].block_num = game.block_num;
2769                             if (!game.gravity)
2770                                 current_time = current_time + 40;
2771                         }
2772                         if (game.gravity)
2773                             path[cnt].blit_time = current_time + 70;
2774                         else
2775                             path[cnt].blit_time = current_time + 20;
2776                         path[cnt].block_num = game.block_num;
2777                         path[game.path_count-1].blit_time = path[cnt].blit_time;
2778                         path[game.path_count-1].block_num = path[cnt].block_num;
2779                     }
2780                     else {
2781                         if (game.skill_level > NORMAL)
2782                             wrong_move_penalty();
2783                         unhighlight_block(x, y);
2784                     }
2785                 }
2786             }
2787         }
2788     }
2789     return path;
2790 
2791 }
2792 
wrong_move_penalty()2793 void wrong_move_penalty() {
2794 
2795     int x, y, x2, y2, newx, newx2, savex, savey, savex2, savey2, savecolor;
2796     int replacecolor, i;
2797 
2798     game.no_wrong_moves = 0;
2799     game.score--;
2800     /* penalize the time for making a mistake */
2801     game.start_timer -= game.level_time;
2802     game.skipped_time += game.level_time;
2803     /* put 2 blocks eliminated back on the screen */
2804     if (game.erased > 0) {
2805         /* only randomize blocks removed if more than one set gone */
2806         if (game.erased > 1) {
2807             for (x = 0; x < game.erased; x++) {
2808                 newx = rnd(game.erased) - 1;
2809                 newx2 = rnd(game.erased) - 1;
2810                 savex = game.eliminated[x].x;
2811                 savey = game.eliminated[x].y;
2812                 savex2 = game.eliminated[x].x2;
2813                 savey2 = game.eliminated[x].y2;
2814                 game.eliminated[x].x = game.eliminated[newx].x;
2815                 game.eliminated[x].y = game.eliminated[newx].y;
2816                 game.eliminated[x].x2 = game.eliminated[newx2].x2;
2817                 game.eliminated[x].y2 = game.eliminated[newx2].y2;
2818                 game.eliminated[newx].x = savex;
2819                 game.eliminated[newx].y = savey;
2820                 game.eliminated[newx2].x2 = savex2;
2821                 game.eliminated[newx2].y2 = savey2;
2822             }
2823         }
2824         x = game.eliminated[game.erased - 1].x;
2825         y = game.eliminated[game.erased - 1].y;
2826         x2 = game.eliminated[game.erased - 1].x2;
2827         y2 = game.eliminated[game.erased - 1].y2;
2828         /* change the color of the x2,y2 to match x,y */
2829         savecolor = -1;
2830         if (game.grid[x][y].color != game.grid[x2][y2].color) {
2831             savecolor = game.grid[x2][y2].color;
2832             replacecolor = game.grid[x][y].color;
2833             game.grid[x2][y2].color = game.grid[x][y].color;
2834         }
2835         if (savecolor >= 0) {
2836             /* since we changed the color of the previous x2,y2 to match
2837                the previous x,y color; we will change another x2,y2 color
2838                to the savecolor */
2839             i = 0;
2840             while (i < game.erased) {
2841                 if (game.grid[game.eliminated[i].x2][game.eliminated[i].y2].color == replacecolor) {
2842                     game.grid[game.eliminated[i].x2][game.eliminated[i].y2].color = savecolor;
2843                     break;
2844                 }
2845                 else
2846                     i++;
2847             }
2848         }
2849         add_gravity_effect_for_blocks_put_back(x, &y, x2, &y2);
2850         /* put the two blocks back on the screen */
2851         game.grid[x][y].status = BLOCK_NORM;
2852         game.grid[x2][y2].status = BLOCK_NORM;
2853         put_block_back_on_screen(x, y);
2854         put_block_back_on_screen(x2, y2);
2855         draw_blocks();
2856         game.erased--;
2857         game.block_num--;
2858     }
2859     /* no blocks have been eliminated, penalize score & time some more! */
2860     else {
2861         game.start_timer -= (game.level_time * 2);
2862         game.skipped_time += (game.level_time * 2);
2863         game.score -= 2;
2864     }
2865 
2866 }
2867 
cleanup_and_exit()2868 void cleanup_and_exit() {
2869 
2870     int x;
2871 
2872     if (game.fading_effect)
2873         fade_screen(FADE_OUT, 200, SDL_MapRGB(game.screen->format, 0, 0, 0));
2874     for (x = 0; x < NUM_SURFACES; x++)
2875         SDL_FreeSurface(game.surface[x]);
2876     for (x = 0; x < NUM_FONTS; x++)
2877         SDL_FreeSurface(game.font[x].name);
2878     free(game.score_file);
2879     write_options_file();
2880     free(game.options_file);
2881     exit(0);
2882 
2883 }
2884 
read_options_file()2885 void read_options_file() {
2886 
2887     FILE *optionsfile;
2888     int x, pos, corrupt_file;
2889     int num_chars[NUM_OPTS] = {20, 1, 1, 1, 1, 1, 3, 3, 1, 1, 10};
2890     int lower[NUM_OPTS] = {31, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47};
2891     int upper[NUM_OPTS] = {127, 54, 54, 50, 50, 50, 58, 58, 50, 50, 50};
2892     char chr, strng[21];
2893 
2894     /* We will read the file one character at a time to ensure each one is
2895        within range. Each field will be separated with a tab character. */
2896 
2897     /* create the initial options in case there is no
2898        options.dat/.twind.opts or they are corrupt */
2899     game.remember_options = 0;
2900     game.player_name[0] = '\0';
2901     game.skill_level = NORMAL;
2902     game.block_set = BRUSHED_METAL;
2903     game.corner_style = SQUARE;
2904     game.pause_mode = PAUSE_ONLY;
2905     game.display_mode = WINDOW;
2906     game.sound_volume = 128;
2907     game.music_volume = 100;
2908     game.fading_effect = 1;
2909     game.gravity = 0;
2910     strcpy(game.color_l_and_r, "1111111110");
2911 
2912     /* open the file & start reading the options */
2913     if ((optionsfile = fopen(game.options_file, "r")) == NULL) {
2914         fprintf(stderr, "Error reading the options file: %s\n", game.options_file);
2915         game.remember_options = 1;
2916     }
2917     else {
2918         corrupt_file = 0;
2919         pos = 0;
2920         /* did we remember the options? */
2921         chr = fgetc(optionsfile);
2922         while (!corrupt_file && !feof(optionsfile) && pos < 1 && chr != '\t') {
2923             /* only accept a 0 or 1 */
2924             if (chr > 47 && chr < 50) {
2925                 game.remember_options = chr - 48;
2926                 pos++;
2927             }
2928             chr = fgetc(optionsfile);
2929         }
2930         if (chr == '\t')
2931             strng[pos] = '\0';
2932         else
2933             corrupt_file = 1;
2934         if (game.remember_options && !corrupt_file) {
2935             for (x = 0; x < NUM_OPTS; x++) {
2936                 pos = 0;
2937                 chr = fgetc(optionsfile);
2938                 while (!corrupt_file && !feof(optionsfile) && pos < num_chars[x] && chr != '\t') {
2939                     /* only accept characters/numbers according to the array above */
2940                     if (chr > lower[x] && chr < upper[x]) {
2941                         strng[pos] = chr;
2942                         pos++;
2943                     }
2944                     chr = fgetc(optionsfile);
2945                 }
2946                 if (chr == '\t') {
2947                     strng[pos] = '\0';
2948                     /* place what we have read from the file into the game structure */
2949                     switch (x) {
2950                         case 0:
2951                             strcpy(game.player_name, strng);
2952                             break;
2953                         case 1:
2954                             game.skill_level = atoi(strng);
2955                             break;
2956                         case 2:
2957                             game.block_set = atoi(strng);
2958                             change_block_set();
2959                             break;
2960                         case 3:
2961                             game.corner_style = atoi(strng);
2962                             break;
2963                         case 4:
2964                             game.pause_mode = atoi(strng);
2965                             break;
2966                         case 5:
2967                             game.display_mode = atoi(strng);
2968                             break;
2969                         case 6:
2970                             game.sound_volume = atoi(strng);
2971                             break;
2972                         case 7:
2973                             game.music_volume = atoi(strng);
2974                             break;
2975                         case 8:
2976                             game.fading_effect = atoi(strng);
2977                             break;
2978                         case 9:
2979                             game.gravity = atoi(strng);
2980                             break;
2981                         case 10:
2982                             strcpy(game.color_l_and_r, strng);
2983                             break;
2984                     }
2985                 }
2986                 else
2987                     corrupt_file = 1;
2988             }
2989             if (corrupt_file)
2990                 /* options.dat corrupted... inform user that some saved options could be lost */
2991                 fprintf(stderr, "Your options file: %s is corrupted\nSome or all saved options may be lost.\n", game.options_file);
2992         }
2993         if (corrupt_file) game.remember_options = 1;
2994         fclose(optionsfile);
2995     }
2996 
2997 }
2998 
write_options_file()2999 void write_options_file() {
3000 
3001     FILE *optionsfile;
3002     int i;
3003 
3004     if ((optionsfile = fopen(game.options_file, "w")) == NULL)
3005         fprintf(stderr, "Error writing the options file: %s\nDo you have write access to this directory?\n", game.options_file);
3006     else {
3007         /* remember options */
3008         fputc(game.remember_options + 48, optionsfile);
3009         fputc('\t', optionsfile);
3010         if (game.remember_options) {
3011             /* player name */
3012             for (i = 0; i < strlen(game.player_name); i++)
3013                 fputc(game.player_name[i], optionsfile);
3014             fputc('\t', optionsfile);
3015             /* skill level */
3016             fputc(game.skill_level + 48, optionsfile);
3017             fputc('\t', optionsfile);
3018             /* block set */
3019             fputc(game.block_set + 48, optionsfile);
3020             fputc('\t', optionsfile);
3021             /* corner style */
3022             fputc(game.corner_style + 48, optionsfile);
3023             fputc('\t', optionsfile);
3024             /* pause mode */
3025             fputc(game.pause_mode + 48, optionsfile);
3026             fputc('\t', optionsfile);
3027             /* display mode */
3028             fputc(game.display_mode + 48, optionsfile);
3029             fputc('\t', optionsfile);
3030             /* sound volume */
3031             num_to_str(game.sound_volume, 3);
3032             for (i = 0; i < strlen(game.temp_string); i++)
3033                 fputc(game.temp_string[i], optionsfile);
3034             free (game.temp_string);
3035             fputc('\t', optionsfile);
3036             /* music volume */
3037             num_to_str(game.music_volume, 3);
3038             for (i = 0; i < strlen(game.temp_string); i++)
3039                 fputc(game.temp_string[i], optionsfile);
3040             free (game.temp_string);
3041             fputc('\t', optionsfile);
3042             /* fading effect */
3043             fputc(game.fading_effect + 48, optionsfile);
3044             fputc('\t', optionsfile);
3045             /* gravity */
3046             fputc(game.gravity + 48, optionsfile);
3047             fputc('\t', optionsfile);
3048             /* colors for l & r in Insane mode */
3049             for (i = 0; i < strlen(game.color_l_and_r); i++)
3050                 fputc(game.color_l_and_r[i], optionsfile);
3051             fputc('\t', optionsfile);
3052         }
3053         fclose(optionsfile);
3054     }
3055 
3056 }
3057 
create_path(struct path_info * path,int cnt)3058 struct path_info *create_path(struct path_info *path, int cnt) {
3059 
3060     int i;
3061     int directions[5][5] = {{0, 0, 0, 0, 0},
3062                             {0, LINE_VER, LINE_LR,  0,        LINE_LL},
3063                             {0, LINE_UL,  LINE_HOR, LINE_LL,  0},
3064                             {0, 0,        LINE_UR,  LINE_VER, LINE_UL},
3065                             {0, LINE_UR,  0,        LINE_LR,  LINE_HOR}};
3066 
3067     /* assign a direction for the path based on the array from above */
3068     for (i = game.path_count - 2; i > cnt; i--)
3069         path[i].direction = directions[path[i-1].direction][path[i].direction];
3070     path[cnt].direction = LINE_NONE;
3071     return path;
3072 
3073 }
3074 
create_random_grid()3075 void create_random_grid() {
3076 
3077     int x, y, newposx, newposy, savecolor;
3078 
3079     srand((unsigned int)time((time_t*)NULL));
3080 
3081     /* setup a 12x12 grid */
3082     for (x = 0; x < 12; x++) {
3083         for (y = 0; y < 12; y++) {
3084             if (x == 0 || y == 0 || x == 11 || y == 11)
3085                 game.grid[x][y].status = BLOCK_GONE;
3086             else {
3087                 game.grid[x][y].status = BLOCK_NORM;
3088                 game.grid[x][y].color = ((x - 1) * 10 + (y - 1)) / 10 * 32;
3089                 game.grid[x][y].left_or_right = rnd(2) - 1;
3090             }
3091         }
3092     }
3093     /* randomize the colors */
3094     for (x = 0; x < 10; x++) {
3095         for (y = 0; y < 10; y++) {
3096             newposx = rnd(10) - 1;
3097             newposy = rnd(10) - 1;
3098             savecolor = game.grid[x + 1][y + 1].color;
3099             game.grid[x + 1][y + 1].color = game.grid[newposx + 1][newposy + 1].color;
3100             game.grid[newposx + 1][newposy + 1].color = savecolor;
3101         }
3102     }
3103 
3104 }
3105 
randomize_blocks()3106 void randomize_blocks() {
3107 
3108     struct existing_blocks existing[100];
3109     int x, y, newx, savecolor, savel_or_r, num_existing;
3110 
3111     srand((unsigned int)time((time_t*)NULL));
3112 
3113     /* initialize the array */
3114     for (x = 0; x < 100; x++) {
3115         existing[x].x = 0;
3116         existing[x].y = 0;
3117         existing[x].color = 0;
3118         existing[x].left_or_right = 0;
3119     }
3120     /* find all the blocks not gone from the screen */
3121     num_existing = 0;
3122     for (x = 1; x < 11; x++) {
3123         for (y = 1; y < 11; y++) {
3124             if (game.grid[x][y].status < BLOCK_GONE) {
3125                 existing[num_existing].x = x;
3126                 existing[num_existing].y = y;
3127                 existing[num_existing].color = game.grid[x][y].color;
3128                 existing[num_existing].left_or_right = game.grid[x][y].left_or_right;
3129                 num_existing++;
3130             }
3131         }
3132     }
3133     /* randomize the blocks */
3134     for (x = 0; x < num_existing; x++) {
3135         newx = rnd(num_existing) - 1;
3136         savecolor = existing[x].color;
3137         savel_or_r = existing[x].left_or_right;
3138         existing[x].color = existing[newx].color;
3139         existing[newx].color = savecolor;
3140         existing[x].left_or_right = existing[newx].left_or_right;
3141         existing[newx].left_or_right = savel_or_r;
3142     }
3143     /* update the game.grid with the new data */
3144     for (x = 0; x < num_existing; x++) {
3145         game.grid[existing[x].x][existing[x].y].color = existing[x].color;
3146         game.grid[existing[x].x][existing[x].y].left_or_right = existing[x].left_or_right;
3147     }
3148 
3149 }
3150 
randomize_music()3151 void randomize_music() {
3152 
3153     int x, newx, savex;
3154 
3155     srand((unsigned int)time((time_t*)NULL));
3156 
3157     /* setup music array */
3158     for (x = 0; x < MUSIC_LOOPS; x++)
3159         game.music_loop[x] = x + 1;
3160 
3161     /* randomize the music array */
3162     for (x = 0; x < MUSIC_LOOPS; x++) {
3163         newx = rnd(MUSIC_LOOPS) - 1;
3164         savex = game.music_loop[x];
3165         game.music_loop[x] = game.music_loop[newx];
3166         game.music_loop[newx] = savex;
3167     }
3168     /*printf("Music loop order:");
3169     for (x = 0; x < MUSIC_LOOPS; x++)
3170         printf(" %d",game.music_loop[x]);
3171     printf("\n");*/
3172 }
3173 
draw_blocks()3174 void draw_blocks() {
3175 
3176     int x, y, destx, desty, color_l_and_r;
3177 
3178     for (x = 1; x < 11; x++) {
3179         for (y = 1; y < 11; y++) {
3180             destx = (x - 1) * 32 + GRIDSTARTX;
3181             desty = (y - 1) * 32 + GRIDSTARTY;
3182             if (game.grid[x][y].status == BLOCK_NORM) {
3183                 blit(game.surface[BLOCKS], game.grid[x][y].color, 0, 32, 32, destx, desty);
3184                 if (game.skill_level == INSANE) {
3185                     color_l_and_r = game.color_l_and_r[game.grid[x][y].color / 32] - 48;
3186                     blit(game.surface[game.grid[x][y].left_or_right * 2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, destx, desty);
3187                 }
3188                 change_corner_style(destx, desty);
3189             }
3190             else if (game.grid[x][y].status == BLOCK_HIGH) {
3191                 blit(game.surface[BLOCKS], game.grid[x][y].color, 0, 32, 32, destx, desty);
3192                 if (game.skill_level == INSANE) {
3193                     color_l_and_r = game.color_l_and_r[game.grid[x][y].color / 32] - 48;
3194                     blit(game.surface[game.grid[x][y].left_or_right * 2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, destx, desty);
3195                 }
3196                 change_corner_style(destx, desty);
3197                 blit(game.surface[HIGHLIGHT1], 0, 0, 32, 32, destx, desty);
3198             }
3199             else
3200                 fill(destx, desty, 32, 32, 0, 0, 0);
3201         }
3202     }
3203 
3204 }
3205 
draw_grid_on_screen()3206 void draw_grid_on_screen() {
3207 
3208     int srch;
3209     char *skill_levels[NUM_SKILLS] = {"Kids", "Easy", "Normal", "Hard", "Very Hard", "Insane"};
3210 
3211     blank_screen();
3212     /* draw the background */
3213     blit(game.surface[BACKGROUND], 0, 0, 406, 406, -800, -600);
3214 
3215     /* draw the title */
3216     blit(game.surface[TITLE_SMALL], 0, 0, game.surface[TITLE_SMALL]->w, game.surface[TITLE_SMALL]->h, -800, 0);
3217     print_word(game.player_name, -800, 45, SERIFG24);
3218     print_word("Skill:", GRIDSTARTX - 41, 71, SERIFG24);
3219     print_word(skill_levels[game.skill_level], GRIDSTARTX - 41 + word_width("Skill: ", SERIFG24), 71, SERIFG24);
3220     print_word("Gravity:", GRIDSTARTX + 361 - word_width("Gravity: Off", SERIFG24), 71, SERIFG24);
3221     if (game.gravity)
3222         print_word("On", GRIDSTARTX + 361 - word_width("On", SERIFG24), 71, SERIFG24);
3223     else
3224         print_word("Off", GRIDSTARTX + 361 - word_width("Off", SERIFG24), 71, SERIFG24);
3225     print_word("Level:", GRIDSTARTX - 41, 505, SERIFG24);
3226     print_number(GRIDSTARTX - 41 + word_width("Level: ", SERIFG24), 505, SERIFG24, game.current_level, 3);
3227     print_score_and_bonus();
3228     print_word("New Game", 5, 5, SERIFG24);
3229     if (game.paused)
3230         print_word("Unpause", 5 + word_width("New Game  ", SERIFG24), 5, SERIFG24);
3231     else {
3232         fill(5 + word_width("New Game  ", SERIFG24), 5, word_width("Unpause", SERIFG24), game.font[SERIFG24].height, 0, 0, 0);
3233         print_word("Pause", 5 + word_width("New Game  ", SERIFG24), 5, SERIFG24);
3234     }
3235     print_word("Help", 795 - word_width("Help  Exit", SERIFG24), 5, SERIFG24);
3236     print_word("Exit", 795 - word_width("Exit", SERIFG24), 5, SERIFG24);
3237 
3238     /* draw the blocks or display a 'Game Paused' message */
3239     if (!game.paused)
3240         draw_blocks();
3241     else
3242         print_word("Game Paused", -800, -600, SERIFW24);
3243 
3244     /* draw the timer */
3245     blit(game.surface[TIMER_FULL], 0, 0, 160, 32, GRIDSTARTX + 80, 545);
3246     if (game.level_start == 1) {
3247         if (!game.paused)
3248             srch = (SDL_GetTicks() - (game.start_timer + game.paused_time)) / game.level_time + 2;
3249         else
3250             srch = (game.temp_time - (game.start_timer + game.paused_time)) / game.level_time + 2;
3251         blit(game.surface[TIMER_EMPTY], 160 - srch, 0, srch, 32, GRIDSTARTX + 80 + 160 - srch, 545);
3252     }
3253 
3254     /* draw the volume meters */
3255     if (game.audio_enabled) {
3256         print_word("Sound:", GRIDSTARTX + 80 - (138 + word_width("Sound: ", SERIFG24)), 548, SERIFG24);
3257         blit(game.surface[VOLUME_BAR], 0, 0, 133, 32, GRIDSTARTX + 80 - 138, 545);
3258         blit(game.surface[VOLUME_SLIDER], 0, 0, 5, 32, game.sound_volume + GRIDSTARTX + 80 - 138, 545);
3259         if (game.music_enabled) {
3260             print_word("Music:", GRIDSTARTX + 80 + 165, 548, SERIFG24);
3261             blit(game.surface[VOLUME_BAR], 0, 0, 133, 32, GRIDSTARTX + 80 + 165 + word_width("Music: ", SERIFG24), 545);
3262             blit(game.surface[VOLUME_SLIDER], 0, 0, 5, 32, game.music_volume + GRIDSTARTX + 80 + 165 + word_width("Music: ", SERIFG24), 545);
3263         }
3264     }
3265 
3266 }
3267 
eliminate_blocks(int x,int y)3268 void eliminate_blocks(int x, int y) {
3269 
3270     /* change the status of the blocks */
3271     game.grid[x][y].status = BLOCK_GONE;
3272     game.grid[game.highx][game.highy].status = BLOCK_GONE;
3273     /* erase them from the screen */
3274     fill((x - 1) * 32 + GRIDSTARTX, (y - 1) * 32 + GRIDSTARTY, 32, 32, 0, 0, 0);
3275     fill((game.highx - 1) * 32 + GRIDSTARTX, (game.highy - 1) * 32 + GRIDSTARTY, 32, 32, 0, 0, 0);
3276     add_gravity_effect(x, &y, game.highx, &game.highy);
3277     /* in case the player makes a wrong move, keep track of the blocks */
3278     keep_track_of_removed_blocks(x, y, game.highx, game.highy);
3279     #ifndef NOAUDIO
3280     if (game.audio_enabled) Mix_PlayChannel(ELIMINATE_BLOCKS, game.sound[ELIMINATE_BLOCKS], 0);
3281     #endif
3282     game.highx = game.highy = game.turns = 0;
3283 
3284 }
3285 
keep_track_of_removed_blocks(int x,int y,int x2,int y2)3286 void keep_track_of_removed_blocks(int x, int y, int x2, int y2) {
3287 
3288     /* store the removed blocks for the Hard, Very Hard & Insane game mode */
3289     if (game.skill_level >= HARD) {
3290         game.eliminated[game.erased - 1].x = x;
3291         game.eliminated[game.erased - 1].y = y;
3292         game.eliminated[game.erased - 1].x2 = x2;
3293         game.eliminated[game.erased - 1].y2 = y2;
3294     }
3295 
3296 }
3297 
erase_path(struct path_info * path,int block_num)3298 struct path_info *erase_path(struct path_info *path, int block_num) {
3299 
3300     int i, cnt, block, blit_time, x, y, x2, y2, destx, desty, color_l_and_r;
3301 
3302     cnt = block = 0;
3303     for (i = 0; i < game.path_count; i++) {
3304         /* only eliminate the blocks & path we are requesting */
3305         if (path[i].block_num == block_num) {
3306             /* used for the wrong move penalty & gravity */
3307             if (game.skill_level >= HARD || game.gravity) {
3308                 /* 1st block, keep track of it & capture blit_time */
3309                 if (!block) {
3310                     blit_time = path[i].blit_time;
3311                     block = 1;
3312                     x = path[i].x;
3313                     y = path[i].y;
3314                 }
3315                 /* 2nd block, keep track of it & the 1st block from above */
3316                 else if (blit_time == path[i].blit_time) {
3317                     x2 = path[i].x;
3318                     y2 = path[i].y;
3319                     add_gravity_effect(x, &y, x2, &y2);
3320                     keep_track_of_removed_blocks(x, y, x2, y2);
3321                 }
3322             }
3323             destx = (path[i].x - 1) * 32 + GRIDSTARTX;
3324             desty = (path[i].y - 1) * 32 + GRIDSTARTY;
3325             /* erase the path (unless the block was put back on because of
3326                a wrong move in the Very Hard or Insane mode) */
3327             if (game.grid[path[i].x][path[i].y].status == BLOCK_NORM) {
3328                 blit(game.surface[BLOCKS], game.grid[path[i].x][path[i].y].color, 0, 32, 32, destx, desty);
3329                 if (game.skill_level == INSANE) {
3330                     color_l_and_r = game.color_l_and_r[game.grid[path[i].x][path[i].y].color / 32] - 48;
3331                     blit(game.surface[game.grid[path[i].x][path[i].y].left_or_right * 2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, destx, desty);
3332                 }
3333                 change_corner_style(destx, desty);
3334             }
3335             else {
3336                 if (game.grid[path[i].x][path[i].y].status == BLOCK_GOING)
3337                     game.grid[path[i].x][path[i].y].status = BLOCK_GONE;
3338                 fill(destx, desty, 32, 32, 0, 0, 0);
3339             }
3340             /* count the block being erased */
3341             cnt++;
3342             /* once the block is erased, update path structure to indicate it */
3343             path[i].x = -1;
3344             #ifndef NOAUDIO
3345             if (game.audio_enabled) Mix_PlayChannel(ELIMINATE_BLOCKS, game.sound[ELIMINATE_BLOCKS], 0);
3346             #endif
3347         }
3348         /* count the block that has already been erased */
3349         else if (path[i].x == -1)
3350             cnt++;
3351     }
3352     /* deallocate list if all block have been erased */
3353     if (cnt == game.path_count) {
3354         free(path);
3355         game.path_count = 0;
3356     }
3357     return path;
3358 
3359 }
3360 
add_gravity_effect(int x,int * y,int x2,int * y2)3361 void add_gravity_effect(int x, int *y, int x2, int *y2) {
3362 
3363     struct block temp;
3364     int i, cnt, switched, tempx, tempy;
3365 
3366     if (game.gravity) {
3367         if (x == x2 && *y > *y2)
3368             switched = 1;
3369         else
3370             switched = 0;
3371         cnt = 0;
3372         while (cnt < 2) {
3373             if ((cnt == 0 && !switched) || (cnt == 1 && switched)) {
3374                 tempx = x;
3375                 tempy = *y;
3376             }
3377             else {
3378                 tempx = x2;
3379                 tempy = *y2;
3380             }
3381             /* move all blocks above the highlighted block down */
3382             temp.color = game.grid[tempx][tempy].color;
3383             temp.left_or_right = game.grid[tempx][tempy].left_or_right;
3384             temp.status = game.grid[tempx][tempy].status;
3385             i = tempy;
3386             while (i > 1) {
3387                 /* stop moving blocks if the one above is empty */
3388                 if (game.grid[tempx][i-1].status > BLOCK_HIGH)
3389                     break;
3390                 game.grid[tempx][i].color = game.grid[tempx][i-1].color;
3391                 game.grid[tempx][i].left_or_right = game.grid[tempx][i-1].left_or_right;
3392                 game.grid[tempx][i].status = game.grid[tempx][i-1].status;
3393                 i--;
3394             }
3395             if ((cnt == 0 && !switched) || (cnt == 1 && switched))
3396                 *y = i;
3397             else
3398                 *y2 = i;
3399             game.grid[tempx][i].color = temp.color;
3400             game.grid[tempx][i].left_or_right = temp.left_or_right;
3401             game.grid[tempx][i].status = temp.status;
3402             cnt++;
3403         }
3404         draw_grid_on_screen();
3405     }
3406 
3407 }
3408 
add_gravity_effect_for_blocks_put_back(int x,int * y,int x2,int * y2)3409 void add_gravity_effect_for_blocks_put_back(int x, int *y, int x2, int *y2) {
3410 
3411     struct block temp;
3412     int i, j, cnt, switched, tempx, tempy;
3413 
3414     if (game.gravity && game.erased > 1) {
3415         if (x == x2 && *y < *y2)
3416             switched = 1;
3417         else
3418             switched = 0;
3419         cnt = 0;
3420         while (cnt < 2) {
3421             if ((cnt == 0 && !switched) || (cnt == 1 && switched)) {
3422                 tempx = x;
3423                 tempy = *y;
3424             }
3425             else {
3426                 tempx = x2;
3427                 tempy = *y2;
3428             }
3429             /* move all blocks above the highlighted block down */
3430             temp.color = game.grid[tempx][tempy].color;
3431             temp.left_or_right = game.grid[tempx][tempy].left_or_right;
3432             temp.status = game.grid[tempx][tempy].status;
3433             i = tempy + 1;
3434             while (i < 11) {
3435                 /* find the next non-empty block */
3436                 if (game.grid[tempx][i].status < BLOCK_GONE)
3437                     break;
3438                 i++;
3439             }
3440             /* we want the previous block, since the loop above found a block
3441                on screen */
3442             i--;
3443             /* switch it all around */
3444             game.grid[tempx][tempy].color = game.grid[tempx][i].color;
3445             game.grid[tempx][tempy].left_or_right = game.grid[tempx][i].left_or_right;
3446             game.grid[tempx][tempy].status = game.grid[tempx][i].status;
3447             game.grid[tempx][i].color = temp.color;
3448             game.grid[tempx][i].left_or_right = temp.left_or_right;
3449             game.grid[tempx][i].status = temp.status;
3450             /* we need to update the game.eliminated array as well */
3451             for (j = 0; j < game.erased - 1; j++) {
3452                 if ((game.eliminated[j].x == tempx && game.eliminated[j].y == i) || (game.eliminated[j].x2 == tempx && game.eliminated[j].y2 == i)) {
3453                     if (game.eliminated[j].x == tempx && game.eliminated[j].y == i)
3454                         game.eliminated[j].y = tempy;
3455                     else
3456                         game.eliminated[j].y2 = tempy;
3457                     j = game.erased;
3458                 }
3459             }
3460             /* update what block we really want to put back on the screen */
3461             if ((cnt == 0 && !switched) || (cnt == 1 && switched))
3462                 *y = i;
3463             else
3464                 *y2 = i;
3465             game.grid[tempx][i].status = BLOCK_NORM;
3466             cnt++;
3467         }
3468         draw_grid_on_screen();
3469     }
3470 
3471 }
initialize_and_load()3472 void initialize_and_load() {
3473 
3474     int initvideo, i, j, pos, lastpos, x, loaded;
3475     SDL_Surface *icon;
3476     Uint8 r, g, b;
3477     #ifndef NOAUDIO
3478     int initaudio;
3479     int audio_rate = 22050;
3480     Uint16 audio_format = AUDIO_S16;
3481     int audio_channels = 2;
3482     int audio_buffers = 1024;
3483     #endif
3484     #ifdef LINUX
3485     char *home;
3486     #endif
3487     char *image[NUM_SURFACES] = {DATA_PREFIX "graphics/background" EXTENSION,
3488                                DATA_PREFIX "graphics/brushed_metal" EXTENSION,
3489                                DATA_PREFIX "graphics/highlight1" EXTENSION,
3490                                DATA_PREFIX "graphics/highlight2" EXTENSION,
3491                                DATA_PREFIX "graphics/highlight3" EXTENSION,
3492                                DATA_PREFIX "graphics/highlight4" EXTENSION,
3493                                DATA_PREFIX "graphics/black_l" EXTENSION,
3494                                DATA_PREFIX "graphics/white_l" EXTENSION,
3495                                DATA_PREFIX "graphics/black_r" EXTENSION,
3496                                DATA_PREFIX "graphics/white_r" EXTENSION,
3497                                DATA_PREFIX "graphics/path_blocks" EXTENSION,
3498                                DATA_PREFIX "graphics/timer_full" EXTENSION,
3499                                DATA_PREFIX "graphics/timer_full_white" EXTENSION,
3500                                DATA_PREFIX "graphics/timer_empty" EXTENSION,
3501                                DATA_PREFIX "graphics/timer_empty_white" EXTENSION,
3502                                DATA_PREFIX "graphics/title_small" EXTENSION,
3503                                DATA_PREFIX "graphics/title_big" EXTENSION,
3504                                DATA_PREFIX "graphics/volume_bar" EXTENSION,
3505                                DATA_PREFIX "graphics/volume_slider" EXTENSION};
3506     char *font[NUM_FONTS] = {DATA_PREFIX "graphics/freeserif_green_20" EXTENSION,
3507                              DATA_PREFIX "graphics/freeserif_white_20" EXTENSION,
3508                              DATA_PREFIX "graphics/freeserif_green_24" EXTENSION,
3509                              DATA_PREFIX "graphics/freeserif_white_24" EXTENSION};
3510     #ifndef NOAUDIO
3511     char *wav[NUM_SOUNDS] = {DATA_PREFIX "sound/menu_option.wav",
3512                              DATA_PREFIX "sound/option_choice.wav",
3513                              DATA_PREFIX "sound/keyboard_click.wav",
3514                              DATA_PREFIX "sound/eliminate_blocks.wav",
3515                              DATA_PREFIX "sound/wrong_move.wav",
3516                              DATA_PREFIX "sound/apply_bonus.wav"};
3517     #endif
3518 
3519     initvideo = SDL_Init(SDL_INIT_VIDEO);
3520     if (initvideo) {
3521         fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
3522         exit(1);
3523     }
3524     #ifndef NOAUDIO
3525     initaudio = SDL_InitSubSystem(SDL_INIT_AUDIO);
3526     if(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) == -1) {
3527         fprintf(stderr, "Unable to open audio: %s\n", Mix_GetError());
3528         game.audio_enabled = 0;
3529     }
3530     else {
3531         game.audio_enabled = game.music_enabled = 1;
3532         /* check to see if ogg music can be played */
3533         game.music = Mix_LoadMUS(DATA_PREFIX "music/0-0.ogg");
3534         if (game.music == NULL) {
3535             fprintf(stderr, "Unable to load %s\n", DATA_PREFIX "music/0-0.ogg");
3536             fprintf(stderr, "%s\n", Mix_GetError());
3537             /* 1st file failed, try another */
3538             game.music = Mix_LoadMUS(DATA_PREFIX "music/1-1.ogg");
3539             if (game.music == NULL) {
3540                 fprintf(stderr, "Unable to load %s\n", DATA_PREFIX "music/1-1.ogg");
3541                 fprintf(stderr, "%s\n", Mix_GetError());
3542                 fprintf(stderr, "\nDisabling music support... it seems that Ogg Vorbis files can't be played.\n");
3543                 fprintf(stderr, "Please install Ogg Vorbis libraries on your system & rebuild your SDL_mixer\n");
3544                 fprintf(stderr, "if you would like to hear the music.\n");
3545                 game.music_enabled = 0;
3546             }
3547         }
3548         if (game.music_enabled)
3549             Mix_FreeMusic(game.music);
3550     }
3551     #else
3552         game.audio_enabled = 0;
3553     #endif
3554     atexit(SDL_Quit);
3555     #ifndef NOAUDIO
3556     if (game.audio_enabled) atexit(Mix_CloseAudio);
3557     #endif
3558     SDL_EnableUNICODE(1);
3559 
3560     game.screen = SDL_SetVideoMode(800, 600, 0, SDL_SWSURFACE);
3561     if (game.screen == NULL)
3562         fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
3563     loaded = 1;
3564     /* load all the graphics */
3565     for (x = 0; x < NUM_SURFACES; x++)
3566         game.surface[x] = load_image(image[x], &loaded);
3567     /* set certain blocks to have transparency */
3568     for (x = HIGHLIGHT1; x <= WHITE_R; x++)
3569         SDL_SetColorKey(game.surface[x], SDL_SRCCOLORKEY, SDL_MapRGB(game.screen->format, 0, 255, 255));
3570     /* load the sounds */
3571     #ifndef NOAUDIO
3572     if (game.audio_enabled) {
3573         for (x = 0; x < NUM_SOUNDS; x++)
3574             game.sound[x] = load_wav(wav[x], &loaded);
3575     }
3576     #endif
3577     /* load the fonts */
3578     for (x = 0; x < NUM_FONTS; x++)
3579         game.font[x].name = load_image(font[x], &loaded);
3580     icon = load_image(DATA_PREFIX "graphics/twind" EXTENSION, &loaded);
3581     /* set the in-game menu options to unhighlighted */
3582     for (x = 0; x < NUM_IN_GAME_MO; x++)
3583         game.highlight[x] = 0;
3584     if (!loaded)
3585         exit(2);
3586 
3587     SDL_WM_SetCaption("Twin Distress", NULL);
3588     SDL_WM_SetIcon(icon, NULL);
3589     SDL_FreeSurface(icon);
3590     /* initialize game variables */
3591     game.destx = game.desty = game.turns = game.fading_off_temp = 0;
3592     game.option_toggle = game.new_game_highlighted = 0;
3593     game.help_highlighted = 0;
3594     game.blit_done = game.unpause_msg = game.menu_music = 1;
3595     game.max_turns = 2;
3596     game.num_groups = NUM_SKILLS * 2;
3597     /* height is one less than actual... 1st row will be used below */
3598     for (i = 0; i < NUM_FONTS; i++)
3599         game.font[i].height = game.font[i].name->h - 1;
3600     /* 1st row of font file contains the positions of each character */
3601     for (i = 0; i < NUM_FONTS; i++) {
3602         lastpos = 0;
3603         pos = 1;
3604         game.font[i].pos[0] = 0;
3605         SDL_LockSurface(game.font[i].name);
3606         for (j = 0; j < 95; j++) {
3607             if (j != 0)
3608                 game.font[i].pos[j] = game.font[i].pos[j] = pos - 1;
3609             /* find the ending position of the character */
3610             SDL_GetRGB(getpixel(game.font[i].name, pos), game.font[i].name->format, &r, &g, &b);
3611             while (1) {
3612                 if (r == 0 && g > 0 && b > 0)
3613                    break;
3614                 else {
3615                     pos++;
3616                     SDL_GetRGB(getpixel(game.font[i].name, pos), game.font[i].name->format, &r, &g, &b);
3617                 }
3618             }
3619             game.font[i].width[j] = (pos - lastpos) + 1;
3620             /* find the start of the next character, if we haven't reached the last one */
3621             if (j != 94) {
3622                 pos++;
3623                 SDL_GetRGB(getpixel(game.font[i].name, pos), game.font[i].name->format, &r, &g, &b);
3624                 while (1) {
3625                     if (r == 0 && g > 0 && b > 0)
3626                        break;
3627                     else {
3628                         pos++;
3629                         SDL_GetRGB(getpixel(game.font[i].name, pos), game.font[i].name->format, &r, &g, &b);
3630                     }
3631                 }
3632             lastpos = pos;
3633             pos++;
3634             }
3635         }
3636         SDL_UnlockSurface(game.font[i].name);
3637     }
3638     #ifndef LINUX
3639     game.options_file = (char *)malloc(sizeof(char) * (12));
3640     #else
3641     /* get home directory (from $HOME variable), or use the current directory */
3642     if (getenv("HOME") != NULL)
3643         home = getenv("HOME");
3644     else
3645         home = ".";
3646     game.options_file = (char *)malloc(strlen(home) + sizeof(char) * (13));
3647     #endif
3648     game.options_file[0] = '\0';
3649     #ifndef LINUX
3650     strcat(game.options_file, "options.dat");
3651     #else
3652     strcat(game.options_file, home);
3653     strcat(game.options_file, "/.twind.opts");
3654     #endif
3655     read_options_file();
3656     /* set the options from the options.dat file */
3657     if (game.display_mode == FULLSCREEN) {
3658         game.screen = SDL_SetVideoMode(800, 600, 0, SDL_FULLSCREEN);
3659         if (game.screen == NULL) {
3660             game.screen = SDL_SetVideoMode(800, 600, 0, SDL_SWSURFACE);
3661             game.display_mode = WINDOW;
3662         }
3663     }
3664     #ifndef NOAUDIO
3665     if (game.audio_enabled) {
3666         game.music = NULL;
3667         game.regular_music = 1;
3668         if (game.music_enabled)
3669             Mix_VolumeMusic(game.music_volume);
3670         Mix_Volume(-1, game.sound_volume);
3671     }
3672     #endif
3673 
3674 }
3675 
getpixel(SDL_Surface * surface,int x)3676 Uint32 getpixel(SDL_Surface *surface, int x) {
3677 
3678     /* this function was taken directly from the SDL documentation */
3679 
3680     int bpp = surface->format->BytesPerPixel;
3681     int y = 0;
3682 
3683     /* Here p is the address to the pixel we want to retrieve */
3684     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
3685 
3686     switch(bpp) {
3687         case 1:
3688             return *p;
3689         case 2:
3690             return *(Uint16 *)p;
3691         case 3:
3692             if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
3693                 return p[0] << 16 | p[1] << 8 | p[2];
3694             else
3695                 return p[0] | p[1] << 8 | p[2] << 16;
3696         case 4:
3697             return *(Uint32 *)p;
3698         default:
3699             return 0;       /* shouldn't happen, but avoids warnings */
3700     }
3701 
3702 }
3703 
legal_move(int x,int y,int cur_dir,int prev_dir,struct path_info * path)3704 struct path_info *legal_move(int x, int y, int cur_dir, int prev_dir, struct path_info *path) {
3705 
3706     int directions[4];
3707     int i, cnt;
3708 
3709     /* we've changed directions */
3710     if (cur_dir != prev_dir && prev_dir != DIR_NONE)
3711         game.turns++;
3712 
3713     /* the block has been found */
3714     if (x == game.highx && y == game.highy) {
3715         if (SDL_GetTicks() < game.start_timer + game.skipped_time + game.paused_time)
3716             game.stop_timer = 0;
3717         else
3718             game.stop_timer = SDL_GetTicks() - (game.start_timer + game.skipped_time + game.paused_time);
3719         game.first_block = 1;
3720         game.block_num++;
3721         return path;
3722     }
3723 
3724     /* we've hit a block */
3725     if (cur_dir != DIR_NONE && game.grid[x][y].status < BLOCK_GONE) {
3726         if (cur_dir != prev_dir && prev_dir != DIR_NONE)
3727             game.turns--;
3728         return path;
3729     }
3730 
3731     /* try to find a path to the block... this WILL NOT always be the shortest
3732        path... maybe I'll implement a better path finding algorithm like A* or
3733        Dijkstra's one of these days, but this one will get the job done! */
3734 
3735     /* the blocks are in the same horizontal line */
3736     if (y == game.highy) {
3737         /* block we are seeking is right */
3738         if (x < game.highx)
3739             setup_array(DIR_RIGHT, DIR_UP, DIR_DOWN, DIR_LEFT, directions);
3740         /* block we are seeking is left */
3741         else
3742             setup_array(DIR_LEFT, DIR_UP, DIR_DOWN, DIR_RIGHT, directions);
3743     }
3744     /* the blocks are in the same vertical line */
3745     else if (x == game.highx) {
3746         /* block we are seeking is down */
3747         if (y < game.highy)
3748             setup_array(DIR_DOWN, DIR_LEFT, DIR_RIGHT, DIR_UP, directions);
3749         /* block we are seeking is up */
3750         else
3751             setup_array(DIR_UP, DIR_LEFT, DIR_RIGHT, DIR_DOWN, directions);
3752     }
3753     /* block we are seeking is to the upper left */
3754     else if (x > game.highx && y > game.highy) {
3755         if (abs(x - game.highx) < abs(y - game.highy))
3756             setup_array(DIR_LEFT, DIR_UP, DIR_RIGHT, DIR_DOWN, directions);
3757         else
3758             setup_array(DIR_UP, DIR_LEFT, DIR_DOWN, DIR_RIGHT, directions);
3759     }
3760     /* block we are seeking is to the upper right */
3761     else if (x < game.highx && y > game.highy) {
3762         if (abs(x - game.highx) < abs(y - game.highy))
3763             setup_array(DIR_RIGHT, DIR_UP, DIR_LEFT, DIR_DOWN, directions);
3764         else
3765             setup_array(DIR_UP, DIR_RIGHT, DIR_DOWN, DIR_LEFT, directions);
3766     }
3767     /* block we are seeking is to the lower right */
3768     else if (x < game.highx && y < game.highy) {
3769         if (abs(x - game.highx) < abs(y - game.highy))
3770             setup_array(DIR_RIGHT, DIR_DOWN, DIR_LEFT, DIR_UP, directions);
3771         else
3772             setup_array(DIR_DOWN, DIR_RIGHT, DIR_UP, DIR_LEFT, directions);
3773     }
3774     /* block we are seeking is to the lower left */
3775     else if (x > game.highx && y < game.highy) {
3776         if (abs(x - game.highx) < abs(y - game.highy))
3777             setup_array(DIR_LEFT, DIR_DOWN, DIR_RIGHT, DIR_UP, directions);
3778         else
3779             setup_array(DIR_DOWN, DIR_LEFT, DIR_UP, DIR_RIGHT, directions);
3780     }
3781 
3782     /* check for legal moves using recursion */
3783     for (i = 0; i < 4; i++) {
3784         switch (directions[i]) {
3785             case 1:
3786                 /* we are going up, we can't go down*/
3787                 if (cur_dir != DIR_DOWN && y != 0)
3788                     /* we can't change directions once we reach the maximum turns */
3789                     if (game.turns != game.max_turns || (game.turns == game.max_turns && cur_dir == DIR_UP)) {
3790                         cnt = game.path_count;
3791                         path = legal_move(x, y - 1, DIR_UP, cur_dir, path);
3792                         if (cnt != game.path_count || game.first_block) {
3793                             path = add_path_to_list(x, y - 1, DIR_DOWN, path);
3794                             return path;
3795                         }
3796                     }
3797                 break;
3798             case 2:
3799                 /* we are going right, we can't go left */
3800                 if (cur_dir != DIR_LEFT && x != 11)
3801                     /* we can't change directions once we reach the maximum turns */
3802                     if (game.turns != game.max_turns || (game.turns == game.max_turns && cur_dir == DIR_RIGHT)) {
3803                         cnt = game.path_count;
3804                         path = legal_move(x + 1, y, DIR_RIGHT, cur_dir, path);
3805                         if (cnt != game.path_count || game.first_block) {
3806                             path = add_path_to_list(x + 1, y, DIR_LEFT, path);
3807                             return path;
3808                         }
3809                     }
3810                 break;
3811             case 3:
3812                 /* we are going down, we can't go up */
3813                 if (cur_dir != DIR_UP && y != 11)
3814                     /* we can't change directions once we reach the maximum turns */
3815                     if (game.turns != game.max_turns || (game.turns == game.max_turns && cur_dir == DIR_DOWN)) {
3816                         cnt = game.path_count;
3817                         path = legal_move(x, y + 1, DIR_DOWN, cur_dir, path);
3818                         if (cnt != game.path_count || game.first_block) {
3819                             path = add_path_to_list(x, y + 1, DIR_UP, path);
3820                             return path;
3821                         }
3822                     }
3823                 break;
3824             case 4:
3825                 /* we are going left, we can't go right */
3826                 if (cur_dir != DIR_RIGHT && x != 0)
3827                     /* we can't change directions once we reach the maximum turns */
3828                     if (game.turns != game.max_turns || (game.turns == game.max_turns && cur_dir == DIR_LEFT)) {
3829                         cnt = game.path_count;
3830                         path = legal_move(x - 1, y, DIR_LEFT, cur_dir, path);
3831                         if (cnt != game.path_count || game.first_block) {
3832                             path = add_path_to_list(x - 1, y, DIR_RIGHT, path);
3833                             return path;
3834                         }
3835                     }
3836                 break;
3837         }
3838     }
3839     if (cur_dir != prev_dir && prev_dir != DIR_NONE)
3840         game.turns--;
3841     return path;
3842 
3843 }
3844 
load_image(char * filename,int * loaded)3845 SDL_Surface *load_image(char *filename, int *loaded) {
3846 
3847     SDL_Surface *temp, *surface;
3848 
3849     temp = IMG_Load(filename);
3850     if (temp == NULL) {
3851         fprintf(stderr, "Unable to load %s\n", filename);
3852         fprintf(stderr, "%s\n", SDL_GetError());
3853         surface = NULL;
3854         *loaded = 0;
3855     }
3856     else {
3857         surface = SDL_DisplayFormat(temp);
3858         SDL_FreeSurface(temp);
3859         if (surface == NULL) {
3860             fprintf(stderr, "Unable to load %s\n", filename);
3861             fprintf(stderr, "%s\n", SDL_GetError());
3862             *loaded = 0;
3863         }
3864     }
3865     return surface;
3866 
3867 }
3868 
3869 #ifndef NOAUDIO
load_wav(char * filename,int * loaded)3870 Mix_Chunk *load_wav(char *filename, int *loaded) {
3871 
3872     Mix_Chunk *sound;
3873 
3874     sound = Mix_LoadWAV(filename);
3875     if (sound == NULL) {
3876         fprintf(stderr, "Unable to load %s\n", filename);
3877         fprintf(stderr, "%s\n", Mix_GetError());
3878         sound = NULL;
3879         *loaded = 0;
3880     }
3881     return sound;
3882 
3883 }
3884 #endif
3885 
pause_game(char * message)3886 void pause_game(char *message) {
3887 
3888     game.temp_time = SDL_GetTicks();
3889     game.paused = 1;
3890     /* hide all the blocks and update the screen */
3891     fill(GRIDSTARTX, GRIDSTARTY, 320, 320, 0, 0, 0);
3892     print_word(message, -800, -600, SERIFW24);
3893     if (game.unpause_msg)
3894         print_word("Unpause", 5 + word_width("New Game  ", SERIFG24), 5, SERIFG24);
3895     SDL_UpdateRect(game.screen, 0, 0, 0, 0);
3896     #ifndef NOAUDIO
3897     if (game.audio_enabled)
3898         if (game.music_enabled)
3899             Mix_PauseMusic();
3900     #endif
3901 
3902 }
3903 
print_char(int character,int destx,int desty,int font)3904 void print_char(int character, int destx, int desty, int font) {
3905 
3906     blit(game.font[font].name, game.font[font].pos[character - 32], 1, game.font[font].width[character - 32], game.font[font].height, destx, desty);
3907 
3908 }
3909 
num_to_str(int num,int max_length)3910 void num_to_str(int num, int max_length) {
3911 
3912     int i;
3913     game.temp_string = (char *)malloc(sizeof(char) * (max_length + 1));
3914 
3915     if (pow(10, max_length) <= num)
3916         strcpy(game.temp_string,"-");
3917     i = sprintf(game.temp_string, "%d", num);
3918 
3919 }
3920 
print_number(int destx,int desty,int font,int number,int len)3921 void print_number(int destx, int desty, int font, int number, int len) {
3922 
3923     num_to_str(number, len);
3924     print_word(game.temp_string, destx, desty, font);
3925     free (game.temp_string);
3926 
3927 }
3928 
print_time(int destx,int desty,int font,int number)3929 void print_time(int destx, int desty, int font, int number) {
3930 
3931     int i, pos, chr;
3932 
3933     pos = 0;
3934     num_to_str(number, 6);
3935     for (i = 0; i < strlen(game.temp_string); i++) {
3936         if (i == 2 && strlen(game.temp_string) == 5) {
3937             print_char('.', destx + pos, desty, font);
3938             pos += game.font[font].width['.' - 32];
3939         }
3940         else if (i == 3 && strlen(game.temp_string) == 6) {
3941             print_char('.', destx + pos, desty, font);
3942             pos += game.font[font].width['.' - 32];
3943         }
3944         print_char(game.temp_string[i], destx + pos, desty, font);
3945         chr = game.temp_string[i];
3946         pos += game.font[font].width[chr - 32];
3947     }
3948     free (game.temp_string);
3949 
3950 }
3951 
print_word(char * word,int destx,int desty,int font)3952 void print_word(char *word, int destx, int desty, int font) {
3953 
3954     int i, pos, chr, newdestx, newdesty;
3955 
3956     pos = 0;
3957     /* if destx or desty is negative, center word according to the co-ordinates */
3958     if (destx < 0)
3959         newdestx = (abs(destx) - word_width(word, font)) / 2;
3960     else
3961         newdestx = destx;
3962     if (desty < 0)
3963         newdesty = (abs(desty) - game.font[font].height) / 2;
3964     else
3965         newdesty = desty;
3966     for (i = 0; i < strlen(word); i++) {
3967         print_char(word[i], newdestx + pos, newdesty, font);
3968         chr = word[i];
3969         pos += game.font[font].width[chr - 32];
3970     }
3971 
3972 }
3973 
rnd(float max)3974 int rnd(float max) {
3975 
3976     /* returns a random number between 1 and max */
3977     return 1 + (int) (max * rand() / (RAND_MAX + 1.0));
3978 
3979 }
3980 
setup_array(int dir1,int dir2,int dir3,int dir4,int directions[])3981 void setup_array(int dir1, int dir2, int dir3, int dir4, int directions[]) {
3982 
3983     directions[0] = dir1;
3984     directions[1] = dir2;
3985     directions[2] = dir3;
3986     directions[3] = dir4;
3987 
3988 }
3989 
show_path(struct path_info * path)3990 int show_path(struct path_info *path) {
3991 
3992     int i;
3993 
3994     for (i = 0; i < game.path_count; i++) {
3995         if (SDL_GetTicks() > path[i].blit_time && path[i].x != -1) {
3996             if (path[i].direction != LINE_NONE) {
3997                 if (!path[i].blit_done) {
3998                     blit(game.surface[PATH_BLOCKS], path[i].direction * 32, 0, 32, 32, (path[i].x - 1) * 32 + GRIDSTARTX, (path[i].y - 1) * 32 + GRIDSTARTY);
3999                     path[i].blit_done = 1;
4000                 }
4001             }
4002             else
4003                 return path[i].block_num;
4004         }
4005     }
4006     return 51;
4007 
4008 }
4009 
unhighlight_block(int x,int y)4010 void unhighlight_block(int x, int y) {
4011 
4012     int destx, desty, color_l_and_r;
4013 
4014     destx = (game.highx - 1) * 32 + GRIDSTARTX;
4015     desty = (game.highy - 1) * 32 + GRIDSTARTY;
4016     game.grid[game.highx][game.highy].status = BLOCK_NORM;
4017     blit(game.surface[BLOCKS], game.grid[game.highx][game.highy].color, 0, 32, 32, destx, desty);
4018     if (game.skill_level == INSANE) {
4019         color_l_and_r = game.color_l_and_r[game.grid[game.highx][game.highy].color / 32] - 48;
4020         blit(game.surface[game.grid[game.highx][game.highy].left_or_right * 2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, destx, desty);
4021     }
4022     change_corner_style(destx, desty);
4023     if ((x != game.highx) || (y != game.highy)) {
4024         game.current_combos_removed = game.current_sets_removed = 0;
4025         game.no_wrong_moves = 0;
4026         game.last_color_removed = -1;
4027         #ifndef NOAUDIO
4028         if (game.audio_enabled) Mix_PlayChannel(WRONG_MOVE, game.sound[WRONG_MOVE], 0);
4029         #endif
4030     }
4031     game.highx = game.highy = game.turns = 0;
4032 
4033 }
4034 
put_block_back_on_screen(int x,int y)4035 void put_block_back_on_screen(int x, int y) {
4036 
4037     int destx, desty, color_l_and_r;
4038 
4039     destx = (x - 1) * 32 + GRIDSTARTX;
4040     desty = (y - 1) * 32 + GRIDSTARTY;
4041     game.grid[x][y].status = BLOCK_NORM;
4042     blit(game.surface[BLOCKS], game.grid[x][y].color, 0, 32, 32, destx, desty);
4043     if (game.skill_level == INSANE) {
4044         color_l_and_r = game.color_l_and_r[game.grid[x][y].color / 32] - 48;
4045         blit(game.surface[game.grid[x][y].left_or_right * 2 + BLACK_L + color_l_and_r], 0, 0, 32, 32, destx, desty);
4046     }
4047     change_corner_style(destx, desty);
4048 
4049 }
4050 
unpause_game()4051 void unpause_game() {
4052 
4053     game.paused_time = game.paused_time + SDL_GetTicks() - game.temp_time;
4054     game.paused = 0;
4055     draw_blocks();
4056     fill(5 + word_width("New Game  ", SERIFG24), 5, word_width("Unpause", SERIFG24), game.font[SERIFG24].height, 0, 0, 0);
4057     print_word("Pause", 5 + word_width("New Game  ", SERIFG24), 5, SERIFG24);
4058     SDL_UpdateRect(game.screen, 0, 0, 0, 0);
4059     #ifndef NOAUDIO
4060     if (game.audio_enabled)
4061         if (game.music_enabled)
4062             Mix_ResumeMusic();
4063     #endif
4064 
4065 }
4066 
change_corner_style(int x,int y)4067 void change_corner_style(int x, int y) {
4068 
4069     if (game.corner_style == ROUNDED) {
4070         /* upper left corner */
4071         fill(x + 1, y + 1, 2, 1, 0, 0, 0);
4072         fill(x + 1, y + 2, 1, 1, 0, 0, 0);
4073         /* upper right corner */
4074         fill(x + 29, y + 1, 2, 1, 0, 0, 0);
4075         fill(x + 30, y + 2, 1, 1, 0, 0, 0);
4076         /* lower left corner */
4077         fill(x + 1, y + 29, 1, 1, 0, 0, 0);
4078         fill(x + 1, y + 30, 2, 1, 0, 0, 0);
4079         /* lower right corner */
4080         fill(x + 30, y + 29, 1, 1, 0, 0, 0);
4081         fill(x + 29, y + 30, 2, 1, 0, 0, 0);
4082     }
4083 
4084 }
4085 
fade_screen(int fade_type,int time,Uint32 color)4086 void fade_screen(int fade_type, int time, Uint32 color) {
4087 
4088     SDL_Surface *screen_copy, *black_surface;
4089 	Uint32 cur_tick, started_fading;
4090 
4091 	/* Copy the screen */
4092 	screen_copy = SDL_DisplayFormatAlpha(game.screen);
4093 	/* Make the color surface */
4094 	black_surface = SDL_CreateRGBSurface(game.screen->flags, game.screen->w, game.screen->h, game.screen->format->BitsPerPixel,	game.screen->format->Rmask,	game.screen->format->Gmask,	game.screen->format->Bmask,	game.screen->format->Amask);
4095 	if (fade_type == FADE_IN)
4096 		SDL_SetAlpha(black_surface, SDL_SRCALPHA | SDL_RLEACCEL, 255);
4097 	else
4098 		SDL_SetAlpha(black_surface, SDL_SRCALPHA | SDL_RLEACCEL, 0);
4099 	SDL_FillRect(black_surface, NULL, color);
4100 	/* Now fade in / out */
4101 	for (started_fading = cur_tick = SDL_GetTicks(); cur_tick-started_fading <= (Uint32)time; cur_tick = SDL_GetTicks()) {
4102 		SDL_BlitSurface(screen_copy, NULL, game.screen, NULL);
4103 		SDL_BlitSurface(black_surface, NULL, game.screen, NULL);
4104 		SDL_UpdateRect(game.screen, 0, 0, 0, 0);
4105 		if (fade_type == FADE_IN)
4106 			SDL_SetAlpha(black_surface,	SDL_SRCALPHA | SDL_RLEACCEL, 255 - ((cur_tick - started_fading) * 255) / time);
4107 		else
4108 			SDL_SetAlpha(black_surface,	SDL_SRCALPHA | SDL_RLEACCEL, ((cur_tick - started_fading) * 255) / time);
4109 		SDL_Delay(10);
4110         SDL_PumpEvents();
4111 	}
4112 	/* Set it to the desired final state */
4113 	if (fade_type == FADE_IN)
4114 		SDL_SetAlpha(black_surface, SDL_SRCALPHA | SDL_RLEACCEL, 0);
4115 	else
4116 		SDL_SetAlpha(black_surface, SDL_SRCALPHA | SDL_RLEACCEL, 255);
4117 	SDL_BlitSurface(screen_copy, NULL, game.screen, NULL);
4118 	SDL_BlitSurface(black_surface, NULL, game.screen, NULL);
4119 	SDL_UpdateRect(game.screen, 0, 0, 0, 0);
4120 	SDL_FreeSurface(screen_copy);
4121 	SDL_FreeSurface(black_surface);
4122 
4123 }
4124