464682ff5e371dfa5baca19df56d15e19326b73b
[rocksndiamonds.git] / src / game_bd / bd_gameplay.c
1 /*
2  * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
3  *
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.
7  *
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.
15  */
16
17 #include "main_bd.h"
18
19
20 /* universal settings */
21 static boolean gd_no_invisible_outbox = FALSE;
22
23
24 void gd_game_free(GdGame *game)
25 {
26   /* stop sounds */
27   gd_sound_off();
28
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);
33   if (game->dir_buffer)
34     gd_cave_map_free(game->dir_buffer);
35   if (game->gfx_buffer)
36     gd_cave_map_free(game->gfx_buffer);
37
38   game->player_lives = 0;
39
40   if (game->cave)
41     gd_cave_free(game->cave);
42
43   free(game);
44 }
45
46 /* add bonus life. if sound enabled, play sound, too. */
47 static void add_bonus_life(GdGame *game, boolean inform_user)
48 {
49   if (inform_user)
50   {
51     gd_sound_play_bonus_life();
52     game->bonus_life_flash = 100;
53   }
54
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)
58   {
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. */
62     game->player_lives++;
63   }
64 }
65
66 /* increment score of player.
67    flash screen if bonus life
68 */
69 static void increment_score(GdGame *game, int increment)
70 {
71   int i;
72
73   i = game->player_score / gd_caveset_data->bonus_life_score;
74   game->player_score += increment;
75   game->cave_score += increment;
76
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);
80 }
81
82 /* do the things associated with loading a new cave. function creates gfx buffer and the like. */
83 static void load_cave(GdGame *game)
84 {
85   int x, y;
86
87   /* delete element buffer */
88   if (game->element_buffer)
89     gd_cave_map_free(game->element_buffer);
90   game->element_buffer = NULL;
91
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;
96
97   /* delete direction buffer */
98   if (game->dir_buffer)
99     gd_cave_map_free(game->dir_buffer);
100   game->dir_buffer = NULL;
101
102   /* delete gfx buffer */
103   if (game->gfx_buffer)
104     gd_cave_map_free(game->gfx_buffer);
105   game->gfx_buffer = NULL;
106
107   /* load the cave */
108   game->cave_score = 0;
109
110   /* delete previous cave */
111   gd_cave_free(game->cave);
112
113   if (native_bd_level.loaded_from_caveset)
114     game->original_cave = gd_get_original_cave_from_caveset(game->cave_num);
115   else
116     game->original_cave = native_bd_level.cave;
117
118   game->cave = gd_get_prepared_cave(game->original_cave, game->level_num);
119
120   if (game->cave->intermission && game->cave->intermission_instantlife)
121     add_bonus_life(game, FALSE);
122
123   game->milliseconds_anim = 0;
124   game->milliseconds_game = 0;        /* set game timer to zero, too */
125
126   /* create new element buffer */
127   game->element_buffer = gd_cave_map_new(game->cave, int);
128
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;
132
133   /* create new last element buffer */
134   game->last_element_buffer = gd_cave_map_new(game->cave, int);
135
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;
139
140   /* create new direction buffer */
141   game->dir_buffer = gd_cave_map_new(game->cave, int);
142
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;
146
147   /* create new gfx buffer */
148   game->gfx_buffer = gd_cave_map_new(game->cave, int);
149
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" */
153 }
154
155 GdCave *gd_create_snapshot(GdGame *game)
156 {
157   GdCave *snapshot;
158
159   if (game->cave == NULL)
160     return NULL;
161
162   /* make an exact copy */
163   snapshot = gd_cave_new_from_cave(game->cave);
164
165   return snapshot;
166 }
167
168 /* this starts a new game */
169 GdGame *gd_game_new(const int cave, const int level)
170 {
171   GdGame *game;
172
173   game = checked_calloc(sizeof(GdGame));
174
175   game->cave_num = cave;
176   game->level_num = level;
177
178   game->player_lives = gd_caveset_data->initial_lives;
179   game->player_score = 0;
180
181   game->player_move = GD_MV_STILL;
182   game->player_move_stick = FALSE;
183   game->player_fire = FALSE;
184
185   game->state_counter = GAME_INT_LOAD_CAVE;
186
187   game->show_story = TRUE;
188
189   return game;
190 }
191
192 static void iterate_cave(GdGame *game, GdDirection player_move, boolean fire)
193 {
194   boolean suicide = FALSE;
195
196   /* ANYTHING EXCEPT A TIMEOUT, WE ITERATE THE CAVE */
197   if (game->cave->player_state != GD_PL_TIMEOUT)
198   {
199     if (TapeIsPlaying_ReplayBD())
200     {
201       byte *action_rnd = TapePlayAction_BD();
202
203       if (action_rnd != NULL)
204       {
205         int action_bd = map_action_RND_to_BD(action_rnd[0]);
206
207         player_move = (action_bd & GD_REPLAY_MOVE_MASK);
208         fire        = (action_bd & GD_REPLAY_FIRE_MASK) != 0;
209       }
210     }
211
212     /* iterate cave */
213     gd_cave_iterate(game->cave, player_move, fire, suicide);
214
215     if (game->cave->score)
216       increment_score(game, game->cave->score);
217
218     gd_sound_play_cave(game->cave);
219   }
220
221   if (game->cave->player_state == GD_PL_EXITED)
222   {
223     if (game->cave->intermission &&
224         game->cave->intermission_rewardlife &&
225         game->player_lives != 0)
226     {
227       /* one life extra for completing intermission */
228       add_bonus_life(game, FALSE);
229     }
230
231     /* start adding points for remaining time */
232     game->state_counter = GAME_INT_CHECK_BONUS_TIME;
233     gd_cave_clear_sounds(game->cave);
234
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);
238   }
239
240   if (game->cave->player_state == GD_PL_DIED ||
241       game->cave->player_state == GD_PL_TIMEOUT)
242   {
243     game_bd.game_over = TRUE;
244     game_bd.cover_screen = TRUE;
245   }
246 }
247
248 static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean fast_forward)
249 {
250   int millisecs_elapsed = 20;
251   boolean frame;    /* set to true, if this will be an animation frame */
252   GdGameState return_state;
253   int counter_next;
254   int x, y;
255
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 */
260
261   if (game->milliseconds_anim >= 40)
262   {
263     frame = TRUE;
264     game->milliseconds_anim -= 40;
265   }
266
267   /* cannot be less than uncover start. */
268   if (game->state_counter < GAME_INT_LOAD_CAVE)
269   {
270     ;
271   }
272   else if (game->state_counter == GAME_INT_LOAD_CAVE)
273   {
274     /* do the things associated with loading a new cave. function creates gfx buffer and the like. */
275     load_cave(game);
276
277     return_state = GD_GAME_NOTHING;
278     counter_next = GAME_INT_SHOW_STORY;
279   }
280   else if (game->state_counter == GAME_INT_SHOW_STORY)
281   {
282     /* for normal game, every cave can have a long string of description/story. show that. */
283
284     /* if we have a story... */
285 #if 0
286     if (game->show_story && game->original_cave && game->original_cave->story != NULL)
287       Info("Cave Story: %s", game->original_cave->story);
288 #endif
289
290     counter_next = GAME_INT_START_UNCOVER;
291     return_state = GD_GAME_NOTHING;
292   }
293   else if (game->state_counter == GAME_INT_START_UNCOVER)
294   {
295     /* the very beginning. */
296
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;
301
302     counter_next = game->state_counter + 1;
303
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;
307   }
308   else if (game->state_counter < GAME_INT_UNCOVER_ALL)
309   {
310     /* uncover animation */
311
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);
315
316     counter_next = game->state_counter;
317
318     if (frame)
319     {
320       int j;
321
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++)
327       {
328         y = gd_random_int_range(0, game->cave->h);
329         x = gd_random_int_range(0, game->cave->w);
330
331         game->cave->map[y][x] &= ~COVERED;
332       }
333
334       counter_next++;    /* as we did something, advance the counter. */
335     }
336
337     return_state = GD_GAME_NOTHING;
338   }
339   else if (game->state_counter == GAME_INT_UNCOVER_ALL)
340   {
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;
345
346     /* to stop uncover sound. */
347     gd_cave_clear_sounds(game->cave);
348     gd_sound_play_cave(game->cave);
349
350     counter_next = GAME_INT_CAVE_RUNNING;
351     return_state = GD_GAME_NOTHING;
352   }
353   else if (game->state_counter == GAME_INT_CAVE_RUNNING)
354   {
355     /* normal. */
356     int cavespeed;
357
358     if (!fast_forward)
359       cavespeed = game->cave->speed;   /* cave speed in ms, like 175ms/frame */
360     else
361       cavespeed = 40;    /* if fast forward, ignore cave speed, and go as 25 iterations/sec */
362
363     /* ITERATION - cave is running. */
364
365     /* normally nothing happes. but if we iterate, this might change. */
366     return_state = GD_GAME_NOTHING;
367
368     /* if allowing cave movements, add elapsed time to timer. and then we can check what to do. */
369     if (allow_iterate)
370       game->milliseconds_game += millisecs_elapsed;
371
372     /* increment cycle (frame) counter for the current cave iteration */
373     game->itercycle++;
374
375     if (game->milliseconds_game >= cavespeed)
376     {
377       GdPlayerState pl;
378
379       game->milliseconds_game -= cavespeed;
380       pl = game->cave->player_state;
381
382       /* initialize buffers for last cave element and direction for next iteration */
383       for (y = 0; y < game->cave->h; y++)
384       {
385         for (x = 0; x < game->cave->w; x++)
386         {
387           game->last_element_buffer[y][x] = game->element_buffer[y][x];
388           game->dir_buffer[y][x] = GD_MV_STILL;
389         }
390       }
391
392       /* store last maximum number of cycles (to force redraw if changed) */
393       game->itermax_last = game->itermax;
394
395       /* update maximum number of cycles (frame) per cave iteration */
396       game->itermax = game->itercycle;
397
398       /* reset cycle (frame) counter for the next cave iteration */
399       game->itercycle = 0;
400
401       iterate_cave(game, game->player_move, game->player_fire);
402
403       if (game->player_move == GD_MV_STILL)
404       {
405         game->player_move_stick = FALSE;
406       }
407       else
408       {
409         game->player_move_stick = TRUE;
410         game->player_move = GD_MV_STILL;
411       }
412
413       /* as we iterated, the score and the like could have been changed. */
414       return_state = GD_GAME_LABELS_CHANGED;
415
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;
419     }
420
421     /* do not change counter state, as it is set by iterate_cave() */
422     counter_next = game->state_counter;
423   }
424   else if (game->state_counter == GAME_INT_CHECK_BONUS_TIME)
425   {
426     /* before covering, we check for time bonus score */
427     if (frame)
428     {
429       /* if time remaining, bonus points are added. do not start animation yet. */
430       if (game->cave->time > 0)
431       {
432         /* subtract number of "milliseconds" - nothing to do with gameplay->millisecs! */
433         game->cave->time -= game->cave->timing_factor;
434
435         /* higher levels get more bonus points per second remained */
436         increment_score(game, game->cave->timevalue);
437
438         /* if much time (> 60s) remained, fast counter :) */
439         if (game->cave->time > 60 * game->cave->timing_factor)
440         {
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);
444         }
445
446         /* just to be neat */
447         if (game->cave->time < 0)
448           game->cave->time = 0;
449
450         counter_next = game->state_counter;    /* do not change yet */
451       }
452       else
453       {
454         game_bd.level_solved = TRUE;
455         game_bd.cover_screen = TRUE;
456
457         /* if no more points, start waiting a bit, and later start covering. */
458         counter_next = GAME_INT_WAIT_BEFORE_COVER;
459       }
460
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 */
463
464       /* play bonus sound */
465       gd_cave_set_seconds_sound(game->cave);
466       gd_sound_play_cave(game->cave);
467
468       return_state = GD_GAME_LABELS_CHANGED;
469     }
470     else
471     {
472       return_state = GD_GAME_NOTHING;
473
474       /* do not change counter state, as it is set by iterate_cave() */
475       counter_next = game->state_counter;
476     }
477   }
478   else if (game->state_counter == GAME_INT_WAIT_BEFORE_COVER)
479   {
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;
484
485     if (game->player_lives == 0)
486       return_state = GD_GAME_NO_MORE_LIVES;
487     else
488       return_state = GD_GAME_NOTHING;
489   }
490   else if (game->state_counter > GAME_INT_WAIT_BEFORE_COVER &&
491            game->state_counter < GAME_INT_COVER_START)
492   {
493     /* after adding bonus points, we wait some time before starting to cover.
494        ... and the other frames. */
495     counter_next = game->state_counter;
496     if (frame)
497       counter_next++;    /* 40 ms elapsed, advance counter */
498
499     return_state = GD_GAME_NOTHING;
500   }
501   else if (game->state_counter == GAME_INT_COVER_START)
502   {
503     /* starting to cover. start cover sound. */
504
505     gd_cave_clear_sounds(game->cave);
506     gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
507
508     /* to play cover sound */
509     gd_sound_play_cave(game->cave);
510
511     counter_next = game->state_counter + 1;
512     return_state = GD_GAME_NOTHING;
513   }
514   else if (game->state_counter > GAME_INT_COVER_START &&
515            game->state_counter < GAME_INT_COVER_ALL)
516   {
517     /* covering. */
518     gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
519
520     counter_next = game->state_counter;
521
522     if (frame)
523     {
524       int j;
525
526       counter_next++;    /* 40 ms elapsed, doing cover: advance counter */
527
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;
531     }
532
533     return_state = GD_GAME_NOTHING;
534   }
535   else if (game->state_counter == GAME_INT_COVER_ALL)
536   {
537     /* 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;
541
542     counter_next = game->state_counter + 1;
543     return_state = GD_GAME_NOTHING;
544
545     /* to stop uncover sound. */
546     gd_cave_clear_sounds(game->cave);
547     gd_sound_play_cave(game->cave);
548   }
549   else
550   {
551     /* cover all + 1 */
552
553     if (game->player_lives != 0)
554       return_state = GD_GAME_NOTHING;    /* and go to next level */
555     else
556       return_state = GD_GAME_GAME_OVER;
557   }
558
559   /* draw the cave */
560   if (frame)
561   {
562     if (game->bonus_life_flash)    /* bonus life - frames */
563       game->bonus_life_flash--;
564
565     game->animcycle = (game->animcycle + 1) % 8;
566   }
567
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);
573
574   game->state_counter = counter_next;
575
576   return return_state;
577 }
578
579 void play_game_func(GdGame *game, int action)
580 {
581   GdGameState state;
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);
587
588   if (game->player_move_stick || move_up || move_down || move_left || move_right) // no "fire"!
589   {
590     /* get movement */
591     game->player_move = gd_direction_from_keypress(move_up, move_down, move_left, move_right);
592
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;
596   }
597
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;
601
602   /* tell the interrupt "20ms has passed" */
603   state = gd_game_main_int(game, !game->out_of_window, gd_keystate[SDL_SCANCODE_F]);
604
605   /* state of game, returned by gd_game_main_int */
606   switch (state)
607   {
608     case GD_GAME_CAVE_LOADED:
609       /* select colors, prepare drawing etc. */
610       gd_scroll_to_origin();
611
612       /* fill whole screen with black - cave might be smaller than previous! */
613       FillRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE, BLACK_PIXEL);
614       break;
615
616     default:
617       break;
618   }
619
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)
626   {
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);
630   }
631 }