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