X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=2e951c1761a96476b13fa9436cb5d6e0d254a2dc;hp=7d83c63f8800dca7c19354af58e6c83de955348f;hb=5c6c42f1;hpb=7e7bea870822b12f26c2266d0bcc23db6a1684f4 diff --git a/src/game.c b/src/game.c index 7d83c63f..1de51be5 100644 --- a/src/game.c +++ b/src/game.c @@ -1,15 +1,13 @@ -/*********************************************************** -* Rocks'n'Diamonds -- McDuffin Strikes Back! * -*----------------------------------------------------------* -* (c) 1995-2006 Artsoft Entertainment * -* Holger Schemel * -* Detmolder Strasse 189 * -* 33604 Bielefeld * -* Germany * -* e-mail: info@artsoft.org * -*----------------------------------------------------------* -* game.c * -***********************************************************/ +// ============================================================================ +// Rocks'n'Diamonds - McDuffin Strikes Back! +// ---------------------------------------------------------------------------- +// (c) 1995-2014 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// game.c +// ============================================================================ #include "libgame/libgame.h" @@ -17,49 +15,45 @@ #include "init.h" #include "tools.h" #include "screens.h" +#include "events.h" #include "files.h" #include "tape.h" #include "network.h" +#include "anim.h" + + +/* DEBUG SETTINGS */ +#define DEBUG_INIT_PLAYER 1 +#define DEBUG_PLAYER_ACTIONS 0 /* EXPERIMENTAL STUFF */ #define USE_NEW_AMOEBA_CODE FALSE /* EXPERIMENTAL STUFF */ -#define USE_NEW_STUFF ( 1) - -#define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1) -#define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1) -#define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1) -#define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1) -#define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1) -#define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1) -#define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1) -#define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1) -#define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1) -#define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1) -#define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1) -#define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1) -#define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1) -#define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1) -#define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1) -#define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1) -#define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1) -#define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1) -#define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0) - -#define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0) - -#define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1) - -#define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1) - -#define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1) -#define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1) - -#define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1) -#define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1) - -#define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1) +#define USE_QUICKSAND_BD_ROCK_BUGFIX 0 +#define USE_QUICKSAND_IMPACT_BUGFIX 0 +#define USE_DELAYED_GFX_REDRAW 0 +#define USE_NEW_PLAYER_ASSIGNMENTS 1 + +#if USE_DELAYED_GFX_REDRAW +#define TEST_DrawLevelField(x, y) \ + GfxRedraw[x][y] |= GFX_REDRAW_TILE +#define TEST_DrawLevelFieldCrumbled(x, y) \ + GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED +#define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \ + GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS +#define TEST_DrawTwinkleOnField(x, y) \ + GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED +#else +#define TEST_DrawLevelField(x, y) \ + DrawLevelField(x, y) +#define TEST_DrawLevelFieldCrumbled(x, y) \ + DrawLevelFieldCrumbled(x, y) +#define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \ + DrawLevelFieldCrumbledNeighbours(x, y) +#define TEST_DrawTwinkleOnField(x, y) \ + DrawTwinkleOnField(x, y) +#endif /* for DigField() */ @@ -92,45 +86,7 @@ #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p)) #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p)) -/* special positions in the game control window (relative to control window) */ -#define XX_LEVEL1 (PANEL_XPOS(game.panel.level)) -#define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1) -#define XX_LEVEL (PANEL_XPOS(game.panel.level)) -#define YY_LEVEL (PANEL_YPOS(game.panel.level)) -#define XX_EMERALDS (PANEL_XPOS(game.panel.gems)) -#define YY_EMERALDS (PANEL_YPOS(game.panel.gems)) -#define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory)) -#define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory)) -#define XX_KEYS (PANEL_XPOS(game.panel.keys)) -#define YY_KEYS (PANEL_YPOS(game.panel.keys)) -#define XX_SCORE (PANEL_XPOS(game.panel.score)) -#define YY_SCORE (PANEL_YPOS(game.panel.score)) -#define XX_TIME1 (PANEL_XPOS(game.panel.time)) -#define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1) -#define XX_TIME (PANEL_XPOS(game.panel.time)) -#define YY_TIME (PANEL_YPOS(game.panel.time)) - -/* special positions in the game control window (relative to main window) */ -#define DX_LEVEL1 (DX + XX_LEVEL1) -#define DX_LEVEL2 (DX + XX_LEVEL2) -#define DX_LEVEL (DX + XX_LEVEL) -#define DY_LEVEL (DY + YY_LEVEL) -#define DX_EMERALDS (DX + XX_EMERALDS) -#define DY_EMERALDS (DY + YY_EMERALDS) -#define DX_DYNAMITE (DX + XX_DYNAMITE) -#define DY_DYNAMITE (DY + YY_DYNAMITE) -#define DX_KEYS (DX + XX_KEYS) -#define DY_KEYS (DY + YY_KEYS) -#define DX_SCORE (DX + XX_SCORE) -#define DY_SCORE (DY + YY_SCORE) -#define DX_TIME1 (DX + XX_TIME1) -#define DX_TIME2 (DX + XX_TIME2) -#define DX_TIME (DX + XX_TIME) -#define DY_TIME (DY + YY_TIME) - -#if 1 /* game panel display and control definitions */ - #define GAME_PANEL_LEVEL_NUMBER 0 #define GAME_PANEL_GEMS 1 #define GAME_PANEL_INVENTORY_COUNT 2 @@ -161,90 +117,95 @@ #define GAME_PANEL_KEY_WHITE 27 #define GAME_PANEL_KEY_WHITE_COUNT 28 #define GAME_PANEL_SCORE 29 -#define GAME_PANEL_TIME 30 -#define GAME_PANEL_TIME_HH 31 -#define GAME_PANEL_TIME_MM 32 -#define GAME_PANEL_TIME_SS 33 -#define GAME_PANEL_SHIELD_NORMAL 34 -#define GAME_PANEL_SHIELD_NORMAL_TIME 35 -#define GAME_PANEL_SHIELD_DEADLY 36 -#define GAME_PANEL_SHIELD_DEADLY_TIME 37 -#define GAME_PANEL_EXIT 38 -#define GAME_PANEL_EMC_MAGIC_BALL 39 -#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 40 -#define GAME_PANEL_LIGHT_SWITCH 41 -#define GAME_PANEL_LIGHT_SWITCH_TIME 42 -#define GAME_PANEL_TIMEGATE_SWITCH 43 -#define GAME_PANEL_TIMEGATE_SWITCH_TIME 44 -#define GAME_PANEL_SWITCHGATE_SWITCH 45 -#define GAME_PANEL_EMC_LENSES 46 -#define GAME_PANEL_EMC_LENSES_TIME 47 -#define GAME_PANEL_EMC_MAGNIFIER 48 -#define GAME_PANEL_EMC_MAGNIFIER_TIME 49 -#define GAME_PANEL_BALLOON_SWITCH 50 -#define GAME_PANEL_DYNABOMB_NUMBER 51 -#define GAME_PANEL_DYNABOMB_SIZE 52 -#define GAME_PANEL_DYNABOMB_POWER 53 -#define GAME_PANEL_PENGUINS 54 -#define GAME_PANEL_SOKOBAN_OBJECTS 55 -#define GAME_PANEL_SOKOBAN_FIELDS 56 -#define GAME_PANEL_ROBOT_WHEEL 57 -#define GAME_PANEL_CONVEYOR_BELT_1 58 -#define GAME_PANEL_CONVEYOR_BELT_2 59 -#define GAME_PANEL_CONVEYOR_BELT_3 60 -#define GAME_PANEL_CONVEYOR_BELT_4 61 -#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 62 -#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 63 -#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 64 -#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 65 -#define GAME_PANEL_MAGIC_WALL 66 -#define GAME_PANEL_MAGIC_WALL_TIME 67 -#define GAME_PANEL_GRAVITY_STATE 68 -#define GAME_PANEL_GRAPHIC_1 69 -#define GAME_PANEL_GRAPHIC_2 70 -#define GAME_PANEL_GRAPHIC_3 71 -#define GAME_PANEL_GRAPHIC_4 72 -#define GAME_PANEL_GRAPHIC_5 73 -#define GAME_PANEL_GRAPHIC_6 74 -#define GAME_PANEL_GRAPHIC_7 75 -#define GAME_PANEL_GRAPHIC_8 76 -#define GAME_PANEL_ELEMENT_1 77 -#define GAME_PANEL_ELEMENT_2 78 -#define GAME_PANEL_ELEMENT_3 79 -#define GAME_PANEL_ELEMENT_4 80 -#define GAME_PANEL_ELEMENT_5 81 -#define GAME_PANEL_ELEMENT_6 82 -#define GAME_PANEL_ELEMENT_7 83 -#define GAME_PANEL_ELEMENT_8 84 -#define GAME_PANEL_ELEMENT_COUNT_1 85 -#define GAME_PANEL_ELEMENT_COUNT_2 86 -#define GAME_PANEL_ELEMENT_COUNT_3 87 -#define GAME_PANEL_ELEMENT_COUNT_4 88 -#define GAME_PANEL_ELEMENT_COUNT_5 89 -#define GAME_PANEL_ELEMENT_COUNT_6 90 -#define GAME_PANEL_ELEMENT_COUNT_7 91 -#define GAME_PANEL_ELEMENT_COUNT_8 92 -#define GAME_PANEL_CE_SCORE_1 93 -#define GAME_PANEL_CE_SCORE_2 94 -#define GAME_PANEL_CE_SCORE_3 95 -#define GAME_PANEL_CE_SCORE_4 96 -#define GAME_PANEL_CE_SCORE_5 97 -#define GAME_PANEL_CE_SCORE_6 98 -#define GAME_PANEL_CE_SCORE_7 99 -#define GAME_PANEL_CE_SCORE_8 100 -#define GAME_PANEL_CE_SCORE_1_ELEMENT 101 -#define GAME_PANEL_CE_SCORE_2_ELEMENT 102 -#define GAME_PANEL_CE_SCORE_3_ELEMENT 103 -#define GAME_PANEL_CE_SCORE_4_ELEMENT 104 -#define GAME_PANEL_CE_SCORE_5_ELEMENT 105 -#define GAME_PANEL_CE_SCORE_6_ELEMENT 106 -#define GAME_PANEL_CE_SCORE_7_ELEMENT 107 -#define GAME_PANEL_CE_SCORE_8_ELEMENT 108 -#define GAME_PANEL_PLAYER_NAME 109 -#define GAME_PANEL_LEVEL_NAME 110 -#define GAME_PANEL_LEVEL_AUTHOR 111 - -#define NUM_GAME_PANEL_CONTROLS 112 +#define GAME_PANEL_HIGHSCORE 30 +#define GAME_PANEL_TIME 31 +#define GAME_PANEL_TIME_HH 32 +#define GAME_PANEL_TIME_MM 33 +#define GAME_PANEL_TIME_SS 34 +#define GAME_PANEL_TIME_ANIM 35 +#define GAME_PANEL_HEALTH 36 +#define GAME_PANEL_HEALTH_ANIM 37 +#define GAME_PANEL_FRAME 38 +#define GAME_PANEL_SHIELD_NORMAL 39 +#define GAME_PANEL_SHIELD_NORMAL_TIME 40 +#define GAME_PANEL_SHIELD_DEADLY 41 +#define GAME_PANEL_SHIELD_DEADLY_TIME 42 +#define GAME_PANEL_EXIT 43 +#define GAME_PANEL_EMC_MAGIC_BALL 44 +#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45 +#define GAME_PANEL_LIGHT_SWITCH 46 +#define GAME_PANEL_LIGHT_SWITCH_TIME 47 +#define GAME_PANEL_TIMEGATE_SWITCH 48 +#define GAME_PANEL_TIMEGATE_SWITCH_TIME 49 +#define GAME_PANEL_SWITCHGATE_SWITCH 50 +#define GAME_PANEL_EMC_LENSES 51 +#define GAME_PANEL_EMC_LENSES_TIME 52 +#define GAME_PANEL_EMC_MAGNIFIER 53 +#define GAME_PANEL_EMC_MAGNIFIER_TIME 54 +#define GAME_PANEL_BALLOON_SWITCH 55 +#define GAME_PANEL_DYNABOMB_NUMBER 56 +#define GAME_PANEL_DYNABOMB_SIZE 57 +#define GAME_PANEL_DYNABOMB_POWER 58 +#define GAME_PANEL_PENGUINS 59 +#define GAME_PANEL_SOKOBAN_OBJECTS 60 +#define GAME_PANEL_SOKOBAN_FIELDS 61 +#define GAME_PANEL_ROBOT_WHEEL 62 +#define GAME_PANEL_CONVEYOR_BELT_1 63 +#define GAME_PANEL_CONVEYOR_BELT_2 64 +#define GAME_PANEL_CONVEYOR_BELT_3 65 +#define GAME_PANEL_CONVEYOR_BELT_4 66 +#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67 +#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68 +#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69 +#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70 +#define GAME_PANEL_MAGIC_WALL 71 +#define GAME_PANEL_MAGIC_WALL_TIME 72 +#define GAME_PANEL_GRAVITY_STATE 73 +#define GAME_PANEL_GRAPHIC_1 74 +#define GAME_PANEL_GRAPHIC_2 75 +#define GAME_PANEL_GRAPHIC_3 76 +#define GAME_PANEL_GRAPHIC_4 77 +#define GAME_PANEL_GRAPHIC_5 78 +#define GAME_PANEL_GRAPHIC_6 79 +#define GAME_PANEL_GRAPHIC_7 80 +#define GAME_PANEL_GRAPHIC_8 81 +#define GAME_PANEL_ELEMENT_1 82 +#define GAME_PANEL_ELEMENT_2 83 +#define GAME_PANEL_ELEMENT_3 84 +#define GAME_PANEL_ELEMENT_4 85 +#define GAME_PANEL_ELEMENT_5 86 +#define GAME_PANEL_ELEMENT_6 87 +#define GAME_PANEL_ELEMENT_7 88 +#define GAME_PANEL_ELEMENT_8 89 +#define GAME_PANEL_ELEMENT_COUNT_1 90 +#define GAME_PANEL_ELEMENT_COUNT_2 91 +#define GAME_PANEL_ELEMENT_COUNT_3 92 +#define GAME_PANEL_ELEMENT_COUNT_4 93 +#define GAME_PANEL_ELEMENT_COUNT_5 94 +#define GAME_PANEL_ELEMENT_COUNT_6 95 +#define GAME_PANEL_ELEMENT_COUNT_7 96 +#define GAME_PANEL_ELEMENT_COUNT_8 97 +#define GAME_PANEL_CE_SCORE_1 98 +#define GAME_PANEL_CE_SCORE_2 99 +#define GAME_PANEL_CE_SCORE_3 100 +#define GAME_PANEL_CE_SCORE_4 101 +#define GAME_PANEL_CE_SCORE_5 102 +#define GAME_PANEL_CE_SCORE_6 103 +#define GAME_PANEL_CE_SCORE_7 104 +#define GAME_PANEL_CE_SCORE_8 105 +#define GAME_PANEL_CE_SCORE_1_ELEMENT 106 +#define GAME_PANEL_CE_SCORE_2_ELEMENT 107 +#define GAME_PANEL_CE_SCORE_3_ELEMENT 108 +#define GAME_PANEL_CE_SCORE_4_ELEMENT 109 +#define GAME_PANEL_CE_SCORE_5_ELEMENT 110 +#define GAME_PANEL_CE_SCORE_6_ELEMENT 111 +#define GAME_PANEL_CE_SCORE_7_ELEMENT 112 +#define GAME_PANEL_CE_SCORE_8_ELEMENT 113 +#define GAME_PANEL_PLAYER_NAME 114 +#define GAME_PANEL_LEVEL_NAME 115 +#define GAME_PANEL_LEVEL_AUTHOR 116 + +#define NUM_GAME_PANEL_CONTROLS 117 struct GamePanelOrderInfo { @@ -261,6 +222,8 @@ struct GamePanelControlInfo struct TextPosInfo *pos; int type; + int graphic, graphic_active; + int value, last_value; int frame, last_frame; int gfx_frame; @@ -419,6 +382,11 @@ static struct GamePanelControlInfo game_panel_controls[] = &game.panel.score, TYPE_INTEGER, }, + { + GAME_PANEL_HIGHSCORE, + &game.panel.highscore, + TYPE_INTEGER, + }, { GAME_PANEL_TIME, &game.panel.time, @@ -439,6 +407,32 @@ static struct GamePanelControlInfo game_panel_controls[] = &game.panel.time_ss, TYPE_INTEGER, }, + { + GAME_PANEL_TIME_ANIM, + &game.panel.time_anim, + TYPE_GRAPHIC, + + IMG_GFX_GAME_PANEL_TIME_ANIM, + IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE + }, + { + GAME_PANEL_HEALTH, + &game.panel.health, + TYPE_INTEGER, + }, + { + GAME_PANEL_HEALTH_ANIM, + &game.panel.health_anim, + TYPE_GRAPHIC, + + IMG_GFX_GAME_PANEL_HEALTH_ANIM, + IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE + }, + { + GAME_PANEL_FRAME, + &game.panel.frame, + TYPE_INTEGER, + }, { GAME_PANEL_SHIELD_NORMAL, &game.panel.shield_normal, @@ -836,8 +830,6 @@ static struct GamePanelControlInfo game_panel_controls[] = -1, } }; -#endif - /* values for delayed check of falling and moving elements and for collision */ #define CHECK_DELAY_MOVING 3 @@ -861,6 +853,14 @@ static struct GamePanelControlInfo game_panel_controls[] = #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value)) #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value)) +/* values for scroll positions */ +#define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \ + (x) > SBX_Right + MIDPOSX ? SBX_Right :\ + (x) - MIDPOSX) +#define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\ + (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\ + (y) - MIDPOSY) + /* values for other actions */ #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED) #define MOVE_STEPSIZE_MIN (1) @@ -1002,15 +1002,28 @@ static struct GamePanelControlInfo game_panel_controls[] = #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d)) #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d)) +#define MM_HEALTH(x) (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH)) + /* game button identifiers */ #define GAME_CTRL_ID_STOP 0 #define GAME_CTRL_ID_PAUSE 1 #define GAME_CTRL_ID_PLAY 2 -#define SOUND_CTRL_ID_MUSIC 3 -#define SOUND_CTRL_ID_LOOPS 4 -#define SOUND_CTRL_ID_SIMPLE 5 - -#define NUM_GAME_BUTTONS 6 +#define GAME_CTRL_ID_UNDO 3 +#define GAME_CTRL_ID_REDO 4 +#define GAME_CTRL_ID_SAVE 5 +#define GAME_CTRL_ID_PAUSE2 6 +#define GAME_CTRL_ID_LOAD 7 +#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 NUM_GAME_BUTTONS 17 /* forward declaration for internal use */ @@ -1027,7 +1040,10 @@ static boolean MovePlayer(struct PlayerInfo *, int, int); static void ScrollPlayer(struct PlayerInfo *, int); static void ScrollScreen(struct PlayerInfo *, int); -int DigField(struct PlayerInfo *, int, int, int, int, int, int, int); +static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int); +static boolean DigFieldByCE(int, int, int); +static boolean SnapField(struct PlayerInfo *, int, int); +static boolean DropElement(struct PlayerInfo *); static void InitBeltMovement(void); static void CloseAllOpenTimegates(void); @@ -1039,9 +1055,6 @@ static void KillPlayerUnlessExplosionProtected(int, int); static void TestIfPlayerTouchesCustomElement(int, int); static void TestIfElementTouchesCustomElement(int, int); static void TestIfElementHitsCustomElement(int, int, int); -#if 0 -static void TestIfElementSmashesCustomElement(int, int, int); -#endif static void HandleElementChange(int, int, int); static void ExecuteCustomElementAction(int, int, int, int); @@ -1073,8 +1086,8 @@ static void PlayLevelSoundElementActionIfLoop(int, int, int, int); static void PlayLevelSoundActionIfLoop(int, int, int); static void StopLevelSoundActionIfLoop(int, int, int); static void PlayLevelMusic(); +static void FadeLevelSoundsAndMusic(); -static void MapGameButtons(); static void HandleGameButtons(struct GadgetInfo *); int AmoebeNachbarNr(int, int); @@ -1094,14 +1107,12 @@ void TestIfBadThingRunsIntoPlayer(int, int, int); void TestIfFriendTouchesBadThing(int, int); void TestIfBadThingTouchesFriend(int, int); void TestIfBadThingTouchesOtherBadThing(int, int); +void TestIfGoodThingGetsHitByBadThing(int, int, int); void KillPlayer(struct PlayerInfo *); void BuryPlayer(struct PlayerInfo *); void RemovePlayer(struct PlayerInfo *); -boolean SnapField(struct PlayerInfo *, int, int); -boolean DropElement(struct PlayerInfo *); - static int getInvisibleActiveFromInvisibleElement(int); static int getInvisibleFromInvisibleActiveElement(int); @@ -1134,6 +1145,8 @@ static int recursion_loop_depth; static boolean recursion_loop_detected; static boolean recursion_loop_element; +static int map_player_action[MAX_PLAYERS]; + /* ------------------------------------------------------------------------- */ /* definition of elements that automatically change to other elements after */ @@ -1228,11 +1241,7 @@ static struct ChangingElementInfo change_delay_list[] = }, { EL_EM_EXIT_CLOSING, -#if 1 EL_EMPTY, -#else - EL_EM_EXIT_CLOSED, -#endif 29, NULL, NULL, @@ -1248,11 +1257,7 @@ static struct ChangingElementInfo change_delay_list[] = }, { EL_EM_STEEL_EXIT_CLOSING, -#if 1 EL_STEELWALL, -#else - EL_EM_STEEL_EXIT_CLOSED, -#endif 29, NULL, NULL, @@ -1649,7 +1654,6 @@ void GetPlayerConfig() setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music); SetAudioMode(setup.sound); - InitJoysticks(); } int GetElementFromGroupElement(int element) @@ -1692,6 +1696,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) } else { + stored_player[0].initial_element = element; stored_player[0].use_murphy = TRUE; if (!level.use_artwork_element[0]) @@ -1736,20 +1741,31 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) StorePlayer[x][y] = Feld[x][y]; +#if DEBUG_INIT_PLAYER if (options.debug) { - printf("Player %d activated.\n", player->element_nr); - printf("[Local player is %d and currently %s.]\n", + printf("- player element %d activated", player->element_nr); + printf(" (local player is %d and currently %s)\n", local_player->element_nr, local_player->active ? "active" : "not active"); } } +#endif Feld[x][y] = EL_EMPTY; player->jx = player->last_jx = x; player->jy = player->last_jy = y; } + + if (!init_game) + { + int player_nr = GET_PLAYER_NR(element); + struct PlayerInfo *player = &stored_player[player_nr]; + + if (player->active && player->killed) + player->reanimated = TRUE; /* if player was just killed, reanimate him */ + } } static void InitField(int x, int y, boolean init_game) @@ -1902,18 +1918,6 @@ static void InitField(int x, int y, boolean init_game) } break; -#if !USE_BOTH_SWITCHGATE_SWITCHES - case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */ - if (init_game) - Feld[x][y] = EL_SWITCHGATE_SWITCH_UP; - break; - - case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */ - if (init_game) - Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP; - break; -#endif - case EL_LIGHT_SWITCH_ACTIVE: if (init_game) game.light_time_left = level.time_light * FRAMES_PER_SECOND; @@ -1937,16 +1941,42 @@ static void InitField(int x, int y, boolean init_game) Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE; break; + case EL_TRIGGER_PLAYER: + case EL_TRIGGER_ELEMENT: + case EL_TRIGGER_CE_VALUE: + case EL_TRIGGER_CE_SCORE: + case EL_SELF: + case EL_ANY_ELEMENT: + case EL_CURRENT_CE_VALUE: + case EL_CURRENT_CE_SCORE: + case EL_PREV_CE_1: + case EL_PREV_CE_2: + case EL_PREV_CE_3: + case EL_PREV_CE_4: + case EL_PREV_CE_5: + case EL_PREV_CE_6: + case EL_PREV_CE_7: + case EL_PREV_CE_8: + case EL_NEXT_CE_1: + case EL_NEXT_CE_2: + case EL_NEXT_CE_3: + case EL_NEXT_CE_4: + case EL_NEXT_CE_5: + case EL_NEXT_CE_6: + case EL_NEXT_CE_7: + case EL_NEXT_CE_8: + /* reference elements should not be used on the playfield */ + Feld[x][y] = EL_EMPTY; + break; + default: if (IS_CUSTOM_ELEMENT(element)) { if (CAN_MOVE(element)) InitMovDir(x, y); -#if USE_NEW_CUSTOM_VALUE if (!element_info[element].use_last_ce_value || init_game) CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]); -#endif } else if (IS_GROUP_ELEMENT(element)) { @@ -1962,7 +1992,7 @@ static void InitField(int x, int y, boolean init_game) CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X); } -static inline void InitField_WithBug1(int x, int y, boolean init_game) +inline static void InitField_WithBug1(int x, int y, boolean init_game) { InitField(x, y, init_game); @@ -1972,7 +2002,7 @@ static inline void InitField_WithBug1(int x, int y, boolean init_game) InitMovDir(x, y); } -static inline void InitField_WithBug2(int x, int y, boolean init_game) +inline static void InitField_WithBug2(int x, int y, boolean init_game) { int old_element = Feld[x][y]; @@ -1993,8 +2023,6 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game) */ } -#if 1 - static int get_key_element_from_nr(int key_nr) { int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS : @@ -2064,6 +2092,16 @@ static int compareGamePanelOrderInfo(const void *object1, const void *object2) return compare_result; } +int getPlayerInventorySize(int player_nr) +{ + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + return level.native_em_level->ply[player_nr]->dynamite; + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + return level.native_sp_level->game_sp->red_disk_count; + else + return stored_player[player_nr].inventory_size; +} + void InitGameControlValues() { int i; @@ -2078,7 +2116,7 @@ void InitGameControlValues() if (nr != i) { - Error(ERR_INFO, "'game_panel_controls' structure corrupted"); + Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i); Error(ERR_EXIT, "this should not happen -- please debug"); } @@ -2111,8 +2149,17 @@ void InitGameControlValues() void UpdatePlayfieldElementCount() { + boolean use_element_count = FALSE; int i, j, x, y; + /* first check if it is needed at all to calculate playfield element count */ + for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++) + if (!PANEL_DEACTIVATED(game_panel_controls[i].pos)) + use_element_count = TRUE; + + if (!use_element_count) + return; + for (i = 0; i < MAX_NUM_ELEMENTS; i++) element_info[i].element_count = 0; @@ -2135,20 +2182,42 @@ void UpdateGameControlValues() local_player->LevelSolved_CountingTime : level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->time : - level.time == 0 ? TimePlayed : TimeLeft); + level.game_engine_type == GAME_ENGINE_TYPE_SP ? + level.native_sp_level->game_sp->time_played : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + game_mm.energy_left : + game.no_time_limit ? TimePlayed : TimeLeft); int score = (local_player->LevelSolved ? local_player->LevelSolved_CountingScore : level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->score : + level.game_engine_type == GAME_ENGINE_TYPE_SP ? + level.native_sp_level->game_sp->score : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + game_mm.score : local_player->score); int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->required : + level.game_engine_type == GAME_ENGINE_TYPE_SP ? + level.native_sp_level->game_sp->infotrons_still_needed : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + game_mm.kettles_still_needed : local_player->gems_still_needed); int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->required > 0 : + level.game_engine_type == GAME_ENGINE_TYPE_SP ? + level.native_sp_level->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 : local_player->gems_still_needed > 0 || local_player->sokobanfields_still_needed > 0 || local_player->lights_still_needed > 0); + int health = (local_player->LevelSolved ? + local_player->LevelSolved_CountingHealth : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + MM_HEALTH(game_mm.laser_overload_value) : + local_player->health); UpdatePlayfieldElementCount(); @@ -2167,6 +2236,10 @@ void UpdateGameControlValues() { for (i = 0; i < MAX_PLAYERS; i++) { + /* only one player in Supaplex game engine */ + if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0) + break; + for (k = 0; k < MAX_NUM_KEYS; k++) { if (level.game_engine_type == GAME_ENGINE_TYPE_EM) @@ -2180,12 +2253,8 @@ void UpdateGameControlValues() get_key_element_from_nr(k); } - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - level.native_em_level->ply[i]->dynamite; - else - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - stored_player[i].inventory_size; + game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += + getPlayerInventorySize(i); if (stored_player[i].num_white_keys > 0) game_panel_controls[GAME_PANEL_KEY_WHITE].value = @@ -2212,12 +2281,8 @@ void UpdateGameControlValues() get_key_element_from_nr(k); } - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - level.native_em_level->ply[player_nr]->dynamite; - else - game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += - stored_player[player_nr].inventory_size; + game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += + getPlayerInventorySize(player_nr); if (stored_player[player_nr].num_white_keys > 0) game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE; @@ -2235,6 +2300,7 @@ void UpdateGameControlValues() } game_panel_controls[GAME_PANEL_SCORE].value = score; + game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score; game_panel_controls[GAME_PANEL_TIME].value = time; @@ -2242,6 +2308,16 @@ void UpdateGameControlValues() game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60; game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60; + if (game.no_time_limit) + game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100; + else + game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time; + + game_panel_controls[GAME_PANEL_HEALTH].value = health; + game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health; + + game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter; + game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value = (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE : EL_EMPTY); @@ -2324,12 +2400,8 @@ void UpdateGameControlValues() game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value = game.magic_wall_time_left; -#if USE_PLAYER_GRAVITY game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = local_player->gravity; -#else - game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity; -#endif for (i = 0; i < NUM_PANEL_GRAPHICS; i++) game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i; @@ -2342,8 +2414,7 @@ void UpdateGameControlValues() for (i = 0; i < NUM_PANEL_ELEMENTS; i++) game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value = (IS_VALID_ELEMENT(game.panel.element_count[i].id) ? - element_info[game.panel.element_count[i].id].element_count : - EL_UNDEFINED); + element_info[game.panel.element_count[i].id].element_count : 0); for (i = 0; i < NUM_PANEL_CE_SCORE; i++) game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = @@ -2368,35 +2439,68 @@ void UpdateGameControlValues() if (gpc->type == TYPE_ELEMENT) { - int last_anim_random_frame = gfx.anim_random_frame; - int element = gpc->value; - int graphic = el2panelimg(element); - - if (gpc->value != gpc->last_value) + if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY) { - gpc->gfx_frame = 0; - gpc->gfx_random = INIT_GFX_RANDOM(); + int last_anim_random_frame = gfx.anim_random_frame; + int element = gpc->value; + int graphic = el2panelimg(element); + + if (gpc->value != gpc->last_value) + { + gpc->gfx_frame = 0; + gpc->gfx_random = INIT_GFX_RANDOM(); + } + else + { + gpc->gfx_frame++; + + if (ANIM_MODE(graphic) == ANIM_RANDOM && + IS_NEXT_FRAME(gpc->gfx_frame, graphic)) + gpc->gfx_random = INIT_GFX_RANDOM(); + } + + if (ANIM_MODE(graphic) == ANIM_RANDOM) + gfx.anim_random_frame = gpc->gfx_random; + + if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + gpc->gfx_frame = element_info[element].collect_score; + + gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value), + gpc->gfx_frame); + + if (ANIM_MODE(graphic) == ANIM_RANDOM) + gfx.anim_random_frame = last_anim_random_frame; } - else + } + else if (gpc->type == TYPE_GRAPHIC) + { + if (gpc->graphic != IMG_UNDEFINED) { - gpc->gfx_frame++; + int last_anim_random_frame = gfx.anim_random_frame; + int graphic = gpc->graphic; - if (ANIM_MODE(graphic) == ANIM_RANDOM && - IS_NEXT_FRAME(gpc->gfx_frame, graphic)) + if (gpc->value != gpc->last_value) + { + gpc->gfx_frame = 0; gpc->gfx_random = INIT_GFX_RANDOM(); - } + } + else + { + gpc->gfx_frame++; - if (ANIM_MODE(graphic) == ANIM_RANDOM) - gfx.anim_random_frame = gpc->gfx_random; + if (ANIM_MODE(graphic) == ANIM_RANDOM && + IS_NEXT_FRAME(gpc->gfx_frame, graphic)) + gpc->gfx_random = INIT_GFX_RANDOM(); + } - if (ANIM_MODE(graphic) == ANIM_CE_SCORE) - gpc->gfx_frame = element_info[element].collect_score; + if (ANIM_MODE(graphic) == ANIM_RANDOM) + gfx.anim_random_frame = gpc->gfx_random; - 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; + if (ANIM_MODE(graphic) == ANIM_RANDOM) + gfx.anim_random_frame = last_anim_random_frame; + } } } } @@ -2424,40 +2528,25 @@ void DisplayGameControlValues() return; /* copy default game door content to main double buffer */ - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, - DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY); + + /* !!! CHECK AGAIN !!! */ + SetPanelBackground(); + // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL); + DrawBackground(DX, DY, DXSIZE, DYSIZE); /* redraw game control buttons */ -#if 1 RedrawGameButtons(); -#else - UnmapGameButtons(); - MapGameButtons(); -#endif - game_status = GAME_MODE_PSEUDO_PANEL; + SetGameStatus(GAME_MODE_PSEUDO_PANEL); -#if 1 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++) -#else - for (i = 0; game_panel_controls[i].nr != -1; i++) -#endif { -#if 1 int nr = game_panel_order[i].nr; struct GamePanelControlInfo *gpc = &game_panel_controls[nr]; -#else - struct GamePanelControlInfo *gpc = &game_panel_controls[i]; - int nr = gpc->nr; -#endif struct TextPosInfo *pos = gpc->pos; int type = gpc->type; int value = gpc->value; int frame = gpc->frame; -#if 0 - int last_value = gpc->last_value; - int last_frame = gpc->last_frame; -#endif int size = pos->size; int font = pos->font; boolean draw_masked = pos->draw_masked; @@ -2466,18 +2555,9 @@ void DisplayGameControlValues() if (PANEL_DEACTIVATED(pos)) continue; -#if 0 - if (value == last_value && frame == last_frame) - continue; -#endif - gpc->last_value = value; gpc->last_frame = frame; -#if 0 - printf("::: value %d changed from %d to %d\n", nr, last_value, value); -#endif - if (type == TYPE_INTEGER) { if (nr == GAME_PANEL_LEVEL_NUMBER || @@ -2495,33 +2575,15 @@ void DisplayGameControlValues() size = (value < value_change ? size1 : size2); font = (value < value_change ? font1 : font2); - -#if 0 - /* clear background if value just changed its size (dynamic digits) */ - if ((last_value < value_change) != (value < value_change)) - { - int width1 = size1 * getFontWidth(font1); - int width2 = size2 * getFontWidth(font2); - int max_width = MAX(width1, width2); - int max_height = MAX(getFontHeight(font1), getFontHeight(font2)); - - pos->width = max_width; - - ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), - max_width, max_height); - } -#endif } } -#if 1 /* correct text size if "digits" is zero or less */ if (size <= 0) size = strlen(int2str(value, size)); /* dynamically correct text alignment */ pos->width = size * getFontWidth(font); -#endif DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font, mask_mode); @@ -2535,7 +2597,6 @@ void DisplayGameControlValues() int dst_x = PANEL_XPOS(pos); int dst_y = PANEL_YPOS(pos); -#if 1 if (value != EL_UNDEFINED && value != EL_EMPTY) { element = value; @@ -2543,10 +2604,8 @@ void DisplayGameControlValues() // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size); -#if 1 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0) size = TILESIZE; -#endif getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x, &src_y); @@ -2555,41 +2614,100 @@ void DisplayGameControlValues() height = graphic_info[graphic].height * size / TILESIZE; if (draw_masked) - { - SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc, - dst_x - src_x, dst_y - src_y); BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y); - } else - { BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y); - } } -#else - if (value == EL_UNDEFINED || value == EL_EMPTY) - { - element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value); - graphic = el2panelimg(element); + } + else if (type == TYPE_GRAPHIC) + { + int graphic = gpc->graphic; + int graphic_active = gpc->graphic_active; + Bitmap *src_bitmap; + int src_x, src_y; + int width, height; + int dst_x = PANEL_XPOS(pos); + int dst_y = PANEL_YPOS(pos); + boolean skip = (pos->class == get_hash_from_key("mm_engine_only") && + level.game_engine_type != GAME_ENGINE_TYPE_MM); - src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap; - src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos); - src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos); - } - else + if (graphic != IMG_UNDEFINED && !skip) { - element = value; - graphic = el2panelimg(value); + if (pos->style == STYLE_REVERSE) + value = 100 - value; - getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y); - } + getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y); - width = graphic_info[graphic].width * size / TILESIZE; - height = graphic_info[graphic].height * size / TILESIZE; + if (pos->direction & MV_HORIZONTAL) + { + width = graphic_info[graphic_active].width * value / 100; + height = graphic_info[graphic_active].height; - BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y); -#endif + if (pos->direction == MV_LEFT) + { + src_x += graphic_info[graphic_active].width - width; + dst_x += graphic_info[graphic_active].width - width; + } + } + else + { + width = graphic_info[graphic_active].width; + height = graphic_info[graphic_active].height * value / 100; + + if (pos->direction == MV_UP) + { + src_y += graphic_info[graphic_active].height - height; + dst_y += graphic_info[graphic_active].height - height; + } + } + + if (draw_masked) + BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, + dst_x, dst_y); + else + BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, + dst_x, dst_y); + + getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y); + + if (pos->direction & MV_HORIZONTAL) + { + if (pos->direction == MV_RIGHT) + { + src_x += width; + dst_x += width; + } + else + { + dst_x = PANEL_XPOS(pos); + } + + width = graphic_info[graphic].width - width; + } + else + { + if (pos->direction == MV_DOWN) + { + src_y += height; + dst_y += height; + } + else + { + dst_y = PANEL_YPOS(pos); + } + + height = graphic_info[graphic].height - height; + } + + if (draw_masked) + BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, + dst_x, dst_y); + else + BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, + dst_x, dst_y); + } } else if (type == TYPE_STRING) { @@ -2606,20 +2724,6 @@ void DisplayGameControlValues() { int font1 = pos->font; /* (used for normal state) */ int font2 = pos->font_alt; /* (used for active state) */ -#if 0 - int size1 = strlen(state_normal); - int size2 = strlen(state_active); - int width1 = size1 * getFontWidth(font1); - int width2 = size2 * getFontWidth(font2); - int max_width = MAX(width1, width2); - int max_height = MAX(getFontHeight(font1), getFontHeight(font2)); - - pos->width = max_width; - - /* clear background for values that may have changed its size */ - ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), - max_width, max_height); -#endif font = (active ? font2 : font1); } @@ -2628,7 +2732,6 @@ void DisplayGameControlValues() { char *s_cut; -#if 1 if (size <= 0) { /* don't truncate output if "chars" is zero or less */ @@ -2637,7 +2740,6 @@ void DisplayGameControlValues() /* dynamically correct text alignment */ pos->width = size * getFontWidth(font); } -#endif s_cut = getStringCopyN(s, size); @@ -2651,462 +2753,60 @@ void DisplayGameControlValues() redraw_mask |= REDRAW_DOOR_1; } - game_status = GAME_MODE_PLAYING; + SetGameStatus(GAME_MODE_PLAYING); } -void DrawGameValue_Emeralds(int value) +void UpdateAndDisplayGameControlValues() { - struct TextPosInfo *pos = &game.panel.gems; -#if 1 - int font_nr = pos->font; -#else - int font_nr = FONT_TEXT_2; -#endif - int font_width = getFontWidth(font_nr); - int chars = pos->size; - -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif - - if (PANEL_DEACTIVATED(pos)) + if (tape.deactivate_display) return; - pos->width = chars * font_width; - - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); + UpdateGameControlValues(); + DisplayGameControlValues(); } -void DrawGameValue_Dynamite(int value) +void UpdateGameDoorValues() { - struct TextPosInfo *pos = &game.panel.inventory_count; -#if 1 - int font_nr = pos->font; -#else - int font_nr = FONT_TEXT_2; -#endif - int font_width = getFontWidth(font_nr); - int chars = pos->size; - -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif + UpdateGameControlValues(); +} - if (PANEL_DEACTIVATED(pos)) - return; +void DrawGameDoorValues() +{ + DisplayGameControlValues(); +} - pos->width = chars * font_width; - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); -} +/* + ============================================================================= + InitGameEngine() + ----------------------------------------------------------------------------- + initialize game engine due to level / tape version number + ============================================================================= +*/ -void DrawGameValue_Score(int value) +static void InitGameEngine() { - struct TextPosInfo *pos = &game.panel.score; -#if 1 - int font_nr = pos->font; -#else - int font_nr = FONT_TEXT_2; -#endif - int font_width = getFontWidth(font_nr); - int chars = pos->size; + int i, j, k, l, x, y; -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif + /* set game engine from tape file when re-playing, else from level file */ + game.engine_version = (tape.playing ? tape.engine_version : + level.game_version); - if (PANEL_DEACTIVATED(pos)) - return; + /* set single or multi-player game mode (needed for re-playing tapes) */ + game.team_mode = setup.team_mode; - pos->width = chars * font_width; - - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); -} - -void DrawGameValue_Time(int value) -{ - struct TextPosInfo *pos = &game.panel.time; - static int last_value = -1; - int chars1 = 3; - int chars2 = 4; - int chars = pos->size; -#if 1 - int font1_nr = pos->font; - int font2_nr = pos->font_alt; -#else - int font1_nr = FONT_TEXT_2; - int font2_nr = FONT_TEXT_1; -#endif - int font_nr = font1_nr; - boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE); - -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif - - if (PANEL_DEACTIVATED(pos)) - return; - - if (use_dynamic_chars) /* use dynamic number of chars */ - { - chars = (value < 1000 ? chars1 : chars2); - font_nr = (value < 1000 ? font1_nr : font2_nr); - } - - /* clear background if value just changed its size (dynamic chars only) */ - if (use_dynamic_chars && (last_value < 1000) != (value < 1000)) - { - int width1 = chars1 * getFontWidth(font1_nr); - int width2 = chars2 * getFontWidth(font2_nr); - int max_width = MAX(width1, width2); - int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr)); - - pos->width = max_width; - - ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), - max_width, max_height); - } - - pos->width = chars * getFontWidth(font_nr); - - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); - - last_value = value; -} - -void DrawGameValue_Level(int value) -{ - struct TextPosInfo *pos = &game.panel.level_number; - int chars1 = 2; - int chars2 = 3; - int chars = pos->size; -#if 1 - int font1_nr = pos->font; - int font2_nr = pos->font_alt; -#else - int font1_nr = FONT_TEXT_2; - int font2_nr = FONT_TEXT_1; -#endif - int font_nr = font1_nr; - boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE); - -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif - - if (PANEL_DEACTIVATED(pos)) - return; - - if (use_dynamic_chars) /* use dynamic number of chars */ - { - chars = (level_nr < 100 ? chars1 : chars2); - font_nr = (level_nr < 100 ? font1_nr : font2_nr); - } - - pos->width = chars * getFontWidth(font_nr); - - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); -} - -void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) -{ -#if 0 - struct TextPosInfo *pos = &game.panel.keys; -#endif -#if 0 - int base_key_graphic = EL_KEY_1; -#endif - int i; - -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif - -#if 0 - if (PANEL_DEACTIVATED(pos)) - return; -#endif - -#if 0 - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - base_key_graphic = EL_EM_KEY_1; -#endif - -#if 0 - pos->width = 4 * MINI_TILEX; -#endif - -#if 1 - for (i = 0; i < MAX_NUM_KEYS; i++) -#else - /* currently only 4 of 8 possible keys are displayed */ - for (i = 0; i < STD_NUM_KEYS; i++) -#endif - { -#if 1 - struct TextPosInfo *pos = &game.panel.key[i]; -#endif - int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX; - int src_y = DOOR_GFX_PAGEY1 + 123; -#if 1 - int dst_x = PANEL_XPOS(pos); - int dst_y = PANEL_YPOS(pos); -#else - int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX; - int dst_y = PANEL_YPOS(pos); -#endif - -#if 1 - int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 : - level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 : - EL_KEY_1) + i; - int graphic = el2edimg(element); -#endif - -#if 1 - if (PANEL_DEACTIVATED(pos)) - continue; -#endif - -#if 0 - /* masked blit with tiles from half-size scaled bitmap does not work yet - (no mask bitmap created for these sizes after loading and scaling) -- - solution: load without creating mask, scale, then create final mask */ - - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, - MINI_TILEX, MINI_TILEY, dst_x, dst_y); - - if (key[i]) - { -#if 0 - int graphic = el2edimg(base_key_graphic + i); -#endif - Bitmap *src_bitmap; - int src_x, src_y; - - getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y); - - SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc, - dst_x - src_x, dst_y - src_y); - BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY, - dst_x, dst_y); - } -#else -#if 1 - if (key[i]) - DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic); - else - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, - MINI_TILEX, MINI_TILEY, dst_x, dst_y); -#else - if (key[i]) - DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i)); - else - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, - MINI_TILEX, MINI_TILEY, dst_x, dst_y); -#endif -#endif - } -} - -#else - -void DrawGameValue_Emeralds(int value) -{ - int font_nr = FONT_TEXT_2; - int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2; - - if (PANEL_DEACTIVATED(game.panel.gems)) - return; - - DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr); -} - -void DrawGameValue_Dynamite(int value) -{ - int font_nr = FONT_TEXT_2; - int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2; - - if (PANEL_DEACTIVATED(game.panel.inventory_count)) - return; - - DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr); -} - -void DrawGameValue_Score(int value) -{ - int font_nr = FONT_TEXT_2; - int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2; - - if (PANEL_DEACTIVATED(game.panel.score)) - return; - - DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr); -} - -void DrawGameValue_Time(int value) -{ - int font1_nr = FONT_TEXT_2; -#if 1 - int font2_nr = FONT_TEXT_1; -#else - int font2_nr = FONT_LEVEL_NUMBER; -#endif - int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2; - int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2; - - if (PANEL_DEACTIVATED(game.panel.time)) - return; - - /* clear background if value just changed its size */ - if (value == 999 || value == 1000) - ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14); - - if (value < 1000) - DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr); - else - DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr); -} - -void DrawGameValue_Level(int value) -{ - int font1_nr = FONT_TEXT_2; -#if 1 - int font2_nr = FONT_TEXT_1; -#else - int font2_nr = FONT_LEVEL_NUMBER; -#endif - - if (PANEL_DEACTIVATED(game.panel.level)) - return; - - if (level_nr < 100) - DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr); - else - DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr); -} - -void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) -{ - int base_key_graphic = EL_KEY_1; - int i; - - if (PANEL_DEACTIVATED(game.panel.keys)) - return; - - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - base_key_graphic = EL_EM_KEY_1; - - /* currently only 4 of 8 possible keys are displayed */ - for (i = 0; i < STD_NUM_KEYS; i++) - { - int x = XX_KEYS + i * MINI_TILEX; - int y = YY_KEYS; - - if (key[i]) - DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i)); - else - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, - DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y); - } -} - -#endif - -void DrawAllGameValues(int emeralds, int dynamite, int score, int time, - int key_bits) -{ - int key[MAX_NUM_KEYS]; - int i; - - /* prevent EM engine from updating time/score values parallel to GameWon() */ - if (level.game_engine_type == GAME_ENGINE_TYPE_EM && - local_player->LevelSolved) - return; - - for (i = 0; i < MAX_NUM_KEYS; i++) - key[i] = key_bits & (1 << i); - - DrawGameValue_Level(level_nr); - - DrawGameValue_Emeralds(emeralds); - DrawGameValue_Dynamite(dynamite); - DrawGameValue_Score(score); - DrawGameValue_Time(time); - - DrawGameValue_Keys(key); -} - -void UpdateGameDoorValues() -{ - UpdateGameControlValues(); -} - -void DrawGameDoorValues() -{ - DisplayGameControlValues(); -} - -void DrawGameDoorValues_OLD() -{ - int time_value = (level.time == 0 ? TimePlayed : TimeLeft); - int dynamite_value = 0; - int score_value = (local_player->LevelSolved ? local_player->score_final : - local_player->score); - int gems_value = local_player->gems_still_needed; - int key_bits = 0; - int i, j; - - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (tape.playing) { - DrawGameDoorValues_EM(); + int num_players = 0; - return; - } - - if (game.centered_player_nr == -1) - { for (i = 0; i < MAX_PLAYERS; i++) - { - for (j = 0; j < MAX_NUM_KEYS; j++) - if (stored_player[i].key[j]) - key_bits |= (1 << j); - - dynamite_value += stored_player[i].inventory_size; - } - } - else - { - int player_nr = game.centered_player_nr; + if (tape.player_participates[i]) + num_players++; - for (i = 0; i < MAX_NUM_KEYS; i++) - if (stored_player[player_nr].key[i]) - key_bits |= (1 << i); - - dynamite_value = stored_player[player_nr].inventory_size; + /* multi-player tapes contain input data for more than one player */ + game.team_mode = (num_players > 1); } - DrawAllGameValues(gems_value, dynamite_value, score_value, time_value, - key_bits); -} - - -/* - ============================================================================= - InitGameEngine() - ----------------------------------------------------------------------------- - initialize game engine due to level / tape version number - ============================================================================= -*/ - -static void InitGameEngine() -{ - int i, j, k, l, x, y; - - /* set game engine from tape file when re-playing, else from level file */ - game.engine_version = (tape.playing ? tape.engine_version : - level.game_version); - /* ---------------------------------------------------------------------- */ /* set flags for bugs and changes according to active game engine version */ /* ---------------------------------------------------------------------- */ @@ -3173,39 +2873,16 @@ static void InitGameEngine() game.use_block_last_field_bug = (game.engine_version < VERSION_IDENT(3,1,1,0)); - /* - Summary of bugfix/change: - Changed behaviour of CE changes with multiple changes per single frame. - - Fixed/changed in version: - 3.2.0-6 + game_em.use_single_button = + (game.engine_version > VERSION_IDENT(4,0,0,2)); - Description: - Before 3.2.0-6, only one single CE change was allowed in each engine frame. - This resulted in race conditions where CEs seem to behave strange in some - situations (where triggered CE changes were just skipped because there was - already a CE change on that tile in the playfield in that engine frame). - Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME. - (The number of changes per frame must be limited in any case, because else - it is easily possible to define CE changes that would result in an infinite - loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value - should be set large enough so that it would only be reached in cases where - the corresponding CE change conditions run into a loop. Therefore, it seems - to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the - maximal number of change pages for custom elements.) + game_em.use_snap_key_bug = + (game.engine_version < VERSION_IDENT(4,0,1,0)); - Affected levels/tapes: - Probably many. - */ + /* ---------------------------------------------------------------------- */ -#if USE_ONLY_ONE_CHANGE_PER_FRAME + /* set maximal allowed number of custom element changes per game frame */ game.max_num_changes_per_frame = 1; -#else - game.max_num_changes_per_frame = - (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32); -#endif - - /* ---------------------------------------------------------------------- */ /* default scan direction: scan playfield from top/left to bottom/right */ InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL); @@ -3289,7 +2966,7 @@ static void InitGameEngine() SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE); } - /* ---------- initialize internal run-time variables ------------- */ + /* ---------- initialize internal run-time variables --------------------- */ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) { @@ -3327,6 +3004,25 @@ static void InitGameEngine() } } + /* ---------- initialize reference elements in change conditions --------- */ + + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + struct ElementInfo *ei = &element_info[element]; + + for (j = 0; j < ei->num_change_pages; j++) + { + int trigger_element = ei->change_page[j].initial_trigger_element; + + if (trigger_element >= EL_PREV_CE_8 && + trigger_element <= EL_NEXT_CE_8) + trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element); + + ei->change_page[j].trigger_element = trigger_element; + } + } + /* ---------- initialize run-time trigger player and element ------------- */ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -3336,8 +3032,8 @@ static void InitGameEngine() for (j = 0; j < ei->num_change_pages; j++) { ei->change_page[j].actual_trigger_element = EL_EMPTY; - ei->change_page[j].actual_trigger_player = EL_PLAYER_1; - ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1; + ei->change_page[j].actual_trigger_player = EL_EMPTY; + ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE; ei->change_page[j].actual_trigger_side = CH_SIDE_NONE; ei->change_page[j].actual_trigger_ce_value = 0; ei->change_page[j].actual_trigger_ce_score = 0; @@ -3524,6 +3220,25 @@ static void InitGameEngine() setup.scroll_delay ? setup.scroll_delay_value : 0); game.scroll_delay_value = MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY); + + /* ---------- initialize game engine snapshots ---------------------------- */ + for (i = 0; i < MAX_PLAYERS; i++) + game.snapshot.last_action[i] = 0; + game.snapshot.changed_action = FALSE; + game.snapshot.collected_item = FALSE; + game.snapshot.mode = + (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ? + SNAPSHOT_MODE_EVERY_STEP : + strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ? + SNAPSHOT_MODE_EVERY_MOVE : + strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ? + SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF); + game.snapshot.save_snapshot = FALSE; + + /* ---------- initialize level time for Supaplex engine ------------------- */ + /* Supaplex levels with time limit currently unsupported -- should be added */ + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + level.time = 0; } int get_num_special_action(int element, int action_first, int action_last) @@ -3560,41 +3275,86 @@ int get_num_special_action(int element, int action_first, int action_last) void InitGame() { + int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0); + int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0); + int fade_mask = REDRAW_FIELD; + boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */ boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */ boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */ -#if 0 - boolean do_fading = (game_status == GAME_MODE_MAIN); -#endif + int initial_move_dir = MV_DOWN; int i, j, x, y; - game_status = GAME_MODE_PLAYING; - - InitGameEngine(); - InitGameControlValues(); + // required here to update video display before fading (FIX THIS) + DrawMaskedBorder(REDRAW_DOOR_2); - /* don't play tapes over network */ - network_playing = (options.network && !tape.playing); + if (!game.restart_level) + CloseDoor(DOOR_CLOSE_1); - for (i = 0; i < MAX_PLAYERS; i++) - { - struct PlayerInfo *player = &stored_player[i]; + SetGameStatus(GAME_MODE_PLAYING); - player->index_nr = i; + if (level_editor_test_game) + FadeSkipNextFadeIn(); + else + FadeSetEnterScreen(); + + if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged()) + fade_mask = REDRAW_ALL; + + FadeLevelSoundsAndMusic(); + + ExpireSoundLoops(TRUE); + + FadeOut(fade_mask); + + /* needed if different viewport properties defined for playing */ + ChangeViewportPropertiesIfNeeded(); + + ClearField(); + + OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); + + DrawCompleteVideoDisplay(); + + InitGameEngine(); + InitGameControlValues(); + + /* don't play tapes over network */ + network_playing = (options.network && !tape.playing); + + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + player->index_nr = i; player->index_bit = (1 << i); player->element_nr = EL_PLAYER_1 + i; player->present = FALSE; player->active = FALSE; + player->mapped = FALSE; + player->killed = FALSE; + player->reanimated = FALSE; player->action = 0; player->effective_action = 0; player->programmed_action = 0; + player->mouse_action.lx = 0; + player->mouse_action.ly = 0; + player->mouse_action.button = 0; + + player->effective_mouse_action.lx = 0; + player->effective_mouse_action.ly = 0; + player->effective_mouse_action.button = 0; + player->score = 0; player->score_final = 0; + player->health = MAX_HEALTH; + player->health_final = MAX_HEALTH; + player->gems_still_needed = level.gems_needed; player->sokobanfields_still_needed = 0; player->lights_still_needed = 0; @@ -3610,18 +3370,19 @@ void InitGame() player->dynabombs_left = 0; player->dynabomb_xl = FALSE; - player->MovDir = MV_NONE; + player->MovDir = initial_move_dir; player->MovPos = 0; player->GfxPos = 0; - player->GfxDir = MV_NONE; + player->GfxDir = initial_move_dir; player->GfxAction = ACTION_DEFAULT; player->Frame = 0; player->StepFrame = 0; - player->use_murphy = FALSE; + player->initial_element = player->element_nr; player->artwork_element = (level.use_artwork_element[i] ? level.artwork_element[i] : player->element_nr); + player->use_murphy = FALSE; player->block_last_field = FALSE; /* initialized in InitPlayerField() */ player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */ @@ -3634,7 +3395,7 @@ void InitGame() player->step_counter = 0; - player->last_move_dir = MV_NONE; + player->last_move_dir = initial_move_dir; player->is_active = FALSE; @@ -3652,13 +3413,20 @@ void InitGame() player->is_bored = FALSE; player->is_sleeping = FALSE; + player->was_waiting = TRUE; + player->was_moving = FALSE; + player->was_snapping = FALSE; + player->was_dropping = FALSE; + + player->force_dropping = FALSE; + player->frame_counter_bored = -1; player->frame_counter_sleeping = -1; player->anim_delay_counter = 0; player->post_delay_counter = 0; - player->dir_waiting = MV_NONE; + player->dir_waiting = initial_move_dir; player->action_waiting = ACTION_DEFAULT; player->last_action_waiting = ACTION_DEFAULT; player->special_action_bored = ACTION_DEFAULT; @@ -3691,6 +3459,26 @@ void InitGame() player->inventory_infinite_element = EL_UNDEFINED; player->inventory_size = 0; + if (level.use_initial_inventory[i]) + { + for (j = 0; j < level.initial_inventory_size[i]; j++) + { + int element = level.initial_inventory_content[i][j]; + int collect_count = element_info[element].collect_count_initial; + int k; + + if (!IS_CUSTOM_ELEMENT(element)) + collect_count = 1; + + if (collect_count == 0) + player->inventory_infinite_element = element; + else + for (k = 0; k < collect_count; k++) + if (player->inventory_size < MAX_INVENTORY_SIZE) + player->inventory_element[player->inventory_size++] = element; + } + } + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); @@ -3702,8 +3490,12 @@ void InitGame() player->LevelSolved_PanelOff = FALSE; player->LevelSolved_SaveTape = FALSE; player->LevelSolved_SaveScore = FALSE; + player->LevelSolved_CountingTime = 0; player->LevelSolved_CountingScore = 0; + player->LevelSolved_CountingHealth = 0; + + map_player_action[i] = i; } network_player_action_received = FALSE; @@ -3731,6 +3523,8 @@ void InitGame() AllPlayersGone = FALSE; + game.no_time_limit = (level.time == 0); + game.yamyam_content_nr = 0; game.robot_wheel_active = FALSE; game.magic_wall_active = FALSE; @@ -3740,11 +3534,6 @@ void InitGame() game.switchgate_pos = 0; game.wind_direction = level.wind_direction_initial; -#if !USE_PLAYER_GRAVITY - game.gravity = FALSE; - game.explosions_delayed = TRUE; -#endif - game.lenses_time_left = 0; game.magnify_time_left = 0; @@ -3774,15 +3563,20 @@ void InitGame() for (i = 0; i < MAX_NUM_AMOEBA; i++) AmoebaCnt[i] = AmoebaCnt2[i] = 0; +#if DEBUG_INIT_PLAYER + if (options.debug) + { + printf("Player status at level initialization:\n"); + } +#endif + SCAN_PLAYFIELD(x, y) { Feld[x][y] = level.field[x][y]; MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; -#if USE_NEW_CUSTOM_VALUE CustomValue[x][y] = 0; /* initialized in InitField() */ -#endif Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0; AmoebaNr[x][y] = 0; WasJustMoving[x][y] = 0; @@ -3807,6 +3601,7 @@ void InitGame() GfxElement[x][y] = EL_UNDEFINED; GfxAction[x][y] = ACTION_DEFAULT; GfxDir[x][y] = MV_NONE; + GfxRedraw[x][y] = GFX_REDRAW_NONE; } SCAN_PLAYFIELD(x, y) @@ -3842,7 +3637,6 @@ void InitGame() emulate_sb ? EMU_SOKOBAN : emulate_sp ? EMU_SUPAPLEX : EMU_NONE); -#if USE_NEW_ALL_SLIPPERY /* initialize type of slippery elements */ for (i = 0; i < MAX_NUM_ELEMENTS; i++) { @@ -3860,7 +3654,6 @@ void InitGame() element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT; } } -#endif /* initialize explosion and ignition delay */ for (i = 0; i < MAX_NUM_ELEMENTS; i++) @@ -3880,14 +3673,6 @@ void InitGame() if (i == EL_BLACK_ORB) element_info[i].ignition_delay = 1; } - -#if 0 - if (element_info[i].explosion_delay < 1) /* !!! check again !!! */ - element_info[i].explosion_delay = 1; - - if (element_info[i].ignition_delay < 1) /* !!! check again !!! */ - element_info[i].ignition_delay = 1; -#endif } /* correct non-moving belts to start moving left */ @@ -3895,6 +3680,164 @@ void InitGame() if (game.belt_dir[i] == MV_NONE) game.belt_dir_nr[i] = 3; /* not moving, next moving left */ +#if USE_NEW_PLAYER_ASSIGNMENTS + /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */ + /* choose default local player */ + local_player = &stored_player[0]; + + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].connected = FALSE; + + local_player->connected = TRUE; + /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */ + + if (tape.playing) + { + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].connected = tape.player_participates[i]; + } + else if (game.team_mode && !options.network) + { + /* try to guess locally connected team mode players (needed for correct + assignment of player figures from level to locally playing players) */ + + for (i = 0; i < MAX_PLAYERS; i++) + if (setup.input[i].use_joystick || + setup.input[i].key.left != KSYM_UNDEFINED) + stored_player[i].connected = TRUE; + } + +#if DEBUG_INIT_PLAYER + if (options.debug) + { + printf("Player status after level initialization:\n"); + + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + printf("- player %d: present == %d, connected == %d, active == %d", + i + 1, + player->present, + player->connected, + player->active); + + if (local_player == player) + printf(" (local player)"); + + printf("\n"); + } + } +#endif + +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("Reassigning players ...\n"); +#endif + + /* check if any connected player was not found in playfield */ + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + if (player->connected && !player->present) + { + struct PlayerInfo *field_player = NULL; + +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("- looking for field player for player %d ...\n", i + 1); +#endif + + /* assign first free player found that is present in the playfield */ + + /* first try: look for unmapped playfield player that is not connected */ + for (j = 0; j < MAX_PLAYERS; j++) + if (field_player == NULL && + stored_player[j].present && + !stored_player[j].mapped && + !stored_player[j].connected) + field_player = &stored_player[j]; + + /* second try: look for *any* unmapped playfield player */ + for (j = 0; j < MAX_PLAYERS; j++) + if (field_player == NULL && + stored_player[j].present && + !stored_player[j].mapped) + field_player = &stored_player[j]; + + if (field_player != NULL) + { + int jx = field_player->jx, jy = field_player->jy; + +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("- found player %d\n", field_player->index_nr + 1); +#endif + + player->present = FALSE; + player->active = FALSE; + + field_player->present = TRUE; + field_player->active = TRUE; + + /* + player->initial_element = field_player->initial_element; + player->artwork_element = field_player->artwork_element; + + player->block_last_field = field_player->block_last_field; + player->block_delay_adjustment = field_player->block_delay_adjustment; + */ + + StorePlayer[jx][jy] = field_player->element_nr; + + field_player->jx = field_player->last_jx = jx; + field_player->jy = field_player->last_jy = jy; + + if (local_player == player) + local_player = field_player; + + map_player_action[field_player->index_nr] = i; + + field_player->mapped = TRUE; + +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("- map_player_action[%d] == %d\n", + field_player->index_nr + 1, i + 1); +#endif + } + } + + if (player->connected && player->present) + player->mapped = TRUE; + } + +#if DEBUG_INIT_PLAYER + if (options.debug) + { + printf("Player status after player assignment (first stage):\n"); + + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + printf("- player %d: present == %d, connected == %d, active == %d", + i + 1, + player->present, + player->connected, + player->active); + + if (local_player == player) + printf(" (local player)"); + + printf("\n"); + } + } +#endif + +#else + /* check if any connected player was not found in playfield */ for (i = 0; i < MAX_PLAYERS; i++) { @@ -3904,24 +3847,26 @@ void InitGame() { for (j = 0; j < MAX_PLAYERS; j++) { - struct PlayerInfo *some_player = &stored_player[j]; - int jx = some_player->jx, jy = some_player->jy; + struct PlayerInfo *field_player = &stored_player[j]; + int jx = field_player->jx, jy = field_player->jy; /* assign first free player found that is present in the playfield */ - if (some_player->present && !some_player->connected) + if (field_player->present && !field_player->connected) { player->present = TRUE; player->active = TRUE; - some_player->present = FALSE; - some_player->active = FALSE; + field_player->present = FALSE; + field_player->active = FALSE; - player->artwork_element = some_player->artwork_element; + player->initial_element = field_player->initial_element; + player->artwork_element = field_player->artwork_element; - player->block_last_field = some_player->block_last_field; - player->block_delay_adjustment = some_player->block_delay_adjustment; + player->block_last_field = field_player->block_last_field; + player->block_delay_adjustment = field_player->block_delay_adjustment; StorePlayer[jx][jy] = player->element_nr; + player->jx = player->last_jx = jx; player->jy = player->last_jy = jy; @@ -3930,14 +3875,46 @@ void InitGame() } } } +#endif + +#if 0 + printf("::: local_player->present == %d\n", local_player->present); +#endif if (tape.playing) { /* when playing a tape, eliminate all players who do not participate */ +#if USE_NEW_PLAYER_ASSIGNMENTS + + if (!game.team_mode) + { + for (i = 0; i < MAX_PLAYERS; i++) + { + if (stored_player[i].active && + !tape.player_participates[map_player_action[i]]) + { + struct PlayerInfo *player = &stored_player[i]; + int jx = player->jx, jy = player->jy; + +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy); +#endif + + player->active = FALSE; + StorePlayer[jx][jy] = 0; + Feld[jx][jy] = EL_EMPTY; + } + } + } + +#else + for (i = 0; i < MAX_PLAYERS; i++) { - if (stored_player[i].active && !tape.player_participates[i]) + if (stored_player[i].active && + !tape.player_participates[i]) { struct PlayerInfo *player = &stored_player[i]; int jx = player->jx, jy = player->jy; @@ -3947,8 +3924,9 @@ void InitGame() Feld[jx][jy] = EL_EMPTY; } } +#endif } - else if (!options.network && !setup.team_mode) /* && !tape.playing */ + else if (!options.network && !game.team_mode) /* && !tape.playing */ { /* when in single player mode, eliminate all but the first active player */ @@ -3977,26 +3955,39 @@ void InitGame() /* when recording the game, store which players take part in the game */ if (tape.recording) { +#if USE_NEW_PLAYER_ASSIGNMENTS + for (i = 0; i < MAX_PLAYERS; i++) + if (stored_player[i].connected) + tape.player_participates[i] = TRUE; +#else for (i = 0; i < MAX_PLAYERS; i++) if (stored_player[i].active) tape.player_participates[i] = TRUE; +#endif } +#if DEBUG_INIT_PLAYER if (options.debug) { + printf("Player status after player assignment (final stage):\n"); + for (i = 0; i < MAX_PLAYERS; i++) { struct PlayerInfo *player = &stored_player[i]; - printf("Player %d: present == %d, connected == %d, active == %d.\n", - i+1, + printf("- player %d: present == %d, connected == %d, active == %d", + i + 1, player->present, player->connected, player->active); + if (local_player == player) - printf("Player %d is local player.\n", i+1); + printf(" (local player)"); + + printf("\n"); } } +#endif if (BorderElement == EL_EMPTY) { @@ -4013,12 +4004,16 @@ void InitGame() SBY_Lower = lev_fieldy - SCR_FIELDY + 1; } - if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX) + if (full_lev_fieldx <= SCR_FIELDX) SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2; - - if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY) + if (full_lev_fieldy <= SCR_FIELDY) SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2; + if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX) + SBX_Left--; + if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY) + SBY_Upper--; + /* if local player not found, look for custom element that might create the player (make some assumptions about the right custom element) */ if (!local_player->present) @@ -4109,118 +4104,82 @@ void InitGame() } } - scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left : - start_x > SBX_Right + MIDPOSX ? SBX_Right : - start_x - MIDPOSX); - - scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper : - start_y > SBY_Lower + MIDPOSY ? SBY_Lower : - start_y - MIDPOSY); + scroll_x = SCROLL_POSITION_X(start_x); + scroll_y = SCROLL_POSITION_Y(start_y); } else { - scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left : - local_player->jx > SBX_Right + MIDPOSX ? SBX_Right : - local_player->jx - MIDPOSX); - - scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : - local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : - local_player->jy - MIDPOSY); + scroll_x = SCROLL_POSITION_X(local_player->jx); + scroll_y = SCROLL_POSITION_Y(local_player->jy); } -#if 0 - /* do not use PLAYING mask for fading out from main screen */ - game_status = GAME_MODE_MAIN; -#endif - - StopAnimation(); - - if (!game.restart_level) - CloseDoor(DOOR_CLOSE_1); - -#if 1 - if (level_editor_test_game) - FadeSkipNextFadeIn(); - else - FadeSetEnterScreen(); -#else - if (level_editor_test_game) - fading = fading_none; - else - fading = menu.destination; -#endif - -#if 1 - FadeOut(REDRAW_FIELD); -#else - if (do_fading) - FadeOut(REDRAW_FIELD); -#endif - -#if 0 - game_status = GAME_MODE_PLAYING; -#endif - /* !!! FIX THIS (START) !!! */ if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { InitGameEngine_EM(); - - /* blit playfield from scroll buffer to normal back buffer for fading in */ - BlitScreenToBitmap_EM(backbuffer); + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + { + InitGameEngine_SP(); + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + InitGameEngine_MM(); } else { - DrawLevel(); + DrawLevel(REDRAW_FIELD); DrawAllPlayers(); /* after drawing the level, correct some elements */ if (game.timegate_time_left == 0) CloseAllOpenTimegates(); - - /* blit playfield from scroll buffer to normal back buffer for fading in */ - if (setup.soft_scrolling) - BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY); - - redraw_mask |= REDRAW_FROM_BACKBUFFER; } + + /* blit playfield from scroll buffer to normal back buffer for fading in */ + BlitScreenToBitmap(backbuffer); /* !!! FIX THIS (END) !!! */ -#if 1 - FadeIn(REDRAW_FIELD); -#else - if (do_fading) - FadeIn(REDRAW_FIELD); + DrawMaskedBorder(fade_mask); + + FadeIn(fade_mask); +#if 1 + // full screen redraw is required at this point in the following cases: + // - special editor door undrawn when game was started from level editor + // - drawing area (playfield) was changed and has to be removed completely + redraw_mask = REDRAW_ALL; BackToFront(); #endif if (!game.restart_level) { /* copy default game door content to main double buffer */ - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, - DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY); + + /* !!! CHECK AGAIN !!! */ + SetPanelBackground(); + // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL); + DrawBackground(DX, DY, DXSIZE, DYSIZE); } SetPanelBackground(); SetDrawBackgroundMask(REDRAW_DOOR_1); - UpdateGameDoorValues(); - DrawGameDoorValues(); + UpdateAndDisplayGameControlValues(); if (!game.restart_level) { UnmapGameButtons(); UnmapTapeButtons(); - game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music; - game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops; - game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple; + + FreeGameButtons(); + CreateGameButtons(); + MapGameButtons(); MapTapeButtons(); /* copy actual game door content to door double buffer for OpenDoor() */ - BlitBitmap(drawto, bitmap_db_door, - DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1); + BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0); OpenDoor(DOOR_OPEN_ALL); @@ -4231,31 +4190,68 @@ void InitGame() KeyboardAutoRepeatOffUnlessAutoplay(); +#if DEBUG_INIT_PLAYER if (options.debug) { + printf("Player status (final):\n"); + for (i = 0; i < MAX_PLAYERS; i++) - printf("Player %d %sactive.\n", - i + 1, (stored_player[i].active ? "" : "not ")); + { + struct PlayerInfo *player = &stored_player[i]; + + printf("- player %d: present == %d, connected == %d, active == %d", + i + 1, + player->present, + player->connected, + player->active); + + if (local_player == player) + printf(" (local player)"); + + printf("\n"); + } } +#endif } -#if 1 UnmapAllGadgets(); MapGameButtons(); MapTapeButtons(); -#endif + + if (!game.restart_level && !tape.playing) + { + LevelStats_incPlayed(level_nr); + + SaveLevelSetup_SeriesInfo(); + } game.restart_level = FALSE; + + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + InitGameActions_MM(); + + SaveEngineSnapshotToListInitial(); } -void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y) +void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y, + int actual_player_x, int actual_player_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; + + /* needed to get player position for "follow finger" playing input method */ + local_player->jx = actual_player_x; + local_player->jy = actual_player_y; } void InitMovDir(int x, int y) @@ -4462,20 +4458,32 @@ static void PlayerWins(struct PlayerInfo *player) player->GameOver = TRUE; player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - level.native_em_level->lev->score : player->score); - - player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft); + level.native_em_level->lev->score : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? + game_mm.score : + player->score); + player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ? + MM_HEALTH(game_mm.laser_overload_value) : + player->health); + + player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : + TimeLeft); player->LevelSolved_CountingScore = player->score_final; + player->LevelSolved_CountingHealth = player->health_final; } void GameWon() { + static int time_count_steps; static int time, time_final; static int score, score_final; + static int health, health_final; static int game_over_delay_1 = 0; static int game_over_delay_2 = 0; + static int game_over_delay_3 = 0; int game_over_delay_value_1 = 50; - int game_over_delay_value_2 = 50; + int game_over_delay_value_2 = 25; + int game_over_delay_value_3 = 50; if (!local_player->LevelSolved_GameWon) { @@ -4489,38 +4497,60 @@ void GameWon() local_player->LevelSolved_SaveTape = tape.recording; local_player->LevelSolved_SaveScore = !tape.playing; + if (!tape.playing) + { + LevelStats_incSolved(level_nr); + + SaveLevelSetup_SeriesInfo(); + } + if (tape.auto_play) /* tape might already be stopped here */ tape.auto_play_level_solved = TRUE; -#if 1 TapeStop(); -#endif - game_over_delay_1 = game_over_delay_value_1; - game_over_delay_2 = game_over_delay_value_2; + game_over_delay_1 = 0; + game_over_delay_2 = 0; + game_over_delay_3 = game_over_delay_value_3; - time = time_final = (level.time == 0 ? TimePlayed : TimeLeft); + time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft); score = score_final = local_player->score_final; + health = health_final = local_player->health_final; - if (TimeLeft > 0) - { - time_final = 0; - score_final += TimeLeft * level.score[SC_TIME_BONUS]; - } - else if (level.time == 0 && TimePlayed < 999) + if (level.score[SC_TIME_BONUS] > 0) { - time_final = 999; - score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS]; - } + if (TimeLeft > 0) + { + time_final = 0; + score_final += TimeLeft * level.score[SC_TIME_BONUS]; + } + else if (game.no_time_limit && TimePlayed < 999) + { + time_final = 999; + score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS]; + } - local_player->score_final = score_final; + time_count_steps = MAX(1, ABS(time_final - time) / 100); - if (level_editor_test_game) - { - time = time_final; + game_over_delay_1 = game_over_delay_value_1; + + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + health_final = 0; + score_final += health * level.score[SC_TIME_BONUS]; + + game_over_delay_2 = game_over_delay_value_2; + } + + local_player->score_final = score_final; + local_player->health_final = health_final; + } + + if (level_editor_test_game) + { + time = time_final; score = score_final; -#if 1 local_player->LevelSolved_CountingTime = time; local_player->LevelSolved_CountingScore = score; @@ -4528,10 +4558,6 @@ void GameWon() game_panel_controls[GAME_PANEL_SCORE].value = score; DisplayGameControlValues(); -#else - DrawGameValue_Time(time); - DrawGameValue_Score(score); -#endif } if (level.game_engine_type == GAME_ENGINE_TYPE_RND) @@ -4548,24 +4574,14 @@ void GameWon() { int element = Feld[ExitX][ExitY]; -#if 0 - if (element == EL_EM_EXIT_OPEN || - element == EL_EM_STEEL_EXIT_OPEN) - { - Bang(ExitX, ExitY); - } - else -#endif - { - Feld[ExitX][ExitY] = - (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); - } + Feld[ExitX][ExitY] = + (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); } /* player disappears */ @@ -4600,12 +4616,13 @@ void GameWon() { int time_to_go = ABS(time_final - time); int time_count_dir = (time < time_final ? +1 : -1); - int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1); + + if (time_to_go < time_count_steps) + time_count_steps = 1; time += time_count_steps * time_count_dir; score += time_count_steps * level.score[SC_TIME_BONUS]; -#if 1 local_player->LevelSolved_CountingTime = time; local_player->LevelSolved_CountingScore = score; @@ -4613,10 +4630,6 @@ void GameWon() game_panel_controls[GAME_PANEL_SCORE].value = score; DisplayGameControlValues(); -#else - DrawGameValue_Time(time); - DrawGameValue_Score(score); -#endif if (time == time_final) StopSound(SND_GAME_LEVELTIME_BONUS); @@ -4628,8 +4641,6 @@ void GameWon() return; } - local_player->LevelSolved_PanelOff = TRUE; - if (game_over_delay_2 > 0) { game_over_delay_2--; @@ -4637,9 +4648,41 @@ void GameWon() return; } -#if 1 + if (health != health_final) + { + int health_count_dir = (health < health_final ? +1 : -1); + + health += health_count_dir; + score += level.score[SC_TIME_BONUS]; + + local_player->LevelSolved_CountingHealth = health; + local_player->LevelSolved_CountingScore = score; + + game_panel_controls[GAME_PANEL_HEALTH].value = health; + game_panel_controls[GAME_PANEL_SCORE].value = score; + + DisplayGameControlValues(); + + if (health == health_final) + StopSound(SND_GAME_LEVELTIME_BONUS); + else if (setup.sound_loops) + PlaySoundLoop(SND_GAME_LEVELTIME_BONUS); + else + PlaySound(SND_GAME_LEVELTIME_BONUS); + + return; + } + + local_player->LevelSolved_PanelOff = TRUE; + + if (game_over_delay_3 > 0) + { + game_over_delay_3--; + + return; + } + GameEnd(); -#endif } void GameEnd() @@ -4649,43 +4692,30 @@ void GameEnd() local_player->LevelSolved_GameEnd = TRUE; - CloseDoor(DOOR_CLOSE_1); + if (!global.use_envelope_request) + CloseDoor(DOOR_CLOSE_1); if (local_player->LevelSolved_SaveTape) { -#if 0 - TapeStop(); -#endif - -#if 1 SaveTapeChecked(tape.level_nr); /* ask to save tape */ -#else - SaveTape(tape.level_nr); /* ask to save tape */ -#endif } + CloseDoor(DOOR_CLOSE_ALL); + if (level_editor_test_game) { - game_status = GAME_MODE_MAIN; + SetGameStatus(GAME_MODE_MAIN); -#if 1 - DrawAndFadeInMainMenu(REDRAW_FIELD); -#else DrawMainMenu(); -#endif return; } if (!local_player->LevelSolved_SaveScore) { -#if 1 - FadeOut(REDRAW_FIELD); -#endif + SetGameStatus(GAME_MODE_MAIN); - game_status = GAME_MODE_MAIN; - - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); return; } @@ -4693,15 +4723,17 @@ void GameEnd() if (level_nr == leveldir_current->handicap_level) { leveldir_current->handicap_level++; + SaveLevelSetup_SeriesInfo(); } - if (level_nr < leveldir_current->last_level) + if (setup.increment_levels && + level_nr < leveldir_current->last_level) raise_level = TRUE; /* advance to next level */ if ((hi_pos = NewHiScore()) >= 0) { - game_status = GAME_MODE_SCORES; + SetGameStatus(GAME_MODE_SCORES); DrawHallOfFame(hi_pos); @@ -4713,11 +4745,7 @@ void GameEnd() } else { -#if 1 - FadeOut(REDRAW_FIELD); -#endif - - game_status = GAME_MODE_MAIN; + SetGameStatus(GAME_MODE_MAIN); if (raise_level) { @@ -4725,7 +4753,7 @@ void GameEnd() TapeErase(); } - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); } } @@ -4733,6 +4761,7 @@ int NewHiScore() { int k, l; int position = -1; + boolean one_score_entry_per_name = !program.many_scores_per_name; LoadScore(level_nr); @@ -4750,13 +4779,15 @@ int NewHiScore() { int m = MAX_SCORE_ENTRIES - 1; -#ifdef ONE_PER_NAME - for (l = k; l < MAX_SCORE_ENTRIES; l++) - if (strEqual(setup.player_name, highscore[l].Name)) - m = l; - if (m == k) /* player's new highscore overwrites his old one */ - goto put_into_list; -#endif + if (one_score_entry_per_name) + { + for (l = k; l < MAX_SCORE_ENTRIES; l++) + if (strEqual(setup.player_name, highscore[l].Name)) + m = l; + + if (m == k) /* player's new highscore overwrites his old one */ + goto put_into_list; + } for (l = m; l > k; l--) { @@ -4765,22 +4796,19 @@ int NewHiScore() } } -#ifdef ONE_PER_NAME put_into_list: -#endif + strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN); highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0'; highscore[k].Score = local_player->score_final; position = k; + break; } - -#ifdef ONE_PER_NAME - else if (!strncmp(setup.player_name, highscore[k].Name, + else if (one_score_entry_per_name && + !strncmp(setup.player_name, highscore[k].Name, MAX_PLAYER_NAME_LEN)) break; /* player already there with a higher score */ -#endif - } if (position >= 0) @@ -4820,11 +4848,6 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir) { if (player->GfxAction != action || player->GfxDir != dir) { -#if 0 - printf("Player frame reset! (%d => %d, %d => %d)\n", - player->GfxAction, action, player->GfxDir, dir); -#endif - player->GfxAction = action; player->GfxDir = dir; player->Frame = 0; @@ -4832,12 +4855,14 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir) } } -#if USE_GFX_RESET_GFX_ANIMATION -static void ResetGfxFrame(int x, int y, boolean redraw) +static void ResetGfxFrame(int x, int y) { + // profiling showed that "autotest" spends 10~20% of its time in this function + if (DrawingDeactivatedField()) + return; + int element = Feld[x][y]; int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - int last_gfx_frame = GfxFrame[x][y]; if (graphic_info[graphic].anim_global_sync) GfxFrame[x][y] = FrameCounter; @@ -4847,11 +4872,7 @@ static void ResetGfxFrame(int x, int y, boolean redraw) GfxFrame[x][y] = element_info[element].collect_score; else if (ANIM_MODE(graphic) == ANIM_CE_DELAY) GfxFrame[x][y] = ChangeDelay[x][y]; - - if (redraw && GfxFrame[x][y] != last_gfx_frame) - DrawLevelGraphicAnimation(x, y, graphic); } -#endif static void ResetGfxAnimation(int x, int y) { @@ -4859,9 +4880,7 @@ static void ResetGfxAnimation(int x, int y) GfxDir[x][y] = MovDir[x][y]; GfxFrame[x][y] = 0; -#if USE_GFX_RESET_GFX_ANIMATION - ResetGfxFrame(x, y, FALSE); -#endif + ResetGfxFrame(x, y); } static void ResetRandomAnimationValue(int x, int y) @@ -4877,51 +4896,24 @@ void InitMovingField(int x, int y, int direction) int newx = x + dx; int newy = y + dy; boolean is_moving_before, is_moving_after; -#if 0 - boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]); -#endif /* check if element was/is moving or being moved before/after mode change */ -#if 1 -#if 1 is_moving_before = (WasJustMoving[x][y] != 0); -#else - /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */ - is_moving_before = WasJustMoving[x][y]; -#endif -#else - is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0); -#endif is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0); /* reset animation only for moving elements which change direction of moving or which just started or stopped moving (else CEs with property "can move" / "not moving" are reset each frame) */ -#if USE_GFX_RESET_ONLY_WHEN_MOVING -#if 1 if (is_moving_before != is_moving_after || direction != MovDir[x][y]) ResetGfxAnimation(x, y); -#else - if ((is_moving_before || is_moving_after) && !continues_moving) - ResetGfxAnimation(x, y); -#endif -#else - if (!continues_moving) - ResetGfxAnimation(x, y); -#endif MovDir[x][y] = direction; GfxDir[x][y] = direction; -#if USE_GFX_RESET_ONLY_WHEN_MOVING GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING : direction == MV_DOWN && CAN_FALL(element) ? ACTION_FALLING : ACTION_MOVING); -#else - GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ? - ACTION_FALLING : ACTION_MOVING); -#endif /* this is needed for CEs with property "can move" / "not moving" */ @@ -4932,9 +4924,7 @@ void InitMovingField(int x, int y, int direction) MovDir[newx][newy] = MovDir[x][y]; -#if USE_NEW_CUSTOM_VALUE CustomValue[newx][newy] = CustomValue[x][y]; -#endif GfxFrame[newx][newy] = GfxFrame[x][y]; GfxRandom[newx][newy] = GfxRandom[x][y]; @@ -5017,19 +5007,13 @@ static void RemoveField(int x, int y) MovDir[x][y] = 0; MovDelay[x][y] = 0; -#if USE_NEW_CUSTOM_VALUE CustomValue[x][y] = 0; -#endif AmoebaNr[x][y] = 0; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; Pushed[x][y] = FALSE; -#if 0 - ExplodeField[x][y] = EX_TYPE_NONE; -#endif - GfxElement[x][y] = EL_UNDEFINED; GfxAction[x][y] = ACTION_DEFAULT; GfxDir[x][y] = MV_NONE; @@ -5058,7 +5042,7 @@ void RemoveMovingField(int x, int y) Store[oldx][oldy] = Store2[oldx][oldy] = 0; - DrawLevelField(oldx, oldy); + TEST_DrawLevelField(oldx, oldy); return; } @@ -5087,8 +5071,8 @@ void RemoveMovingField(int x, int y) if (next_element != EL_UNDEFINED) Feld[oldx][oldy] = next_element; - DrawLevelField(oldx, oldy); - DrawLevelField(newx, newy); + TEST_DrawLevelField(oldx, oldy); + TEST_DrawLevelField(newx, newy); } void DrawDynamite(int x, int y) @@ -5189,166 +5173,92 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy) void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, boolean center_screen, boolean quick_relocation) { + unsigned int frame_delay_value_old = GetVideoFrameDelay(); boolean ffwd_delay = (tape.playing && tape.fast_forward); boolean no_delay = (tape.warp_forward); int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay); int wait_delay_value = (no_delay ? 0 : frame_delay_value); + int new_scroll_x, new_scroll_y; - if (quick_relocation) + if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y))) { - int offset = game.scroll_delay_value; - - if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen) - { - if (!level.shifted_relocation || center_screen) - { - /* quick relocation (without scrolling), with centering of screen */ - - scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : - x > SBX_Right + MIDPOSX ? SBX_Right : - x - MIDPOSX); - - scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper : - y > SBY_Lower + MIDPOSY ? SBY_Lower : - y - MIDPOSY); - } - else - { - /* quick relocation (without scrolling), but do not center screen */ - - int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left : - old_x > SBX_Right + MIDPOSX ? SBX_Right : - old_x - MIDPOSX); - - int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper : - old_y > SBY_Lower + MIDPOSY ? SBY_Lower : - old_y - MIDPOSY); + /* case 1: quick relocation inside visible screen (without scrolling) */ - int offset_x = x + (scroll_x - center_scroll_x); - int offset_y = y + (scroll_y - center_scroll_y); + RedrawPlayfield(); - scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left : - offset_x > SBX_Right + MIDPOSX ? SBX_Right : - offset_x - MIDPOSX); - - scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper : - offset_y > SBY_Lower + MIDPOSY ? SBY_Lower : - offset_y - MIDPOSY); - } - } - else - { - /* quick relocation (without scrolling), inside visible screen area */ - - if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) || - (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset)) - scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset); - - if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) || - (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset)) - scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset); - - /* 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); + return; + } - /* 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); - } + if (!level.shifted_relocation || center_screen) + { + /* relocation _with_ centering of screen */ - RedrawPlayfield(TRUE, 0,0,0,0); + new_scroll_x = SCROLL_POSITION_X(x); + new_scroll_y = SCROLL_POSITION_Y(y); } else { -#if 1 - int scroll_xx, scroll_yy; - - if (!level.shifted_relocation || center_screen) - { - /* visible relocation (with scrolling), with centering of screen */ - - scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left : - x > SBX_Right + MIDPOSX ? SBX_Right : - x - MIDPOSX); + /* relocation _without_ centering of screen */ - scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper : - y > SBY_Lower + MIDPOSY ? SBY_Lower : - y - MIDPOSY); - } - else - { - /* visible relocation (with scrolling), but do not center screen */ - - int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left : - old_x > SBX_Right + MIDPOSX ? SBX_Right : - old_x - MIDPOSX); + int center_scroll_x = SCROLL_POSITION_X(old_x); + int center_scroll_y = SCROLL_POSITION_Y(old_y); + int offset_x = x + (scroll_x - center_scroll_x); + int offset_y = y + (scroll_y - center_scroll_y); - int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper : - old_y > SBY_Lower + MIDPOSY ? SBY_Lower : - old_y - MIDPOSY); + /* for new screen position, apply previous offset to center position */ + new_scroll_x = SCROLL_POSITION_X(offset_x); + new_scroll_y = SCROLL_POSITION_Y(offset_y); + } - int offset_x = x + (scroll_x - center_scroll_x); - int offset_y = y + (scroll_y - center_scroll_y); + if (quick_relocation) + { + /* case 2: quick relocation (redraw without visible scrolling) */ - scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left : - offset_x > SBX_Right + MIDPOSX ? SBX_Right : - offset_x - MIDPOSX); + scroll_x = new_scroll_x; + scroll_y = new_scroll_y; - scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper : - offset_y > SBY_Lower + MIDPOSY ? SBY_Lower : - offset_y - MIDPOSY); - } + RedrawPlayfield(); -#else + return; + } - /* visible relocation (with scrolling), with centering of screen */ + /* case 3: visible relocation (with scrolling to new position) */ - int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left : - x > SBX_Right + MIDPOSX ? SBX_Right : - x - MIDPOSX); + ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */ - int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper : - y > SBY_Lower + MIDPOSY ? SBY_Lower : - y - MIDPOSY); -#endif + SetVideoFrameDelay(wait_delay_value); - ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */ + while (scroll_x != new_scroll_x || scroll_y != new_scroll_y) + { + int dx = 0, dy = 0; + int fx = FX, fy = FY; - while (scroll_x != scroll_xx || scroll_y != scroll_yy) - { - 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); - dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0); - dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0); + if (dx == 0 && dy == 0) /* no scrolling needed at all */ + break; - if (dx == 0 && dy == 0) /* no scrolling needed at all */ - break; + scroll_x -= dx; + scroll_y -= dy; - scroll_x -= dx; - scroll_y -= dy; + fx += dx * TILEX / 2; + fy += dy * TILEY / 2; - fx += dx * TILEX / 2; - fy += dy * TILEY / 2; + ScrollLevel(dx, dy); + DrawAllPlayers(); - 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); - /* scroll in two steps of half tile size to make things smoother */ - BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY); - FlushDisplay(); - Delay(wait_delay_value); + /* scroll second step to align at full tile size */ + BlitScreenToBitmap(window); + } - /* scroll second step to align at full tile size */ - BackToFront(); - Delay(wait_delay_value); - } + DrawAllPlayers(); + BackToFront(); - DrawAllPlayers(); - BackToFront(); - Delay(wait_delay_value); - } + SetVideoFrameDelay(frame_delay_value_old); } void RelocatePlayer(int jx, int jy, int el_player_raw) @@ -5398,8 +5308,7 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) DrawPlayer(player); - BackToFront(); - Delay(wait_delay_value); + BackToFront_WithFrameDelay(wait_delay_value); } DrawPlayer(player); /* needed here only to cleanup last field */ @@ -5420,10 +5329,13 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) Feld[jx][jy] = el_player; InitPlayerField(jx, jy, el_player, TRUE); + /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be + possible that the relocation target field did not contain a player element, + but a walkable element, to which the new player was relocated -- in this + case, restore that (already initialized!) element on the player field */ if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */ { - Feld[jx][jy] = element; - InitField(jx, jy, FALSE); + Feld[jx][jy] = element; /* restore previously existing element */ } /* only visually relocate centered player */ @@ -5439,6 +5351,19 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X, player->index_bit, enter_side); + + if (player->is_switching) + { + /* ensure that relocation while still switching an element does not cause + a new element to be treated as also switched directly after relocation + (this is important for teleporter switches that teleport the player to + a place where another teleporter switch is in the same direction, which + would then incorrectly be treated as immediately switched before the + direction key that caused the switch was released) */ + + player->switch_x += jx - old_jx; + player->switch_y += jy - old_jy; + } } void Explode(int ex, int ey, int phase, int mode) @@ -5461,23 +5386,6 @@ void Explode(int ex, int ey, int phase, int mode) int center_element = Feld[ex][ey]; int artwork_element, explosion_element; /* set these values later */ -#if 0 - /* --- This is only really needed (and now handled) in "Impact()". --- */ - /* do not explode moving elements that left the explode field in time */ - if (game.engine_version >= VERSION_IDENT(2,2,0,7) && - center_element == EL_EMPTY && - (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)) - return; -#endif - -#if 0 - /* !!! at this place, the center element may be EL_BLOCKED !!! */ - if (mode == EX_TYPE_NORMAL || - mode == EX_TYPE_CENTER || - mode == EX_TYPE_CROSS) - PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING); -#endif - /* remove things displayed in background while burning dynamite */ if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey])) Back[ex][ey] = 0; @@ -5507,12 +5415,10 @@ void Explode(int ex, int ey, int phase, int mode) } } -#if 1 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER || mode == EX_TYPE_CROSS) PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING); -#endif last_phase = element_info[explosion_element].explosion_delay + 1; @@ -5649,31 +5555,9 @@ void Explode(int ex, int ey, int phase, int mode) ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0); -#ifdef DEBUG - - /* activate this even in non-DEBUG version until cause for crash in - getGraphicAnimationFrame() (see below) is found and eliminated */ - -#endif -#if 1 - -#if 1 /* this can happen if the player leaves an explosion just in time */ if (GfxElement[x][y] == EL_UNDEFINED) GfxElement[x][y] = EL_EMPTY; -#else - if (GfxElement[x][y] == EL_UNDEFINED) - { - printf("\n\n"); - printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y); - printf("Explode(): This should never happen!\n"); - printf("\n\n"); - - GfxElement[x][y] = EL_EMPTY; - } -#endif - -#endif border_element = Store2[x][y]; if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y)) @@ -5745,18 +5629,16 @@ void Explode(int ex, int ey, int phase, int mode) ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; -#if USE_NEW_CUSTOM_VALUE CustomValue[x][y] = 0; -#endif InitField_WithBug2(x, y, FALSE); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); TestIfElementTouchesCustomElement(x, y); if (GFX_CRUMBLED(element)) - DrawLevelFieldCrumbledSandNeighbours(x, y); + TEST_DrawLevelFieldCrumbledNeighbours(x, y); if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present) StorePlayer[x][y] = 0; @@ -5770,7 +5652,7 @@ void Explode(int ex, int ey, int phase, int mode) int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]); if (phase == delay) - DrawLevelFieldCrumbledSand(x, y); + TEST_DrawLevelFieldCrumbled(x, y); if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY) { @@ -5847,8 +5729,7 @@ void Bang(int x, int y) { struct PlayerInfo *player = PLAYERINFO(x, y); - element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY : - player->element_nr); + element = Feld[x][y] = player->initial_element; if (level.use_explosion_element[player->index_nr]) { @@ -5886,10 +5767,6 @@ void Bang(int x, int y) break; case EL_DC_LANDMINE: -#if 0 - case EL_EM_EXIT_OPEN: - case EL_EM_STEEL_EXIT_OPEN: -#endif explosion_type = EX_TYPE_CENTER; break; @@ -6073,7 +5950,7 @@ static void ToggleBeltSwitch(int x, int y) if (e_belt_nr == belt_nr) { Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } } else if (IS_BELT(element) && belt_dir != MV_NONE) @@ -6085,7 +5962,7 @@ static void ToggleBeltSwitch(int x, int y) int belt_part = Feld[xx][yy] - belt_base_element[belt_nr]; Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } } else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE) @@ -6097,7 +5974,7 @@ static void ToggleBeltSwitch(int x, int y) int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr]; Feld[xx][yy] = belt_base_element[belt_nr] + belt_part; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } } } @@ -6113,41 +5990,26 @@ static void ToggleSwitchgateSwitch(int x, int y) { int element = Feld[xx][yy]; -#if !USE_BOTH_SWITCHGATE_SWITCHES - if (element == EL_SWITCHGATE_SWITCH_UP || - element == EL_SWITCHGATE_SWITCH_DOWN) - { - Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos; - DrawLevelField(xx, yy); - } - else if (element == EL_DC_SWITCHGATE_SWITCH_UP || - element == EL_DC_SWITCHGATE_SWITCH_DOWN) - { - Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos; - DrawLevelField(xx, yy); - } -#else if (element == EL_SWITCHGATE_SWITCH_UP) { Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } else if (element == EL_SWITCHGATE_SWITCH_DOWN) { Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } else if (element == EL_DC_SWITCHGATE_SWITCH_UP) { Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN) { Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } -#endif else if (element == EL_SWITCHGATE_OPEN || element == EL_SWITCHGATE_OPENING) { @@ -6193,25 +6055,25 @@ static void RedrawAllLightSwitchesAndInvisibleElements() game.light_time_left > 0) { Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_LIGHT_SWITCH_ACTIVE && game.light_time_left == 0) { Feld[x][y] = EL_LIGHT_SWITCH; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_EMC_DRIPPER && game.light_time_left > 0) { Feld[x][y] = EL_EMC_DRIPPER_ACTIVE; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_EMC_DRIPPER_ACTIVE && game.light_time_left == 0) { Feld[x][y] = EL_EMC_DRIPPER; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_INVISIBLE_STEELWALL || element == EL_INVISIBLE_WALL || @@ -6220,11 +6082,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements() if (game.light_time_left > 0) Feld[x][y] = getInvisibleActiveFromInvisibleElement(element); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); /* uncrumble neighbour fields, if needed */ if (element == EL_INVISIBLE_SAND) - DrawLevelFieldCrumbledSandNeighbours(x, y); + TEST_DrawLevelFieldCrumbledNeighbours(x, y); } else if (element == EL_INVISIBLE_STEELWALL_ACTIVE || element == EL_INVISIBLE_WALL_ACTIVE || @@ -6233,11 +6095,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements() if (game.light_time_left == 0) Feld[x][y] = getInvisibleFromInvisibleActiveElement(element); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); /* re-crumble neighbour fields, if needed */ if (element == EL_INVISIBLE_SAND) - DrawLevelFieldCrumbledSandNeighbours(x, y); + TEST_DrawLevelFieldCrumbledNeighbours(x, y); } } } @@ -6254,13 +6116,13 @@ static void RedrawAllInvisibleElementsForLenses() game.lenses_time_left > 0) { Feld[x][y] = EL_EMC_DRIPPER_ACTIVE; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_EMC_DRIPPER_ACTIVE && game.lenses_time_left == 0) { Feld[x][y] = EL_EMC_DRIPPER; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_INVISIBLE_STEELWALL || element == EL_INVISIBLE_WALL || @@ -6269,11 +6131,11 @@ static void RedrawAllInvisibleElementsForLenses() if (game.lenses_time_left > 0) Feld[x][y] = getInvisibleActiveFromInvisibleElement(element); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); /* uncrumble neighbour fields, if needed */ if (element == EL_INVISIBLE_SAND) - DrawLevelFieldCrumbledSandNeighbours(x, y); + TEST_DrawLevelFieldCrumbledNeighbours(x, y); } else if (element == EL_INVISIBLE_STEELWALL_ACTIVE || element == EL_INVISIBLE_WALL_ACTIVE || @@ -6282,11 +6144,11 @@ static void RedrawAllInvisibleElementsForLenses() if (game.lenses_time_left == 0) Feld[x][y] = getInvisibleFromInvisibleActiveElement(element); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); /* re-crumble neighbour fields, if needed */ if (element == EL_INVISIBLE_SAND) - DrawLevelFieldCrumbledSandNeighbours(x, y); + TEST_DrawLevelFieldCrumbledNeighbours(x, y); } } } @@ -6303,13 +6165,13 @@ static void RedrawAllInvisibleElementsForMagnifier() game.magnify_time_left > 0) { Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_EMC_FAKE_GRASS_ACTIVE && game.magnify_time_left == 0) { Feld[x][y] = EL_EMC_FAKE_GRASS; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (IS_GATE_GRAY(element) && game.magnify_time_left > 0) @@ -6320,8 +6182,10 @@ static void RedrawAllInvisibleElementsForMagnifier() element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE : IS_EMC_GATE_GRAY(element) ? element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE : + IS_DC_GATE_GRAY(element) ? + EL_DC_GATE_WHITE_GRAY_ACTIVE : element); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (IS_GATE_GRAY_ACTIVE(element) && game.magnify_time_left == 0) @@ -6332,8 +6196,10 @@ static void RedrawAllInvisibleElementsForMagnifier() element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY : IS_EMC_GATE_GRAY_ACTIVE(element) ? element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY : + IS_DC_GATE_GRAY_ACTIVE(element) ? + EL_DC_GATE_WHITE_GRAY : element); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } } } @@ -6370,18 +6236,14 @@ static void ActivateTimegateSwitch(int x, int y) else if (element == EL_TIMEGATE_SWITCH_ACTIVE) { Feld[xx][yy] = EL_TIMEGATE_SWITCH; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } */ } -#if 1 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE : EL_DC_TIMEGATE_SWITCH_ACTIVE); -#else - Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE; -#endif } void Impact(int x, int y) @@ -6412,7 +6274,7 @@ void Impact(int x, int y) RemoveMovingField(x, y + 1); Feld[x][y + 1] = EL_QUICKSAND_EMPTY; Feld[x][y + 2] = EL_ROCK; - DrawLevelField(x, y + 2); + TEST_DrawLevelField(x, y + 2); object_hit = TRUE; } @@ -6422,7 +6284,7 @@ void Impact(int x, int y) RemoveMovingField(x, y + 1); Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY; Feld[x][y + 2] = EL_ROCK; - DrawLevelField(x, y + 2); + TEST_DrawLevelField(x, y + 2); object_hit = TRUE; } @@ -6446,7 +6308,7 @@ void Impact(int x, int y) el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element)) { ResetGfxAnimation(x, y); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } if (impact && CAN_EXPLODE_IMPACT(element)) @@ -6607,10 +6469,6 @@ void Impact(int x, int y) } else { -#if 0 - TestIfElementSmashesCustomElement(x, y, MV_DOWN); -#endif - CheckElementChange(x, y + 1, smashed, element, CE_SMASHED); CheckElementChangeBySide(x, y + 1, smashed, element, @@ -6912,7 +6770,6 @@ inline static void TurnRoundExt(int x, int y) } else if (element == EL_SPRING) { -#if USE_NEW_SPRING_BUMPER if (MovDir[x][y] & MV_HORIZONTAL) { if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) && @@ -6920,7 +6777,7 @@ inline static void TurnRoundExt(int x, int y) { Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE; ResetGfxAnimation(move_x, move_y); - DrawLevelField(move_x, move_y); + TEST_DrawLevelField(move_x, move_y); MovDir[x][y] = back_dir; } @@ -6928,12 +6785,6 @@ inline static void TurnRoundExt(int x, int y) SPRING_CAN_ENTER_FIELD(element, x, y + 1)) MovDir[x][y] = MV_NONE; } -#else - if (MovDir[x][y] & MV_HORIZONTAL && - (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) || - SPRING_CAN_ENTER_FIELD(element, x, y + 1))) - MovDir[x][y] = MV_NONE; -#endif MovDelay[x][y] = 0; } @@ -7475,7 +7326,7 @@ static void TurnRound(int x, int y) if (MovDelay[x][y]) GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction); - ResetGfxFrame(x, y, FALSE); + ResetGfxFrame(x, y); } static boolean JustBeingPushed(int x, int y) @@ -7537,10 +7388,18 @@ void StartMoving(int x, int y) else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY) { if (!MovDelay[x][y]) + { MovDelay[x][y] = TILEY + 1; + ResetGfxAnimation(x, y); + ResetGfxAnimation(x, y + 1); + } + if (MovDelay[x][y]) { + DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING); + DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING); + MovDelay[x][y]--; if (MovDelay[x][y]) return; @@ -7551,6 +7410,33 @@ void StartMoving(int x, int y) Store[x][y + 1] = Store[x][y]; Store[x][y] = 0; + PlayLevelSoundAction(x, y, ACTION_FILLING); + } + else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY) + { + if (!MovDelay[x][y]) + { + MovDelay[x][y] = TILEY + 1; + + ResetGfxAnimation(x, y); + ResetGfxAnimation(x, y + 1); + } + + if (MovDelay[x][y]) + { + DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING); + DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING); + + MovDelay[x][y]--; + if (MovDelay[x][y]) + return; + } + + Feld[x][y] = EL_QUICKSAND_EMPTY; + Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL; + Store[x][y + 1] = Store[x][y]; + Store[x][y] = 0; + PlayLevelSoundAction(x, y, ACTION_FILLING); } } @@ -7574,10 +7460,18 @@ void StartMoving(int x, int y) else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY) { if (!MovDelay[x][y]) + { MovDelay[x][y] = TILEY + 1; + ResetGfxAnimation(x, y); + ResetGfxAnimation(x, y + 1); + } + if (MovDelay[x][y]) { + DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING); + DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING); + MovDelay[x][y]--; if (MovDelay[x][y]) return; @@ -7588,6 +7482,33 @@ void StartMoving(int x, int y) Store[x][y + 1] = Store[x][y]; Store[x][y] = 0; + PlayLevelSoundAction(x, y, ACTION_FILLING); + } + else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY) + { + if (!MovDelay[x][y]) + { + MovDelay[x][y] = TILEY + 1; + + ResetGfxAnimation(x, y); + ResetGfxAnimation(x, y + 1); + } + + if (MovDelay[x][y]) + { + DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING); + DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING); + + MovDelay[x][y]--; + if (MovDelay[x][y]) + return; + } + + Feld[x][y] = EL_QUICKSAND_FAST_EMPTY; + Feld[x][y + 1] = EL_QUICKSAND_FULL; + Store[x][y + 1] = Store[x][y]; + Store[x][y] = 0; + PlayLevelSoundAction(x, y, ACTION_FILLING); } } @@ -7626,7 +7547,7 @@ void StartMoving(int x, int y) else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE) { if (!MovDelay[x][y]) - MovDelay[x][y] = TILEY/4 + 1; + MovDelay[x][y] = TILEY / 4 + 1; if (MovDelay[x][y]) { @@ -7654,7 +7575,7 @@ void StartMoving(int x, int y) else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE) { if (!MovDelay[x][y]) - MovDelay[x][y] = TILEY/4 + 1; + MovDelay[x][y] = TILEY / 4 + 1; if (MovDelay[x][y]) { @@ -7682,7 +7603,7 @@ void StartMoving(int x, int y) else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE) { if (!MovDelay[x][y]) - MovDelay[x][y] = TILEY/4 + 1; + MovDelay[x][y] = TILEY / 4 + 1; if (MovDelay[x][y]) { @@ -7723,13 +7644,8 @@ void StartMoving(int x, int y) Store[x][y] = EL_ACID; } else if ( -#if USE_FIX_IMPACT_COLLISION (game.engine_version >= VERSION_IDENT(3,1,0,0) && CheckImpact[x][y] && !IS_FREE(x, y + 1)) || -#else - (game.engine_version >= VERSION_IDENT(3,1,0,0) && - CheckCollision[x][y] && !IS_FREE(x, y + 1)) || -#endif (game.engine_version >= VERSION_IDENT(3,0,7,0) && CAN_FALL(element) && WasJustFalling[x][y] && (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) || @@ -7789,7 +7705,6 @@ void StartMoving(int x, int y) boolean can_fall_both = (can_fall_left && can_fall_right); int slippery_type = element_info[Feld[x][y + 1]].slippery_type; -#if USE_NEW_ALL_SLIPPERY if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM) { if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both) @@ -7804,37 +7719,7 @@ void StartMoving(int x, int y) can_fall_any = (can_fall_left || can_fall_right); can_fall_both = FALSE; } -#else - if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1])) - { - if (slippery_type == SLIPPERY_ONLY_LEFT) - can_fall_right = FALSE; - else if (slippery_type == SLIPPERY_ONLY_RIGHT) - can_fall_left = FALSE; - else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both) - can_fall_right = FALSE; - else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both) - can_fall_left = FALSE; - - can_fall_any = (can_fall_left || can_fall_right); - can_fall_both = (can_fall_left && can_fall_right); - } -#endif - -#if USE_NEW_ALL_SLIPPERY -#else -#if USE_NEW_SP_SLIPPERY - /* !!! better use the same properties as for custom elements here !!! */ - else if (game.engine_version >= VERSION_IDENT(3,1,1,0) && - can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1])) - { - can_fall_right = FALSE; /* slip down on left side */ - can_fall_both = FALSE; - } -#endif -#endif -#if USE_NEW_ALL_SLIPPERY if (can_fall_both) { if (element == EL_BD_ROCK || element == EL_BD_DIAMOND) @@ -7844,18 +7729,6 @@ void StartMoving(int x, int y) can_fall_both = FALSE; } -#else - if (can_fall_both) - { - if (game.emulation == EMU_BOULDERDASH || - element == EL_BD_ROCK || element == EL_BD_DIAMOND) - can_fall_right = FALSE; /* slip down on left side */ - else - can_fall_left = !(can_fall_right = RND(2)); - - can_fall_both = FALSE; - } -#endif if (can_fall_any) { @@ -7864,11 +7737,7 @@ void StartMoving(int x, int y) started_moving = TRUE; } } -#if 0 - else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element)) -#else else if (IS_BELT_ACTIVE(Feld[x][y + 1])) -#endif { boolean left_is_free = (x > 0 && IS_FREE(x - 1, y)); boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y)); @@ -7896,26 +7765,11 @@ void StartMoving(int x, int y) } /* not "else if" because of elements that can fall and move (EL_SPRING) */ -#if 0 - if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE) -#else if (CAN_MOVE(element) && !started_moving) -#endif { int move_pattern = element_info[element].move_pattern; int newx, newy; -#if 0 -#if DEBUG - if (MovDir[x][y] == MV_NONE) - { - printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n", - x, y, element, element_info[element].token_name); - printf("StartMoving(): This should never happen!\n"); - } -#endif -#endif - Moving2Blocked(x, y, &newx, &newy); if (IS_PUSHABLE(element) && JustBeingPushed(x, y)) @@ -7955,7 +7809,7 @@ void StartMoving(int x, int y) element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON || element == EL_MOLE)) - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } } @@ -7989,7 +7843,7 @@ void StartMoving(int x, int y) if (IS_PLAYER(x, y)) DrawPlayerField(x, y); else - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING); @@ -8008,20 +7862,10 @@ void StartMoving(int x, int y) { int flamed = MovingOrBlocked2Element(xx, yy); - /* !!! */ -#if 0 - if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed)) - Bang(xx, yy); - else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy)) - RemoveMovingField(xx, yy); - else - RemoveField(xx, yy); -#else if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed)) Bang(xx, yy); else RemoveMovingField(xx, yy); -#endif ChangeDelay[xx][yy] = 0; @@ -8029,7 +7873,7 @@ void StartMoving(int x, int y) if (IN_SCR_FIELD(sx, sy)) { - DrawLevelFieldCrumbledSand(xx, yy); + TEST_DrawLevelFieldCrumbled(xx, yy); DrawGraphic(sx, sy, flame_graphic, frame); } } @@ -8037,7 +7881,7 @@ void StartMoving(int x, int y) { if (Feld[xx][yy] == EL_FLAMES) Feld[xx][yy] = EL_EMPTY; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } } } @@ -8080,7 +7924,7 @@ void StartMoving(int x, int y) Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN) { RemoveField(x, y); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); PlayLevelSound(newx, newy, SND_PENGUIN_PASSING); if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy))) @@ -8096,7 +7940,7 @@ void StartMoving(int x, int y) else if (IS_FOOD_PENGUIN(Feld[newx][newy])) { if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING) - DrawLevelField(newx, newy); + TEST_DrawLevelField(newx, newy); else GfxDir[x][y] = MovDir[x][y] = MV_NONE; } @@ -8107,7 +7951,7 @@ void StartMoving(int x, int y) if (IS_PLAYER(x, y)) DrawPlayerField(x, y); else - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); return; } @@ -8121,7 +7965,7 @@ void StartMoving(int x, int y) else { Feld[newx][newy] = EL_EMPTY; - DrawLevelField(newx, newy); + TEST_DrawLevelField(newx, newy); } PlayLevelSound(x, y, SND_PIG_DIGGING); @@ -8131,7 +7975,7 @@ void StartMoving(int x, int y) if (IS_PLAYER(x, y)) DrawPlayerField(x, y); else - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); return; } @@ -8210,78 +8054,21 @@ void StartMoving(int x, int y) else { Feld[newx][newy] = EL_EMPTY; - DrawLevelField(newx, newy); + TEST_DrawLevelField(newx, newy); PlayLevelSoundAction(x, y, ACTION_DIGGING); } } else if (!IS_FREE(newx, newy)) { -#if 0 - if (IS_PLAYER(x, y)) - DrawPlayerField(x, y); - else - DrawLevelField(x, y); -#endif - return; } } else if (IS_CUSTOM_ELEMENT(element) && CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)) { - int new_element = Feld[newx][newy]; - - if (!IS_FREE(newx, newy)) - { - int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING : - IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING : - ACTION_BREAKING); - - /* no element can dig solid indestructible elements */ - if (IS_INDESTRUCTIBLE(new_element) && - !IS_DIGGABLE(new_element) && - !IS_COLLECTIBLE(new_element)) - return; - - if (AmoebaNr[newx][newy] && - (new_element == EL_AMOEBA_FULL || - new_element == EL_BD_AMOEBA || - new_element == EL_AMOEBA_GROWING)) - { - AmoebaCnt[AmoebaNr[newx][newy]]--; - AmoebaCnt2[AmoebaNr[newx][newy]]--; - } - - if (IS_MOVING(newx, newy)) - RemoveMovingField(newx, newy); - else - { - RemoveField(newx, newy); - DrawLevelField(newx, newy); - } - - /* if digged element was about to explode, prevent the explosion */ - ExplodeField[newx][newy] = EX_TYPE_NONE; - - PlayLevelSoundAction(x, y, action); - } - - Store[newx][newy] = EL_EMPTY; -#if 1 - /* this makes it possible to leave the removed element again */ - if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element))) - Store[newx][newy] = new_element; -#else - if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element))) - { - int move_leave_element = element_info[element].move_leave_element; - - /* this makes it possible to leave the removed element again */ - Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ? - new_element : move_leave_element); - } -#endif + if (!DigFieldByCE(newx, newy, element)) + return; if (move_pattern & MV_MAZE_RUNNER_STYLE) { @@ -8296,7 +8083,7 @@ void StartMoving(int x, int y) if (IS_PLAYER(x, y)) DrawPlayerField(x, y); else - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); return; } @@ -8323,31 +8110,17 @@ void StartMoving(int x, int y) if (IS_PLAYER(x, y)) DrawPlayerField(x, y); else - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); PlayLevelSound(x, y, SND_DRAGON_ATTACKING); MovDelay[x][y] = 50; - /* !!! */ -#if 0 - RemoveField(newx, newy); -#endif Feld[newx][newy] = EL_FLAMES; if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY) - { -#if 0 - RemoveField(newx1, newy1); -#endif Feld[newx1][newy1] = EL_FLAMES; - } if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY) - { -#if 0 - RemoveField(newx2, newy2); -#endif Feld[newx2][newy2] = EL_FLAMES; - } return; } @@ -8361,7 +8134,7 @@ void StartMoving(int x, int y) else { Feld[newx][newy] = EL_EMPTY; - DrawLevelField(newx, newy); + TEST_DrawLevelField(newx, newy); } PlayLevelSound(x, y, SND_YAMYAM_DIGGING); @@ -8377,22 +8150,14 @@ void StartMoving(int x, int y) AmoebaCnt[AmoebaNr[newx][newy]]--; } -#if 0 - /* !!! test !!! */ - if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy)) - { - RemoveMovingField(newx, newy); - } -#else if (IS_MOVING(newx, newy)) { RemoveMovingField(newx, newy); } -#endif else { Feld[newx][newy] = EL_EMPTY; - DrawLevelField(newx, newy); + TEST_DrawLevelField(newx, newy); } PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING); @@ -8415,7 +8180,7 @@ void StartMoving(int x, int y) ResetGfxAnimation(x, y); GfxAction[x][y] = ACTION_DIGGING; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */ @@ -8424,7 +8189,7 @@ void StartMoving(int x, int y) else /* element == EL_PACMAN */ { Feld[newx][newy] = EL_EMPTY; - DrawLevelField(newx, newy); + TEST_DrawLevelField(newx, newy); PlayLevelSound(x, y, SND_PACMAN_DIGGING); } } @@ -8441,21 +8206,6 @@ void StartMoving(int x, int y) TurnRound(x, y); -#if 0 - /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */ - if (move_pattern & MV_ANY_DIRECTION && - move_pattern == MovDir[x][y]) - { - int blocking_element = - (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement); - - CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED, - MovDir[x][y]); - - element = Feld[x][y]; /* element might have changed */ - } -#endif - if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */ DrawLevelElementAnimation(x, y, element); @@ -8495,18 +8245,7 @@ void ContinueMoving(int x, int y) if (ABS(MovPos[x][y]) < TILEX) { -#if 0 - int ee = Feld[x][y]; - int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]); - - printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n", - x, y, ABS(MovPos[x][y]), - ee, gg, ff, - GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]); -#endif - - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); return; /* element is still moving */ } @@ -8525,7 +8264,7 @@ void ContinueMoving(int x, int y) { Feld[x][y] = EL_SAND; - DrawLevelFieldCrumbledSandNeighbours(x, y); + TEST_DrawLevelFieldCrumbledNeighbours(x, y); } else if (element == EL_QUICKSAND_FILLING) { @@ -8561,9 +8300,7 @@ void ContinueMoving(int x, int y) Feld[x][y] = EL_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; -#if USE_NEW_CUSTOM_VALUE InitField(newx, newy, FALSE); -#endif } else if (element == EL_BD_MAGIC_WALL_FILLING) { @@ -8579,9 +8316,7 @@ void ContinueMoving(int x, int y) Feld[x][y] = EL_BD_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; -#if USE_NEW_CUSTOM_VALUE InitField(newx, newy, FALSE); -#endif } else if (element == EL_DC_MAGIC_WALL_FILLING) { @@ -8597,9 +8332,7 @@ void ContinueMoving(int x, int y) Feld[x][y] = EL_DC_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; -#if USE_NEW_CUSTOM_VALUE InitField(newx, newy, FALSE); -#endif } else if (element == EL_AMOEBA_DROPPING) { @@ -8633,18 +8366,14 @@ void ContinueMoving(int x, int y) ChangeEvent[newx][newy] = ChangeEvent[x][y]; } -#if USE_NEW_CUSTOM_VALUE - CustomValue[newx][newy] = CustomValue[x][y]; -#endif + CustomValue[newx][newy] = CustomValue[x][y]; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; ChangeCount[x][y] = 0; ChangeEvent[x][y] = -1; -#if USE_NEW_CUSTOM_VALUE CustomValue[x][y] = 0; -#endif /* copy animation control values to new field */ GfxFrame[newx][newy] = GfxFrame[x][y]; @@ -8655,34 +8384,15 @@ void ContinueMoving(int x, int y) Pushed[x][y] = Pushed[newx][newy] = FALSE; /* some elements can leave other elements behind after moving */ -#if 1 if (ei->move_leave_element != EL_EMPTY && (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) && (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element))) -#else - if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY && - (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) && - (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element))) -#endif { int move_leave_element = ei->move_leave_element; -#if 1 -#if 1 /* this makes it possible to leave the removed element again */ if (ei->move_leave_element == EL_TRIGGER_ELEMENT) move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored); -#else - /* this makes it possible to leave the removed element again */ - if (ei->move_leave_element == EL_TRIGGER_ELEMENT) - move_leave_element = stored; -#endif -#else - /* this makes it possible to leave the removed element again */ - if (ei->move_leave_type == LEAVE_TYPE_LIMITED && - ei->move_leave_element == EL_TRIGGER_ELEMENT) - move_leave_element = stored; -#endif Feld[x][y] = move_leave_element; @@ -8692,7 +8402,7 @@ void ContinueMoving(int x, int y) InitField(x, y, FALSE); if (GFX_CRUMBLED(Feld[x][y])) - DrawLevelFieldCrumbledSandNeighbours(x, y); + TEST_DrawLevelFieldCrumbledNeighbours(x, y); if (ELEM_IS_PLAYER(move_leave_element)) RelocatePlayer(x, y, move_leave_element); @@ -8708,8 +8418,8 @@ void ContinueMoving(int x, int y) element_info[element].move_pattern == MV_WHEN_DROPPED))) GfxDir[x][y] = MovDir[newx][newy] = 0; - DrawLevelField(x, y); - DrawLevelField(newx, newy); + TEST_DrawLevelField(x, y); + TEST_DrawLevelField(newx, newy); Stop[newx][newy] = TRUE; /* ignore this element until the next frame */ @@ -8737,10 +8447,8 @@ void ContinueMoving(int x, int y) if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again) CheckCollision[newx][newy] = CHECK_DELAY_COLLISION; -#if USE_FIX_IMPACT_COLLISION if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again) CheckImpact[newx][newy] = CHECK_DELAY_IMPACT; -#endif } if (DONT_TOUCH(element)) /* object may be nasty to player or others */ @@ -8754,6 +8462,11 @@ void ContinueMoving(int x, int y) else if (element == EL_PENGUIN) TestIfFriendTouchesBadThing(newx, newy); + if (DONT_GET_HIT_BY(element)) + { + TestIfGoodThingGetsHitByBadThing(newx, newy, direction); + } + /* give the player one last chance (one more frame) to move away */ if (CAN_FALL(element) && direction == MV_DOWN && (last_line || (!IS_FREE(x, newy + 1) && @@ -8778,29 +8491,6 @@ void ContinueMoving(int x, int y) CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction); TestIfElementTouchesCustomElement(x, y); /* empty or new element */ - -#if 0 - if (ChangePage[newx][newy] != -1) /* delayed change */ - { - int page = ChangePage[newx][newy]; - struct ElementChangeInfo *change = &ei->change_page[page]; - - ChangePage[newx][newy] = -1; - - if (change->can_change) - { - if (ChangeElement(newx, newy, element, page)) - { - if (change->post_change_function) - change->post_change_function(newx, newy); - } - } - - if (change->has_action) - ExecuteCustomElementAction(newx, newy, element, page); - } -#endif - TestIfElementHitsCustomElement(newx, newy, direction); TestIfPlayerTouchesCustomElement(newx, newy); TestIfElementTouchesCustomElement(newx, newy); @@ -8971,7 +8661,7 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element) AmoebaNr[x][y] = 0; Feld[x][y] = new_element; InitField(x, y, FALSE); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); done = TRUE; } } @@ -8984,8 +8674,8 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element) void AmoebeWaechst(int x, int y) { - static unsigned long sound_delay = 0; - static unsigned long sound_delay_value = 0; + static unsigned int sound_delay = 0; + static unsigned int sound_delay_value = 0; if (!MovDelay[x][y]) /* start new growing cycle */ { @@ -9013,15 +8703,15 @@ void AmoebeWaechst(int x, int y) { Feld[x][y] = Store[x][y]; Store[x][y] = 0; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } } } void AmoebaDisappearing(int x, int y) { - static unsigned long sound_delay = 0; - static unsigned long sound_delay_value = 0; + static unsigned int sound_delay = 0; + static unsigned int sound_delay_value = 0; if (!MovDelay[x][y]) /* start new shrinking cycle */ { @@ -9045,7 +8735,7 @@ void AmoebaDisappearing(int x, int y) if (!MovDelay[x][y]) { Feld[x][y] = EL_EMPTY; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); /* don't let mole enter this field in this cycle; (give priority to objects falling to this field from above) */ @@ -9072,7 +8762,7 @@ void AmoebeAbleger(int ax, int ay) if (!level.amoeba_speed && element != EL_EMC_DRIPPER) { Feld[ax][ay] = EL_AMOEBA_DEAD; - DrawLevelField(ax, ay); + TEST_DrawLevelField(ax, ay); return; } @@ -9142,7 +8832,7 @@ void AmoebeAbleger(int ax, int ay) if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA)) { Feld[ax][ay] = EL_AMOEBA_DEAD; - DrawLevelField(ax, ay); + TEST_DrawLevelField(ax, ay); AmoebaCnt[AmoebaNr[ax][ay]]--; if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */ @@ -9206,7 +8896,7 @@ void AmoebeAbleger(int ax, int ay) return; } - DrawLevelField(newax, neway); + TEST_DrawLevelField(newax, neway); } void Life(int ax, int ay) @@ -9264,7 +8954,7 @@ void Life(int ax, int ay) { Feld[xx][yy] = EL_EMPTY; if (!Stop[xx][yy]) - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); Stop[xx][yy] = TRUE; changed = TRUE; } @@ -9277,7 +8967,7 @@ void Life(int ax, int ay) Feld[xx][yy] = element; MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1); if (!Stop[xx][yy]) - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); Stop[xx][yy] = TRUE; changed = TRUE; } @@ -9321,11 +9011,7 @@ static void RunTimegateWheel(int x, int y) static void InitMagicBallDelay(int x, int y) { -#if 1 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1; -#else - ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1; -#endif } static void ActivateMagicBall(int bx, int by) @@ -9505,9 +9191,6 @@ void DrawTwinkleOnField(int x, int y) { MovDelay[x][y]--; - if (setup.direct_draw && MovDelay[x][y]) - SetDrawtoField(DRAW_BUFFERED); - DrawLevelElementAnimation(x, y, Feld[x][y]); if (MovDelay[x][y] != 0) @@ -9516,18 +9199,6 @@ void DrawTwinkleOnField(int x, int y) 10 - MovDelay[x][y]); DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame); - - if (setup.direct_draw) - { - int dest_x, dest_y; - - dest_x = FX + SCREENX(x) * TILEX; - dest_y = FY + SCREENY(y) * TILEY; - - BlitBitmap(drawto_field, window, - dest_x, dest_y, TILEX, TILEY, dest_x, dest_y); - SetDrawtoField(DRAW_DIRECT); - } } } } @@ -9556,28 +9227,28 @@ void MauerWaechst(int x, int y) if (MovDir[x][y] == MV_LEFT) { if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y])) - DrawLevelField(x - 1, y); + TEST_DrawLevelField(x - 1, y); } else if (MovDir[x][y] == MV_RIGHT) { if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y])) - DrawLevelField(x + 1, y); + TEST_DrawLevelField(x + 1, y); } else if (MovDir[x][y] == MV_UP) { if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1])) - DrawLevelField(x, y - 1); + TEST_DrawLevelField(x, y - 1); } else { if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1])) - DrawLevelField(x, y + 1); + TEST_DrawLevelField(x, y + 1); } Feld[x][y] = Store[x][y]; Store[x][y] = 0; GfxDir[x][y] = MovDir[x][y] = MV_NONE; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } } } @@ -9668,7 +9339,7 @@ void MauerAbleger(int ax, int ay) } if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei)) - DrawLevelField(ax, ay); + TEST_DrawLevelField(ax, ay); if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1])) oben_massiv = TRUE; @@ -9786,7 +9457,7 @@ void MauerAblegerStahl(int ax, int ay) element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) && ((links_massiv && rechts_massiv) || element == EL_EXPANDABLE_STEELWALL_VERTICAL)) - Feld[ax][ay] = EL_WALL; + Feld[ax][ay] = EL_STEELWALL; if (new_wall) PlayLevelSoundAction(ax, ay, ACTION_GROWING); @@ -9832,7 +9503,7 @@ void CheckForDragon(int x, int y) if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES) { Feld[xx][yy] = EL_EMPTY; - DrawLevelField(xx, yy); + TEST_DrawLevelField(xx, yy); } else break; @@ -9896,7 +9567,7 @@ static void ChangeActiveTrap(int x, int y) /* if new animation frame was drawn, correct crumbled sand border */ if (IS_NEW_FRAME(GfxFrame[x][y], graphic)) - DrawLevelFieldCrumbledSand(x, y); + TEST_DrawLevelFieldCrumbled(x, y); } static int getSpecialActionElement(int element, int number, int base_element) @@ -9930,6 +9601,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) int action_type = change->action_type; int action_mode = change->action_mode; int action_arg = change->action_arg; + int action_element = change->action_element; int i; if (!change->has_action) @@ -9941,11 +9613,16 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) (level.time > 0 ? TimeLeft : TimePlayed); - int action_arg_element = + int action_arg_element_raw = (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player : action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element : action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element : + action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element : + action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element: + action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element : + action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element : EL_EMPTY); + int action_arg_element = GetElementFromGroupElement(action_arg_element_raw); int action_arg_direction = (action_arg >= CA_ARG_DIRECTION_LEFT && @@ -9974,11 +9651,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_type == CA_SET_LEVEL_GEMS ? level.gems_needed : action_type == CA_SET_LEVEL_TIME ? level.time : action_type == CA_SET_LEVEL_SCORE ? 0 : -#if USE_NEW_CUSTOM_VALUE action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) : -#else - action_type == CA_SET_CE_VALUE ? ei->custom_value_initial : -#endif action_type == CA_SET_CE_SCORE ? 0 : 0); @@ -9990,11 +9663,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min : action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max : action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset : -#if USE_NEW_CUSTOM_VALUE action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] : -#else - action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial : -#endif action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score : action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) : action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value : @@ -10002,10 +9671,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score : action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element): action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value: + action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element): action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) : action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score: + action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) : action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element : action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element : + action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element : -1); int action_arg_number_old = @@ -10021,20 +9693,15 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_mode, action_arg_number, action_arg_number_min, action_arg_number_max); -#if 1 - int trigger_player_bits = change->actual_trigger_player_bits; -#else int trigger_player_bits = - (change->actual_trigger_player >= EL_PLAYER_1 && - change->actual_trigger_player <= EL_PLAYER_4 ? - (1 << (change->actual_trigger_player - EL_PLAYER_1)) : - PLAYER_BITS_ANY); -#endif + (change->actual_trigger_player_bits != CH_PLAYER_NONE ? + change->actual_trigger_player_bits : change->trigger_player); int action_arg_player_bits = (action_arg >= CA_ARG_PLAYER_1 && action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER : action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits : + action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) : PLAYER_BITS_ANY); /* ---------- execute action -------------------------------------------- */ @@ -10072,13 +9739,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { TimeLeft = action_arg_number_new; -#if 1 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif if (!TimeLeft && setup.time_limit) for (i = 0; i < MAX_PLAYERS; i++) @@ -10092,13 +9755,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { local_player->score = action_arg_number_new; -#if 1 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score; DisplayGameControlValues(); -#else - DrawGameValue_Score(local_player->score); -#endif break; } @@ -10107,31 +9766,27 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { local_player->gems_still_needed = action_arg_number_new; -#if 1 - game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed; + game.snapshot.collected_item = TRUE; + + game_panel_controls[GAME_PANEL_GEMS].value = + local_player->gems_still_needed; DisplayGameControlValues(); -#else - DrawGameValue_Emeralds(local_player->gems_still_needed); -#endif break; } -#if !USE_PLAYER_GRAVITY - case CA_SET_LEVEL_GRAVITY: + case CA_SET_LEVEL_WIND: { - game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE : - action_arg == CA_ARG_GRAVITY_ON ? TRUE : - action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity : - game.gravity); + game.wind_direction = action_arg_direction; + break; } -#endif - case CA_SET_LEVEL_WIND: + case CA_SET_LEVEL_RANDOM_SEED: { - game.wind_direction = action_arg_direction; + /* ensure that setting a new random seed while playing is predictable */ + InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1); break; } @@ -10253,7 +9908,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } -#if USE_PLAYER_GRAVITY case CA_SET_PLAYER_GRAVITY: { for (i = 0; i < MAX_PLAYERS; i++) @@ -10270,7 +9924,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } -#endif case CA_SET_PLAYER_ARTWORK: { @@ -10285,10 +9938,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) (level.use_artwork_element[i] ? level.artwork_element[i] : stored_player[i].element_nr); -#if USE_GFX_RESET_PLAYER_ARTWORK if (stored_player[i].artwork_element != artwork_element) stored_player[i].Frame = 0; -#endif stored_player[i].artwork_element = artwork_element; @@ -10307,11 +9958,108 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } + case CA_SET_PLAYER_INVENTORY: + { + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + int j, k; + + if (trigger_player_bits & (1 << i)) + { + int inventory_element = action_arg_element; + + if (action_arg == CA_ARG_ELEMENT_TARGET || + action_arg == CA_ARG_ELEMENT_TRIGGER || + action_arg == CA_ARG_ELEMENT_ACTION) + { + int element = inventory_element; + int collect_count = element_info[element].collect_count_initial; + + if (!IS_CUSTOM_ELEMENT(element)) + collect_count = 1; + + if (collect_count == 0) + player->inventory_infinite_element = element; + else + for (k = 0; k < collect_count; k++) + if (player->inventory_size < MAX_INVENTORY_SIZE) + player->inventory_element[player->inventory_size++] = + element; + } + else if (action_arg == CA_ARG_INVENTORY_RM_TARGET || + action_arg == CA_ARG_INVENTORY_RM_TRIGGER || + action_arg == CA_ARG_INVENTORY_RM_ACTION) + { + if (player->inventory_infinite_element != EL_UNDEFINED && + IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element, + action_arg_element_raw)) + player->inventory_infinite_element = EL_UNDEFINED; + + for (k = 0, j = 0; j < player->inventory_size; j++) + { + if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j], + action_arg_element_raw)) + player->inventory_element[k++] = player->inventory_element[j]; + } + + player->inventory_size = k; + } + else if (action_arg == CA_ARG_INVENTORY_RM_FIRST) + { + if (player->inventory_size > 0) + { + for (j = 0; j < player->inventory_size - 1; j++) + player->inventory_element[j] = player->inventory_element[j + 1]; + + player->inventory_size--; + } + } + else if (action_arg == CA_ARG_INVENTORY_RM_LAST) + { + if (player->inventory_size > 0) + player->inventory_size--; + } + else if (action_arg == CA_ARG_INVENTORY_RM_ALL) + { + player->inventory_infinite_element = EL_UNDEFINED; + player->inventory_size = 0; + } + else if (action_arg == CA_ARG_INVENTORY_RESET) + { + player->inventory_infinite_element = EL_UNDEFINED; + player->inventory_size = 0; + + if (level.use_initial_inventory[i]) + { + for (j = 0; j < level.initial_inventory_size[i]; j++) + { + int element = level.initial_inventory_content[i][j]; + int collect_count = element_info[element].collect_count_initial; + + if (!IS_CUSTOM_ELEMENT(element)) + collect_count = 1; + + if (collect_count == 0) + player->inventory_infinite_element = element; + else + for (k = 0; k < collect_count; k++) + if (player->inventory_size < MAX_INVENTORY_SIZE) + player->inventory_element[player->inventory_size++] = + element; + } + } + } + } + } + + break; + } + /* ---------- CE actions ---------------------------------------------- */ case CA_SET_CE_VALUE: { -#if USE_NEW_CUSTOM_VALUE int last_ce_value = CustomValue[x][y]; CustomValue[x][y] = action_arg_number_new; @@ -10327,14 +10075,12 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X); } } -#endif break; } case CA_SET_CE_SCORE: { -#if USE_NEW_CUSTOM_VALUE int last_ce_score = ei->collect_score; ei->collect_score = action_arg_number_new; @@ -10370,13 +10116,44 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) } } } -#endif break; } - /* ---------- engine actions ------------------------------------------ */ - + case CA_SET_CE_ARTWORK: + { + int artwork_element = action_arg_element; + boolean reset_frame = FALSE; + int xx, yy; + + if (action_arg == CA_ARG_ELEMENT_RESET) + artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial : + element); + + if (ei->gfx_element != artwork_element) + reset_frame = TRUE; + + ei->gfx_element = artwork_element; + + SCAN_PLAYFIELD(xx, yy) + { + if (Feld[xx][yy] == element) + { + if (reset_frame) + { + ResetGfxAnimation(xx, yy); + ResetRandomAnimationValue(xx, yy); + } + + TEST_DrawLevelField(xx, yy); + } + } + + break; + } + + /* ---------- engine actions ------------------------------------------ */ + case CA_SET_ENGINE_SCAN_MODE: { InitPlayfieldScanMode(action_arg); @@ -10394,32 +10171,13 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) int old_element = Feld[x][y]; int new_element = GetElementFromGroupElement(element); int previous_move_direction = MovDir[x][y]; -#if USE_NEW_CUSTOM_VALUE int last_ce_value = CustomValue[x][y]; -#endif boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y); boolean new_element_is_player = ELEM_IS_PLAYER(new_element); boolean add_player_onto_element = (new_element_is_player && -#if USE_CODE_THAT_BREAKS_SNAKE_BITE - /* this breaks SnakeBite when a snake is - halfway through a door that closes */ - /* NOW FIXED AT LEVEL INIT IN files.c */ new_element != EL_SOKOBAN_FIELD_PLAYER && -#endif IS_WALKABLE(old_element)); -#if 0 - /* check if element under the player changes from accessible to unaccessible - (needed for special case of dropping element which then changes) */ - if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && - IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) - { - Bang(x, y); - - return; - } -#endif - if (!add_player_onto_element) { if (IS_MOVING(x, y) || IS_BLOCKED(x, y)) @@ -10429,39 +10187,28 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) Feld[x][y] = new_element; -#if !USE_GFX_RESET_GFX_ANIMATION - ResetGfxAnimation(x, y); - ResetRandomAnimationValue(x, y); -#endif - if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS) MovDir[x][y] = previous_move_direction; -#if USE_NEW_CUSTOM_VALUE if (element_info[new_element].use_last_ce_value) CustomValue[x][y] = last_ce_value; -#endif InitField_WithBug1(x, y, FALSE); new_element = Feld[x][y]; /* element may have changed */ -#if USE_GFX_RESET_GFX_ANIMATION ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); -#endif - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); if (GFX_CRUMBLED(new_element)) - DrawLevelFieldCrumbledSandNeighbours(x, y); + TEST_DrawLevelFieldCrumbledNeighbours(x, y); } -#if 1 /* check if element under the player changes from accessible to unaccessible (needed for special case of dropping element which then changes) */ /* (must be checked after creating new element for walkable group elements) */ -#if USE_FIX_KILLED_BY_NON_WALKABLE if (IS_PLAYER(x, y) && !player_explosion_protected && IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) { @@ -10469,16 +10216,6 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) return; } -#else - if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && - IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) - { - Bang(x, y); - - return; - } -#endif -#endif /* "ChangeCount" not set yet to allow "entered by player" change one time */ if (new_element_is_player) @@ -10501,7 +10238,6 @@ static void CreateElementFromChange(int x, int y, int element) { element = GET_VALID_RUNTIME_ELEMENT(element); -#if USE_STOP_CHANGED_ELEMENTS if (game.engine_version >= VERSION_IDENT(3,2,0,7)) { int old_element = Feld[x][y]; @@ -10512,7 +10248,6 @@ static void CreateElementFromChange(int x, int y, int element) (!CAN_MOVE(old_element) || !CAN_MOVE(element))) Stop[x][y] = TRUE; } -#endif CreateFieldExt(x, y, element, TRUE); } @@ -10534,8 +10269,8 @@ static boolean ChangeElement(int x, int y, int element, int page) { /* reset actual trigger element, trigger player and action element */ change->actual_trigger_element = EL_EMPTY; - change->actual_trigger_player = EL_PLAYER_1; - change->actual_trigger_player_bits = CH_PLAYER_1; + change->actual_trigger_player = EL_EMPTY; + change->actual_trigger_player_bits = CH_PLAYER_NONE; change->actual_trigger_side = CH_SIDE_NONE; change->actual_trigger_ce_value = 0; change->actual_trigger_ce_score = 0; @@ -10688,13 +10423,12 @@ static boolean ChangeElement(int x, int y, int element, int page) return TRUE; } -#if USE_NEW_DELAYED_ACTION - static void HandleElementChange(int x, int y, int page) { int element = MovingOrBlocked2Element(x, y); struct ElementInfo *ei = &element_info[element]; struct ElementChangeInfo *change = &ei->change_page[page]; + boolean handle_action_before_change = FALSE; #ifdef DEBUG if (!CAN_CHANGE_OR_HAS_ACTION(element) && @@ -10711,11 +10445,6 @@ static void HandleElementChange(int x, int y, int page) /* this can happen with classic bombs on walkable, changing elements */ if (!CAN_CHANGE_OR_HAS_ACTION(element)) { -#if 0 - if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */ - ChangeDelay[x][y] = 0; -#endif - return; } @@ -10725,19 +10454,56 @@ static void HandleElementChange(int x, int y, int page) if (change->can_change) { -#if 1 /* !!! not clear why graphic animation should be reset at all here !!! */ /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */ -#if USE_GFX_RESET_WHEN_NOT_MOVING + /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */ + + /* + GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION: + + When using an animation frame delay of 1 (this only happens with + "sp_zonk.moving.left/right" in the classic graphics), the default + (non-moving) animation shows wrong animation frames (while the + moving animation, like "sp_zonk.moving.left/right", is correct, + so this graphical bug never shows up with the classic graphics). + For an animation with 4 frames, this causes wrong frames 0,0,1,2 + be drawn instead of the correct frames 0,1,2,3. This is caused by + "GfxFrame[][]" being reset *twice* (in two successive frames) after + an element change: First when the change delay ("ChangeDelay[][]") + counter has reached zero after decrementing, then a second time in + the next frame (after "GfxFrame[][]" was already incremented) when + "ChangeDelay[][]" is reset to the initial delay value again. + + This causes frame 0 to be drawn twice, while the last frame won't + be drawn anymore, resulting in the wrong frame sequence 0,0,1,2. + + As some animations may already be cleverly designed around this bug + (at least the "Snake Bite" snake tail animation does this), it cannot + simply be fixed here without breaking such existing animations. + Unfortunately, it cannot easily be detected if a graphics set was + designed "before" or "after" the bug was fixed. As a workaround, + a new graphics set option "game.graphics_engine_version" was added + to be able to specify the game's major release version for which the + graphics set was designed, which can then be used to decide if the + bugfix should be used (version 4 and above) or not (version 3 or + below, or if no version was specified at all, as with old sets). + + (The wrong/fixed animation frames can be tested with the test level set + "test_gfxframe" and level "000", which contains a specially prepared + custom element at level position (x/y) == (11/9) which uses the zonk + animation mentioned above. Using "game.graphics_engine_version: 4" + fixes the wrong animation frames, showing the correct frames 0,1,2,3. + This can also be seen from the debug output for this test element.) + */ + /* when a custom element is about to change (for example by change delay), do not reset graphic animation when the custom element is moving */ - if (!IS_MOVING(x, y)) -#endif + if (game.graphics_engine_version < 4 && + !IS_MOVING(x, y)) { ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); } -#endif if (change->pre_change_function) change->pre_change_function(x, y); @@ -10777,6 +10543,13 @@ static void HandleElementChange(int x, int y, int page) return; } + /* special case: set new level random seed before changing element */ + if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED) + handle_action_before_change = TRUE; + + if (change->has_action && handle_action_before_change) + ExecuteCustomElementAction(x, y, element, page); + if (change->can_change) { if (ChangeElement(x, y, element, page)) @@ -10786,92 +10559,11 @@ static void HandleElementChange(int x, int y, int page) } } - if (change->has_action) + if (change->has_action && !handle_action_before_change) ExecuteCustomElementAction(x, y, element, page); } } -#else - -static void HandleElementChange(int x, int y, int page) -{ - int element = MovingOrBlocked2Element(x, y); - struct ElementInfo *ei = &element_info[element]; - struct ElementChangeInfo *change = &ei->change_page[page]; - -#ifdef DEBUG - if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y])) - { - printf("\n\n"); - printf("HandleElementChange(): %d,%d: element = %d ('%s')\n", - x, y, element, element_info[element].token_name); - printf("HandleElementChange(): This should never happen!\n"); - printf("\n\n"); - } -#endif - - /* this can happen with classic bombs on walkable, changing elements */ - if (!CAN_CHANGE(element)) - { -#if 0 - if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */ - ChangeDelay[x][y] = 0; -#endif - - return; - } - - if (ChangeDelay[x][y] == 0) /* initialize element change */ - { - ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1; - - ResetGfxAnimation(x, y); - ResetRandomAnimationValue(x, y); - - if (change->pre_change_function) - change->pre_change_function(x, y); - } - - ChangeDelay[x][y]--; - - if (ChangeDelay[x][y] != 0) /* continue element change */ - { - int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - - if (IS_ANIMATED(graphic)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - - if (change->change_function) - change->change_function(x, y); - } - else /* finish element change */ - { - if (ChangePage[x][y] != -1) /* remember page from delayed change */ - { - page = ChangePage[x][y]; - ChangePage[x][y] = -1; - - change = &ei->change_page[page]; - } - - if (IS_MOVING(x, y)) /* never change a running system ;-) */ - { - ChangeDelay[x][y] = 1; /* try change after next move step */ - ChangePage[x][y] = page; /* remember page to use for change */ - - return; - } - - if (ChangeElement(x, y, element, page)) - { - if (change->post_change_function) - change->post_change_function(x, y); - } - } -} - -#endif - static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, int trigger_element, int trigger_event, @@ -10886,12 +10578,6 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, if (!(trigger_events[trigger_element][trigger_event])) return FALSE; -#if 0 - printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n", - trigger_event, recursion_loop_depth, recursion_loop_detected, - recursion_loop_element, EL_NAME(recursion_loop_element)); -#endif - RECURSION_LOOP_DETECTION_START(trigger_element, FALSE); for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -10932,24 +10618,32 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, { if (change->can_change && !change_done) { + /* if element already changed in this frame, not only prevent + another element change (checked in ChangeElement()), but + also prevent additional element actions for this element */ + + if (ChangeCount[x][y] >= game.max_num_changes_per_frame && + !level.use_action_after_change_bug) + continue; + ChangeDelay[x][y] = 1; ChangeEvent[x][y] = trigger_event; HandleElementChange(x, y, p); } -#if USE_NEW_DELAYED_ACTION else if (change->has_action) { + /* if element already changed in this frame, not only prevent + another element change (checked in ChangeElement()), but + also prevent additional element actions for this element */ + + if (ChangeCount[x][y] >= game.max_num_changes_per_frame && + !level.use_action_after_change_bug) + continue; + ExecuteCustomElementAction(x, y, element, p); PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); } -#else - if (change->has_action) - { - ExecuteCustomElementAction(x, y, element, p); - PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); - } -#endif } } @@ -10988,11 +10682,6 @@ static boolean CheckElementChangeExt(int x, int y, element = Feld[x][y]; } -#if 0 - /* check if element has already changed */ - if (Feld[x][y] != element) - return FALSE; -#else /* check if element has already changed or is about to change after moving */ if ((game.engine_version < VERSION_IDENT(3,2,0,7) && Feld[x][y] != element) || @@ -11001,13 +10690,6 @@ static boolean CheckElementChangeExt(int x, int y, (ChangeCount[x][y] >= game.max_num_changes_per_frame || ChangePage[x][y] != -1))) return FALSE; -#endif - -#if 0 - printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n", - trigger_event, recursion_loop_depth, recursion_loop_detected, - recursion_loop_element, EL_NAME(recursion_loop_element)); -#endif RECURSION_LOOP_DETECTION_START(trigger_element, FALSE); @@ -11023,10 +10705,7 @@ static boolean CheckElementChangeExt(int x, int y, (trigger_event == CE_TOUCHING_X || trigger_event == CE_HITTING_X || trigger_event == CE_HIT_BY_X || -#if 1 - /* this one was forgotten until 3.2.3 */ - trigger_event == CE_DIGGING_X); -#endif + trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */ if (change->can_change_or_has_action && change->has_event[trigger_event] && @@ -11075,19 +10754,11 @@ static boolean CheckElementChangeExt(int x, int y, change_done = TRUE; } -#if USE_NEW_DELAYED_ACTION else if (change->has_action) { ExecuteCustomElementAction(x, y, element, p); PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); } -#else - if (change->has_action) - { - ExecuteCustomElementAction(x, y, element, p); - PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); - } -#endif } } @@ -11278,9 +10949,53 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) } } +static void CheckSaveEngineSnapshot(struct PlayerInfo *player) +{ + if ((!player->is_moving && player->was_moving) || + (player->MovPos == 0 && player->was_moving) || + (player->is_snapping && !player->was_snapping) || + (player->is_dropping && !player->was_dropping)) + { + if (!CheckSaveEngineSnapshotToList()) + return; + + player->was_moving = FALSE; + player->was_snapping = TRUE; + player->was_dropping = TRUE; + } + else + { + if (player->is_moving) + player->was_moving = TRUE; + + if (!player->is_snapping) + player->was_snapping = FALSE; + + if (!player->is_dropping) + player->was_dropping = FALSE; + } +} + +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); + SnapField(player, 0, 0); /* stop snapping */ + } + } + + CheckSaveEngineSnapshot(player); +} + static byte PlayerActions(struct PlayerInfo *player, byte player_action) { - boolean moved = FALSE, snapped = FALSE, dropped = FALSE; int left = player_action & JOY_LEFT; int right = player_action & JOY_RIGHT; int up = player_action & JOY_UP; @@ -11296,23 +11011,16 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) if (player_action) { if (button1) - snapped = SnapField(player, dx, dy); + SnapField(player, dx, dy); else { if (button2) - dropped = DropElement(player); + DropElement(player); - moved = MovePlayer(player, dx, dy); + MovePlayer(player, dx, dy); } - if (tape.single_step && tape.recording && !tape.pausing) - { - if (button1 || (dropped && !moved)) - { - TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); - SnapField(player, 0, 0); /* stop snapping */ - } - } + CheckSingleStepMode(player); SetPlayerWaiting(player, FALSE); @@ -11336,14 +11044,39 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) player->is_dropping_pressed = FALSE; player->drop_pressed_delay = 0; + CheckSingleStepMode(player); + return 0; } } +static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action, + byte *tape_action) +{ + if (!tape.use_mouse) + return; + + mouse_action->lx = tape_action[TAPE_ACTION_LX]; + mouse_action->ly = tape_action[TAPE_ACTION_LY]; + mouse_action->button = tape_action[TAPE_ACTION_BUTTON]; +} + +static void SetTapeActionFromMouseAction(byte *tape_action, + struct MouseActionInfo *mouse_action) +{ + if (!tape.use_mouse) + return; + + tape_action[TAPE_ACTION_LX] = mouse_action->lx; + tape_action[TAPE_ACTION_LY] = mouse_action->ly; + tape_action[TAPE_ACTION_BUTTON] = mouse_action->button; +} + static void CheckLevelTime() { int i; + /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */ if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { if (level.native_em_level->lev->home == 0) /* all players at home */ @@ -11361,6 +11094,36 @@ static void CheckLevelTime() level.native_em_level->ply[3]->alive == 0) /* all dead */ AllPlayersGone = TRUE; } + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + { + if (game_sp.LevelSolved && + !game_sp.GameOver) /* game won */ + { + PlayerWins(local_player); + + game_sp.GameOver = TRUE; + + AllPlayersGone = TRUE; + } + + if (game_sp.GameOver) /* game lost */ + AllPlayersGone = TRUE; + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + if (game_mm.level_solved && + !game_mm.game_over) /* game won */ + { + PlayerWins(local_player); + + game_mm.game_over = TRUE; + + AllPlayersGone = TRUE; + } + + if (game_mm.game_over) /* game lost */ + AllPlayersGone = TRUE; + } if (TimeFrames >= FRAMES_PER_SECOND) { @@ -11391,13 +11154,10 @@ static void CheckLevelTime() if (TimeLeft <= 10 && setup.time_limit) PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); -#if 1 - game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; + /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value + is reset from other values in UpdateGameDoorValues() -- FIX THIS */ - DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif + game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; if (!TimeLeft && setup.time_limit) { @@ -11408,28 +11168,23 @@ static void CheckLevelTime() KillPlayer(&stored_player[i]); } } -#if 1 - else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ + else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */ { game_panel_controls[GAME_PANEL_TIME].value = TimePlayed; - - DisplayGameControlValues(); } -#else - else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ - DrawGameValue_Time(TimePlayed); -#endif level.native_em_level->lev->time = - (level.time == 0 ? TimePlayed : TimeLeft); + (game.no_time_limit ? TimePlayed : TimeLeft); } if (tape.recording || tape.playing) DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); } - UpdateGameDoorValues(); - DrawGameDoorValues(); + if (tape.recording || tape.playing) + DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); + + UpdateAndDisplayGameControlValues(); } void AdvanceFrameAndPlayerCounters(int player_nr) @@ -11450,7 +11205,6 @@ void AdvanceFrameAndPlayerCounters(int player_nr) if (!advance_player_counters) /* not all players may be affected */ continue; -#if USE_NEW_PLAYER_ANIM if (move_frames == 0) /* less than one move per game frame */ { int stepsize = TILEX / move_delay_value; @@ -11461,7 +11215,6 @@ void AdvanceFrameAndPlayerCounters(int player_nr) if (count % delay == 0) move_frames = 1; } -#endif stored_player[i].Frame += move_frames; @@ -11484,9 +11237,9 @@ void AdvanceFrameAndPlayerCounters(int player_nr) } void StartGameActions(boolean init_network_game, boolean record_tape, - long random_seed) + int random_seed) { - unsigned long new_random_seed = InitRND(random_seed); + unsigned int new_random_seed = InitRND(random_seed); if (record_tape) TapeStartRecording(new_random_seed); @@ -11503,10 +11256,12 @@ void StartGameActions(boolean init_network_game, boolean record_tape, InitGame(); } -void GameActions() +void GameActionsExt() { - static unsigned long game_frame_delay = 0; - unsigned long game_frame_delay_value; +#if 0 + static unsigned int game_frame_delay = 0; +#endif + unsigned int game_frame_delay_value; byte *recorded_player_action; byte summarized_player_action = 0; byte tape_action[MAX_PLAYERS]; @@ -11515,9 +11270,9 @@ void GameActions() /* detect endless loops, caused by custom element programming */ if (recursion_loop_detected && recursion_loop_depth == 0) { - char *message = getStringCat3("Internal Error ! Element ", + char *message = getStringCat3("Internal Error! Element ", EL_NAME(recursion_loop_element), - " caused endless loop ! Quit the game ?"); + " caused endless loop! Quit the game?"); Error(ERR_WARN, "element '%s' caused endless loop in game engine", EL_NAME(recursion_loop_element)); @@ -11532,8 +11287,9 @@ void GameActions() } if (game.restart_level) - StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE); + StartGameActions(options.network, setup.autorecord, level.random_seed); + /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */ if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { if (level.native_em_level->lev->home == 0) /* all players at home */ @@ -11551,6 +11307,36 @@ void GameActions() level.native_em_level->ply[3]->alive == 0) /* all dead */ AllPlayersGone = TRUE; } + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + { + if (game_sp.LevelSolved && + !game_sp.GameOver) /* game won */ + { + PlayerWins(local_player); + + game_sp.GameOver = TRUE; + + AllPlayersGone = TRUE; + } + + if (game_sp.GameOver) /* game lost */ + AllPlayersGone = TRUE; + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + if (game_mm.level_solved && + !game_mm.game_over) /* game won */ + { + PlayerWins(local_player); + + game_mm.game_over = TRUE; + + AllPlayersGone = TRUE; + } + + if (game_mm.game_over) /* game lost */ + AllPlayersGone = TRUE; + } if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd) GameWon(); @@ -11567,9 +11353,22 @@ void GameActions() if (tape.playing && tape.warp_forward && !tape.pausing) game_frame_delay_value = 0; + SetVideoFrameDelay(game_frame_delay_value); + +#if 0 +#if 0 + /* ---------- main game synchronization point ---------- */ + + int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value); + + printf("::: skip == %d\n", skip); + +#else /* ---------- main game synchronization point ---------- */ WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value); +#endif +#endif if (network_playing && !network_player_action_received) { @@ -11600,11 +11399,15 @@ void GameActions() /* when playing tape, read previously recorded player input from tape data */ recorded_player_action = (tape.playing ? TapePlayAction() : NULL); -#if 1 + local_player->effective_mouse_action = local_player->mouse_action; + + if (recorded_player_action != NULL) + SetMouseActionFromTapeAction(&local_player->effective_mouse_action, + recorded_player_action); + /* TapePlayAction() may return NULL when toggling to "pause before death" */ if (tape.pausing) return; -#endif if (tape.set_centered_player) { @@ -11616,7 +11419,7 @@ void GameActions() { summarized_player_action |= stored_player[i].action; - if (!network_playing) + if (!network_playing && (game.team_mode || tape.playing)) stored_player[i].effective_action = stored_player[i].action; } @@ -11625,10 +11428,16 @@ void GameActions() SendToServer_MovePlayer(summarized_player_action); #endif - if (!options.network && !setup.team_mode) - local_player->effective_action = summarized_player_action; + // summarize all actions at local players mapped input device position + // (this allows using different input devices in single player mode) + if (!options.network && !game.team_mode) + stored_player[map_player_action[local_player->index_nr]].effective_action = + summarized_player_action; - if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1) + if (tape.recording && + setup.team_mode && + setup.input_on_focus && + game.centered_player_nr != -1) { for (i = 0; i < MAX_PLAYERS; i++) stored_player[i].effective_action = @@ -11643,25 +11452,140 @@ void GameActions() { tape_action[i] = stored_player[i].effective_action; - /* (this can only happen in the R'n'D game engine) */ - if (tape.recording && tape_action[i] && !tape.player_participates[i]) - tape.player_participates[i] = TRUE; /* player just appeared from CE */ + /* (this may happen in the RND game engine if a player was not present on + the playfield on level start, but appeared later from a custom element */ + if (setup.team_mode && + tape.recording && + tape_action[i] && + !tape.player_participates[i]) + tape.player_participates[i] = TRUE; } + SetTapeActionFromMouseAction(tape_action, + &local_player->effective_mouse_action); + /* only record actions from input devices, but not programmed actions */ if (tape.recording) TapeRecordAction(tape_action); +#if USE_NEW_PLAYER_ASSIGNMENTS + // !!! also map player actions in single player mode !!! + // if (game.team_mode) + if (1) + { + byte mapped_action[MAX_PLAYERS]; + +#if DEBUG_PLAYER_ACTIONS + printf(":::"); + for (i = 0; i < MAX_PLAYERS; i++) + printf(" %d, ", stored_player[i].effective_action); +#endif + + for (i = 0; i < MAX_PLAYERS; i++) + mapped_action[i] = stored_player[map_player_action[i]].effective_action; + + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].effective_action = mapped_action[i]; + +#if DEBUG_PLAYER_ACTIONS + printf(" =>"); + for (i = 0; i < MAX_PLAYERS; i++) + printf(" %d, ", stored_player[i].effective_action); + printf("\n"); +#endif + } +#if DEBUG_PLAYER_ACTIONS + else + { + printf(":::"); + for (i = 0; i < MAX_PLAYERS; i++) + printf(" %d, ", stored_player[i].effective_action); + printf("\n"); + } +#endif +#endif + + for (i = 0; i < MAX_PLAYERS; i++) + { + // allow engine snapshot in case of changed movement attempt + if ((game.snapshot.last_action[i] & KEY_MOTION) != + (stored_player[i].effective_action & KEY_MOTION)) + game.snapshot.changed_action = TRUE; + + // allow engine snapshot in case of snapping/dropping attempt + if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 && + (stored_player[i].effective_action & KEY_BUTTON) != 0) + game.snapshot.changed_action = TRUE; + + game.snapshot.last_action[i] = stored_player[i].effective_action; + } + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { GameActions_EM_Main(); } + else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + { + GameActions_SP_Main(); + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + { + GameActions_MM_Main(); + } else { - GameActions_RND(); + GameActions_RND_Main(); + } + + BlitScreenToBitmap(backbuffer); + + CheckLevelTime(); + + AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */ + + if (global.show_frames_per_second) + { + static unsigned int fps_counter = 0; + static int fps_frames = 0; + unsigned int fps_delay_ms = Counter() - fps_counter; + + fps_frames++; + + if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */ + { + global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms; + + fps_frames = 0; + fps_counter = Counter(); + + /* always draw FPS to screen after FPS value was updated */ + redraw_mask |= REDRAW_FPS; + } + + /* only draw FPS if no screen areas are deactivated (invisible warp mode) */ + if (GetDrawDeactivationMask() == REDRAW_NONE) + redraw_mask |= REDRAW_FPS; } } +static void GameActions_CheckSaveEngineSnapshot() +{ + if (!game.snapshot.save_snapshot) + return; + + // clear flag for saving snapshot _before_ saving snapshot + game.snapshot.save_snapshot = FALSE; + + SaveEngineSnapshotToList(); +} + +void GameActions() +{ + GameActionsExt(); + + GameActions_CheckSaveEngineSnapshot(); +} + void GameActions_EM_Main() { byte effective_action[MAX_PLAYERS]; @@ -11672,20 +11596,47 @@ void GameActions_EM_Main() effective_action[i] = stored_player[i].effective_action; GameActions_EM(effective_action, warp_mode); +} - CheckLevelTime(); +void GameActions_SP_Main() +{ + byte effective_action[MAX_PLAYERS]; + boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); + int i; - AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */ + for (i = 0; i < MAX_PLAYERS; i++) + effective_action[i] = stored_player[i].effective_action; + + GameActions_SP(effective_action, warp_mode); + + for (i = 0; i < MAX_PLAYERS; i++) + { + if (stored_player[i].force_dropping) + stored_player[i].action |= KEY_BUTTON_DROP; + + stored_player[i].force_dropping = FALSE; + } +} + +void GameActions_MM_Main() +{ + boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); + + GameActions_MM(local_player->effective_mouse_action, warp_mode); +} + +void GameActions_RND_Main() +{ + GameActions_RND(); } void GameActions_RND() { int magic_wall_x = 0, magic_wall_y = 0; - int i, x, y, element, graphic; + int i, x, y, element, graphic, last_gfx_frame; InitPlayfieldScanModeVars(); -#if USE_ONE_MORE_CHANGE_PER_FRAME if (game.engine_version >= VERSION_IDENT(3,2,0,7)) { SCAN_PLAYFIELD(x, y) @@ -11694,7 +11645,6 @@ void GameActions_RND() ChangeEvent[x][y] = -1; } } -#endif if (game.set_centered_player) { @@ -11794,10 +11744,6 @@ void GameActions_RND() } } -#if 0 - debug_print_timestamp(0, "start main loop profiling"); -#endif - SCAN_PLAYFIELD(x, y) { ChangeCount[x][y] = 0; @@ -11811,19 +11757,17 @@ void GameActions_RND() RemoveField(x, y); } -#if USE_NEW_SNAP_DELAY if (Feld[x][y] == EL_ELEMENT_SNAPPING) { MovDelay[x][y]--; if (MovDelay[x][y] <= 0) { RemoveField(x, y); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); TestIfElementTouchesCustomElement(x, y); /* for empty space */ } } -#endif #if DEBUG if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1) @@ -11845,270 +11789,72 @@ void GameActions_RND() if (CheckImpact[x][y] > 0) CheckImpact[x][y]--; - GfxFrame[x][y]++; - - /* reset finished pushing action (not done in ContinueMoving() to allow - continuous pushing animation for elements with zero push delay) */ - if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y)) - { - ResetGfxAnimation(x, y); - DrawLevelField(x, y); - } - -#if DEBUG - if (IS_BLOCKED(x, y)) - { - int oldx, oldy; - - Blocked2Moving(x, y, &oldx, &oldy); - if (!IS_MOVING(oldx, oldy)) - { - printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n"); - printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y); - printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy); - printf("GameActions(): This should never happen!\n"); - } - } -#endif - } - -#if 0 - debug_print_timestamp(0, "- time for pre-main loop:"); -#endif - -#if 0 // -------------------- !!! TEST ONLY !!! -------------------- - SCAN_PLAYFIELD(x, y) - { - element = Feld[x][y]; - graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - -#if 1 - { -#if 1 - int element2 = element; - int graphic2 = graphic; -#else - int element2 = Feld[x][y]; - int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]); -#endif - int last_gfx_frame = GfxFrame[x][y]; - - if (graphic_info[graphic2].anim_global_sync) - GfxFrame[x][y] = FrameCounter; - else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE) - GfxFrame[x][y] = CustomValue[x][y]; - else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE) - GfxFrame[x][y] = element_info[element2].collect_score; - else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY) - GfxFrame[x][y] = ChangeDelay[x][y]; - - if (redraw && GfxFrame[x][y] != last_gfx_frame) - DrawLevelGraphicAnimation(x, y, graphic2); - } -#else - ResetGfxFrame(x, y, TRUE); -#endif - -#if 1 - if (ANIM_MODE(graphic) == ANIM_RANDOM && - IS_NEXT_FRAME(GfxFrame[x][y], graphic)) - ResetRandomAnimationValue(x, y); -#endif - -#if 1 - SetRandomAnimationValue(x, y); -#endif - -#if 1 - PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]); -#endif - } -#endif // -------------------- !!! TEST ONLY !!! -------------------- - -#if 0 - debug_print_timestamp(0, "- time for TEST loop: -->"); -#endif - - SCAN_PLAYFIELD(x, y) - { - element = Feld[x][y]; - graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - - ResetGfxFrame(x, y, TRUE); - - if (ANIM_MODE(graphic) == ANIM_RANDOM && - IS_NEXT_FRAME(GfxFrame[x][y], graphic)) - ResetRandomAnimationValue(x, y); - - SetRandomAnimationValue(x, y); - - PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]); - - if (IS_INACTIVE(element)) - { - if (IS_ANIMATED(graphic)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - - continue; - } - - /* this may take place after moving, so 'element' may have changed */ - if (IS_CHANGING(x, y) && - (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y])) - { - int page = element_info[element].event_page_nr[CE_DELAY]; - -#if 1 - HandleElementChange(x, y, page); -#else - if (CAN_CHANGE(element)) - HandleElementChange(x, y, page); - - if (HAS_ACTION(element)) - ExecuteCustomElementAction(x, y, element, page); -#endif - - element = Feld[x][y]; - graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - } - -#if 0 // --------------------------------------------------------------------- - - if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element))) - { - StartMoving(x, y); - - element = Feld[x][y]; - graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - - if (IS_ANIMATED(graphic) && - !IS_MOVING(x, y) && - !Stop[x][y]) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - - if (IS_GEM(element) || element == EL_SP_INFOTRON) - DrawTwinkleOnField(x, y); - } - else if (IS_MOVING(x, y)) - ContinueMoving(x, y); - else - { - switch (element) - { - case EL_ACID: - case EL_EXIT_OPEN: - case EL_EM_EXIT_OPEN: - case EL_SP_EXIT_OPEN: - case EL_STEEL_EXIT_OPEN: - case EL_EM_STEEL_EXIT_OPEN: - case EL_SP_TERMINAL: - case EL_SP_TERMINAL_ACTIVE: - case EL_EXTRA_TIME: - case EL_SHIELD_NORMAL: - case EL_SHIELD_DEADLY: - if (IS_ANIMATED(graphic)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - break; - - case EL_DYNAMITE_ACTIVE: - case EL_EM_DYNAMITE_ACTIVE: - case EL_DYNABOMB_PLAYER_1_ACTIVE: - case EL_DYNABOMB_PLAYER_2_ACTIVE: - case EL_DYNABOMB_PLAYER_3_ACTIVE: - case EL_DYNABOMB_PLAYER_4_ACTIVE: - case EL_SP_DISK_RED_ACTIVE: - CheckDynamite(x, y); - break; - - case EL_AMOEBA_GROWING: - AmoebeWaechst(x, y); - break; - - case EL_AMOEBA_SHRINKING: - AmoebaDisappearing(x, y); - break; - -#if !USE_NEW_AMOEBA_CODE - case EL_AMOEBA_WET: - case EL_AMOEBA_DRY: - case EL_AMOEBA_FULL: - case EL_BD_AMOEBA: - case EL_EMC_DRIPPER: - AmoebeAbleger(x, y); - break; -#endif + GfxFrame[x][y]++; - case EL_GAME_OF_LIFE: - case EL_BIOMAZE: - Life(x, y); - break; + /* reset finished pushing action (not done in ContinueMoving() to allow + continuous pushing animation for elements with zero push delay) */ + if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y)) + { + ResetGfxAnimation(x, y); + TEST_DrawLevelField(x, y); + } - case EL_EXIT_CLOSED: - CheckExit(x, y); - break; +#if DEBUG + if (IS_BLOCKED(x, y)) + { + int oldx, oldy; - case EL_EM_EXIT_CLOSED: - CheckExitEM(x, y); - break; + Blocked2Moving(x, y, &oldx, &oldy); + if (!IS_MOVING(oldx, oldy)) + { + printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n"); + printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y); + printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy); + printf("GameActions(): This should never happen!\n"); + } + } +#endif + } - case EL_STEEL_EXIT_CLOSED: - CheckExitSteel(x, y); - break; + SCAN_PLAYFIELD(x, y) + { + element = Feld[x][y]; + graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + last_gfx_frame = GfxFrame[x][y]; - case EL_EM_STEEL_EXIT_CLOSED: - CheckExitSteelEM(x, y); - break; + ResetGfxFrame(x, y); - case EL_SP_EXIT_CLOSED: - CheckExitSP(x, y); - break; + if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y]) + DrawLevelGraphicAnimation(x, y, graphic); - case EL_EXPANDABLE_WALL_GROWING: - case EL_EXPANDABLE_STEELWALL_GROWING: - MauerWaechst(x, y); - break; + if (ANIM_MODE(graphic) == ANIM_RANDOM && + IS_NEXT_FRAME(GfxFrame[x][y], graphic)) + ResetRandomAnimationValue(x, y); - case EL_EXPANDABLE_WALL: - case EL_EXPANDABLE_WALL_HORIZONTAL: - case EL_EXPANDABLE_WALL_VERTICAL: - case EL_EXPANDABLE_WALL_ANY: - case EL_BD_EXPANDABLE_WALL: - MauerAbleger(x, y); - break; + SetRandomAnimationValue(x, y); - case EL_EXPANDABLE_STEELWALL_HORIZONTAL: - case EL_EXPANDABLE_STEELWALL_VERTICAL: - case EL_EXPANDABLE_STEELWALL_ANY: - MauerAblegerStahl(x, y); - break; + PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]); - case EL_FLAMES: - CheckForDragon(x, y); - break; + if (IS_INACTIVE(element)) + { + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - case EL_EXPLOSION: - break; + continue; + } - case EL_ELEMENT_SNAPPING: - case EL_DIAGONAL_SHRINKING: - case EL_DIAGONAL_GROWING: - { - graphic = - el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]); + /* this may take place after moving, so 'element' may have changed */ + if (IS_CHANGING(x, y) && + (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y])) + { + int page = element_info[element].event_page_nr[CE_DELAY]; - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - break; - } + HandleElementChange(x, y, page); - default: - if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - break; - } + element = Feld[x][y]; + graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); } -#else // --------------------------------------------------------------------- - if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element))) { StartMoving(x, y); @@ -12122,10 +11868,14 @@ void GameActions_RND() DrawLevelGraphicAnimationIfNeeded(x, y, graphic); if (IS_GEM(element) || element == EL_SP_INFOTRON) - DrawTwinkleOnField(x, y); + TEST_DrawTwinkleOnField(x, y); + } + else if (element == EL_ACID) + { + if (!Stop[x][y]) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); } - else if ((element == EL_ACID || - element == EL_EXIT_OPEN || + else if ((element == EL_EXIT_OPEN || element == EL_EM_EXIT_OPEN || element == EL_SP_EXIT_OPEN || element == EL_STEEL_EXIT_OPEN || @@ -12191,8 +11941,6 @@ void GameActions_RND() else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y)) DrawLevelGraphicAnimationIfNeeded(x, y, graphic); -#endif // --------------------------------------------------------------------- - if (IS_BELT_ACTIVE(element)) PlayLevelSoundAction(x, y, ACTION_ACTIVE); @@ -12218,15 +11966,11 @@ void GameActions_RND() } } -#if 0 - debug_print_timestamp(0, "- time for MAIN loop: -->"); -#endif - #if USE_NEW_AMOEBA_CODE /* new experimental amoeba growth stuff */ if (!(FrameCounter % 8)) { - static unsigned long random = 1684108901; + static unsigned int random = 1684108901; for (i = 0; i < level.amoeba_speed * 28 / 8; i++) { @@ -12254,27 +11998,22 @@ void GameActions_RND() } #endif -#if 0 - if (game.explosions_delayed) -#endif - { - game.explosions_delayed = FALSE; - - SCAN_PLAYFIELD(x, y) - { - element = Feld[x][y]; + game.explosions_delayed = FALSE; - if (ExplodeField[x][y]) - Explode(x, y, EX_PHASE_START, ExplodeField[x][y]); - else if (element == EL_EXPLOSION) - Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL); + SCAN_PLAYFIELD(x, y) + { + element = Feld[x][y]; - ExplodeField[x][y] = EX_TYPE_NONE; - } + if (ExplodeField[x][y]) + Explode(x, y, EX_PHASE_START, ExplodeField[x][y]); + else if (element == EL_EXPLOSION) + Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL); - game.explosions_delayed = TRUE; + ExplodeField[x][y] = EX_TYPE_NONE; } + game.explosions_delayed = TRUE; + if (game.magic_wall_active) { if (!(game.magic_wall_time_left % 4)) @@ -12307,19 +12046,19 @@ void GameActions_RND() element == EL_MAGIC_WALL_FULL) { Feld[x][y] = EL_MAGIC_WALL_DEAD; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_BD_MAGIC_WALL_ACTIVE || element == EL_BD_MAGIC_WALL_FULL) { Feld[x][y] = EL_BD_MAGIC_WALL_DEAD; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_DC_MAGIC_WALL_ACTIVE || element == EL_DC_MAGIC_WALL_FULL) { Feld[x][y] = EL_DC_MAGIC_WALL_DEAD; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } } @@ -12373,31 +12112,33 @@ void GameActions_RND() } } - CheckLevelTime(); - - DrawAllPlayers(); - PlayAllPlayersSound(); - - if (options.debug) /* calculate frames per second */ +#if USE_DELAYED_GFX_REDRAW + SCAN_PLAYFIELD(x, y) { - static unsigned long fps_counter = 0; - static int fps_frames = 0; - unsigned long fps_delay_ms = Counter() - fps_counter; + if (GfxRedraw[x][y] != GFX_REDRAW_NONE) + { + /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES + !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */ - fps_frames++; + if (GfxRedraw[x][y] & GFX_REDRAW_TILE) + DrawLevelField(x, y); - if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */ - { - global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms; + if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED) + DrawLevelFieldCrumbled(x, y); - fps_frames = 0; - fps_counter = Counter(); + if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS) + DrawLevelFieldCrumbledNeighbours(x, y); + + if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED) + DrawTwinkleOnField(x, y); } - redraw_mask |= REDRAW_FPS; + GfxRedraw[x][y] = GFX_REDRAW_NONE; } +#endif - AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */ + DrawAllPlayers(); + PlayAllPlayersSound(); if (local_player->show_envelope != 0 && local_player->MovPos == 0) { @@ -12406,11 +12147,6 @@ void GameActions_RND() local_player->show_envelope = 0; } -#if 0 - debug_print_timestamp(0, "stop main loop profiling "); - printf("----------------------------------------------------------\n"); -#endif - /* use random number generator in every frame to make it less predictable */ if (game.engine_version >= VERSION_IDENT(3,1,1,0)) RND(1); @@ -12457,76 +12193,16 @@ static boolean AllPlayersInVisibleScreen() void ScrollLevel(int dx, int dy) { -#if 1 - static Bitmap *bitmap_db_field2 = NULL; - int softscroll_offset = (setup.soft_scrolling ? TILEX : 0); + int scroll_offset = 2 * TILEX_VAR; int x, y; -#else - int i, x, y; -#endif - -#if 0 - /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */ - /* only horizontal XOR vertical scroll direction allowed */ - if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0)) - return; -#endif - -#if 1 - if (bitmap_db_field2 == NULL) - bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH); - - /* needed when blitting directly to same bitmap -- should not be needed with - recent SDL libraries, but apparently does not work in 1.2.11 directly */ - BlitBitmap(drawto_field, bitmap_db_field2, - FX + TILEX * (dx == -1) - softscroll_offset, - FY + TILEY * (dy == -1) - softscroll_offset, - SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset, - SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset, - FX + TILEX * (dx == 1) - softscroll_offset, - FY + TILEY * (dy == 1) - softscroll_offset); - BlitBitmap(bitmap_db_field2, drawto_field, - FX + TILEX * (dx == 1) - softscroll_offset, - FY + TILEY * (dy == 1) - softscroll_offset, - SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset, - SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset, - FX + TILEX * (dx == 1) - softscroll_offset, - FY + TILEY * (dy == 1) - softscroll_offset); - -#else - -#if 1 - /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */ - int xsize = (BX2 - BX1 + 1); - int ysize = (BY2 - BY1 + 1); - int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2)); - int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1)); - int step = (start < end ? +1 : -1); - - for (i = start; i != end; i += step) - { - BlitBitmap(drawto_field, drawto_field, - FX + TILEX * (dx != 0 ? i + step : 0), - FY + TILEY * (dy != 0 ? i + step : 0), - TILEX * (dx != 0 ? 1 : xsize), - TILEY * (dy != 0 ? 1 : ysize), - FX + TILEX * (dx != 0 ? i : 0), - FY + TILEY * (dy != 0 ? i : 0)); - } - -#else - - int softscroll_offset = (setup.soft_scrolling ? TILEX : 0); BlitBitmap(drawto_field, drawto_field, - FX + TILEX * (dx == -1) - softscroll_offset, - FY + TILEY * (dy == -1) - softscroll_offset, - SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset, - SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset, - FX + TILEX * (dx == 1) - softscroll_offset, - FY + TILEY * (dy == 1) - softscroll_offset); -#endif -#endif + FX + TILEX_VAR * (dx == -1) - scroll_offset, + FY + TILEY_VAR * (dy == -1) - scroll_offset, + SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset, + SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset, + FX + TILEX_VAR * (dx == 1) - scroll_offset, + FY + TILEY_VAR * (dy == 1) - scroll_offset); if (dx != 0) { @@ -12589,11 +12265,7 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir) static void CheckGravityMovement(struct PlayerInfo *player) { -#if USE_PLAYER_GRAVITY if (player->gravity && !player->programmed_action) -#else - if (game.gravity && !player->programmed_action) -#endif { int move_dir_horizontal = player->effective_action & MV_HORIZONTAL; int move_dir_vertical = player->effective_action & MV_VERTICAL; @@ -12615,11 +12287,7 @@ static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player) { return CheckGravityMovement(player); -#if USE_PLAYER_GRAVITY if (player->gravity && !player->programmed_action) -#else - if (game.gravity && !player->programmed_action) -#endif { int jx = player->jx, jy = player->jy; boolean field_under_player_is_free = @@ -12646,9 +12314,6 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, { int jx = player->jx, jy = player->jy; int new_jx = jx + dx, new_jy = jy + dy; -#if !USE_FIXED_DONT_RUN_INTO - int element; -#endif int can_move; boolean player_can_move = !player->cannot_move; @@ -12675,36 +12340,9 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, } } -#if 1 if (!options.network && game.centered_player_nr == -1 && !AllPlayersInSight(player, new_jx, new_jy)) return MP_NO_ACTION; -#else - if (!options.network && !AllPlayersInSight(player, new_jx, new_jy)) - return MP_NO_ACTION; -#endif - -#if !USE_FIXED_DONT_RUN_INTO - element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy); - - /* (moved to DigField()) */ - if (player_can_move && DONT_RUN_INTO(element)) - { - if (element == EL_ACID && dx == 0 && dy == 1) - { - SplashAcid(new_jx, new_jy); - Feld[jx][jy] = EL_PLAYER_1; - InitMovingField(jx, jy, MV_DOWN); - Store[jx][jy] = EL_ACID; - ContinueMoving(jx, jy); - BuryPlayer(player); - } - else - TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); - - return MP_MOVING; - } -#endif can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG); if (can_move != MP_MOVING) @@ -12734,9 +12372,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, PlayerVisit[jx][jy] = FrameCounter; -#if USE_UFAST_PLAYER_EXIT_BUGFIX player->is_moving = TRUE; -#endif #if 1 /* should better be called in MovePlayer(), but this breaks some tapes */ @@ -12788,7 +12424,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) int original_move_delay_value = player->move_delay_value; #if DEBUG - printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n", + printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n", tape.counter); #endif @@ -12803,7 +12439,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) AdvanceFrameAndPlayerCounters(player->index_nr); DrawAllPlayers(); - BackToFront(); + BackToFront_WithFrameDelay(0); } player->move_delay_value = original_move_delay_value; @@ -12822,7 +12458,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) moved |= MovePlayerOneStep(player, 0, dy, dx, dy); } -#if USE_FIXED_BORDER_RUNNING_GFX if (!moved && !player->is_active) { player->is_moving = FALSE; @@ -12831,19 +12466,13 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->is_snapping = FALSE; player->is_pushing = FALSE; } -#endif jx = player->jx; jy = player->jy; -#if 1 if (moved & MP_MOVING && !ScreenMovPos && (player->index_nr == game.centered_player_nr || game.centered_player_nr == -1)) -#else - if (moved & MP_MOVING && !ScreenMovPos && - (player == local_player || !options.network)) -#endif { int old_scroll_x = scroll_x, old_scroll_y = scroll_y; int offset = game.scroll_delay_value; @@ -12898,7 +12527,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) if (scroll_x != old_scroll_x || scroll_y != old_scroll_y) { -#if 1 if (!options.network && game.centered_player_nr == -1 && !AllPlayersInVisibleScreen()) { @@ -12906,14 +12534,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) scroll_y = old_scroll_y; } else -#else - if (!options.network && !AllPlayersInVisibleScreen()) - { - scroll_x = old_scroll_x; - scroll_y = old_scroll_y; - } - else -#endif { ScrollScreen(player, SCROLL_INIT); ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y); @@ -12930,7 +12550,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) else if (old_jx == jx && old_jy != jy) player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP); - DrawLevelField(jx, jy); /* for "crumbled sand" */ + TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */ player->last_move_dir = player->MovDir; player->is_moving = TRUE; @@ -12981,16 +12601,11 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) int last_jx = player->last_jx, last_jy = player->last_jy; int move_stepsize = TILEX / player->move_delay_value; -#if USE_NEW_PLAYER_SPEED if (!player->active) return; if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */ return; -#else - if (!player->active || player->MovPos == 0) - return; -#endif if (mode == SCROLL_INIT) { @@ -13009,13 +12624,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) last_field_block_delay += player->move_delay_value; /* when blocking enabled, prevent moving up despite gravity */ -#if USE_PLAYER_GRAVITY if (player->gravity && player->MovDir == MV_UP) block_delay_adjustment = -1; -#else - if (game.gravity && player->MovDir == MV_UP) - block_delay_adjustment = -1; -#endif } /* add block delay adjustment (also possible when not blocking) */ @@ -13025,17 +12635,12 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) MovDelay[last_jx][last_jy] = last_field_block_delay + 1; } -#if USE_NEW_PLAYER_SPEED if (player->MovPos != 0) /* player has not yet reached destination */ return; -#else - return; -#endif } else if (!FrameReached(&player->actual_frame_counter, 1)) return; -#if USE_NEW_PLAYER_SPEED if (player->MovPos != 0) { player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize; @@ -13045,14 +12650,6 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (player->MovPos == 0) CheckGravityMovement(player); } -#else - player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize; - player->GfxPos = move_stepsize * (player->MovPos / move_stepsize); - - /* before DrawPlayer() to draw correct player graphic for this case */ - if (player->MovPos == 0) - CheckGravityMovement(player); -#endif if (player->MovPos == 0) /* player reached destination field */ { @@ -13075,8 +12672,10 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (Feld[jx][jy] == EL_EXIT_OPEN || Feld[jx][jy] == EL_EM_EXIT_OPEN || + Feld[jx][jy] == EL_EM_EXIT_OPENING || Feld[jx][jy] == EL_STEEL_EXIT_OPEN || Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN || + Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING || Feld[jx][jy] == EL_SP_EXIT_OPEN || Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */ { @@ -13115,7 +12714,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) CE_PLAYER_ENTERS_X, player->index_bit, enter_side); - CheckTriggeredElementChangeBySide(jx, jy, player->element_nr, + CheckTriggeredElementChangeBySide(jx, jy, player->initial_element, CE_MOVE_OF_X, move_direction); } @@ -13146,40 +12745,34 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (TimeLeft <= 10 && setup.time_limit) PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); -#if 1 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif if (!TimeLeft && setup.time_limit) for (i = 0; i < MAX_PLAYERS; i++) KillPlayer(&stored_player[i]); } -#if 1 - else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ + else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */ { game_panel_controls[GAME_PANEL_TIME].value = TimePlayed; DisplayGameControlValues(); } -#else - else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ - DrawGameValue_Time(TimePlayed); -#endif } if (tape.single_step && tape.recording && !tape.pausing && !player->programmed_action) TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); + + if (!player->programmed_action) + CheckSaveEngineSnapshot(player); } } void ScrollScreen(struct PlayerInfo *player, int mode) { - static unsigned long screen_frame_counter = 0; + static unsigned int screen_frame_counter = 0; if (mode == SCROLL_INIT) { @@ -13243,7 +12836,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y) if (!IN_LEV_FIELD(xx, yy)) continue; - if (IS_PLAYER(x, y)) + if (IS_PLAYER(x, y)) /* player found at center element */ { struct PlayerInfo *player = PLAYERINFO(x, y); @@ -13261,8 +12854,19 @@ void TestIfPlayerTouchesCustomElement(int x, int y) CheckTriggeredElementChangeByPlayer(xx, yy, border_element, CE_PLAYER_TOUCHES_X, player->index_bit, border_side); + + { + /* use player element that is initially defined in the level playfield, + not the player element that corresponds to the runtime player number + (example: a level that contains EL_PLAYER_3 as the only player would + incorrectly give EL_PLAYER_1 for "player->element_nr") */ + int player_element = PLAYERINFO(x, y)->initial_element; + + CheckElementChangeBySide(xx, yy, border_element, player_element, + CE_TOUCHING_X, border_side); + } } - else if (IS_PLAYER(xx, yy)) + else if (IS_PLAYER(xx, yy)) /* player found at border element */ { struct PlayerInfo *player = PLAYERINFO(xx, yy); @@ -13277,13 +12881,23 @@ void TestIfPlayerTouchesCustomElement(int x, int y) CheckTriggeredElementChangeByPlayer(x, y, center_element, CE_PLAYER_TOUCHES_X, player->index_bit, center_side); + + { + /* use player element that is initially defined in the level playfield, + not the player element that corresponds to the runtime player number + (example: a level that contains EL_PLAYER_3 as the only player would + incorrectly give EL_PLAYER_1 for "player->element_nr") */ + int player_element = PLAYERINFO(xx, yy)->initial_element; + + CheckElementChangeBySide(x, y, center_element, player_element, + CE_TOUCHING_X, center_side); + } + break; } } } -#if USE_ELEMENT_TOUCHING_BUGFIX - void TestIfElementTouchesCustomElement(int x, int y) { static int xy[4][2] = @@ -13341,81 +12955,27 @@ void TestIfElementTouchesCustomElement(int x, int y) int xx = x + xy[i][0]; int yy = y + xy[i][1]; int center_side = trigger_sides[i][0]; - int border_element = border_element_old[i]; - - if (border_element == -1) - continue; - - /* check for change of border element */ - CheckElementChangeBySide(xx, yy, border_element, center_element, - CE_TOUCHING_X, center_side); - } - - for (i = 0; i < NUM_DIRECTIONS; i++) - { - int border_side = trigger_sides[i][1]; - int border_element = border_element_old[i]; - - if (border_element == -1) - continue; - - /* check for change of center element (but change it only once) */ - if (!change_center_element) - change_center_element = - CheckElementChangeBySide(x, y, center_element, border_element, - CE_TOUCHING_X, border_side); - } -} - -#else - -void TestIfElementTouchesCustomElement_OLD(int x, int y) -{ - static int xy[4][2] = - { - { 0, -1 }, - { -1, 0 }, - { +1, 0 }, - { 0, +1 } - }; - static int trigger_sides[4][2] = - { - /* center side border side */ - { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */ - { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */ - { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */ - { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */ - }; - static int touch_dir[4] = - { - MV_LEFT | MV_RIGHT, - MV_UP | MV_DOWN, - MV_UP | MV_DOWN, - MV_LEFT | MV_RIGHT - }; - boolean change_center_element = FALSE; - int center_element = Feld[x][y]; /* should always be non-moving! */ - int i; - - for (i = 0; i < NUM_DIRECTIONS; i++) - { - int xx = x + xy[i][0]; - int yy = y + xy[i][1]; - int center_side = trigger_sides[i][0]; - int border_side = trigger_sides[i][1]; - int border_element; + int border_element = border_element_old[i]; - if (!IN_LEV_FIELD(xx, yy)) + if (border_element == -1) continue; - if (game.engine_version < VERSION_IDENT(3,0,7,0)) - border_element = Feld[xx][yy]; /* may be moving! */ - else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy)) - border_element = Feld[xx][yy]; - else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */ - border_element = MovingOrBlocked2Element(xx, yy); - else - continue; /* center and border element do not touch */ + /* check for change of border element */ + CheckElementChangeBySide(xx, yy, border_element, center_element, + CE_TOUCHING_X, center_side); + + /* (center element cannot be player, so we dont have to check this here) */ + } + + for (i = 0; i < NUM_DIRECTIONS; i++) + { + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; + int border_side = trigger_sides[i][1]; + int border_element = border_element_old[i]; + + if (border_element == -1) + continue; /* check for change of center element (but change it only once) */ if (!change_center_element) @@ -13423,14 +12983,20 @@ void TestIfElementTouchesCustomElement_OLD(int x, int y) CheckElementChangeBySide(x, y, center_element, border_element, CE_TOUCHING_X, border_side); - /* check for change of border element */ - CheckElementChangeBySide(xx, yy, border_element, center_element, - CE_TOUCHING_X, center_side); + if (IS_PLAYER(xx, yy)) + { + /* use player element that is initially defined in the level playfield, + not the player element that corresponds to the runtime player number + (example: a level that contains EL_PLAYER_3 as the only player would + incorrectly give EL_PLAYER_1 for "player->element_nr") */ + int player_element = PLAYERINFO(xx, yy)->initial_element; + + CheckElementChangeBySide(x, y, center_element, player_element, + CE_TOUCHING_X, border_side); + } } } -#endif - void TestIfElementHitsCustomElement(int x, int y, int direction) { int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); @@ -13461,11 +13027,23 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) CheckElementChangeBySide(x, y, hitting_element, touched_element, CE_HITTING_X, touched_side); - CheckElementChangeBySide(hitx, hity, touched_element, - hitting_element, CE_HIT_BY_X, hitting_side); + CheckElementChangeBySide(hitx, hity, touched_element, hitting_element, + CE_HIT_BY_X, hitting_side); CheckElementChangeBySide(hitx, hity, touched_element, hitting_element, CE_HIT_BY_SOMETHING, opposite_direction); + + if (IS_PLAYER(hitx, hity)) + { + /* use player element that is initially defined in the level playfield, + not the player element that corresponds to the runtime player number + (example: a level that contains EL_PLAYER_3 as the only player would + incorrectly give EL_PLAYER_1 for "player->element_nr") */ + int player_element = PLAYERINFO(hitx, hity)->initial_element; + + CheckElementChangeBySide(x, y, hitting_element, player_element, + CE_HITTING_X, touched_side); + } } } @@ -13474,69 +13052,6 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) CE_HITTING_SOMETHING, direction); } -#if 0 -void TestIfElementSmashesCustomElement(int x, int y, int direction) -{ - int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); - int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); - int hitx = x + dx, hity = y + dy; - int hitting_element = Feld[x][y]; - int touched_element; -#if 0 - boolean object_hit = (IN_LEV_FIELD(hitx, hity) && - !IS_FREE(hitx, hity) && - (!IS_MOVING(hitx, hity) || - MovDir[hitx][hity] != direction || - ABS(MovPos[hitx][hity]) <= TILEY / 2)); -#endif - - if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity)) - return; - -#if 0 - if (IN_LEV_FIELD(hitx, hity) && !object_hit) - return; -#endif - - touched_element = (IN_LEV_FIELD(hitx, hity) ? - MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL); - - CheckElementChangeBySide(x, y, hitting_element, touched_element, - EP_CAN_SMASH_EVERYTHING, direction); - - if (IN_LEV_FIELD(hitx, hity)) - { - int opposite_direction = MV_DIR_OPPOSITE(direction); - int hitting_side = direction; - int touched_side = opposite_direction; -#if 0 - int touched_element = MovingOrBlocked2Element(hitx, hity); -#endif -#if 1 - boolean object_hit = (!IS_MOVING(hitx, hity) || - MovDir[hitx][hity] != direction || - ABS(MovPos[hitx][hity]) <= TILEY / 2); - - object_hit = TRUE; -#endif - - if (object_hit) - { - int i; - - CheckElementChangeBySide(hitx, hity, touched_element, hitting_element, - CE_SMASHED_BY_SOMETHING, opposite_direction); - - CheckElementChangeBySide(x, y, hitting_element, touched_element, - CE_OTHER_IS_SMASHING, touched_side); - - CheckElementChangeBySide(hitx, hity, touched_element, hitting_element, - CE_OTHER_GETS_SMASHED, hitting_side); - } - } -} -#endif - void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) { int i, kill_x = -1, kill_y = -1; @@ -13638,6 +13153,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) test_x = bad_x + test_xy[i][0]; test_y = bad_y + test_xy[i][1]; + if (!IN_LEV_FIELD(test_x, test_y)) continue; @@ -13668,12 +13184,14 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) kill_x = test_x; kill_y = test_y; + break; } else if (test_element == EL_PENGUIN) { kill_x = test_x; kill_y = test_y; + break; } } @@ -13696,6 +13214,63 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) } } +void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir) +{ + int bad_element = Feld[bad_x][bad_y]; + int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0); + int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0); + int test_x = bad_x + dx, test_y = bad_y + dy; + int test_move_dir, test_element; + int kill_x = -1, kill_y = -1; + + if (!IN_LEV_FIELD(test_x, test_y)) + return; + + test_move_dir = + (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE); + + test_element = Feld[test_x][test_y]; + + if (test_move_dir != bad_move_dir) + { + /* good thing can be player or penguin that does not move away */ + if (IS_PLAYER(test_x, test_y)) + { + struct PlayerInfo *player = PLAYERINFO(test_x, test_y); + + /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the + player as being hit when he is moving towards the bad thing, because + the "get hit by" condition would be lost after the player stops) */ + if (player->MovPos != 0 && player->MovDir == bad_move_dir) + return; /* player moves away from bad thing */ + + kill_x = test_x; + kill_y = test_y; + } + else if (test_element == EL_PENGUIN) + { + kill_x = test_x; + kill_y = test_y; + } + } + + if (kill_x != -1 || kill_y != -1) + { + if (IS_PLAYER(kill_x, kill_y)) + { + struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y); + + if (player->shield_deadly_time_left > 0 && + !IS_INDESTRUCTIBLE(bad_element)) + Bang(bad_x, bad_y); + else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y)) + KillPlayer(player); + } + else + Bang(kill_x, kill_y); + } +} + void TestIfPlayerTouchesBadThing(int x, int y) { TestIfGoodThingHitsBadThing(x, y, MV_NONE); @@ -13767,6 +13342,11 @@ void KillPlayer(struct PlayerInfo *player) if (!player->active) return; +#if 0 + printf("::: 0: killed == %d, active == %d, reanimated == %d\n", + player->killed, player->active, player->reanimated); +#endif + /* the following code was introduced to prevent an infinite loop when calling -> Bang() -> CheckTriggeredElementChangeExt() @@ -13791,8 +13371,22 @@ void KillPlayer(struct PlayerInfo *player) player->shield_normal_time_left = 0; player->shield_deadly_time_left = 0; +#if 0 + printf("::: 1: killed == %d, active == %d, reanimated == %d\n", + player->killed, player->active, player->reanimated); +#endif + Bang(jx, jy); - BuryPlayer(player); + +#if 0 + printf("::: 2: killed == %d, active == %d, reanimated == %d\n", + player->killed, player->active, player->reanimated); +#endif + + if (player->reanimated) /* killed player may have been reanimated */ + player->killed = player->reanimated = FALSE; + else + BuryPlayer(player); } static void KillPlayerUnlessEnemyProtected(int x, int y) @@ -13833,7 +13427,7 @@ void RemovePlayer(struct PlayerInfo *player) StorePlayer[jx][jy] = 0; if (player->is_moving) - DrawLevelField(player->last_jx, player->last_jy); + TEST_DrawLevelField(player->last_jx, player->last_jy); for (i = 0; i < MAX_PLAYERS; i++) if (stored_player[i].active) @@ -13846,7 +13440,6 @@ void RemovePlayer(struct PlayerInfo *player) ExitY = ZY = jy; } -#if USE_NEW_SNAP_DELAY static void setFieldForSnapping(int x, int y, int element, int direction) { struct ElementInfo *ei = &element_info[element]; @@ -13865,7 +13458,6 @@ static void setFieldForSnapping(int x, int y, int element, int direction) GfxDir[x][y] = direction; GfxFrame[x][y] = -1; } -#endif /* ============================================================================= @@ -13904,9 +13496,9 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player, ============================================================================= */ -int DigField(struct PlayerInfo *player, - int oldx, int oldy, int x, int y, - int real_dx, int real_dy, int mode) +static int DigField(struct PlayerInfo *player, + int oldx, int oldy, int x, int y, + int real_dx, int real_dy, int mode) { boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG); boolean player_was_pushing = player->is_pushing; @@ -13922,11 +13514,7 @@ int DigField(struct PlayerInfo *player, int opposite_direction = MV_DIR_OPPOSITE(move_direction); int dig_side = MV_DIR_OPPOSITE(move_direction); int old_element = Feld[jx][jy]; -#if USE_FIXED_DONT_RUN_INTO int element = MovingOrBlocked2ElementIfNotLeaving(x, y); -#else - int element; -#endif int collect_count; if (is_player) /* function can also be called by EL_PENGUIN */ @@ -13949,11 +13537,6 @@ int DigField(struct PlayerInfo *player, } } -#if !USE_FIXED_DONT_RUN_INTO - if (IS_MOVING(x, y) || IS_PLAYER(x, y)) - return MP_NO_ACTION; -#endif - if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)) old_element = Back[jx][jy]; @@ -13968,7 +13551,6 @@ int DigField(struct PlayerInfo *player, if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction)) return MP_NO_ACTION; /* field has no opening in this direction */ -#if USE_FIXED_DONT_RUN_INTO if (player_can_move && element == EL_ACID && move_direction == MV_DOWN) { SplashAcid(x, y); @@ -13981,25 +13563,16 @@ int DigField(struct PlayerInfo *player, return MP_DONT_RUN_INTO; } -#endif -#if USE_FIXED_DONT_RUN_INTO if (player_can_move && DONT_RUN_INTO(element)) { TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); return MP_DONT_RUN_INTO; } -#endif -#if USE_FIXED_DONT_RUN_INTO if (IS_MOVING(x, y) || IS_PLAYER(x, y)) return MP_NO_ACTION; -#endif - -#if !USE_FIXED_DONT_RUN_INTO - element = Feld[x][y]; -#endif collect_count = element_info[element].collect_count_initial; @@ -14026,17 +13599,10 @@ int DigField(struct PlayerInfo *player, return MP_NO_ACTION; } -#if USE_PLAYER_GRAVITY if (player->gravity && is_player && !player->is_auto_moving && canFallDown(player) && move_direction != MV_DOWN && !canMoveToValidFieldWithGravity(jx, jy, move_direction)) return MP_NO_ACTION; /* player cannot walk here due to gravity */ -#else - if (game.gravity && is_player && !player->is_auto_moving && - canFallDown(player) && move_direction != MV_DOWN && - !canMoveToValidFieldWithGravity(jx, jy, move_direction)) - return MP_NO_ACTION; /* player cannot walk here due to gravity */ -#endif if (player_can_move && IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction)) @@ -14061,8 +13627,10 @@ int DigField(struct PlayerInfo *player, } else if (element == EL_EXIT_OPEN || element == EL_EM_EXIT_OPEN || + element == EL_EM_EXIT_OPENING || element == EL_STEEL_EXIT_OPEN || element == EL_EM_STEEL_EXIT_OPEN || + element == EL_EM_STEEL_EXIT_OPENING || element == EL_SP_EXIT_OPEN || element == EL_SP_EXIT_OPENING) { @@ -14133,29 +13701,17 @@ int DigField(struct PlayerInfo *player, element == EL_SP_GRAVITY_PORT_RIGHT || element == EL_SP_GRAVITY_PORT_UP || element == EL_SP_GRAVITY_PORT_DOWN) -#if USE_PLAYER_GRAVITY player->gravity = !player->gravity; -#else - game.gravity = !game.gravity; -#endif else if (element == EL_SP_GRAVITY_ON_PORT_LEFT || element == EL_SP_GRAVITY_ON_PORT_RIGHT || element == EL_SP_GRAVITY_ON_PORT_UP || element == EL_SP_GRAVITY_ON_PORT_DOWN) -#if USE_PLAYER_GRAVITY player->gravity = TRUE; -#else - game.gravity = TRUE; -#endif else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT || element == EL_SP_GRAVITY_OFF_PORT_RIGHT || element == EL_SP_GRAVITY_OFF_PORT_UP || element == EL_SP_GRAVITY_OFF_PORT_DOWN) -#if USE_PLAYER_GRAVITY player->gravity = FALSE; -#else - game.gravity = FALSE; -#endif } /* automatically move to the next field with double speed */ @@ -14187,14 +13743,10 @@ int DigField(struct PlayerInfo *player, if (mode == DF_SNAP) { -#if USE_NEW_SNAP_DELAY if (level.block_snap_field) setFieldForSnapping(x, y, element, move_direction); else TestIfElementTouchesCustomElement(x, y); /* for empty space */ -#else - TestIfElementTouchesCustomElement(x, y); /* for empty space */ -#endif CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, player->index_bit, dig_side); @@ -14218,13 +13770,9 @@ int DigField(struct PlayerInfo *player, { TimeLeft += level.extra_time; -#if 1 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif } else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY) { @@ -14303,13 +13851,11 @@ int DigField(struct PlayerInfo *player, if (local_player->gems_still_needed < 0) local_player->gems_still_needed = 0; -#if 1 + game.snapshot.collected_item = TRUE; + game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed; DisplayGameControlValues(); -#else - DrawGameValue_Emeralds(local_player->gems_still_needed); -#endif } RaiseScoreElement(element); @@ -14321,14 +13867,10 @@ int DigField(struct PlayerInfo *player, if (mode == DF_SNAP) { -#if USE_NEW_SNAP_DELAY if (level.block_snap_field) setFieldForSnapping(x, y, element, move_direction); else TestIfElementTouchesCustomElement(x, y); /* for empty space */ -#else - TestIfElementTouchesCustomElement(x, y); /* for empty space */ -#endif CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, player->index_bit, dig_side); @@ -14385,8 +13927,10 @@ int DigField(struct PlayerInfo *player, if (!(IN_LEV_FIELD(nextx, nexty) && (IS_FREE(nextx, nexty) || - (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY && - IS_SB_ELEMENT(element))))) + (IS_SB_ELEMENT(element) && + Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) || + (IS_CUSTOM_ELEMENT(element) && + CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))))) return MP_NO_ACTION; if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) @@ -14406,6 +13950,13 @@ int DigField(struct PlayerInfo *player, return MP_NO_ACTION; } + if (IS_CUSTOM_ELEMENT(element) && + CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)) + { + if (!DigFieldByCE(nextx, nexty, element)) + return MP_NO_ACTION; + } + if (IS_SB_ELEMENT(element)) { if (element == EL_SOKOBAN_FIELD_FULL) @@ -14432,7 +13983,7 @@ int DigField(struct PlayerInfo *player, ACTION_FILLING); if (local_player->sokobanfields_still_needed == 0 && - game.emulation == EMU_SOKOBAN) + (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban)) { PlayerWins(player); @@ -14491,7 +14042,7 @@ int DigField(struct PlayerInfo *player, game.robot_wheel_active = TRUE; - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_SP_TERMINAL) { @@ -14500,9 +14051,16 @@ int DigField(struct PlayerInfo *player, SCAN_PLAYFIELD(xx, yy) { if (Feld[xx][yy] == EL_SP_DISK_YELLOW) + { Bang(xx, yy); + } else if (Feld[xx][yy] == EL_SP_TERMINAL) + { Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE; + + ResetGfxAnimation(xx, yy); + TEST_DrawLevelField(xx, yy); + } } } else if (IS_BELT_SWITCH(element)) @@ -14546,7 +14104,7 @@ int DigField(struct PlayerInfo *player, local_player->lights_still_needed--; ResetGfxAnimation(x, y); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_TIME_ORB_FULL) { @@ -14555,18 +14113,15 @@ int DigField(struct PlayerInfo *player, if (level.time > 0 || level.use_time_orb_bug) { TimeLeft += level.time_orb_time; + game.no_time_limit = FALSE; -#if 1 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif } ResetGfxAnimation(x, y); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } else if (element == EL_EMC_MAGIC_BALL_SWITCH || element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE) @@ -14648,7 +14203,55 @@ int DigField(struct PlayerInfo *player, return MP_MOVING; } -boolean SnapField(struct PlayerInfo *player, int dx, int dy) +static boolean DigFieldByCE(int x, int y, int digging_element) +{ + int element = Feld[x][y]; + + if (!IS_FREE(x, y)) + { + int action = (IS_DIGGABLE(element) ? ACTION_DIGGING : + IS_COLLECTIBLE(element) ? ACTION_COLLECTING : + ACTION_BREAKING); + + /* no element can dig solid indestructible elements */ + if (IS_INDESTRUCTIBLE(element) && + !IS_DIGGABLE(element) && + !IS_COLLECTIBLE(element)) + return FALSE; + + if (AmoebaNr[x][y] && + (element == EL_AMOEBA_FULL || + element == EL_BD_AMOEBA || + element == EL_AMOEBA_GROWING)) + { + AmoebaCnt[AmoebaNr[x][y]]--; + AmoebaCnt2[AmoebaNr[x][y]]--; + } + + if (IS_MOVING(x, y)) + RemoveMovingField(x, y); + else + { + RemoveField(x, y); + TEST_DrawLevelField(x, y); + } + + /* if digged element was about to explode, prevent the explosion */ + ExplodeField[x][y] = EX_TYPE_NONE; + + PlayLevelSoundAction(x, y, action); + } + + Store[x][y] = EL_EMPTY; + + /* this makes it possible to leave the removed element again */ + if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element))) + Store[x][y] = element; + + return TRUE; +} + +static boolean SnapField(struct PlayerInfo *player, int dx, int dy) { int jx = player->jx, jy = player->jy; int x = jx + dx, y = jy + dy; @@ -14685,14 +14288,9 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) return FALSE; } -#if USE_NEW_CONTINUOUS_SNAPPING /* prevent snapping with already pressed snap key when not allowed */ if (player->is_snapping && !can_continue_snapping) return FALSE; -#else - if (player->is_snapping) - return FALSE; -#endif player->MovDir = snap_direction; @@ -14721,32 +14319,20 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) } if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */ - DrawLevelField(player->last_jx, player->last_jy); + TEST_DrawLevelField(player->last_jx, player->last_jy); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); return TRUE; } -boolean DropElement(struct PlayerInfo *player) +static boolean DropElement(struct PlayerInfo *player) { int old_element, new_element; int dropx = player->jx, dropy = player->jy; int drop_direction = player->MovDir; int drop_side = drop_direction; -#if 1 int drop_element = get_next_dropped_element(player); -#else - int drop_element = (player->inventory_size > 0 ? - player->inventory_element[player->inventory_size - 1] : - player->inventory_infinite_element != EL_UNDEFINED ? - player->inventory_infinite_element : - player->dynabombs_left > 0 ? - EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr : - EL_UNDEFINED); -#endif - - player->is_dropping_pressed = TRUE; /* do not drop an element on top of another element; when holding drop key pressed without moving, dropped element must move away before the next @@ -14775,6 +14361,9 @@ boolean DropElement(struct PlayerInfo *player) if (new_element == EL_UNDEFINED) return FALSE; + /* only set if player has anything that can be dropped */ + player->is_dropping_pressed = TRUE; + /* check if drop key was pressed long enough for EM style dynamite */ if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40) return FALSE; @@ -14850,23 +14439,13 @@ boolean DropElement(struct PlayerInfo *player) if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) && element_info[new_element].move_pattern == MV_WHEN_DROPPED) { - int move_direction, nextx, nexty; - if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC) MovDir[dropx][dropy] = drop_direction; - move_direction = MovDir[dropx][dropy]; - nextx = dropx + GET_DX_FROM_DIR(move_direction); - nexty = dropy + GET_DY_FROM_DIR(move_direction); - ChangeCount[dropx][dropy] = 0; /* allow at least one more change */ -#if USE_FIX_IMPACT_COLLISION /* do not cause impact style collision by dropping elements that can fall */ CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION; -#else - CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION; -#endif } player->drop_delay = GET_NEW_DROP_DELAY(drop_element); @@ -14991,12 +14570,43 @@ static void StopLevelSoundActionIfLoop(int x, int y, int action) StopSound(sound_effect); } -static void PlayLevelMusic() +static int getLevelMusicNr() { if (levelset.music[level_nr] != MUS_UNDEFINED) - PlayMusic(levelset.music[level_nr]); /* from config file */ + return levelset.music[level_nr]; /* from config file */ else - PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */ + return MAP_NOCONF_MUSIC(level_nr); /* from music dir */ +} + +static void FadeLevelSounds() +{ + FadeSounds(); +} + +static void FadeLevelMusic() +{ + int music_nr = getLevelMusicNr(); + char *curr_music = getCurrentlyPlayingMusicFilename(); + char *next_music = getMusicInfoEntryFilename(music_nr); + + if (!strEqual(curr_music, next_music)) + FadeMusic(); +} + +void FadeLevelSoundsAndMusic() +{ + FadeLevelSounds(); + FadeLevelMusic(); +} + +static void PlayLevelMusic() +{ + int music_nr = getLevelMusicNr(); + char *curr_music = getCurrentlyPlayingMusicFilename(); + char *next_music = getMusicInfoEntryFilename(music_nr); + + if (!strEqual(curr_music, next_music)) + PlayMusic(music_nr); } void PlayLevelSound_EM(int xx, int yy, int element_em, int sample) @@ -15159,42 +14769,68 @@ void PlayLevelSound_EM(int xx, int yy, int element_em, int sample) } } -#if 0 -void ChangeTime(int value) +void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp) +{ + int element = map_element_SP_to_RND(element_sp); + int action = map_action_SP_to_RND(action_sp); + int offset = (setup.sp_show_border_elements ? 0 : 1); + int x = xx - offset; + int y = yy - offset; + + PlayLevelSoundElementAction(x, y, element, action); +} + +void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm) +{ + int element = map_element_MM_to_RND(element_mm); + int action = map_action_MM_to_RND(action_mm); + int offset = 0; + int x = xx - offset; + int y = yy - offset; + + if (!IS_MM_ELEMENT(element)) + element = EL_MM_DEFAULT; + + PlayLevelSoundElementAction(x, y, element, action); +} + +void PlaySound_MM(int sound_mm) { - int *time = (level.time == 0 ? &TimePlayed : &TimeLeft); + int sound = map_sound_MM_to_RND(sound_mm); + + if (sound == SND_UNDEFINED) + return; - *time += value; + PlaySound(sound); +} + +void PlaySoundLoop_MM(int sound_mm) +{ + int sound = map_sound_MM_to_RND(sound_mm); - /* EMC game engine uses value from time counter of RND game engine */ - level.native_em_level->lev->time = *time; + if (sound == SND_UNDEFINED) + return; - DrawGameValue_Time(*time); + PlaySoundLoop(sound); } -void RaiseScore(int value) +void StopSound_MM(int sound_mm) { - /* EMC game engine and RND game engine have separate score counters */ - int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - &level.native_em_level->lev->score : &local_player->score); + int sound = map_sound_MM_to_RND(sound_mm); - *score += value; + if (sound == SND_UNDEFINED) + return; - DrawGameValue_Score(*score); + StopSound(sound); } -#endif void RaiseScore(int value) { local_player->score += value; -#if 1 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score; DisplayGameControlValues(); -#else - DrawGameValue_Score(local_player->score); -#endif } void RaiseScoreElement(int element) @@ -15281,6 +14917,10 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) { if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED)) { + /* closing door required in case of envelope style request dialogs */ + if (!skip_request) + CloseDoor(DOOR_CLOSE_1); + #if defined(NETWORK_AVALIABLE) if (options.network) SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER); @@ -15288,37 +14928,11 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) #endif { if (quick_quit) - { -#if 1 - -#if 1 FadeSkipNextFadeIn(); -#else - fading = fading_none; -#endif - -#else - OpenDoor(DOOR_CLOSE_1); -#endif - - game_status = GAME_MODE_MAIN; - -#if 1 - DrawAndFadeInMainMenu(REDRAW_FIELD); -#else - DrawMainMenu(); -#endif - } - else - { -#if 0 - FadeOut(REDRAW_FIELD); -#endif - game_status = GAME_MODE_MAIN; + SetGameStatus(GAME_MODE_MAIN); - DrawAndFadeInMainMenu(REDRAW_FIELD); - } + DrawMainMenu(); } } else /* continue playing the game */ @@ -15339,7 +14953,7 @@ void RequestQuitGame(boolean ask_if_really_quit) boolean skip_request = AllPlayersGone || quick_quit; RequestQuitGameExt(skip_request, quick_quit, - "Do you really want to quit the game ?"); + "Do you really want to quit the game?"); } @@ -15347,22 +14961,11 @@ void RequestQuitGame(boolean ask_if_really_quit) /* random generator functions */ /* ------------------------------------------------------------------------- */ -unsigned int InitEngineRandom_RND(long seed) +unsigned int InitEngineRandom_RND(int seed) { game.num_random_calls = 0; -#if 0 - unsigned int rnd_seed = InitEngineRandom(seed); - - printf("::: START RND: %d\n", rnd_seed); - - return rnd_seed; -#else - return InitEngineRandom(seed); - -#endif - } unsigned int RND(int max) @@ -15382,8 +14985,6 @@ unsigned int RND(int max) /* game engine snapshot handling functions */ /* ------------------------------------------------------------------------- */ -#define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x)) - struct EngineSnapshotInfo { /* runtime values for custom element collect score */ @@ -15393,32 +14994,14 @@ struct EngineSnapshotInfo int choice_pos[NUM_GROUP_ELEMENTS]; /* runtime values for belt position animations */ - int belt_graphic[4 * NUM_BELT_PARTS]; - int belt_anim_mode[4 * NUM_BELT_PARTS]; -}; - -struct EngineSnapshotNodeInfo -{ - void *buffer_orig; - void *buffer_copy; - int size; + int belt_graphic[4][NUM_BELT_PARTS]; + int belt_anim_mode[4][NUM_BELT_PARTS]; }; static struct EngineSnapshotInfo engine_snapshot_rnd; -static ListNode *engine_snapshot_list = NULL; static char *snapshot_level_identifier = NULL; static int snapshot_level_nr = -1; -void FreeEngineSnapshot() -{ - while (engine_snapshot_list != NULL) - deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key, - checked_free); - - setString(&snapshot_level_identifier, NULL); - snapshot_level_nr = -1; -} - static void SaveEngineSnapshotValues_RND() { static int belt_base_active_element[4] = @@ -15452,15 +15035,15 @@ static void SaveEngineSnapshotValues_RND() int graphic = el2img(element); int anim_mode = graphic_info[graphic].anim_mode; - engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic; - engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode; + engine_snapshot_rnd.belt_graphic[i][j] = graphic; + engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode; } } } static void LoadEngineSnapshotValues_RND() { - unsigned long num_random_calls = game.num_random_calls; + unsigned int num_random_calls = game.num_random_calls; int i, j; for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -15481,8 +15064,8 @@ static void LoadEngineSnapshotValues_RND() { for (j = 0; j < NUM_BELT_PARTS; j++) { - int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j]; - int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j]; + int graphic = engine_snapshot_rnd.belt_graphic[i][j]; + int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j]; graphic_info[graphic].anim_mode = anim_mode; } @@ -15504,110 +15087,113 @@ static void LoadEngineSnapshotValues_RND() } } -static void SaveEngineSnapshotBuffer(void *buffer, int size) +void FreeEngineSnapshotSingle() { - struct EngineSnapshotNodeInfo *bi = - checked_calloc(sizeof(struct EngineSnapshotNodeInfo)); - - bi->buffer_orig = buffer; - bi->buffer_copy = checked_malloc(size); - bi->size = size; + FreeSnapshotSingle(); - memcpy(bi->buffer_copy, buffer, size); - - addNodeToList(&engine_snapshot_list, NULL, bi); + setString(&snapshot_level_identifier, NULL); + snapshot_level_nr = -1; } -void SaveEngineSnapshot() +void FreeEngineSnapshotList() { - FreeEngineSnapshot(); /* free previous snapshot, if needed */ + FreeSnapshotList(); +} - if (level_editor_test_game) /* do not save snapshots from editor */ - return; +ListNode *SaveEngineSnapshotBuffers() +{ + ListNode *buffers = NULL; /* copy some special values to a structure better suited for the snapshot */ - SaveEngineSnapshotValues_RND(); - SaveEngineSnapshotValues_EM(); + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) + SaveEngineSnapshotValues_RND(); + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + SaveEngineSnapshotValues_EM(); + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + SaveEngineSnapshotValues_SP(&buffers); + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + SaveEngineSnapshotValues_MM(&buffers); /* save values stored in special snapshot structure */ - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em)); + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd)); + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em)); + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp)); + if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm)); /* save further RND engine values */ - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y)); - - /* save level identification information */ - - setString(&snapshot_level_identifier, leveldir_current->identifier); - snapshot_level_nr = level_nr; + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player)); + 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)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y)); #if 0 - ListNode *node = engine_snapshot_list; + ListNode *node = engine_snapshot_list_rnd; int num_bytes = 0; while (node != NULL) @@ -15619,134 +15205,201 @@ void SaveEngineSnapshot() printf("::: size of engine snapshot: %d bytes\n", num_bytes); #endif + + return buffers; } -static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi) +void SaveEngineSnapshotSingle() { - memcpy(bi->buffer_orig, bi->buffer_copy, bi->size); + ListNode *buffers = SaveEngineSnapshotBuffers(); + + /* finally save all snapshot buffers to single snapshot */ + SaveSnapshotSingle(buffers); + + /* save level identification information */ + setString(&snapshot_level_identifier, leveldir_current->identifier); + snapshot_level_nr = level_nr; } -void LoadEngineSnapshot() +boolean CheckSaveEngineSnapshotToList() { - ListNode *node = engine_snapshot_list; + boolean save_snapshot = + ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) || + (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE && + game.snapshot.changed_action) || + (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT && + game.snapshot.collected_item)); + + game.snapshot.changed_action = FALSE; + game.snapshot.collected_item = FALSE; + game.snapshot.save_snapshot = save_snapshot; - if (engine_snapshot_list == NULL) + return save_snapshot; +} + +void SaveEngineSnapshotToList() +{ + if (game.snapshot.mode == SNAPSHOT_MODE_OFF || + tape.quick_resume) return; - while (node != NULL) - { - LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content); + ListNode *buffers = SaveEngineSnapshotBuffers(); - node = node->next; - } + /* finally save all snapshot buffers to snapshot list */ + SaveSnapshotToList(buffers); +} + +void SaveEngineSnapshotToListInitial() +{ + FreeEngineSnapshotList(); + + SaveEngineSnapshotToList(); +} +void LoadEngineSnapshotValues() +{ /* restore special values from snapshot structure */ - LoadEngineSnapshotValues_RND(); - LoadEngineSnapshotValues_EM(); + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) + LoadEngineSnapshotValues_RND(); + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + LoadEngineSnapshotValues_EM(); + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + LoadEngineSnapshotValues_SP(); + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + LoadEngineSnapshotValues_MM(); +} + +void LoadEngineSnapshotSingle() +{ + LoadSnapshotSingle(); + + LoadEngineSnapshotValues(); +} + +void LoadEngineSnapshot_Undo(int steps) +{ + LoadSnapshotFromList_Older(steps); + + LoadEngineSnapshotValues(); +} + +void LoadEngineSnapshot_Redo(int steps) +{ + LoadSnapshotFromList_Newer(steps); + + LoadEngineSnapshotValues(); } -boolean CheckEngineSnapshot() +boolean CheckEngineSnapshotSingle() { return (strEqual(snapshot_level_identifier, leveldir_current->identifier) && snapshot_level_nr == level_nr); } +boolean CheckEngineSnapshotList() +{ + return CheckSnapshotList(); +} + /* ---------- new game button stuff ---------------------------------------- */ -/* graphic position values for game buttons */ -#define GAME_BUTTON_XSIZE 30 -#define GAME_BUTTON_YSIZE 30 -#define GAME_BUTTON_XPOS 5 -#define GAME_BUTTON_YPOS 215 -#define SOUND_BUTTON_XPOS 5 -#define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE) - -#define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE) -#define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE) -#define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE) -#define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE) -#define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE) -#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE) - static struct { - int *x, *y; - int gd_x, gd_y; + int graphic; + struct XY *pos; int gadget_id; + boolean *setup_value; + boolean allowed_on_tape; char *infotext; } gamebutton_info[NUM_GAME_BUTTONS] = { -#if 1 { - &game.button.stop.x, &game.button.stop.y, - GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS, - GAME_CTRL_ID_STOP, - "stop game" + IMG_GFX_GAME_BUTTON_STOP, &game.button.stop, + GAME_CTRL_ID_STOP, NULL, + TRUE, "stop game" }, { - &game.button.pause.x, &game.button.pause.y, - GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS, - GAME_CTRL_ID_PAUSE, - "pause game" + IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause, + GAME_CTRL_ID_PAUSE, NULL, + TRUE, "pause game" }, { - &game.button.play.x, &game.button.play.y, - GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS, - GAME_CTRL_ID_PLAY, - "play game" + IMG_GFX_GAME_BUTTON_PLAY, &game.button.play, + GAME_CTRL_ID_PLAY, NULL, + TRUE, "play game" }, { - &game.button.sound_music.x, &game.button.sound_music.y, - SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS, - SOUND_CTRL_ID_MUSIC, - "background music on/off" + IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo, + GAME_CTRL_ID_UNDO, NULL, + TRUE, "undo step" }, { - &game.button.sound_loops.x, &game.button.sound_loops.y, - SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS, - SOUND_CTRL_ID_LOOPS, - "sound loops on/off" + IMG_GFX_GAME_BUTTON_REDO, &game.button.redo, + GAME_CTRL_ID_REDO, NULL, + TRUE, "redo step" }, { - &game.button.sound_simple.x,&game.button.sound_simple.y, - SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS, - SOUND_CTRL_ID_SIMPLE, - "normal sounds on/off" - } -#else + IMG_GFX_GAME_BUTTON_SAVE, &game.button.save, + GAME_CTRL_ID_SAVE, NULL, + TRUE, "save game" + }, + { + IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2, + GAME_CTRL_ID_PAUSE2, NULL, + TRUE, "pause game" + }, + { + IMG_GFX_GAME_BUTTON_LOAD, &game.button.load, + GAME_CTRL_ID_LOAD, NULL, + TRUE, "load game" + }, + { + IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop, + GAME_CTRL_ID_PANEL_STOP, NULL, + FALSE, "stop game" + }, + { + IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause, + GAME_CTRL_ID_PANEL_PAUSE, NULL, + FALSE, "pause game" + }, + { + IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play, + GAME_CTRL_ID_PANEL_PLAY, NULL, + FALSE, "play game" + }, { - GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS, - GAME_CTRL_ID_STOP, - "stop game" + IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music, + SOUND_CTRL_ID_MUSIC, &setup.sound_music, + TRUE, "background music on/off" }, { - GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS, - GAME_CTRL_ID_PAUSE, - "pause game" + IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops, + SOUND_CTRL_ID_LOOPS, &setup.sound_loops, + TRUE, "sound loops on/off" }, { - GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS, - GAME_CTRL_ID_PLAY, - "play game" + IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple, + SOUND_CTRL_ID_SIMPLE, &setup.sound_simple, + TRUE, "normal sounds on/off" }, { - SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS, - SOUND_CTRL_ID_MUSIC, - "background music 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" }, { - SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS, - SOUND_CTRL_ID_LOOPS, - "sound loops 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" }, { - SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS, - SOUND_CTRL_ID_SIMPLE, - "normal sounds 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" } -#endif }; void CreateGameButtons() @@ -15755,63 +15408,72 @@ void CreateGameButtons() for (i = 0; i < NUM_GAME_BUTTONS; i++) { - Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap; + struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic]; + struct XY *pos = gamebutton_info[i].pos; struct GadgetInfo *gi; int button_type; boolean checked; - unsigned long event_mask; - int x, y; - int gd_xoffset, gd_yoffset; - int gd_x1, gd_x2, gd_y1, gd_y2; + unsigned int event_mask; + 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 gd_x = gfx->src_x; + int gd_y = gfx->src_y; + int gd_xp = gfx->src_x + gfx->pressed_xoffset; + int gd_yp = gfx->src_y + gfx->pressed_yoffset; + int gd_xa = gfx->src_x + gfx->active_xoffset; + 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 id = i; - x = DX + *gamebutton_info[i].x; - y = DY + *gamebutton_info[i].y; - gd_xoffset = gamebutton_info[i].gd_x; - gd_yoffset = gamebutton_info[i].gd_y; - gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset; - gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset; + if (gfx->bitmap == NULL) + { + game_gadget[id] = NULL; + + continue; + } if (id == GAME_CTRL_ID_STOP || - id == GAME_CTRL_ID_PAUSE || - id == GAME_CTRL_ID_PLAY) + id == GAME_CTRL_ID_PANEL_STOP || + id == GAME_CTRL_ID_PLAY || + id == GAME_CTRL_ID_PANEL_PLAY || + id == GAME_CTRL_ID_SAVE || + id == GAME_CTRL_ID_LOAD) { button_type = GD_TYPE_NORMAL_BUTTON; checked = FALSE; event_mask = GD_EVENT_RELEASED; - gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE; - gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE; + } + else if (id == GAME_CTRL_ID_UNDO || + id == GAME_CTRL_ID_REDO) + { + button_type = GD_TYPE_NORMAL_BUTTON; + checked = FALSE; + event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED; } else { button_type = GD_TYPE_CHECK_BUTTON; - checked = - ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) || - (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) || - (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE); + checked = (gamebutton_info[i].setup_value != NULL ? + *gamebutton_info[i].setup_value : FALSE); event_mask = GD_EVENT_PRESSED; - gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset; - gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE; } gi = CreateGadget(GDI_CUSTOM_ID, id, GDI_INFO_TEXT, gamebutton_info[i].infotext, -#if 1 - GDI_X, x, - GDI_Y, y, -#else - GDI_X, DX + gd_xoffset, - GDI_Y, DY + gd_yoffset, -#endif - GDI_WIDTH, GAME_BUTTON_XSIZE, - GDI_HEIGHT, GAME_BUTTON_YSIZE, + GDI_X, base_x + GDI_ACTIVE_POS(pos->x), + GDI_Y, base_y + GDI_ACTIVE_POS(pos->y), + GDI_WIDTH, gfx->width, + GDI_HEIGHT, gfx->height, GDI_TYPE, button_type, GDI_STATE, GD_BUTTON_UNPRESSED, GDI_CHECKED, checked, - GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1, - GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1, - GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2, - GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2, + GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y, + GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp, + 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_EVENT_MASK, event_mask, GDI_CALLBACK_ACTION, HandleGameButtons, @@ -15832,48 +15494,226 @@ void FreeGameButtons() FreeGadget(game_gadget[i]); } -static void MapGameButtons() +static void UnmapGameButtonsAtSamePosition(int id) { int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - MapGadget(game_gadget[i]); + if (i != id && + gamebutton_info[i].pos->x == gamebutton_info[id].pos->x && + gamebutton_info[i].pos->y == gamebutton_info[id].pos->y) + UnmapGadget(game_gadget[i]); } -void UnmapGameButtons() +static void UnmapGameButtonsAtSamePosition_All() +{ + if (setup.show_snapshot_buttons) + { + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD); + } + else + { + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY); + + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY); + } +} + +static void MapGameButtonsAtSamePosition(int id) { int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - UnmapGadget(game_gadget[i]); + if (i != id && + gamebutton_info[i].pos->x == gamebutton_info[id].pos->x && + gamebutton_info[i].pos->y == gamebutton_info[id].pos->y) + MapGadget(game_gadget[i]); + + UnmapGameButtonsAtSamePosition_All(); } -void RedrawGameButtons() +void MapUndoRedoButtons() +{ + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); + + 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() +{ + UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]); + UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]); + + MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); + MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); + + ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END); +} + +void MapGameButtonsExt(boolean on_tape) { int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - RedrawGadget(game_gadget[i]); + if ((!on_tape || gamebutton_info[i].allowed_on_tape) && + i != GAME_CTRL_ID_UNDO && + i != GAME_CTRL_ID_REDO) + MapGadget(game_gadget[i]); + + UnmapGameButtonsAtSamePosition_All(); + + RedrawGameButtons(); } -static void HandleGameButtons(struct GadgetInfo *gi) +void UnmapGameButtonsExt(boolean on_tape) +{ + int i; + + for (i = 0; i < NUM_GAME_BUTTONS; i++) + if (!on_tape || gamebutton_info[i].allowed_on_tape) + UnmapGadget(game_gadget[i]); +} + +void RedrawGameButtonsExt(boolean on_tape) +{ + int i; + + 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; +} + +void SetGadgetState(struct GadgetInfo *gi, boolean state) +{ + if (gi == NULL) + return; + + gi->checked = state; +} + +void RedrawSoundButtonGadget(int id) +{ + int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC : + id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS : + id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE : + id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC : + id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS : + id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE : + id); + + SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value); + RedrawGadget(game_gadget[id2]); +} + +void MapGameButtons() +{ + MapGameButtonsExt(FALSE); +} + +void UnmapGameButtons() +{ + UnmapGameButtonsExt(FALSE); +} + +void RedrawGameButtons() +{ + RedrawGameButtonsExt(FALSE); +} + +void MapGameButtonsOnTape() { - int id = gi->custom_id; + MapGameButtonsExt(TRUE); +} + +void UnmapGameButtonsOnTape() +{ + UnmapGameButtonsExt(TRUE); +} + +void RedrawGameButtonsOnTape() +{ + RedrawGameButtonsExt(TRUE); +} + +void GameUndoRedoExt() +{ + ClearPlayerAction(); + + tape.pausing = TRUE; + + RedrawPlayfield(); + UpdateAndDisplayGameControlValues(); + + DrawCompleteVideoDisplay(); + DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); + DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); + DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0); + + BackToFront(); +} + +void GameUndo(int steps) +{ + if (!CheckEngineSnapshotList()) + return; + + LoadEngineSnapshot_Undo(steps); + + GameUndoRedoExt(); +} + +void GameRedo(int steps) +{ + if (!CheckEngineSnapshotList()) + return; - if (game_status != GAME_MODE_PLAYING) + LoadEngineSnapshot_Redo(steps); + + GameUndoRedoExt(); +} + +static void HandleGameButtonsExt(int id, int button) +{ + static boolean game_undo_executed = FALSE; + int steps = BUTTON_STEPSIZE(button); + boolean handle_game_buttons = + (game_status == GAME_MODE_PLAYING || + (game_status == GAME_MODE_MAIN && tape.show_game_buttons)); + + if (!handle_game_buttons) return; switch (id) { case GAME_CTRL_ID_STOP: + case GAME_CTRL_ID_PANEL_STOP: + if (game_status == GAME_MODE_MAIN) + break; + if (tape.playing) TapeStop(); else RequestQuitGame(TRUE); + break; case GAME_CTRL_ID_PAUSE: - if (options.network) + case GAME_CTRL_ID_PAUSE2: + case GAME_CTRL_ID_PANEL_PAUSE: + if (options.network && game_status == GAME_MODE_PLAYING) { #if defined(NETWORK_AVALIABLE) if (tape.pausing) @@ -15884,27 +15724,61 @@ static void HandleGameButtons(struct GadgetInfo *gi) } else TapeTogglePause(TAPE_TOGGLE_MANUAL); + + game_undo_executed = FALSE; + break; case GAME_CTRL_ID_PLAY: - if (tape.pausing) + case GAME_CTRL_ID_PANEL_PLAY: + if (game_status == GAME_MODE_MAIN) + { + StartGameActions(options.network, setup.autorecord, level.random_seed); + } + else if (tape.pausing) { #if defined(NETWORK_AVALIABLE) if (options.network) SendToServer_ContinuePlaying(); else #endif - { - tape.pausing = FALSE; - DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0); - } + TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE); } break; + case GAME_CTRL_ID_UNDO: + // Important: When using "save snapshot when collecting an item" mode, + // load last (current) snapshot for first "undo" after pressing "pause" + // (else the last-but-one snapshot would be loaded, because the snapshot + // pointer already points to the last snapshot when pressing "pause", + // which is fine for "every step/move" mode, but not for "every collect") + if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT && + !game_undo_executed) + steps--; + + game_undo_executed = TRUE; + + GameUndo(steps); + break; + + case GAME_CTRL_ID_REDO: + GameRedo(steps); + break; + + case GAME_CTRL_ID_SAVE: + TapeQuickSave(); + break; + + case GAME_CTRL_ID_LOAD: + TapeQuickLoad(); + break; + case SOUND_CTRL_ID_MUSIC: + case SOUND_CTRL_ID_PANEL_MUSIC: if (setup.sound_music) { setup.sound_music = FALSE; + FadeMusic(); } else if (audio.music_available) @@ -15913,31 +15787,60 @@ static void HandleGameButtons(struct GadgetInfo *gi) SetAudioMode(setup.sound); - PlayLevelMusic(); + if (game_status == GAME_MODE_PLAYING) + PlayLevelMusic(); } + + RedrawSoundButtonGadget(id); + break; case SOUND_CTRL_ID_LOOPS: + case SOUND_CTRL_ID_PANEL_LOOPS: if (setup.sound_loops) setup.sound_loops = FALSE; else if (audio.loops_available) { setup.sound = setup.sound_loops = TRUE; + SetAudioMode(setup.sound); } + + RedrawSoundButtonGadget(id); + break; case SOUND_CTRL_ID_SIMPLE: + case SOUND_CTRL_ID_PANEL_SIMPLE: if (setup.sound_simple) setup.sound_simple = FALSE; else if (audio.sound_available) { setup.sound = setup.sound_simple = TRUE; + SetAudioMode(setup.sound); } + + RedrawSoundButtonGadget(id); + break; default: break; } } + +static void HandleGameButtons(struct GadgetInfo *gi) +{ + HandleGameButtonsExt(gi->custom_id, gi->event.button); +} + +void HandleSoundButtonKeys(Key key) +{ + if (key == setup.shortcut.sound_simple) + ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON); + else if (key == setup.shortcut.sound_loops) + ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON); + else if (key == setup.shortcut.sound_music) + ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON); +}