added setup option to always play native BD caves with random colors
[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 static void increment_score(GdGame *game, int increment)
69 {
70   int i;
71
72   i = game->player_score / gd_caveset_data->bonus_life_score;
73   game->player_score += increment;
74   game->cave_score += increment;
75
76   // if score crossed bonus_life_score point boundary, player won a bonus life
77   if (game->player_score / gd_caveset_data->bonus_life_score > i)
78     add_bonus_life(game, TRUE);
79 }
80
81 // do the things associated with loading a new cave. function creates gfx buffer and the like.
82 static void load_cave(GdGame *game)
83 {
84   int x, y;
85
86   // delete element buffer
87   if (game->element_buffer)
88     gd_cave_map_free(game->element_buffer);
89   game->element_buffer = NULL;
90
91   // delete last element buffer
92   if (game->last_element_buffer)
93     gd_cave_map_free(game->last_element_buffer);
94   game->last_element_buffer = NULL;
95
96   // delete direction buffer
97   if (game->dir_buffer)
98     gd_cave_map_free(game->dir_buffer);
99   game->dir_buffer = NULL;
100
101   // delete gfx buffer
102   if (game->gfx_buffer)
103     gd_cave_map_free(game->gfx_buffer);
104   game->gfx_buffer = NULL;
105
106   // load the cave
107   game->cave_score = 0;
108
109   // delete previous cave
110   gd_cave_free(game->cave);
111
112   if (native_bd_level.loaded_from_caveset)
113     game->original_cave = gd_get_original_cave_from_caveset(game->cave_num);
114   else
115     game->original_cave = native_bd_level.cave;
116
117   game->cave = gd_get_prepared_cave(game->original_cave, game->level_num);
118
119   // if requested, recolor cave (cave is a copy only, so no worries)
120   if (setup.bd_random_colors)
121     gd_cave_set_random_colors(game->cave, setup.bd_default_color_type);
122
123   if (game->cave->intermission && game->cave->intermission_instantlife)
124     add_bonus_life(game, FALSE);
125
126   game->milliseconds_anim = 0;
127   game->milliseconds_game = 0;        // set game timer to zero, too
128
129   // create new element buffer
130   game->element_buffer = gd_cave_map_new(game->cave, int);
131
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;
135
136   // create new last element buffer
137   game->last_element_buffer = gd_cave_map_new(game->cave, int);
138
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;
142
143   // create new direction buffer
144   game->dir_buffer = gd_cave_map_new(game->cave, int);
145
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;
149
150   // create new gfx buffer
151   game->gfx_buffer = gd_cave_map_new(game->cave, int);
152
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"
156 }
157
158 GdCave *gd_create_snapshot(GdGame *game)
159 {
160   GdCave *snapshot;
161
162   if (game->cave == NULL)
163     return NULL;
164
165   // make an exact copy
166   snapshot = gd_cave_new_from_cave(game->cave);
167
168   return snapshot;
169 }
170
171 // this starts a new game
172 GdGame *gd_game_new(const int cave, const int level)
173 {
174   GdGame *game;
175
176   game = checked_calloc(sizeof(GdGame));
177
178   game->cave_num = cave;
179   game->level_num = level;
180
181   game->player_lives = gd_caveset_data->initial_lives;
182   game->player_score = 0;
183
184   game->player_move = GD_MV_STILL;
185   game->player_move_stick = FALSE;
186   game->player_fire = FALSE;
187
188   game->state_counter = GAME_INT_LOAD_CAVE;
189
190   game->show_story = TRUE;
191
192   return game;
193 }
194
195 static void iterate_cave(GdGame *game, GdDirection player_move, boolean fire)
196 {
197   boolean suicide = FALSE;
198
199   // ANYTHING EXCEPT A TIMEOUT, WE ITERATE THE CAVE
200   if (game->cave->player_state != GD_PL_TIMEOUT)
201   {
202     if (TapeIsPlaying_ReplayBD())
203     {
204       byte *action_rnd = TapePlayAction_BD();
205
206       if (action_rnd != NULL)
207       {
208         int action_bd = map_action_RND_to_BD(action_rnd[0]);
209
210         player_move = (action_bd & GD_REPLAY_MOVE_MASK);
211         fire        = (action_bd & GD_REPLAY_FIRE_MASK) != 0;
212       }
213     }
214
215     // iterate cave
216     gd_cave_iterate(game->cave, player_move, fire, suicide);
217
218     if (game->cave->score)
219       increment_score(game, game->cave->score);
220
221     gd_sound_play_cave(game->cave);
222   }
223
224   if (game->cave->player_state == GD_PL_EXITED)
225   {
226     if (game->cave->intermission &&
227         game->cave->intermission_rewardlife &&
228         game->player_lives != 0)
229     {
230       // one life extra for completing intermission
231       add_bonus_life(game, FALSE);
232     }
233
234     // start adding points for remaining time
235     game->state_counter = GAME_INT_CHECK_BONUS_TIME;
236     gd_cave_clear_sounds(game->cave);
237
238     // play cave finished sound
239     gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1);
240     gd_sound_play_cave(game->cave);
241   }
242
243   if (game->cave->player_state == GD_PL_DIED ||
244       game->cave->player_state == GD_PL_TIMEOUT)
245   {
246     game_bd.game_over = TRUE;
247     game_bd.cover_screen = TRUE;
248   }
249 }
250
251 static GdGameState gd_game_main_int(GdGame *game, boolean allow_iterate, boolean fast_forward)
252 {
253   int millisecs_elapsed = 20;
254   boolean frame;    // set to true, if this will be an animation frame
255   GdGameState return_state;
256   int counter_next;
257   int x, y;
258
259   counter_next = GAME_INT_INVALID;
260   return_state = GD_GAME_INVALID_STATE;
261   game->milliseconds_anim += millisecs_elapsed;    // keep track of time
262   frame = FALSE;    // set to true, if this will be an animation frame
263
264   if (game->milliseconds_anim >= 40)
265   {
266     frame = TRUE;
267     game->milliseconds_anim -= 40;
268   }
269
270   // cannot be less than uncover start.
271   if (game->state_counter < GAME_INT_LOAD_CAVE)
272   {
273     ;
274   }
275   else if (game->state_counter == GAME_INT_LOAD_CAVE)
276   {
277     // do the things associated with loading a new cave. function creates gfx buffer and the like.
278     load_cave(game);
279
280     return_state = GD_GAME_NOTHING;
281     counter_next = GAME_INT_SHOW_STORY;
282   }
283   else if (game->state_counter == GAME_INT_SHOW_STORY)
284   {
285     // for normal game, every cave can have a long string of description/story. show that.
286
287     // if we have a story...
288 #if 0
289     if (game->show_story && game->original_cave && game->original_cave->story != NULL)
290       Info("Cave Story: %s", game->original_cave->story);
291 #endif
292
293     counter_next = GAME_INT_START_UNCOVER;
294     return_state = GD_GAME_NOTHING;
295   }
296   else if (game->state_counter == GAME_INT_START_UNCOVER)
297   {
298     // the very beginning.
299
300     // cover all cells of cave
301     for (y = 0; y < game->cave->h; y++)
302       for (x = 0; x < game->cave->w; x++)
303         game->cave->map[y][x] |= COVERED;
304
305     counter_next = game->state_counter + 1;
306
307     // very important: tell the caller that we loaded a new cave.
308     // size of the cave might be new, colors might be new, and so on.
309     return_state = GD_GAME_CAVE_LOADED;
310   }
311   else if (game->state_counter < GAME_INT_UNCOVER_ALL)
312   {
313     // uncover animation
314
315     // to play cover sound
316     gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
317     gd_sound_play_cave(game->cave);
318
319     counter_next = game->state_counter;
320
321     if (frame)
322     {
323       int j;
324
325       // original game uncovered one cell per line each frame.
326       // we have different cave sizes, so uncover width * height / 40 random
327       // cells each frame. (original was width = 40).
328       // this way the uncovering is the same speed also for intermissions.
329       for (j = 0; j < game->cave->w * game->cave->h / 40; j++)
330       {
331         y = gd_random_int_range(0, game->cave->h);
332         x = gd_random_int_range(0, game->cave->w);
333
334         game->cave->map[y][x] &= ~COVERED;
335       }
336
337       counter_next++;    // as we did something, advance the counter.
338     }
339
340     return_state = GD_GAME_NOTHING;
341   }
342   else if (game->state_counter == GAME_INT_UNCOVER_ALL)
343   {
344     // time to uncover the whole cave.
345     for (y = 0; y < game->cave->h; y++)
346       for (x = 0; x < game->cave->w; x++)
347         game->cave->map[y][x] &= ~COVERED;
348
349     // to stop uncover sound.
350     gd_cave_clear_sounds(game->cave);
351     gd_sound_play_cave(game->cave);
352
353     counter_next = GAME_INT_CAVE_RUNNING;
354     return_state = GD_GAME_NOTHING;
355   }
356   else if (game->state_counter == GAME_INT_CAVE_RUNNING)
357   {
358     // normal.
359     int cavespeed;
360
361     if (!fast_forward)
362       cavespeed = game->cave->speed;   // cave speed in ms, like 175ms/frame
363     else
364       cavespeed = 40;    // if fast forward, ignore cave speed, and go as 25 iterations/sec
365
366     // ITERATION - cave is running.
367
368     // normally nothing happes. but if we iterate, this might change.
369     return_state = GD_GAME_NOTHING;
370
371     // if allowing cave movements, add elapsed time to timer. and then we can check what to do.
372     if (allow_iterate)
373       game->milliseconds_game += millisecs_elapsed;
374
375     // increment cycle (frame) counter for the current cave iteration
376     game->itercycle++;
377
378     if (game->milliseconds_game >= cavespeed)
379     {
380       GdPlayerState pl;
381
382       game->milliseconds_game -= cavespeed;
383       pl = game->cave->player_state;
384
385       // initialize buffers for last cave element and direction for next iteration
386       for (y = 0; y < game->cave->h; y++)
387       {
388         for (x = 0; x < game->cave->w; x++)
389         {
390           game->last_element_buffer[y][x] = game->element_buffer[y][x] & ~SKIPPED;
391           game->dir_buffer[y][x] = GD_MV_STILL;
392         }
393       }
394
395       // store last maximum number of cycles (to force redraw if changed)
396       game->itermax_last = game->itermax;
397
398       // update maximum number of cycles (frame) per cave iteration
399       game->itermax = game->itercycle;
400
401       // reset cycle (frame) counter for the next cave iteration
402       game->itercycle = 0;
403
404       iterate_cave(game, game->player_move, game->player_fire);
405
406       if (game->player_move == GD_MV_STILL)
407       {
408         game->player_move_stick = FALSE;
409       }
410       else
411       {
412         game->player_move_stick = TRUE;
413         game->player_move = GD_MV_STILL;
414       }
415
416       // as we iterated, the score and the like could have been changed.
417       return_state = GD_GAME_LABELS_CHANGED;
418
419       // and if the cave timeouted at this moment, that is a special case.
420       if (pl != GD_PL_TIMEOUT && game->cave->player_state == GD_PL_TIMEOUT)
421         return_state = GD_GAME_TIMEOUT_NOW;
422     }
423
424     // do not change counter state, as it is set by iterate_cave()
425     counter_next = game->state_counter;
426   }
427   else if (game->state_counter == GAME_INT_CHECK_BONUS_TIME)
428   {
429     // before covering, we check for time bonus score
430     if (frame)
431     {
432       // if time remaining, bonus points are added. do not start animation yet.
433       if (game->cave->time > 0)
434       {
435         // subtract number of "milliseconds" - nothing to do with gameplay->millisecs!
436         game->cave->time -= game->cave->timing_factor;
437
438         // higher levels get more bonus points per second remained
439         increment_score(game, game->cave->timevalue);
440
441         // if much time (> 60s) remained, fast counter :)
442         if (game->cave->time > 60 * game->cave->timing_factor)
443         {
444           // decrement by nine each frame, so it also looks like a fast counter. 9 is 8 + 1!
445           game->cave->time -= 8 * game->cave->timing_factor;
446           increment_score(game, game->cave->timevalue * 8);
447         }
448
449         // just to be neat
450         if (game->cave->time < 0)
451           game->cave->time = 0;
452
453         counter_next = game->state_counter;    // do not change yet
454       }
455       else
456       {
457         game_bd.level_solved = TRUE;
458         game_bd.cover_screen = TRUE;
459
460         // if no more points, start waiting a bit, and later start covering.
461         counter_next = GAME_INT_WAIT_BEFORE_COVER;
462       }
463
464       if (game->cave->time / game->cave->timing_factor > 8)
465         gd_sound_play(game->cave, GD_S_FINISHED, O_NONE, -1, -1); // play cave finished sound
466
467       // play bonus sound
468       gd_cave_set_seconds_sound(game->cave);
469       gd_sound_play_cave(game->cave);
470
471       return_state = GD_GAME_LABELS_CHANGED;
472     }
473     else
474     {
475       return_state = GD_GAME_NOTHING;
476
477       // do not change counter state, as it is set by iterate_cave()
478       counter_next = game->state_counter;
479     }
480   }
481   else if (game->state_counter == GAME_INT_WAIT_BEFORE_COVER)
482   {
483     // after adding bonus points, we wait some time before starting to cover.
484     // this is the FIRST frame... so we check for game over and maybe jump there
485     // if no more lives, game is over.
486     counter_next = game->state_counter;
487
488     if (game->player_lives == 0)
489       return_state = GD_GAME_NO_MORE_LIVES;
490     else
491       return_state = GD_GAME_NOTHING;
492   }
493   else if (game->state_counter > GAME_INT_WAIT_BEFORE_COVER &&
494            game->state_counter < GAME_INT_COVER_START)
495   {
496     // after adding bonus points, we wait some time before starting to cover.
497     // ... and the other frames.
498     counter_next = game->state_counter;
499     if (frame)
500       counter_next++;    // 40 ms elapsed, advance counter
501
502     return_state = GD_GAME_NOTHING;
503   }
504   else if (game->state_counter == GAME_INT_COVER_START)
505   {
506     // starting to cover. start cover sound.
507
508     gd_cave_clear_sounds(game->cave);
509     gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
510
511     // to play cover sound
512     gd_sound_play_cave(game->cave);
513
514     counter_next = game->state_counter + 1;
515     return_state = GD_GAME_NOTHING;
516   }
517   else if (game->state_counter > GAME_INT_COVER_START &&
518            game->state_counter < GAME_INT_COVER_ALL)
519   {
520     // covering.
521     gd_sound_play(game->cave, GD_S_COVERING, O_COVERED, -1, -1);
522
523     counter_next = game->state_counter;
524
525     if (frame)
526     {
527       int j;
528
529       counter_next++;    // 40 ms elapsed, doing cover: advance counter
530
531       // covering eight times faster than uncovering.
532       for (j = 0; j < game->cave->w * game->cave->h * 8 / 40; j++)
533         game->cave->map[gd_random_int_range(0, game->cave->h)][gd_random_int_range (0, game->cave->w)] |= COVERED;
534     }
535
536     return_state = GD_GAME_NOTHING;
537   }
538   else if (game->state_counter == GAME_INT_COVER_ALL)
539   {
540     // cover all
541     for (y = 0; y < game->cave->h; y++)
542       for (x = 0; x < game->cave->w; x++)
543         game->cave->map[y][x] |= COVERED;
544
545     counter_next = game->state_counter + 1;
546     return_state = GD_GAME_NOTHING;
547
548     // to stop uncover sound.
549     gd_cave_clear_sounds(game->cave);
550     gd_sound_play_cave(game->cave);
551   }
552   else
553   {
554     // cover all + 1
555
556     if (game->player_lives != 0)
557       return_state = GD_GAME_NOTHING;    // and go to next level
558     else
559       return_state = GD_GAME_GAME_OVER;
560   }
561
562   // draw the cave
563   if (frame)
564   {
565     if (game->bonus_life_flash)    // bonus life - frames
566       game->bonus_life_flash--;
567
568     game->animcycle = (game->animcycle + 1) % 8;
569   }
570
571   // always render the cave to the gfx buffer;
572   // however it may do nothing if animcycle was not changed.
573   if (game->element_buffer && game->gfx_buffer)
574     gd_drawcave_game(game->cave, game->element_buffer, game->gfx_buffer,
575                      game->bonus_life_flash != 0, game->animcycle, gd_no_invisible_outbox);
576
577   game->state_counter = counter_next;
578
579   return return_state;
580 }
581
582 void play_game_func(GdGame *game, int action)
583 {
584   GdGameState state;
585   boolean move_up    = ((action & JOY_UP)    != 0);
586   boolean move_down  = ((action & JOY_DOWN)  != 0);
587   boolean move_left  = ((action & JOY_LEFT)  != 0);
588   boolean move_right = ((action & JOY_RIGHT) != 0);
589   boolean fire  = ((action & (JOY_BUTTON_1 | JOY_BUTTON_2)) != 0);
590
591   if (game->player_move_stick || move_up || move_down || move_left || move_right) // no "fire"!
592   {
593     // get movement
594     game->player_move = gd_direction_from_keypress(move_up, move_down, move_left, move_right);
595
596     // when storing last action, only update fire action with direction
597     // (prevents clearing direction if snapping stopped before action is performed)
598     game->player_fire = fire;
599   }
600
601   // if no direction was stored before, allow setting fire to current state
602   if (game->player_move == GD_MV_STILL)
603     game->player_fire = fire;
604
605   // tell the interrupt "20ms has passed"
606   state = gd_game_main_int(game, !game->out_of_window, gd_keystate[SDL_SCANCODE_F]);
607
608   // state of game, returned by gd_game_main_int
609   switch (state)
610   {
611     case GD_GAME_CAVE_LOADED:
612       // scroll to start position
613       gd_scroll_to_origin();
614
615       // fill whole screen with black - cave might be smaller than previous!
616       FillRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE, BLACK_PIXEL);
617       break;
618
619     default:
620       break;
621   }
622
623   // for the sdl version, it seems nicer if we first scroll, and then draw.
624   // scrolling for the sdl version will merely invalidate the whole gfx buffer.
625   // if drawcave was before scrolling, it would draw, scroll would invalidate,
626   // and then it should be drawn again
627   // only do the drawing if the cave already exists.
628   if (game->cave && game->element_buffer && game->gfx_buffer)
629   {
630     // if fine scrolling, scroll at 50hz. if not, only scroll at every second call, so 25hz.
631     // do the scrolling. scroll exactly, if player is not yet alive
632     game->out_of_window = gd_scroll(game, game->cave->player_state == GD_PL_NOT_YET, FALSE);
633   }
634 }