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);
35 gd_cave_map_free(game->gfx_buffer);
37 game->player_lives = 0;
40 gd_cave_free(game->cave);
42 /* if we recorded some replays during this run, we check them.
43 we remove those which are too short */
44 if (game->replays_recorded)
49 for (citer = gd_caveset; citer != NULL; citer = citer->next)
51 GdCave *cave = (GdCave *)citer->data;
54 /* check replays of all caves */
55 for (riter = cave->replays; riter != NULL; )
57 GdReplay *replay = (GdReplay *)riter->data;
59 /* remember next iter, as we may delete the current */
60 GList *nextrep = riter->next;
62 /* if we recorded this replay now, and it is too short, we delete it */
63 /* but do not delete successful ones! */
64 if (g_list_find(game->replays_recorded, replay) &&
65 (replay->movements->len < 16) &&
68 /* delete from list */
69 cave->replays = g_list_delete_link(cave->replays, riter);
71 /* also free replay */
72 gd_replay_free(replay);
79 /* free the list of newly recorded replays, as we checked them */
80 g_list_free(game->replays_recorded);
81 game->replays_recorded = NULL;
87 /* add bonus life. if sound enabled, play sound, too. */
88 static void add_bonus_life(GdGame *game, boolean inform_user)
90 /* only inform about bonus life when playing a game */
91 /* or when testing the cave (so the user can see that a bonus life can be earned in that cave */
92 if (game->type == GD_GAMETYPE_NORMAL ||
93 game->type == GD_GAMETYPE_TEST)
97 gd_sound_play_bonus_life();
98 game->bonus_life_flash = 100;
102 /* really increment number of lifes? only in a real game, nowhere else. */
103 if (game->player_lives &&
104 game->player_lives < gd_caveset_data->maximum_lives)
106 /* only add a life, if lives is > 0.
107 lives == 0 is a test run or a snapshot, no bonus life then. */
108 /* also, obey max number of bonus lives. */
109 game->player_lives++;
113 /* increment score of player.
114 flash screen if bonus life
116 static void increment_score(GdGame *game, int increment)
120 i = game->player_score / gd_caveset_data->bonus_life_score;
121 game->player_score += increment;
122 game->cave_score += increment;
124 /* also record to replay */
125 if (game->replay_record)
126 game->replay_record->score += increment;
128 /* if score crossed bonus_life_score point boundary, player won a bonus life */
129 if (game->player_score / gd_caveset_data->bonus_life_score > i)
130 add_bonus_life(game, TRUE);
133 /* do the things associated with loading a new cave. function creates gfx buffer and the like. */
134 static void load_cave(GdGame *game)
138 /* delete element buffer */
139 if (game->element_buffer)
140 gd_cave_map_free(game->element_buffer);
141 game->element_buffer = NULL;
143 /* delete gfx buffer */
144 if (game->gfx_buffer)
145 gd_cave_map_free(game->gfx_buffer);
146 game->gfx_buffer = NULL;
149 game->cave_score = 0;
151 /* delete previous cave */
152 gd_cave_free(game->cave);
154 if (native_bd_level.loaded_from_caveset)
155 game->original_cave = gd_get_original_cave_from_caveset(game->cave_num);
157 game->original_cave = native_bd_level.cave;
159 game->cave = gd_get_prepared_cave(game->original_cave, game->level_num);
161 if (game->cave->intermission && game->cave->intermission_instantlife)
162 add_bonus_life(game, FALSE);
164 game->milliseconds_anim = 0;
165 game->milliseconds_game = 0; /* set game timer to zero, too */
167 /* create new element buffer */
168 game->element_buffer = gd_cave_map_new(game->cave, int);
170 for (y = 0; y < game->cave->h; y++)
171 for (x = 0; x < game->cave->w; x++)
172 game->element_buffer[y][x] = O_NONE;
174 /* create new gfx buffer */
175 game->gfx_buffer = gd_cave_map_new(game->cave, int);
177 for (y = 0; y < game->cave->h; y++)
178 for (x = 0; x < game->cave->w; x++)
179 game->gfx_buffer[y][x] = -1; /* fill with "invalid" */
182 GdCave *gd_create_snapshot(GdGame *game)
185 g_return_val_if_fail (game->cave != NULL, NULL);
187 /* make an exact copy */
188 snapshot = gd_cave_new_from_cave(game->cave);
193 /* this starts a new game */
194 GdGame *gd_game_new(const int cave, const int level)
198 game = checked_calloc(sizeof(GdGame));
200 game->cave_num = cave;
201 game->level_num = level;
203 game->player_lives = gd_caveset_data->initial_lives;
204 game->player_score = 0;
206 game->player_move = GD_MV_STILL;
207 game->player_move_stick = FALSE;
208 game->player_fire = FALSE;
210 game->type = GD_GAMETYPE_NORMAL;
211 game->state_counter = GAME_INT_LOAD_CAVE;
213 game->show_story = TRUE;
218 /* starts a new snapshot playing */
219 GdGame *gd_game_new_replay(GdCave *cave, GdReplay *replay)
223 game = checked_calloc(sizeof(GdGame));
225 gd_strcpy(game->player_name, "");
227 game->player_lives = 0;
228 game->player_score = 0;
230 game->player_move = GD_MV_STILL;
231 game->player_move_stick = FALSE;
232 game->player_fire = FALSE;
234 game->original_cave = cave;
235 game->replay_from = replay;
237 game->type = GD_GAMETYPE_REPLAY;
238 game->state_counter = GAME_INT_LOAD_CAVE;
243 static void iterate_cave(GdGame *game, GdDirection player_move, boolean fire)
245 boolean suicide = FALSE;
247 /* if we are playing a replay, but the user intervents, continue as a snapshot. */
248 /* do not trigger this for fire, as it would not be too intuitive. */
249 if (game->type == GD_GAMETYPE_REPLAY)
251 if (player_move != GD_MV_STILL)
253 game->type = GD_GAMETYPE_CONTINUE_REPLAY;
254 game->replay_from = NULL;
258 /* ANYTHING EXCEPT A TIMEOUT, WE ITERATE THE CAVE */
259 if (game->cave->player_state != GD_PL_TIMEOUT)
261 /* IF PLAYING FROM REPLAY, OVERWRITE KEYPRESS VARIABLES FROM REPLAY */
262 if (game->type == GD_GAMETYPE_REPLAY)
266 /* if the user does touch the keyboard, we immediately exit replay,
267 and he can continue playing */
268 result = gd_replay_get_next_movement(game->replay_from, &player_move, &fire, &suicide);
269 /* if could not get move from snapshot, continue from keyboard input. */
271 game->replay_no_more_movements++;
273 /* if no more available movements, and the user does not do anything,
274 we cover cave and stop game. */
275 if (game->replay_no_more_movements > 15)
276 game->state_counter = GAME_INT_COVER_START;
279 if (TapeIsPlaying_ReplayBD())
281 byte *action_rnd = TapePlayAction_BD();
283 if (action_rnd != NULL)
285 int action_bd = map_action_RND_to_BD(action_rnd[0]);
287 player_move = (action_bd & GD_REPLAY_MOVE_MASK);
288 fire = (action_bd & GD_REPLAY_FIRE_MASK) != 0;
293 gd_cave_iterate(game->cave, player_move, fire, suicide);
295 if (game->replay_record)
296 gd_replay_store_movement(game->replay_record, player_move, fire, suicide);
298 if (game->cave->score)
299 increment_score(game, game->cave->score);
301 gd_sound_play_cave(game->cave);
304 if (game->cave->player_state == GD_PL_EXITED)
306 if (game->cave->intermission &&
307 game->cave->intermission_rewardlife &&
308 game->player_lives != 0)
310 /* one life extra for completing intermission */
311 add_bonus_life(game, FALSE);
314 if (game->replay_record)
315 game->replay_record->success = TRUE;
317 /* start adding points for remaining time */
318 game->state_counter = GAME_INT_CHECK_BONUS_TIME;
319 gd_cave_clear_sounds(game->cave);
321 /* play cave finished sound */
322 gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1);
323 gd_sound_play_cave(game->cave);
326 if (game->cave->player_state == GD_PL_DIED ||
327 game->cave->player_state == GD_PL_TIMEOUT)
329 game_bd.game_over = TRUE;
330 game_bd.cover_screen = TRUE;
334 static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean fast_forward)
336 int millisecs_elapsed = 20;
337 boolean frame; /* set to true, if this will be an animation frame */
338 GdGameState return_state;
342 counter_next = GAME_INT_INVALID;
343 return_state = GD_GAME_INVALID_STATE;
344 game->milliseconds_anim += millisecs_elapsed; /* keep track of time */
345 frame = FALSE; /* set to true, if this will be an animation frame */
347 if (game->milliseconds_anim >= 40)
350 game->milliseconds_anim -= 40;
353 /* cannot be less than uncover start. */
354 if (game->state_counter < GAME_INT_LOAD_CAVE)
358 else if (game->state_counter == GAME_INT_LOAD_CAVE)
360 /* do the things associated with loading a new cave. function creates gfx buffer and the like. */
363 return_state = GD_GAME_NOTHING;
364 counter_next = GAME_INT_SHOW_STORY;
366 else if (game->state_counter == GAME_INT_SHOW_STORY)
368 /* for normal game, every cave can have a long string of description/story. show that. */
370 /* if we have a story... */
372 if (game->show_story && game->original_cave && game->original_cave->story->len != 0)
373 Info("Cave Story: %s", game->original_cave->story->str);
376 counter_next = GAME_INT_START_UNCOVER;
377 return_state = GD_GAME_NOTHING;
379 else if (game->state_counter == GAME_INT_START_UNCOVER)
381 /* the very beginning. */
383 /* cover all cells of cave */
384 for (y = 0; y < game->cave->h; y++)
385 for (x = 0; x < game->cave->w; x++)
386 game->cave->map[y][x] |= COVERED;
388 counter_next = game->state_counter + 1;
390 /* very important: tell the caller that we loaded a new cave. */
391 /* size of the cave might be new, colors might be new, and so on. */
392 return_state = GD_GAME_CAVE_LOADED;
394 else if (game->state_counter < GAME_INT_UNCOVER_ALL)
396 /* uncover animation */
398 /* to play cover sound */
399 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
400 gd_sound_play_cave(game->cave);
402 counter_next = game->state_counter;
408 /* original game uncovered one cell per line each frame.
409 * we have different cave sizes, so uncover width * height / 40 random
410 * cells each frame. (original was width = 40).
411 * this way the uncovering is the same speed also for intermissions. */
412 for (j = 0; j < game->cave->w * game->cave->h / 40; j++)
414 y = g_random_int_range(0, game->cave->h);
415 x = g_random_int_range(0, game->cave->w);
417 game->cave->map[y][x] &= ~COVERED;
420 counter_next++; /* as we did something, advance the counter. */
423 return_state = GD_GAME_NOTHING;
425 else if (game->state_counter == GAME_INT_UNCOVER_ALL)
427 /* time to uncover the whole cave. */
428 for (y = 0; y < game->cave->h; y++)
429 for (x = 0; x < game->cave->w; x++)
430 game->cave->map[y][x] &= ~COVERED;
432 /* to stop uncover sound. */
433 gd_cave_clear_sounds(game->cave);
434 gd_sound_play_cave(game->cave);
436 counter_next = GAME_INT_CAVE_RUNNING;
437 return_state = GD_GAME_NOTHING;
439 else if (game->state_counter == GAME_INT_CAVE_RUNNING)
445 cavespeed = game->cave->speed; /* cave speed in ms, like 175ms/frame */
447 cavespeed = 40; /* if fast forward, ignore cave speed, and go as 25 iterations/sec */
449 /* ITERATION - cave is running. */
451 /* normally nothing happes. but if we iterate, this might change. */
452 return_state = GD_GAME_NOTHING;
454 /* if allowing cave movements, add elapsed time to timer. and then we can check what to do. */
456 game->milliseconds_game += millisecs_elapsed;
458 if (game->milliseconds_game >= cavespeed)
462 game->milliseconds_game -= cavespeed;
463 pl = game->cave->player_state;
465 iterate_cave(game, game->player_move, game->player_fire);
467 if (game->player_move == GD_MV_STILL)
469 game->player_move_stick = FALSE;
473 game->player_move_stick = TRUE;
474 game->player_move = GD_MV_STILL;
477 /* as we iterated, the score and the like could have been changed. */
478 return_state = GD_GAME_LABELS_CHANGED;
480 /* and if the cave timeouted at this moment, that is a special case. */
481 if (pl != GD_PL_TIMEOUT && game->cave->player_state == GD_PL_TIMEOUT)
482 return_state = GD_GAME_TIMEOUT_NOW;
485 /* do not change counter state, as it is set by iterate_cave() */
486 counter_next = game->state_counter;
488 else if (game->state_counter == GAME_INT_CHECK_BONUS_TIME)
490 /* before covering, we check for time bonus score */
493 /* if time remaining, bonus points are added. do not start animation yet. */
494 if (game->cave->time > 0)
496 /* subtract number of "milliseconds" - nothing to do with gameplay->millisecs! */
497 game->cave->time -= game->cave->timing_factor;
499 /* higher levels get more bonus points per second remained */
500 increment_score(game, game->cave->timevalue);
502 /* if much time (> 60s) remained, fast counter :) */
503 if (game->cave->time > 60 * game->cave->timing_factor)
505 /* decrement by nine each frame, so it also looks like a fast counter. 9 is 8 + 1! */
506 game->cave->time -= 8 * game->cave->timing_factor;
507 increment_score(game, game->cave->timevalue * 8);
510 /* just to be neat */
511 if (game->cave->time < 0)
512 game->cave->time = 0;
514 counter_next = game->state_counter; /* do not change yet */
518 game_bd.level_solved = TRUE;
519 game_bd.cover_screen = TRUE;
521 /* if no more points, start waiting a bit, and later start covering. */
522 counter_next = GAME_INT_WAIT_BEFORE_COVER;
525 if (game->cave->time / game->cave->timing_factor > 8)
526 gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1); /* play cave finished sound */
528 /* play bonus sound */
529 gd_cave_set_seconds_sound(game->cave);
530 gd_sound_play_cave(game->cave);
532 return_state = GD_GAME_LABELS_CHANGED;
536 return_state = GD_GAME_NOTHING;
538 /* do not change counter state, as it is set by iterate_cave() */
539 counter_next = game->state_counter;
542 else if (game->state_counter == GAME_INT_WAIT_BEFORE_COVER)
544 /* after adding bonus points, we wait some time before starting to cover.
545 this is the FIRST frame... so we check for game over and maybe jump there */
546 /* if no more lives, game is over. */
547 counter_next = game->state_counter;
549 if (game->type == GD_GAMETYPE_NORMAL && game->player_lives == 0)
550 return_state = GD_GAME_NO_MORE_LIVES;
552 return_state = GD_GAME_NOTHING;
554 else if (game->state_counter > GAME_INT_WAIT_BEFORE_COVER &&
555 game->state_counter < GAME_INT_COVER_START)
557 /* after adding bonus points, we wait some time before starting to cover.
558 ... and the other frames. */
559 counter_next = game->state_counter;
561 counter_next++; /* 40 ms elapsed, advance counter */
563 return_state = GD_GAME_NOTHING;
565 else if (game->state_counter == GAME_INT_COVER_START)
567 /* starting to cover. start cover sound. */
569 gd_cave_clear_sounds(game->cave);
570 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
572 /* to play cover sound */
573 gd_sound_play_cave(game->cave);
575 counter_next = game->state_counter + 1;
576 return_state = GD_GAME_NOTHING;
578 else if (game->state_counter > GAME_INT_COVER_START &&
579 game->state_counter < GAME_INT_COVER_ALL)
582 gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
584 counter_next = game->state_counter;
590 counter_next++; /* 40 ms elapsed, doing cover: advance counter */
592 /* covering eight times faster than uncovering. */
593 for (j = 0; j < game->cave->w * game->cave->h * 8 / 40; j++)
594 game->cave->map[g_random_int_range(0, game->cave->h)][g_random_int_range (0, game->cave->w)] |= COVERED;
597 return_state = GD_GAME_NOTHING;
599 else if (game->state_counter == GAME_INT_COVER_ALL)
602 for (y = 0; y < game->cave->h; y++)
603 for (x = 0; x < game->cave->w; x++)
604 game->cave->map[y][x] |= COVERED;
606 counter_next = game->state_counter + 1;
607 return_state = GD_GAME_NOTHING;
609 /* to stop uncover sound. */
610 gd_cave_clear_sounds(game->cave);
611 gd_sound_play_cave(game->cave);
617 /* if this is a normal game: */
618 if (game->type == GD_GAMETYPE_NORMAL)
620 if (game->player_lives != 0)
621 return_state = GD_GAME_NOTHING; /* and go to next level */
623 return_state = GD_GAME_GAME_OVER;
627 /* for snapshots and replays and the like, this is the end. */
628 return_state = GD_GAME_STOP;
635 if (game->bonus_life_flash) /* bonus life - frames */
636 game->bonus_life_flash--;
638 game->animcycle = (game->animcycle + 1) % 8;
641 /* always render the cave to the gfx buffer;
642 however it may do nothing if animcycle was not changed. */
643 if (game->element_buffer && game->gfx_buffer)
644 gd_drawcave_game(game->cave, game->element_buffer, game->gfx_buffer,
645 game->bonus_life_flash != 0, game->animcycle, gd_no_invisible_outbox);
647 game->state_counter = counter_next;
652 void play_game_func(GdGame *game, int action)
655 boolean move_up = ((action & JOY_UP) != 0);
656 boolean move_down = ((action & JOY_DOWN) != 0);
657 boolean move_left = ((action & JOY_LEFT) != 0);
658 boolean move_right = ((action & JOY_RIGHT) != 0);
659 boolean fire = ((action & (JOY_BUTTON_1 | JOY_BUTTON_2)) != 0);
661 if (game->player_move_stick || move_up || move_down || move_left || move_right) // no "fire"!
664 game->player_move = gd_direction_from_keypress(move_up, move_down, move_left, move_right);
666 /* when storing last action, only update fire action with direction */
667 /* (prevents clearing direction if snapping stopped before action is performed) */
668 game->player_fire = fire;
671 /* if no direction was stored before, allow setting fire to current state */
672 if (game->player_move == GD_MV_STILL)
673 game->player_fire = fire;
675 /* tell the interrupt "20ms has passed" */
676 state = gd_game_main_int(game, !game->out_of_window, gd_keystate[SDL_SCANCODE_F]);
678 /* state of game, returned by gd_game_main_int */
681 case GD_GAME_CAVE_LOADED:
682 /* select colors, prepare drawing etc. */
683 gd_scroll_to_origin();
685 /* fill whole screen with black - cave might be smaller than previous! */
686 FillRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE, BLACK_PIXEL);
693 /* for the sdl version, it seems nicer if we first scroll, and then draw. */
694 /* scrolling for the sdl version will merely invalidate the whole gfx buffer. */
695 /* if drawcave was before scrolling, it would draw, scroll would invalidate,
696 and then it should be drawn again */
697 /* only do the drawing if the cave already exists. */
698 if (game->cave && game->element_buffer && game->gfx_buffer)
700 /* if fine scrolling, scroll at 50hz. if not, only scroll at every second call, so 25hz. */
701 /* do the scrolling. scroll exactly, if player is not yet alive */
702 game->out_of_window = gd_scroll(game, game->cave->player_state == GD_PL_NOT_YET, FALSE);