X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=2ccc79cd1ca3c16341cfdf204b3a052249ba2e1c;hp=859cf24e4cfb6812f03c2017c17872fa7a82cf0c;hb=3915df993ef01086469a8a7f575b2525e9887f65;hpb=de49192b75b9ff43a8c2ed6cb200efb4240d8d6d diff --git a/src/game.c b/src/game.c index 859cf24e..2ccc79cd 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 @@ -2251,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(); @@ -2325,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 = @@ -2478,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 { @@ -2490,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) @@ -2499,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; @@ -2512,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 { @@ -2524,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) @@ -2589,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; @@ -3511,11 +3578,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 @@ -3648,6 +3719,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; @@ -4382,7 +4455,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(); @@ -4643,7 +4718,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; @@ -4651,6 +4726,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) { @@ -4684,19 +4761,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; @@ -4704,7 +4785,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; } @@ -4793,7 +4874,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; @@ -4825,7 +4910,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; @@ -11200,18 +11285,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); @@ -11661,9 +11757,8 @@ static void GameActionsExt(void) byte mapped_action[MAX_PLAYERS]; #if DEBUG_PLAYER_ACTIONS - Print(":::"); for (i = 0; i < MAX_PLAYERS; i++) - Print(" %d, ", stored_player[i].effective_action); + DebugContinued("", "%d, ", stored_player[i].effective_action); #endif for (i = 0; i < MAX_PLAYERS; i++) @@ -11673,19 +11768,18 @@ static void GameActionsExt(void) stored_player[i].effective_action = mapped_action[i]; #if DEBUG_PLAYER_ACTIONS - Print(" =>"); + DebugContinued("", "=> "); for (i = 0; i < MAX_PLAYERS; i++) - Print(" %d, ", stored_player[i].effective_action); - Print("\n"); + DebugContinued("", "%d, ", stored_player[i].effective_action); + DebugContinued("game:playing:player", "\n"); #endif } #if DEBUG_PLAYER_ACTIONS else { - Print(":::"); for (i = 0; i < MAX_PLAYERS; i++) - Print(" %d, ", stored_player[i].effective_action); - Print("\n"); + DebugContinued("", "%d, ", stored_player[i].effective_action); + DebugContinued("game:playing:player", "\n"); } #endif #endif @@ -11876,6 +11970,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 +11998,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,10 +12054,17 @@ 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); } } @@ -12373,6 +12482,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++) @@ -12388,7 +12499,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) @@ -12954,6 +13065,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); } @@ -13688,7 +13814,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); @@ -13698,6 +13825,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); @@ -13707,6 +13837,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() @@ -13986,18 +14130,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)) @@ -14109,19 +14263,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)) @@ -14459,6 +14622,8 @@ static int DigField(struct PlayerInfo *player, { player->is_collecting = !player->is_digging; player->is_active = TRUE; + + player->last_removed_element = element; } } @@ -15234,6 +15399,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();