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 void gd_game_free(GdGame *game)
25 if (game->element_buffer)
26 gd_cave_map_free(game->element_buffer);
27 if (game->last_element_buffer)
28 gd_cave_map_free(game->last_element_buffer);
29 if (game->dir_buffer_from)
30 gd_cave_map_free(game->dir_buffer_from);
31 if (game->dir_buffer_to)
32 gd_cave_map_free(game->dir_buffer_to);
34 gd_cave_map_free(game->gfx_buffer);
36 game->player_lives = 0;
39 gd_cave_free(game->cave);
44 // add bonus life. if sound enabled, play sound, too.
45 static void add_bonus_life(GdGame *game, boolean inform_user)
49 gd_sound_play_bonus_life();
50 game->bonus_life_flash = 100;
53 // really increment number of lifes? only in a real game, nowhere else.
54 if (game->player_lives &&
55 game->player_lives < gd_caveset_data->maximum_lives)
57 // only add a life, if lives is > 0.
58 // lives == 0 is a test run or a snapshot, no bonus life then.
59 // also, obey max number of bonus lives.
64 // increment score of player.
65 // flash screen if bonus life
66 static void increment_score(GdGame *game, int increment)
70 i = game->player_score / gd_caveset_data->bonus_life_score;
71 game->player_score += increment;
72 game->cave_score += increment;
74 // if score crossed bonus_life_score point boundary, player won a bonus life
75 if (game->player_score / gd_caveset_data->bonus_life_score > i)
76 add_bonus_life(game, TRUE);
79 // do the things associated with loading a new cave. function creates gfx buffer and the like.
80 static void load_cave(GdGame *game)
84 // delete element buffer
85 if (game->element_buffer)
86 gd_cave_map_free(game->element_buffer);
87 game->element_buffer = NULL;
89 // delete last element buffer
90 if (game->last_element_buffer)
91 gd_cave_map_free(game->last_element_buffer);
92 game->last_element_buffer = NULL;
94 // delete direction buffer (from)
95 if (game->dir_buffer_from)
96 gd_cave_map_free(game->dir_buffer_from);
97 game->dir_buffer_from = NULL;
99 // delete direction buffer (to)
100 if (game->dir_buffer_to)
101 gd_cave_map_free(game->dir_buffer_to);
102 game->dir_buffer_to = NULL;
105 if (game->gfx_buffer)
106 gd_cave_map_free(game->gfx_buffer);
107 game->gfx_buffer = NULL;
110 game->cave_score = 0;
112 // delete previous cave
113 gd_cave_free(game->cave);
115 if (native_bd_level.loaded_from_caveset)
116 game->original_cave = gd_get_original_cave_from_caveset(game->cave_num);
118 game->original_cave = native_bd_level.cave;
120 game->cave = gd_get_prepared_cave(game->original_cave, game->level_num);
122 // if requested, recolor cave (cave is a copy only, so no worries)
123 if (setup.bd_random_colors)
124 gd_cave_set_random_colors(game->cave, setup.bd_default_color_type);
126 if (game->cave->intermission && game->cave->intermission_instantlife)
127 add_bonus_life(game, FALSE);
129 game->milliseconds_anim = 0;
130 game->milliseconds_game = 0; // set game timer to zero, too
132 game->cycle_counter = 0;
134 // create new element buffer
135 game->element_buffer = gd_cave_map_new(game->cave, int);
137 for (y = 0; y < game->cave->h; y++)
138 for (x = 0; x < game->cave->w; x++)
139 game->element_buffer[y][x] = O_NONE;
141 // create new last element buffer
142 game->last_element_buffer = gd_cave_map_new(game->cave, int);
144 for (y = 0; y < game->cave->h; y++)
145 for (x = 0; x < game->cave->w; x++)
146 game->last_element_buffer[y][x] = O_NONE;
148 // create new direction buffer (from)
149 game->dir_buffer_from = gd_cave_map_new(game->cave, int);
151 for (y = 0; y < game->cave->h; y++)
152 for (x = 0; x < game->cave->w; x++)
153 game->dir_buffer_from[y][x] = GD_MV_STILL;
155 // create new direction buffer (to)
156 game->dir_buffer_to = gd_cave_map_new(game->cave, int);
158 for (y = 0; y < game->cave->h; y++)
159 for (x = 0; x < game->cave->w; x++)
160 game->dir_buffer_to[y][x] = GD_MV_STILL;
162 // create new gfx buffer
163 game->gfx_buffer = gd_cave_map_new(game->cave, int);
165 for (y = 0; y < game->cave->h; y++)
166 for (x = 0; x < game->cave->w; x++)
167 game->gfx_buffer[y][x] = -1; // fill with "invalid"
170 GdCave *gd_create_snapshot(GdGame *game)
174 if (game->cave == NULL)
177 // make an exact copy
178 snapshot = gd_cave_new_from_cave(game->cave);
183 // this starts a new game
184 GdGame *gd_game_new(const int cave, const int level)
188 game = checked_calloc(sizeof(GdGame));
190 game->cave_num = cave;
191 game->level_num = level;
193 game->player_lives = gd_caveset_data->initial_lives;
194 game->player_score = 0;
196 game->player_move = GD_MV_STILL;
197 game->player_move_stick = FALSE;
198 game->player_fire = FALSE;
200 game->state_counter = GAME_INT_LOAD_CAVE;
202 game->show_story = TRUE;
207 boolean check_iteration_reached(GdGame *game)
209 int millisecs_elapsed = 20;
211 return (game->milliseconds_game + millisecs_elapsed >= game->cave->speed);
214 static void iterate_cave(GdGame *game, GdDirection player_move, boolean fire)
216 boolean suicide = FALSE;
218 // ANYTHING EXCEPT A TIMEOUT, WE ITERATE THE CAVE
219 if (game->cave->player_state != GD_PL_TIMEOUT)
221 if (TapeIsPlaying_ReplayBD())
223 byte *action_rnd = TapePlayAction_BD();
225 if (action_rnd != NULL)
227 int action_bd = map_action_RND_to_BD(action_rnd[0]);
229 player_move = (action_bd & GD_REPLAY_MOVE_MASK);
230 fire = (action_bd & GD_REPLAY_FIRE_MASK) != 0;
235 gd_cave_iterate(game->cave, player_move, fire, suicide);
237 if (game->cave->score)
238 increment_score(game, game->cave->score);
240 gd_sound_play_cave(game->cave);
243 if (game->cave->player_state == GD_PL_EXITED)
245 if (game->cave->intermission &&
246 game->cave->intermission_rewardlife &&
247 game->player_lives != 0)
249 // one life extra for completing intermission
250 add_bonus_life(game, FALSE);
253 // start adding points for remaining time
254 game->state_counter = GAME_INT_CHECK_BONUS_TIME;
255 gd_cave_clear_sounds(game->cave);
257 // play cave finished sound
258 gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1);
259 gd_sound_play_cave(game->cave);
262 if (game->cave->player_state == GD_PL_DIED ||
263 game->cave->player_state == GD_PL_TIMEOUT)
265 game_bd.game_over = TRUE;
266 game_bd.cover_screen = TRUE;
270 static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean fast_forward)
272 int millisecs_elapsed = 20;
273 boolean frame; // set to true, if this will be an animation frame
274 GdGameState return_state;
278 counter_next = GAME_INT_INVALID;
279 return_state = GD_GAME_INVALID_STATE;
280 game->milliseconds_anim += millisecs_elapsed; // keep track of time
281 frame = FALSE; // set to true, if this will be an animation frame
283 if (game->milliseconds_anim >= 40)
286 game->milliseconds_anim -= 40;
289 // cannot be less than uncover start.
290 if (game->state_counter < GAME_INT_LOAD_CAVE)
294 else if (game->state_counter == GAME_INT_LOAD_CAVE)
296 // do the things associated with loading a new cave. function creates gfx buffer and the like.
299 return_state = GD_GAME_NOTHING;
300 counter_next = GAME_INT_SHOW_STORY;
302 else if (game->state_counter == GAME_INT_SHOW_STORY)
304 // for normal game, every cave can have a long string of description/story. show that.
306 // if we have a story...
308 if (game->show_story && game->original_cave && game->original_cave->story != NULL)
309 Info("Cave Story: %s", game->original_cave->story);
312 counter_next = GAME_INT_START_UNCOVER;
313 return_state = GD_GAME_NOTHING;
315 else if (game->state_counter == GAME_INT_START_UNCOVER)
317 // the very beginning.
319 // cover all cells of cave
320 for (y = 0; y < game->cave->h; y++)
321 for (x = 0; x < game->cave->w; x++)
322 game->cave->map[y][x] |= COVERED;
324 counter_next = game->state_counter + 1;
326 // very important: tell the caller that we loaded a new cave.
327 // size of the cave might be new, colors might be new, and so on.
328 return_state = GD_GAME_CAVE_LOADED;
330 else if (game->state_counter < GAME_INT_UNCOVER_ALL)
334 // to play cover sound
335 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
336 gd_sound_play_cave(game->cave);
338 counter_next = game->state_counter;
344 // original game uncovered one cell per line each frame.
345 // we have different cave sizes, so uncover width * height / 40 random
346 // cells each frame. (original was width = 40).
347 // this way the uncovering is the same speed also for intermissions.
348 for (j = 0; j < game->cave->w * game->cave->h / 40; j++)
350 y = gd_random_int_range(0, game->cave->h);
351 x = gd_random_int_range(0, game->cave->w);
353 game->cave->map[y][x] &= ~COVERED;
356 counter_next++; // as we did something, advance the counter.
359 return_state = GD_GAME_NOTHING;
361 else if (game->state_counter == GAME_INT_UNCOVER_ALL)
363 // time to uncover the whole cave.
364 for (y = 0; y < game->cave->h; y++)
365 for (x = 0; x < game->cave->w; x++)
366 game->cave->map[y][x] &= ~COVERED;
368 // to stop uncover sound.
369 gd_cave_clear_sounds(game->cave);
370 gd_sound_play_cave(game->cave);
372 counter_next = GAME_INT_CAVE_RUNNING;
373 return_state = GD_GAME_NOTHING;
375 else if (game->state_counter == GAME_INT_CAVE_RUNNING)
381 cavespeed = game->cave->speed; // cave speed in ms, like 175ms/frame
383 cavespeed = 40; // if fast forward, ignore cave speed, and go as 25 iterations/sec
385 // ITERATION - cave is running.
387 // normally nothing happes. but if we iterate, this might change.
388 return_state = GD_GAME_NOTHING;
390 // if allowing cave movements, add elapsed time to timer. and then we can check what to do.
392 game->milliseconds_game += millisecs_elapsed;
394 // increment cycle (frame) counter for the current cave iteration
397 if (game->milliseconds_game >= cavespeed)
401 game->milliseconds_game -= cavespeed;
402 pl = game->cave->player_state;
404 // initialize buffers for last cave element and direction for next iteration
405 for (y = 0; y < game->cave->h; y++)
407 for (x = 0; x < game->cave->w; x++)
409 game->last_element_buffer[y][x] = game->element_buffer[y][x];
410 game->dir_buffer_from[y][x] = GD_MV_STILL;
411 game->dir_buffer_to[y][x] = GD_MV_STILL;
415 // store last maximum number of cycle frames (to force redraw if changed)
416 game->itermax_last = game->itermax;
418 // store maximum number of cycle frames separately for even and odd cycles
419 game->itermax2[game->cycle_counter % 2] = game->itercycle;
421 // update maximum number of cycle frames per cave iteration
422 game->itermax = game->itermax2[!(game->cycle_counter % 2)];
424 // reset cycle frame counter for the next cave iteration
427 iterate_cave(game, game->player_move, game->player_fire);
429 game->cycle_counter++;
431 if (game->player_move == GD_MV_STILL)
433 game->player_move_stick = FALSE;
437 game->player_move_stick = TRUE;
438 game->player_move = GD_MV_STILL;
441 // as we iterated, the score and the like could have been changed.
442 return_state = GD_GAME_LABELS_CHANGED;
444 // and if the cave timeouted at this moment, that is a special case.
445 if (pl != GD_PL_TIMEOUT && game->cave->player_state == GD_PL_TIMEOUT)
446 return_state = GD_GAME_TIMEOUT_NOW;
449 // do not change counter state, as it is set by iterate_cave()
450 counter_next = game->state_counter;
452 else if (game->state_counter == GAME_INT_CHECK_BONUS_TIME)
454 // before covering, we check for time bonus score
457 // if time remaining, bonus points are added. do not start animation yet.
458 if (game->cave->time > 0)
460 // subtract number of "milliseconds" - nothing to do with gameplay->millisecs!
461 game->cave->time -= game->cave->timing_factor;
463 // higher levels get more bonus points per second remained
464 increment_score(game, game->cave->timevalue);
466 // if much time (> 60s) remained, fast counter :)
467 if (game->cave->time > 60 * game->cave->timing_factor)
469 // decrement by nine each frame, so it also looks like a fast counter. 9 is 8 + 1!
470 game->cave->time -= 8 * game->cave->timing_factor;
471 increment_score(game, game->cave->timevalue * 8);
475 if (game->cave->time < 0)
476 game->cave->time = 0;
478 counter_next = game->state_counter; // do not change yet
482 game_bd.level_solved = TRUE;
483 game_bd.cover_screen = TRUE;
485 // if no more points, start waiting a bit, and later start covering.
486 counter_next = GAME_INT_WAIT_BEFORE_COVER;
489 if (game->cave->time / game->cave->timing_factor > 8)
490 gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1); // play cave finished sound
493 gd_cave_set_seconds_sound(game->cave);
494 gd_sound_play_cave(game->cave);
496 return_state = GD_GAME_LABELS_CHANGED;
500 return_state = GD_GAME_NOTHING;
502 // do not change counter state, as it is set by iterate_cave()
503 counter_next = game->state_counter;
506 else if (game->state_counter == GAME_INT_WAIT_BEFORE_COVER)
508 // after adding bonus points, we wait some time before starting to cover.
509 // this is the FIRST frame... so we check for game over and maybe jump there
510 // if no more lives, game is over.
511 counter_next = game->state_counter;
513 if (game->player_lives == 0)
514 return_state = GD_GAME_NO_MORE_LIVES;
516 return_state = GD_GAME_NOTHING;
518 else if (game->state_counter > GAME_INT_WAIT_BEFORE_COVER &&
519 game->state_counter < GAME_INT_COVER_START)
521 // after adding bonus points, we wait some time before starting to cover.
522 // ... and the other frames.
523 counter_next = game->state_counter;
525 counter_next++; // 40 ms elapsed, advance counter
527 return_state = GD_GAME_NOTHING;
529 else if (game->state_counter == GAME_INT_COVER_START)
531 // starting to cover. start cover sound.
533 gd_cave_clear_sounds(game->cave);
534 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
536 // to play cover sound
537 gd_sound_play_cave(game->cave);
539 counter_next = game->state_counter + 1;
540 return_state = GD_GAME_NOTHING;
542 else if (game->state_counter > GAME_INT_COVER_START &&
543 game->state_counter < GAME_INT_COVER_ALL)
546 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
548 counter_next = game->state_counter;
554 counter_next++; // 40 ms elapsed, doing cover: advance counter
556 // covering eight times faster than uncovering.
557 for (j = 0; j < game->cave->w * game->cave->h * 8 / 40; j++)
558 game->cave->map[gd_random_int_range(0, game->cave->h)][gd_random_int_range (0, game->cave->w)] |= COVERED;
561 return_state = GD_GAME_NOTHING;
563 else if (game->state_counter == GAME_INT_COVER_ALL)
566 for (y = 0; y < game->cave->h; y++)
567 for (x = 0; x < game->cave->w; x++)
568 game->cave->map[y][x] |= COVERED;
570 counter_next = game->state_counter + 1;
571 return_state = GD_GAME_NOTHING;
573 // to stop uncover sound.
574 gd_cave_clear_sounds(game->cave);
575 gd_sound_play_cave(game->cave);
581 if (game->player_lives != 0)
582 return_state = GD_GAME_NOTHING; // and go to next level
584 return_state = GD_GAME_GAME_OVER;
590 if (game->bonus_life_flash) // bonus life - frames
591 game->bonus_life_flash--;
593 game->animcycle = (game->animcycle + 1) % 8;
596 // always render the cave to the gfx buffer;
597 // however it may do nothing if animcycle was not changed.
598 if (game->element_buffer && game->gfx_buffer)
599 gd_drawcave_game(game->cave, game->element_buffer, game->last_element_buffer, game->gfx_buffer,
600 game->bonus_life_flash != 0, game->animcycle, setup.bd_show_invisible_outbox);
602 game->state_counter = counter_next;
607 void play_game_func(GdGame *game, int action)
610 boolean move_up = ((action & JOY_UP) != 0);
611 boolean move_down = ((action & JOY_DOWN) != 0);
612 boolean move_left = ((action & JOY_LEFT) != 0);
613 boolean move_right = ((action & JOY_RIGHT) != 0);
614 boolean fire = ((action & (JOY_BUTTON_1 | JOY_BUTTON_2)) != 0);
616 if (game->player_move_stick || move_up || move_down || move_left || move_right) // no "fire"!
619 game->player_move = gd_direction_from_keypress(move_up, move_down, move_left, move_right);
621 // when storing last action, only update fire action with direction
622 // (prevents clearing direction if snapping stopped before action is performed)
623 game->player_fire = fire;
626 // if no direction was stored before, allow setting fire to current state
627 if (game->player_move == GD_MV_STILL)
628 game->player_fire = fire;
630 // tell the interrupt "20ms has passed"
631 state = gd_game_main_int(game, !game->out_of_window, FALSE);
633 // state of game, returned by gd_game_main_int
636 case GD_GAME_CAVE_LOADED:
637 // scroll to start position
638 gd_scroll_to_origin();
640 // fill whole screen with black - cave might be smaller than previous!
641 FillRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE, BLACK_PIXEL);
648 // for the sdl version, it seems nicer if we first scroll, and then draw.
649 // scrolling for the sdl version will merely invalidate the whole gfx buffer.
650 // if drawcave was before scrolling, it would draw, scroll would invalidate,
651 // and then it should be drawn again
652 // only do the drawing if the cave already exists.
653 if (game->cave && game->element_buffer && game->gfx_buffer)
655 // if fine scrolling, scroll at 50hz. if not, only scroll at every second call, so 25hz.
656 // do the scrolling. scroll exactly, if player is not yet alive
657 game->out_of_window = gd_scroll(game, game->cave->player_state == GD_PL_NOT_YET, FALSE);