X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=5bf2c6a47488c989818e6c7e1f2d30c545249942;hb=c68b766d8c38df1c17c8f32b2b7839065d02cc84;hp=fbe34cc7a6eeef5328fc8bb5b85bef4a62c741fb;hpb=5e53b3af62af5a0fb7726afebf62dc4a5c297596;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index fbe34cc7..5bf2c6a4 100644 --- a/src/game.c +++ b/src/game.c @@ -4,7 +4,7 @@ // (c) 1995-2014 by Artsoft Entertainment // Holger Schemel // info@artsoft.org -// http://www.artsoft.org/ +// https://www.artsoft.org/ // ---------------------------------------------------------------------------- // game.c // ============================================================================ @@ -1092,8 +1092,8 @@ static void FadeLevelSoundsAndMusic(void); static void HandleGameButtons(struct GadgetInfo *); -int AmoebeNachbarNr(int, int); -void AmoebeUmwandeln(int, int); +int AmoebaNeighbourNr(int, int); +void AmoebaToDiamond(int, int); void ContinueMoving(int, int); void Bang(int, int); void InitMovDir(int, int); @@ -1785,7 +1785,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) player->jy = player->last_jy = y; } - if (!init_game) + // always check if player was just killed and should be reanimated { int player_nr = GET_PLAYER_NR(element); struct PlayerInfo *player = &stored_player[player_nr]; @@ -1877,6 +1877,8 @@ static void InitField(int x, int y, boolean init_game) case EL_MOLE_RIGHT: case EL_MOLE_UP: case EL_MOLE_DOWN: + case EL_SPRING_LEFT: + case EL_SPRING_RIGHT: InitMovDir(x, y); break; @@ -1963,12 +1965,12 @@ static void InitField(int x, int y, boolean init_game) break; case EL_EMC_MAGIC_BALL: - if (game.ball_state) + if (game.ball_active) Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE; break; case EL_EMC_MAGIC_BALL_SWITCH: - if (game.ball_state) + if (game.ball_active) Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE; break; @@ -2228,14 +2230,14 @@ static void UpdateGameControlValues(void) game_mm.score : game.score); int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - game_em.lev->required : + game_em.lev->gems_needed : level.game_engine_type == GAME_ENGINE_TYPE_SP ? game_sp.infotrons_still_needed : level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.kettles_still_needed : game.gems_still_needed); int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - game_em.lev->required > 0 : + game_em.lev->gems_needed > 0 : level.game_engine_type == GAME_ENGINE_TYPE_SP ? game_sp.infotrons_still_needed > 0 : level.game_engine_type == GAME_ENGINE_TYPE_MM ? @@ -2366,9 +2368,9 @@ static void UpdateGameControlValues(void) (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN); game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value = - (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL); + (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL); game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value = - (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE : + (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE : EL_EMC_MAGIC_BALL_SWITCH); game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value = @@ -2840,10 +2842,84 @@ static void InitGameEngine(void) game.team_mode = (num_players > 1); } +#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")); +#endif + // -------------------------------------------------------------------------- // set flags for bugs and changes according to active game engine version // -------------------------------------------------------------------------- + /* + Summary of bugfix: + Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING" + + Bug was introduced in version: + 2.0.1 + + Bug was fixed in version: + 4.2.0.0 + + Description: + In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added, + but the property "can fall" was missing, which caused some levels to be + unsolvable. This was fixed in version 4.2.0.0. + + Affected levels/tapes: + An example for a tape that was fixed by this bugfix is tape 029 from the + level set "rnd_sam_bateman". + The wrong behaviour will still be used for all levels or tapes that were + created/recorded with it. An example for this is tape 023 from the level + set "rnd_gerhard_haeusler", which was recorded with a buggy game engine. + */ + + boolean use_amoeba_dropping_cannot_fall_bug = + ((game.engine_version >= VERSION_IDENT(2,0,1,0) && + game.engine_version < VERSION_IDENT(4,2,0,0)) || + (tape.playing && + tape.game_version >= VERSION_IDENT(2,0,1,0) && + tape.game_version < VERSION_IDENT(4,2,0,0))); + + /* + Summary of bugfix/change: + Fixed move speed of elements entering or leaving magic wall. + + Fixed/changed in version: + 2.0.1 + + Description: + Before 2.0.1, move speed of elements entering or leaving magic wall was + twice as fast as it is now. + Since 2.0.1, this is set to a lower value by using move_stepsize_list[]. + + Affected levels/tapes: + The first condition is generally needed for all levels/tapes before version + 2.0.1, which might use the old behaviour before it was changed; known tapes + that are affected: Tape 014 from the level set "rnd_conor_mancone". + The second condition is an exception from the above case and is needed for + the special case of tapes recorded with game (not engine!) version 2.0.1 or + above, but before it was known that this change would break tapes like the + above and was fixed in 4.2.0.0, so that the changed behaviour was active + although the engine version while recording maybe was before 2.0.1. There + are a lot of tapes that are affected by this exception, like tape 006 from + the level set "rnd_conor_mancone". + */ + + boolean use_old_move_stepsize_for_magic_wall = + (game.engine_version < VERSION_IDENT(2,0,1,0) && + !(tape.playing && + tape.game_version >= VERSION_IDENT(2,0,1,0) && + tape.game_version < VERSION_IDENT(4,2,0,0))); + /* Summary of bugfix/change: Fixed handling for custom elements that change when pushed by the player. @@ -2906,12 +2982,26 @@ static void InitGameEngine(void) game.use_block_last_field_bug = (game.engine_version < VERSION_IDENT(3,1,1,0)); + /* various special flags and settings for native Emerald Mine game engine */ + game_em.use_single_button = (game.engine_version > VERSION_IDENT(4,0,0,2)); game_em.use_snap_key_bug = (game.engine_version < VERSION_IDENT(4,0,1,0)); + game_em.use_random_bug = + (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG); + + boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0)); + + game_em.use_old_explosions = use_old_em_engine; + game_em.use_old_android = use_old_em_engine; + game_em.use_old_push_elements = use_old_em_engine; + game_em.use_old_push_into_acid = use_old_em_engine; + + game_em.use_wrap_around = !use_old_em_engine; + // -------------------------------------------------------------------------- // set maximal allowed number of custom element changes per game frame @@ -2923,13 +3013,11 @@ static void InitGameEngine(void) // dynamically adjust element properties according to game engine version InitElementPropertiesEngine(game.engine_version); -#if 0 - printf("level %d: level version == %06d\n", level_nr, level.game_version); - printf(" tape version == %06d [%s] [file: %06d]\n", - tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"), - tape.file_version); - printf(" => game.engine_version == %06d\n", game.engine_version); -#endif + // ---------- initialize special element properties ------------------------- + + // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions + if (use_amoeba_dropping_cannot_fall_bug) + SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE); // ---------- initialize player's initial move delay ------------------------ @@ -3176,6 +3264,16 @@ static void InitGameEngine(void) int e = move_stepsize_list[i].element; element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize; + + // set move stepsize value for certain elements for older engine versions + if (use_old_move_stepsize_for_magic_wall) + { + if (e == EL_MAGIC_WALL_FILLING || + e == EL_MAGIC_WALL_EMPTYING || + e == EL_BD_MAGIC_WALL_FILLING || + e == EL_BD_MAGIC_WALL_EMPTYING) + element_info[e].move_stepsize *= 2; + } } // ---------- initialize collect score -------------------------------------- @@ -3250,6 +3348,8 @@ static void InitGameEngine(void) // ---------- initialize graphics engine ------------------------------------ game.scroll_delay_value = (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value : + level.game_engine_type == GAME_ENGINE_TYPE_EM && + !setup.forced_scroll_delay ? 0 : setup.scroll_delay ? setup.scroll_delay_value : 0); game.scroll_delay_value = MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY); @@ -3272,6 +3372,34 @@ static void InitGameEngine(void) // Supaplex levels with time limit currently unsupported -- should be added if (level.game_engine_type == GAME_ENGINE_TYPE_SP) level.time = 0; + + // ---------- initialize flags for handling game actions -------------------- + + // set flags for game actions to default values + game.use_key_actions = TRUE; + game.use_mouse_actions = FALSE; + + // when using Mirror Magic game engine, handle mouse events only + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + game.use_key_actions = FALSE; + game.use_mouse_actions = TRUE; + } + + // check for custom elements with mouse click events + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) + { + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + + if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || + HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) || + HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) || + HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X)) + game.use_mouse_actions = TRUE; + } + } } static int get_num_special_action(int element, int action_first, @@ -3384,6 +3512,13 @@ void InitGame(void) InitGameEngine(); InitGameControlValues(); + // initialize tape actions from game when recording tape + if (tape.recording) + { + tape.use_key_actions = game.use_key_actions; + tape.use_mouse_actions = game.use_mouse_actions; + } + // don't play tapes over network network_playing = (network.enabled && !tape.playing); @@ -3612,7 +3747,7 @@ void InitGame(void) game.lenses_time_left = 0; game.magnify_time_left = 0; - game.ball_state = level.ball_state_initial; + game.ball_active = level.ball_active_initial; game.ball_content_nr = 0; game.explosions_delayed = TRUE; @@ -3746,12 +3881,13 @@ void InitGame(void) // use preferred player also in local single-player mode if (!network.enabled && !game.team_mode) { - int old_index_nr = local_player->index_nr; int new_index_nr = setup.network_player_nr; if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS) { - stored_player[old_index_nr].connected_locally = FALSE; + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].connected_locally = FALSE; + stored_player[new_index_nr].connected_locally = TRUE; } } @@ -3933,7 +4069,7 @@ void InitGame(void) game.centered_player_nr = (network_playing ? local_player->index_nr : -1); game.centered_player_nr_next = game.centered_player_nr; game.set_centered_player = FALSE; - game.set_centered_player_fast = FALSE; + game.set_centered_player_wrap = FALSE; if (network_playing && tape.recording) { @@ -4269,12 +4405,6 @@ void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y, { // this is used for non-R'n'D game engines to update certain engine values - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - { - actual_player_x = correctLevelPosX_EM(actual_player_x); - actual_player_y = correctLevelPosY_EM(actual_player_y); - } - // needed to determine if sounds are played within the visible screen area scroll_x = actual_scroll_x; scroll_y = actual_scroll_y; @@ -4367,6 +4497,12 @@ void InitMovDir(int x, int y) MovDir[x][y] = direction[2][element - EL_MOLE_LEFT]; break; + case EL_SPRING_LEFT: + case EL_SPRING_RIGHT: + Feld[x][y] = EL_SPRING; + MovDir[x][y] = direction[2][element - EL_SPRING_LEFT]; + break; + default: if (IS_CUSTOM_ELEMENT(element)) { @@ -4463,7 +4599,7 @@ void InitMovDir(int x, int y) void InitAmoebaNr(int x, int y) { int i; - int group_nr = AmoebeNachbarNr(x, y); + int group_nr = AmoebaNeighbourNr(x, y); if (group_nr == 0) { @@ -5633,7 +5769,7 @@ static void Explode(int ex, int ey, int phase, int mode) } else if (border_element == EL_AMOEBA_TO_DIAMOND) { - AmoebeUmwandeln(x, y); + AmoebaToDiamond(x, y); Store2[x][y] = 0; border_explosion = TRUE; } @@ -8554,7 +8690,7 @@ void ContinueMoving(int x, int y) MV_DIR_OPPOSITE(direction)); } -int AmoebeNachbarNr(int ax, int ay) +int AmoebaNeighbourNr(int ax, int ay) { int i; int element = Feld[ax][ay]; @@ -8582,7 +8718,7 @@ int AmoebeNachbarNr(int ax, int ay) return group_nr; } -static void AmoebenVereinigen(int ax, int ay) +static void AmoebaMerge(int ax, int ay) { int i, x, y, xx, yy; int new_group_nr = AmoebaNr[ax][ay]; @@ -8629,7 +8765,7 @@ static void AmoebenVereinigen(int ax, int ay) } } -void AmoebeUmwandeln(int ax, int ay) +void AmoebaToDiamond(int ax, int ay) { int i, x, y; @@ -8640,8 +8776,8 @@ void AmoebeUmwandeln(int ax, int ay) #ifdef DEBUG if (group_nr == 0) { - printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay); - printf("AmoebeUmwandeln(): This should never happen!\n"); + printf("AmoebaToDiamond(): ax = %d, ay = %d\n", ax, ay); + printf("AmoebaToDiamond(): This should never happen!\n"); return; } #endif @@ -8689,7 +8825,7 @@ void AmoebeUmwandeln(int ax, int ay) } } -static void AmoebeUmwandelnBD(int ax, int ay, int new_element) +static void AmoebaToDiamondBD(int ax, int ay, int new_element) { int x, y; int group_nr = AmoebaNr[ax][ay]; @@ -8698,8 +8834,8 @@ static void AmoebeUmwandelnBD(int ax, int ay, int new_element) #ifdef DEBUG if (group_nr == 0) { - printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay); - printf("AmoebeUmwandelnBD(): This should never happen!\n"); + printf("AmoebaToDiamondBD(): ax = %d, ay = %d\n", ax, ay); + printf("AmoebaToDiamondBD(): This should never happen!\n"); return; } #endif @@ -8725,7 +8861,7 @@ static void AmoebeUmwandelnBD(int ax, int ay, int new_element) SND_BD_AMOEBA_TURNING_TO_GEM)); } -static void AmoebeWaechst(int x, int y) +static void AmoebaGrowing(int x, int y) { static unsigned int sound_delay = 0; static unsigned int sound_delay_value = 0; @@ -8761,7 +8897,7 @@ static void AmoebeWaechst(int x, int y) } } -static void AmoebaDisappearing(int x, int y) +static void AmoebaShrinking(int x, int y) { static unsigned int sound_delay = 0; static unsigned int sound_delay_value = 0; @@ -8797,7 +8933,7 @@ static void AmoebaDisappearing(int x, int y) } } -static void AmoebeAbleger(int ax, int ay) +static void AmoebaReproduce(int ax, int ay) { int i; int element = Feld[ax][ay]; @@ -8891,9 +9027,9 @@ static void AmoebeAbleger(int ax, int ay) if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) // amoeba is completely dead { if (element == EL_AMOEBA_FULL) - AmoebeUmwandeln(ax, ay); + AmoebaToDiamond(ax, ay); else if (element == EL_BD_AMOEBA) - AmoebeUmwandelnBD(ax, ay, level.amoeba_content); + AmoebaToDiamondBD(ax, ay, level.amoeba_content); } } return; @@ -8907,8 +9043,8 @@ static void AmoebeAbleger(int ax, int ay) #ifdef DEBUG if (new_group_nr == 0) { - printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway); - printf("AmoebeAbleger(): This should never happen!\n"); + printf("AmoebaReproduce(): newax = %d, neway = %d\n", newax, neway); + printf("AmoebaReproduce(): This should never happen!\n"); return; } #endif @@ -8918,11 +9054,11 @@ static void AmoebeAbleger(int ax, int ay) AmoebaCnt2[new_group_nr]++; // if amoeba touches other amoeba(s) after growing, unify them - AmoebenVereinigen(newax, neway); + AmoebaMerge(newax, neway); if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200) { - AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK); + AmoebaToDiamondBD(newax, neway, EL_BD_ROCK); return; } } @@ -9872,11 +10008,14 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) // ---------- player actions --------------------------------------------- case CA_MOVE_PLAYER: + case CA_MOVE_PLAYER_NEW: { // automatically move to the next field in specified direction for (i = 0; i < MAX_PLAYERS; i++) if (trigger_player_bits & (1 << i)) - stored_player[i].programmed_action = action_arg_direction; + if (action_type == CA_MOVE_PLAYER || + stored_player[i].MovPos == 0) + stored_player[i].programmed_action = action_arg_direction; break; } @@ -10152,6 +10291,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) if (CustomValue[x][y] == 0) { + // reset change counter (else CE_VALUE_GETS_ZERO would not work) + ChangeCount[x][y] = 0; // allow at least one more change + CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO); CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X); } @@ -10175,6 +10317,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { int xx, yy; + // reset change counter (else CE_SCORE_GETS_ZERO would not work) + ChangeCount[x][y] = 0; // allow at least one more change + CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO); CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X); @@ -11131,7 +11276,7 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action, byte *tape_action) { - if (!tape.use_mouse) + if (!tape.use_mouse_actions) return; mouse_action->lx = tape_action[TAPE_ACTION_LX]; @@ -11142,7 +11287,7 @@ static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action, static void SetTapeActionFromMouseAction(byte *tape_action, struct MouseActionInfo *mouse_action) { - if (!tape.use_mouse) + if (!tape.use_mouse_actions) return; tape_action[TAPE_ACTION_LX] = mouse_action->lx; @@ -11340,7 +11485,7 @@ static void GameActionsExt(void) unsigned int game_frame_delay_value; byte *recorded_player_action; byte summarized_player_action = 0; - byte tape_action[MAX_PLAYERS]; + byte tape_action[MAX_TAPE_ACTIONS] = { 0 }; int i; // detect endless loops, caused by custom element programming @@ -11673,6 +11818,8 @@ void GameActions_RND_Main(void) void GameActions_RND(void) { + static struct MouseActionInfo mouse_action_last = { 0 }; + struct MouseActionInfo mouse_action = local_player->effective_mouse_action; int magic_wall_x = 0, magic_wall_y = 0; int i, x, y, element, graphic, last_gfx_frame; @@ -11859,6 +12006,24 @@ void GameActions_RND(void) #endif } + if (mouse_action.button) + { + int new_button = (mouse_action.button && mouse_action_last.button == 0); + + x = mouse_action.lx; + y = mouse_action.ly; + element = Feld[x][y]; + + if (new_button) + { + CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE); + CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X); + } + + CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE); + CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X); + } + SCAN_PLAYFIELD(x, y) { element = Feld[x][y]; @@ -11935,13 +12100,13 @@ void GameActions_RND(void) else if (IS_ACTIVE_BOMB(element)) CheckDynamite(x, y); else if (element == EL_AMOEBA_GROWING) - AmoebeWaechst(x, y); + AmoebaGrowing(x, y); else if (element == EL_AMOEBA_SHRINKING) - AmoebaDisappearing(x, y); + AmoebaShrinking(x, y); #if !USE_NEW_AMOEBA_CODE else if (IS_AMOEBALIVE(element)) - AmoebeAbleger(x, y); + AmoebaReproduce(x, y); #endif else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE) @@ -12200,6 +12365,8 @@ void GameActions_RND(void) // use random number generator in every frame to make it less predictable if (game.engine_version >= VERSION_IDENT(3,1,1,0)) RND(1); + + mouse_action_last = mouse_action; } static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y) @@ -14218,13 +14385,13 @@ static int DigField(struct PlayerInfo *player, { int xx, yy; - game.ball_state = !game.ball_state; + game.ball_active = !game.ball_active; SCAN_PLAYFIELD(xx, yy) { int e = Feld[xx][yy]; - if (game.ball_state) + if (game.ball_active) { if (e == EL_EMC_MAGIC_BALL) CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE); @@ -14701,10 +14868,10 @@ static void PlayLevelMusic(void) void PlayLevelSound_EM(int xx, int yy, int element_em, int sample) { - int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0); - int offset = (BorderElement == EL_STEELWALL ? 1 : 0); - int x = xx - 1 - offset; - int y = yy - 1 - offset; + int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0); + int offset = 0; + int x = xx - offset; + int y = yy - offset; switch (sample) {