X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=d8a3d16270af01b54ed8dcff09072d13f79aff4a;hp=a7aa775237a91e4c643aae0f277ab72967577586;hb=580cc0293954391cf0cfd2f99638d8134f88f76c;hpb=4cd59cef0737229da365e385a8762e681a5e471f diff --git a/src/game.c b/src/game.c index a7aa7752..d8a3d162 100644 --- a/src/game.c +++ b/src/game.c @@ -1119,6 +1119,8 @@ void ExitPlayer(struct PlayerInfo *); static int getInvisibleActiveFromInvisibleElement(int); static int getInvisibleFromInvisibleActiveElement(int); +static void TestFieldAfterSnapping(int, int, int, int, int); + static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS]; // for detection of endless loops, caused by custom element programming @@ -1769,13 +1771,11 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) StorePlayer[x][y] = Tile[x][y]; #if DEBUG_INIT_PLAYER - if (options.debug) - { - printf("- player element %d activated", player->element_nr); - printf(" (local player is %d and currently %s)\n", - local_player->element_nr, - local_player->active ? "active" : "not active"); - } + Debug("game:init:player", "- player element %d activated", + player->element_nr); + Debug("game:init:player", " (local player is %d and currently %s)", + local_player->element_nr, + local_player->active ? "active" : "not active"); } #endif @@ -2149,8 +2149,9 @@ static void InitGameControlValues(void) if (nr != i) { - Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i); - Error(ERR_EXIT, "this should not happen -- please debug"); + Error("'game_panel_controls' structure corrupted at %d", i); + + Fail("this should not happen -- please debug"); } // force update of game controls after initialization @@ -2252,6 +2253,7 @@ static void UpdateGameControlValues(void) level.game_engine_type == GAME_ENGINE_TYPE_MM ? MM_HEALTH(game_mm.laser_overload_value) : game.health); + int sync_random_frame = INIT_GFX_RANDOM(); // random, but synchronized UpdatePlayfieldElementCount(); @@ -2326,6 +2328,63 @@ static void UpdateGameControlValues(void) stored_player[player_nr].num_white_keys; } + // re-arrange keys on game panel, if needed or if defined by style settings + for (i = 0; i < MAX_NUM_KEYS + 1; i++) // all normal keys + white key + { + int nr = GAME_PANEL_KEY_1 + i; + struct GamePanelControlInfo *gpc = &game_panel_controls[nr]; + struct TextPosInfo *pos = gpc->pos; + + // skip check if key is not in the player's inventory + if (gpc->value == EL_EMPTY) + continue; + + // check if keys should be arranged on panel from left to right + if (pos->style == STYLE_LEFTMOST_POSITION) + { + // check previous key positions (left from current key) + for (k = 0; k < i; k++) + { + int nr_new = GAME_PANEL_KEY_1 + k; + + if (game_panel_controls[nr_new].value == EL_EMPTY) + { + game_panel_controls[nr_new].value = gpc->value; + gpc->value = EL_EMPTY; + + break; + } + } + } + + // check if "undefined" keys can be placed at some other position + if (pos->x == -1 && pos->y == -1) + { + int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS; + + // 1st try: display key at the same position as normal or EM keys + if (game_panel_controls[nr_new].value == EL_EMPTY) + { + game_panel_controls[nr_new].value = gpc->value; + } + else + { + // 2nd try: display key at the next free position in the key panel + for (k = 0; k < STD_NUM_KEYS; k++) + { + nr_new = GAME_PANEL_KEY_1 + k; + + if (game_panel_controls[nr_new].value == EL_EMPTY) + { + game_panel_controls[nr_new].value = gpc->value; + + break; + } + } + } + } + } + for (i = 0; i < NUM_PANEL_INVENTORY; i++) { game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value = @@ -2479,11 +2538,13 @@ static void UpdateGameControlValues(void) int last_anim_random_frame = gfx.anim_random_frame; int element = gpc->value; int graphic = el2panelimg(element); + int init_gfx_random = (graphic_info[graphic].anim_global_sync ? + sync_random_frame : INIT_GFX_RANDOM()); if (gpc->value != gpc->last_value) { gpc->gfx_frame = 0; - gpc->gfx_random = INIT_GFX_RANDOM(); + gpc->gfx_random = init_gfx_random; } else { @@ -2491,7 +2552,7 @@ static void UpdateGameControlValues(void) if (ANIM_MODE(graphic) == ANIM_RANDOM && IS_NEXT_FRAME(gpc->gfx_frame, graphic)) - gpc->gfx_random = INIT_GFX_RANDOM(); + gpc->gfx_random = init_gfx_random; } if (ANIM_MODE(graphic) == ANIM_RANDOM) @@ -2500,8 +2561,7 @@ static void UpdateGameControlValues(void) if (ANIM_MODE(graphic) == ANIM_CE_SCORE) gpc->gfx_frame = element_info[element].collect_score; - gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value), - gpc->gfx_frame); + gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame); if (ANIM_MODE(graphic) == ANIM_RANDOM) gfx.anim_random_frame = last_anim_random_frame; @@ -2513,11 +2573,13 @@ static void UpdateGameControlValues(void) { int last_anim_random_frame = gfx.anim_random_frame; int graphic = gpc->graphic; + int init_gfx_random = (graphic_info[graphic].anim_global_sync ? + sync_random_frame : INIT_GFX_RANDOM()); if (gpc->value != gpc->last_value) { gpc->gfx_frame = 0; - gpc->gfx_random = INIT_GFX_RANDOM(); + gpc->gfx_random = init_gfx_random; } else { @@ -2525,7 +2587,7 @@ static void UpdateGameControlValues(void) if (ANIM_MODE(graphic) == ANIM_RANDOM && IS_NEXT_FRAME(gpc->gfx_frame, graphic)) - gpc->gfx_random = INIT_GFX_RANDOM(); + gpc->gfx_random = init_gfx_random; } if (ANIM_MODE(graphic) == ANIM_RANDOM) @@ -2590,6 +2652,10 @@ static void DisplayGameControlValues(void) if (PANEL_DEACTIVATED(pos)) continue; + if (pos->class == get_hash_from_key("extra_panel_items") && + !setup.prefer_extra_panel_items) + continue; + gpc->last_value = value; gpc->last_frame = frame; @@ -2637,7 +2703,10 @@ static void DisplayGameControlValues(void) element = value; graphic = el2panelimg(value); - // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size); +#if 0 + Debug("game:DisplayGameControlValues", "%d, '%s' [%d]", + element, EL_NAME(element), size); +#endif if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0) size = TILESIZE; @@ -2800,12 +2869,10 @@ void UpdateAndDisplayGameControlValues(void) DisplayGameControlValues(); } -#if 0 -static void UpdateGameDoorValues(void) +void UpdateGameDoorValues(void) { UpdateGameControlValues(); } -#endif void DrawGameDoorValues(void) { @@ -2843,16 +2910,16 @@ static void InitGameEngine(void) } #if 0 - printf("level %d: level.game_version == %06d\n", level_nr, - level.game_version); - printf(" tape.file_version == %06d\n", - tape.file_version); - printf(" tape.game_version == %06d\n", - tape.game_version); - printf(" tape.engine_version == %06d\n", - tape.engine_version); - printf(" => game.engine_version == %06d [tape mode: %s]\n", - game.engine_version, (tape.playing ? "PLAYING" : "RECORDING")); + Debug("game:init:level", "level %d: level.game_version == %06d", level_nr, + level.game_version); + Debug("game:init:level", " tape.file_version == %06d", + tape.file_version); + Debug("game:init:level", " tape.game_version == %06d", + tape.game_version); + Debug("game:init:level", " tape.engine_version == %06d", + tape.engine_version); + Debug("game:init:level", " => game.engine_version == %06d [tape mode: %s]", + game.engine_version, (tape.playing ? "PLAYING" : "RECORDING")); #endif // -------------------------------------------------------------------------- @@ -3441,24 +3508,21 @@ static void DebugPrintPlayerStatus(char *message) if (!options.debug) return; - printf("%s:\n", message); + Debug("game:init:player", "%s:", message); for (i = 0; i < MAX_PLAYERS; i++) { struct PlayerInfo *player = &stored_player[i]; - printf("- player %d: present == %d, connected == %d [%d/%d], active == %d", - i + 1, - player->present, - player->connected, - player->connected_locally, - player->connected_network, - player->active); - - if (local_player == player) - printf(" (local player)"); - - printf("\n"); + Debug("game:init:player", + "- player %d: present == %d, connected == %d [%d/%d], active == %d%s", + i + 1, + player->present, + player->connected, + player->connected_locally, + player->connected_network, + player->active, + (local_player == player ? " (local player)" : "")); } } #endif @@ -3512,11 +3576,15 @@ void InitGame(void) InitGameEngine(); InitGameControlValues(); - // initialize tape actions from game when recording tape if (tape.recording) { + // initialize tape actions from game when recording tape tape.use_key_actions = game.use_key_actions; tape.use_mouse_actions = game.use_mouse_actions; + + // initialize visible playfield size when recording tape (for team mode) + tape.scr_fieldx = SCR_FIELDX; + tape.scr_fieldy = SCR_FIELDY; } // don't play tapes over network @@ -3649,6 +3717,8 @@ void InitGame(void) player->shield_normal_time_left = 0; player->shield_deadly_time_left = 0; + player->last_removed_element = EL_UNDEFINED; + player->inventory_infinite_element = EL_UNDEFINED; player->inventory_size = 0; @@ -3934,8 +4004,7 @@ void InitGame(void) #endif #if DEBUG_INIT_PLAYER - if (options.debug) - printf("Reassigning players ...\n"); + Debug("game:init:player", "Reassigning players ..."); #endif // check if any connected player was not found in playfield @@ -3948,8 +4017,8 @@ void InitGame(void) struct PlayerInfo *field_player = NULL; #if DEBUG_INIT_PLAYER - if (options.debug) - printf("- looking for field player for player %d ...\n", i + 1); + Debug("game:init:player", + "- looking for field player for player %d ...", i + 1); #endif // assign first free player found that is present in the playfield @@ -3974,8 +4043,8 @@ void InitGame(void) int jx = field_player->jx, jy = field_player->jy; #if DEBUG_INIT_PLAYER - if (options.debug) - printf("- found player %d\n", field_player->index_nr + 1); + Debug("game:init:player", "- found player %d", + field_player->index_nr + 1); #endif player->present = FALSE; @@ -4005,9 +4074,8 @@ void InitGame(void) field_player->mapped = TRUE; #if DEBUG_INIT_PLAYER - if (options.debug) - printf("- map_player_action[%d] == %d\n", - field_player->index_nr + 1, i + 1); + Debug("game:init:player", "- map_player_action[%d] == %d", + field_player->index_nr + 1, i + 1); #endif } } @@ -4062,7 +4130,8 @@ void InitGame(void) #endif #if 0 - printf("::: local_player->present == %d\n", local_player->present); + Debug("game:init:player", "local_player->present == %d", + local_player->present); #endif // set focus to local player for network games, else to all players @@ -4095,8 +4164,8 @@ void InitGame(void) int jx = player->jx, jy = player->jy; #if DEBUG_INIT_PLAYER - if (options.debug) - printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy); + Debug("game:init:player", "Removing player %d at (%d, %d)", + i + 1, jx, jy); #endif player->active = FALSE; @@ -4384,7 +4453,9 @@ void InitGame(void) game.restart_level = FALSE; game.restart_game_message = NULL; + game.request_active = FALSE; + game.request_active_or_moving = FALSE; if (level.game_engine_type == GAME_ENGINE_TYPE_MM) InitGameActions_MM(); @@ -4645,7 +4716,7 @@ void GameWon(void) { static int time_count_steps; static int time, time_final; - static int score, score_final; + static float score, score_final; // needed for time score < 10 for 10 seconds static int health, health_final; static int game_over_delay_1 = 0; static int game_over_delay_2 = 0; @@ -4653,6 +4724,8 @@ void GameWon(void) int game_over_delay_value_1 = 50; int game_over_delay_value_2 = 25; int game_over_delay_value_3 = 50; + int time_score_base = MIN(MAX(1, level.time_score_base), 10); + float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base; if (!game.LevelSolved_GameWon) { @@ -4686,19 +4759,23 @@ void GameWon(void) score = score_final = game.score_final; health = health_final = game.health_final; - if (level.score[SC_TIME_BONUS] > 0) + if (time_score > 0) { + int time_frames = 0; + if (TimeLeft > 0) { time_final = 0; - score_final += TimeLeft * level.score[SC_TIME_BONUS]; + time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames; } else if (game.no_time_limit && TimePlayed < 999) { time_final = 999; - score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS]; + time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames; } + score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5; + time_count_steps = MAX(1, ABS(time_final - time) / 100); game_over_delay_1 = game_over_delay_value_1; @@ -4706,7 +4783,7 @@ void GameWon(void) if (level.game_engine_type == GAME_ENGINE_TYPE_MM) { health_final = 0; - score_final += health * level.score[SC_TIME_BONUS]; + score_final += health * time_score; game_over_delay_2 = game_over_delay_value_2; } @@ -4795,7 +4872,11 @@ void GameWon(void) time_count_steps = 1; time += time_count_steps * time_count_dir; - score += time_count_steps * level.score[SC_TIME_BONUS]; + score += time_count_steps * time_score; + + // set final score to correct rounding differences after counting score + if (time == time_final) + score = score_final; game.LevelSolved_CountingTime = time; game.LevelSolved_CountingScore = score; @@ -4827,7 +4908,7 @@ void GameWon(void) int health_count_dir = (health < health_final ? +1 : -1); health += health_count_dir; - score += level.score[SC_TIME_BONUS]; + score += time_score; game.LevelSolved_CountingHealth = health; game.LevelSolved_CountingScore = score; @@ -8776,8 +8857,9 @@ void AmoebaToDiamond(int ax, int ay) #ifdef DEBUG if (group_nr == 0) { - printf("AmoebaToDiamond(): ax = %d, ay = %d\n", ax, ay); - printf("AmoebaToDiamond(): This should never happen!\n"); + Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay); + Debug("game:playing:AmoebaToDiamond", "This should never happen!"); + return; } #endif @@ -8834,8 +8916,9 @@ static void AmoebaToDiamondBD(int ax, int ay, int new_element) #ifdef DEBUG if (group_nr == 0) { - printf("AmoebaToDiamondBD(): ax = %d, ay = %d\n", ax, ay); - printf("AmoebaToDiamondBD(): This should never happen!\n"); + Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay); + Debug("game:playing:AmoebaToDiamondBD", "This should never happen!"); + return; } #endif @@ -9043,8 +9126,10 @@ static void AmoebaReproduce(int ax, int ay) #ifdef DEBUG if (new_group_nr == 0) { - printf("AmoebaReproduce(): newax = %d, neway = %d\n", newax, neway); - printf("AmoebaReproduce(): This should never happen!\n"); + Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d", + newax, neway); + Debug("game:playing:AmoebaReproduce", "This should never happen!"); + return; } #endif @@ -10660,11 +10745,9 @@ static void HandleElementChange(int x, int y, int page) if (!CAN_CHANGE_OR_HAS_ACTION(element) && !CAN_CHANGE_OR_HAS_ACTION(Back[x][y])) { - printf("\n\n"); - printf("HandleElementChange(): %d,%d: element = %d ('%s')\n", - x, y, element, element_info[element].token_name); - printf("HandleElementChange(): This should never happen!\n"); - printf("\n\n"); + Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')", + x, y, element, element_info[element].token_name); + Debug("game:playing:HandleElementChange", "This should never happen!"); } #endif @@ -11200,18 +11283,29 @@ static void CheckSaveEngineSnapshot(struct PlayerInfo *player) if (!player->is_dropping) player->was_dropping = FALSE; } + + static struct MouseActionInfo mouse_action_last = { 0 }; + struct MouseActionInfo mouse_action = player->effective_mouse_action; + boolean new_released = (!mouse_action.button && mouse_action_last.button); + + if (new_released) + CheckSaveEngineSnapshotToList(); + + mouse_action_last = mouse_action; } static void CheckSingleStepMode(struct PlayerInfo *player) { if (tape.single_step && tape.recording && !tape.pausing) { - /* as it is called "single step mode", just return to pause mode when the - player stopped moving after one tile (or never starts moving at all) */ - if (!player->is_moving && - !player->is_pushing && - !player->is_dropping_pressed) - TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); + // as it is called "single step mode", just return to pause mode when the + // player stopped moving after one tile (or never starts moving at all) + // (reverse logic needed here in case single step mode used in team mode) + if (player->is_moving || + player->is_pushing || + player->is_dropping_pressed || + player->effective_mouse_action.button) + game.enter_single_step_mode = FALSE; } CheckSaveEngineSnapshot(player); @@ -11544,7 +11638,7 @@ static void GameActionsExt(void) int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value); - printf("::: skip == %d\n", skip); + Debug("game:playing:skip", "skip == %d", skip); #else // ---------- main game synchronization point ---------- @@ -11661,9 +11755,8 @@ static void GameActionsExt(void) byte mapped_action[MAX_PLAYERS]; #if DEBUG_PLAYER_ACTIONS - printf(":::"); for (i = 0; i < MAX_PLAYERS; i++) - printf(" %d, ", stored_player[i].effective_action); + DebugContinued("", "%d, ", stored_player[i].effective_action); #endif for (i = 0; i < MAX_PLAYERS; i++) @@ -11673,19 +11766,18 @@ static void GameActionsExt(void) stored_player[i].effective_action = mapped_action[i]; #if DEBUG_PLAYER_ACTIONS - printf(" =>"); + DebugContinued("", "=> "); for (i = 0; i < MAX_PLAYERS; i++) - printf(" %d, ", stored_player[i].effective_action); - printf("\n"); + DebugContinued("", "%d, ", stored_player[i].effective_action); + DebugContinued("game:playing:player", "\n"); #endif } #if DEBUG_PLAYER_ACTIONS else { - printf(":::"); for (i = 0; i < MAX_PLAYERS; i++) - printf(" %d, ", stored_player[i].effective_action); - printf("\n"); + DebugContinued("", "%d, ", stored_player[i].effective_action); + DebugContinued("game:playing:player", "\n"); } #endif #endif @@ -11876,6 +11968,10 @@ void GameActions_RND(void) DrawGameDoorValues(); } + // check single step mode (set flag and clear again if any player is active) + game.enter_single_step_mode = + (tape.single_step && tape.recording && !tape.pausing); + for (i = 0; i < MAX_PLAYERS; i++) { int actual_player_action = stored_player[i].effective_action; @@ -11900,6 +11996,10 @@ void GameActions_RND(void) ScrollPlayer(&stored_player[i], SCROLL_GO_ON); } + // single step pause mode may already have been toggled by "ScrollPlayer()" + if (game.enter_single_step_mode && !tape.pausing) + TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); + ScrollScreen(NULL, SCROLL_GO_ON); /* for backwards compatibility, the following code emulates a fixed bug that @@ -11952,18 +12052,26 @@ void GameActions_RND(void) MovDelay[x][y]--; if (MovDelay[x][y] <= 0) { + int element = Store[x][y]; + int move_direction = MovDir[x][y]; + int player_index_bit = Store2[x][y]; + + Store[x][y] = 0; + Store2[x][y] = 0; + RemoveField(x, y); TEST_DrawLevelField(x, y); - TestIfElementTouchesCustomElement(x, y); // for empty space + TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit); } } #if DEBUG if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1) { - printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y); - printf("GameActions(): This should never happen!\n"); + Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1", + x, y); + Debug("game:playing:GameActions_RND", "This should never happen!"); ChangePage[x][y] = -1; } @@ -11997,10 +12105,10 @@ void GameActions_RND(void) Blocked2Moving(x, y, &oldx, &oldy); if (!IS_MOVING(oldx, oldy)) { - printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n"); - printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y); - printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy); - printf("GameActions(): This should never happen!\n"); + Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!"); + Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y); + Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy); + Debug("game:playing:GameActions_RND", "This should never happen!"); } } #endif @@ -12372,6 +12480,8 @@ void GameActions_RND(void) static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y) { int min_x = x, min_y = y, max_x = x, max_y = y; + int scr_fieldx = getScreenFieldSizeX(); + int scr_fieldy = getScreenFieldSizeY(); int i; for (i = 0; i < MAX_PLAYERS; i++) @@ -12387,7 +12497,7 @@ static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y) max_y = MAX(max_y, jy); } - return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY); + return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy); } static boolean AllPlayersInVisibleScreen(void) @@ -12641,8 +12751,9 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) int original_move_delay_value = player->move_delay_value; #if DEBUG - printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n", - tape.counter); + Debug("game:playing:MovePlayer", + "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]", + tape.counter); #endif // scroll remaining steps with finest movement resolution @@ -12952,6 +13063,21 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (!player->is_pushing) TestIfElementTouchesCustomElement(jx, jy); // for empty space + if (level.finish_dig_collect && + (player->is_digging || player->is_collecting)) + { + int last_element = player->last_removed_element; + int move_direction = player->MovDir; + int enter_side = MV_DIR_OPPOSITE(move_direction); + int change_event = (player->is_digging ? CE_PLAYER_DIGS_X : + CE_PLAYER_COLLECTS_X); + + CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event, + player->index_bit, enter_side); + + player->last_removed_element = EL_UNDEFINED; + } + if (!player->active) RemovePlayer(player); } @@ -13567,8 +13693,9 @@ void KillPlayer(struct PlayerInfo *player) return; #if 0 - printf("::: 0: killed == %d, active == %d, reanimated == %d\n", - player->killed, player->active, player->reanimated); + Debug("game:playing:KillPlayer", + "0: killed == %d, active == %d, reanimated == %d", + player->killed, player->active, player->reanimated); #endif /* the following code was introduced to prevent an infinite loop when calling @@ -13596,15 +13723,17 @@ void KillPlayer(struct PlayerInfo *player) player->shield_deadly_time_left = 0; #if 0 - printf("::: 1: killed == %d, active == %d, reanimated == %d\n", - player->killed, player->active, player->reanimated); + Debug("game:playing:KillPlayer", + "1: killed == %d, active == %d, reanimated == %d", + player->killed, player->active, player->reanimated); #endif Bang(jx, jy); #if 0 - printf("::: 2: killed == %d, active == %d, reanimated == %d\n", - player->killed, player->active, player->reanimated); + Debug("game:playing:KillPlayer", + "2: killed == %d, active == %d, reanimated == %d", + player->killed, player->active, player->reanimated); #endif if (player->reanimated) // killed player may have been reanimated @@ -13683,7 +13812,8 @@ void ExitPlayer(struct PlayerInfo *player) game.players_still_needed--; } -static void setFieldForSnapping(int x, int y, int element, int direction) +static void SetFieldForSnapping(int x, int y, int element, int direction, + int player_index_bit) { struct ElementInfo *ei = &element_info[element]; int direction_bit = MV_DIR_TO_BIT(direction); @@ -13693,6 +13823,9 @@ static void setFieldForSnapping(int x, int y, int element, int direction) Tile[x][y] = EL_ELEMENT_SNAPPING; MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1; + MovDir[x][y] = direction; + Store[x][y] = element; + Store2[x][y] = player_index_bit; ResetGfxAnimation(x, y); @@ -13702,6 +13835,20 @@ static void setFieldForSnapping(int x, int y, int element, int direction) GfxFrame[x][y] = -1; } +static void TestFieldAfterSnapping(int x, int y, int element, int direction, + int player_index_bit) +{ + TestIfElementTouchesCustomElement(x, y); // for empty space + + if (level.finish_dig_collect) + { + int dig_side = MV_DIR_OPPOSITE(direction); + + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player_index_bit, dig_side); + } +} + /* ============================================================================= checkDiagonalPushing() @@ -13981,18 +14128,28 @@ static int DigField(struct PlayerInfo *player, PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING); - CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X, - player->index_bit, dig_side); + // use old behaviour for old levels (digging) + if (!level.finish_dig_collect) + { + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X, + player->index_bit, dig_side); + + // if digging triggered player relocation, finish digging tile + if (mode == DF_DIG && (player->jx != jx || player->jy != jy)) + SetFieldForSnapping(x, y, element, move_direction, player->index_bit); + } if (mode == DF_SNAP) { if (level.block_snap_field) - setFieldForSnapping(x, y, element, move_direction); + SetFieldForSnapping(x, y, element, move_direction, player->index_bit); else - TestIfElementTouchesCustomElement(x, y); // for empty space + TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit); - CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, - player->index_bit, dig_side); + // use old behaviour for old levels (snapping) + if (!level.finish_dig_collect) + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); } } else if (player_can_move_or_snap && IS_COLLECTIBLE(element)) @@ -14104,19 +14261,28 @@ static int DigField(struct PlayerInfo *player, RaiseScoreElement(element); PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING); - if (is_player) + // use old behaviour for old levels (collecting) + if (!level.finish_dig_collect && is_player) + { CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X, player->index_bit, dig_side); + // if collecting triggered player relocation, finish collecting tile + if (mode == DF_DIG && (player->jx != jx || player->jy != jy)) + SetFieldForSnapping(x, y, element, move_direction, player->index_bit); + } + if (mode == DF_SNAP) { if (level.block_snap_field) - setFieldForSnapping(x, y, element, move_direction); + SetFieldForSnapping(x, y, element, move_direction, player->index_bit); else - TestIfElementTouchesCustomElement(x, y); // for empty space + TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit); - CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, - player->index_bit, dig_side); + // use old behaviour for old levels (snapping) + if (!level.finish_dig_collect) + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); } } else if (player_can_move_or_snap && IS_PUSHABLE(element)) @@ -14454,6 +14620,8 @@ static int DigField(struct PlayerInfo *player, { player->is_collecting = !player->is_digging; player->is_active = TRUE; + + player->last_removed_element = element; } } @@ -15229,6 +15397,9 @@ void RequestRestartGame(char *message) } else { + // needed in case of envelope request to close game panel + CloseDoor(DOOR_CLOSE_1); + SetGameStatus(GAME_MODE_MAIN); DrawMainMenu(); @@ -15420,10 +15591,11 @@ static void LoadEngineSnapshotValues_RND(void) if (game.num_random_calls != num_random_calls) { - Error(ERR_INFO, "number of random calls out of sync"); - Error(ERR_INFO, "number of random calls should be %d", num_random_calls); - Error(ERR_INFO, "number of random calls is %d", game.num_random_calls); - Error(ERR_EXIT, "this should not happen -- please debug"); + Error("number of random calls out of sync"); + Error("number of random calls should be %d", num_random_calls); + Error("number of random calls is %d", game.num_random_calls); + + Fail("this should not happen -- please debug"); } } @@ -15536,7 +15708,8 @@ static ListNode *SaveEngineSnapshotBuffers(void) node = node->next; } - printf("::: size of engine snapshot: %d bytes\n", num_bytes); + Debug("game:playing:SaveEngineSnapshotBuffers", + "size of engine snapshot: %d bytes", num_bytes); #endif return buffers; @@ -15831,7 +16004,7 @@ void CreateGameButtons(void) GDI_END); if (gi == NULL) - Error(ERR_EXIT, "cannot create gadget"); + Fail("cannot create gadget"); game_gadget[id] = gi; }