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