X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=d580212fd8e725218ce9cb3cdc68ceff033ecbbc;hp=6148015ddeac6d4cc5a60ea170e91a66cb6e9303;hb=44c787009717e23436775bb7f4e3a5115eaba214;hpb=c0f3e2e3c8f0c8b88fca68375d0b942bda8ffcbf diff --git a/src/game.c b/src/game.c index 6148015d..d580212f 100644 --- a/src/game.c +++ b/src/game.c @@ -57,6 +57,9 @@ #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) /* for DigField() */ @@ -84,27 +87,33 @@ #define EX_TYPE_DYNA (1 << 4) #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER) -#define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0) +#define PANEL_OFF() (local_player->LevelSolved_PanelOff) +#define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF()) +#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 (game.panel.level.x) -#define XX_LEVEL2 (game.panel.level.x - 1) -#define YY_LEVEL (game.panel.level.y) -#define XX_EMERALDS (game.panel.gems.x) -#define YY_EMERALDS (game.panel.gems.y) -#define XX_DYNAMITE (game.panel.inventory.x) -#define YY_DYNAMITE (game.panel.inventory.y) -#define XX_KEYS (game.panel.keys.x) -#define YY_KEYS (game.panel.keys.y) -#define XX_SCORE (game.panel.score.x) -#define YY_SCORE (game.panel.score.y) -#define XX_TIME1 (game.panel.time.x) -#define XX_TIME2 (game.panel.time.x + 1) -#define YY_TIME (game.panel.time.y) +#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) @@ -116,12 +125,457 @@ #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_CONTROL_LEVEL_NUMBER 0 +#define GAME_CONTROL_GEMS 1 +#define GAME_CONTROL_INVENTORY 2 +#define GAME_CONTROL_KEY_1 3 +#define GAME_CONTROL_KEY_2 4 +#define GAME_CONTROL_KEY_3 5 +#define GAME_CONTROL_KEY_4 6 +#define GAME_CONTROL_KEY_5 7 +#define GAME_CONTROL_KEY_6 8 +#define GAME_CONTROL_KEY_7 9 +#define GAME_CONTROL_KEY_8 10 +#define GAME_CONTROL_KEY_WHITE 11 +#define GAME_CONTROL_KEY_WHITE_COUNT 12 +#define GAME_CONTROL_SCORE 13 +#define GAME_CONTROL_TIME 14 +#define GAME_CONTROL_TIME_HH 15 +#define GAME_CONTROL_TIME_MM 16 +#define GAME_CONTROL_TIME_SS 17 +#define GAME_CONTROL_DROP_NEXT_1 18 +#define GAME_CONTROL_DROP_NEXT_2 19 +#define GAME_CONTROL_DROP_NEXT_3 20 +#define GAME_CONTROL_DROP_NEXT_4 21 +#define GAME_CONTROL_DROP_NEXT_5 22 +#define GAME_CONTROL_DROP_NEXT_6 23 +#define GAME_CONTROL_DROP_NEXT_7 24 +#define GAME_CONTROL_DROP_NEXT_8 25 +#define GAME_CONTROL_SHIELD_NORMAL 26 +#define GAME_CONTROL_SHIELD_NORMAL_TIME 27 +#define GAME_CONTROL_SHIELD_DEADLY 28 +#define GAME_CONTROL_SHIELD_DEADLY_TIME 29 +#define GAME_CONTROL_EXIT 30 +#define GAME_CONTROL_EM_EXIT 31 +#define GAME_CONTROL_SP_EXIT 32 +#define GAME_CONTROL_STEEL_EXIT 33 +#define GAME_CONTROL_EM_STEEL_EXIT 34 +#define GAME_CONTROL_EMC_MAGIC_BALL 35 +#define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH 36 +#define GAME_CONTROL_LIGHT_SWITCH 37 +#define GAME_CONTROL_LIGHT_SWITCH_TIME 38 +#define GAME_CONTROL_TIMEGATE_SWITCH 39 +#define GAME_CONTROL_TIMEGATE_SWITCH_TIME 40 +#define GAME_CONTROL_SWITCHGATE_SWITCH 41 +#define GAME_CONTROL_EMC_LENSES 42 +#define GAME_CONTROL_EMC_LENSES_TIME 43 +#define GAME_CONTROL_EMC_MAGNIFIER 44 +#define GAME_CONTROL_EMC_MAGNIFIER_TIME 45 +#define GAME_CONTROL_BALLOON_SWITCH 46 +#define GAME_CONTROL_DYNABOMB_NUMBER 47 +#define GAME_CONTROL_DYNABOMB_SIZE 48 +#define GAME_CONTROL_DYNABOMB_POWER 49 +#define GAME_CONTROL_PENGUINS 50 +#define GAME_CONTROL_SOKOBAN_OBJECTS 51 +#define GAME_CONTROL_SOKOBAN_FIELDS 52 +#define GAME_CONTROL_ROBOT_WHEEL 53 +#define GAME_CONTROL_CONVEYOR_BELT_1 54 +#define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 55 +#define GAME_CONTROL_CONVEYOR_BELT_2 56 +#define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 57 +#define GAME_CONTROL_CONVEYOR_BELT_3 58 +#define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 59 +#define GAME_CONTROL_CONVEYOR_BELT_4 60 +#define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 61 +#define GAME_CONTROL_MAGIC_WALL 62 +#define GAME_CONTROL_MAGIC_WALL_TIME 63 +#define GAME_CONTROL_BD_MAGIC_WALL 64 +#define GAME_CONTROL_DC_MAGIC_WALL 65 +#define GAME_CONTROL_PLAYER_NAME 66 +#define GAME_CONTROL_LEVEL_NAME 67 +#define GAME_CONTROL_LEVEL_AUTHOR 68 + +#define NUM_GAME_CONTROLS 69 + +int game_control_value[NUM_GAME_CONTROLS]; +int last_game_control_value[NUM_GAME_CONTROLS]; + +struct GameControlInfo +{ + int nr; + + struct TextPosInfo *pos; + int type; +}; + +static struct GameControlInfo game_controls[] = +{ + { + GAME_CONTROL_LEVEL_NUMBER, + &game.panel.level_number, + TYPE_INTEGER, + }, + { + GAME_CONTROL_GEMS, + &game.panel.gems, + TYPE_INTEGER, + }, + { + GAME_CONTROL_INVENTORY, + &game.panel.inventory, + TYPE_INTEGER, + }, + { + GAME_CONTROL_KEY_1, + &game.panel.key[0], + TYPE_ELEMENT, + }, + { + GAME_CONTROL_KEY_2, + &game.panel.key[1], + TYPE_ELEMENT, + }, + { + GAME_CONTROL_KEY_3, + &game.panel.key[2], + TYPE_ELEMENT, + }, + { + GAME_CONTROL_KEY_4, + &game.panel.key[3], + TYPE_ELEMENT, + }, + { + GAME_CONTROL_KEY_5, + &game.panel.key[4], + TYPE_ELEMENT, + }, + { + GAME_CONTROL_KEY_6, + &game.panel.key[5], + TYPE_ELEMENT, + }, + { + GAME_CONTROL_KEY_7, + &game.panel.key[6], + TYPE_ELEMENT, + }, + { + GAME_CONTROL_KEY_8, + &game.panel.key[7], + TYPE_ELEMENT, + }, + { + GAME_CONTROL_KEY_WHITE, + &game.panel.key_white, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_KEY_WHITE_COUNT, + &game.panel.key_white_count, + TYPE_INTEGER, + }, + { + GAME_CONTROL_SCORE, + &game.panel.score, + TYPE_INTEGER, + }, + { + GAME_CONTROL_TIME, + &game.panel.time, + TYPE_INTEGER, + }, + { + GAME_CONTROL_TIME_HH, + &game.panel.time_hh, + TYPE_INTEGER, + }, + { + GAME_CONTROL_TIME_MM, + &game.panel.time_mm, + TYPE_INTEGER, + }, + { + GAME_CONTROL_TIME_SS, + &game.panel.time_ss, + TYPE_INTEGER, + }, + { + GAME_CONTROL_DROP_NEXT_1, + &game.panel.drop_next_1, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_DROP_NEXT_2, + &game.panel.drop_next_2, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_DROP_NEXT_3, + &game.panel.drop_next_3, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_DROP_NEXT_4, + &game.panel.drop_next_4, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_DROP_NEXT_5, + &game.panel.drop_next_5, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_DROP_NEXT_6, + &game.panel.drop_next_6, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_DROP_NEXT_7, + &game.panel.drop_next_7, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_DROP_NEXT_8, + &game.panel.drop_next_8, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_SHIELD_NORMAL, + &game.panel.shield_normal, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_SHIELD_NORMAL_TIME, + &game.panel.shield_normal_time, + TYPE_INTEGER, + }, + { + GAME_CONTROL_SHIELD_DEADLY, + &game.panel.shield_deadly, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_SHIELD_DEADLY_TIME, + &game.panel.shield_deadly_time, + TYPE_INTEGER, + }, + { + GAME_CONTROL_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, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_EMC_MAGIC_BALL_SWITCH, + &game.panel.emc_magic_ball_switch, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_LIGHT_SWITCH, + &game.panel.light_switch, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_LIGHT_SWITCH_TIME, + &game.panel.light_switch_time, + TYPE_INTEGER, + }, + { + GAME_CONTROL_TIMEGATE_SWITCH, + &game.panel.timegate_switch, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_TIMEGATE_SWITCH_TIME, + &game.panel.timegate_switch_time, + TYPE_INTEGER, + }, + { + GAME_CONTROL_SWITCHGATE_SWITCH, + &game.panel.switchgate_switch, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_EMC_LENSES, + &game.panel.emc_lenses, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_EMC_LENSES_TIME, + &game.panel.emc_lenses_time, + TYPE_INTEGER, + }, + { + GAME_CONTROL_EMC_MAGNIFIER, + &game.panel.emc_magnifier, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_EMC_MAGNIFIER_TIME, + &game.panel.emc_magnifier_time, + TYPE_INTEGER, + }, + { + GAME_CONTROL_BALLOON_SWITCH, + &game.panel.balloon_switch, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_DYNABOMB_NUMBER, + &game.panel.dynabomb_number, + TYPE_INTEGER, + }, + { + GAME_CONTROL_DYNABOMB_SIZE, + &game.panel.dynabomb_size, + TYPE_INTEGER, + }, + { + GAME_CONTROL_DYNABOMB_POWER, + &game.panel.dynabomb_power, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_PENGUINS, + &game.panel.penguins, + TYPE_INTEGER, + }, + { + GAME_CONTROL_SOKOBAN_OBJECTS, + &game.panel.sokoban_objects, + TYPE_INTEGER, + }, + { + GAME_CONTROL_SOKOBAN_FIELDS, + &game.panel.sokoban_fields, + TYPE_INTEGER, + }, + { + GAME_CONTROL_ROBOT_WHEEL, + &game.panel.robot_wheel, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_CONVEYOR_BELT_1, + &game.panel.conveyor_belt_1, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_CONVEYOR_BELT_1_SWITCH, + &game.panel.conveyor_belt_1_switch, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_CONVEYOR_BELT_2, + &game.panel.conveyor_belt_2, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_CONVEYOR_BELT_2_SWITCH, + &game.panel.conveyor_belt_2_switch, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_CONVEYOR_BELT_3, + &game.panel.conveyor_belt_3, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_CONVEYOR_BELT_3_SWITCH, + &game.panel.conveyor_belt_3_switch, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_CONVEYOR_BELT_4, + &game.panel.conveyor_belt_4, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_CONVEYOR_BELT_4_SWITCH, + &game.panel.conveyor_belt_4_switch, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_MAGIC_WALL, + &game.panel.magic_wall, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_MAGIC_WALL_TIME, + &game.panel.magic_wall_time, + TYPE_INTEGER, + }, + { + GAME_CONTROL_BD_MAGIC_WALL, + &game.panel.bd_magic_wall, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_DC_MAGIC_WALL, + &game.panel.dc_magic_wall, + TYPE_ELEMENT, + }, + { + GAME_CONTROL_PLAYER_NAME, + &game.panel.player_name, + TYPE_STRING, + }, + { + GAME_CONTROL_LEVEL_NAME, + &game.panel.level_name, + TYPE_STRING, + }, + { + GAME_CONTROL_LEVEL_AUTHOR, + &game.panel.level_author, + TYPE_STRING, + }, + + { + -1, + NULL, + -1, + } +}; +#endif + + /* values for delayed check of falling and moving elements and for collision */ #define CHECK_DELAY_MOVING 3 -#define CHECK_DELAY_FALLING 3 +#define CHECK_DELAY_FALLING CHECK_DELAY_MOVING #define CHECK_DELAY_COLLISION 2 +#define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION /* values for initial player move delay (initial delay counter value) */ #define INITIAL_MOVE_DELAY_OFF -1 @@ -242,7 +696,10 @@ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y])) #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \ - ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\ + ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \ + Feld[x][y] == EL_EM_EXIT_OPEN || \ + Feld[x][y] == EL_STEEL_EXIT_OPEN || \ + Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \ IS_FOOD_PENGUIN(Feld[x][y]))) #define DRAGON_CAN_ENTER_FIELD(e, x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0) @@ -377,6 +834,33 @@ static int getInvisibleFromInvisibleActiveElement(int); static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS]; +/* for detection of endless loops, caused by custom element programming */ +/* (using maximal playfield width x 10 is just a rough approximation) */ +#define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10) + +#define RECURSION_LOOP_DETECTION_START(e, rc) \ +{ \ + if (recursion_loop_detected) \ + return (rc); \ + \ + if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \ + { \ + recursion_loop_detected = TRUE; \ + recursion_loop_element = (e); \ + } \ + \ + recursion_loop_depth++; \ +} + +#define RECURSION_LOOP_DETECTION_END() \ +{ \ + recursion_loop_depth--; \ +} + +static int recursion_loop_depth; +static boolean recursion_loop_detected; +static boolean recursion_loop_element; + /* ------------------------------------------------------------------------- */ /* definition of elements that automatically change to other elements after */ @@ -445,6 +929,62 @@ static struct ChangingElementInfo change_delay_list[] = NULL, NULL }, + { + EL_STEEL_EXIT_OPENING, + EL_STEEL_EXIT_OPEN, + 29, + NULL, + NULL, + NULL + }, + { + EL_STEEL_EXIT_CLOSING, + EL_STEEL_EXIT_CLOSED, + 29, + NULL, + NULL, + NULL + }, + { + EL_EM_EXIT_OPENING, + EL_EM_EXIT_OPEN, + 29, + NULL, + NULL, + NULL + }, + { + EL_EM_EXIT_CLOSING, +#if 1 + EL_EMPTY, +#else + EL_EM_EXIT_CLOSED, +#endif + 29, + NULL, + NULL, + NULL + }, + { + EL_EM_STEEL_EXIT_OPENING, + EL_EM_STEEL_EXIT_OPEN, + 29, + NULL, + NULL, + NULL + }, + { + EL_EM_STEEL_EXIT_CLOSING, +#if 1 + EL_STEELWALL, +#else + EL_EM_STEEL_EXIT_CLOSED, +#endif + 29, + NULL, + NULL, + NULL + }, { EL_SP_EXIT_OPENING, EL_SP_EXIT_OPEN, @@ -566,6 +1106,14 @@ static struct ChangingElementInfo change_delay_list[] = RunTimegateWheel, NULL }, + { + EL_DC_TIMEGATE_SWITCH_ACTIVE, + EL_DC_TIMEGATE_SWITCH, + 0, + InitTimegateWheel, + RunTimegateWheel, + NULL + }, { EL_EMC_MAGIC_BALL_ACTIVE, EL_EMC_MAGIC_BALL_ACTIVE, @@ -638,10 +1186,14 @@ move_stepsize_list[] = { EL_AMOEBA_DROPPING, 2 }, { EL_QUICKSAND_FILLING, 1 }, { EL_QUICKSAND_EMPTYING, 1 }, + { EL_QUICKSAND_FAST_FILLING, 2 }, + { EL_QUICKSAND_FAST_EMPTYING, 2 }, { EL_MAGIC_WALL_FILLING, 2 }, - { EL_BD_MAGIC_WALL_FILLING, 2 }, { EL_MAGIC_WALL_EMPTYING, 2 }, + { EL_BD_MAGIC_WALL_FILLING, 2 }, { EL_BD_MAGIC_WALL_EMPTYING, 2 }, + { EL_DC_MAGIC_WALL_FILLING, 2 }, + { EL_DC_MAGIC_WALL_EMPTYING, 2 }, { EL_UNDEFINED, 0 }, }; @@ -728,7 +1280,7 @@ static int playfield_scan_delta_y = 1; (y) += playfield_scan_delta_y) \ for ((x) = playfield_scan_start_x; \ (x) >= 0 && (x) <= lev_fieldx - 1; \ - (x) += playfield_scan_delta_x) \ + (x) += playfield_scan_delta_x) #ifdef DEBUG void DEBUG_SetMaximumDynamite() @@ -807,6 +1359,8 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize, void GetPlayerConfig() { + GameFrameDelay = setup.game_frame_delay; + if (!audio.sound_available) setup.sound_simple = FALSE; @@ -825,58 +1379,7 @@ void GetPlayerConfig() InitJoysticks(); } -static int getBeltNrFromBeltElement(int element) -{ - return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 : - element < EL_CONVEYOR_BELT_3_LEFT ? 1 : - element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3); -} - -static int getBeltNrFromBeltActiveElement(int element) -{ - return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 : - element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 : - element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3); -} - -static int getBeltNrFromBeltSwitchElement(int element) -{ - return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 : - element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 : - element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3); -} - -static int getBeltDirNrFromBeltSwitchElement(int element) -{ - static int belt_base_element[4] = - { - EL_CONVEYOR_BELT_1_SWITCH_LEFT, - EL_CONVEYOR_BELT_2_SWITCH_LEFT, - EL_CONVEYOR_BELT_3_SWITCH_LEFT, - EL_CONVEYOR_BELT_4_SWITCH_LEFT - }; - - int belt_nr = getBeltNrFromBeltSwitchElement(element); - int belt_dir_nr = element - belt_base_element[belt_nr]; - - return (belt_dir_nr % 3); -} - -static int getBeltDirFromBeltSwitchElement(int element) -{ - static int belt_move_dir[3] = - { - MV_LEFT, - MV_NONE, - MV_RIGHT - }; - - int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element); - - return belt_move_dir[belt_dir_nr]; -} - -static int get_element_from_group_element(int element) +int GetElementFromGroupElement(int element) { if (IS_GROUP_ELEMENT(element)) { @@ -1131,6 +1634,11 @@ static void InitField(int x, int y, boolean init_game) 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: @@ -1169,70 +1677,679 @@ static void InitField(int x, int y, boolean init_game) } else if (IS_GROUP_ELEMENT(element)) { - Feld[x][y] = get_element_from_group_element(element); + Feld[x][y] = GetElementFromGroupElement(element); InitField(x, y, init_game); } - break; - } + break; + } + + if (!init_game) + CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X); +} + +static inline void InitField_WithBug1(int x, int y, boolean init_game) +{ + InitField(x, y, init_game); + + /* not needed to call InitMovDir() -- already done by InitField()! */ + if (game.engine_version < VERSION_IDENT(3,1,0,0) && + CAN_MOVE(Feld[x][y])) + InitMovDir(x, y); +} + +static inline void InitField_WithBug2(int x, int y, boolean init_game) +{ + int old_element = Feld[x][y]; + + InitField(x, y, init_game); + + /* not needed to call InitMovDir() -- already done by InitField()! */ + if (game.engine_version < VERSION_IDENT(3,1,0,0) && + CAN_MOVE(old_element) && + (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN)) + InitMovDir(x, y); + + /* this case is in fact a combination of not less than three bugs: + first, it calls InitMovDir() for elements that can move, although this is + already done by InitField(); then, it checks the element that was at this + field _before_ the call to InitField() (which can change it); lastly, it + was not called for "mole with direction" elements, which were treated as + "cannot move" due to (fixed) wrong element initialization in "src/init.c" + */ +} + +#if 1 + +void InitGameControlValues() +{ + int i; + + for (i = 0; i < NUM_GAME_CONTROLS; i++) + game_control_value[i] = last_game_control_value[i] = -1; + + 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; + + game_control_value[nr] = last_game_control_value[nr] = -1; + + /* determine panel value width for later calculation of alignment */ + if (type == TYPE_INTEGER || type == TYPE_STRING) + pos->width = pos->chars * getFontWidth(pos->font); + else if (type == TYPE_ELEMENT) + pos->width = MINI_TILESIZE; + } +} + +void UpdateGameControlValues() +{ + int i, j; + + 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] = 0; + for (i = 0; i < MAX_NUM_KEYS; i++) + game_control_value[GAME_CONTROL_KEY_1 + i] = 0; + game_control_value[GAME_CONTROL_KEY_WHITE] = 0; + game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0; + + 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]) + game_control_value[GAME_CONTROL_KEY_1 + j] = 1; + + game_control_value[GAME_CONTROL_INVENTORY] += + stored_player[i].inventory_size; + + if (stored_player[i].num_white_keys > 0) + game_control_value[GAME_CONTROL_KEY_WHITE] = 1; + + game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] += + stored_player[i].num_white_keys; + } + } + else + { + int player_nr = game.centered_player_nr; + + for (i = 0; i < MAX_NUM_KEYS; i++) + if (stored_player[player_nr].key[i]) + game_control_value[GAME_CONTROL_KEY_1 + i] = 1; + + game_control_value[GAME_CONTROL_INVENTORY] += + stored_player[player_nr].inventory_size; + + if (stored_player[player_nr].num_white_keys > 0) + game_control_value[GAME_CONTROL_KEY_WHITE] = 1; + + game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] += + stored_player[player_nr].num_white_keys; + } + + game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ? + local_player->score_final : + local_player->score); + + game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ? + TimePlayed : + TimeLeft); + + game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600; + game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60; + game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60; + + for (i = 0; i < 8; i++) + game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = EL_UNDEFINED; + + game_control_value[GAME_CONTROL_SHIELD_NORMAL] = + (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE : + EL_EMPTY); + game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] = + local_player->shield_normal_time_left; + game_control_value[GAME_CONTROL_SHIELD_DEADLY] = + (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE : + EL_EMPTY); + game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] = + 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_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED; + game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED; + + game_control_value[GAME_CONTROL_LIGHT_SWITCH] = + (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_control_value[GAME_CONTROL_TIMEGATE_SWITCH] = + (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED); + game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] = + game.timegate_time_left; + + game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED; + + game_control_value[GAME_CONTROL_EMC_LENSES] = + (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY); + game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left; + + game_control_value[GAME_CONTROL_EMC_MAGNIFIER] = + (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY); + game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left; + + game_control_value[GAME_CONTROL_BALLOON_SWITCH] = + (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] = + local_player->dynabomb_count; + game_control_value[GAME_CONTROL_DYNABOMB_SIZE] = + local_player->dynabomb_size; + game_control_value[GAME_CONTROL_DYNABOMB_POWER] = + (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY); + + game_control_value[GAME_CONTROL_PENGUINS] = + local_player->friends_still_needed; + + game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] = + local_player->sokobanfields_still_needed; + game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] = + local_player->sokobanfields_still_needed; + + game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED; + + 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; + + game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED; + game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] = + 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; +} + +void DisplayGameControlValues() +{ + int i; + + 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 chars = pos->chars; + int font = pos->font; + + if (value == last_value) + continue; + + last_game_control_value[nr] = value; + +#if 0 + printf("::: value %d changed from %d to %d\n", nr, last_value, value); +#endif + + if (PANEL_DEACTIVATED(pos)) + continue; + + if (type == TYPE_INTEGER) + { + if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME) + { + boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE); + + if (use_dynamic_chars) /* use dynamic number of chars */ + { + int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000); + int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3); + int chars2 = chars1 + 1; + int font1 = pos->font; + int font2 = pos->font_alt; + + chars = (value < value_change ? chars1 : chars2); + font = (value < value_change ? font1 : font2); + + /* clear background if value just changed its size (dynamic chars) */ + if ((last_value < value_change) != (value < value_change)) + { + int width1 = chars1 * getFontWidth(font1); + int width2 = chars2 * 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); + } + } + + pos->width = chars * getFontWidth(font); + } + + DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font); + } + else if (type == TYPE_ELEMENT) + { + if (nr >= GAME_CONTROL_KEY_1 && nr <= GAME_CONTROL_KEY_8) + { + int key_nr = nr - GAME_CONTROL_KEY_1; + int src_x = DOOR_GFX_PAGEX5 + 18 + (key_nr % STD_NUM_KEYS) * MINI_TILEX; + int src_y = DOOR_GFX_PAGEY1 + 123; + int dst_x = PANEL_XPOS(pos); + int dst_y = PANEL_YPOS(pos); + int element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS : + level.game_engine_type == GAME_ENGINE_TYPE_EM ? + EL_EM_KEY_1 : EL_KEY_1) + key_nr; + int graphic = el2edimg(element); + + if (value) + 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 (value != EL_UNDEFINED) + { + int graphic = el2edimg(value); + int dst_x = PANEL_XPOS(pos); + int dst_y = PANEL_YPOS(pos); + + DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic); + } + } + 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 (s != NULL) + { + char *s_cut = getStringCopyN(s, pos->chars); + + DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font); + + free(s_cut); + } + } + + redraw_mask |= REDRAW_DOOR_1; + } +} + +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->chars; + +#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; +#if 1 + int font_nr = pos->font; +#else + int font_nr = FONT_TEXT_2; +#endif + int font_width = getFontWidth(font_nr); + int chars = pos->chars; + +#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->chars; + +#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) +{ + struct TextPosInfo *pos = &game.panel.time; + static int last_value = -1; + int chars1 = 3; + int chars2 = 4; + int chars = pos->chars; +#if 1 + int font1_nr = pos->font; + int font2_nr = pos->font_alt; +#else + int font1_nr = FONT_TEXT_2; + int font2_nr = FONT_TEXT_1; +#endif + int font_nr = font1_nr; + boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE); + +#if 1 + return; /* !!! USE NEW STUFF !!! */ +#endif + + if (PANEL_DEACTIVATED(pos)) + return; + + if (use_dynamic_chars) /* use dynamic number of chars */ + { + chars = (value < 1000 ? chars1 : chars2); + font_nr = (value < 1000 ? font1_nr : font2_nr); + } + + /* clear background if value just changed its size (dynamic chars only) */ + if (use_dynamic_chars && (last_value < 1000) != (value < 1000)) + { + int width1 = chars1 * getFontWidth(font1_nr); + int width2 = chars2 * getFontWidth(font2_nr); + int max_width = MAX(width1, width2); + int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr)); + + pos->width = max_width; + + ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), + max_width, max_height); + } + + pos->width = chars * getFontWidth(font_nr); + + DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); + + last_value = value; +} + +void DrawGameValue_Level(int value) +{ + struct TextPosInfo *pos = &game.panel.level_number; + int chars1 = 2; + int chars2 = 3; + int chars = pos->chars; +#if 1 + int font1_nr = pos->font; + int font2_nr = pos->font_alt; +#else + int font1_nr = FONT_TEXT_2; + int font2_nr = FONT_TEXT_1; +#endif + int font_nr = font1_nr; + boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE); + +#if 1 + return; /* !!! USE NEW STUFF !!! */ +#endif + + if (PANEL_DEACTIVATED(pos)) + return; + + if (use_dynamic_chars) /* use dynamic number of chars */ + { + chars = (level_nr < 100 ? chars1 : chars2); + font_nr = (level_nr < 100 ? font1_nr : font2_nr); + } + + pos->width = chars * getFontWidth(font_nr); + + DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); +} + +void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) +{ +#if 0 + struct TextPosInfo *pos = &game.panel.keys; +#endif +#if 0 + int base_key_graphic = EL_KEY_1; +#endif + int i; + +#if 1 + return; /* !!! USE NEW STUFF !!! */ +#endif + +#if 0 + if (PANEL_DEACTIVATED(pos)) + return; +#endif + +#if 0 + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + base_key_graphic = EL_EM_KEY_1; +#endif + +#if 0 + pos->width = 4 * MINI_TILEX; +#endif + +#if 1 + for (i = 0; i < MAX_NUM_KEYS; i++) +#else + /* currently only 4 of 8 possible keys are displayed */ + for (i = 0; i < STD_NUM_KEYS; i++) +#endif + { +#if 1 + struct TextPosInfo *pos = &game.panel.key[i]; +#endif + int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX; + int src_y = DOOR_GFX_PAGEY1 + 123; +#if 1 + int dst_x = PANEL_XPOS(pos); + int dst_y = PANEL_YPOS(pos); +#else + int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX; + int dst_y = PANEL_YPOS(pos); +#endif + +#if 1 + int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 : + level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 : + EL_KEY_1) + i; + int graphic = el2edimg(element); +#endif + +#if 1 + if (PANEL_DEACTIVATED(pos)) + continue; +#endif + +#if 0 + /* masked blit with tiles from half-size scaled bitmap does not work yet + (no mask bitmap created for these sizes after loading and scaling) -- + solution: load without creating mask, scale, then create final mask */ + + BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, + MINI_TILEX, MINI_TILEY, dst_x, dst_y); + + if (key[i]) + { +#if 0 + int graphic = el2edimg(base_key_graphic + i); +#endif + Bitmap *src_bitmap; + int src_x, src_y; + + getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y); + + SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc, + dst_x - src_x, dst_y - src_y); + BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY, + dst_x, dst_y); + } +#else +#if 1 + if (key[i]) + DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic); + else + BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, + MINI_TILEX, MINI_TILEY, dst_x, dst_y); +#else + if (key[i]) + DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i)); + else + BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, + MINI_TILEX, MINI_TILEY, dst_x, dst_y); +#endif +#endif + } +} + +#else + +void DrawGameValue_Emeralds(int value) +{ + int font_nr = FONT_TEXT_2; + int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2; + + if (PANEL_DEACTIVATED(game.panel.gems)) + return; - if (!init_game) - CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X); + DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr); } -static inline void InitField_WithBug1(int x, int y, boolean init_game) +void DrawGameValue_Dynamite(int value) { - InitField(x, y, init_game); + int font_nr = FONT_TEXT_2; + int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2; - /* not needed to call InitMovDir() -- already done by InitField()! */ - if (game.engine_version < VERSION_IDENT(3,1,0,0) && - CAN_MOVE(Feld[x][y])) - InitMovDir(x, y); + if (PANEL_DEACTIVATED(game.panel.inventory)) + return; + + DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr); } -static inline void InitField_WithBug2(int x, int y, boolean init_game) +void DrawGameValue_Score(int value) { - int old_element = Feld[x][y]; - - InitField(x, y, init_game); + int font_nr = FONT_TEXT_2; + int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2; - /* not needed to call InitMovDir() -- already done by InitField()! */ - if (game.engine_version < VERSION_IDENT(3,1,0,0) && - CAN_MOVE(old_element) && - (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN)) - InitMovDir(x, y); + if (PANEL_DEACTIVATED(game.panel.score)) + return; - /* this case is in fact a combination of not less than three bugs: - first, it calls InitMovDir() for elements that can move, although this is - already done by InitField(); then, it checks the element that was at this - field _before_ the call to InitField() (which can change it); lastly, it - was not called for "mole with direction" elements, which were treated as - "cannot move" due to (fixed) wrong element initialization in "src/init.c" - */ + DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr); } -inline void DrawGameValue_Emeralds(int value) +void DrawGameValue_Time(int value) { - int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2; + int font1_nr = FONT_TEXT_2; +#if 1 + int font2_nr = FONT_TEXT_1; +#else + int font2_nr = FONT_LEVEL_NUMBER; +#endif + int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2; + int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2; - if (PANEL_DEACTIVATED(game.panel.gems)) + if (PANEL_DEACTIVATED(game.panel.time)) return; - DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2); + /* clear background if value just changed its size */ + if (value == 999 || value == 1000) + ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14); + + if (value < 1000) + DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr); + else + DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr); } -inline void DrawGameValue_Dynamite(int value) +void DrawGameValue_Level(int value) { - int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2; + int font1_nr = FONT_TEXT_2; +#if 1 + int font2_nr = FONT_TEXT_1; +#else + int font2_nr = FONT_LEVEL_NUMBER; +#endif - if (PANEL_DEACTIVATED(game.panel.inventory)) + if (PANEL_DEACTIVATED(game.panel.level)) return; - DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2); + 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); } -inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) +void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) { int base_key_graphic = EL_KEY_1; int i; @@ -1257,44 +2374,7 @@ inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) } } -inline void DrawGameValue_Score(int value) -{ - int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2; - - if (PANEL_DEACTIVATED(game.panel.score)) - return; - - DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2); -} - -inline void DrawGameValue_Time(int value) -{ - int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2; - int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2; - - if (PANEL_DEACTIVATED(game.panel.time)) - return; - - /* clear background if value just changed its size */ - if (value == 999 || value == 1000) - ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14); - - if (value < 1000) - DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2); - else - DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER); -} - -inline void DrawGameValue_Level(int value) -{ - if (PANEL_DEACTIVATED(game.panel.level)) - return; - - if (level_nr < 100) - DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2); - else - DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER); -} +#endif void DrawAllGameValues(int emeralds, int dynamite, int score, int time, int key_bits) @@ -1321,6 +2401,12 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time, } void DrawGameDoorValues() +{ + UpdateGameControlValues(); + DisplayGameControlValues(); +} + +void DrawGameDoorValues_OLD() { int time_value = (level.time == 0 ? TimePlayed : TimeLeft); int dynamite_value = 0; @@ -1784,6 +2870,18 @@ static void InitGameEngine() EL_EMPTY); } } + + /* ---------- initialize recursion detection ------------------------------ */ + recursion_loop_depth = 0; + recursion_loop_detected = FALSE; + recursion_loop_element = EL_UNDEFINED; + + /* ---------- initialize graphics engine ---------------------------------- */ + game.scroll_delay_value = + (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value : + setup.scroll_delay ? setup.scroll_delay_value : 0); + game.scroll_delay_value = + MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY); } int get_num_special_action(int element, int action_first, int action_last) @@ -1823,12 +2921,15 @@ void InitGame() 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 i, j, x, y; game_status = GAME_MODE_PLAYING; InitGameEngine(); + InitGameControlValues(); /* don't play tapes over network */ network_playing = (options.network && !tape.playing); @@ -1843,6 +2944,7 @@ void InitGame() player->present = FALSE; player->active = FALSE; + player->killed = FALSE; player->action = 0; player->effective_action = 0; @@ -1859,6 +2961,8 @@ void InitGame() for (j = 0; j < MAX_NUM_KEYS; j++) player->key[j] = FALSE; + player->num_white_keys = 0; + player->dynabomb_count = 0; player->dynabomb_size = 1; player->dynabombs_left = 0; @@ -1951,7 +3055,9 @@ void InitGame() player->LevelSolved = FALSE; player->GameOver = FALSE; + player->LevelSolved_GameWon = FALSE; player->LevelSolved_GameEnd = FALSE; + player->LevelSolved_PanelOff = FALSE; player->LevelSolved_SaveTape = FALSE; player->LevelSolved_SaveScore = FALSE; } @@ -2037,6 +3143,7 @@ void InitGame() WasJustMoving[x][y] = 0; WasJustFalling[x][y] = 0; CheckCollision[x][y] = 0; + CheckImpact[x][y] = 0; Stop[x][y] = FALSE; Pushed[x][y] = FALSE; @@ -2303,7 +3410,8 @@ void InitGame() content = element_info[element].change_page[i].target_element; is_player = ELEM_IS_PLAYER(content); - if (is_player && (found_rating < 3 || element < found_element)) + if (is_player && (found_rating < 3 || + (found_rating == 3 && element < found_element))) { start_x = x; start_y = y; @@ -2320,7 +3428,8 @@ void InitGame() content = element_info[element].content.e[xx][yy]; is_player = ELEM_IS_PLAYER(content); - if (is_player && (found_rating < 2 || element < found_element)) + if (is_player && (found_rating < 2 || + (found_rating == 2 && element < found_element))) { start_x = x + xx - 1; start_y = y + yy - 1; @@ -2340,7 +3449,8 @@ void InitGame() is_player = ELEM_IS_PLAYER(content); - if (is_player && (found_rating < 1 || element < found_element)) + if (is_player && (found_rating < 1 || + (found_rating == 1 && element < found_element))) { start_x = x + xx - 1; start_y = y + yy - 1; @@ -2376,8 +3486,24 @@ void InitGame() if (!game.restart_level) CloseDoor(DOOR_CLOSE_1); +#if 1 + if (level_editor_test_game) + FadeSkipNextFadeIn(); + else + FadeSetStartItem(); +#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) @@ -2404,10 +3530,14 @@ void InitGame() } /* !!! FIX THIS (END) !!! */ +#if 1 + FadeIn(REDRAW_FIELD); +#else if (do_fading) FadeIn(REDRAW_FIELD); BackToFront(); +#endif if (!game.restart_level) { @@ -2488,7 +3618,7 @@ void InitMovDir(int x, int y) { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN } }; - switch(element) + switch (element) { case EL_BUG_RIGHT: case EL_BUG_UP: @@ -2682,10 +3812,12 @@ void GameWon() { static int time, time_final; static int score, score_final; - static int game_over_delay = 0; - int game_over_delay_value = 50; + static int game_over_delay_1 = 0; + static int game_over_delay_2 = 0; + int game_over_delay_value_1 = 50; + int game_over_delay_value_2 = 50; - if (!local_player->LevelSolved_GameEnd) + if (!local_player->LevelSolved_GameWon) { int i; @@ -2693,7 +3825,7 @@ void GameWon() if (local_player->MovPos) return; - local_player->LevelSolved_GameEnd = TRUE; + local_player->LevelSolved_GameWon = TRUE; local_player->LevelSolved_SaveTape = tape.recording; local_player->LevelSolved_SaveScore = !tape.playing; @@ -2704,7 +3836,8 @@ void GameWon() TapeStop(); #endif - game_over_delay = game_over_delay_value; + 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); score = score_final = local_player->score_final; @@ -2727,48 +3860,75 @@ void GameWon() time = time_final; score = score_final; +#if 1 + game_control_value[GAME_CONTROL_TIME] = time; + game_control_value[GAME_CONTROL_SCORE] = score; + + DisplayGameControlValues(); +#else DrawGameValue_Time(time); DrawGameValue_Score(score); +#endif } - if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */ + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) { - /* close exit door after last player */ - if (AllPlayersGone && - (Feld[ExitX][ExitY] == EL_EXIT_OPEN || - Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN)) + if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */ { - int element = Feld[ExitX][ExitY]; + /* close exit door after last player */ + if ((AllPlayersGone && + (Feld[ExitX][ExitY] == EL_EXIT_OPEN || + Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN || + Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) || + Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN || + Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN) + { + int element = Feld[ExitX][ExitY]; - Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING : - EL_SP_EXIT_CLOSING); +#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); + } + } - PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING); + /* player disappears */ + DrawLevelField(ExitX, ExitY); } - /* player disappears */ - DrawLevelField(ExitX, ExitY); - } - - for (i = 0; i < MAX_PLAYERS; i++) - { - struct PlayerInfo *player = &stored_player[i]; - - if (player->present) + for (i = 0; i < MAX_PLAYERS; i++) { - RemovePlayer(player); + struct PlayerInfo *player = &stored_player[i]; - /* player disappears */ - DrawLevelField(player->jx, player->jy); + if (player->present) + { + RemovePlayer(player); + + /* player disappears */ + DrawLevelField(player->jx, player->jy); + } } } PlaySound(SND_GAME_WINNING); } - if (game_over_delay > 0) + if (game_over_delay_1 > 0) { - game_over_delay--; + game_over_delay_1--; return; } @@ -2782,8 +3942,15 @@ 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; + + DisplayGameControlValues(); +#else DrawGameValue_Time(time); DrawGameValue_Score(score); +#endif if (time == time_final) StopSound(SND_GAME_LEVELTIME_BONUS); @@ -2791,7 +3958,22 @@ void GameWon() PlaySoundLoop(SND_GAME_LEVELTIME_BONUS); else PlaySound(SND_GAME_LEVELTIME_BONUS); + + return; + } + + local_player->LevelSolved_PanelOff = TRUE; + + if (game_over_delay_2 > 0) + { + game_over_delay_2--; + + return; } + +#if 1 + GameEnd(); +#endif } void GameEnd() @@ -2799,6 +3981,8 @@ void GameEnd() int hi_pos; boolean raise_level = FALSE; + local_player->LevelSolved_GameEnd = TRUE; + CloseDoor(DOOR_CLOSE_1); if (local_player->LevelSolved_SaveTape) @@ -2818,14 +4002,20 @@ void GameEnd() { game_status = 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; @@ -2857,7 +4047,9 @@ void GameEnd() } else { +#if 1 FadeOut(REDRAW_FIELD); +#endif game_status = GAME_MODE_MAIN; @@ -3025,7 +4217,12 @@ void InitMovingField(int x, int y, int direction) /* 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 @@ -3209,8 +4406,10 @@ void RemoveMovingField(int x, int y) if (element == EL_BLOCKED && (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING || + Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING || Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING || Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING || + Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING || Feld[oldx][oldy] == EL_AMOEBA_DROPPING)) next_element = get_next_element(Feld[oldx][oldy]); @@ -3321,8 +4520,8 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy) *sy = (sy1 + sy2) / 2; } -void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen, - boolean quick_relocation) +void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, + boolean center_screen, boolean quick_relocation) { boolean ffwd_delay = (tape.playing && tape.fast_forward); boolean no_delay = (tape.warp_forward); @@ -3331,20 +4530,50 @@ void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen, if (quick_relocation) { - int offset = (setup.scroll_delay ? 3 : 0); + int offset = game.scroll_delay_value; if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen) { - scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : - x > SBX_Right + MIDPOSX ? SBX_Right : - x - MIDPOSX); + if (!level.shifted_relocation || center_screen) + { + /* quick relocation (without scrolling), with centering of screen */ + + scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : + x > SBX_Right + MIDPOSX ? SBX_Right : + x - MIDPOSX); + + scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper : + y > SBY_Lower + MIDPOSY ? SBY_Lower : + y - MIDPOSY); + } + else + { + /* quick relocation (without scrolling), but do not center screen */ + + int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left : + old_x > SBX_Right + MIDPOSX ? SBX_Right : + old_x - MIDPOSX); + + int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper : + old_y > SBY_Lower + MIDPOSY ? SBY_Lower : + old_y - MIDPOSY); - scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper : - y > SBY_Lower + MIDPOSY ? SBY_Lower : - y - MIDPOSY); + 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 : + offset_x > SBX_Right + MIDPOSX ? SBX_Right : + offset_x - MIDPOSX); + + scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper : + offset_y > SBY_Lower + MIDPOSY ? SBY_Lower : + offset_y - MIDPOSY); + } } else { + /* quick relocation (without scrolling), inside visible screen area */ + if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) || (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset)) scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset); @@ -3366,6 +4595,49 @@ void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen, } 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); + + scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left : + offset_x > SBX_Right + MIDPOSX ? SBX_Right : + offset_x - MIDPOSX); + + scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper : + offset_y > SBY_Lower + MIDPOSY ? SBY_Lower : + offset_y - MIDPOSY); + } + +#else + + /* visible relocation (with scrolling), with centering of screen */ + int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left : x > SBX_Right + MIDPOSX ? SBX_Right : x - MIDPOSX); @@ -3373,6 +4645,7 @@ void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen, int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper : y > SBY_Lower + MIDPOSY ? SBY_Lower : y - MIDPOSY); +#endif ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */ @@ -3488,8 +4761,8 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) } /* only visually relocate centered player */ - DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE, - level.instant_relocation); + DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir, + FALSE, level.instant_relocation); TestIfPlayerTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); @@ -3922,7 +5195,7 @@ void Bang(int x, int y) } } - switch(element) + switch (element) { case EL_BUG: case EL_SPACESHIP: @@ -3946,6 +5219,14 @@ void Bang(int x, int y) explosion_type = EX_TYPE_DYNA; 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; + case EL_PENGUIN: case EL_LAMP: case EL_LAMP_ACTIVE: @@ -4159,6 +5440,12 @@ static void ToggleSwitchgateSwitch(int x, int y) 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) { @@ -4170,6 +5457,16 @@ static void ToggleSwitchgateSwitch(int x, int y) Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP; DrawLevelField(xx, yy); } + else if (element == EL_DC_SWITCHGATE_SWITCH_UP) + { + Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN; + DrawLevelField(xx, yy); + } + else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN) + { + Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP; + DrawLevelField(xx, yy); + } #endif else if (element == EL_SWITCHGATE_OPEN || element == EL_SWITCHGATE_OPENING) @@ -4386,7 +5683,7 @@ static void ActivateTimegateSwitch(int x, int y) element == EL_TIMEGATE_CLOSING) { Feld[xx][yy] = EL_TIMEGATE_OPENING; - PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING); + PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING); } /* @@ -4399,7 +5696,12 @@ static void ActivateTimegateSwitch(int x, int y) } +#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) @@ -4434,6 +5736,16 @@ void Impact(int x, int y) object_hit = TRUE; } + + if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE) + { + RemoveMovingField(x, y + 1); + Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY; + Feld[x][y + 2] = EL_ROCK; + DrawLevelField(x, y + 2); + + object_hit = TRUE; + } #endif if (object_hit) @@ -4462,7 +5774,8 @@ void Impact(int x, int y) Bang(x, y); return; } - else if (impact && element == EL_PEARL) + else if (impact && element == EL_PEARL && + smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE) { ResetGfxAnimation(x, y); @@ -4495,26 +5808,33 @@ void Impact(int x, int y) if (object_hit) /* check which object was hit */ { - if (CAN_PASS_MAGIC_WALL(element) && - (smashed == EL_MAGIC_WALL || - smashed == EL_BD_MAGIC_WALL)) + if ((CAN_PASS_MAGIC_WALL(element) && + (smashed == EL_MAGIC_WALL || + smashed == EL_BD_MAGIC_WALL)) || + (CAN_PASS_DC_MAGIC_WALL(element) && + smashed == EL_DC_MAGIC_WALL)) { int xx, yy; int activated_magic_wall = (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE : - EL_BD_MAGIC_WALL_ACTIVE); + smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE : + EL_DC_MAGIC_WALL_ACTIVE); /* activate magic wall / mill */ SCAN_PLAYFIELD(xx, yy) + { if (Feld[xx][yy] == smashed) Feld[xx][yy] = activated_magic_wall; + } game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND; game.magic_wall_active = TRUE; PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ? SND_MAGIC_WALL_ACTIVATING : - SND_BD_MAGIC_WALL_ACTIVATING)); + smashed == EL_BD_MAGIC_WALL ? + SND_BD_MAGIC_WALL_ACTIVATING : + SND_DC_MAGIC_WALL_ACTIVATING)); } if (IS_PLAYER(x, y + 1)) @@ -4594,7 +5914,9 @@ void Impact(int x, int y) ToggleBeltSwitch(x, y + 1); } else if (smashed == EL_SWITCHGATE_SWITCH_UP || - smashed == EL_SWITCHGATE_SWITCH_DOWN) + smashed == EL_SWITCHGATE_SWITCH_DOWN || + smashed == EL_DC_SWITCHGATE_SWITCH_UP || + smashed == EL_DC_SWITCHGATE_SWITCH_DOWN) { ToggleSwitchgateSwitch(x, y + 1); } @@ -4627,12 +5949,15 @@ void Impact(int x, int y) /* play sound of magic wall / mill */ if (!last_line && (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE || - Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) + Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE || + Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)) { if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE) PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING); else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE) PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING); + else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE) + PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING); return; } @@ -4989,7 +6314,10 @@ inline static void TurnRoundExt(int x, int y) int ex = x + xy[i][0]; int ey = y + xy[i][1]; - if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN) + if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN || + Feld[ex][ey] == EL_EM_EXIT_OPEN || + Feld[ex][ey] == EL_STEEL_EXIT_OPEN || + Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN)) { attr_x = ex; attr_y = ey; @@ -5546,13 +6874,61 @@ void StartMoving(int x, int y) PlayLevelSoundAction(x, y, ACTION_FILLING); } } + else if (element == EL_QUICKSAND_FAST_FULL) + { + if (IS_FREE(x, y + 1)) + { + InitMovingField(x, y, MV_DOWN); + started_moving = TRUE; + + Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING; +#if USE_QUICKSAND_BD_ROCK_BUGFIX + if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK) + Store[x][y] = EL_ROCK; +#else + Store[x][y] = EL_ROCK; +#endif + + PlayLevelSoundAction(x, y, ACTION_EMPTYING); + } + else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY) + { + if (!MovDelay[x][y]) + MovDelay[x][y] = TILEY + 1; + + if (MovDelay[x][y]) + { + MovDelay[x][y]--; + if (MovDelay[x][y]) + return; + } + + Feld[x][y] = EL_QUICKSAND_FAST_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); + } + } else if ((element == EL_ROCK || element == EL_BD_ROCK) && Feld[x][y + 1] == EL_QUICKSAND_EMPTY) { InitMovingField(x, y, MV_DOWN); started_moving = TRUE; - Feld[x][y] = EL_QUICKSAND_FILLING; + Feld[x][y] = EL_QUICKSAND_FILLING; + Store[x][y] = element; + + PlayLevelSoundAction(x, y, ACTION_FILLING); + } + else if ((element == EL_ROCK || element == EL_BD_ROCK) && + Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY) + { + InitMovingField(x, y, MV_DOWN); + started_moving = TRUE; + + Feld[x][y] = EL_QUICKSAND_FAST_FILLING; Store[x][y] = element; PlayLevelSoundAction(x, y, ACTION_FILLING); @@ -5593,7 +6969,7 @@ void StartMoving(int x, int y) started_moving = TRUE; Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING; - Store[x][y] = EL_CHANGED2(Store[x][y]); + Store[x][y] = EL_CHANGED_BD(Store[x][y]); } else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE) { @@ -5609,20 +6985,52 @@ void StartMoving(int x, int y) Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE; Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL; - Store[x][y + 1] = EL_CHANGED2(Store[x][y]); + Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]); + Store[x][y] = 0; + } + } + else if (element == EL_DC_MAGIC_WALL_FULL) + { + if (IS_FREE(x, y + 1)) + { + InitMovingField(x, y, MV_DOWN); + started_moving = TRUE; + + Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING; + Store[x][y] = EL_CHANGED_DC(Store[x][y]); + } + else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE) + { + if (!MovDelay[x][y]) + MovDelay[x][y] = TILEY/4 + 1; + + if (MovDelay[x][y]) + { + MovDelay[x][y]--; + if (MovDelay[x][y]) + return; + } + + Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE; + Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL; + Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]); Store[x][y] = 0; } } - else if (CAN_PASS_MAGIC_WALL(element) && - (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE || - Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) + else if ((CAN_PASS_MAGIC_WALL(element) && + (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE || + Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) || + (CAN_PASS_DC_MAGIC_WALL(element) && + (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))) + { InitMovingField(x, y, MV_DOWN); started_moving = TRUE; Feld[x][y] = (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING : - EL_BD_MAGIC_WALL_FILLING); + Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING : + EL_DC_MAGIC_WALL_FILLING); Store[x][y] = element; } else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID) @@ -5634,9 +7042,14 @@ void StartMoving(int x, int y) Store[x][y] = EL_ACID; } - else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) && + 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))) || @@ -5656,6 +7069,7 @@ void StartMoving(int x, int y) simply not covered here... :-/ ) */ CheckCollision[x][y] = 0; + CheckImpact[x][y] = 0; Impact(x, y); } @@ -5980,7 +7394,10 @@ void StartMoving(int x, int y) } else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy)) { - if (Feld[newx][newy] == EL_EXIT_OPEN) + if (Feld[newx][newy] == EL_EXIT_OPEN || + Feld[newx][newy] == EL_EM_EXIT_OPEN || + Feld[newx][newy] == EL_STEEL_EXIT_OPEN || + Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN) { RemoveField(x, y); DrawLevelField(x, y); @@ -6398,6 +7815,17 @@ 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); return; /* element is still moving */ @@ -6429,6 +7857,16 @@ void ContinueMoving(int x, int y) Feld[x][y] = get_next_element(element); element = Feld[newx][newy] = Store[x][y]; } + else if (element == EL_QUICKSAND_FAST_FILLING) + { + element = Feld[newx][newy] = get_next_element(element); + Store[newx][newy] = Store[x][y]; + } + else if (element == EL_QUICKSAND_FAST_EMPTYING) + { + Feld[x][y] = get_next_element(element); + element = Feld[newx][newy] = Store[x][y]; + } else if (element == EL_MAGIC_WALL_FILLING) { element = Feld[newx][newy] = get_next_element(element); @@ -6461,6 +7899,24 @@ 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) + { + element = Feld[newx][newy] = get_next_element(element); + if (!game.magic_wall_active) + element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD; + Store[newx][newy] = Store[x][y]; + } + else if (element == EL_DC_MAGIC_WALL_EMPTYING) + { + Feld[x][y] = get_next_element(element); + if (!game.magic_wall_active) + 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 @@ -6600,6 +8056,11 @@ 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 */ @@ -6667,7 +8128,7 @@ void ContinueMoving(int x, int y) if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY && IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element)) CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X, - MV_DIR_OPPOSITE(direction)); + MV_DIR_OPPOSITE(direction)); } int AmoebeNachbarNr(int ax, int ay) @@ -6959,7 +8420,8 @@ void AmoebeAbleger(int ax, int ay) if (IS_FREE(x, y) || CAN_GROW_INTO(Feld[x][y]) || - Feld[x][y] == EL_QUICKSAND_EMPTY) + Feld[x][y] == EL_QUICKSAND_EMPTY || + Feld[x][y] == EL_QUICKSAND_FAST_EMPTY) { newax = x; neway = y; @@ -6984,7 +8446,8 @@ void AmoebeAbleger(int ax, int ay) if (IS_FREE(x, y) || CAN_GROW_INTO(Feld[x][y]) || - Feld[x][y] == EL_QUICKSAND_EMPTY) + Feld[x][y] == EL_QUICKSAND_EMPTY || + Feld[x][y] == EL_QUICKSAND_FAST_EMPTY) { newax = x; neway = y; @@ -7169,7 +8632,7 @@ static void InitTimegateWheel(int x, int y) static void RunTimegateWheel(int x, int y) { - PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE); + PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE); } static void InitMagicBallDelay(int x, int y) @@ -7236,6 +8699,75 @@ void CheckExit(int x, int y) PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING); } +void CheckExitEM(int x, int y) +{ + if (local_player->gems_still_needed > 0 || + local_player->sokobanfields_still_needed > 0 || + local_player->lights_still_needed > 0) + { + int element = Feld[x][y]; + int graphic = el2img(element); + + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + + return; + } + + if (AllPlayersGone) /* do not re-open exit door closed after last player */ + return; + + Feld[x][y] = EL_EM_EXIT_OPENING; + + PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING); +} + +void CheckExitSteel(int x, int y) +{ + if (local_player->gems_still_needed > 0 || + local_player->sokobanfields_still_needed > 0 || + local_player->lights_still_needed > 0) + { + int element = Feld[x][y]; + int graphic = el2img(element); + + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + + return; + } + + if (AllPlayersGone) /* do not re-open exit door closed after last player */ + return; + + Feld[x][y] = EL_STEEL_EXIT_OPENING; + + PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING); +} + +void CheckExitSteelEM(int x, int y) +{ + if (local_player->gems_still_needed > 0 || + local_player->sokobanfields_still_needed > 0 || + local_player->lights_still_needed > 0) + { + int element = Feld[x][y]; + int graphic = el2img(element); + + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + + return; + } + + if (AllPlayersGone) /* do not re-open exit door closed after last player */ + return; + + Feld[x][y] = EL_EM_STEEL_EXIT_OPENING; + + PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING); +} + void CheckExitSP(int x, int y) { if (local_player->gems_still_needed > 0) @@ -7474,6 +9006,108 @@ void MauerAbleger(int ax, int ay) PlayLevelSoundAction(ax, ay, ACTION_GROWING); } +void MauerAblegerStahl(int ax, int ay) +{ + int element = Feld[ax][ay]; + int graphic = el2img(element); + boolean oben_frei = FALSE, unten_frei = FALSE; + boolean links_frei = FALSE, rechts_frei = FALSE; + boolean oben_massiv = FALSE, unten_massiv = FALSE; + boolean links_massiv = FALSE, rechts_massiv = FALSE; + boolean new_wall = FALSE; + + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic); + + if (!MovDelay[ax][ay]) /* start building new wall */ + MovDelay[ax][ay] = 6; + + if (MovDelay[ax][ay]) /* wait some time before building new wall */ + { + MovDelay[ax][ay]--; + if (MovDelay[ax][ay]) + return; + } + + if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1)) + oben_frei = TRUE; + if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1)) + unten_frei = TRUE; + if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay)) + links_frei = TRUE; + if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay)) + rechts_frei = TRUE; + + if (element == EL_EXPANDABLE_STEELWALL_VERTICAL || + element == EL_EXPANDABLE_STEELWALL_ANY) + { + if (oben_frei) + { + Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING; + Store[ax][ay-1] = element; + GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP; + if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1))) + DrawGraphic(SCREENX(ax), SCREENY(ay - 1), + IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0); + new_wall = TRUE; + } + if (unten_frei) + { + Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING; + Store[ax][ay+1] = element; + GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN; + if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1))) + DrawGraphic(SCREENX(ax), SCREENY(ay + 1), + IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0); + new_wall = TRUE; + } + } + + if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL || + element == EL_EXPANDABLE_STEELWALL_ANY) + { + if (links_frei) + { + Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING; + Store[ax-1][ay] = element; + GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT; + if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay))) + DrawGraphic(SCREENX(ax - 1), SCREENY(ay), + IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0); + new_wall = TRUE; + } + + if (rechts_frei) + { + Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING; + Store[ax+1][ay] = element; + GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT; + if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay))) + DrawGraphic(SCREENX(ax + 1), SCREENY(ay), + IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0); + new_wall = TRUE; + } + } + + if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1])) + oben_massiv = TRUE; + if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1])) + unten_massiv = TRUE; + if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay])) + links_massiv = TRUE; + if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay])) + rechts_massiv = TRUE; + + if (((oben_massiv && unten_massiv) || + element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) && + ((links_massiv && rechts_massiv) || + element == EL_EXPANDABLE_STEELWALL_VERTICAL)) + Feld[ax][ay] = EL_WALL; + + if (new_wall) + PlayLevelSoundAction(ax, ay, ACTION_GROWING); +} + void CheckForDragon(int x, int y) { int i, j; @@ -7717,7 +9351,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) /* ---------- execute action -------------------------------------------- */ - switch(action_type) + switch (action_type) { case CA_NO_ACTION: { @@ -7750,7 +9384,13 @@ 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; + + DisplayGameControlValues(); +#else DrawGameValue_Time(TimeLeft); +#endif if (!TimeLeft && setup.time_limit) for (i = 0; i < MAX_PLAYERS; i++) @@ -7764,7 +9404,13 @@ 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; + + DisplayGameControlValues(); +#else DrawGameValue_Score(local_player->score); +#endif break; } @@ -7773,7 +9419,13 @@ 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; + + DisplayGameControlValues(); +#else DrawGameValue_Emeralds(local_player->gems_still_needed); +#endif break; } @@ -8052,7 +9704,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) static void CreateFieldExt(int x, int y, int element, boolean is_change) { int old_element = Feld[x][y]; - int new_element = get_element_from_group_element(element); + int new_element = GetElementFromGroupElement(element); int previous_move_direction = MovDir[x][y]; #if USE_NEW_CUSTOM_VALUE int last_ce_value = CustomValue[x][y]; @@ -8384,8 +10036,19 @@ static void HandleElementChange(int x, int y, int page) if (change->can_change) { - ResetGfxAnimation(x, y); - ResetRandomAnimationValue(x, y); +#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 + /* 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 + { + ResetGfxAnimation(x, y); + ResetRandomAnimationValue(x, y); + } +#endif if (change->pre_change_function) change->pre_change_function(x, y); @@ -8534,6 +10197,14 @@ 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++) { int element = EL_CUSTOM_START + i; @@ -8602,6 +10273,8 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, } } + RECURSION_LOOP_DETECTION_END(); + return change_done_any; } @@ -8640,14 +10313,30 @@ static boolean CheckElementChangeExt(int x, int y, 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); + for (p = 0; p < element_info[element].num_change_pages; p++) { struct ElementChangeInfo *change = &element_info[element].change_page[p]; + /* check trigger element for all events where the element that is checked + for changing interacts with a directly adjacent element -- this is + different to element changes that affect other elements to change on the + whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */ boolean check_trigger_element = (trigger_event == CE_TOUCHING_X || trigger_event == CE_HITTING_X || - trigger_event == CE_HIT_BY_X); + trigger_event == CE_HIT_BY_X || +#if 1 + /* this one was forgotten until 3.2.3 */ + trigger_event == CE_DIGGING_X); +#endif if (change->can_change_or_has_action && change->has_event[trigger_event] && @@ -8711,6 +10400,8 @@ static boolean CheckElementChangeExt(int x, int y, } } + RECURSION_LOOP_DETECTION_END(); + return change_done; } @@ -9009,7 +10700,13 @@ 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; + + DisplayGameControlValues(); +#else DrawGameValue_Time(TimeLeft); +#endif if (!TimeLeft && setup.time_limit) { @@ -9020,8 +10717,17 @@ static void CheckLevelTime() KillPlayer(&stored_player[i]); } } +#if 1 + else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ + { + game_control_value[GAME_CONTROL_TIME] = TimePlayed; + + DisplayGameControlValues(); + } +#else else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ DrawGameValue_Time(TimePlayed); +#endif level.native_em_level->lev->time = (level.time == 0 ? TimePlayed : TimeLeft); @@ -9030,6 +10736,8 @@ static void CheckLevelTime() if (tape.recording || tape.playing) DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); } + + DrawGameDoorValues(); } void AdvanceFrameAndPlayerCounters(int player_nr) @@ -9112,6 +10820,25 @@ void GameActions() byte tape_action[MAX_PLAYERS]; int i; + /* detect endless loops, caused by custom element programming */ + if (recursion_loop_detected && recursion_loop_depth == 0) + { + char *message = getStringCat3("Internal Error ! Element ", + EL_NAME(recursion_loop_element), + " caused endless loop ! Quit the game ?"); + + Error(ERR_WARN, "element '%s' caused endless loop in game engine", + EL_NAME(recursion_loop_element)); + + RequestQuitGameExt(FALSE, level_editor_test_game, message); + + recursion_loop_detected = FALSE; /* if game should be continued */ + + free(message); + + return; + } + if (game.restart_level) StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE); @@ -9133,7 +10860,7 @@ void GameActions() AllPlayersGone = TRUE; } - if (local_player->LevelSolved) + if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd) GameWon(); if (AllPlayersGone && !TAPE_IS_STOPPED(tape)) @@ -9315,7 +11042,7 @@ void GameActions_RND() game.centered_player_nr = game.centered_player_nr_next; game.set_centered_player = FALSE; - DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch); + DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch); DrawGameDoorValues(); } @@ -9370,13 +11097,15 @@ void GameActions_RND() /* continue moving after pushing (this is actually a bug) */ if (!IS_MOVING(x, y)) - { Stop[x][y] = FALSE; - } } } } +#if 0 + debug_print_timestamp(0, "start main loop profiling"); +#endif + SCAN_PLAYFIELD(x, y) { ChangeCount[x][y] = 0; @@ -9421,6 +11150,8 @@ void GameActions_RND() WasJustFalling[x][y]--; if (CheckCollision[x][y] > 0) CheckCollision[x][y]--; + if (CheckImpact[x][y] > 0) + CheckImpact[x][y]--; GfxFrame[x][y]++; @@ -9449,6 +11180,63 @@ 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]; @@ -9492,6 +11280,143 @@ void GameActions_RND() 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); @@ -9509,7 +11434,10 @@ void GameActions_RND() } else if ((element == EL_ACID || element == EL_EXIT_OPEN || + element == EL_EM_EXIT_OPEN || element == EL_SP_EXIT_OPEN || + element == EL_STEEL_EXIT_OPEN || + element == EL_EM_STEEL_EXIT_OPEN || element == EL_SP_TERMINAL || element == EL_SP_TERMINAL_ACTIVE || element == EL_EXTRA_TIME || @@ -9535,9 +11463,16 @@ void GameActions_RND() Life(x, y); else if (element == EL_EXIT_CLOSED) CheckExit(x, y); + else if (element == EL_EM_EXIT_CLOSED) + CheckExitEM(x, y); + else if (element == EL_STEEL_EXIT_CLOSED) + CheckExitSteel(x, y); + else if (element == EL_EM_STEEL_EXIT_CLOSED) + CheckExitSteelEM(x, y); else if (element == EL_SP_EXIT_CLOSED) CheckExitSP(x, y); - else if (element == EL_EXPANDABLE_WALL_GROWING) + else if (element == EL_EXPANDABLE_WALL_GROWING || + element == EL_EXPANDABLE_STEELWALL_GROWING) MauerWaechst(x, y); else if (element == EL_EXPANDABLE_WALL || element == EL_EXPANDABLE_WALL_HORIZONTAL || @@ -9545,6 +11480,10 @@ void GameActions_RND() element == EL_EXPANDABLE_WALL_ANY || element == EL_BD_EXPANDABLE_WALL) MauerAbleger(x, y); + else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL || + element == EL_EXPANDABLE_STEELWALL_VERTICAL || + element == EL_EXPANDABLE_STEELWALL_ANY) + MauerAblegerStahl(x, y); else if (element == EL_FLAMES) CheckForDragon(x, y); else if (element == EL_EXPLOSION) @@ -9560,6 +11499,8 @@ 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); @@ -9573,7 +11514,10 @@ void GameActions_RND() element == EL_MAGIC_WALL_EMPTYING || element == EL_BD_MAGIC_WALL_FULL || element == EL_BD_MAGIC_WALL_ACTIVE || - element == EL_BD_MAGIC_WALL_EMPTYING) && + element == EL_BD_MAGIC_WALL_EMPTYING || + element == EL_DC_MAGIC_WALL_FULL || + element == EL_DC_MAGIC_WALL_ACTIVE || + element == EL_DC_MAGIC_WALL_EMPTYING) && ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy)) { magic_wall_x = x; @@ -9582,6 +11526,10 @@ 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)) @@ -9598,6 +11546,7 @@ void GameActions_RND() (element == EL_EMPTY || CAN_GROW_INTO(element) || element == EL_QUICKSAND_EMPTY || + element == EL_QUICKSAND_FAST_EMPTY || element == EL_ACID_SPLASH_LEFT || element == EL_ACID_SPLASH_RIGHT)) { @@ -9644,6 +11593,10 @@ void GameActions_RND() element == EL_BD_MAGIC_WALL_ACTIVE || element == EL_BD_MAGIC_WALL_EMPTYING) PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE); + else if (element == EL_DC_MAGIC_WALL_FULL || + element == EL_DC_MAGIC_WALL_ACTIVE || + element == EL_DC_MAGIC_WALL_EMPTYING) + PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE); else PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE); } @@ -9669,6 +11622,12 @@ void GameActions_RND() Feld[x][y] = EL_BD_MAGIC_WALL_DEAD; 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); + } } game.magic_wall_active = FALSE; @@ -9754,6 +11713,11 @@ 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); @@ -9800,25 +11764,85 @@ 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 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, + 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 - if (dx) + if (dx != 0) { x = (dx == 1 ? BX1 : BX2); for (y = BY1; y <= BY2; y++) DrawScreenField(x, y); } - if (dy) + if (dy != 0) { y = (dy == 1 ? BY1 : BY2); for (x = BX1; x <= BX2; x++) @@ -10129,7 +12153,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) #endif { int old_scroll_x = scroll_x, old_scroll_y = scroll_y; - int offset = (setup.scroll_delay ? 3 : 0); + int offset = game.scroll_delay_value; if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy))) { @@ -10357,6 +12381,9 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) player->last_jy = jy; if (Feld[jx][jy] == EL_EXIT_OPEN || + Feld[jx][jy] == EL_EM_EXIT_OPEN || + Feld[jx][jy] == EL_STEEL_EXIT_OPEN || + Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN || Feld[jx][jy] == EL_SP_EXIT_OPEN || Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */ { @@ -10426,14 +12453,29 @@ 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; + + 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 */ + { + game_control_value[GAME_CONTROL_TIME] = 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 && @@ -11032,6 +13074,23 @@ void KillPlayer(struct PlayerInfo *player) if (!player->active) return; + /* the following code was introduced to prevent an infinite loop when calling + -> Bang() + -> CheckTriggeredElementChangeExt() + -> ExecuteCustomElementAction() + -> KillPlayer() + -> (infinitely repeating the above sequence of function calls) + which occurs when killing the player while having a CE with the setting + "kill player X when explosion of "; the solution using a new + field "player->killed" was chosen for backwards compatibility, although + clever use of the fields "player->active" etc. would probably also work */ +#if 1 + if (player->killed) + return; +#endif + + player->killed = TRUE; + /* remove accessible field at the player's position */ Feld[jx][jy] = EL_EMPTY; @@ -11265,6 +13324,9 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, player->index_bit, dig_side); + if (element == EL_DC_LANDMINE) + Bang(x, y); + if (Feld[x][y] != element) /* field changed by snapping */ return MP_ACTION; @@ -11305,6 +13367,9 @@ int DigField(struct PlayerInfo *player, return MP_NO_ACTION; } else if (element == EL_EXIT_OPEN || + element == EL_EM_EXIT_OPEN || + element == EL_STEEL_EXIT_OPEN || + element == EL_EM_STEEL_EXIT_OPEN || element == EL_SP_EXIT_OPEN || element == EL_SP_EXIT_OPENING) { @@ -11360,6 +13425,15 @@ int DigField(struct PlayerInfo *player, if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)]) return MP_NO_ACTION; } + else if (element == EL_DC_GATE_WHITE || + element == EL_DC_GATE_WHITE_GRAY || + element == EL_DC_GATE_WHITE_GRAY_ACTIVE) + { + if (player->num_white_keys == 0) + return MP_NO_ACTION; + + player->num_white_keys--; + } else if (IS_SP_PORT(element)) { if (element == EL_SP_GRAVITY_PORT_LEFT || @@ -11450,7 +13524,14 @@ int DigField(struct PlayerInfo *player, else if (element == EL_EXTRA_TIME && level.time > 0) { TimeLeft += level.extra_time; + +#if 1 + game_control_value[GAME_CONTROL_TIME] = TimeLeft; + + DisplayGameControlValues(); +#else DrawGameValue_Time(TimeLeft); +#endif } else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY) { @@ -11486,6 +13567,13 @@ int DigField(struct PlayerInfo *player, DrawGameDoorValues(); } + else if (element == EL_DC_KEY_WHITE) + { + player->num_white_keys++; + + /* display white keys? */ + /* DrawGameDoorValues(); */ + } else if (IS_ENVELOPE(element)) { player->show_envelope = element; @@ -11522,7 +13610,13 @@ 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; + + DisplayGameControlValues(); +#else DrawGameValue_Emeralds(local_player->gems_still_needed); +#endif } RaiseScoreElement(element); @@ -11721,7 +13815,9 @@ int DigField(struct PlayerInfo *player, ToggleBeltSwitch(x, y); } else if (element == EL_SWITCHGATE_SWITCH_UP || - element == EL_SWITCHGATE_SWITCH_DOWN) + element == EL_SWITCHGATE_SWITCH_DOWN || + element == EL_DC_SWITCHGATE_SWITCH_UP || + element == EL_DC_SWITCHGATE_SWITCH_DOWN) { ToggleSwitchgateSwitch(x, y); } @@ -11730,7 +13826,8 @@ int DigField(struct PlayerInfo *player, { ToggleLightSwitch(x, y); } - else if (element == EL_TIMEGATE_SWITCH) + else if (element == EL_TIMEGATE_SWITCH || + element == EL_DC_TIMEGATE_SWITCH) { ActivateTimegateSwitch(x, y); } @@ -11763,7 +13860,14 @@ int DigField(struct PlayerInfo *player, if (level.time > 0 || level.use_time_orb_bug) { TimeLeft += level.time_orb_time; + +#if 1 + game_control_value[GAME_CONTROL_TIME] = TimeLeft; + + DisplayGameControlValues(); +#else DrawGameValue_Time(TimeLeft); +#endif } ResetGfxAnimation(x, y); @@ -12057,7 +14161,13 @@ boolean DropElement(struct PlayerInfo *player) 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); @@ -12379,12 +14489,18 @@ void RaiseScore(int value) { local_player->score += value; +#if 1 + game_control_value[GAME_CONTROL_SCORE] = local_player->score; + + DisplayGameControlValues(); +#else DrawGameValue_Score(local_player->score); +#endif } void RaiseScoreElement(int element) { - switch(element) + switch (element) { case EL_EMERALD: case EL_BD_DIAMOND: @@ -12453,6 +14569,7 @@ void RaiseScoreElement(int element) case EL_EMC_KEY_6: case EL_EMC_KEY_7: case EL_EMC_KEY_8: + case EL_DC_KEY_WHITE: RaiseScore(level.score[SC_KEY]); break; default: @@ -12461,13 +14578,9 @@ void RaiseScoreElement(int element) } } -void RequestQuitGame(boolean ask_if_really_quit) +void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) { - if (AllPlayersGone || - !ask_if_really_quit || - level_editor_test_game || - Request("Do you really want to quit the game ?", - REQ_ASK | REQ_STAY_CLOSED)) + if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED)) { #if defined(NETWORK_AVALIABLE) if (options.network) @@ -12475,15 +14588,33 @@ void RequestQuitGame(boolean ask_if_really_quit) else #endif { - if (!ask_if_really_quit || level_editor_test_game) + 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; @@ -12491,7 +14622,7 @@ void RequestQuitGame(boolean ask_if_really_quit) } } } - else + else /* continue playing the game */ { if (tape.playing && tape.deactivate_display) TapeDeactivateDisplayOff(TRUE); @@ -12503,6 +14634,15 @@ void RequestQuitGame(boolean ask_if_really_quit) } } +void RequestQuitGame(boolean ask_if_really_quit) +{ + boolean quick_quit = (!ask_if_really_quit || level_editor_test_game); + boolean skip_request = AllPlayersGone || quick_quit; + + RequestQuitGameExt(skip_request, quick_quit, + "Do you really want to quit the game ?"); +} + /* ------------------------------------------------------------------------- */ /* random generator functions */ @@ -12658,9 +14798,9 @@ static void LoadEngineSnapshotValues_RND() if (game.num_random_calls != num_random_calls) { - Error(ERR_RETURN, "number of random calls out of sync"); - Error(ERR_RETURN, "number of random calls should be %d", num_random_calls); - Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls); + Error(ERR_INFO, "number of random calls out of sync"); + Error(ERR_INFO, "number of random calls should be %d", num_random_calls); + Error(ERR_INFO, "number of random calls is %d", game.num_random_calls); Error(ERR_EXIT, "this should not happen -- please debug"); } } @@ -12683,6 +14823,9 @@ void SaveEngineSnapshot() { FreeEngineSnapshot(); /* free previous snapshot, if needed */ + if (level_editor_test_game) /* do not save snapshots from editor */ + return; + /* copy some special values to a structure better suited for the snapshot */ SaveEngineSnapshotValues_RND(); @@ -12736,6 +14879,7 @@ void SaveEngineSnapshot() 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)); @@ -12829,11 +14973,50 @@ boolean CheckEngineSnapshot() static struct { - int x, y; + int *x, *y; + int gd_x, gd_y; 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" + }, + { + &game.button.pause.x, &game.button.pause.y, + GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS, + 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" + }, + { + &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" + }, + { + &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" + }, + { + &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, @@ -12864,6 +15047,7 @@ static struct SOUND_CTRL_ID_SIMPLE, "normal sounds on/off" } +#endif }; void CreateGameButtons() @@ -12877,12 +15061,15 @@ void CreateGameButtons() 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; int id = i; - gd_xoffset = gamebutton_info[i].x; - gd_yoffset = gamebutton_info[i].y; + 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; @@ -12910,8 +15097,13 @@ void CreateGameButtons() 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_TYPE, button_type,