X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=803eebb327f93d0ca0f6cc636a4f388025ceafbb;hp=c8c5de48969841d896ffc8ef6e3042847c2266b0;hb=b641818c787e48bbf03ce2a0cd5b542c4c21e523;hpb=bf88ae3e5f7cc53fec7a06298f6632368c6c8e0c diff --git a/src/game.c b/src/game.c index c8c5de48..803eebb3 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 // ============================================================================ @@ -1016,14 +1016,16 @@ static struct GamePanelControlInfo game_panel_controls[] = #define GAME_CTRL_ID_PANEL_STOP 8 #define GAME_CTRL_ID_PANEL_PAUSE 9 #define GAME_CTRL_ID_PANEL_PLAY 10 -#define SOUND_CTRL_ID_MUSIC 11 -#define SOUND_CTRL_ID_LOOPS 12 -#define SOUND_CTRL_ID_SIMPLE 13 -#define SOUND_CTRL_ID_PANEL_MUSIC 14 -#define SOUND_CTRL_ID_PANEL_LOOPS 15 -#define SOUND_CTRL_ID_PANEL_SIMPLE 16 +#define GAME_CTRL_ID_TOUCH_STOP 11 +#define GAME_CTRL_ID_TOUCH_PAUSE 12 +#define SOUND_CTRL_ID_MUSIC 13 +#define SOUND_CTRL_ID_LOOPS 14 +#define SOUND_CTRL_ID_SIMPLE 15 +#define SOUND_CTRL_ID_PANEL_MUSIC 16 +#define SOUND_CTRL_ID_PANEL_LOOPS 17 +#define SOUND_CTRL_ID_PANEL_SIMPLE 18 -#define NUM_GAME_BUTTONS 17 +#define NUM_GAME_BUTTONS 19 // forward declaration for internal use @@ -1783,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]; @@ -1875,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; @@ -1961,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; @@ -2124,9 +2128,9 @@ static int compareGamePanelOrderInfo(const void *object1, const void *object2) int getPlayerInventorySize(int player_nr) { if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - return level.native_em_level->ply[player_nr]->dynamite; + return game_em.ply[player_nr]->dynamite; else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) - return level.native_sp_level->game_sp->red_disk_count; + return game_sp.red_disk_count; else return stored_player[player_nr].inventory_size; } @@ -2210,32 +2214,32 @@ static void UpdateGameControlValues(void) int time = (game.LevelSolved ? game.LevelSolved_CountingTime : level.game_engine_type == GAME_ENGINE_TYPE_EM ? - level.native_em_level->lev->time : + game_em.lev->time : level.game_engine_type == GAME_ENGINE_TYPE_SP ? - level.native_sp_level->game_sp->time_played : + game_sp.time_played : level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.energy_left : game.no_time_limit ? TimePlayed : TimeLeft); int score = (game.LevelSolved ? game.LevelSolved_CountingScore : level.game_engine_type == GAME_ENGINE_TYPE_EM ? - level.native_em_level->lev->score : + game_em.lev->score : level.game_engine_type == GAME_ENGINE_TYPE_SP ? - level.native_sp_level->game_sp->score : + game_sp.score : level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score : game.score); int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - level.native_em_level->lev->required : + game_em.lev->gems_needed : level.game_engine_type == GAME_ENGINE_TYPE_SP ? - level.native_sp_level->game_sp->infotrons_still_needed : + 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 ? - level.native_em_level->lev->required > 0 : + game_em.lev->gems_needed > 0 : level.game_engine_type == GAME_ENGINE_TYPE_SP ? - level.native_sp_level->game_sp->infotrons_still_needed > 0 : + game_sp.infotrons_still_needed > 0 : level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.kettles_still_needed > 0 || game_mm.lights_still_needed > 0 : @@ -2275,7 +2279,7 @@ static void UpdateGameControlValues(void) { if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { - if (level.native_em_level->ply[i]->keys & (1 << k)) + if (game_em.ply[i]->keys & (1 << k)) game_panel_controls[GAME_PANEL_KEY_1 + k].value = get_key_element_from_nr(k); } @@ -2303,7 +2307,7 @@ static void UpdateGameControlValues(void) { if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { - if (level.native_em_level->ply[player_nr]->keys & (1 << k)) + if (game_em.ply[player_nr]->keys & (1 << k)) game_panel_controls[GAME_PANEL_KEY_1 + k].value = get_key_element_from_nr(k); } @@ -2364,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 = @@ -2838,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.1.4.2 + + 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.1.4.2. + + 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,1,4,1)) || + (tape.playing && + tape.game_version >= VERSION_IDENT(2,0,1,0) && + tape.game_version <= VERSION_IDENT(4,1,4,1))); + + /* + 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.1.4.2, 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,1,4,2))); + /* Summary of bugfix/change: Fixed handling for custom elements that change when pushed by the player. @@ -2904,12 +2982,29 @@ 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_old_explosions = + (game.engine_version < VERSION_IDENT(4,1,4,2)); + + game_em.use_old_android = + (game.engine_version < VERSION_IDENT(4,1,4,2)); + + game_em.use_old_push_elements = + (game.engine_version < VERSION_IDENT(4,1,4,2)); + + game_em.use_old_push_into_acid = + (game.engine_version < VERSION_IDENT(4,1,4,2)); + + game_em.use_wrap_around = + (game.engine_version > VERSION_IDENT(4,1,4,1)); + // -------------------------------------------------------------------------- // set maximal allowed number of custom element changes per game frame @@ -2921,13 +3016,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" between 2.0.1 and 4.1.4.1 + if (use_amoeba_dropping_cannot_fall_bug) + SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE); // ---------- initialize player's initial move delay ------------------------ @@ -3174,6 +3267,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 -------------------------------------- @@ -3248,6 +3351,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); @@ -3270,6 +3375,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, @@ -3354,11 +3487,11 @@ void InitGame(void) SetGameStatus(GAME_MODE_PLAYING); if (level_editor_test_game) - FadeSkipNextFadeIn(); + FadeSkipNextFadeOut(); else FadeSetEnterScreen(); - if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged()) + if (CheckFadeAll()) fade_mask = REDRAW_ALL; FadeLevelSoundsAndMusic(); @@ -3367,6 +3500,9 @@ void InitGame(void) FadeOut(fade_mask); + if (level_editor_test_game) + FadeSkipNextFadeIn(); + // needed if different viewport properties defined for playing ChangeViewportPropertiesIfNeeded(); @@ -3379,6 +3515,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); @@ -3401,6 +3544,7 @@ void InitGame(void) player->action = 0; player->effective_action = 0; player->programmed_action = 0; + player->snap_action = 0; player->mouse_action.lx = 0; player->mouse_action.ly = 0; @@ -3543,9 +3687,6 @@ void InitGame(void) if (network_playing) SendToServer_MovePlayer(MV_NONE); - ZX = ZY = -1; - ExitX = ExitY = -1; - FrameCounter = 0; TimeFrames = 0; TimePlayed = 0; @@ -3558,11 +3699,19 @@ void InitGame(void) ScrollStepSize = 0; // will be correctly initialized by ScrollScreen() + game.robot_wheel_x = -1; + game.robot_wheel_y = -1; + + game.exit_x = -1; + game.exit_y = -1; + game.all_players_gone = FALSE; game.LevelSolved = FALSE; game.GameOver = FALSE; + game.GamePlayed = !tape.playing; + game.LevelSolved_GameWon = FALSE; game.LevelSolved_GameEnd = FALSE; game.LevelSolved_SaveTape = FALSE; @@ -3601,7 +3750,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; @@ -3732,6 +3881,20 @@ void InitGame(void) game.belt_dir_nr[i] = 3; // not moving, next moving left #if USE_NEW_PLAYER_ASSIGNMENTS + // use preferred player also in local single-player mode + if (!network.enabled && !game.team_mode) + { + int new_index_nr = setup.network_player_nr; + + if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS) + { + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].connected_locally = FALSE; + + stored_player[new_index_nr].connected_locally = TRUE; + } + } + for (i = 0; i < MAX_PLAYERS; i++) { stored_player[i].connected = FALSE; @@ -3909,6 +4072,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_wrap = FALSE; if (network_playing && tape.recording) { @@ -4244,12 +4408,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; @@ -4342,6 +4500,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)) { @@ -4467,7 +4631,7 @@ static void LevelSolved(void) game.GameOver = TRUE; game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - level.native_em_level->lev->score : + game_em.lev->score : level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score : game.score); @@ -4498,7 +4662,7 @@ void GameWon(void) int i; // do not start end game actions before the player stops moving (to exit) - if (local_player->MovPos) + if (local_player->active && local_player->MovPos) return; game.LevelSolved_GameWon = TRUE; @@ -4570,30 +4734,35 @@ void GameWon(void) if (level.game_engine_type == GAME_ENGINE_TYPE_RND) { - if (ExitX >= 0 && ExitY >= 0) // local player has left the level + // check if last player has left the level + if (game.exit_x >= 0 && + game.exit_y >= 0) { + int x = game.exit_x; + int y = game.exit_y; + int element = Feld[x][y]; + // close exit door after last player if ((game.all_players_gone && - (Feld[ExitX][ExitY] == EL_EXIT_OPEN || - Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN || - Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) || - Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN || - Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN) + (element == EL_EXIT_OPEN || + element == EL_SP_EXIT_OPEN || + element == EL_STEEL_EXIT_OPEN)) || + element == EL_EM_EXIT_OPEN || + element == EL_EM_STEEL_EXIT_OPEN) { - int element = Feld[ExitX][ExitY]; - Feld[ExitX][ExitY] = + Feld[x][y] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING : element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING : element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING: element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING: EL_EM_STEEL_EXIT_CLOSING); - PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING); + PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING); } // player disappears - DrawLevelField(ExitX, ExitY); + DrawLevelField(x, y); } for (i = 0; i < MAX_PLAYERS; i++) @@ -5248,11 +5417,8 @@ static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, while (scroll_x != new_scroll_x || scroll_y != new_scroll_y) { - int dx = 0, dy = 0; - int fx = FX, fy = FY; - - dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0); - dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0); + int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0); + int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0); if (dx == 0 && dy == 0) // no scrolling needed at all break; @@ -5260,14 +5426,19 @@ static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, scroll_x -= dx; scroll_y -= dy; - fx += dx * TILEX / 2; - fy += dy * TILEY / 2; + // set values for horizontal/vertical screen scrolling (half tile size) + int dir_x = (dx != 0 ? MV_HORIZONTAL : 0); + int dir_y = (dy != 0 ? MV_VERTICAL : 0); + int pos_x = dx * TILEX / 2; + int pos_y = dy * TILEY / 2; + int fx = getFieldbufferOffsetX_RND(dir_x, pos_x); + int fy = getFieldbufferOffsetY_RND(dir_y, pos_y); ScrollLevel(dx, dy); DrawAllPlayers(); // scroll in two steps of half tile size to make things smoother - BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY); + BlitScreenToBitmapExt_RND(window, fx, fy); // scroll second step to align at full tile size BlitScreenToBitmap(window); @@ -6815,8 +6986,8 @@ static void TurnRoundExt(int x, int y) if (game.all_players_gone) { - attr_x = ExitX; - attr_y = ExitY; + attr_x = game.exit_x; + attr_y = game.exit_y; } else { @@ -6839,12 +7010,14 @@ static void TurnRoundExt(int x, int y) } } - if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 && - (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE || + if (element == EL_ROBOT && + game.robot_wheel_x >= 0 && + game.robot_wheel_y >= 0 && + (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE || game.engine_version < VERSION_IDENT(3,1,0,0))) { - attr_x = ZX; - attr_y = ZY; + attr_x = game.robot_wheel_x; + attr_y = game.robot_wheel_y; } if (element == EL_PENGUIN) @@ -7184,8 +7357,8 @@ static void TurnRoundExt(int x, int y) if (game.all_players_gone) { - attr_x = ExitX; - attr_y = ExitY; + attr_x = game.exit_x; + attr_y = game.exit_y; } else { @@ -9026,10 +9199,11 @@ static void RunRobotWheel(int x, int y) static void StopRobotWheel(int x, int y) { - if (ZX == x && ZY == y) + if (game.robot_wheel_x == x && + game.robot_wheel_y == y) { - ZX = ZY = -1; - + game.robot_wheel_x = -1; + game.robot_wheel_y = -1; game.robot_wheel_active = FALSE; } } @@ -9837,11 +10011,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; } @@ -10117,6 +10294,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); } @@ -10140,6 +10320,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); @@ -11031,10 +11214,7 @@ static void CheckSingleStepMode(struct PlayerInfo *player) if (!player->is_moving && !player->is_pushing && !player->is_dropping_pressed) - { TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); - SnapField(player, 0, 0); // stop snapping - } } CheckSaveEngineSnapshot(player); @@ -11099,7 +11279,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]; @@ -11110,7 +11290,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; @@ -11208,7 +11388,7 @@ static void CheckLevelTime(void) if (!TimeLeft && setup.time_limit) { if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - level.native_em_level->lev->killed_out_of_time = TRUE; + game_em.lev->killed_out_of_time = TRUE; else for (i = 0; i < MAX_PLAYERS; i++) KillPlayer(&stored_player[i]); @@ -11219,8 +11399,7 @@ static void CheckLevelTime(void) game_panel_controls[GAME_PANEL_TIME].value = TimePlayed; } - level.native_em_level->lev->time = - (game.no_time_limit ? TimePlayed : TimeLeft); + game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft); } if (tape.recording || tape.playing) @@ -11309,7 +11488,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 @@ -11353,6 +11532,15 @@ static void GameActionsExt(void) SetVideoFrameDelay(game_frame_delay_value); + // (de)activate virtual buttons depending on current game status + if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS)) + { + if (game.all_players_gone) // if no players there to be controlled anymore + SetOverlayActive(FALSE); + else if (!tape.playing) // if game continues after tape stopped playing + SetOverlayActive(TRUE); + } + #if 0 #if 0 // ---------- main game synchronization point ---------- @@ -11429,13 +11617,14 @@ static void GameActionsExt(void) stored_player[map_player_action[local_player->index_nr]].effective_action = summarized_player_action; + // summarize all actions at centered player in local team mode if (tape.recording && - setup.team_mode && + setup.team_mode && !network.enabled && setup.input_on_focus && game.centered_player_nr != -1) { for (i = 0; i < MAX_PLAYERS; i++) - stored_player[i].effective_action = + stored_player[map_player_action[i]].effective_action = (i == game.centered_player_nr ? summarized_player_action : 0); } @@ -11463,6 +11652,10 @@ static void GameActionsExt(void) if (tape.recording) TapeRecordAction(tape_action); + // remember if game was played (especially after tape stopped playing) + if (!tape.playing && summarized_player_action) + game.GamePlayed = TRUE; + #if USE_NEW_PLAYER_ASSIGNMENTS // !!! also map player actions in single player mode !!! // if (game.team_mode) @@ -11628,6 +11821,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; @@ -11814,6 +12009,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]; @@ -11956,7 +12169,8 @@ void GameActions_RND(void) element == EL_DC_MAGIC_WALL_FULL || element == EL_DC_MAGIC_WALL_ACTIVE || element == EL_DC_MAGIC_WALL_EMPTYING) && - ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy)) + ABS(x - jx) + ABS(y - jy) < + ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy)) { magic_wall_x = x; magic_wall_y = y; @@ -12138,16 +12352,24 @@ void GameActions_RND(void) DrawAllPlayers(); PlayAllPlayersSound(); - if (local_player->show_envelope != 0 && local_player->MovPos == 0) + for (i = 0; i < MAX_PLAYERS; i++) { - ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1); + struct PlayerInfo *player = &stored_player[i]; - local_player->show_envelope = 0; + if (player->show_envelope != 0 && (!player->active || + player->MovPos == 0)) + { + ShowEnvelope(player->show_envelope - EL_ENVELOPE_1); + + player->show_envelope = 0; + } } // 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) @@ -12473,7 +12695,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) game.centered_player_nr == -1)) { int old_scroll_x = scroll_x, old_scroll_y = scroll_y; - int offset = game.scroll_delay_value; if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy))) { @@ -12485,15 +12706,20 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) } else { + int offset_raw = game.scroll_delay_value; + if (jx != old_jx) // player has moved horizontally { - if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) || - (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset)) - scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset); + int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2); + int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1); + int new_scroll_x = jx - MIDPOSX + offset_x; + + if ((player->MovDir == MV_LEFT && scroll_x > new_scroll_x) || + (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x)) + scroll_x = new_scroll_x; // don't scroll over playfield boundaries - if (scroll_x < SBX_Left || scroll_x > SBX_Right) - scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right); + scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right); // don't scroll more than one field at a time scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x); @@ -12505,13 +12731,16 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) } else // player has moved vertically { - if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) || - (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset)) - scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset); + int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2); + int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1); + int new_scroll_y = jy - MIDPOSY + offset_y; + + if ((player->MovDir == MV_UP && scroll_y > new_scroll_y) || + (player->MovDir == MV_DOWN && scroll_y < new_scroll_y)) + scroll_y = new_scroll_y; // don't scroll over playfield boundaries - if (scroll_y < SBY_Upper || scroll_y > SBY_Lower) - scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower); + scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower); // don't scroll more than one field at a time scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y); @@ -13425,6 +13654,9 @@ void RemovePlayer(struct PlayerInfo *player) player->present = FALSE; player->active = FALSE; + // required for some CE actions (even if the player is not active anymore) + player->MovPos = 0; + if (!ExplodeField[jx][jy]) StorePlayer[jx][jy] = 0; @@ -13441,8 +13673,8 @@ void RemovePlayer(struct PlayerInfo *player) game.GameOver = TRUE; } - ExitX = ZX = jx; - ExitY = ZY = jy; + game.exit_x = game.robot_wheel_x = jx; + game.exit_y = game.robot_wheel_y = jy; } void ExitPlayer(struct PlayerInfo *player) @@ -14065,9 +14297,9 @@ static int DigField(struct PlayerInfo *player, if (element == EL_ROBOT_WHEEL) { Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE; - ZX = x; - ZY = y; + game.robot_wheel_x = x; + game.robot_wheel_y = y; game.robot_wheel_active = TRUE; TEST_DrawLevelField(x, y); @@ -14156,13 +14388,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); @@ -14639,78 +14871,78 @@ 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) { - case SAMPLE_blank: + case SOUND_blank: PlayLevelSoundElementAction(x, y, element, ACTION_WALKING); break; - case SAMPLE_roll: + case SOUND_roll: PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING); break; - case SAMPLE_stone: + case SOUND_stone: PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT); break; - case SAMPLE_nut: + case SOUND_nut: PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT); break; - case SAMPLE_crack: + case SOUND_crack: PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING); break; - case SAMPLE_bug: + case SOUND_bug: PlayLevelSoundElementAction(x, y, element, ACTION_MOVING); break; - case SAMPLE_tank: + case SOUND_tank: PlayLevelSoundElementAction(x, y, element, ACTION_MOVING); break; - case SAMPLE_android_clone: + case SOUND_android_clone: PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING); break; - case SAMPLE_android_move: + case SOUND_android_move: PlayLevelSoundElementAction(x, y, element, ACTION_MOVING); break; - case SAMPLE_spring: + case SOUND_spring: PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT); break; - case SAMPLE_slurp: + case SOUND_slurp: PlayLevelSoundElementAction(x, y, element, ACTION_EATING); break; - case SAMPLE_eater: + case SOUND_eater: PlayLevelSoundElementAction(x, y, element, ACTION_WAITING); break; - case SAMPLE_eater_eat: + case SOUND_eater_eat: PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING); break; - case SAMPLE_alien: + case SOUND_alien: PlayLevelSoundElementAction(x, y, element, ACTION_MOVING); break; - case SAMPLE_collect: + case SOUND_collect: PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING); break; - case SAMPLE_diamond: + case SOUND_diamond: PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT); break; - case SAMPLE_squash: + case SOUND_squash: // !!! CHECK THIS !!! #if 1 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING); @@ -14719,75 +14951,75 @@ void PlayLevelSound_EM(int xx, int yy, int element_em, int sample) #endif break; - case SAMPLE_wonderfall: + case SOUND_wonderfall: PlayLevelSoundElementAction(x, y, element, ACTION_FILLING); break; - case SAMPLE_drip: + case SOUND_drip: PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT); break; - case SAMPLE_push: + case SOUND_push: PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING); break; - case SAMPLE_dirt: + case SOUND_dirt: PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING); break; - case SAMPLE_acid: + case SOUND_acid: PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING); break; - case SAMPLE_ball: + case SOUND_ball: PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING); break; - case SAMPLE_grow: + case SOUND_slide: PlayLevelSoundElementAction(x, y, element, ACTION_GROWING); break; - case SAMPLE_wonder: + case SOUND_wonder: PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE); break; - case SAMPLE_door: + case SOUND_door: PlayLevelSoundElementAction(x, y, element, ACTION_PASSING); break; - case SAMPLE_exit_open: + case SOUND_exit_open: PlayLevelSoundElementAction(x, y, element, ACTION_OPENING); break; - case SAMPLE_exit_leave: + case SOUND_exit_leave: PlayLevelSoundElementAction(x, y, element, ACTION_PASSING); break; - case SAMPLE_dynamite: + case SOUND_dynamite: PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING); break; - case SAMPLE_tick: + case SOUND_tick: PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE); break; - case SAMPLE_press: + case SOUND_press: PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING); break; - case SAMPLE_wheel: + case SOUND_wheel: PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE); break; - case SAMPLE_boom: + case SOUND_boom: PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING); break; - case SAMPLE_die: + case SOUND_die: PlayLevelSoundElementAction(x, y, element, ACTION_DYING); break; - case SAMPLE_time: + case SOUND_time: PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); break; @@ -14947,7 +15179,12 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) { // closing door required in case of envelope style request dialogs if (!skip_request) + { + // prevent short reactivation of overlay buttons while closing door + SetOverlayActive(FALSE); + CloseDoor(DOOR_CLOSE_1); + } if (network.enabled) SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER); @@ -15012,6 +15249,10 @@ void CheckGameOver(void) if (game.request_active) return; + // do not ask to play again if game was never actually played + if (!game.GamePlayed) + return; + if (!game_over) { last_game_over = FALSE; @@ -15234,11 +15475,6 @@ static ListNode *SaveEngineSnapshotBuffers(void) SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed)); @@ -15412,93 +15648,104 @@ static struct int gadget_id; boolean *setup_value; boolean allowed_on_tape; + boolean is_touch_button; char *infotext; } gamebutton_info[NUM_GAME_BUTTONS] = { { IMG_GFX_GAME_BUTTON_STOP, &game.button.stop, GAME_CTRL_ID_STOP, NULL, - TRUE, "stop game" + TRUE, FALSE, "stop game" }, { IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause, GAME_CTRL_ID_PAUSE, NULL, - TRUE, "pause game" + TRUE, FALSE, "pause game" }, { IMG_GFX_GAME_BUTTON_PLAY, &game.button.play, GAME_CTRL_ID_PLAY, NULL, - TRUE, "play game" + TRUE, FALSE, "play game" }, { IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo, GAME_CTRL_ID_UNDO, NULL, - TRUE, "undo step" + TRUE, FALSE, "undo step" }, { IMG_GFX_GAME_BUTTON_REDO, &game.button.redo, GAME_CTRL_ID_REDO, NULL, - TRUE, "redo step" + TRUE, FALSE, "redo step" }, { IMG_GFX_GAME_BUTTON_SAVE, &game.button.save, GAME_CTRL_ID_SAVE, NULL, - TRUE, "save game" + TRUE, FALSE, "save game" }, { IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2, GAME_CTRL_ID_PAUSE2, NULL, - TRUE, "pause game" + TRUE, FALSE, "pause game" }, { IMG_GFX_GAME_BUTTON_LOAD, &game.button.load, GAME_CTRL_ID_LOAD, NULL, - TRUE, "load game" + TRUE, FALSE, "load game" }, { IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop, GAME_CTRL_ID_PANEL_STOP, NULL, - FALSE, "stop game" + FALSE, FALSE, "stop game" }, { IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause, GAME_CTRL_ID_PANEL_PAUSE, NULL, - FALSE, "pause game" + FALSE, FALSE, "pause game" }, { IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play, GAME_CTRL_ID_PANEL_PLAY, NULL, - FALSE, "play game" + FALSE, FALSE, "play game" + }, + { + IMG_GFX_GAME_BUTTON_TOUCH_STOP, &game.button.touch_stop, + GAME_CTRL_ID_TOUCH_STOP, NULL, + FALSE, TRUE, "stop game" + }, + { + IMG_GFX_GAME_BUTTON_TOUCH_PAUSE, &game.button.touch_pause, + GAME_CTRL_ID_TOUCH_PAUSE, NULL, + FALSE, TRUE, "pause game" }, { IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music, SOUND_CTRL_ID_MUSIC, &setup.sound_music, - TRUE, "background music on/off" + TRUE, FALSE, "background music on/off" }, { IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops, SOUND_CTRL_ID_LOOPS, &setup.sound_loops, - TRUE, "sound loops on/off" + TRUE, FALSE, "sound loops on/off" }, { IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple, SOUND_CTRL_ID_SIMPLE, &setup.sound_simple, - TRUE, "normal sounds on/off" + TRUE, FALSE, "normal sounds on/off" }, { IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music, SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music, - FALSE, "background music on/off" + FALSE, FALSE, "background music on/off" }, { IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops, SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops, - FALSE, "sound loops on/off" + FALSE, FALSE, "sound loops on/off" }, { IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple, SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple, - FALSE, "normal sounds on/off" + FALSE, FALSE, "normal sounds on/off" } }; @@ -15515,10 +15762,11 @@ void CreateGameButtons(void) int button_type; boolean checked; unsigned int event_mask; + boolean is_touch_button = gamebutton_info[i].is_touch_button; boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape; boolean on_tape = (tape.show_game_buttons && allowed_on_tape); - int base_x = (on_tape ? VX : DX); - int base_y = (on_tape ? VY : DY); + int base_x = (is_touch_button ? 0 : on_tape ? VX : DX); + int base_y = (is_touch_button ? 0 : on_tape ? VY : DY); int gd_x = gfx->src_x; int gd_y = gfx->src_y; int gd_xp = gfx->src_x + gfx->pressed_xoffset; @@ -15527,6 +15775,8 @@ void CreateGameButtons(void) int gd_ya = gfx->src_y + gfx->active_yoffset; int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset; int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset; + int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x)); + int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y)); int id = i; if (gfx->bitmap == NULL) @@ -15538,6 +15788,7 @@ void CreateGameButtons(void) if (id == GAME_CTRL_ID_STOP || id == GAME_CTRL_ID_PANEL_STOP || + id == GAME_CTRL_ID_TOUCH_STOP || id == GAME_CTRL_ID_PLAY || id == GAME_CTRL_ID_PANEL_PLAY || id == GAME_CTRL_ID_SAVE || @@ -15565,8 +15816,8 @@ void CreateGameButtons(void) gi = CreateGadget(GDI_CUSTOM_ID, id, GDI_IMAGE_ID, graphic, GDI_INFO_TEXT, gamebutton_info[i].infotext, - GDI_X, base_x + GDI_ACTIVE_POS(pos->x), - GDI_Y, base_y + GDI_ACTIVE_POS(pos->y), + GDI_X, base_x + x, + GDI_Y, base_y + y, GDI_WIDTH, gfx->width, GDI_HEIGHT, gfx->height, GDI_TYPE, button_type, @@ -15577,6 +15828,7 @@ void CreateGameButtons(void) GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya, GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap, GDI_DIRECT_DRAW, FALSE, + GDI_OVERLAY_TOUCH_BUTTON, is_touch_button, GDI_EVENT_MASK, event_mask, GDI_CALLBACK_ACTION, HandleGameButtons, GDI_END); @@ -15647,8 +15899,6 @@ void MapUndoRedoButtons(void) MapGadget(game_gadget[GAME_CTRL_ID_UNDO]); MapGadget(game_gadget[GAME_CTRL_ID_REDO]); - - ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END); } void UnmapUndoRedoButtons(void) @@ -15658,8 +15908,22 @@ void UnmapUndoRedoButtons(void) MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); +} + +void ModifyPauseButtons(void) +{ + static int ids[] = + { + GAME_CTRL_ID_PAUSE, + GAME_CTRL_ID_PAUSE2, + GAME_CTRL_ID_PANEL_PAUSE, + GAME_CTRL_ID_TOUCH_PAUSE, + -1 + }; + int i; - ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END); + for (i = 0; ids[i] > -1; i++) + ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END); } static void MapGameButtonsExt(boolean on_tape) @@ -15693,9 +15957,6 @@ static void RedrawGameButtonsExt(boolean on_tape) for (i = 0; i < NUM_GAME_BUTTONS; i++) if (!on_tape || gamebutton_info[i].allowed_on_tape) RedrawGadget(game_gadget[i]); - - // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area - redraw_mask &= ~REDRAW_ALL; } static void SetGadgetState(struct GadgetInfo *gi, boolean state) @@ -15802,6 +16063,7 @@ static void HandleGameButtonsExt(int id, int button) { case GAME_CTRL_ID_STOP: case GAME_CTRL_ID_PANEL_STOP: + case GAME_CTRL_ID_TOUCH_STOP: if (game_status == GAME_MODE_MAIN) break; @@ -15815,6 +16077,7 @@ static void HandleGameButtonsExt(int id, int button) case GAME_CTRL_ID_PAUSE: case GAME_CTRL_ID_PAUSE2: case GAME_CTRL_ID_PANEL_PAUSE: + case GAME_CTRL_ID_TOUCH_PAUSE: if (network.enabled && game_status == GAME_MODE_PLAYING) { if (tape.pausing)