added game engine support for playing native Boulder Dash levels
[rocksndiamonds.git] / src / game_bd / main_bd.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2024 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // main_bd.c
10 // ============================================================================
11
12 #include "main_bd.h"
13
14
15 struct GameInfo_BD game_bd;
16 struct LevelInfo_BD native_bd_level;
17 struct EngineSnapshotInfo_BD engine_snapshot_bd;
18
19
20 // ============================================================================
21 // initialization functions
22 // ============================================================================
23
24 void InitGfxBuffers_BD(void)
25 {
26   ReCreateBitmap(&gd_screen_bitmap, SXSIZE, SYSIZE);
27
28   set_cell_size(TILESIZE_VAR);
29   set_play_area(SXSIZE, SYSIZE);
30 }
31
32 void bd_open_all(void)
33 {
34   InitGraphicInfo_BD();
35
36   gd_cave_init();
37   gd_cave_db_init();
38
39   gd_c64_import_init_tables();
40
41   gd_caveset_clear();
42
43   gd_init_keystate();
44
45   gd_sound_init();
46 }
47
48 void bd_close_all(void)
49 {
50 }
51
52
53 // ============================================================================
54 // level file functions
55 // ============================================================================
56
57 void setLevelInfoToDefaults_BD_Ext(int width, int height)
58 {
59   GdCave *cave = native_bd_level.cave;
60
61   if (cave != NULL)
62     gd_cave_free(cave);
63
64   // get empty cave, using default values
65   cave = gd_cave_new();
66
67   // set cave size, if defined
68   if (width > 0 && height > 0)
69   {
70     cave->w = width;
71     cave->h = height;
72   }
73
74   gd_flatten_cave(cave, 0);
75
76   cave->selectable = TRUE;
77   cave->intermission = FALSE;
78
79   native_bd_level.cave = cave;
80   native_bd_level.replay = NULL;
81
82   native_bd_level.cave_nr = 0;
83   native_bd_level.level_nr = 0;
84
85   native_bd_level.loaded_from_caveset = FALSE;
86 }
87
88 void setLevelInfoToDefaults_BD(void)
89 {
90   setLevelInfoToDefaults_BD_Ext(0, 0);
91 }
92
93 boolean LoadNativeLevel_BD(char *filename, int level_pos, boolean level_info_only)
94 {
95   static char *filename_loaded = NULL;
96
97   if (filename_loaded == NULL || !strEqual(filename, filename_loaded))
98   {
99     if (!gd_caveset_load_from_file(filename))
100     {
101       if (!level_info_only)
102         Warn("cannot load BD cave set from file '%s'", filename);
103
104       return FALSE;
105     }
106
107     setString(&filename_loaded, filename);
108   }
109
110   if (level_pos < 0 || level_pos >= 5 * gd_caveset_count())
111   {
112     Warn("invalid level position %d in BD cave set", level_pos);
113
114     return FALSE;
115   }
116
117   native_bd_level.cave_nr  = level_pos % gd_caveset_count();
118   native_bd_level.level_nr = level_pos / gd_caveset_count();
119
120   if (native_bd_level.cave != NULL)
121     gd_cave_free(native_bd_level.cave);
122
123   // get selected cave, prepared for playing
124   native_bd_level.cave = gd_get_prepared_cave_from_caveset(native_bd_level.cave_nr,
125                                                            native_bd_level.level_nr);
126
127   // set better initial cave speed (to set better native replay tape length)
128   set_initial_cave_speed(native_bd_level.cave);
129
130   native_bd_level.loaded_from_caveset = TRUE;
131
132   // check if this cave has any replays
133   if (native_bd_level.cave->replays != NULL)
134   {
135     GList *item = native_bd_level.cave->replays;
136
137     // try to find replay that was recorded for this difficulty level
138     while (item != NULL &&
139            (item->data == NULL ||
140             ((GdReplay *)item->data)->success == FALSE ||
141             ((GdReplay *)item->data)->level != native_bd_level.level_nr))
142       item = item->next;
143
144     // matching replay found
145     if (item != NULL)
146       native_bd_level.replay = (GdReplay *)item->data;
147   }
148
149   return TRUE;
150 }
151
152
153 // ============================================================================
154 // game engine functions
155 // ============================================================================
156
157 int map_action_RND_to_BD(int action)
158 {
159   GdDirection player_move = gd_direction_from_keypress(action & JOY_UP,
160                                                        action & JOY_DOWN,
161                                                        action & JOY_LEFT,
162                                                        action & JOY_RIGHT);
163   boolean player_fire = (action & (JOY_BUTTON_1 | JOY_BUTTON_2));
164
165   return (player_move | (player_fire ? GD_REPLAY_FIRE_MASK : 0));
166 }
167
168 int map_action_BD_to_RND(int action)
169 {
170   GdDirection player_move = action & GD_REPLAY_MOVE_MASK;
171   boolean     player_fire = action & GD_REPLAY_FIRE_MASK;
172
173   int action_move = (player_move == GD_MV_UP            ? JOY_UP                :
174                      player_move == GD_MV_UP_RIGHT      ? JOY_UP   | JOY_RIGHT  :
175                      player_move == GD_MV_RIGHT         ?            JOY_RIGHT  :
176                      player_move == GD_MV_DOWN_RIGHT    ? JOY_DOWN | JOY_RIGHT  :
177                      player_move == GD_MV_DOWN          ? JOY_DOWN              :
178                      player_move == GD_MV_DOWN_LEFT     ? JOY_DOWN | JOY_LEFT   :
179                      player_move == GD_MV_LEFT          ?            JOY_LEFT   :
180                      player_move == GD_MV_UP_LEFT       ? JOY_UP   | JOY_LEFT   : JOY_NO_ACTION);
181   int action_fire = (player_fire ? JOY_BUTTON_1 : JOY_NO_ACTION);
182
183   return (action_move | action_fire);
184 }
185
186 boolean checkGameRunning_BD(void)
187 {
188   return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CAVE_RUNNING);
189 }
190
191 boolean checkGamePlaying_BD(void)
192 {
193   return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CAVE_RUNNING &&
194           game_bd.game->cave != NULL && game_bd.game->cave->hatched);
195 }
196
197 boolean checkBonusTime_BD(void)
198 {
199   return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CHECK_BONUS_TIME);
200 }
201
202 int getFramesPerSecond_BD(void)
203 {
204   if (game_bd.game != NULL && game_bd.game->cave != NULL && game_bd.game->cave->pal_timing)
205     return FRAMES_PER_SECOND_NTSC;
206
207   return FRAMES_PER_SECOND_PAL;
208 }
209
210 int getTimeLeft_BD(void)
211 {
212   if (game_bd.game != NULL && game_bd.game->cave != NULL)
213     return gd_cave_time_show(game_bd.game->cave, game_bd.game->cave->time);
214
215   return 0;
216 }
217
218 static void UpdateGameDoorValues_BD(void)
219 {
220   GdCave *cave = game_bd.game->cave;
221   int time_secs = gd_cave_time_show(cave, cave->time);
222   int gems_still_needed = MAX(0, (cave->diamonds_needed - cave->diamonds_collected));
223
224   game_bd.time_played = time_secs;
225   game_bd.gems_still_needed = gems_still_needed;
226   game_bd.score = game_bd.game->player_score;
227
228   if (game.LevelSolved)
229   {
230     // update time and score in panel while counting bonus time
231     game.LevelSolved_CountingTime  = game_bd.time_played;
232     game.LevelSolved_CountingScore = game_bd.score;
233   }
234 }
235
236 unsigned int InitEngineRandom_BD(int seed)
237 {
238   if (seed == NEW_RANDOMIZE)
239   {
240     // get randomly selected seed to render the cave
241     seed = g_random_int_range(0, GD_CAVE_SEED_MAX);
242   }
243
244   game_bd.random_seed = seed;
245
246   return (unsigned int)seed;
247 }
248
249 void InitGameEngine_BD(void)
250 {
251   game_bd.level_solved = FALSE;
252   game_bd.game_over = FALSE;
253   game_bd.cover_screen = FALSE;
254
255   game_bd.time_played = 0;
256   game_bd.gems_still_needed = 0;
257   game_bd.score = 0;
258
259   gd_caveset_last_selected       = native_bd_level.cave_nr;
260   gd_caveset_last_selected_level = native_bd_level.level_nr;
261
262   if (game_bd.game != NULL)
263     gd_game_free(game_bd.game);
264
265   game_bd.game = gd_game_new(native_bd_level.cave_nr, native_bd_level.level_nr);
266
267   // default: start with completely covered playfield
268   int next_state = GAME_INT_START_UNCOVER + 1;
269
270   // when skipping uncovering, start with uncovered playfield
271   if (setup.bd_skip_uncovering)
272     next_state = GAME_INT_LOAD_CAVE + 1;
273
274   // fast-forward game engine until cave loaded (covered or uncovered)
275   while (game_bd.game->state_counter < next_state)
276     play_game_func(game_bd.game, 0);
277
278   // when skipping uncovering, continue with uncovered playfield
279   if (setup.bd_skip_uncovering)
280     game_bd.game->state_counter = GAME_INT_UNCOVER_ALL + 1;
281
282   if (setup.bd_skip_uncovering)
283     gd_scroll(game_bd.game, TRUE, TRUE);
284
285   RedrawPlayfield_BD(TRUE);
286
287   UpdateGameDoorValues_BD();
288 }
289
290 void GameActions_BD(byte action[MAX_PLAYERS])
291 {
292   GdCave *cave = game_bd.game->cave;
293   boolean player_found = FALSE;
294   int player_x = 0;
295   int player_y = 0;
296   int x, y;
297
298   if (cave->getp)
299   {
300     for (y = 0; y < cave->h && !player_found; y++)
301     {
302       for (x = 0; x < cave->w && !player_found; x++)
303       {
304         int element = *cave->getp(cave, x, y);
305
306         if (element == O_PLAYER ||
307             element == O_PLAYER_BOMB ||
308             element == O_PLAYER_STIRRING ||
309             element == O_PLAYER_PNEUMATIC_LEFT ||
310             element == O_PLAYER_PNEUMATIC_RIGHT)
311         {
312           player_x = x;
313           player_y = y;
314
315           player_found = TRUE;
316         }
317       }
318     }
319   }
320
321   UpdateEngineValues(get_scroll_x(),
322                      get_scroll_y(),
323                      player_x,
324                      player_y);
325
326   if (setup.bd_skip_hatching && !game_bd.game->cave->hatched &&
327       game_bd.game->state_counter == GAME_INT_CAVE_RUNNING)
328   {
329     // fast-forward game engine until player hatched
330     while (!game_bd.game->cave->hatched)
331     {
332       play_game_func(game_bd.game, 0);
333
334       // also record or replay tape action during fast-forward
335       action = TapeCorrectAction_BD(action);
336     }
337   }
338
339   play_game_func(game_bd.game, action[0]);
340
341   RedrawPlayfield_BD(FALSE);
342
343   UpdateGameDoorValues_BD();
344 }
345
346
347 // ============================================================================
348 // graphics functions
349 // ============================================================================
350
351 void CoverScreen_BD(void)
352 {
353   game_bd.cover_screen = FALSE;
354
355   if (setup.bd_skip_uncovering)
356     return;
357
358   game_bd.game->state_counter = GAME_INT_COVER_START;
359
360   // play game engine (with normal speed) until cave covered
361   while (game_bd.game->state_counter < GAME_INT_COVER_ALL + 1)
362   {
363     play_game_func(game_bd.game, 0);
364
365     RedrawPlayfield_BD(TRUE);
366
367     BlitScreenToBitmap_BD(backbuffer);
368
369     BackToFront();
370   }
371
372   // stop uncovering loop sound when not using native sound engine
373   FadeSounds();
374 }
375
376 void BlitScreenToBitmap_BD(Bitmap *target_bitmap)
377 {
378   int xsize = SXSIZE;
379   int ysize = SYSIZE;
380   int full_xsize = native_bd_level.cave->w * TILESIZE_VAR;
381   int full_ysize = native_bd_level.cave->h * TILESIZE_VAR;
382   int sx = SX + (full_xsize < xsize ? (xsize - full_xsize) / 2 : 0);
383   int sy = SY + (full_ysize < ysize ? (ysize - full_ysize) / 2 : 0);
384   int sxsize = (full_xsize < xsize ? full_xsize : xsize);
385   int sysize = (full_ysize < ysize ? full_ysize : ysize);
386
387   BlitBitmap(gd_screen_bitmap, target_bitmap, 0, 0, sxsize, sysize, sx, sy);
388 }
389
390 void RedrawPlayfield_BD(boolean force_redraw)
391 {
392   gd_drawcave(gd_screen_bitmap, game_bd.game, force_redraw);
393 }