X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=d8a3d16270af01b54ed8dcff09072d13f79aff4a;hp=c6a7ef73146a7cf1bd90b59121cbf0856b802189;hb=580cc0293954391cf0cfd2f99638d8134f88f76c;hpb=fd0ec980e5c4c22978545aa72a2b0b9da0944de7 diff --git a/src/game.c b/src/game.c index c6a7ef73..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 @@ -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; @@ -2802,12 +2869,10 @@ void UpdateAndDisplayGameControlValues(void) DisplayGameControlValues(); } -#if 0 -static void UpdateGameDoorValues(void) +void UpdateGameDoorValues(void) { UpdateGameControlValues(); } -#endif void DrawGameDoorValues(void) { @@ -3511,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 @@ -3648,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; @@ -4382,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(); @@ -4643,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; @@ -4651,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) { @@ -4684,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; @@ -4704,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; } @@ -4793,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; @@ -4825,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; @@ -11215,13 +11298,14 @@ 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 && - !player->effective_mouse_action.button) - 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); @@ -11884,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; @@ -11908,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 @@ -11960,10 +12052,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); } } @@ -12381,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++) @@ -12396,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) @@ -12962,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); } @@ -13696,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); @@ -13706,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); @@ -13715,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() @@ -13994,22 +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); + // 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)) @@ -14121,25 +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); + 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)) @@ -14477,6 +14620,8 @@ static int DigField(struct PlayerInfo *player, { player->is_collecting = !player->is_digging; player->is_active = TRUE; + + player->last_removed_element = element; } } @@ -15252,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();