replaced glib function calls to g_rand_*() and g_random_*()
[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->last_element_buffer)
35     gd_cave_map_free(game->last_element_buffer);
36   if (game->dir_buffer)
37     gd_cave_map_free(game->dir_buffer);
38   if (game->gfx_buffer)
39     gd_cave_map_free(game->gfx_buffer);
40
41   game->player_lives = 0;
42
43   if (game->cave)
44     gd_cave_free(game->cave);
45
46   free(game);
47 }
48
49 /* add bonus life. if sound enabled, play sound, too. */
50 static void add_bonus_life(GdGame *game, boolean inform_user)
51 {
52   if (inform_user)
53   {
54     gd_sound_play_bonus_life();
55     game->bonus_life_flash = 100;
56   }
57
58   /* really increment number of lifes? only in a real game, nowhere else. */
59   if (game->player_lives &&
60       game->player_lives < gd_caveset_data->maximum_lives)
61   {
62     /* only add a life, if lives is > 0.
63        lives == 0 is a test run or a snapshot, no bonus life then. */
64     /* also, obey max number of bonus lives. */
65     game->player_lives++;
66   }
67 }
68
69 /* increment score of player.
70    flash screen if bonus life
71 */
72 static void increment_score(GdGame *game, int increment)
73 {
74   int i;
75
76   i = game->player_score / gd_caveset_data->bonus_life_score;
77   game->player_score += increment;
78   game->cave_score += increment;
79
80   /* if score crossed bonus_life_score point boundary, player won a bonus life */
81   if (game->player_score / gd_caveset_data->bonus_life_score > i)
82     add_bonus_life(game, TRUE);
83 }
84
85 /* do the things associated with loading a new cave. function creates gfx buffer and the like. */
86 static void load_cave(GdGame *game)
87 {
88   int x, y;
89
90   /* delete element buffer */
91   if (game->element_buffer)
92     gd_cave_map_free(game->element_buffer);
93   game->element_buffer = NULL;
94
95   /* delete last element buffer */
96   if (game->last_element_buffer)
97     gd_cave_map_free(game->last_element_buffer);
98   game->last_element_buffer = NULL;
99
100   /* delete direction buffer */
101   if (game->dir_buffer)
102     gd_cave_map_free(game->dir_buffer);
103   game->dir_buffer = NULL;
104
105   /* delete gfx buffer */
106   if (game->gfx_buffer)
107     gd_cave_map_free(game->gfx_buffer);
108   game->gfx_buffer = NULL;
109
110   /* load the cave */
111   game->cave_score = 0;
112
113   /* delete previous cave */
114   gd_cave_free(game->cave);
115
116   if (native_bd_level.loaded_from_caveset)
117     game->original_cave = gd_get_original_cave_from_caveset(game->cave_num);
118   else
119     game->original_cave = native_bd_level.cave;
120
121   game->cave = gd_get_prepared_cave(game->original_cave, game->level_num);
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];
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       /* select colors, prepare drawing etc. */
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 }