2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 /* universal settings */
21 static boolean gd_no_invisible_outbox = FALSE;
24 void gd_game_free(GdGame *game)
29 if (game->element_buffer)
30 gd_cave_map_free(game->element_buffer);
31 if (game->last_element_buffer)
32 gd_cave_map_free(game->last_element_buffer);
34 gd_cave_map_free(game->dir_buffer);
36 gd_cave_map_free(game->gfx_buffer);
38 game->player_lives = 0;
41 gd_cave_free(game->cave);
46 /* add bonus life. if sound enabled, play sound, too. */
47 static void add_bonus_life(GdGame *game, boolean inform_user)
51 gd_sound_play_bonus_life();
52 game->bonus_life_flash = 100;
55 /* really increment number of lifes? only in a real game, nowhere else. */
56 if (game->player_lives &&
57 game->player_lives < gd_caveset_data->maximum_lives)
59 /* only add a life, if lives is > 0.
60 lives == 0 is a test run or a snapshot, no bonus life then. */
61 /* also, obey max number of bonus lives. */
66 /* increment score of player.
67 flash screen if bonus life
69 static void increment_score(GdGame *game, int increment)
73 i = game->player_score / gd_caveset_data->bonus_life_score;
74 game->player_score += increment;
75 game->cave_score += increment;
77 /* if score crossed bonus_life_score point boundary, player won a bonus life */
78 if (game->player_score / gd_caveset_data->bonus_life_score > i)
79 add_bonus_life(game, TRUE);
82 /* do the things associated with loading a new cave. function creates gfx buffer and the like. */
83 static void load_cave(GdGame *game)
87 /* delete element buffer */
88 if (game->element_buffer)
89 gd_cave_map_free(game->element_buffer);
90 game->element_buffer = NULL;
92 /* delete last element buffer */
93 if (game->last_element_buffer)
94 gd_cave_map_free(game->last_element_buffer);
95 game->last_element_buffer = NULL;
97 /* delete direction buffer */
99 gd_cave_map_free(game->dir_buffer);
100 game->dir_buffer = NULL;
102 /* delete gfx buffer */
103 if (game->gfx_buffer)
104 gd_cave_map_free(game->gfx_buffer);
105 game->gfx_buffer = NULL;
108 game->cave_score = 0;
110 /* delete previous cave */
111 gd_cave_free(game->cave);
113 if (native_bd_level.loaded_from_caveset)
114 game->original_cave = gd_get_original_cave_from_caveset(game->cave_num);
116 game->original_cave = native_bd_level.cave;
118 game->cave = gd_get_prepared_cave(game->original_cave, game->level_num);
120 if (game->cave->intermission && game->cave->intermission_instantlife)
121 add_bonus_life(game, FALSE);
123 game->milliseconds_anim = 0;
124 game->milliseconds_game = 0; /* set game timer to zero, too */
126 /* create new element buffer */
127 game->element_buffer = gd_cave_map_new(game->cave, int);
129 for (y = 0; y < game->cave->h; y++)
130 for (x = 0; x < game->cave->w; x++)
131 game->element_buffer[y][x] = O_NONE;
133 /* create new last element buffer */
134 game->last_element_buffer = gd_cave_map_new(game->cave, int);
136 for (y = 0; y < game->cave->h; y++)
137 for (x = 0; x < game->cave->w; x++)
138 game->last_element_buffer[y][x] = O_NONE;
140 /* create new direction buffer */
141 game->dir_buffer = gd_cave_map_new(game->cave, int);
143 for (y = 0; y < game->cave->h; y++)
144 for (x = 0; x < game->cave->w; x++)
145 game->dir_buffer[y][x] = GD_MV_STILL;
147 /* create new gfx buffer */
148 game->gfx_buffer = gd_cave_map_new(game->cave, int);
150 for (y = 0; y < game->cave->h; y++)
151 for (x = 0; x < game->cave->w; x++)
152 game->gfx_buffer[y][x] = -1; /* fill with "invalid" */
155 GdCave *gd_create_snapshot(GdGame *game)
159 if (game->cave == NULL)
162 /* make an exact copy */
163 snapshot = gd_cave_new_from_cave(game->cave);
168 /* this starts a new game */
169 GdGame *gd_game_new(const int cave, const int level)
173 game = checked_calloc(sizeof(GdGame));
175 game->cave_num = cave;
176 game->level_num = level;
178 game->player_lives = gd_caveset_data->initial_lives;
179 game->player_score = 0;
181 game->player_move = GD_MV_STILL;
182 game->player_move_stick = FALSE;
183 game->player_fire = FALSE;
185 game->state_counter = GAME_INT_LOAD_CAVE;
187 game->show_story = TRUE;
192 static void iterate_cave(GdGame *game, GdDirection player_move, boolean fire)
194 boolean suicide = FALSE;
196 /* ANYTHING EXCEPT A TIMEOUT, WE ITERATE THE CAVE */
197 if (game->cave->player_state != GD_PL_TIMEOUT)
199 if (TapeIsPlaying_ReplayBD())
201 byte *action_rnd = TapePlayAction_BD();
203 if (action_rnd != NULL)
205 int action_bd = map_action_RND_to_BD(action_rnd[0]);
207 player_move = (action_bd & GD_REPLAY_MOVE_MASK);
208 fire = (action_bd & GD_REPLAY_FIRE_MASK) != 0;
213 gd_cave_iterate(game->cave, player_move, fire, suicide);
215 if (game->cave->score)
216 increment_score(game, game->cave->score);
218 gd_sound_play_cave(game->cave);
221 if (game->cave->player_state == GD_PL_EXITED)
223 if (game->cave->intermission &&
224 game->cave->intermission_rewardlife &&
225 game->player_lives != 0)
227 /* one life extra for completing intermission */
228 add_bonus_life(game, FALSE);
231 /* start adding points for remaining time */
232 game->state_counter = GAME_INT_CHECK_BONUS_TIME;
233 gd_cave_clear_sounds(game->cave);
235 /* play cave finished sound */
236 gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1);
237 gd_sound_play_cave(game->cave);
240 if (game->cave->player_state == GD_PL_DIED ||
241 game->cave->player_state == GD_PL_TIMEOUT)
243 game_bd.game_over = TRUE;
244 game_bd.cover_screen = TRUE;
248 static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean fast_forward)
250 int millisecs_elapsed = 20;
251 boolean frame; /* set to true, if this will be an animation frame */
252 GdGameState return_state;
256 counter_next = GAME_INT_INVALID;
257 return_state = GD_GAME_INVALID_STATE;
258 game->milliseconds_anim += millisecs_elapsed; /* keep track of time */
259 frame = FALSE; /* set to true, if this will be an animation frame */
261 if (game->milliseconds_anim >= 40)
264 game->milliseconds_anim -= 40;
267 /* cannot be less than uncover start. */
268 if (game->state_counter < GAME_INT_LOAD_CAVE)
272 else if (game->state_counter == GAME_INT_LOAD_CAVE)
274 /* do the things associated with loading a new cave. function creates gfx buffer and the like. */
277 return_state = GD_GAME_NOTHING;
278 counter_next = GAME_INT_SHOW_STORY;
280 else if (game->state_counter == GAME_INT_SHOW_STORY)
282 /* for normal game, every cave can have a long string of description/story. show that. */
284 /* if we have a story... */
286 if (game->show_story && game->original_cave && game->original_cave->story != NULL)
287 Info("Cave Story: %s", game->original_cave->story);
290 counter_next = GAME_INT_START_UNCOVER;
291 return_state = GD_GAME_NOTHING;
293 else if (game->state_counter == GAME_INT_START_UNCOVER)
295 /* the very beginning. */
297 /* cover all cells of cave */
298 for (y = 0; y < game->cave->h; y++)
299 for (x = 0; x < game->cave->w; x++)
300 game->cave->map[y][x] |= COVERED;
302 counter_next = game->state_counter + 1;
304 /* very important: tell the caller that we loaded a new cave. */
305 /* size of the cave might be new, colors might be new, and so on. */
306 return_state = GD_GAME_CAVE_LOADED;
308 else if (game->state_counter < GAME_INT_UNCOVER_ALL)
310 /* uncover animation */
312 /* to play cover sound */
313 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
314 gd_sound_play_cave(game->cave);
316 counter_next = game->state_counter;
322 /* original game uncovered one cell per line each frame.
323 * we have different cave sizes, so uncover width * height / 40 random
324 * cells each frame. (original was width = 40).
325 * this way the uncovering is the same speed also for intermissions. */
326 for (j = 0; j < game->cave->w * game->cave->h / 40; j++)
328 y = gd_random_int_range(0, game->cave->h);
329 x = gd_random_int_range(0, game->cave->w);
331 game->cave->map[y][x] &= ~COVERED;
334 counter_next++; /* as we did something, advance the counter. */
337 return_state = GD_GAME_NOTHING;
339 else if (game->state_counter == GAME_INT_UNCOVER_ALL)
341 /* time to uncover the whole cave. */
342 for (y = 0; y < game->cave->h; y++)
343 for (x = 0; x < game->cave->w; x++)
344 game->cave->map[y][x] &= ~COVERED;
346 /* to stop uncover sound. */
347 gd_cave_clear_sounds(game->cave);
348 gd_sound_play_cave(game->cave);
350 counter_next = GAME_INT_CAVE_RUNNING;
351 return_state = GD_GAME_NOTHING;
353 else if (game->state_counter == GAME_INT_CAVE_RUNNING)
359 cavespeed = game->cave->speed; /* cave speed in ms, like 175ms/frame */
361 cavespeed = 40; /* if fast forward, ignore cave speed, and go as 25 iterations/sec */
363 /* ITERATION - cave is running. */
365 /* normally nothing happes. but if we iterate, this might change. */
366 return_state = GD_GAME_NOTHING;
368 /* if allowing cave movements, add elapsed time to timer. and then we can check what to do. */
370 game->milliseconds_game += millisecs_elapsed;
372 /* increment cycle (frame) counter for the current cave iteration */
375 if (game->milliseconds_game >= cavespeed)
379 game->milliseconds_game -= cavespeed;
380 pl = game->cave->player_state;
382 /* initialize buffers for last cave element and direction for next iteration */
383 for (y = 0; y < game->cave->h; y++)
385 for (x = 0; x < game->cave->w; x++)
387 game->last_element_buffer[y][x] = game->element_buffer[y][x];
388 game->dir_buffer[y][x] = GD_MV_STILL;
392 /* store last maximum number of cycles (to force redraw if changed) */
393 game->itermax_last = game->itermax;
395 /* update maximum number of cycles (frame) per cave iteration */
396 game->itermax = game->itercycle;
398 /* reset cycle (frame) counter for the next cave iteration */
401 iterate_cave(game, game->player_move, game->player_fire);
403 if (game->player_move == GD_MV_STILL)
405 game->player_move_stick = FALSE;
409 game->player_move_stick = TRUE;
410 game->player_move = GD_MV_STILL;
413 /* as we iterated, the score and the like could have been changed. */
414 return_state = GD_GAME_LABELS_CHANGED;
416 /* and if the cave timeouted at this moment, that is a special case. */
417 if (pl != GD_PL_TIMEOUT && game->cave->player_state == GD_PL_TIMEOUT)
418 return_state = GD_GAME_TIMEOUT_NOW;
421 /* do not change counter state, as it is set by iterate_cave() */
422 counter_next = game->state_counter;
424 else if (game->state_counter == GAME_INT_CHECK_BONUS_TIME)
426 /* before covering, we check for time bonus score */
429 /* if time remaining, bonus points are added. do not start animation yet. */
430 if (game->cave->time > 0)
432 /* subtract number of "milliseconds" - nothing to do with gameplay->millisecs! */
433 game->cave->time -= game->cave->timing_factor;
435 /* higher levels get more bonus points per second remained */
436 increment_score(game, game->cave->timevalue);
438 /* if much time (> 60s) remained, fast counter :) */
439 if (game->cave->time > 60 * game->cave->timing_factor)
441 /* decrement by nine each frame, so it also looks like a fast counter. 9 is 8 + 1! */
442 game->cave->time -= 8 * game->cave->timing_factor;
443 increment_score(game, game->cave->timevalue * 8);
446 /* just to be neat */
447 if (game->cave->time < 0)
448 game->cave->time = 0;
450 counter_next = game->state_counter; /* do not change yet */
454 game_bd.level_solved = TRUE;
455 game_bd.cover_screen = TRUE;
457 /* if no more points, start waiting a bit, and later start covering. */
458 counter_next = GAME_INT_WAIT_BEFORE_COVER;
461 if (game->cave->time / game->cave->timing_factor > 8)
462 gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1); /* play cave finished sound */
464 /* play bonus sound */
465 gd_cave_set_seconds_sound(game->cave);
466 gd_sound_play_cave(game->cave);
468 return_state = GD_GAME_LABELS_CHANGED;
472 return_state = GD_GAME_NOTHING;
474 /* do not change counter state, as it is set by iterate_cave() */
475 counter_next = game->state_counter;
478 else if (game->state_counter == GAME_INT_WAIT_BEFORE_COVER)
480 /* after adding bonus points, we wait some time before starting to cover.
481 this is the FIRST frame... so we check for game over and maybe jump there */
482 /* if no more lives, game is over. */
483 counter_next = game->state_counter;
485 if (game->player_lives == 0)
486 return_state = GD_GAME_NO_MORE_LIVES;
488 return_state = GD_GAME_NOTHING;
490 else if (game->state_counter > GAME_INT_WAIT_BEFORE_COVER &&
491 game->state_counter < GAME_INT_COVER_START)
493 /* after adding bonus points, we wait some time before starting to cover.
494 ... and the other frames. */
495 counter_next = game->state_counter;
497 counter_next++; /* 40 ms elapsed, advance counter */
499 return_state = GD_GAME_NOTHING;
501 else if (game->state_counter == GAME_INT_COVER_START)
503 /* starting to cover. start cover sound. */
505 gd_cave_clear_sounds(game->cave);
506 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
508 /* to play cover sound */
509 gd_sound_play_cave(game->cave);
511 counter_next = game->state_counter + 1;
512 return_state = GD_GAME_NOTHING;
514 else if (game->state_counter > GAME_INT_COVER_START &&
515 game->state_counter < GAME_INT_COVER_ALL)
518 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
520 counter_next = game->state_counter;
526 counter_next++; /* 40 ms elapsed, doing cover: advance counter */
528 /* covering eight times faster than uncovering. */
529 for (j = 0; j < game->cave->w * game->cave->h * 8 / 40; j++)
530 game->cave->map[gd_random_int_range(0, game->cave->h)][gd_random_int_range (0, game->cave->w)] |= COVERED;
533 return_state = GD_GAME_NOTHING;
535 else if (game->state_counter == GAME_INT_COVER_ALL)
538 for (y = 0; y < game->cave->h; y++)
539 for (x = 0; x < game->cave->w; x++)
540 game->cave->map[y][x] |= COVERED;
542 counter_next = game->state_counter + 1;
543 return_state = GD_GAME_NOTHING;
545 /* to stop uncover sound. */
546 gd_cave_clear_sounds(game->cave);
547 gd_sound_play_cave(game->cave);
553 if (game->player_lives != 0)
554 return_state = GD_GAME_NOTHING; /* and go to next level */
556 return_state = GD_GAME_GAME_OVER;
562 if (game->bonus_life_flash) /* bonus life - frames */
563 game->bonus_life_flash--;
565 game->animcycle = (game->animcycle + 1) % 8;
568 /* always render the cave to the gfx buffer;
569 however it may do nothing if animcycle was not changed. */
570 if (game->element_buffer && game->gfx_buffer)
571 gd_drawcave_game(game->cave, game->element_buffer, game->gfx_buffer,
572 game->bonus_life_flash != 0, game->animcycle, gd_no_invisible_outbox);
574 game->state_counter = counter_next;
579 void play_game_func(GdGame *game, int action)
582 boolean move_up = ((action & JOY_UP) != 0);
583 boolean move_down = ((action & JOY_DOWN) != 0);
584 boolean move_left = ((action & JOY_LEFT) != 0);
585 boolean move_right = ((action & JOY_RIGHT) != 0);
586 boolean fire = ((action & (JOY_BUTTON_1 | JOY_BUTTON_2)) != 0);
588 if (game->player_move_stick || move_up || move_down || move_left || move_right) // no "fire"!
591 game->player_move = gd_direction_from_keypress(move_up, move_down, move_left, move_right);
593 /* when storing last action, only update fire action with direction */
594 /* (prevents clearing direction if snapping stopped before action is performed) */
595 game->player_fire = fire;
598 /* if no direction was stored before, allow setting fire to current state */
599 if (game->player_move == GD_MV_STILL)
600 game->player_fire = fire;
602 /* tell the interrupt "20ms has passed" */
603 state = gd_game_main_int(game, !game->out_of_window, gd_keystate[SDL_SCANCODE_F]);
605 /* state of game, returned by gd_game_main_int */
608 case GD_GAME_CAVE_LOADED:
609 /* select colors, prepare drawing etc. */
610 gd_scroll_to_origin();
612 /* fill whole screen with black - cave might be smaller than previous! */
613 FillRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE, BLACK_PIXEL);
620 /* for the sdl version, it seems nicer if we first scroll, and then draw. */
621 /* scrolling for the sdl version will merely invalidate the whole gfx buffer. */
622 /* if drawcave was before scrolling, it would draw, scroll would invalidate,
623 and then it should be drawn again */
624 /* only do the drawing if the cave already exists. */
625 if (game->cave && game->element_buffer && game->gfx_buffer)
627 /* if fine scrolling, scroll at 50hz. if not, only scroll at every second call, so 25hz. */
628 /* do the scrolling. scroll exactly, if player is not yet alive */
629 game->out_of_window = gd_scroll(game, game->cave->player_state == GD_PL_NOT_YET, FALSE);