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.
18 #include <glib/gi18n.h>
23 /* universal settings */
24 static boolean gd_no_invisible_outbox = FALSE;
27 void gd_game_free(GdGame *game)
32 if (game->element_buffer)
33 gd_cave_map_free(game->element_buffer);
34 if (game->last_element_buffer)
35 gd_cave_map_free(game->last_element_buffer);
37 gd_cave_map_free(game->dir_buffer);
39 gd_cave_map_free(game->gfx_buffer);
41 game->player_lives = 0;
44 gd_cave_free(game->cave);
49 /* add bonus life. if sound enabled, play sound, too. */
50 static void add_bonus_life(GdGame *game, boolean inform_user)
54 gd_sound_play_bonus_life();
55 game->bonus_life_flash = 100;
58 /* really increment number of lifes? only in a real game, nowhere else. */
59 if (game->player_lives &&
60 game->player_lives < gd_caveset_data->maximum_lives)
62 /* only add a life, if lives is > 0.
63 lives == 0 is a test run or a snapshot, no bonus life then. */
64 /* also, obey max number of bonus lives. */
69 /* increment score of player.
70 flash screen if bonus life
72 static void increment_score(GdGame *game, int increment)
76 i = game->player_score / gd_caveset_data->bonus_life_score;
77 game->player_score += increment;
78 game->cave_score += increment;
80 /* if score crossed bonus_life_score point boundary, player won a bonus life */
81 if (game->player_score / gd_caveset_data->bonus_life_score > i)
82 add_bonus_life(game, TRUE);
85 /* do the things associated with loading a new cave. function creates gfx buffer and the like. */
86 static void load_cave(GdGame *game)
90 /* delete element buffer */
91 if (game->element_buffer)
92 gd_cave_map_free(game->element_buffer);
93 game->element_buffer = NULL;
95 /* delete last element buffer */
96 if (game->last_element_buffer)
97 gd_cave_map_free(game->last_element_buffer);
98 game->last_element_buffer = NULL;
100 /* delete direction buffer */
101 if (game->dir_buffer)
102 gd_cave_map_free(game->dir_buffer);
103 game->dir_buffer = NULL;
105 /* delete gfx buffer */
106 if (game->gfx_buffer)
107 gd_cave_map_free(game->gfx_buffer);
108 game->gfx_buffer = NULL;
111 game->cave_score = 0;
113 /* delete previous cave */
114 gd_cave_free(game->cave);
116 if (native_bd_level.loaded_from_caveset)
117 game->original_cave = gd_get_original_cave_from_caveset(game->cave_num);
119 game->original_cave = native_bd_level.cave;
121 game->cave = gd_get_prepared_cave(game->original_cave, game->level_num);
123 if (game->cave->intermission && game->cave->intermission_instantlife)
124 add_bonus_life(game, FALSE);
126 game->milliseconds_anim = 0;
127 game->milliseconds_game = 0; /* set game timer to zero, too */
129 /* create new element buffer */
130 game->element_buffer = gd_cave_map_new(game->cave, int);
132 for (y = 0; y < game->cave->h; y++)
133 for (x = 0; x < game->cave->w; x++)
134 game->element_buffer[y][x] = O_NONE;
136 /* create new last element buffer */
137 game->last_element_buffer = gd_cave_map_new(game->cave, int);
139 for (y = 0; y < game->cave->h; y++)
140 for (x = 0; x < game->cave->w; x++)
141 game->last_element_buffer[y][x] = O_NONE;
143 /* create new direction buffer */
144 game->dir_buffer = gd_cave_map_new(game->cave, int);
146 for (y = 0; y < game->cave->h; y++)
147 for (x = 0; x < game->cave->w; x++)
148 game->dir_buffer[y][x] = GD_MV_STILL;
150 /* create new gfx buffer */
151 game->gfx_buffer = gd_cave_map_new(game->cave, int);
153 for (y = 0; y < game->cave->h; y++)
154 for (x = 0; x < game->cave->w; x++)
155 game->gfx_buffer[y][x] = -1; /* fill with "invalid" */
158 GdCave *gd_create_snapshot(GdGame *game)
161 g_return_val_if_fail (game->cave != NULL, NULL);
163 /* make an exact copy */
164 snapshot = gd_cave_new_from_cave(game->cave);
169 /* this starts a new game */
170 GdGame *gd_game_new(const int cave, const int level)
174 game = checked_calloc(sizeof(GdGame));
176 game->cave_num = cave;
177 game->level_num = level;
179 game->player_lives = gd_caveset_data->initial_lives;
180 game->player_score = 0;
182 game->player_move = GD_MV_STILL;
183 game->player_move_stick = FALSE;
184 game->player_fire = FALSE;
186 game->state_counter = GAME_INT_LOAD_CAVE;
188 game->show_story = TRUE;
193 static void iterate_cave(GdGame *game, GdDirection player_move, boolean fire)
195 boolean suicide = FALSE;
197 /* ANYTHING EXCEPT A TIMEOUT, WE ITERATE THE CAVE */
198 if (game->cave->player_state != GD_PL_TIMEOUT)
200 if (TapeIsPlaying_ReplayBD())
202 byte *action_rnd = TapePlayAction_BD();
204 if (action_rnd != NULL)
206 int action_bd = map_action_RND_to_BD(action_rnd[0]);
208 player_move = (action_bd & GD_REPLAY_MOVE_MASK);
209 fire = (action_bd & GD_REPLAY_FIRE_MASK) != 0;
214 gd_cave_iterate(game->cave, player_move, fire, suicide);
216 if (game->cave->score)
217 increment_score(game, game->cave->score);
219 gd_sound_play_cave(game->cave);
222 if (game->cave->player_state == GD_PL_EXITED)
224 if (game->cave->intermission &&
225 game->cave->intermission_rewardlife &&
226 game->player_lives != 0)
228 /* one life extra for completing intermission */
229 add_bonus_life(game, FALSE);
232 /* start adding points for remaining time */
233 game->state_counter = GAME_INT_CHECK_BONUS_TIME;
234 gd_cave_clear_sounds(game->cave);
236 /* play cave finished sound */
237 gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1);
238 gd_sound_play_cave(game->cave);
241 if (game->cave->player_state == GD_PL_DIED ||
242 game->cave->player_state == GD_PL_TIMEOUT)
244 game_bd.game_over = TRUE;
245 game_bd.cover_screen = TRUE;
249 static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean fast_forward)
251 int millisecs_elapsed = 20;
252 boolean frame; /* set to true, if this will be an animation frame */
253 GdGameState return_state;
257 counter_next = GAME_INT_INVALID;
258 return_state = GD_GAME_INVALID_STATE;
259 game->milliseconds_anim += millisecs_elapsed; /* keep track of time */
260 frame = FALSE; /* set to true, if this will be an animation frame */
262 if (game->milliseconds_anim >= 40)
265 game->milliseconds_anim -= 40;
268 /* cannot be less than uncover start. */
269 if (game->state_counter < GAME_INT_LOAD_CAVE)
273 else if (game->state_counter == GAME_INT_LOAD_CAVE)
275 /* do the things associated with loading a new cave. function creates gfx buffer and the like. */
278 return_state = GD_GAME_NOTHING;
279 counter_next = GAME_INT_SHOW_STORY;
281 else if (game->state_counter == GAME_INT_SHOW_STORY)
283 /* for normal game, every cave can have a long string of description/story. show that. */
285 /* if we have a story... */
287 if (game->show_story && game->original_cave && game->original_cave->story != NULL)
288 Info("Cave Story: %s", game->original_cave->story);
291 counter_next = GAME_INT_START_UNCOVER;
292 return_state = GD_GAME_NOTHING;
294 else if (game->state_counter == GAME_INT_START_UNCOVER)
296 /* the very beginning. */
298 /* cover all cells of cave */
299 for (y = 0; y < game->cave->h; y++)
300 for (x = 0; x < game->cave->w; x++)
301 game->cave->map[y][x] |= COVERED;
303 counter_next = game->state_counter + 1;
305 /* very important: tell the caller that we loaded a new cave. */
306 /* size of the cave might be new, colors might be new, and so on. */
307 return_state = GD_GAME_CAVE_LOADED;
309 else if (game->state_counter < GAME_INT_UNCOVER_ALL)
311 /* uncover animation */
313 /* to play cover sound */
314 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
315 gd_sound_play_cave(game->cave);
317 counter_next = game->state_counter;
323 /* original game uncovered one cell per line each frame.
324 * we have different cave sizes, so uncover width * height / 40 random
325 * cells each frame. (original was width = 40).
326 * this way the uncovering is the same speed also for intermissions. */
327 for (j = 0; j < game->cave->w * game->cave->h / 40; j++)
329 y = g_random_int_range(0, game->cave->h);
330 x = g_random_int_range(0, game->cave->w);
332 game->cave->map[y][x] &= ~COVERED;
335 counter_next++; /* as we did something, advance the counter. */
338 return_state = GD_GAME_NOTHING;
340 else if (game->state_counter == GAME_INT_UNCOVER_ALL)
342 /* time to uncover the whole cave. */
343 for (y = 0; y < game->cave->h; y++)
344 for (x = 0; x < game->cave->w; x++)
345 game->cave->map[y][x] &= ~COVERED;
347 /* to stop uncover sound. */
348 gd_cave_clear_sounds(game->cave);
349 gd_sound_play_cave(game->cave);
351 counter_next = GAME_INT_CAVE_RUNNING;
352 return_state = GD_GAME_NOTHING;
354 else if (game->state_counter == GAME_INT_CAVE_RUNNING)
360 cavespeed = game->cave->speed; /* cave speed in ms, like 175ms/frame */
362 cavespeed = 40; /* if fast forward, ignore cave speed, and go as 25 iterations/sec */
364 /* ITERATION - cave is running. */
366 /* normally nothing happes. but if we iterate, this might change. */
367 return_state = GD_GAME_NOTHING;
369 /* if allowing cave movements, add elapsed time to timer. and then we can check what to do. */
371 game->milliseconds_game += millisecs_elapsed;
373 /* increment cycle (frame) counter for the current cave iteration */
376 if (game->milliseconds_game >= cavespeed)
380 game->milliseconds_game -= cavespeed;
381 pl = game->cave->player_state;
383 /* initialize buffers for last cave element and direction for next iteration */
384 for (y = 0; y < game->cave->h; y++)
386 for (x = 0; x < game->cave->w; x++)
388 game->last_element_buffer[y][x] = game->element_buffer[y][x];
389 game->dir_buffer[y][x] = GD_MV_STILL;
393 /* store last maximum number of cycles (to force redraw if changed) */
394 game->itermax_last = game->itermax;
396 /* update maximum number of cycles (frame) per cave iteration */
397 game->itermax = game->itercycle;
399 /* reset cycle (frame) counter for the next cave iteration */
402 iterate_cave(game, game->player_move, game->player_fire);
404 if (game->player_move == GD_MV_STILL)
406 game->player_move_stick = FALSE;
410 game->player_move_stick = TRUE;
411 game->player_move = GD_MV_STILL;
414 /* as we iterated, the score and the like could have been changed. */
415 return_state = GD_GAME_LABELS_CHANGED;
417 /* and if the cave timeouted at this moment, that is a special case. */
418 if (pl != GD_PL_TIMEOUT && game->cave->player_state == GD_PL_TIMEOUT)
419 return_state = GD_GAME_TIMEOUT_NOW;
422 /* do not change counter state, as it is set by iterate_cave() */
423 counter_next = game->state_counter;
425 else if (game->state_counter == GAME_INT_CHECK_BONUS_TIME)
427 /* before covering, we check for time bonus score */
430 /* if time remaining, bonus points are added. do not start animation yet. */
431 if (game->cave->time > 0)
433 /* subtract number of "milliseconds" - nothing to do with gameplay->millisecs! */
434 game->cave->time -= game->cave->timing_factor;
436 /* higher levels get more bonus points per second remained */
437 increment_score(game, game->cave->timevalue);
439 /* if much time (> 60s) remained, fast counter :) */
440 if (game->cave->time > 60 * game->cave->timing_factor)
442 /* decrement by nine each frame, so it also looks like a fast counter. 9 is 8 + 1! */
443 game->cave->time -= 8 * game->cave->timing_factor;
444 increment_score(game, game->cave->timevalue * 8);
447 /* just to be neat */
448 if (game->cave->time < 0)
449 game->cave->time = 0;
451 counter_next = game->state_counter; /* do not change yet */
455 game_bd.level_solved = TRUE;
456 game_bd.cover_screen = TRUE;
458 /* if no more points, start waiting a bit, and later start covering. */
459 counter_next = GAME_INT_WAIT_BEFORE_COVER;
462 if (game->cave->time / game->cave->timing_factor > 8)
463 gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1); /* play cave finished sound */
465 /* play bonus sound */
466 gd_cave_set_seconds_sound(game->cave);
467 gd_sound_play_cave(game->cave);
469 return_state = GD_GAME_LABELS_CHANGED;
473 return_state = GD_GAME_NOTHING;
475 /* do not change counter state, as it is set by iterate_cave() */
476 counter_next = game->state_counter;
479 else if (game->state_counter == GAME_INT_WAIT_BEFORE_COVER)
481 /* after adding bonus points, we wait some time before starting to cover.
482 this is the FIRST frame... so we check for game over and maybe jump there */
483 /* if no more lives, game is over. */
484 counter_next = game->state_counter;
486 if (game->player_lives == 0)
487 return_state = GD_GAME_NO_MORE_LIVES;
489 return_state = GD_GAME_NOTHING;
491 else if (game->state_counter > GAME_INT_WAIT_BEFORE_COVER &&
492 game->state_counter < GAME_INT_COVER_START)
494 /* after adding bonus points, we wait some time before starting to cover.
495 ... and the other frames. */
496 counter_next = game->state_counter;
498 counter_next++; /* 40 ms elapsed, advance counter */
500 return_state = GD_GAME_NOTHING;
502 else if (game->state_counter == GAME_INT_COVER_START)
504 /* starting to cover. start cover sound. */
506 gd_cave_clear_sounds(game->cave);
507 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
509 /* to play cover sound */
510 gd_sound_play_cave(game->cave);
512 counter_next = game->state_counter + 1;
513 return_state = GD_GAME_NOTHING;
515 else if (game->state_counter > GAME_INT_COVER_START &&
516 game->state_counter < GAME_INT_COVER_ALL)
519 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
521 counter_next = game->state_counter;
527 counter_next++; /* 40 ms elapsed, doing cover: advance counter */
529 /* covering eight times faster than uncovering. */
530 for (j = 0; j < game->cave->w * game->cave->h * 8 / 40; j++)
531 game->cave->map[g_random_int_range(0, game->cave->h)][g_random_int_range (0, game->cave->w)] |= COVERED;
534 return_state = GD_GAME_NOTHING;
536 else if (game->state_counter == GAME_INT_COVER_ALL)
539 for (y = 0; y < game->cave->h; y++)
540 for (x = 0; x < game->cave->w; x++)
541 game->cave->map[y][x] |= COVERED;
543 counter_next = game->state_counter + 1;
544 return_state = GD_GAME_NOTHING;
546 /* to stop uncover sound. */
547 gd_cave_clear_sounds(game->cave);
548 gd_sound_play_cave(game->cave);
554 if (game->player_lives != 0)
555 return_state = GD_GAME_NOTHING; /* and go to next level */
557 return_state = GD_GAME_GAME_OVER;
563 if (game->bonus_life_flash) /* bonus life - frames */
564 game->bonus_life_flash--;
566 game->animcycle = (game->animcycle + 1) % 8;
569 /* always render the cave to the gfx buffer;
570 however it may do nothing if animcycle was not changed. */
571 if (game->element_buffer && game->gfx_buffer)
572 gd_drawcave_game(game->cave, game->element_buffer, game->gfx_buffer,
573 game->bonus_life_flash != 0, game->animcycle, gd_no_invisible_outbox);
575 game->state_counter = counter_next;
580 void play_game_func(GdGame *game, int action)
583 boolean move_up = ((action & JOY_UP) != 0);
584 boolean move_down = ((action & JOY_DOWN) != 0);
585 boolean move_left = ((action & JOY_LEFT) != 0);
586 boolean move_right = ((action & JOY_RIGHT) != 0);
587 boolean fire = ((action & (JOY_BUTTON_1 | JOY_BUTTON_2)) != 0);
589 if (game->player_move_stick || move_up || move_down || move_left || move_right) // no "fire"!
592 game->player_move = gd_direction_from_keypress(move_up, move_down, move_left, move_right);
594 /* when storing last action, only update fire action with direction */
595 /* (prevents clearing direction if snapping stopped before action is performed) */
596 game->player_fire = fire;
599 /* if no direction was stored before, allow setting fire to current state */
600 if (game->player_move == GD_MV_STILL)
601 game->player_fire = fire;
603 /* tell the interrupt "20ms has passed" */
604 state = gd_game_main_int(game, !game->out_of_window, gd_keystate[SDL_SCANCODE_F]);
606 /* state of game, returned by gd_game_main_int */
609 case GD_GAME_CAVE_LOADED:
610 /* select colors, prepare drawing etc. */
611 gd_scroll_to_origin();
613 /* fill whole screen with black - cave might be smaller than previous! */
614 FillRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE, BLACK_PIXEL);
621 /* for the sdl version, it seems nicer if we first scroll, and then draw. */
622 /* scrolling for the sdl version will merely invalidate the whole gfx buffer. */
623 /* if drawcave was before scrolling, it would draw, scroll would invalidate,
624 and then it should be drawn again */
625 /* only do the drawing if the cave already exists. */
626 if (game->cave && game->element_buffer && game->gfx_buffer)
628 /* if fine scrolling, scroll at 50hz. if not, only scroll at every second call, so 25hz. */
629 /* do the scrolling. scroll exactly, if player is not yet alive */
630 game->out_of_window = gd_scroll(game, game->cave->player_state == GD_PL_NOT_YET, FALSE);