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