added saving BD style levels in R'n'D format as native BDCFF files
[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     cave->x1 = 0;
74     cave->y1 = 0;
75     cave->x2 = cave->w - 1;
76     cave->y2 = cave->h - 1;
77   }
78
79   gd_flatten_cave(cave, 0);
80
81   cave->selectable = TRUE;
82   cave->intermission = FALSE;
83
84   native_bd_level.cave = cave;
85   native_bd_level.replay = NULL;
86
87   native_bd_level.cave_nr = 0;
88   native_bd_level.level_nr = 0;
89
90   native_bd_level.loaded_from_caveset = FALSE;
91 }
92
93 void setLevelInfoToDefaults_BD(void)
94 {
95   setLevelInfoToDefaults_BD_Ext(0, 0);
96 }
97
98 boolean LoadNativeLevel_BD(char *filename, int level_pos, boolean level_info_only)
99 {
100   static char *filename_loaded = NULL;
101
102   if (filename_loaded == NULL || !strEqual(filename, filename_loaded))
103   {
104     if (!gd_caveset_load_from_file(filename))
105     {
106       if (!level_info_only)
107         Warn("cannot load BD cave set from file '%s'", filename);
108
109       return FALSE;
110     }
111
112     setString(&filename_loaded, filename);
113   }
114
115   if (level_pos < 0 || level_pos >= 5 * gd_caveset_count())
116   {
117     Warn("invalid level position %d in BD cave set", level_pos);
118
119     return FALSE;
120   }
121
122   native_bd_level.cave_nr  = level_pos % gd_caveset_count();
123   native_bd_level.level_nr = level_pos / gd_caveset_count();
124
125   if (native_bd_level.cave != NULL)
126     gd_cave_free(native_bd_level.cave);
127
128   // get selected cave, prepared for playing
129   native_bd_level.cave = gd_get_prepared_cave_from_caveset(native_bd_level.cave_nr,
130                                                            native_bd_level.level_nr);
131
132   // set better initial cave speed (to set better native replay tape length)
133   set_initial_cave_speed(native_bd_level.cave);
134
135   native_bd_level.loaded_from_caveset = TRUE;
136
137   // check if this cave has any replays
138   if (native_bd_level.cave->replays != NULL)
139   {
140     List *item = native_bd_level.cave->replays;
141
142     // try to find replay that was recorded for this difficulty level
143     while (item != NULL &&
144            (item->data == NULL ||
145             ((GdReplay *)item->data)->success == FALSE ||
146             ((GdReplay *)item->data)->level != native_bd_level.level_nr))
147       item = item->next;
148
149     // matching replay found
150     if (item != NULL)
151       native_bd_level.replay = (GdReplay *)item->data;
152   }
153
154   return TRUE;
155 }
156
157 boolean SaveNativeLevel_BD(char *filename)
158 {
159   GdCave *cave = gd_cave_new_from_cave(native_bd_level.cave);
160
161   gd_caveset_clear();
162   gd_caveset = list_append(gd_caveset, cave);
163
164   return gd_caveset_save_to_file(filename);
165 }
166
167
168 // ============================================================================
169 // game engine functions
170 // ============================================================================
171
172 int map_action_RND_to_BD(int action)
173 {
174   GdDirection player_move = gd_direction_from_keypress(action & JOY_UP,
175                                                        action & JOY_DOWN,
176                                                        action & JOY_LEFT,
177                                                        action & JOY_RIGHT);
178   boolean player_fire = (action & (JOY_BUTTON_1 | JOY_BUTTON_2));
179
180   return (player_move | (player_fire ? GD_REPLAY_FIRE_MASK : 0));
181 }
182
183 int map_action_BD_to_RND(int action)
184 {
185   GdDirection player_move = action & GD_REPLAY_MOVE_MASK;
186   boolean     player_fire = action & GD_REPLAY_FIRE_MASK;
187
188   int action_move = (player_move == GD_MV_UP            ? JOY_UP                :
189                      player_move == GD_MV_UP_RIGHT      ? JOY_UP   | JOY_RIGHT  :
190                      player_move == GD_MV_RIGHT         ?            JOY_RIGHT  :
191                      player_move == GD_MV_DOWN_RIGHT    ? JOY_DOWN | JOY_RIGHT  :
192                      player_move == GD_MV_DOWN          ? JOY_DOWN              :
193                      player_move == GD_MV_DOWN_LEFT     ? JOY_DOWN | JOY_LEFT   :
194                      player_move == GD_MV_LEFT          ?            JOY_LEFT   :
195                      player_move == GD_MV_UP_LEFT       ? JOY_UP   | JOY_LEFT   : JOY_NO_ACTION);
196   int action_fire = (player_fire ? JOY_BUTTON_1 : JOY_NO_ACTION);
197
198   return (action_move | action_fire);
199 }
200
201 boolean checkGameRunning_BD(void)
202 {
203   return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CAVE_RUNNING);
204 }
205
206 boolean checkGamePlaying_BD(void)
207 {
208   return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CAVE_RUNNING &&
209           game_bd.game->cave != NULL && game_bd.game->cave->hatched);
210 }
211
212 boolean checkBonusTime_BD(void)
213 {
214   return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CHECK_BONUS_TIME);
215 }
216
217 int getFramesPerSecond_BD(void)
218 {
219   if (game_bd.game != NULL && game_bd.game->cave != NULL && game_bd.game->cave->pal_timing)
220     return FRAMES_PER_SECOND_NTSC;
221
222   return FRAMES_PER_SECOND_PAL;
223 }
224
225 int getTimeLeft_BD(void)
226 {
227   if (game_bd.game != NULL && game_bd.game->cave != NULL)
228     return gd_cave_time_show(game_bd.game->cave, game_bd.game->cave->time);
229
230   return 0;
231 }
232
233 static void UpdateGameDoorValues_BD(void)
234 {
235   GdCave *cave = game_bd.game->cave;
236   int time_secs = gd_cave_time_show(cave, cave->time);
237   int gems_still_needed = MAX(0, (cave->diamonds_needed - cave->diamonds_collected));
238
239   game_bd.time_played = time_secs;
240   game_bd.gems_still_needed = gems_still_needed;
241   game_bd.score = game_bd.game->player_score;
242
243   if (game.LevelSolved)
244   {
245     // update time and score in panel while counting bonus time
246     game.LevelSolved_CountingTime  = game_bd.time_played;
247     game.LevelSolved_CountingScore = game_bd.score;
248   }
249 }
250
251 unsigned int InitEngineRandom_BD(int seed)
252 {
253   if (seed == NEW_RANDOMIZE)
254   {
255     // get randomly selected seed to render the cave
256     seed = gd_random_int_range(0, GD_CAVE_SEED_MAX);
257   }
258
259   game_bd.random_seed = seed;
260
261   return (unsigned int)seed;
262 }
263
264 void InitGameEngine_BD(void)
265 {
266   game_bd.level_solved = FALSE;
267   game_bd.game_over = FALSE;
268   game_bd.cover_screen = FALSE;
269
270   game_bd.time_played = 0;
271   game_bd.gems_still_needed = 0;
272   game_bd.score = 0;
273
274   gd_caveset_last_selected       = native_bd_level.cave_nr;
275   gd_caveset_last_selected_level = native_bd_level.level_nr;
276
277   if (game_bd.game != NULL)
278     gd_game_free(game_bd.game);
279
280   game_bd.game = gd_game_new(native_bd_level.cave_nr, native_bd_level.level_nr);
281
282   game_bd.game->itercycle = 0;
283   game_bd.game->itermax = 8;    // default; dynamically changed at runtime
284   game_bd.game->itermax_last = game_bd.game->itermax;
285
286   // default: start with completely covered playfield
287   int next_state = GAME_INT_START_UNCOVER + 1;
288
289   // when skipping uncovering, start with uncovered playfield
290   if (setup.bd_skip_uncovering)
291     next_state = GAME_INT_LOAD_CAVE + 1;
292
293   // fast-forward game engine until cave loaded (covered or uncovered)
294   while (game_bd.game->state_counter < next_state)
295     play_game_func(game_bd.game, 0);
296
297   // when skipping uncovering, continue with uncovered playfield
298   if (setup.bd_skip_uncovering)
299     game_bd.game->state_counter = GAME_INT_UNCOVER_ALL + 1;
300
301   if (setup.bd_skip_uncovering)
302     gd_scroll(game_bd.game, TRUE, TRUE);
303
304   ClearRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE);
305
306   RedrawPlayfield_BD(TRUE);
307
308   UpdateGameDoorValues_BD();
309 }
310
311 void GameActions_BD(byte action[MAX_PLAYERS])
312 {
313   GdCave *cave = game_bd.game->cave;
314   boolean player_found = FALSE;
315   int player_x = 0;
316   int player_y = 0;
317   int x, y;
318
319   if (cave->getp)
320   {
321     for (y = 0; y < cave->h && !player_found; y++)
322     {
323       for (x = 0; x < cave->w && !player_found; x++)
324       {
325         int element = *cave->getp(cave, x, y);
326
327         if (element == O_PLAYER ||
328             element == O_PLAYER_BOMB ||
329             element == O_PLAYER_STIRRING ||
330             element == O_PLAYER_PNEUMATIC_LEFT ||
331             element == O_PLAYER_PNEUMATIC_RIGHT)
332         {
333           player_x = x;
334           player_y = y;
335
336           player_found = TRUE;
337         }
338       }
339     }
340   }
341
342   UpdateEngineValues(get_scroll_x(),
343                      get_scroll_y(),
344                      player_x,
345                      player_y);
346
347   if (setup.bd_skip_hatching && !game_bd.game->cave->hatched &&
348       game_bd.game->state_counter == GAME_INT_CAVE_RUNNING)
349   {
350     // fast-forward game engine until player hatched
351     while (!game_bd.game->cave->hatched)
352     {
353       play_game_func(game_bd.game, 0);
354
355       // also record or replay tape action during fast-forward
356       action = TapeCorrectAction_BD(action);
357     }
358   }
359
360   play_game_func(game_bd.game, action[0]);
361
362   RedrawPlayfield_BD(FALSE);
363
364   UpdateGameDoorValues_BD();
365 }
366
367
368 // ============================================================================
369 // graphics functions
370 // ============================================================================
371
372 void CoverScreen_BD(void)
373 {
374   game_bd.cover_screen = FALSE;
375
376   if (setup.bd_skip_uncovering)
377     return;
378
379   game_bd.game->state_counter = GAME_INT_COVER_START;
380
381   // play game engine (with normal speed) until cave covered
382   while (game_bd.game->state_counter < GAME_INT_COVER_ALL + 1)
383   {
384     play_game_func(game_bd.game, 0);
385
386     RedrawPlayfield_BD(TRUE);
387
388     BlitScreenToBitmap_BD(backbuffer);
389
390     BackToFront();
391   }
392
393   // stop uncovering loop sound when not using native sound engine
394   FadeSounds();
395 }
396
397 void BlitScreenToBitmap_BD(Bitmap *target_bitmap)
398 {
399   GdCave *cave = native_bd_level.cave;
400   int xsize = SXSIZE;
401   int ysize = SYSIZE;
402   int full_xsize = (cave->x2 - cave->x1 + 1) * TILESIZE_VAR;
403   int full_ysize = (cave->y2 - cave->y1 + 1) * TILESIZE_VAR;
404   int sx = SX + (full_xsize < xsize ? (xsize - full_xsize) / 2 : 0);
405   int sy = SY + (full_ysize < ysize ? (ysize - full_ysize) / 2 : 0);
406   int sxsize = (full_xsize < xsize ? full_xsize : xsize);
407   int sysize = (full_ysize < ysize ? full_ysize : ysize);
408
409   BlitBitmap(gd_screen_bitmap, target_bitmap, 0, 0, sxsize, sysize, sx, sy);
410 }
411
412 void RedrawPlayfield_BD(boolean force_redraw)
413 {
414   gd_drawcave(gd_screen_bitmap, game_bd.game, force_redraw);
415 }