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