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