X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=46f050d2c3c18dd37c7ab8ae7fcdc40b3de2ad7a;hp=d2787a10f8da741baf8f8e6d5433f5a5a789083f;hb=567cfcb396e3fc031a25362b747dd0a5a096b4ec;hpb=1e2fa0f4dad96d33535e3dd3d85fbf9219c0d1bc diff --git a/src/game.c b/src/game.c index d2787a10..3d2f055f 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,520 +86,714 @@ #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 +#define GAME_PANEL_INVENTORY_FIRST_1 3 +#define GAME_PANEL_INVENTORY_FIRST_2 4 +#define GAME_PANEL_INVENTORY_FIRST_3 5 +#define GAME_PANEL_INVENTORY_FIRST_4 6 +#define GAME_PANEL_INVENTORY_FIRST_5 7 +#define GAME_PANEL_INVENTORY_FIRST_6 8 +#define GAME_PANEL_INVENTORY_FIRST_7 9 +#define GAME_PANEL_INVENTORY_FIRST_8 10 +#define GAME_PANEL_INVENTORY_LAST_1 11 +#define GAME_PANEL_INVENTORY_LAST_2 12 +#define GAME_PANEL_INVENTORY_LAST_3 13 +#define GAME_PANEL_INVENTORY_LAST_4 14 +#define GAME_PANEL_INVENTORY_LAST_5 15 +#define GAME_PANEL_INVENTORY_LAST_6 16 +#define GAME_PANEL_INVENTORY_LAST_7 17 +#define GAME_PANEL_INVENTORY_LAST_8 18 +#define GAME_PANEL_KEY_1 19 +#define GAME_PANEL_KEY_2 20 +#define GAME_PANEL_KEY_3 21 +#define GAME_PANEL_KEY_4 22 +#define GAME_PANEL_KEY_5 23 +#define GAME_PANEL_KEY_6 24 +#define GAME_PANEL_KEY_7 25 +#define GAME_PANEL_KEY_8 26 +#define GAME_PANEL_KEY_WHITE 27 +#define GAME_PANEL_KEY_WHITE_COUNT 28 +#define GAME_PANEL_SCORE 29 +#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_FRAME 35 +#define GAME_PANEL_SHIELD_NORMAL 36 +#define GAME_PANEL_SHIELD_NORMAL_TIME 37 +#define GAME_PANEL_SHIELD_DEADLY 38 +#define GAME_PANEL_SHIELD_DEADLY_TIME 39 +#define GAME_PANEL_EXIT 40 +#define GAME_PANEL_EMC_MAGIC_BALL 41 +#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42 +#define GAME_PANEL_LIGHT_SWITCH 43 +#define GAME_PANEL_LIGHT_SWITCH_TIME 44 +#define GAME_PANEL_TIMEGATE_SWITCH 45 +#define GAME_PANEL_TIMEGATE_SWITCH_TIME 46 +#define GAME_PANEL_SWITCHGATE_SWITCH 47 +#define GAME_PANEL_EMC_LENSES 48 +#define GAME_PANEL_EMC_LENSES_TIME 49 +#define GAME_PANEL_EMC_MAGNIFIER 50 +#define GAME_PANEL_EMC_MAGNIFIER_TIME 51 +#define GAME_PANEL_BALLOON_SWITCH 52 +#define GAME_PANEL_DYNABOMB_NUMBER 53 +#define GAME_PANEL_DYNABOMB_SIZE 54 +#define GAME_PANEL_DYNABOMB_POWER 55 +#define GAME_PANEL_PENGUINS 56 +#define GAME_PANEL_SOKOBAN_OBJECTS 57 +#define GAME_PANEL_SOKOBAN_FIELDS 58 +#define GAME_PANEL_ROBOT_WHEEL 59 +#define GAME_PANEL_CONVEYOR_BELT_1 60 +#define GAME_PANEL_CONVEYOR_BELT_2 61 +#define GAME_PANEL_CONVEYOR_BELT_3 62 +#define GAME_PANEL_CONVEYOR_BELT_4 63 +#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64 +#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65 +#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66 +#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67 +#define GAME_PANEL_MAGIC_WALL 68 +#define GAME_PANEL_MAGIC_WALL_TIME 69 +#define GAME_PANEL_GRAVITY_STATE 70 +#define GAME_PANEL_GRAPHIC_1 71 +#define GAME_PANEL_GRAPHIC_2 72 +#define GAME_PANEL_GRAPHIC_3 73 +#define GAME_PANEL_GRAPHIC_4 74 +#define GAME_PANEL_GRAPHIC_5 75 +#define GAME_PANEL_GRAPHIC_6 76 +#define GAME_PANEL_GRAPHIC_7 77 +#define GAME_PANEL_GRAPHIC_8 78 +#define GAME_PANEL_ELEMENT_1 79 +#define GAME_PANEL_ELEMENT_2 80 +#define GAME_PANEL_ELEMENT_3 81 +#define GAME_PANEL_ELEMENT_4 82 +#define GAME_PANEL_ELEMENT_5 83 +#define GAME_PANEL_ELEMENT_6 84 +#define GAME_PANEL_ELEMENT_7 85 +#define GAME_PANEL_ELEMENT_8 86 +#define GAME_PANEL_ELEMENT_COUNT_1 87 +#define GAME_PANEL_ELEMENT_COUNT_2 88 +#define GAME_PANEL_ELEMENT_COUNT_3 89 +#define GAME_PANEL_ELEMENT_COUNT_4 90 +#define GAME_PANEL_ELEMENT_COUNT_5 91 +#define GAME_PANEL_ELEMENT_COUNT_6 92 +#define GAME_PANEL_ELEMENT_COUNT_7 93 +#define GAME_PANEL_ELEMENT_COUNT_8 94 +#define GAME_PANEL_CE_SCORE_1 95 +#define GAME_PANEL_CE_SCORE_2 96 +#define GAME_PANEL_CE_SCORE_3 97 +#define GAME_PANEL_CE_SCORE_4 98 +#define GAME_PANEL_CE_SCORE_5 99 +#define GAME_PANEL_CE_SCORE_6 100 +#define GAME_PANEL_CE_SCORE_7 101 +#define GAME_PANEL_CE_SCORE_8 102 +#define GAME_PANEL_CE_SCORE_1_ELEMENT 103 +#define GAME_PANEL_CE_SCORE_2_ELEMENT 104 +#define GAME_PANEL_CE_SCORE_3_ELEMENT 105 +#define GAME_PANEL_CE_SCORE_4_ELEMENT 106 +#define GAME_PANEL_CE_SCORE_5_ELEMENT 107 +#define GAME_PANEL_CE_SCORE_6_ELEMENT 108 +#define GAME_PANEL_CE_SCORE_7_ELEMENT 109 +#define GAME_PANEL_CE_SCORE_8_ELEMENT 110 +#define GAME_PANEL_PLAYER_NAME 111 +#define GAME_PANEL_LEVEL_NAME 112 +#define GAME_PANEL_LEVEL_AUTHOR 113 + +#define NUM_GAME_PANEL_CONTROLS 114 + +struct GamePanelOrderInfo +{ + int nr; + int sort_priority; +}; -#define GAME_CONTROL_LEVEL_NUMBER 0 -#define GAME_CONTROL_GEMS 1 -#define GAME_CONTROL_INVENTORY_COUNT 2 -#define GAME_CONTROL_INVENTORY_FIRST_1 3 -#define GAME_CONTROL_INVENTORY_FIRST_2 4 -#define GAME_CONTROL_INVENTORY_FIRST_3 5 -#define GAME_CONTROL_INVENTORY_FIRST_4 6 -#define GAME_CONTROL_INVENTORY_FIRST_5 7 -#define GAME_CONTROL_INVENTORY_FIRST_6 8 -#define GAME_CONTROL_INVENTORY_FIRST_7 9 -#define GAME_CONTROL_INVENTORY_FIRST_8 10 -#define GAME_CONTROL_INVENTORY_LAST_1 11 -#define GAME_CONTROL_INVENTORY_LAST_2 12 -#define GAME_CONTROL_INVENTORY_LAST_3 13 -#define GAME_CONTROL_INVENTORY_LAST_4 14 -#define GAME_CONTROL_INVENTORY_LAST_5 15 -#define GAME_CONTROL_INVENTORY_LAST_6 16 -#define GAME_CONTROL_INVENTORY_LAST_7 17 -#define GAME_CONTROL_INVENTORY_LAST_8 18 -#define GAME_CONTROL_KEY_1 19 -#define GAME_CONTROL_KEY_2 20 -#define GAME_CONTROL_KEY_3 21 -#define GAME_CONTROL_KEY_4 22 -#define GAME_CONTROL_KEY_5 23 -#define GAME_CONTROL_KEY_6 24 -#define GAME_CONTROL_KEY_7 25 -#define GAME_CONTROL_KEY_8 26 -#define GAME_CONTROL_KEY_WHITE 27 -#define GAME_CONTROL_KEY_WHITE_COUNT 28 -#define GAME_CONTROL_SCORE 29 -#define GAME_CONTROL_TIME 30 -#define GAME_CONTROL_TIME_HH 31 -#define GAME_CONTROL_TIME_MM 32 -#define GAME_CONTROL_TIME_SS 33 -#define GAME_CONTROL_SHIELD_NORMAL 34 -#define GAME_CONTROL_SHIELD_NORMAL_TIME 35 -#define GAME_CONTROL_SHIELD_DEADLY 36 -#define GAME_CONTROL_SHIELD_DEADLY_TIME 37 -#define GAME_CONTROL_EXIT 38 -#define GAME_CONTROL_EM_EXIT 39 -#define GAME_CONTROL_SP_EXIT 40 -#define GAME_CONTROL_STEEL_EXIT 41 -#define GAME_CONTROL_EM_STEEL_EXIT 42 -#define GAME_CONTROL_EMC_MAGIC_BALL 43 -#define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH 44 -#define GAME_CONTROL_LIGHT_SWITCH 45 -#define GAME_CONTROL_LIGHT_SWITCH_TIME 46 -#define GAME_CONTROL_TIMEGATE_SWITCH 47 -#define GAME_CONTROL_TIMEGATE_SWITCH_TIME 48 -#define GAME_CONTROL_SWITCHGATE_SWITCH 49 -#define GAME_CONTROL_EMC_LENSES 50 -#define GAME_CONTROL_EMC_LENSES_TIME 51 -#define GAME_CONTROL_EMC_MAGNIFIER 52 -#define GAME_CONTROL_EMC_MAGNIFIER_TIME 53 -#define GAME_CONTROL_BALLOON_SWITCH 54 -#define GAME_CONTROL_DYNABOMB_NUMBER 55 -#define GAME_CONTROL_DYNABOMB_SIZE 56 -#define GAME_CONTROL_DYNABOMB_POWER 57 -#define GAME_CONTROL_PENGUINS 58 -#define GAME_CONTROL_SOKOBAN_OBJECTS 59 -#define GAME_CONTROL_SOKOBAN_FIELDS 60 -#define GAME_CONTROL_ROBOT_WHEEL 61 -#define GAME_CONTROL_CONVEYOR_BELT_1 62 -#define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 63 -#define GAME_CONTROL_CONVEYOR_BELT_2 64 -#define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 65 -#define GAME_CONTROL_CONVEYOR_BELT_3 66 -#define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 67 -#define GAME_CONTROL_CONVEYOR_BELT_4 68 -#define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 69 -#define GAME_CONTROL_MAGIC_WALL 70 -#define GAME_CONTROL_MAGIC_WALL_TIME 71 -#define GAME_CONTROL_BD_MAGIC_WALL 72 -#define GAME_CONTROL_DC_MAGIC_WALL 73 -#define GAME_CONTROL_PLAYER_NAME 74 -#define GAME_CONTROL_LEVEL_NAME 75 -#define GAME_CONTROL_LEVEL_AUTHOR 76 - -#define NUM_GAME_CONTROLS 77 - -int game_control_value[NUM_GAME_CONTROLS]; -int last_game_control_value[NUM_GAME_CONTROLS]; - -struct GameControlInfo +static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS]; + +struct GamePanelControlInfo { int nr; struct TextPosInfo *pos; int type; + + int value, last_value; + int frame, last_frame; + int gfx_frame; + int gfx_random; }; -static struct GameControlInfo game_controls[] = +static struct GamePanelControlInfo game_panel_controls[] = { { - GAME_CONTROL_LEVEL_NUMBER, + GAME_PANEL_LEVEL_NUMBER, &game.panel.level_number, TYPE_INTEGER, }, { - GAME_CONTROL_GEMS, + GAME_PANEL_GEMS, &game.panel.gems, TYPE_INTEGER, }, { - GAME_CONTROL_INVENTORY_COUNT, + GAME_PANEL_INVENTORY_COUNT, &game.panel.inventory_count, TYPE_INTEGER, }, { - GAME_CONTROL_INVENTORY_FIRST_1, - &game.panel.inventory_first_1, + GAME_PANEL_INVENTORY_FIRST_1, + &game.panel.inventory_first[0], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_FIRST_2, - &game.panel.inventory_first_2, + GAME_PANEL_INVENTORY_FIRST_2, + &game.panel.inventory_first[1], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_FIRST_3, - &game.panel.inventory_first_3, + GAME_PANEL_INVENTORY_FIRST_3, + &game.panel.inventory_first[2], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_FIRST_4, - &game.panel.inventory_first_4, + GAME_PANEL_INVENTORY_FIRST_4, + &game.panel.inventory_first[3], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_FIRST_5, - &game.panel.inventory_first_5, + GAME_PANEL_INVENTORY_FIRST_5, + &game.panel.inventory_first[4], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_FIRST_6, - &game.panel.inventory_first_6, + GAME_PANEL_INVENTORY_FIRST_6, + &game.panel.inventory_first[5], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_FIRST_7, - &game.panel.inventory_first_7, + GAME_PANEL_INVENTORY_FIRST_7, + &game.panel.inventory_first[6], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_FIRST_8, - &game.panel.inventory_first_8, + GAME_PANEL_INVENTORY_FIRST_8, + &game.panel.inventory_first[7], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_LAST_1, - &game.panel.inventory_last_1, + GAME_PANEL_INVENTORY_LAST_1, + &game.panel.inventory_last[0], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_LAST_2, - &game.panel.inventory_last_2, + GAME_PANEL_INVENTORY_LAST_2, + &game.panel.inventory_last[1], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_LAST_3, - &game.panel.inventory_last_3, + GAME_PANEL_INVENTORY_LAST_3, + &game.panel.inventory_last[2], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_LAST_4, - &game.panel.inventory_last_4, + GAME_PANEL_INVENTORY_LAST_4, + &game.panel.inventory_last[3], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_LAST_5, - &game.panel.inventory_last_5, + GAME_PANEL_INVENTORY_LAST_5, + &game.panel.inventory_last[4], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_LAST_6, - &game.panel.inventory_last_6, + GAME_PANEL_INVENTORY_LAST_6, + &game.panel.inventory_last[5], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_LAST_7, - &game.panel.inventory_last_7, + GAME_PANEL_INVENTORY_LAST_7, + &game.panel.inventory_last[6], TYPE_ELEMENT, }, { - GAME_CONTROL_INVENTORY_LAST_8, - &game.panel.inventory_last_8, + GAME_PANEL_INVENTORY_LAST_8, + &game.panel.inventory_last[7], TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_1, + GAME_PANEL_KEY_1, &game.panel.key[0], TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_2, + GAME_PANEL_KEY_2, &game.panel.key[1], TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_3, + GAME_PANEL_KEY_3, &game.panel.key[2], TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_4, + GAME_PANEL_KEY_4, &game.panel.key[3], TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_5, + GAME_PANEL_KEY_5, &game.panel.key[4], TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_6, + GAME_PANEL_KEY_6, &game.panel.key[5], TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_7, + GAME_PANEL_KEY_7, &game.panel.key[6], TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_8, + GAME_PANEL_KEY_8, &game.panel.key[7], TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_WHITE, + GAME_PANEL_KEY_WHITE, &game.panel.key_white, TYPE_ELEMENT, }, { - GAME_CONTROL_KEY_WHITE_COUNT, + GAME_PANEL_KEY_WHITE_COUNT, &game.panel.key_white_count, TYPE_INTEGER, }, { - GAME_CONTROL_SCORE, + GAME_PANEL_SCORE, &game.panel.score, TYPE_INTEGER, }, { - GAME_CONTROL_TIME, + GAME_PANEL_HIGHSCORE, + &game.panel.highscore, + TYPE_INTEGER, + }, + { + GAME_PANEL_TIME, &game.panel.time, TYPE_INTEGER, }, { - GAME_CONTROL_TIME_HH, + GAME_PANEL_TIME_HH, &game.panel.time_hh, TYPE_INTEGER, }, { - GAME_CONTROL_TIME_MM, + GAME_PANEL_TIME_MM, &game.panel.time_mm, TYPE_INTEGER, }, { - GAME_CONTROL_TIME_SS, + GAME_PANEL_TIME_SS, &game.panel.time_ss, TYPE_INTEGER, }, { - GAME_CONTROL_SHIELD_NORMAL, + GAME_PANEL_FRAME, + &game.panel.frame, + TYPE_INTEGER, + }, + { + GAME_PANEL_SHIELD_NORMAL, &game.panel.shield_normal, TYPE_ELEMENT, }, { - GAME_CONTROL_SHIELD_NORMAL_TIME, + GAME_PANEL_SHIELD_NORMAL_TIME, &game.panel.shield_normal_time, TYPE_INTEGER, }, { - GAME_CONTROL_SHIELD_DEADLY, + GAME_PANEL_SHIELD_DEADLY, &game.panel.shield_deadly, TYPE_ELEMENT, }, { - GAME_CONTROL_SHIELD_DEADLY_TIME, + GAME_PANEL_SHIELD_DEADLY_TIME, &game.panel.shield_deadly_time, TYPE_INTEGER, }, { - GAME_CONTROL_EXIT, + GAME_PANEL_EXIT, &game.panel.exit, TYPE_ELEMENT, }, { - GAME_CONTROL_EM_EXIT, - &game.panel.em_exit, - TYPE_ELEMENT, - }, - { - GAME_CONTROL_SP_EXIT, - &game.panel.sp_exit, - TYPE_ELEMENT, - }, - { - GAME_CONTROL_STEEL_EXIT, - &game.panel.steel_exit, - TYPE_ELEMENT, - }, - { - GAME_CONTROL_EM_STEEL_EXIT, - &game.panel.em_steel_exit, - TYPE_ELEMENT, - }, - { - GAME_CONTROL_EMC_MAGIC_BALL, + GAME_PANEL_EMC_MAGIC_BALL, &game.panel.emc_magic_ball, TYPE_ELEMENT, }, { - GAME_CONTROL_EMC_MAGIC_BALL_SWITCH, + GAME_PANEL_EMC_MAGIC_BALL_SWITCH, &game.panel.emc_magic_ball_switch, TYPE_ELEMENT, }, { - GAME_CONTROL_LIGHT_SWITCH, + GAME_PANEL_LIGHT_SWITCH, &game.panel.light_switch, TYPE_ELEMENT, }, { - GAME_CONTROL_LIGHT_SWITCH_TIME, + GAME_PANEL_LIGHT_SWITCH_TIME, &game.panel.light_switch_time, TYPE_INTEGER, }, { - GAME_CONTROL_TIMEGATE_SWITCH, + GAME_PANEL_TIMEGATE_SWITCH, &game.panel.timegate_switch, TYPE_ELEMENT, }, { - GAME_CONTROL_TIMEGATE_SWITCH_TIME, + GAME_PANEL_TIMEGATE_SWITCH_TIME, &game.panel.timegate_switch_time, TYPE_INTEGER, }, { - GAME_CONTROL_SWITCHGATE_SWITCH, + GAME_PANEL_SWITCHGATE_SWITCH, &game.panel.switchgate_switch, TYPE_ELEMENT, }, { - GAME_CONTROL_EMC_LENSES, + GAME_PANEL_EMC_LENSES, &game.panel.emc_lenses, TYPE_ELEMENT, }, { - GAME_CONTROL_EMC_LENSES_TIME, + GAME_PANEL_EMC_LENSES_TIME, &game.panel.emc_lenses_time, TYPE_INTEGER, }, { - GAME_CONTROL_EMC_MAGNIFIER, + GAME_PANEL_EMC_MAGNIFIER, &game.panel.emc_magnifier, TYPE_ELEMENT, }, { - GAME_CONTROL_EMC_MAGNIFIER_TIME, + GAME_PANEL_EMC_MAGNIFIER_TIME, &game.panel.emc_magnifier_time, TYPE_INTEGER, }, { - GAME_CONTROL_BALLOON_SWITCH, + GAME_PANEL_BALLOON_SWITCH, &game.panel.balloon_switch, TYPE_ELEMENT, }, { - GAME_CONTROL_DYNABOMB_NUMBER, + GAME_PANEL_DYNABOMB_NUMBER, &game.panel.dynabomb_number, TYPE_INTEGER, }, { - GAME_CONTROL_DYNABOMB_SIZE, + GAME_PANEL_DYNABOMB_SIZE, &game.panel.dynabomb_size, TYPE_INTEGER, }, { - GAME_CONTROL_DYNABOMB_POWER, + GAME_PANEL_DYNABOMB_POWER, &game.panel.dynabomb_power, TYPE_ELEMENT, }, { - GAME_CONTROL_PENGUINS, + GAME_PANEL_PENGUINS, &game.panel.penguins, TYPE_INTEGER, }, { - GAME_CONTROL_SOKOBAN_OBJECTS, + GAME_PANEL_SOKOBAN_OBJECTS, &game.panel.sokoban_objects, TYPE_INTEGER, }, { - GAME_CONTROL_SOKOBAN_FIELDS, + GAME_PANEL_SOKOBAN_FIELDS, &game.panel.sokoban_fields, TYPE_INTEGER, }, { - GAME_CONTROL_ROBOT_WHEEL, + GAME_PANEL_ROBOT_WHEEL, &game.panel.robot_wheel, TYPE_ELEMENT, }, { - GAME_CONTROL_CONVEYOR_BELT_1, - &game.panel.conveyor_belt_1, + GAME_PANEL_CONVEYOR_BELT_1, + &game.panel.conveyor_belt[0], TYPE_ELEMENT, }, { - GAME_CONTROL_CONVEYOR_BELT_1_SWITCH, - &game.panel.conveyor_belt_1_switch, + GAME_PANEL_CONVEYOR_BELT_2, + &game.panel.conveyor_belt[1], TYPE_ELEMENT, }, { - GAME_CONTROL_CONVEYOR_BELT_2, - &game.panel.conveyor_belt_2, + GAME_PANEL_CONVEYOR_BELT_3, + &game.panel.conveyor_belt[2], TYPE_ELEMENT, }, { - GAME_CONTROL_CONVEYOR_BELT_2_SWITCH, - &game.panel.conveyor_belt_2_switch, + GAME_PANEL_CONVEYOR_BELT_4, + &game.panel.conveyor_belt[3], TYPE_ELEMENT, }, { - GAME_CONTROL_CONVEYOR_BELT_3, - &game.panel.conveyor_belt_3, + GAME_PANEL_CONVEYOR_BELT_1_SWITCH, + &game.panel.conveyor_belt_switch[0], TYPE_ELEMENT, }, { - GAME_CONTROL_CONVEYOR_BELT_3_SWITCH, - &game.panel.conveyor_belt_3_switch, + GAME_PANEL_CONVEYOR_BELT_2_SWITCH, + &game.panel.conveyor_belt_switch[1], TYPE_ELEMENT, }, { - GAME_CONTROL_CONVEYOR_BELT_4, - &game.panel.conveyor_belt_4, + GAME_PANEL_CONVEYOR_BELT_3_SWITCH, + &game.panel.conveyor_belt_switch[2], TYPE_ELEMENT, }, { - GAME_CONTROL_CONVEYOR_BELT_4_SWITCH, - &game.panel.conveyor_belt_4_switch, + GAME_PANEL_CONVEYOR_BELT_4_SWITCH, + &game.panel.conveyor_belt_switch[3], TYPE_ELEMENT, }, { - GAME_CONTROL_MAGIC_WALL, + GAME_PANEL_MAGIC_WALL, &game.panel.magic_wall, TYPE_ELEMENT, }, { - GAME_CONTROL_MAGIC_WALL_TIME, + GAME_PANEL_MAGIC_WALL_TIME, &game.panel.magic_wall_time, TYPE_INTEGER, }, { - GAME_CONTROL_BD_MAGIC_WALL, - &game.panel.bd_magic_wall, + GAME_PANEL_GRAVITY_STATE, + &game.panel.gravity_state, + TYPE_STRING, + }, + { + GAME_PANEL_GRAPHIC_1, + &game.panel.graphic[0], + TYPE_ELEMENT, + }, + { + GAME_PANEL_GRAPHIC_2, + &game.panel.graphic[1], + TYPE_ELEMENT, + }, + { + GAME_PANEL_GRAPHIC_3, + &game.panel.graphic[2], + TYPE_ELEMENT, + }, + { + GAME_PANEL_GRAPHIC_4, + &game.panel.graphic[3], + TYPE_ELEMENT, + }, + { + GAME_PANEL_GRAPHIC_5, + &game.panel.graphic[4], + TYPE_ELEMENT, + }, + { + GAME_PANEL_GRAPHIC_6, + &game.panel.graphic[5], + TYPE_ELEMENT, + }, + { + GAME_PANEL_GRAPHIC_7, + &game.panel.graphic[6], + TYPE_ELEMENT, + }, + { + GAME_PANEL_GRAPHIC_8, + &game.panel.graphic[7], + TYPE_ELEMENT, + }, + { + GAME_PANEL_ELEMENT_1, + &game.panel.element[0], + TYPE_ELEMENT, + }, + { + GAME_PANEL_ELEMENT_2, + &game.panel.element[1], + TYPE_ELEMENT, + }, + { + GAME_PANEL_ELEMENT_3, + &game.panel.element[2], + TYPE_ELEMENT, + }, + { + GAME_PANEL_ELEMENT_4, + &game.panel.element[3], + TYPE_ELEMENT, + }, + { + GAME_PANEL_ELEMENT_5, + &game.panel.element[4], + TYPE_ELEMENT, + }, + { + GAME_PANEL_ELEMENT_6, + &game.panel.element[5], + TYPE_ELEMENT, + }, + { + GAME_PANEL_ELEMENT_7, + &game.panel.element[6], + TYPE_ELEMENT, + }, + { + GAME_PANEL_ELEMENT_8, + &game.panel.element[7], + TYPE_ELEMENT, + }, + { + GAME_PANEL_ELEMENT_COUNT_1, + &game.panel.element_count[0], + TYPE_INTEGER, + }, + { + GAME_PANEL_ELEMENT_COUNT_2, + &game.panel.element_count[1], + TYPE_INTEGER, + }, + { + GAME_PANEL_ELEMENT_COUNT_3, + &game.panel.element_count[2], + TYPE_INTEGER, + }, + { + GAME_PANEL_ELEMENT_COUNT_4, + &game.panel.element_count[3], + TYPE_INTEGER, + }, + { + GAME_PANEL_ELEMENT_COUNT_5, + &game.panel.element_count[4], + TYPE_INTEGER, + }, + { + GAME_PANEL_ELEMENT_COUNT_6, + &game.panel.element_count[5], + TYPE_INTEGER, + }, + { + GAME_PANEL_ELEMENT_COUNT_7, + &game.panel.element_count[6], + TYPE_INTEGER, + }, + { + GAME_PANEL_ELEMENT_COUNT_8, + &game.panel.element_count[7], + TYPE_INTEGER, + }, + { + GAME_PANEL_CE_SCORE_1, + &game.panel.ce_score[0], + TYPE_INTEGER, + }, + { + GAME_PANEL_CE_SCORE_2, + &game.panel.ce_score[1], + TYPE_INTEGER, + }, + { + GAME_PANEL_CE_SCORE_3, + &game.panel.ce_score[2], + TYPE_INTEGER, + }, + { + GAME_PANEL_CE_SCORE_4, + &game.panel.ce_score[3], + TYPE_INTEGER, + }, + { + GAME_PANEL_CE_SCORE_5, + &game.panel.ce_score[4], + TYPE_INTEGER, + }, + { + GAME_PANEL_CE_SCORE_6, + &game.panel.ce_score[5], + TYPE_INTEGER, + }, + { + GAME_PANEL_CE_SCORE_7, + &game.panel.ce_score[6], + TYPE_INTEGER, + }, + { + GAME_PANEL_CE_SCORE_8, + &game.panel.ce_score[7], + TYPE_INTEGER, + }, + { + GAME_PANEL_CE_SCORE_1_ELEMENT, + &game.panel.ce_score_element[0], + TYPE_ELEMENT, + }, + { + GAME_PANEL_CE_SCORE_2_ELEMENT, + &game.panel.ce_score_element[1], + TYPE_ELEMENT, + }, + { + GAME_PANEL_CE_SCORE_3_ELEMENT, + &game.panel.ce_score_element[2], + TYPE_ELEMENT, + }, + { + GAME_PANEL_CE_SCORE_4_ELEMENT, + &game.panel.ce_score_element[3], TYPE_ELEMENT, }, { - GAME_CONTROL_DC_MAGIC_WALL, - &game.panel.dc_magic_wall, + GAME_PANEL_CE_SCORE_5_ELEMENT, + &game.panel.ce_score_element[4], TYPE_ELEMENT, }, { - GAME_CONTROL_PLAYER_NAME, + GAME_PANEL_CE_SCORE_6_ELEMENT, + &game.panel.ce_score_element[5], + TYPE_ELEMENT, + }, + { + GAME_PANEL_CE_SCORE_7_ELEMENT, + &game.panel.ce_score_element[6], + TYPE_ELEMENT, + }, + { + GAME_PANEL_CE_SCORE_8_ELEMENT, + &game.panel.ce_score_element[7], + TYPE_ELEMENT, + }, + { + GAME_PANEL_PLAYER_NAME, &game.panel.player_name, TYPE_STRING, }, { - GAME_CONTROL_LEVEL_NAME, + GAME_PANEL_LEVEL_NAME, &game.panel.level_name, TYPE_STRING, }, { - GAME_CONTROL_LEVEL_AUTHOR, + GAME_PANEL_LEVEL_AUTHOR, &game.panel.level_author, TYPE_STRING, }, @@ -616,8 +804,6 @@ static struct GameControlInfo game_controls[] = -1, } }; -#endif - /* values for delayed check of falling and moving elements and for collision */ #define CHECK_DELAY_MOVING 3 @@ -676,6 +862,9 @@ static struct GameControlInfo game_controls[] = (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \ (be) + (e) - EL_SELF) +#define GET_PLAYER_FROM_BITS(p) \ + (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0)) + #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \ ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \ (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \ @@ -783,17 +972,24 @@ static struct GameControlInfo game_controls[] = #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 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 SOUND_CTRL_ID_MUSIC 8 +#define SOUND_CTRL_ID_LOOPS 9 +#define SOUND_CTRL_ID_SIMPLE 10 -#define NUM_GAME_BUTTONS 6 +#define NUM_GAME_BUTTONS 11 /* forward declaration for internal use */ static void CreateField(int, int, int); +static void ResetGfxAnimation(int, int); + static void SetPlayerWaiting(struct PlayerInfo *, boolean); static void AdvanceFrameAndPlayerCounters(int); @@ -802,7 +998,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); @@ -814,9 +1013,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); @@ -849,7 +1045,6 @@ static void PlayLevelSoundActionIfLoop(int, int, int); static void StopLevelSoundActionIfLoop(int, int, int); static void PlayLevelMusic(); -static void MapGameButtons(); static void HandleGameButtons(struct GadgetInfo *); int AmoebeNachbarNr(int, int); @@ -869,14 +1064,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); @@ -909,6 +1102,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 */ @@ -1003,11 +1198,7 @@ static struct ChangingElementInfo change_delay_list[] = }, { EL_EM_EXIT_CLOSING, -#if 1 EL_EMPTY, -#else - EL_EM_EXIT_CLOSED, -#endif 29, NULL, NULL, @@ -1023,11 +1214,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, @@ -1467,6 +1654,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]) @@ -1511,20 +1699,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) @@ -1677,18 +1876,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; @@ -1712,16 +1899,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)) { @@ -1737,7 +1950,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); @@ -1747,7 +1960,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]; @@ -1768,8 +1981,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 : @@ -1779,7 +1990,7 @@ static int get_key_element_from_nr(int key_nr) return key_base_element + key_nr; } -static int get_next_drop_element(struct PlayerInfo *player) +static int get_next_dropped_element(struct PlayerInfo *player) { return (player->inventory_size > 0 ? player->inventory_element[player->inventory_size - 1] : @@ -1790,7 +2001,7 @@ static int get_next_drop_element(struct PlayerInfo *player) EL_UNDEFINED); } -static int get_drop_element_from_pos(struct PlayerInfo *player, int pos) +static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos) { /* pos >= 0: get element from bottom of the stack; pos < 0: get element from top of the stack */ @@ -1806,7 +2017,7 @@ static int get_drop_element_from_pos(struct PlayerInfo *player, int pos) player->inventory_infinite_element != EL_UNDEFINED ? player->inventory_infinite_element : player->dynabombs_left >= min_dynabombs_left ? - EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr : + EL_DYNABOMB_PLAYER_1 + player->index_nr : EL_UNDEFINED); } else @@ -1818,25 +2029,49 @@ static int get_drop_element_from_pos(struct PlayerInfo *player, int pos) return (player->inventory_infinite_element != EL_UNDEFINED ? player->inventory_infinite_element : player->dynabombs_left >= min_dynabombs_left ? - EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr : + EL_DYNABOMB_PLAYER_1 + player->index_nr : player->inventory_size >= min_inventory_size ? player->inventory_element[inventory_pos] : EL_UNDEFINED); } } +static int compareGamePanelOrderInfo(const void *object1, const void *object2) +{ + const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1; + const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2; + int compare_result; + + if (gpo1->sort_priority != gpo2->sort_priority) + compare_result = gpo1->sort_priority - gpo2->sort_priority; + else + compare_result = gpo1->nr - gpo2->nr; + + return compare_result; +} + void InitGameControlValues() { int i; - for (i = 0; game_controls[i].nr != -1; i++) + for (i = 0; game_panel_controls[i].nr != -1; i++) { - int nr = game_controls[i].nr; - int type = game_controls[i].type; - struct TextPosInfo *pos = game_controls[i].pos; + struct GamePanelControlInfo *gpc = &game_panel_controls[i]; + struct GamePanelOrderInfo *gpo = &game_panel_order[i]; + struct TextPosInfo *pos = gpc->pos; + int nr = gpc->nr; + int type = gpc->type; + + if (nr != i) + { + Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i); + Error(ERR_EXIT, "this should not happen -- please debug"); + } /* force update of game controls after initialization */ - game_control_value[nr] = last_game_control_value[nr] = -1; + gpc->value = gpc->last_value = -1; + gpc->frame = gpc->last_frame = -1; + gpc->gfx_frame = -1; /* determine panel value width for later calculation of alignment */ if (type == TYPE_INTEGER || type == TYPE_STRING) @@ -1849,41 +2084,124 @@ void InitGameControlValues() pos->width = pos->size; pos->height = pos->size; } + + /* fill structure for game panel draw order */ + gpo->nr = gpc->nr; + gpo->sort_priority = pos->sort_priority; + } + + /* sort game panel controls according to sort_priority and control number */ + qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS, + sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo); +} + +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; + + SCAN_PLAYFIELD(x, y) + { + element_info[Feld[x][y]].element_count++; } + + for (i = 0; i < NUM_GROUP_ELEMENTS; i++) + for (j = 0; j < MAX_NUM_ELEMENTS; j++) + if (IS_IN_GROUP(j, i)) + element_info[EL_GROUP_START + i].element_count += + element_info[j].element_count; } void UpdateGameControlValues() { int i, k; - int time = (level.time == 0 ? TimePlayed : TimeLeft); - int score = (local_player->LevelSolved ? local_player->score_final : + int time = (local_player->LevelSolved ? + local_player->LevelSolved_CountingTime : + level.game_engine_type == GAME_ENGINE_TYPE_EM ? + level.native_em_level->lev->time : + level.game_engine_type == GAME_ENGINE_TYPE_SP ? + level.native_sp_level->game_sp->time_played : + 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 : local_player->score); - - game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr; - game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed; - - game_control_value[GAME_CONTROL_INVENTORY_COUNT] = 0; + 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 : + 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 : + local_player->gems_still_needed > 0 || + local_player->sokobanfields_still_needed > 0 || + local_player->lights_still_needed > 0); + + UpdatePlayfieldElementCount(); + + /* update game panel control values */ + + game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr; + game_panel_controls[GAME_PANEL_GEMS].value = gems; + + game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0; for (i = 0; i < MAX_NUM_KEYS; i++) - game_control_value[GAME_CONTROL_KEY_1 + i] = EL_EMPTY; - game_control_value[GAME_CONTROL_KEY_WHITE] = EL_EMPTY; - game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0; + game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY; + game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY; + game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0; if (game.centered_player_nr == -1) { 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 (stored_player[i].key[k]) - game_control_value[GAME_CONTROL_KEY_1 + k] = + { + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + { + if (level.native_em_level->ply[i]->keys & (1 << k)) + game_panel_controls[GAME_PANEL_KEY_1 + k].value = + get_key_element_from_nr(k); + } + else if (stored_player[i].key[k]) + game_panel_controls[GAME_PANEL_KEY_1 + k].value = get_key_element_from_nr(k); + } - game_control_value[GAME_CONTROL_INVENTORY_COUNT] += - stored_player[i].inventory_size; + 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 if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += + level.native_sp_level->game_sp->red_disk_count; + else + game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += + stored_player[i].inventory_size; if (stored_player[i].num_white_keys > 0) - game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE; + game_panel_controls[GAME_PANEL_KEY_WHITE].value = + EL_DC_KEY_WHITE; - game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] += + game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value += stored_player[i].num_white_keys; } } @@ -1892,674 +2210,391 @@ void UpdateGameControlValues() int player_nr = game.centered_player_nr; for (k = 0; k < MAX_NUM_KEYS; k++) - if (stored_player[player_nr].key[k]) - game_control_value[GAME_CONTROL_KEY_1 + k] = + { + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + { + if (level.native_em_level->ply[player_nr]->keys & (1 << k)) + game_panel_controls[GAME_PANEL_KEY_1 + k].value = + get_key_element_from_nr(k); + } + else if (stored_player[player_nr].key[k]) + game_panel_controls[GAME_PANEL_KEY_1 + k].value = get_key_element_from_nr(k); + } - game_control_value[GAME_CONTROL_INVENTORY_COUNT] += - stored_player[player_nr].inventory_size; + 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 if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += + level.native_sp_level->game_sp->red_disk_count; + else + game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value += + stored_player[player_nr].inventory_size; if (stored_player[player_nr].num_white_keys > 0) - game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE; + game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE; - game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] += + game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value += stored_player[player_nr].num_white_keys; } - for (i = 0; i < 8; i++) + for (i = 0; i < NUM_PANEL_INVENTORY; i++) { - game_control_value[GAME_CONTROL_INVENTORY_FIRST_1 + i] = - get_drop_element_from_pos(local_player, i); - game_control_value[GAME_CONTROL_INVENTORY_LAST_1 + i] = - get_drop_element_from_pos(local_player, -i - 1); + game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value = + get_inventory_element_from_pos(local_player, i); + game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value = + get_inventory_element_from_pos(local_player, -i - 1); } - game_control_value[GAME_CONTROL_SCORE] = score; + 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; - game_control_value[GAME_CONTROL_TIME] = time; + game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600; + game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60; + game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60; - game_control_value[GAME_CONTROL_TIME_HH] = time / 3600; - game_control_value[GAME_CONTROL_TIME_MM] = (time / 60) % 60; - game_control_value[GAME_CONTROL_TIME_SS] = time % 60; + game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter; - game_control_value[GAME_CONTROL_SHIELD_NORMAL] = + game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value = (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE : EL_EMPTY); - game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] = + game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value = local_player->shield_normal_time_left; - game_control_value[GAME_CONTROL_SHIELD_DEADLY] = + game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value = (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE : EL_EMPTY); - game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] = + game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value = local_player->shield_deadly_time_left; - if (local_player->gems_still_needed > 0 || - local_player->sokobanfields_still_needed > 0 || - local_player->lights_still_needed > 0) - { - game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_CLOSED; - game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_CLOSED; - game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_CLOSED; - game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_CLOSED; - game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED; - } - else - { - game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_OPEN; - game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_OPEN; - game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_OPEN; - game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_OPEN; - game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN; - } + game_panel_controls[GAME_PANEL_EXIT].value = + (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN); - /* !!! TODO !!! */ - game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED; + game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value = + (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL); + game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value = + (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE : + EL_EMC_MAGIC_BALL_SWITCH); - game_control_value[GAME_CONTROL_LIGHT_SWITCH] = + game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value = (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH); - game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left; + game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value = + game.light_time_left; - game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] = + game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value = (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED); - game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] = + game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value = game.timegate_time_left; - /* !!! TODO !!! */ - game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED; + game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value = + EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos; - game_control_value[GAME_CONTROL_EMC_LENSES] = + game_panel_controls[GAME_PANEL_EMC_LENSES].value = (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY); - game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left; + game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value = + game.lenses_time_left; - game_control_value[GAME_CONTROL_EMC_MAGNIFIER] = + game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value = (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY); - game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left; + game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value = + game.magnify_time_left; - game_control_value[GAME_CONTROL_BALLOON_SWITCH] = + game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value = (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT : game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT : game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP : game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN : EL_BALLOON_SWITCH_NONE); - game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] = + game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value = local_player->dynabomb_count; - game_control_value[GAME_CONTROL_DYNABOMB_SIZE] = + game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value = local_player->dynabomb_size; - game_control_value[GAME_CONTROL_DYNABOMB_POWER] = + game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value = (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY); - game_control_value[GAME_CONTROL_PENGUINS] = + game_panel_controls[GAME_PANEL_PENGUINS].value = local_player->friends_still_needed; - game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] = + game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value = local_player->sokobanfields_still_needed; - game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] = + game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value = local_player->sokobanfields_still_needed; - /* !!! TODO !!! */ - game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED; - - /* !!! TODO !!! */ - game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED; - - /* !!! TODO !!! */ - game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] = + game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value = + (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL); + + for (i = 0; i < NUM_BELTS; i++) + { + game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value = + (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE : + EL_CONVEYOR_BELT_1_MIDDLE) + i; + game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value = + getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]); + } + + game_panel_controls[GAME_PANEL_MAGIC_WALL].value = + (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL); + game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value = game.magic_wall_time_left; - game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED; - game_control_value[GAME_CONTROL_PLAYER_NAME] = 0; - game_control_value[GAME_CONTROL_LEVEL_NAME] = 0; - game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0; -} + game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = + local_player->gravity; -void DisplayGameControlValues() -{ - int i; + for (i = 0; i < NUM_PANEL_GRAPHICS; i++) + game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i; - game_status = GAME_MODE_PSEUDO_PANEL; + for (i = 0; i < NUM_PANEL_ELEMENTS; i++) + game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value = + (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ? + game.panel.element[i].id : EL_UNDEFINED); - for (i = 0; game_controls[i].nr != -1; i++) - { - int nr = game_controls[i].nr; - int type = game_controls[i].type; - struct TextPosInfo *pos = game_controls[i].pos; - int value = game_control_value[nr]; - int last_value = last_game_control_value[nr]; - int size = pos->size; - int font = pos->font; + 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 : 0); - if (value == last_value) - continue; + for (i = 0; i < NUM_PANEL_CE_SCORE; i++) + game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = + (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ? + element_info[game.panel.ce_score[i].id].collect_score : 0); - last_game_control_value[nr] = value; + for (i = 0; i < NUM_PANEL_CE_SCORE; i++) + game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value = + (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ? + element_info[game.panel.ce_score_element[i].id].collect_score : + EL_UNDEFINED); -#if 0 - printf("::: value %d changed from %d to %d\n", nr, last_value, value); -#endif + game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0; + game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0; + game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0; - if (PANEL_DEACTIVATED(pos)) - continue; + /* update game panel control frames */ - if (type == TYPE_INTEGER) + for (i = 0; game_panel_controls[i].nr != -1; i++) + { + struct GamePanelControlInfo *gpc = &game_panel_controls[i]; + + if (gpc->type == TYPE_ELEMENT) { - if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME) + if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY) { - boolean use_dynamic_size = (size == -1 ? TRUE : FALSE); + int last_anim_random_frame = gfx.anim_random_frame; + int element = gpc->value; + int graphic = el2panelimg(element); - if (use_dynamic_size) /* use dynamic number of digits */ + if (gpc->value != gpc->last_value) { - int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000); - int size1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3); - int size2 = size1 + 1; - int font1 = pos->font; - int font2 = pos->font_alt; - - size = (value < value_change ? size1 : size2); - font = (value < value_change ? font1 : font2); - - /* 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); - } + gpc->gfx_frame = 0; + gpc->gfx_random = INIT_GFX_RANDOM(); } + else + { + gpc->gfx_frame++; - pos->width = size * getFontWidth(font); - } - - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font); - } - else if (type == TYPE_ELEMENT) - { - int dst_x = PANEL_XPOS(pos); - int dst_y = PANEL_YPOS(pos); - - if (value == EL_UNDEFINED || value == EL_EMPTY) - { - int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos); - int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos); + if (ANIM_MODE(graphic) == ANIM_RANDOM && + IS_NEXT_FRAME(gpc->gfx_frame, graphic)) + gpc->gfx_random = INIT_GFX_RANDOM(); + } - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, - size, size, dst_x, dst_y); - } - else - { - int graphic = el2panelimg(value); + if (ANIM_MODE(graphic) == ANIM_RANDOM) + gfx.anim_random_frame = gpc->gfx_random; - DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, size); - } - } - else if (type == TYPE_STRING) - { - char *s = (nr == GAME_CONTROL_PLAYER_NAME ? setup.player_name : - nr == GAME_CONTROL_LEVEL_NAME ? level.name : - nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL); + if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + gpc->gfx_frame = element_info[element].collect_score; - if (s != NULL) - { - char *s_cut = getStringCopyN(s, size); + gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value), + gpc->gfx_frame); - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font); - - free(s_cut); + if (ANIM_MODE(graphic) == ANIM_RANDOM) + gfx.anim_random_frame = last_anim_random_frame; } } - - redraw_mask |= REDRAW_DOOR_1; } - - game_status = GAME_MODE_PLAYING; -} - -void DrawGameValue_Emeralds(int value) -{ - 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)) - return; - - pos->width = chars * font_width; - - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); -} - -void DrawGameValue_Dynamite(int value) -{ - 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 - - if (PANEL_DEACTIVATED(pos)) - return; - - pos->width = chars * font_width; - - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); } -void DrawGameValue_Score(int value) -{ - 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; - -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif - - if (PANEL_DEACTIVATED(pos)) - return; - - pos->width = chars * font_width; - - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); -} - -void DrawGameValue_Time(int value) +void DisplayGameControlValues() { - 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; + boolean redraw_panel = FALSE; + int i; - if (use_dynamic_chars) /* use dynamic number of chars */ + for (i = 0; game_panel_controls[i].nr != -1; i++) { - chars = (value < 1000 ? chars1 : chars2); - font_nr = (value < 1000 ? font1_nr : font2_nr); - } + struct GamePanelControlInfo *gpc = &game_panel_controls[i]; - /* 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)); + if (PANEL_DEACTIVATED(gpc->pos)) + continue; - pos->width = max_width; + if (gpc->value == gpc->last_value && + gpc->frame == gpc->last_frame) + continue; - ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), - max_width, max_height); + redraw_panel = TRUE; } - 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)) + if (!redraw_panel) 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); + /* copy default game door content to main double buffer */ - 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; + /* !!! CHECK AGAIN !!! */ + SetPanelBackground(); + // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL); + DrawBackground(DX, DY, DXSIZE, DYSIZE); -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif + /* redraw game control buttons */ + RedrawGameButtons(); -#if 0 - if (PANEL_DEACTIVATED(pos)) - return; -#endif + SetGameStatus(GAME_MODE_PSEUDO_PANEL); -#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 + for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++) { -#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 + int nr = game_panel_order[i].nr; + struct GamePanelControlInfo *gpc = &game_panel_controls[nr]; + struct TextPosInfo *pos = gpc->pos; + int type = gpc->type; + int value = gpc->value; + int frame = gpc->frame; + int size = pos->size; + int font = pos->font; + boolean draw_masked = pos->draw_masked; + int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE); -#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 */ + gpc->last_value = value; + gpc->last_frame = frame; - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, - MINI_TILEX, MINI_TILEY, dst_x, dst_y); - - if (key[i]) + if (type == TYPE_INTEGER) { -#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 (nr == GAME_PANEL_LEVEL_NUMBER || + nr == GAME_PANEL_TIME) + { + boolean use_dynamic_size = (size == -1 ? TRUE : FALSE); - if (PANEL_DEACTIVATED(game.panel.gems)) - return; + if (use_dynamic_size) /* use dynamic number of digits */ + { + int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000); + int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3); + int size2 = size1 + 1; + int font1 = pos->font; + int font2 = pos->font_alt; - DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr); -} + size = (value < value_change ? size1 : size2); + font = (value < value_change ? font1 : font2); + } + } -void DrawGameValue_Dynamite(int value) -{ - int font_nr = FONT_TEXT_2; - int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2; + /* correct text size if "digits" is zero or less */ + if (size <= 0) + size = strlen(int2str(value, size)); - if (PANEL_DEACTIVATED(game.panel.inventory_count)) - return; + /* dynamically correct text alignment */ + pos->width = size * getFontWidth(font); - DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr); -} + DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), + int2str(value, size), font, mask_mode); + } + else if (type == TYPE_ELEMENT) + { + int element, graphic; + Bitmap *src_bitmap; + int src_x, src_y; + int width, height; + int dst_x = PANEL_XPOS(pos); + int dst_y = PANEL_YPOS(pos); -void DrawGameValue_Score(int value) -{ - int font_nr = FONT_TEXT_2; - int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2; + if (value != EL_UNDEFINED && value != EL_EMPTY) + { + element = value; + graphic = el2panelimg(value); - if (PANEL_DEACTIVATED(game.panel.score)) - return; + // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size); - DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr); -} + if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0) + size = TILESIZE; -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; + getSizedGraphicSource(graphic, frame, size, &src_bitmap, + &src_x, &src_y); - if (PANEL_DEACTIVATED(game.panel.time)) - return; + width = graphic_info[graphic].width * size / TILESIZE; + height = graphic_info[graphic].height * size / TILESIZE; - /* clear background if value just changed its size */ - if (value == 999 || value == 1000) - ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14); + 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) + { + boolean active = (value != 0); + char *state_normal = "off"; + char *state_active = "on"; + char *state = (active ? state_active : state_normal); + char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state : + nr == GAME_PANEL_PLAYER_NAME ? setup.player_name : + nr == GAME_PANEL_LEVEL_NAME ? level.name : + nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL); - 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); -} + if (nr == GAME_PANEL_GRAVITY_STATE) + { + int font1 = pos->font; /* (used for normal state) */ + int font2 = pos->font_alt; /* (used for active state) */ -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 + font = (active ? font2 : font1); + } - if (PANEL_DEACTIVATED(game.panel.level)) - return; + if (s != NULL) + { + char *s_cut; - 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); -} + if (size <= 0) + { + /* don't truncate output if "chars" is zero or less */ + size = strlen(s); -void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) -{ - int base_key_graphic = EL_KEY_1; - int i; + /* dynamically correct text alignment */ + pos->width = size * getFontWidth(font); + } - if (PANEL_DEACTIVATED(game.panel.keys)) - return; + s_cut = getStringCopyN(s, size); - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - base_key_graphic = EL_EM_KEY_1; + DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), + s_cut, font, mask_mode); - /* 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; + free(s_cut); + } + } - 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); + redraw_mask |= REDRAW_DOOR_1; } -} -#endif + SetGameStatus(GAME_MODE_PLAYING); +} -void DrawAllGameValues(int emeralds, int dynamite, int score, int time, - int key_bits) +void UpdateAndDisplayGameControlValues() { - 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) + if (tape.deactivate_display) 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); + UpdateGameControlValues(); + DisplayGameControlValues(); } -void DrawGameDoorValues() +void UpdateGameDoorValues() { UpdateGameControlValues(); - DisplayGameControlValues(); } -void DrawGameDoorValues_OLD() +void DrawGameDoorValues() { - 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) - { - DrawGameDoorValues_EM(); - - 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; - - 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; - } - - DrawAllGameValues(gems_value, dynamite_value, score_value, time_value, - key_bits); + DisplayGameControlValues(); } @@ -2579,6 +2614,21 @@ static void InitGameEngine() game.engine_version = (tape.playing ? tape.engine_version : level.game_version); + /* set single or multi-player game mode (needed for re-playing tapes) */ + game.team_mode = setup.team_mode; + + if (tape.playing) + { + int num_players = 0; + + for (i = 0; i < MAX_PLAYERS; i++) + if (tape.player_participates[i]) + num_players++; + + /* multi-player tapes contain input data for more than one player */ + game.team_mode = (num_players > 1); + } + /* ---------------------------------------------------------------------- */ /* set flags for bugs and changes according to active game engine version */ /* ---------------------------------------------------------------------- */ @@ -2645,39 +2695,10 @@ 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 - - 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.) - - 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); @@ -2761,7 +2782,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++) { @@ -2799,6 +2820,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++) @@ -2808,7 +2848,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 = 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; @@ -2995,6 +3036,20 @@ 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; } int get_num_special_action(int element, int action_first, int action_last) @@ -3031,15 +3086,46 @@ 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; + // required here to update video display before fading (FIX THIS) + DrawMaskedBorder(REDRAW_DOOR_2); + + if (!game.restart_level) + CloseDoor(DOOR_CLOSE_1); + + SetGameStatus(GAME_MODE_PLAYING); + + if (level_editor_test_game) + FadeSkipNextFadeIn(); + else + FadeSetEnterScreen(); + + if (CheckIfGlobalBorderHasChanged()) + fade_mask = REDRAW_ALL; + + FadeSoundsAndMusic(); + + 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(); @@ -3057,7 +3143,10 @@ void InitGame() player->present = FALSE; player->active = FALSE; + player->mapped = FALSE; + player->killed = FALSE; + player->reanimated = FALSE; player->action = 0; player->effective_action = 0; @@ -3081,18 +3170,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() */ @@ -3105,7 +3195,7 @@ void InitGame() player->step_counter = 0; - player->last_move_dir = MV_NONE; + player->last_move_dir = initial_move_dir; player->is_active = FALSE; @@ -3123,13 +3213,18 @@ 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->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; @@ -3162,6 +3257,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); @@ -3173,6 +3288,10 @@ void InitGame() player->LevelSolved_PanelOff = FALSE; player->LevelSolved_SaveTape = FALSE; player->LevelSolved_SaveScore = FALSE; + player->LevelSolved_CountingTime = 0; + player->LevelSolved_CountingScore = 0; + + map_player_action[i] = i; } network_player_action_received = FALSE; @@ -3200,7 +3319,10 @@ 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; game.magic_wall_time_left = 0; game.light_time_left = 0; @@ -3208,11 +3330,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; @@ -3242,15 +3359,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; @@ -3275,6 +3397,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) @@ -3287,6 +3410,8 @@ void InitGame() emulate_sp = FALSE; InitField(x, y, TRUE); + + ResetGfxAnimation(x, y); } InitBeltMovement(); @@ -3308,7 +3433,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++) { @@ -3326,7 +3450,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++) @@ -3346,14 +3469,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 */ @@ -3361,6 +3476,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++) { @@ -3370,24 +3643,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; @@ -3396,14 +3671,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; @@ -3413,8 +3720,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 */ @@ -3443,26 +3751,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) { @@ -3479,12 +3800,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) @@ -3594,89 +3919,73 @@ void InitGame() local_player->jy - MIDPOSY); } - 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 - /* !!! 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 { - 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); - DrawGameDoorValues(); + UpdateAndDisplayGameControlValues(); if (!game.restart_level) { UnmapGameButtons(); UnmapTapeButtons(); + + FreeGameButtons(); + CreateGameButtons(); + 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; + 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); @@ -3687,31 +3996,65 @@ 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; + + 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) @@ -3919,6 +4262,10 @@ static void PlayerWins(struct PlayerInfo *player) player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->score : player->score); + + player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : + TimeLeft); + player->LevelSolved_CountingScore = player->score_final; } void GameWon() @@ -3942,17 +4289,22 @@ 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; - time = time_final = (level.time == 0 ? TimePlayed : TimeLeft); + time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft); score = score_final = local_player->score_final; if (TimeLeft > 0) @@ -3960,7 +4312,7 @@ void GameWon() time_final = 0; score_final += TimeLeft * level.score[SC_TIME_BONUS]; } - else if (level.time == 0 && TimePlayed < 999) + else if (game.no_time_limit && TimePlayed < 999) { time_final = 999; score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS]; @@ -3973,15 +4325,13 @@ void GameWon() time = time_final; score = score_final; -#if 1 - game_control_value[GAME_CONTROL_TIME] = time; - game_control_value[GAME_CONTROL_SCORE] = score; + local_player->LevelSolved_CountingTime = time; + local_player->LevelSolved_CountingScore = score; + + game_panel_controls[GAME_PANEL_TIME].value = time; + 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) @@ -3998,24 +4348,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 */ @@ -4055,15 +4395,13 @@ void GameWon() time += time_count_steps * time_count_dir; score += time_count_steps * level.score[SC_TIME_BONUS]; -#if 1 - game_control_value[GAME_CONTROL_TIME] = time; - game_control_value[GAME_CONTROL_SCORE] = score; + local_player->LevelSolved_CountingTime = time; + local_player->LevelSolved_CountingScore = score; + + game_panel_controls[GAME_PANEL_TIME].value = time; + 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); @@ -4084,9 +4422,7 @@ void GameWon() return; } -#if 1 GameEnd(); -#endif } void GameEnd() @@ -4096,43 +4432,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 - - game_status = GAME_MODE_MAIN; + SetGameStatus(GAME_MODE_MAIN); - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); return; } @@ -4140,6 +4463,7 @@ void GameEnd() if (level_nr == leveldir_current->handicap_level) { leveldir_current->handicap_level++; + SaveLevelSetup_SeriesInfo(); } @@ -4148,7 +4472,7 @@ void GameEnd() if ((hi_pos = NewHiScore()) >= 0) { - game_status = GAME_MODE_SCORES; + SetGameStatus(GAME_MODE_SCORES); DrawHallOfFame(hi_pos); @@ -4160,11 +4484,7 @@ void GameEnd() } else { -#if 1 - FadeOut(REDRAW_FIELD); -#endif - - game_status = GAME_MODE_MAIN; + SetGameStatus(GAME_MODE_MAIN); if (raise_level) { @@ -4172,7 +4492,7 @@ void GameEnd() TapeErase(); } - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); } } @@ -4267,11 +4587,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; @@ -4279,7 +4594,6 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir) } } -#if USE_GFX_RESET_GFX_ANIMATION static void ResetGfxFrame(int x, int y, boolean redraw) { int element = Feld[x][y]; @@ -4298,7 +4612,6 @@ static void ResetGfxFrame(int x, int y, boolean redraw) if (redraw && GfxFrame[x][y] != last_gfx_frame) DrawLevelGraphicAnimation(x, y, graphic); } -#endif static void ResetGfxAnimation(int x, int y) { @@ -4306,9 +4619,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 } static void ResetRandomAnimationValue(int x, int y) @@ -4324,51 +4635,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" */ @@ -4379,9 +4663,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]; @@ -4464,19 +4746,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; @@ -4505,7 +4781,7 @@ void RemoveMovingField(int x, int y) Store[oldx][oldy] = Store2[oldx][oldy] = 0; - DrawLevelField(oldx, oldy); + TEST_DrawLevelField(oldx, oldy); return; } @@ -4534,8 +4810,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) @@ -4636,166 +4912,107 @@ 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; + /* case 1: quick relocation inside visible screen (without scrolling) */ - if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen) - { - if (!level.shifted_relocation || center_screen) - { - /* quick relocation (without scrolling), with centering of screen */ + RedrawPlayfield(); + + return; + } + + if (!level.shifted_relocation || center_screen) + { + /* relocation _with_ centering of screen */ - scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : + new_scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : x > SBX_Right + MIDPOSX ? SBX_Right : x - MIDPOSX); - scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper : + new_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 */ + } + else + { + /* relocation _without_ centering of 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 = (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); + int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper : + old_y > SBY_Lower + MIDPOSY ? SBY_Lower : + old_y - MIDPOSY); - int offset_x = x + (scroll_x - center_scroll_x); - int offset_y = y + (scroll_y - center_scroll_y); + int offset_x = x + (scroll_x - center_scroll_x); + int offset_y = y + (scroll_y - center_scroll_y); - scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left : + new_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 : + new_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); - - /* 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); - } - - RedrawPlayfield(TRUE, 0,0,0,0); } - 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); - - 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_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper : - old_y > SBY_Lower + MIDPOSY ? SBY_Lower : - old_y - MIDPOSY); - - 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) @@ -4845,8 +5062,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 */ @@ -4867,10 +5083,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 */ @@ -4886,6 +5105,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) @@ -4908,23 +5140,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; @@ -4954,12 +5169,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; @@ -5096,31 +5309,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)) @@ -5192,18 +5383,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; @@ -5217,7 +5406,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) { @@ -5294,8 +5483,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]) { @@ -5333,10 +5521,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; @@ -5406,12 +5590,19 @@ static void InitBeltMovement() for (j = 0; j < NUM_BELT_PARTS; j++) { int element = belt_base_active_element[belt_nr] + j; - int graphic = el2img(element); + int graphic_1 = el2img(element); + int graphic_2 = el2panelimg(element); if (game.belt_dir[i] == MV_LEFT) - graphic_info[graphic].anim_mode &= ~ANIM_REVERSE; + { + graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE; + graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE; + } else - graphic_info[graphic].anim_mode |= ANIM_REVERSE; + { + graphic_info[graphic_1].anim_mode |= ANIM_REVERSE; + graphic_info[graphic_2].anim_mode |= ANIM_REVERSE; + } } } @@ -5487,12 +5678,19 @@ static void ToggleBeltSwitch(int x, int y) for (i = 0; i < NUM_BELT_PARTS; i++) { int element = belt_base_active_element[belt_nr] + i; - int graphic = el2img(element); + int graphic_1 = el2img(element); + int graphic_2 = el2panelimg(element); if (belt_dir == MV_LEFT) - graphic_info[graphic].anim_mode &= ~ANIM_REVERSE; + { + graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE; + graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE; + } else - graphic_info[graphic].anim_mode |= ANIM_REVERSE; + { + graphic_info[graphic_1].anim_mode |= ANIM_REVERSE; + graphic_info[graphic_2].anim_mode |= ANIM_REVERSE; + } } SCAN_PLAYFIELD(xx, yy) @@ -5506,7 +5704,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) @@ -5518,7 +5716,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) @@ -5530,7 +5728,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); } } } @@ -5546,41 +5744,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) { @@ -5626,25 +5809,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 || @@ -5653,11 +5836,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 || @@ -5666,11 +5849,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); } } } @@ -5687,13 +5870,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 || @@ -5702,11 +5885,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 || @@ -5715,11 +5898,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); } } } @@ -5736,13 +5919,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) @@ -5753,8 +5936,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) @@ -5765,8 +5950,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); } } } @@ -5803,18 +5990,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) @@ -5845,7 +6028,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; } @@ -5855,7 +6038,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; } @@ -5879,7 +6062,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)) @@ -6040,10 +6223,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, @@ -6345,7 +6524,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) && @@ -6353,7 +6531,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; } @@ -6361,12 +6539,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; } @@ -6970,10 +7142,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; @@ -6984,6 +7164,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); } } @@ -7007,10 +7214,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; @@ -7021,6 +7236,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); } } @@ -7059,7 +7301,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]) { @@ -7087,7 +7329,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]) { @@ -7115,7 +7357,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]) { @@ -7156,13 +7398,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))) || @@ -7222,73 +7459,30 @@ 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) can_fall_right = FALSE; else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both) - can_fall_left = FALSE; - else if (slippery_type == SLIPPERY_ONLY_LEFT) - can_fall_right = FALSE; - else if (slippery_type == SLIPPERY_ONLY_RIGHT) - can_fall_left = FALSE; - - 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) - can_fall_right = FALSE; /* slip down on left side */ - else - can_fall_left = !(can_fall_right = RND(2)); + can_fall_left = FALSE; + else if (slippery_type == SLIPPERY_ONLY_LEFT) + can_fall_right = FALSE; + else if (slippery_type == SLIPPERY_ONLY_RIGHT) + can_fall_left = FALSE; + can_fall_any = (can_fall_left || can_fall_right); can_fall_both = FALSE; } -#else + if (can_fall_both) { - if (game.emulation == EMU_BOULDERDASH || - element == EL_BD_ROCK || element == EL_BD_DIAMOND) + if (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) { @@ -7297,11 +7491,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)); @@ -7329,26 +7519,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)) @@ -7388,7 +7563,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); } } @@ -7422,7 +7597,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); @@ -7441,20 +7616,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; @@ -7462,7 +7627,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); } } @@ -7470,7 +7635,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); } } } @@ -7513,7 +7678,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))) @@ -7529,7 +7694,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; } @@ -7540,7 +7705,7 @@ void StartMoving(int x, int y) if (IS_PLAYER(x, y)) DrawPlayerField(x, y); else - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); return; } @@ -7554,7 +7719,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); @@ -7564,7 +7729,7 @@ void StartMoving(int x, int y) if (IS_PLAYER(x, y)) DrawPlayerField(x, y); else - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); return; } @@ -7643,78 +7808,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) { @@ -7729,7 +7837,7 @@ void StartMoving(int x, int y) if (IS_PLAYER(x, y)) DrawPlayerField(x, y); else - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); return; } @@ -7756,31 +7864,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; } @@ -7794,7 +7888,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); @@ -7810,22 +7904,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); @@ -7848,7 +7934,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 */ @@ -7857,7 +7943,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); } } @@ -7874,21 +7960,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); @@ -7928,18 +7999,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 */ } @@ -7958,7 +8018,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) { @@ -7994,9 +8054,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) { @@ -8012,9 +8070,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) { @@ -8030,9 +8086,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) { @@ -8066,18 +8120,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]; @@ -8088,34 +8138,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; @@ -8125,7 +8156,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); @@ -8141,8 +8172,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 */ @@ -8170,10 +8201,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 */ @@ -8187,6 +8216,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) && @@ -8211,29 +8245,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); @@ -8404,7 +8415,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; } } @@ -8417,8 +8428,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 */ { @@ -8446,15 +8457,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 */ { @@ -8478,7 +8489,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) */ @@ -8505,7 +8516,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; } @@ -8575,7 +8586,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 */ @@ -8639,7 +8650,7 @@ void AmoebeAbleger(int ax, int ay) return; } - DrawLevelField(newax, neway); + TEST_DrawLevelField(newax, neway); } void Life(int ax, int ay) @@ -8697,7 +8708,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; } @@ -8710,7 +8721,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; } @@ -8735,7 +8746,11 @@ static void RunRobotWheel(int x, int y) static void StopRobotWheel(int x, int y) { if (ZX == x && ZY == y) + { ZX = ZY = -1; + + game.robot_wheel_active = FALSE; + } } static void InitTimegateWheel(int x, int y) @@ -8750,11 +8765,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) @@ -8934,9 +8945,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) @@ -8945,18 +8953,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); - } } } } @@ -8985,28 +8981,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); } } } @@ -9097,7 +9093,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; @@ -9215,7 +9211,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); @@ -9261,7 +9257,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; @@ -9325,7 +9321,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) @@ -9359,6 +9355,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) @@ -9370,11 +9367,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 && @@ -9403,11 +9405,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); @@ -9419,11 +9417,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 : @@ -9431,10 +9425,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 = @@ -9451,15 +9448,14 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg_number_min, action_arg_number_max); 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); + (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 -------------------------------------------- */ @@ -9497,13 +9493,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { TimeLeft = action_arg_number_new; -#if 1 - game_control_value[GAME_CONTROL_TIME] = TimeLeft; + 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++) @@ -9517,13 +9509,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { local_player->score = action_arg_number_new; -#if 1 - game_control_value[GAME_CONTROL_SCORE] = local_player->score; + game_panel_controls[GAME_PANEL_SCORE].value = local_player->score; DisplayGameControlValues(); -#else - DrawGameValue_Score(local_player->score); -#endif break; } @@ -9532,31 +9520,27 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { local_player->gems_still_needed = action_arg_number_new; -#if 1 - game_control_value[GAME_CONTROL_GEMS] = 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; } @@ -9678,7 +9662,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++) @@ -9695,7 +9678,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } -#endif case CA_SET_PLAYER_ARTWORK: { @@ -9710,10 +9692,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; @@ -9732,11 +9712,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; @@ -9752,14 +9829,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; @@ -9795,7 +9870,38 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) } } } -#endif + + break; + } + + 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; } @@ -9819,32 +9925,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)) @@ -9854,56 +9941,35 @@ 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)) - { - Bang(x, y); - - return; - } -#else - if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && + /* 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 (IS_PLAYER(x, y) && !player_explosion_protected && 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) @@ -9926,7 +9992,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]; @@ -9937,7 +10002,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); } @@ -9959,7 +10023,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 = 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; @@ -10112,13 +10177,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) && @@ -10135,11 +10199,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; } @@ -10149,19 +10208,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); @@ -10201,6 +10297,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)) @@ -10210,92 +10313,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, @@ -10310,12 +10332,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++) @@ -10340,7 +10356,8 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)) { change->actual_trigger_element = trigger_element; - change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player); + change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player); + change->actual_trigger_player_bits = trigger_player; change->actual_trigger_side = trigger_side; change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y]; change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element); @@ -10355,24 +10372,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 } } @@ -10411,11 +10436,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) || @@ -10424,13 +10444,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); @@ -10446,10 +10459,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] && @@ -10459,7 +10469,8 @@ static boolean CheckElementChangeExt(int x, int y, IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))) { change->actual_trigger_element = trigger_element; - change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player); + change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player); + change->actual_trigger_player_bits = trigger_player; change->actual_trigger_side = trigger_side; change->actual_trigger_ce_value = CustomValue[x][y]; change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element); @@ -10497,19 +10508,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 } } @@ -10700,9 +10703,51 @@ 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) + { + 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; @@ -10718,23 +10763,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); @@ -10758,6 +10796,8 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) player->is_dropping_pressed = FALSE; player->drop_pressed_delay = 0; + CheckSingleStepMode(player); + return 0; } } @@ -10766,6 +10806,7 @@ 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 */ @@ -10783,6 +10824,21 @@ 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; + } if (TimeFrames >= FRAMES_PER_SECOND) { @@ -10813,13 +10869,10 @@ static void CheckLevelTime() if (TimeLeft <= 10 && setup.time_limit) PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); -#if 1 - game_control_value[GAME_CONTROL_TIME] = 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) { @@ -10830,27 +10883,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_control_value[GAME_CONTROL_TIME] = TimePlayed; - - DisplayGameControlValues(); + game_panel_controls[GAME_PANEL_TIME].value = TimePlayed; } -#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); } - DrawGameDoorValues(); + if (tape.recording || tape.playing) + DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); + + UpdateAndDisplayGameControlValues(); } void AdvanceFrameAndPlayerCounters(int player_nr) @@ -10871,7 +10920,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; @@ -10882,7 +10930,6 @@ void AdvanceFrameAndPlayerCounters(int player_nr) if (count % delay == 0) move_frames = 1; } -#endif stored_player[i].Frame += move_frames; @@ -10905,9 +10952,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); @@ -10924,10 +10971,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]; @@ -10936,9 +10985,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)); @@ -10953,8 +11002,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 */ @@ -10972,6 +11022,21 @@ 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; + } if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd) GameWon(); @@ -10988,9 +11053,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) { @@ -11021,11 +11099,9 @@ void GameActions() /* when playing tape, read previously recorded player input from tape data */ recorded_player_action = (tape.playing ? TapePlayAction() : NULL); -#if 1 /* TapePlayAction() may return NULL when toggling to "pause before death" */ if (tape.pausing) return; -#endif if (tape.set_centered_player) { @@ -11037,7 +11113,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; } @@ -11046,41 +11122,150 @@ 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 (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 = + (i == game.centered_player_nr ? summarized_player_action : 0); + } + + if (recorded_player_action != NULL) + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].effective_action = recorded_player_action[i]; + + for (i = 0; i < MAX_PLAYERS; i++) + { + tape_action[i] = stored_player[i].effective_action; + + /* (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; + } + + /* 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 + { + GameActions_RND_Main(); + } + + BlitScreenToBitmap(backbuffer); + + CheckLevelTime(); + + AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */ + + if (options.debug) /* calculate 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(); + } - if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1) - { - for (i = 0; i < MAX_PLAYERS; i++) - stored_player[i].effective_action = - (i == game.centered_player_nr ? summarized_player_action : 0); + redraw_mask |= REDRAW_FPS; } +} - if (recorded_player_action != NULL) - for (i = 0; i < MAX_PLAYERS; i++) - stored_player[i].effective_action = recorded_player_action[i]; +static void GameActions_CheckSaveEngineSnapshot() +{ + if (!game.snapshot.save_snapshot) + return; - for (i = 0; i < MAX_PLAYERS; i++) - { - tape_action[i] = stored_player[i].effective_action; + // clear flag for saving snapshot _before_ saving snapshot + game.snapshot.save_snapshot = FALSE; - /* (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 */ - } + SaveEngineSnapshotToList(); +} - /* only record actions from input devices, but not programmed actions */ - if (tape.recording) - TapeRecordAction(tape_action); +void GameActions() +{ + GameActionsExt(); - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - { - GameActions_EM_Main(); - } - else - { - GameActions_RND(); - } + GameActions_CheckSaveEngineSnapshot(); } void GameActions_EM_Main() @@ -11093,10 +11278,23 @@ 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); +} + +void GameActions_RND_Main() +{ + GameActions_RND(); } void GameActions_RND() @@ -11106,7 +11304,6 @@ void GameActions_RND() InitPlayfieldScanModeVars(); -#if USE_ONE_MORE_CHANGE_PER_FRAME if (game.engine_version >= VERSION_IDENT(3,2,0,7)) { SCAN_PLAYFIELD(x, y) @@ -11115,7 +11312,6 @@ void GameActions_RND() ChangeEvent[x][y] = -1; } } -#endif if (game.set_centered_player) { @@ -11215,10 +11411,6 @@ void GameActions_RND() } } -#if 0 - debug_print_timestamp(0, "start main loop profiling"); -#endif - SCAN_PLAYFIELD(x, y) { ChangeCount[x][y] = 0; @@ -11232,19 +11424,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) @@ -11273,7 +11463,7 @@ void GameActions_RND() if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y)) { ResetGfxAnimation(x, y); - DrawLevelField(x, y); + TEST_DrawLevelField(x, y); } #if DEBUG @@ -11293,63 +11483,6 @@ void GameActions_RND() #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]; @@ -11379,157 +11512,12 @@ void GameActions_RND() { 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 - - case EL_GAME_OF_LIFE: - case EL_BIOMAZE: - Life(x, y); - break; - - case EL_EXIT_CLOSED: - CheckExit(x, y); - break; - - case EL_EM_EXIT_CLOSED: - CheckExitEM(x, y); - break; - - case EL_STEEL_EXIT_CLOSED: - CheckExitSteel(x, y); - break; - - case EL_EM_STEEL_EXIT_CLOSED: - CheckExitSteelEM(x, y); - break; - - case EL_SP_EXIT_CLOSED: - CheckExitSP(x, y); - break; - - case EL_EXPANDABLE_WALL_GROWING: - case EL_EXPANDABLE_STEELWALL_GROWING: - MauerWaechst(x, y); - break; - - 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; - - case EL_EXPANDABLE_STEELWALL_HORIZONTAL: - case EL_EXPANDABLE_STEELWALL_VERTICAL: - case EL_EXPANDABLE_STEELWALL_ANY: - MauerAblegerStahl(x, y); - break; - - case EL_FLAMES: - CheckForDragon(x, y); - break; - - case EL_EXPLOSION: - break; - - 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]); - - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - break; - } - - default: - if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - break; - } } -#else // --------------------------------------------------------------------- - if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element))) { StartMoving(x, y); @@ -11543,7 +11531,7 @@ 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 || element == EL_EXIT_OPEN || @@ -11612,8 +11600,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); @@ -11639,15 +11625,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++) { @@ -11675,27 +11657,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)) @@ -11717,6 +11694,7 @@ void GameActions_RND() if (game.magic_wall_time_left > 0) { game.magic_wall_time_left--; + if (!game.magic_wall_time_left) { SCAN_PLAYFIELD(x, y) @@ -11727,19 +11705,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); } } @@ -11793,31 +11771,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) { @@ -11826,11 +11806,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); @@ -11877,76 +11852,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) { @@ -12009,11 +11924,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; @@ -12035,11 +11946,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 = @@ -12066,9 +11973,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; @@ -12095,36 +11999,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) @@ -12154,9 +12031,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 */ @@ -12208,7 +12083,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 @@ -12223,7 +12098,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; @@ -12242,7 +12117,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; @@ -12251,19 +12125,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 + game.centered_player_nr == -1)) { int old_scroll_x = scroll_x, old_scroll_y = scroll_y; int offset = game.scroll_delay_value; @@ -12318,7 +12186,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()) { @@ -12326,14 +12193,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); @@ -12350,7 +12209,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; @@ -12401,16 +12260,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) { @@ -12429,13 +12283,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) */ @@ -12445,17 +12294,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; @@ -12465,14 +12309,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 */ { @@ -12495,8 +12331,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 */ { @@ -12535,7 +12373,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); } @@ -12566,40 +12404,34 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (TimeLeft <= 10 && setup.time_limit) PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); -#if 1 - game_control_value[GAME_CONTROL_TIME] = TimeLeft; + 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_control_value[GAME_CONTROL_TIME] = TimePlayed; + 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) { @@ -12663,7 +12495,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); @@ -12681,8 +12513,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); @@ -12697,13 +12540,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] = @@ -12769,88 +12622,40 @@ void TestIfElementTouchesCustomElement(int x, int y) /* 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); + /* (center element cannot be player, so we dont have to check this here) */ } -} - -#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 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); - /* 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); @@ -12881,11 +12686,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); + } } } @@ -12894,69 +12711,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; @@ -13058,6 +12812,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; @@ -13088,12 +12843,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; } } @@ -13116,6 +12873,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); @@ -13187,6 +13001,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() @@ -13211,8 +13030,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) @@ -13253,7 +13086,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) @@ -13266,7 +13099,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]; @@ -13285,7 +13117,6 @@ static void setFieldForSnapping(int x, int y, int element, int direction) GfxDir[x][y] = direction; GfxFrame[x][y] = -1; } -#endif /* ============================================================================= @@ -13324,9 +13155,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; @@ -13342,11 +13173,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 */ @@ -13369,11 +13196,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]; @@ -13388,7 +13210,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); @@ -13401,25 +13222,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; @@ -13446,17 +13258,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)) @@ -13481,8 +13286,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) { @@ -13553,29 +13360,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 */ @@ -13607,14 +13402,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); @@ -13638,13 +13429,9 @@ int DigField(struct PlayerInfo *player, { TimeLeft += level.extra_time; -#if 1 - game_control_value[GAME_CONTROL_TIME] = TimeLeft; + game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif } else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY) { @@ -13723,13 +13510,11 @@ int DigField(struct PlayerInfo *player, if (local_player->gems_still_needed < 0) local_player->gems_still_needed = 0; -#if 1 - game_control_value[GAME_CONTROL_GEMS] = 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 } RaiseScoreElement(element); @@ -13741,14 +13526,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); @@ -13805,8 +13586,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)) @@ -13826,6 +13609,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) @@ -13852,7 +13642,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); @@ -13909,7 +13699,9 @@ int DigField(struct PlayerInfo *player, ZX = x; ZY = y; - DrawLevelField(x, y); + game.robot_wheel_active = TRUE; + + TEST_DrawLevelField(x, y); } else if (element == EL_SP_TERMINAL) { @@ -13918,9 +13710,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)) @@ -13964,7 +13763,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) { @@ -13973,18 +13772,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_control_value[GAME_CONTROL_TIME] = TimeLeft; + 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) @@ -14066,7 +13862,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; @@ -14103,14 +13947,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; @@ -14139,30 +13978,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_drop_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 + int drop_element = get_next_dropped_element(player); player->is_dropping_pressed = TRUE; @@ -14268,23 +14097,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); @@ -14577,42 +14396,24 @@ void PlayLevelSound_EM(int xx, int yy, int element_em, int sample) } } -#if 0 -void ChangeTime(int value) -{ - int *time = (level.time == 0 ? &TimePlayed : &TimeLeft); - - *time += value; - - /* EMC game engine uses value from time counter of RND game engine */ - level.native_em_level->lev->time = *time; - - DrawGameValue_Time(*time); -} - -void RaiseScore(int value) +void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp) { - /* 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); - - *score += value; + 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; - DrawGameValue_Score(*score); + PlayLevelSoundElementAction(x, y, element, action); } -#endif void RaiseScore(int value) { local_player->score += value; -#if 1 - game_control_value[GAME_CONTROL_SCORE] = local_player->score; + game_panel_controls[GAME_PANEL_SCORE].value = local_player->score; DisplayGameControlValues(); -#else - DrawGameValue_Score(local_player->score); -#endif } void RaiseScoreElement(int element) @@ -14699,6 +14500,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); @@ -14706,37 +14511,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 */ @@ -14757,7 +14536,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?"); } @@ -14765,22 +14544,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) @@ -14800,8 +14568,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 */ @@ -14811,32 +14577,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] = @@ -14870,15 +14618,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++) @@ -14899,8 +14647,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; } @@ -14922,110 +14670,109 @@ 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); /* 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)); /* 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) @@ -15037,134 +14784,156 @@ 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; + + return save_snapshot; +} - if (engine_snapshot_list == NULL) +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(); +} + +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) +/* ---------- new game button stuff ---------------------------------------- */ static struct { - int *x, *y; - int gd_x, gd_y; + int graphic; + struct XY *pos; int gadget_id; 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, "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, "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, "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, "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, "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 - { - GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS, - GAME_CTRL_ID_STOP, - "stop game" + IMG_GFX_GAME_BUTTON_SAVE, &game.button.save, + GAME_CTRL_ID_SAVE, "save game" }, { - GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS, - GAME_CTRL_ID_PAUSE, - "pause game" + IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2, + GAME_CTRL_ID_PAUSE2, "pause game" }, { - GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS, - GAME_CTRL_ID_PLAY, - "play game" + IMG_GFX_GAME_BUTTON_LOAD, &game.button.load, + GAME_CTRL_ID_LOAD, "load game" }, { - SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS, - SOUND_CTRL_ID_MUSIC, - "background music on/off" + IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music, + SOUND_CTRL_ID_MUSIC, "background music on/off" }, { - SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS, - SOUND_CTRL_ID_LOOPS, - "sound loops on/off" + IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops, + SOUND_CTRL_ID_LOOPS, "sound loops on/off" }, { - SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS, - SOUND_CTRL_ID_SIMPLE, - "normal sounds on/off" + IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple, + SOUND_CTRL_ID_SIMPLE, "normal sounds on/off" } -#endif }; void CreateGameButtons() @@ -15173,32 +14942,46 @@ 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; + int base_x = (tape.show_game_buttons ? VX : DX); + int base_y = (tape.show_game_buttons ? 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_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 { @@ -15208,28 +14991,22 @@ void CreateGameButtons() (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) || (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : 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, GDI_END); @@ -15249,12 +15026,80 @@ void FreeGameButtons() FreeGadget(game_gadget[i]); } -static void MapGameButtons() +static void UnmapGameButtonsAtSamePosition(int id) +{ + int i; + + for (i = 0; i < NUM_GAME_BUTTONS; 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]); +} + +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); + } +} + +static void MapGameButtonsAtSamePosition(int id) +{ + int i; + + for (i = 0; i < NUM_GAME_BUTTONS; 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 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 MapGameButtons() { int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - MapGadget(game_gadget[i]); + if (i != GAME_CTRL_ID_UNDO && + i != GAME_CTRL_ID_REDO) + MapGadget(game_gadget[i]); + + UnmapGameButtonsAtSamePosition_All(); + + RedrawGameButtons(); } void UnmapGameButtons() @@ -15265,24 +15110,81 @@ void UnmapGameButtons() UnmapGadget(game_gadget[i]); } -static void HandleGameButtons(struct GadgetInfo *gi) +void RedrawGameButtons() +{ + int i; + + for (i = 0; i < NUM_GAME_BUTTONS; i++) + RedrawGadget(game_gadget[i]); + + // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area + redraw_mask &= ~REDRAW_ALL; +} + +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; + + LoadEngineSnapshot_Redo(steps); + + GameUndoRedoExt(); +} + +static void HandleGameButtonsExt(int id, int button) { - int id = gi->custom_id; + 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 (game_status != GAME_MODE_PLAYING) + if (!handle_game_buttons) return; switch (id) { case GAME_CTRL_ID_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: + if (options.network && game_status == GAME_MODE_PLAYING) { #if defined(NETWORK_AVALIABLE) if (tape.pausing) @@ -15293,27 +15195,59 @@ static void HandleGameButtons(struct GadgetInfo *gi) } else TapeTogglePause(TAPE_TOGGLE_MANUAL); + + game_undo_executed = FALSE; + break; case GAME_CTRL_ID_PLAY: - if (tape.pausing) + 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: if (setup.sound_music) { setup.sound_music = FALSE; + FadeMusic(); } else if (audio.music_available) @@ -15332,6 +15266,7 @@ static void HandleGameButtons(struct GadgetInfo *gi) else if (audio.loops_available) { setup.sound = setup.sound_loops = TRUE; + SetAudioMode(setup.sound); } break; @@ -15342,6 +15277,7 @@ static void HandleGameButtons(struct GadgetInfo *gi) else if (audio.sound_available) { setup.sound = setup.sound_simple = TRUE; + SetAudioMode(setup.sound); } break; @@ -15350,3 +15286,19 @@ static void HandleGameButtons(struct GadgetInfo *gi) 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); +}