1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2024 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
15 struct GameInfo_BD game_bd;
16 struct LevelInfo_BD native_bd_level;
17 struct EngineSnapshotInfo_BD engine_snapshot_bd;
20 // ============================================================================
21 // initialization functions
22 // ============================================================================
24 void InitGfxBuffers_BD(void)
26 ReCreateBitmap(&gd_screen_bitmap, SXSIZE, SYSIZE);
28 set_cell_size(TILESIZE_VAR);
29 set_play_area(SXSIZE, SYSIZE);
32 void bd_open_all(void)
39 gd_c64_import_init_tables();
48 void bd_close_all(void)
53 // ============================================================================
54 // level file functions
55 // ============================================================================
57 void setLevelInfoToDefaults_BD_Ext(int width, int height)
59 GdCave *cave = native_bd_level.cave;
64 // get empty cave, using default values
67 // set cave size, if defined
68 if (width > 0 && height > 0)
75 cave->x2 = cave->w - 1;
76 cave->y2 = cave->h - 1;
79 gd_flatten_cave(cave, 0);
81 cave->selectable = TRUE;
82 cave->intermission = FALSE;
84 native_bd_level.cave = cave;
85 native_bd_level.replay = NULL;
87 native_bd_level.cave_nr = 0;
88 native_bd_level.level_nr = 0;
90 native_bd_level.loaded_from_caveset = FALSE;
93 void setLevelInfoToDefaults_BD(void)
95 setLevelInfoToDefaults_BD_Ext(0, 0);
98 static List *getNativeLevelReplay_BD_Ext(List *item, boolean only_successful_replays)
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))
110 static List *getNativeLevelReplay_BD(List *replays)
112 // 1st try: look for successful replay
113 List *item = getNativeLevelReplay_BD_Ext(replays, TRUE);
118 // 2nd try: look for any replay
119 return getNativeLevelReplay_BD_Ext(replays, FALSE);
122 boolean LoadNativeLevel_BD(char *filename, int level_pos, boolean level_info_only)
124 static char *filename_loaded = NULL;
126 if (filename_loaded == NULL || !strEqual(filename, filename_loaded))
128 if (!gd_caveset_load_from_file(filename))
130 if (!level_info_only)
131 Warn("cannot load BD cave set from file '%s'", filename);
136 setString(&filename_loaded, filename);
139 if (level_pos < 0 || level_pos >= 5 * gd_caveset_count())
141 Warn("invalid level position %d in BD cave set", level_pos);
146 native_bd_level.cave_nr = level_pos % gd_caveset_count();
147 native_bd_level.level_nr = level_pos / gd_caveset_count();
149 if (native_bd_level.cave != NULL)
150 gd_cave_free(native_bd_level.cave);
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);
156 // set better initial cave speed (to set better native replay tape length)
157 set_initial_cave_speed(native_bd_level.cave);
159 native_bd_level.loaded_from_caveset = TRUE;
161 // check if this cave has any replays
162 if (native_bd_level.cave->replays != NULL)
164 List *item = getNativeLevelReplay_BD(native_bd_level.cave->replays);
166 // check if any matching replay was found
168 native_bd_level.replay = (GdReplay *)item->data;
174 boolean SaveNativeLevel_BD(char *filename)
176 GdCave *cave = gd_cave_new_from_cave(native_bd_level.cave);
179 gd_caveset = list_append(gd_caveset, cave);
181 return gd_caveset_save_to_file(filename);
184 void DumpLevelset_BD(void)
186 int num_levels_per_cave = (gd_caveset_has_levels() ? 5 : 1);
188 Print("Number of levels: %d\n", num_levels_per_cave * gd_caveset_count());
189 Print("First level number: %d\n", 1);
193 // ============================================================================
194 // game engine functions
195 // ============================================================================
197 int map_action_RND_to_BD(int action)
199 GdDirection player_move = gd_direction_from_keypress(action & JOY_UP,
203 boolean player_fire = (action & (JOY_BUTTON_1 | JOY_BUTTON_2));
205 return (player_move | (player_fire ? GD_REPLAY_FIRE_MASK : 0));
208 int map_action_BD_to_RND(int action)
210 GdDirection player_move = action & GD_REPLAY_MOVE_MASK;
211 boolean player_fire = action & GD_REPLAY_FIRE_MASK;
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);
223 return (action_move | action_fire);
226 boolean checkGameRunning_BD(void)
228 return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CAVE_RUNNING);
231 boolean checkGamePlaying_BD(void)
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);
237 boolean checkBonusTime_BD(void)
239 return (game_bd.game != NULL && game_bd.game->state_counter == GAME_INT_CHECK_BONUS_TIME);
242 int getFramesPerSecond_BD(void)
244 if (game_bd.game != NULL && game_bd.game->cave != NULL && game_bd.game->cave->pal_timing)
245 return FRAMES_PER_SECOND_NTSC;
247 return FRAMES_PER_SECOND_PAL;
250 int getTimeLeft_BD(void)
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);
258 void SetTimeFrames_BD(int frames_played)
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;
266 static void UpdateGameDoorValues_BD(void)
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));
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;
276 if (game.LevelSolved)
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;
284 static void PrepareGameTileBitmap_BD(void)
286 struct GraphicInfo_BD *g_template = &graphic_info_bd_color_template;
287 struct GraphicInfo_BD *g_default = &graphic_info_bd_object[O_STONE][0];
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);
292 // set reference bitmap which should be replaced with prepared bitmap
293 gd_set_tile_bitmap_reference(g_default->bitmap);
296 void PreparePreviewTileBitmap_BD(Bitmap *bitmap, int scale_down_factor)
298 // prepare bitmap using cave from file (with originally defined colors)
299 gd_prepare_tile_bitmap(native_bd_level.cave, bitmap, scale_down_factor);
302 void SetPreviewTileBitmapReference_BD(Bitmap *bitmap)
304 gd_set_tile_bitmap_reference(bitmap);
307 Bitmap *GetPreviewTileBitmap_BD(Bitmap *bitmap)
309 return gd_get_tile_bitmap(bitmap);
312 unsigned int InitEngineRandom_BD(int seed)
314 if (seed == NEW_RANDOMIZE)
316 // get randomly selected seed to render the cave
317 seed = gd_random_int_range(0, GD_CAVE_SEED_MAX);
320 game_bd.random_seed = seed;
322 return (unsigned int)seed;
325 void InitGameEngine_BD(void)
327 game_bd.level_solved = FALSE;
328 game_bd.game_over = FALSE;
329 game_bd.cover_screen = FALSE;
331 gd_caveset_last_selected = native_bd_level.cave_nr;
332 gd_caveset_last_selected_level = native_bd_level.level_nr;
334 if (game_bd.game != NULL)
335 gd_game_free(game_bd.game);
337 game_bd.game = gd_game_new(native_bd_level.cave_nr, native_bd_level.level_nr);
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;
345 game_bd.player_moving = FALSE;
346 game_bd.player_snapping = FALSE;
348 // default: start with completely covered playfield
349 int next_state = GAME_INT_START_UNCOVER + 1;
351 // when skipping uncovering, start with uncovered playfield
352 if (setup.bd_skip_uncovering)
353 next_state = GAME_INT_LOAD_CAVE + 1;
355 // first iteration loads and prepares the cave (may change colors)
356 play_game_func(game_bd.game, 0);
358 // prepare tile bitmap with level-specific colors, if available
359 PrepareGameTileBitmap_BD();
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);
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;
371 if (setup.bd_skip_uncovering || isLevelEditorFastStart())
372 gd_scroll(game_bd.game, TRUE, TRUE);
374 ClearRectangle(gd_screen_bitmap, 0, 0, SXSIZE, SYSIZE);
376 RedrawPlayfield_BD(TRUE);
378 UpdateGameDoorValues_BD();
381 void GameActions_BD(byte action[MAX_PLAYERS])
383 GdCave *cave = game_bd.game->cave;
384 boolean player_found = FALSE;
391 for (y = 0; y < cave->h && !player_found; y++)
393 for (x = 0; x < cave->w && !player_found; x++)
395 int element = *cave->getp(cave, x, y);
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)
412 UpdateEngineValues(get_scroll_x(),
417 if (setup.bd_skip_hatching && !game_bd.game->cave->hatched &&
418 game_bd.game->state_counter == GAME_INT_CAVE_RUNNING)
420 // fast-forward game engine until player hatched
421 while (!game_bd.game->cave->hatched)
423 play_game_func(game_bd.game, 0);
425 // also record or replay tape action during fast-forward
426 action = TapeCorrectAction_BD(action);
430 play_game_func(game_bd.game, action[0]);
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)
436 RedrawPlayfield_BD(TRUE);
438 BlitScreenToBitmap_BD(backbuffer);
442 play_game_func(game_bd.game, action[0]);
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);
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;
454 RedrawPlayfield_BD(FALSE);
456 UpdateGameDoorValues_BD();
460 // ============================================================================
461 // graphics functions
462 // ============================================================================
464 // check if native BD graphics engine requested in custom graphics configuration
465 boolean use_native_bd_graphics_engine(void)
467 return game.use_native_bd_graphics_engine;
470 // check if smooth game element movements selected in setup menu
471 boolean use_bd_smooth_movements(void)
473 return ((setup.bd_smooth_movements == STATE_TRUE) ||
474 (setup.bd_smooth_movements == STATE_AUTO && !use_native_bd_graphics_engine()));
477 // check if player pushing graphics selected in setup menu
478 boolean use_bd_pushing_graphics(void)
480 return ((setup.bd_pushing_graphics == STATE_TRUE) ||
481 (setup.bd_pushing_graphics == STATE_AUTO && !use_native_bd_graphics_engine()));
484 // check if player up/down graphics selected in setup menu
485 boolean use_bd_up_down_graphics(void)
487 return ((setup.bd_up_down_graphics == STATE_TRUE) ||
488 (setup.bd_up_down_graphics == STATE_AUTO && !use_native_bd_graphics_engine()));
491 // check if element falling sounds selected in setup menu
492 boolean use_bd_falling_sounds(void)
494 return ((setup.bd_falling_sounds == STATE_TRUE) ||
495 (setup.bd_falling_sounds == STATE_AUTO && game.use_native_bd_sound_engine));
498 boolean hasColorTemplate_BD(void)
500 return gd_bitmap_has_c64_colors(graphic_info_bd_color_template.bitmap);
503 Bitmap **GetTitleScreenBitmaps_BD(void)
505 Bitmap **title_screen_bitmaps = gd_get_title_screen_bitmaps();
507 if (title_screen_bitmaps == NULL || title_screen_bitmaps[0] == NULL)
510 return title_screen_bitmaps;
513 void CoverScreen_BD(void)
515 game_bd.cover_screen = FALSE;
517 if (setup.bd_skip_uncovering)
520 game_bd.game->state_counter = GAME_INT_COVER_START;
522 // play game engine (with normal speed) until cave covered
523 while (game_bd.game->state_counter < GAME_INT_COVER_ALL + 1)
525 play_game_func(game_bd.game, 0);
527 RedrawPlayfield_BD(TRUE);
529 BlitScreenToBitmap_BD(backbuffer);
534 // stop uncovering loop sound when not using native sound engine
538 void BlitScreenToBitmap_BD(Bitmap *target_bitmap)
540 GdCave *cave = native_bd_level.cave;
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);
550 BlitBitmap(gd_screen_bitmap, target_bitmap, 0, 0, sxsize, sysize, sx, sy);
553 void RedrawPlayfield_BD(boolean force_redraw)
555 gd_drawcave(gd_screen_bitmap, game_bd.game, force_redraw);
559 // ============================================================================
560 // snapshot functions
561 // ============================================================================
563 void SaveEngineSnapshotValues_BD(void)
565 GdGame *game = game_bd.game;
566 GdCave *cave = game_bd.game->cave;
569 engine_snapshot_bd.game = *game;
571 for (y = 0; y < cave->h; y++)
573 for (x = 0; x < cave->w; x++)
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.drawing_buffer[x][y] = game->drawing_buffer[y][x];
578 engine_snapshot_bd.last_drawing_buffer[x][y] = game->last_drawing_buffer[y][x];
579 engine_snapshot_bd.dir_buffer_from[x][y] = game->dir_buffer_from[y][x];
580 engine_snapshot_bd.dir_buffer_to[x][y] = game->dir_buffer_to[y][x];
581 engine_snapshot_bd.gfx_buffer[x][y] = game->gfx_buffer[y][x];
585 engine_snapshot_bd.cave = *cave;
587 for (y = 0; y < cave->h; y++)
589 for (x = 0; x < cave->w; x++)
591 engine_snapshot_bd.map[x][y] = cave->map[y][x];
593 if (cave->hammered_walls_reappear)
594 engine_snapshot_bd.hammered_reappear[x][y] = cave->hammered_reappear[y][x];
599 void LoadEngineSnapshotValues_BD(void)
601 GdGame *game = game_bd.game;
602 GdCave *cave = game_bd.game->cave;
606 engine_snapshot_bd.game.cave = game->cave;
607 engine_snapshot_bd.game.original_cave = game->original_cave;
609 engine_snapshot_bd.game.element_buffer = game->element_buffer;
610 engine_snapshot_bd.game.last_element_buffer = game->last_element_buffer;
611 engine_snapshot_bd.game.drawing_buffer = game->drawing_buffer;
612 engine_snapshot_bd.game.last_drawing_buffer = game->last_drawing_buffer;
613 engine_snapshot_bd.game.dir_buffer_from = game->dir_buffer_from;
614 engine_snapshot_bd.game.dir_buffer_to = game->dir_buffer_to;
615 engine_snapshot_bd.game.gfx_buffer = game->gfx_buffer;
617 *game = engine_snapshot_bd.game;
619 for (y = 0; y < cave->h; y++)
621 for (x = 0; x < cave->w; x++)
623 game->element_buffer[y][x] = engine_snapshot_bd.element_buffer[x][y];
624 game->last_element_buffer[y][x] = engine_snapshot_bd.last_element_buffer[x][y];
625 game->drawing_buffer[y][x] = engine_snapshot_bd.drawing_buffer[x][y];
626 game->last_drawing_buffer[y][x] = engine_snapshot_bd.last_drawing_buffer[x][y];
627 game->dir_buffer_from[y][x] = engine_snapshot_bd.dir_buffer_from[x][y];
628 game->dir_buffer_to[y][x] = engine_snapshot_bd.dir_buffer_to[x][y];
629 game->gfx_buffer[y][x] = engine_snapshot_bd.gfx_buffer[x][y];
634 engine_snapshot_bd.cave.story = cave->story;
635 engine_snapshot_bd.cave.remark = cave->remark;
636 engine_snapshot_bd.cave.tags = cave->tags;
637 engine_snapshot_bd.cave.map = cave->map;
638 engine_snapshot_bd.cave.objects = cave->objects;
639 engine_snapshot_bd.cave.replays = cave->replays;
640 engine_snapshot_bd.cave.random = cave->random;
641 engine_snapshot_bd.cave.objects_order = cave->objects_order;
642 engine_snapshot_bd.cave.hammered_reappear = cave->hammered_reappear;
644 *cave = engine_snapshot_bd.cave;
646 for (y = 0; y < cave->h; y++)
648 for (x = 0; x < cave->w; x++)
650 cave->map[y][x] = engine_snapshot_bd.map[x][y];
652 if (cave->hammered_walls_reappear)
653 cave->hammered_reappear[y][x] = engine_snapshot_bd.hammered_reappear[x][y];
657 gd_scroll(game_bd.game, TRUE, TRUE);