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