cleanup of unnecessarily convoluted function call
[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_play_area();
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 static List *getNativeLevelReplay_BD_Ext(List *item, boolean only_successful_replays)
99 {
100   // look for replay that was recorded for the current difficulty level
101   while (item != NULL &&
102          (item->data == NULL ||
103           (((GdReplay *)item->data)->success == FALSE && only_successful_replays) ||
104           ((GdReplay *)item->data)->level != native_bd_level.level_nr))
105     item = item->next;
106
107   return item;
108 }
109
110 static List *getNativeLevelReplay_BD(List *replays)
111 {
112   // 1st try: look for successful replay
113   List *item = getNativeLevelReplay_BD_Ext(replays, TRUE);
114
115   if (item != NULL)
116     return item;
117
118   // 2nd try: look for any replay
119   return getNativeLevelReplay_BD_Ext(replays, FALSE);
120 }
121
122 boolean LoadNativeLevel_BD(char *filename, int level_pos, boolean level_info_only)
123 {
124   static char *filename_loaded = NULL;
125
126   if (filename_loaded == NULL || !strEqual(filename, filename_loaded))
127   {
128     if (!gd_caveset_load_from_file(filename))
129     {
130       if (!level_info_only)
131         Warn("cannot load BD cave set from file '%s'", filename);
132
133       return FALSE;
134     }
135
136     setString(&filename_loaded, filename);
137   }
138
139   if (level_pos < 0 || level_pos >= 5 * gd_caveset_count())
140   {
141     Warn("invalid level position %d in BD cave set", level_pos);
142
143     return FALSE;
144   }
145
146   native_bd_level.cave_nr  = level_pos % gd_caveset_count();
147   native_bd_level.level_nr = level_pos / gd_caveset_count();
148
149   if (native_bd_level.cave != NULL)
150     gd_cave_free(native_bd_level.cave);
151
152   // get selected cave, prepared for playing
153   native_bd_level.cave = gd_get_prepared_cave_from_caveset(native_bd_level.cave_nr,
154                                                            native_bd_level.level_nr);
155
156   // set better initial cave speed (to set better native replay tape length)
157   set_initial_cave_speed(native_bd_level.cave);
158
159   native_bd_level.loaded_from_caveset = TRUE;
160
161   // check if this cave has any replays
162   if (native_bd_level.cave->replays != NULL)
163   {
164     List *item = getNativeLevelReplay_BD(native_bd_level.cave->replays);
165
166     // check if any matching replay was found
167     if (item != NULL)
168       native_bd_level.replay = (GdReplay *)item->data;
169   }
170
171   return TRUE;
172 }
173
174 boolean SaveNativeLevel_BD(char *filename)
175 {
176   GdCave *cave = gd_cave_new_from_cave(native_bd_level.cave);
177
178   gd_caveset_clear();
179   gd_caveset = list_append(gd_caveset, cave);
180
181   return gd_caveset_save_to_file(filename);
182 }
183
184 void DumpLevelset_BD(void)
185 {
186   int num_levels_per_cave = (gd_caveset_has_levels() ? 5 : 1);
187
188   Print("Number of levels:   %d\n", num_levels_per_cave * gd_caveset_count());
189   Print("First level number: %d\n", 1);
190 }
191
192
193 // ============================================================================
194 // game engine functions
195 // ============================================================================
196
197 int map_action_RND_to_BD(int action)
198 {
199   GdDirection player_move = gd_direction_from_keypress(action & JOY_UP,
200                                                        action & JOY_DOWN,
201                                                        action & JOY_LEFT,
202                                                        action & JOY_RIGHT);
203   boolean player_fire = (action & (JOY_BUTTON_1 | JOY_BUTTON_2));
204
205   return (player_move | (player_fire ? GD_REPLAY_FIRE_MASK : 0));
206 }
207
208 int map_action_BD_to_RND(int action)
209 {
210   GdDirection player_move = action & GD_REPLAY_MOVE_MASK;
211   boolean     player_fire = action & GD_REPLAY_FIRE_MASK;
212
213   int action_move = (player_move == GD_MV_UP            ? JOY_UP                :
214                      player_move == GD_MV_UP_RIGHT      ? JOY_UP   | JOY_RIGHT  :
215                      player_move == GD_MV_RIGHT         ?            JOY_RIGHT  :
216                      player_move == GD_MV_DOWN_RIGHT    ? JOY_DOWN | JOY_RIGHT  :
217                      player_move == GD_MV_DOWN          ? JOY_DOWN              :
218                      player_move == GD_MV_DOWN_LEFT     ? JOY_DOWN | JOY_LEFT   :
219                      player_move == GD_MV_LEFT          ?            JOY_LEFT   :
220                      player_move == GD_MV_UP_LEFT       ? JOY_UP   | JOY_LEFT   : JOY_NO_ACTION);
221   int action_fire = (player_fire ? JOY_BUTTON_1 : JOY_NO_ACTION);
222
223   return (action_move | action_fire);
224 }
225
226 boolean checkGameRunning_BD(void)
227 {
228   return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CAVE_RUNNING);
229 }
230
231 boolean checkGamePlaying_BD(void)
232 {
233   return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CAVE_RUNNING &&
234           game_bd.game->cave != NULL && game_bd.game->cave->hatched);
235 }
236
237 boolean checkBonusTime_BD(void)
238 {
239   return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CHECK_BONUS_TIME);
240 }
241
242 int getFramesPerSecond_BD(void)
243 {
244   if (game_bd.game != NULL && game_bd.game->cave != NULL && game_bd.game->cave->pal_timing)
245     return FRAMES_PER_SECOND_NTSC;
246
247   return FRAMES_PER_SECOND_PAL;
248 }
249
250 int getTimeLeft_BD(void)
251 {
252   if (game_bd.game != NULL && game_bd.game->cave != NULL)
253     return gd_cave_time_show(game_bd.game->cave, game_bd.game->cave->time);
254
255   return 0;
256 }
257
258 void SetTimeFrames_BD(int frames_played)
259 {
260   // needed to store final time after solving game (before counting down remaining time)
261   if (game_bd.game->state_counter == GAME_INT_CAVE_RUNNING)
262     game_bd.frames_played = frames_played;
263
264 }
265
266 static void UpdateGameDoorValues_BD(void)
267 {
268   GdCave *cave = game_bd.game->cave;
269   int time_left = gd_cave_time_show(cave, cave->time);
270   int gems_still_needed = MAX(0, (cave->diamonds_needed - cave->diamonds_collected));
271
272   game_bd.time_left = time_left;
273   game_bd.gems_still_needed = gems_still_needed;
274   game_bd.score = game_bd.game->player_score;
275
276   if (game.LevelSolved)
277   {
278     // update time and score in panel while counting bonus time
279     game.LevelSolved_CountingTime  = game_bd.time_left;
280     game.LevelSolved_CountingScore = game_bd.score;
281   }
282 }
283
284 static void PrepareGameTileBitmap_BD(void)
285 {
286   struct GraphicInfo_BD *g_template = &graphic_info_bd_color_template;
287   struct GraphicInfo_BD *g_default  = &graphic_info_bd_object[O_STONE][0];
288
289   // prepare bitmap using cave ready for playing (may have changed colors)
290   gd_prepare_tile_bitmap(game_bd.game->cave, g_template->bitmap, 1);
291
292   // set reference bitmap which should be replaced with prepared bitmap
293   gd_set_tile_bitmap_reference(g_default->bitmap);
294 }
295
296 void PreparePreviewTileBitmap_BD(Bitmap *bitmap, int scale_down_factor)
297 {
298   // prepare bitmap using cave from file (with originally defined colors)
299   gd_prepare_tile_bitmap(native_bd_level.cave, bitmap, scale_down_factor);
300 }
301
302 void SetPreviewTileBitmapReference_BD(Bitmap *bitmap)
303 {
304   gd_set_tile_bitmap_reference(bitmap);
305 }
306
307 Bitmap *GetPreviewTileBitmap_BD(Bitmap *bitmap)
308 {
309   return gd_get_tile_bitmap(bitmap);
310 }
311
312 unsigned int InitEngineRandom_BD(int seed)
313 {
314   if (seed == NEW_RANDOMIZE)
315   {
316     // get randomly selected seed to render the cave
317     seed = gd_random_int_range(0, GD_CAVE_SEED_MAX);
318   }
319
320   game_bd.random_seed = seed;
321
322   return (unsigned int)seed;
323 }
324
325 void InitGameEngine_BD(void)
326 {
327   game_bd.level_solved = FALSE;
328   game_bd.game_over = FALSE;
329   game_bd.cover_screen = FALSE;
330
331   gd_caveset_last_selected       = native_bd_level.cave_nr;
332   gd_caveset_last_selected_level = native_bd_level.level_nr;
333
334   if (game_bd.game != NULL)
335     gd_game_free(game_bd.game);
336
337   game_bd.game = gd_game_new(native_bd_level.cave_nr, native_bd_level.level_nr);
338
339   game_bd.game->itercycle = 0;
340   game_bd.game->itermax = 8;    // default; dynamically changed at runtime
341   game_bd.game->itermax_last = game_bd.game->itermax;
342   game_bd.game->itermax2[0] = game_bd.game->itermax;
343   game_bd.game->itermax2[1] = game_bd.game->itermax;
344
345   game_bd.player_moving = FALSE;
346   game_bd.player_snapping = FALSE;
347
348   // default: start with completely covered playfield
349   int next_state = GAME_INT_START_UNCOVER + 1;
350
351   // when skipping uncovering, start with uncovered playfield
352   if (setup.bd_skip_uncovering)
353     next_state = GAME_INT_LOAD_CAVE + 1;
354
355   // first iteration loads and prepares the cave (may change colors)
356   play_game_func(game_bd.game, 0);
357
358   // prepare tile bitmap with level-specific colors, if available
359   PrepareGameTileBitmap_BD();
360
361   // fast-forward game engine to selected state (covered or uncovered)
362   while (game_bd.game->state_counter < next_state)
363     play_game_func(game_bd.game, 0);
364
365   // when skipping uncovering, continue with uncovered playfield
366   if (setup.bd_skip_uncovering)
367     game_bd.game->state_counter = GAME_INT_UNCOVER_ALL + 1;
368   else if (isLevelEditorFastStart())
369     game_bd.game->state_counter = GAME_INT_UNCOVER_ALL - 8;
370
371   if (setup.bd_skip_uncovering || isLevelEditorFastStart())
372     gd_scroll(game_bd.game, TRUE, TRUE);
373
374   ClearRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE);
375
376   RedrawPlayfield_BD(TRUE);
377
378   UpdateGameDoorValues_BD();
379 }
380
381 void GameActions_BD(byte action[MAX_PLAYERS])
382 {
383   GdCave *cave = game_bd.game->cave;
384   boolean player_found = FALSE;
385   int player_x = 0;
386   int player_y = 0;
387   int x, y;
388
389   if (cave->getp)
390   {
391     for (y = 0; y < cave->h && !player_found; y++)
392     {
393       for (x = 0; x < cave->w && !player_found; x++)
394       {
395         int element = *cave->getp(cave, x, y);
396
397         if (element == O_PLAYER ||
398             element == O_PLAYER_BOMB ||
399             element == O_PLAYER_STIRRING ||
400             element == O_PLAYER_PNEUMATIC_LEFT ||
401             element == O_PLAYER_PNEUMATIC_RIGHT)
402         {
403           player_x = x;
404           player_y = y;
405
406           player_found = TRUE;
407         }
408       }
409     }
410   }
411
412   UpdateEngineValues(get_scroll_x(),
413                      get_scroll_y(),
414                      player_x,
415                      player_y);
416
417   if (setup.bd_skip_hatching && !game_bd.game->cave->hatched &&
418       game_bd.game->state_counter == GAME_INT_CAVE_RUNNING)
419   {
420     // fast-forward game engine until player hatched
421     while (!game_bd.game->cave->hatched)
422     {
423       play_game_func(game_bd.game, 0);
424
425       // also record or replay tape action during fast-forward
426       action = TapeCorrectAction_BD(action);
427     }
428   }
429
430   play_game_func(game_bd.game, action[0]);
431
432   // scroll without iterating engine if player out of sight (mainly due to wrap-around)
433   // (this is needed to prevent broken tapes in case of viewport or tile size changes)
434   while (game_bd.game->out_of_window)
435   {
436     RedrawPlayfield_BD(TRUE);
437
438     BlitScreenToBitmap_BD(backbuffer);
439
440     BackToFront();
441
442     play_game_func(game_bd.game, action[0]);
443   }
444
445   boolean single_step_mode_paused =
446     CheckSingleStepMode_BD(check_iteration_reached(game_bd.game),
447                            game_bd.player_moving,
448                            game_bd.player_snapping);
449
450   // draw final movement animation frame before going to single step pause mode
451   if (single_step_mode_paused)
452     game_bd.game->itercycle = game_bd.game->itermax - 1;
453
454   RedrawPlayfield_BD(FALSE);
455
456   UpdateGameDoorValues_BD();
457 }
458
459
460 // ============================================================================
461 // graphics functions
462 // ============================================================================
463
464 // check if native BD graphics engine requested in custom graphics configuration
465 boolean use_native_bd_graphics_engine(void)
466 {
467   return game.use_native_bd_graphics_engine;
468 }
469
470 // check if smooth game element movements selected in setup menu
471 boolean use_bd_smooth_movements(void)
472 {
473   return ((setup.bd_smooth_movements == STATE_TRUE) ||
474           (setup.bd_smooth_movements == STATE_AUTO && !use_native_bd_graphics_engine()));
475 }
476
477 // check if player pushing graphics selected in setup menu
478 boolean use_bd_pushing_graphics(void)
479 {
480   return ((setup.bd_pushing_graphics == STATE_TRUE) ||
481           (setup.bd_pushing_graphics == STATE_AUTO && !use_native_bd_graphics_engine()));
482 }
483
484 // check if player up/down graphics selected in setup menu
485 boolean use_bd_up_down_graphics(void)
486 {
487   return ((setup.bd_up_down_graphics == STATE_TRUE) ||
488           (setup.bd_up_down_graphics == STATE_AUTO && !use_native_bd_graphics_engine()));
489 }
490
491 // check if element falling sounds selected in setup menu
492 boolean use_bd_falling_sounds(void)
493 {
494   return ((setup.bd_falling_sounds == STATE_TRUE) ||
495           (setup.bd_falling_sounds == STATE_AUTO && game.use_native_bd_sound_engine));
496 }
497
498 boolean hasColorTemplate_BD(void)
499 {
500   return gd_bitmap_has_c64_colors(graphic_info_bd_color_template.bitmap);
501 }
502
503 Bitmap **GetTitleScreenBitmaps_BD(void)
504 {
505   Bitmap **title_screen_bitmaps = gd_get_title_screen_bitmaps();
506
507   if (title_screen_bitmaps == NULL || title_screen_bitmaps[0] == NULL)
508     return NULL;
509
510   return title_screen_bitmaps;
511 }
512
513 void CoverScreen_BD(void)
514 {
515   game_bd.cover_screen = FALSE;
516
517   if (setup.bd_skip_uncovering)
518     return;
519
520   game_bd.game->state_counter = GAME_INT_COVER_START;
521
522   // play game engine (with normal speed) until cave covered
523   while (game_bd.game->state_counter < GAME_INT_COVER_ALL + 1)
524   {
525     play_game_func(game_bd.game, 0);
526
527     RedrawPlayfield_BD(TRUE);
528
529     BlitScreenToBitmap_BD(backbuffer);
530
531     BackToFront();
532   }
533
534   // stop uncovering loop sound when not using native sound engine
535   FadeSounds();
536 }
537
538 void BlitScreenToBitmap_BD(Bitmap *target_bitmap)
539 {
540   GdCave *cave = native_bd_level.cave;
541   int xsize = SXSIZE;
542   int ysize = SYSIZE;
543   int full_xsize = (cave->x2 - cave->x1 + 1) * TILESIZE_VAR;
544   int full_ysize = (cave->y2 - cave->y1 + 1) * TILESIZE_VAR;
545   int sx = SX + (full_xsize < xsize ? (xsize - full_xsize) / 2 : 0);
546   int sy = SY + (full_ysize < ysize ? (ysize - full_ysize) / 2 : 0);
547   int sxsize = (full_xsize < xsize ? full_xsize : xsize);
548   int sysize = (full_ysize < ysize ? full_ysize : ysize);
549
550   BlitBitmap(gd_screen_bitmap, target_bitmap, 0, 0, sxsize, sysize, sx, sy);
551 }
552
553 void RedrawPlayfield_BD(boolean force_redraw)
554 {
555   gd_drawcave(gd_screen_bitmap, game_bd.game, force_redraw);
556 }
557
558
559 // ============================================================================
560 // snapshot functions
561 // ============================================================================
562
563 void SaveEngineSnapshotValues_BD(void)
564 {
565   GdGame *game = game_bd.game;
566   GdCave *cave = game_bd.game->cave;
567   int x, y;
568
569   engine_snapshot_bd.game = *game;
570
571   for (y = 0; y < cave->h; y++)
572   {
573     for (x = 0; x < cave->w; x++)
574     {
575       engine_snapshot_bd.element_buffer[x][y]      = game->element_buffer[y][x];
576       engine_snapshot_bd.last_element_buffer[x][y] = game->last_element_buffer[y][x];
577       engine_snapshot_bd.dir_buffer_from[x][y]     = game->dir_buffer_from[y][x];
578       engine_snapshot_bd.dir_buffer_to[x][y]       = game->dir_buffer_to[y][x];
579       engine_snapshot_bd.gfx_buffer[x][y]          = game->gfx_buffer[y][x];
580     }
581   }
582
583   engine_snapshot_bd.cave = *cave;
584
585   for (y = 0; y < cave->h; y++)
586   {
587     for (x = 0; x < cave->w; x++)
588     {
589       engine_snapshot_bd.map[x][y] = cave->map[y][x];
590
591       if (cave->hammered_walls_reappear)
592         engine_snapshot_bd.hammered_reappear[x][y] = cave->hammered_reappear[y][x];
593     }
594   }
595 }
596
597 void LoadEngineSnapshotValues_BD(void)
598 {
599   GdGame *game = game_bd.game;
600   GdCave *cave = game_bd.game->cave;
601   int x, y;
602
603   // copy pointers
604   engine_snapshot_bd.game.cave                = game->cave;
605   engine_snapshot_bd.game.original_cave       = game->original_cave;
606
607   engine_snapshot_bd.game.element_buffer      = game->element_buffer;
608   engine_snapshot_bd.game.last_element_buffer = game->last_element_buffer;
609   engine_snapshot_bd.game.dir_buffer_from     = game->dir_buffer_from;
610   engine_snapshot_bd.game.dir_buffer_to       = game->dir_buffer_to;
611   engine_snapshot_bd.game.gfx_buffer          = game->gfx_buffer;
612
613   *game = engine_snapshot_bd.game;
614
615   for (y = 0; y < cave->h; y++)
616   {
617     for (x = 0; x < cave->w; x++)
618     {
619       game->element_buffer[y][x]      = engine_snapshot_bd.element_buffer[x][y];
620       game->last_element_buffer[y][x] = engine_snapshot_bd.last_element_buffer[x][y];
621       game->dir_buffer_from[y][x]     = engine_snapshot_bd.dir_buffer_from[x][y];
622       game->dir_buffer_to[y][x]       = engine_snapshot_bd.dir_buffer_to[x][y];
623       game->gfx_buffer[y][x]          = engine_snapshot_bd.gfx_buffer[x][y];
624     }
625   }
626
627   // copy pointers
628   engine_snapshot_bd.cave.story             = cave->story;
629   engine_snapshot_bd.cave.remark            = cave->remark;
630   engine_snapshot_bd.cave.tags              = cave->tags;
631   engine_snapshot_bd.cave.map               = cave->map;
632   engine_snapshot_bd.cave.objects           = cave->objects;
633   engine_snapshot_bd.cave.replays           = cave->replays;
634   engine_snapshot_bd.cave.random            = cave->random;
635   engine_snapshot_bd.cave.objects_order     = cave->objects_order;
636   engine_snapshot_bd.cave.hammered_reappear = cave->hammered_reappear;
637
638   *cave = engine_snapshot_bd.cave;
639
640   for (y = 0; y < cave->h; y++)
641   {
642     for (x = 0; x < cave->w; x++)
643     {
644       cave->map[y][x] = engine_snapshot_bd.map[x][y];
645
646       if (cave->hammered_walls_reappear)
647         cave->hammered_reappear[y][x] = engine_snapshot_bd.hammered_reappear[x][y];
648     }
649   }
650
651   gd_scroll(game_bd.game, TRUE, TRUE);
652 }