X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=64f521a9dd2c15cad2a6ea300bc779ed7d67c27c;hb=d9b86b7b2ebe0b2be3926656c3bbdcd060ee5811;hp=440016b024f5770cc63b49452494bc1408e1a881;hpb=4add6d49e770bbdcd7dc4176fdec56d6dad16bb2;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 440016b0..64f521a9 100644 --- a/src/game.c +++ b/src/game.c @@ -33,6 +33,15 @@ #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1) #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1) #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1) +#define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1) +#define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1) +#define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1) +#define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1) +#define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1) +#define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1) +#define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1) + +#define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0) /* for DigField() */ #define DF_NO_PUSH 0 @@ -40,21 +49,23 @@ #define DF_SNAP 2 /* for MovePlayer() */ -#define MF_NO_ACTION 0 -#define MF_MOVING 1 -#define MF_ACTION 2 +#define MP_NO_ACTION 0 +#define MP_MOVING 1 +#define MP_ACTION 2 +#define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION) /* for ScrollPlayer() */ #define SCROLL_INIT 0 #define SCROLL_GO_ON 1 -/* for Explode() */ +/* for Bang()/Explode() */ #define EX_PHASE_START 0 #define EX_TYPE_NONE 0 #define EX_TYPE_NORMAL (1 << 0) #define EX_TYPE_CENTER (1 << 1) #define EX_TYPE_BORDER (1 << 2) #define EX_TYPE_CROSS (1 << 3) +#define EX_TYPE_DYNA (1 << 4) #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER) /* special positions in the game control window (relative to control window) */ @@ -129,13 +140,21 @@ RND(element_info[e].ce_value_random_initial)) #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \ RND((c)->delay_random * (c)->delay_frames)) +#define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \ + RND((c)->delay_random)) -#define GET_TARGET_ELEMENT(e, ch) \ - ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \ - (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e)) +#if 1 +#define GET_VALID_RUNTIME_ELEMENT(e) \ + ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e)) +#else +#define GET_VALID_FILE_ELEMENT(e) \ + ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e)) +#endif -#define GET_VALID_PLAYER_ELEMENT(e) \ - ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1) +#define GET_TARGET_ELEMENT(e, ch) \ + ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \ + (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \ + (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e)) #define CAN_GROW_INTO(e) \ ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable)) @@ -171,6 +190,13 @@ #define SATELLITE_CAN_ENTER_FIELD(x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0) +#define ANDROID_CAN_ENTER_FIELD(e, x, y) \ + ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT) + +#define ANDROID_CAN_CLONE_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \ + CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT))) + #define ENEMY_CAN_ENTER_FIELD(e, x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0) @@ -198,13 +224,20 @@ #define SPRING_CAN_ENTER_FIELD(e, x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0) +#define SPRING_CAN_BUMP_FROM_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \ + Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE)) + +#if 0 #define GROUP_NR(e) ((e) - EL_GROUP_START) -#define MOVE_ENTER_EL(e) (element_info[e].move_enter_element) #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE) #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START)) #define IS_EQUAL_OR_IN_GROUP(e, ge) \ (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge)) +#endif + +#define MOVE_ENTER_EL(e) (element_info[e].move_enter_element) #define CE_ENTER_FIELD_COND(e, x, y) \ (!IS_PLAYER(x, y) && \ @@ -234,6 +267,9 @@ /* forward declaration for internal use */ +static void CreateField(int, int, int); + +static void SetPlayerWaiting(struct PlayerInfo *, boolean); static void AdvanceFrameAndPlayerCounters(int); static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int); @@ -257,7 +293,9 @@ static void TestIfElementHitsCustomElement(int, int, int); static void TestIfElementSmashesCustomElement(int, int, int); #endif -static void ChangeElement(int, int, int); +static void HandleElementChange(int, int, int); +static void ExecuteCustomElementAction(int, int, int, int); +static boolean ChangeElement(int, int, int, int); static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int); #define CheckTriggeredElementChange(x, y, e, ev) \ @@ -298,19 +336,24 @@ static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS]; /* ------------------------------------------------------------------------- */ /* forward declaration for changer functions */ -static void InitBuggyBase(int x, int y); -static void WarnBuggyBase(int x, int y); +static void InitBuggyBase(int, int); +static void WarnBuggyBase(int, int); + +static void InitTrap(int, int); +static void ActivateTrap(int, int); +static void ChangeActiveTrap(int, int); -static void InitTrap(int x, int y); -static void ActivateTrap(int x, int y); -static void ChangeActiveTrap(int x, int y); +static void InitRobotWheel(int, int); +static void RunRobotWheel(int, int); +static void StopRobotWheel(int, int); -static void InitRobotWheel(int x, int y); -static void RunRobotWheel(int x, int y); -static void StopRobotWheel(int x, int y); +static void InitTimegateWheel(int, int); +static void RunTimegateWheel(int, int); -static void InitTimegateWheel(int x, int y); -static void RunTimegateWheel(int x, int y); +static void InitMagicBallDelay(int, int); +static void ActivateMagicBall(int, int); + +static void InitDiagonalMovingElement(int, int); struct ChangingElementInfo { @@ -477,6 +520,38 @@ static struct ChangingElementInfo change_delay_list[] = RunTimegateWheel, NULL }, + { + EL_EMC_MAGIC_BALL_ACTIVE, + EL_EMC_MAGIC_BALL_ACTIVE, + 0, + InitMagicBallDelay, + NULL, + ActivateMagicBall + }, + { + EL_EMC_SPRING_BUMPER_ACTIVE, + EL_EMC_SPRING_BUMPER, + 8, + NULL, + NULL, + NULL + }, + { + EL_DIAGONAL_SHRINKING, + EL_UNDEFINED, + 0, + NULL, + NULL, + NULL + }, + { + EL_DIAGONAL_GROWING, + EL_UNDEFINED, + 0, + NULL, + NULL, + InitDiagonalMovingElement + }, { EL_UNDEFINED, @@ -596,6 +671,80 @@ static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS]; #define CE_PAGE(e, ce) (element_info[e].event_page[ce]) +/* static variables for playfield scan mode (scanning forward or backward) */ +static int playfield_scan_start_x = 0; +static int playfield_scan_start_y = 0; +static int playfield_scan_delta_x = 1; +static int playfield_scan_delta_y = 1; + +#define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \ + (y) >= 0 && (y) <= lev_fieldy - 1; \ + (y) += playfield_scan_delta_y) \ + for ((x) = playfield_scan_start_x; \ + (x) >= 0 && (x) <= lev_fieldx - 1; \ + (x) += playfield_scan_delta_x) \ + +static void InitPlayfieldScanModeVars() +{ + if (game.use_reverse_scan_direction) + { + playfield_scan_start_x = lev_fieldx - 1; + playfield_scan_start_y = lev_fieldy - 1; + + playfield_scan_delta_x = -1; + playfield_scan_delta_y = -1; + } + else + { + playfield_scan_start_x = 0; + playfield_scan_start_y = 0; + + playfield_scan_delta_x = 1; + playfield_scan_delta_y = 1; + } +} + +static void InitPlayfieldScanMode(int mode) +{ + game.use_reverse_scan_direction = + (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE); + + InitPlayfieldScanModeVars(); +} + +static int get_move_delay_from_stepsize(int move_stepsize) +{ + move_stepsize = + MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX); + + /* make sure that stepsize value is always a power of 2 */ + move_stepsize = (1 << log_2(move_stepsize)); + + return TILEX / move_stepsize; +} + +static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize, + boolean init_game) +{ + int move_delay = get_move_delay_from_stepsize(move_stepsize); + boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE); + + /* do no immediately change move delay -- the player might just be moving */ + player->move_delay_value_next = move_delay; + + /* information if player can move must be set separately */ + player->cannot_move = cannot_move; + + if (init_game) + { + player->move_delay = game.initial_move_delay; + player->move_delay_value = game.initial_move_delay_value; + + player->move_delay_value_next = -1; + + player->move_delay_reset_counter = 0; + } +} void GetPlayerConfig() { @@ -682,7 +831,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) } else { - stored_player[0].use_murphy_graphic = TRUE; + stored_player[0].use_murphy = TRUE; } Feld[x][y] = EL_PLAYER_1; @@ -838,6 +987,10 @@ static void InitField(int x, int y, boolean init_game) MovDelay[x][y] = 96; break; + case EL_EM_DYNAMITE_ACTIVE: + MovDelay[x][y] = 32; + break; + case EL_LAMP: local_player->lights_still_needed++; break; @@ -891,9 +1044,32 @@ static void InitField(int x, int y, boolean init_game) game.light_time_left = level.time_light * FRAMES_PER_SECOND; break; + case EL_EMC_MAGIC_BALL: + if (game.ball_state) + Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE; + break; + + case EL_EMC_MAGIC_BALL_SWITCH: + if (game.ball_state) + Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE; + break; + default: +#if 1 + if (IS_CUSTOM_ELEMENT(element)) + { + if (CAN_MOVE(element)) + InitMovDir(x, y); + +#if USE_NEW_CUSTOM_VALUE + if (!element_info[element].use_last_ce_value || init_game) + CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]); +#endif + } +#else if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element)) InitMovDir(x, y); +#endif else if (IS_GROUP_ELEMENT(element)) { struct ElementGroupInfo *group = element_info[element].group; @@ -919,6 +1095,13 @@ static void InitField(int x, int y, boolean init_game) break; } +#if 1 + if (!init_game) + CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X); +#endif + +#if 0 + #if USE_NEW_CUSTOM_VALUE #if 1 @@ -927,6 +1110,8 @@ static void InitField(int x, int y, boolean init_game) CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial; #endif +#endif + #endif } @@ -1066,6 +1251,7 @@ void DrawGameDoorValues() DrawGameValue_Keys(stored_player[i].key); } +#if 0 static void resolve_group_element(int group_element, int recursion_depth) { static int group_nr; @@ -1109,7 +1295,7 @@ static void resolve_group_element(int group_element, int recursion_depth) } } } - +#endif /* ============================================================================= @@ -1121,7 +1307,7 @@ static void resolve_group_element(int group_element, int recursion_depth) static void InitGameEngine() { - int i, j, k, l; + int i, j, k, l, x, y; /* set game engine from tape file when re-playing, else from level file */ game.engine_version = (tape.playing ? tape.engine_version : @@ -1193,8 +1379,43 @@ static void InitGameEngine() game.use_block_last_field_bug = (game.engine_version < VERSION_IDENT(3,1,1,0)); + /* + Summary of bugfix/change: + Changed behaviour of CE changes with multiple changes per single frame. + + Fixed/changed in version: + 3.2.0-6 + + Description: + Before 3.2.0-6, only one single CE change was allowed in each engine frame. + This resulted in race conditions where CEs seem to behave strange in some + situations (where triggered CE changes were just skipped because there was + already a CE change on that tile in the playfield in that engine frame). + Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME. + (The number of changes per frame must be limited in any case, because else + it is easily possible to define CE changes that would result in an infinite + loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value + should be set large enough so that it would only be reached in cases where + the corresponding CE change conditions run into a loop. Therefore, it seems + to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the + maximal number of change pages for custom elements.) + + Affected levels/tapes: + Probably many. + */ + +#if USE_ONLY_ONE_CHANGE_PER_FRAME + game.max_num_changes_per_frame = 1; +#else + game.max_num_changes_per_frame = + (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32); +#endif + /* ---------------------------------------------------------------------- */ + /* default scan direction: scan playfield from top/left to bottom/right */ + InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL); + /* dynamically adjust element properties according to game engine version */ InitElementPropertiesEngine(game.engine_version); @@ -1206,6 +1427,7 @@ static void InitGameEngine() printf(" => game.engine_version == %06d\n", game.engine_version); #endif +#if 0 /* ---------- recursively resolve group elements ------------------------- */ for (i = 0; i < MAX_NUM_ELEMENTS; i++) @@ -1214,12 +1436,19 @@ static void InitGameEngine() for (i = 0; i < NUM_GROUP_ELEMENTS; i++) resolve_group_element(EL_GROUP_START + i, 0); +#endif /* ---------- initialize player's initial move delay --------------------- */ +#if 1 + /* dynamically adjust player properties according to level information */ + game.initial_move_delay_value = + get_move_delay_from_stepsize(level.initial_player_stepsize); +#else /* dynamically adjust player properties according to level information */ game.initial_move_delay_value = (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED); +#endif /* dynamically adjust player properties according to game engine version */ game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ? @@ -1456,8 +1685,63 @@ static void InitGameEngine() for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++) element_info[access_direction_list[i].element].access_direction = access_direction_list[i].direction; + + /* ---------- initialize explosion content ------------------------------- */ + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + { + if (IS_CUSTOM_ELEMENT(i)) + continue; + + for (y = 0; y < 3; y++) for (x = 0; x < 3; x++) + { + /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */ + + element_info[i].content.e[x][y] = + (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW : + i == EL_PLAYER_2 ? EL_EMERALD_RED : + i == EL_PLAYER_3 ? EL_EMERALD : + i == EL_PLAYER_4 ? EL_EMERALD_PURPLE : + i == EL_MOLE ? EL_EMERALD_RED : + i == EL_PENGUIN ? EL_EMERALD_PURPLE : + i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) : + i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND : + i == EL_SP_ELECTRON ? EL_SP_INFOTRON : + i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content : + i == EL_WALL_EMERALD ? EL_EMERALD : + i == EL_WALL_DIAMOND ? EL_DIAMOND : + i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND : + i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW : + i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED : + i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE : + i == EL_WALL_PEARL ? EL_PEARL : + i == EL_WALL_CRYSTAL ? EL_CRYSTAL : + EL_EMPTY); + } + } } +int get_num_special_action(int element, int action_first, int action_last) +{ + int num_special_action = 0; + int i, j; + + for (i = action_first; i <= action_last; i++) + { + boolean found = FALSE; + + for (j = 0; j < NUM_DIRECTIONS; j++) + if (el_act_dir2img(element, i, j) != + el_act_dir2img(element, ACTION_DEFAULT, j)) + found = TRUE; + + if (found) + num_special_action++; + else + break; + } + + return num_special_action; +} /* ============================================================================= @@ -1472,7 +1756,7 @@ 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 */ - int i, j, k, x, y; + int i, j, x, y; InitGameEngine(); @@ -1516,7 +1800,10 @@ void InitGame() player->Frame = 0; player->StepFrame = 0; - player->use_murphy_graphic = FALSE; + player->use_murphy = FALSE; + player->artwork_element = + (level.use_artwork_element[i] ? level.artwork_element[i] : + player->element_nr); player->block_last_field = FALSE; /* initialized in InitPlayerField() */ player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */ @@ -1553,38 +1840,13 @@ void InitGame() player->special_action_bored = ACTION_DEFAULT; player->special_action_sleeping = ACTION_DEFAULT; - player->num_special_action_bored = 0; - player->num_special_action_sleeping = 0; - - /* determine number of special actions for bored and sleeping animation */ - for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++) - { - boolean found = FALSE; - - for (k = 0; k < NUM_DIRECTIONS; k++) - if (el_act_dir2img(player->element_nr, j, k) != - el_act_dir2img(player->element_nr, ACTION_DEFAULT, k)) - found = TRUE; - - if (found) - player->num_special_action_bored++; - else - break; - } - for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++) - { - boolean found = FALSE; - - for (k = 0; k < NUM_DIRECTIONS; k++) - if (el_act_dir2img(player->element_nr, j, k) != - el_act_dir2img(player->element_nr, ACTION_DEFAULT, k)) - found = TRUE; - - if (found) - player->num_special_action_sleeping++; - else - break; - } + /* set number of special actions for bored and sleeping animation */ + player->num_special_action_bored = + get_num_special_action(player->artwork_element, + ACTION_BORING_1, ACTION_BORING_LAST); + player->num_special_action_sleeping = + get_num_special_action(player->artwork_element, + ACTION_SLEEPING_1, ACTION_SLEEPING_LAST); player->switch_x = -1; player->switch_y = -1; @@ -1594,6 +1856,9 @@ void InitGame() player->show_envelope = 0; +#if 1 + SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE); +#else player->move_delay = game.initial_move_delay; player->move_delay_value = game.initial_move_delay_value; @@ -1601,6 +1866,9 @@ void InitGame() player->move_delay_reset_counter = 0; + player->cannot_move = FALSE; +#endif + player->push_delay = -1; /* initialized when pushing starts */ player->push_delay_value = game.initial_push_delay_value; @@ -1657,8 +1925,16 @@ void InitGame() game.gravity = level.initial_gravity; game.explosions_delayed = TRUE; + game.lenses_time_left = 0; + game.magnify_time_left = 0; + + game.ball_state = level.ball_state_initial; + game.ball_content_nr = 0; + game.envelope_active = FALSE; + game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */ + for (i = 0; i < NUM_BELTS; i++) { game.belt_dir[i] = MV_NONE; @@ -1668,56 +1944,58 @@ void InitGame() for (i = 0; i < MAX_NUM_AMOEBA; i++) AmoebaCnt[i] = AmoebaCnt2[i] = 0; - for (x = 0; x < lev_fieldx; x++) +#if 1 + SCAN_PLAYFIELD(x, y) +#else + for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++) +#endif { - for (y = 0; y < lev_fieldy; y++) - { - Feld[x][y] = level.field[x][y]; - MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0; - ChangeDelay[x][y] = 0; - ChangePage[x][y] = -1; + Feld[x][y] = level.field[x][y]; + MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0; + ChangeDelay[x][y] = 0; + ChangePage[x][y] = -1; #if USE_NEW_CUSTOM_VALUE - CustomValue[x][y] = 0; /* initialized in InitField() */ + CustomValue[x][y] = 0; /* initialized in InitField() */ #endif - Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0; - AmoebaNr[x][y] = 0; - WasJustMoving[x][y] = 0; - WasJustFalling[x][y] = 0; - CheckCollision[x][y] = 0; - Stop[x][y] = FALSE; - Pushed[x][y] = FALSE; + Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0; + AmoebaNr[x][y] = 0; + WasJustMoving[x][y] = 0; + WasJustFalling[x][y] = 0; + CheckCollision[x][y] = 0; + Stop[x][y] = FALSE; + Pushed[x][y] = FALSE; - Changed[x][y] = FALSE; - ChangeEvent[x][y] = -1; + ChangeCount[x][y] = 0; + ChangeEvent[x][y] = -1; - ExplodePhase[x][y] = 0; - ExplodeDelay[x][y] = 0; - ExplodeField[x][y] = EX_TYPE_NONE; + ExplodePhase[x][y] = 0; + ExplodeDelay[x][y] = 0; + ExplodeField[x][y] = EX_TYPE_NONE; - RunnerVisit[x][y] = 0; - PlayerVisit[x][y] = 0; + RunnerVisit[x][y] = 0; + PlayerVisit[x][y] = 0; - GfxFrame[x][y] = 0; - GfxRandom[x][y] = INIT_GFX_RANDOM(); - GfxElement[x][y] = EL_UNDEFINED; - GfxAction[x][y] = ACTION_DEFAULT; - GfxDir[x][y] = MV_NONE; - } + GfxFrame[x][y] = 0; + GfxRandom[x][y] = INIT_GFX_RANDOM(); + GfxElement[x][y] = EL_UNDEFINED; + GfxAction[x][y] = ACTION_DEFAULT; + GfxDir[x][y] = MV_NONE; } - for (y = 0; y < lev_fieldy; y++) +#if 1 + SCAN_PLAYFIELD(x, y) +#else + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { - for (x = 0; x < lev_fieldx; x++) - { - if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y])) - emulate_bd = FALSE; - if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y])) - emulate_sb = FALSE; - if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y])) - emulate_sp = FALSE; + if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y])) + emulate_bd = FALSE; + if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y])) + emulate_sb = FALSE; + if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y])) + emulate_sp = FALSE; - InitField(x, y, TRUE); - } + InitField(x, y, TRUE); } InitBeltMovement(); @@ -1804,6 +2082,8 @@ void InitGame() player->element_nr = some_player->element_nr; #endif + player->artwork_element = some_player->artwork_element; + player->block_last_field = some_player->block_last_field; player->block_delay_adjustment = some_player->block_delay_adjustment; @@ -1819,7 +2099,7 @@ void InitGame() if (tape.playing) { - /* when playing a tape, eliminate all players which do not participate */ + /* when playing a tape, eliminate all players who do not participate */ for (i = 0; i < MAX_PLAYERS; i++) { @@ -1912,14 +2192,30 @@ void InitGame() int start_x = 0, start_y = 0; int found_rating = 0; int found_element = EL_UNDEFINED; + int player_nr = local_player->index_nr; +#if 1 + SCAN_PLAYFIELD(x, y) +#else for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { int element = Feld[x][y]; int content; int xx, yy; boolean is_player; + if (level.use_start_element[player_nr] && + level.start_element[player_nr] == element && + found_rating < 4) + { + start_x = x; + start_y = y; + + found_rating = 4; + found_element = element; + } + if (!IS_CUSTOM_ELEMENT(element)) continue; @@ -1927,6 +2223,7 @@ void InitGame() { for (i = 0; i < element_info[element].num_change_pages; i++) { + /* check for player created from custom element as single target */ content = element_info[element].change_page[i].target_element; is_player = ELEM_IS_PLAYER(content); @@ -1943,6 +2240,7 @@ void InitGame() for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++) { + /* check for player created from custom element as explosion content */ content = element_info[element].content.e[xx][yy]; is_player = ELEM_IS_PLAYER(content); @@ -1960,6 +2258,7 @@ void InitGame() for (i = 0; i < element_info[element].num_change_pages; i++) { + /* check for player created from custom element as extended target */ content = element_info[element].change_page[i].target_content.e[xx][yy]; @@ -2288,12 +2587,17 @@ void GameWon() { if (!tape.playing && !setup.sound_loops) PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE); - if (TimeLeft > 0 && !(TimeLeft % 10)) - RaiseScore(level.score[SC_TIME_BONUS]); - if (TimeLeft > 100 && !(TimeLeft % 10)) + + if (TimeLeft > 100 && TimeLeft % 10 == 0) + { TimeLeft -= 10; + RaiseScore(level.score[SC_TIME_BONUS] * 10); + } else + { TimeLeft--; + RaiseScore(level.score[SC_TIME_BONUS]); + } DrawGameValue_Time(TimeLeft); @@ -2316,12 +2620,17 @@ void GameWon() { if (!tape.playing && !setup.sound_loops) PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE); - if (TimePlayed < 999 && !(TimePlayed % 10)) - RaiseScore(level.score[SC_TIME_BONUS]); - if (TimePlayed < 900 && !(TimePlayed % 10)) + + if (TimePlayed < 900 && TimePlayed % 10 == 0) + { TimePlayed += 10; + RaiseScore(level.score[SC_TIME_BONUS] * 10); + } else + { TimePlayed++; + RaiseScore(level.score[SC_TIME_BONUS]); + } DrawGameValue_Time(TimePlayed); @@ -2354,6 +2663,13 @@ void GameWon() BackToFront(); +#if 0 + if (tape.playing) + printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n"); + else + printf("::: NO TAPE PLAYING -> SAVING SCORE\n"); +#endif + if (tape.playing) return; @@ -2514,14 +2830,33 @@ static void ResetRandomAnimationValue(int x, int y) static void ResetGfxAnimation(int x, int y) { +#if 0 + int element, graphic; +#endif + GfxFrame[x][y] = 0; GfxAction[x][y] = ACTION_DEFAULT; GfxDir[x][y] = MovDir[x][y]; + +#if 0 + element = Feld[x][y]; + graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + + if (graphic_info[graphic].anim_global_sync) + GfxFrame[x][y] = FrameCounter; + else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) + GfxFrame[x][y] = CustomValue[x][y]; + else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + GfxFrame[x][y] = element_info[element].collect_score; +#endif } void InitMovingField(int x, int y, int direction) { int element = Feld[x][y]; +#if 0 + int graphic; +#endif int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); int newx = x + dx; @@ -2535,6 +2870,17 @@ void InitMovingField(int x, int y, int direction) GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ? ACTION_FALLING : ACTION_MOVING); +#if 0 + graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + + if (graphic_info[graphic].anim_global_sync) + GfxFrame[x][y] = FrameCounter; + else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) + GfxFrame[x][y] = CustomValue[x][y]; + else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + GfxFrame[x][y] = element_info[element].collect_score; +#endif + /* this is needed for CEs with property "can move" / "not moving" */ if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */ @@ -2558,8 +2904,13 @@ void InitMovingField(int x, int y, int direction) void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y) { int direction = MovDir[x][y]; +#if 1 + int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0); + int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0); +#else int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); +#endif *goes_to_x = newx; *goes_to_y = newy; @@ -2746,7 +3097,7 @@ void CheckDynamite(int x, int y) Bang(x, y); } -void DrawRelocatePlayer(struct PlayerInfo *player) +void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation) { boolean ffwd_delay = (tape.playing && tape.fast_forward); boolean no_delay = (tape.warp_forward); @@ -2755,19 +3106,19 @@ void DrawRelocatePlayer(struct PlayerInfo *player) int jx = player->jx; int jy = player->jy; - if (level.instant_relocation) + if (quick_relocation) { int offset = (setup.scroll_delay ? 3 : 0); if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy))) { - scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left : - local_player->jx > SBX_Right + MIDPOSX ? SBX_Right : - local_player->jx - MIDPOSX); + scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left : + player->jx > SBX_Right + MIDPOSX ? SBX_Right : + player->jx - MIDPOSX); - scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : - local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : - local_player->jy - MIDPOSY); + scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : + player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : + player->jy - MIDPOSY); } else { @@ -2801,13 +3152,13 @@ void DrawRelocatePlayer(struct PlayerInfo *player) int dx = 0, dy = 0; int fx = FX, fy = FY; - scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left : - local_player->jx > SBX_Right + MIDPOSX ? SBX_Right : - local_player->jx - MIDPOSX); + scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left : + player->jx > SBX_Right + MIDPOSX ? SBX_Right : + player->jx - MIDPOSX); - scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : - local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : - local_player->jy - MIDPOSY); + scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : + player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : + player->jy - MIDPOSY); dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0); dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0); @@ -2842,8 +3193,9 @@ void DrawRelocatePlayer(struct PlayerInfo *player) void RelocatePlayer(int jx, int jy, int el_player_raw) { - int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw); - struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1]; + int el_player = GET_PLAYER_ELEMENT(el_player_raw); + int player_nr = GET_PLAYER_NR(el_player); + struct PlayerInfo *player = &stored_player[player_nr]; boolean ffwd_delay = (tape.playing && tape.fast_forward); boolean no_delay = (tape.warp_forward); int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay); @@ -2914,8 +3266,14 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) InitField(jx, jy, FALSE); } +#if 1 + /* only visually relocate centered player */ + if (player->index_nr == game.centered_player_nr) + DrawRelocatePlayer(player, level.instant_relocation); +#else if (player == local_player) /* only visually relocate local player */ - DrawRelocatePlayer(player); + DrawRelocatePlayer(player, level.instant_relocation); +#endif TestIfPlayerTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); @@ -2946,6 +3304,7 @@ void Explode(int ex, int ey, int phase, int mode) if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */ { int center_element = Feld[ex][ey]; + int artwork_element, explosion_element; /* set these values later */ #if 0 /* --- This is only really needed (and now handled) in "Impact()". --- */ @@ -2956,10 +3315,13 @@ void Explode(int ex, int ey, int phase, int mode) return; #endif +#if 0 + /* !!! at this place, the center element may be EL_BLOCKED !!! */ if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER || mode == EX_TYPE_CROSS) - PlayLevelSoundAction(ex, ey, ACTION_EXPLODING); + PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING); +#endif /* remove things displayed in background while burning dynamite */ if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey])) @@ -2973,7 +3335,35 @@ void Explode(int ex, int ey, int phase, int mode) Feld[ex][ey] = center_element; } + /* now "center_element" is finally determined -- set related values now */ + artwork_element = center_element; /* for custom player artwork */ + explosion_element = center_element; /* for custom player artwork */ + + if (IS_PLAYER(ex, ey)) + { + int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]); + + artwork_element = stored_player[player_nr].artwork_element; + + if (level.use_explosion_element[player_nr]) + { + explosion_element = level.explosion_element[player_nr]; + artwork_element = explosion_element; + } + } + +#if 1 + if (mode == EX_TYPE_NORMAL || + mode == EX_TYPE_CENTER || + mode == EX_TYPE_CROSS) + PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING); +#endif + +#if 1 + last_phase = element_info[explosion_element].explosion_delay + 1; +#else last_phase = element_info[center_element].explosion_delay + 1; +#endif for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++) { @@ -3046,6 +3436,11 @@ void Explode(int ex, int ey, int phase, int mode) if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey)) { +#if 1 + int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1; + + Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr; +#else switch(StorePlayer[ex][ey]) { case EL_PLAYER_2: @@ -3062,10 +3457,33 @@ void Explode(int ex, int ey, int phase, int mode) Store[x][y] = EL_PLAYER_IS_EXPLODING_1; break; } +#endif - if (PLAYERINFO(ex, ey)->use_murphy_graphic) + if (PLAYERINFO(ex, ey)->use_murphy) Store[x][y] = EL_EMPTY; } +#if 1 + /* !!! check this case -- currently needed for rnd_rado_negundo_v, + !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */ + else if (ELEM_IS_PLAYER(center_element)) + Store[x][y] = EL_EMPTY; + else if (center_element == EL_YAMYAM) + Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy]; + else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY) + Store[x][y] = element_info[center_element].content.e[xx][yy]; +#if 1 + /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE" + (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond + otherwise) -- FIX THIS !!! */ + else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY) + Store[x][y] = element_info[element].content.e[1][1]; +#else + else if (!CAN_EXPLODE(element)) + Store[x][y] = element_info[element].content.e[1][1]; +#endif + else + Store[x][y] = EL_EMPTY; +#else else if (center_element == EL_MOLE) Store[x][y] = EL_EMERALD_RED; else if (center_element == EL_PENGUIN) @@ -3103,13 +3521,14 @@ void Explode(int ex, int ey, int phase, int mode) Store[x][y] = element_info[element].content.e[1][1]; else Store[x][y] = EL_EMPTY; +#endif if (x != ex || y != ey || mode == EX_TYPE_BORDER || center_element == EL_AMOEBA_TO_DIAMOND) Store2[x][y] = element; Feld[x][y] = EL_EXPLOSION; - GfxElement[x][y] = center_element; + GfxElement[x][y] = artwork_element; ExplodePhase[x][y] = 1; ExplodeDelay[x][y] = last_phase; @@ -3203,12 +3622,18 @@ void Explode(int ex, int ey, int phase, int mode) /* player can escape from explosions and might therefore be still alive */ if (element >= EL_PLAYER_IS_EXPLODING_1 && element <= EL_PLAYER_IS_EXPLODING_4) - Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ? - EL_EMPTY : - element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW : - element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED : - element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD : - EL_EMERALD_PURPLE); + { + int player_nr = element - EL_PLAYER_IS_EXPLODING_1; + int explosion_element = EL_PLAYER_1 + player_nr; + int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2); + int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2); + + if (level.use_explosion_element[player_nr]) + explosion_element = level.explosion_element[player_nr]; + + Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY : + element_info[explosion_element].content.e[xx][yy]); + } /* restore probably existing indestructible background element */ if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y])) @@ -3316,13 +3741,24 @@ void DynaExplode(int ex, int ey) void Bang(int x, int y) { int element = MovingOrBlocked2Element(x, y); + int explosion_type = EX_TYPE_NORMAL; if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y)) { struct PlayerInfo *player = PLAYERINFO(x, y); - element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY : + element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY : player->element_nr); + + if (level.use_explosion_element[player->index_nr]) + { + int explosion_element = level.explosion_element[player->index_nr]; + + if (element_info[explosion_element].explosion_type == EXPLODES_CROSS) + explosion_type = EX_TYPE_CROSS; + else if (element_info[explosion_element].explosion_type == EXPLODES_1X1) + explosion_type = EX_TYPE_CENTER; + } } switch(element) @@ -3337,8 +3773,8 @@ void Bang(int x, int y) case EL_PACMAN: case EL_MOLE: RaiseScoreElement(element); - Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL); break; + case EL_DYNABOMB_PLAYER_1_ACTIVE: case EL_DYNABOMB_PLAYER_2_ACTIVE: case EL_DYNABOMB_PLAYER_3_ACTIVE: @@ -3346,27 +3782,30 @@ void Bang(int x, int y) case EL_DYNABOMB_INCREASE_NUMBER: case EL_DYNABOMB_INCREASE_SIZE: case EL_DYNABOMB_INCREASE_POWER: - DynaExplode(x, y); + explosion_type = EX_TYPE_DYNA; break; + case EL_PENGUIN: case EL_LAMP: case EL_LAMP_ACTIVE: case EL_AMOEBA_TO_DIAMOND: - if (IS_PLAYER(x, y)) - Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL); - else - Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER); + if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */ + explosion_type = EX_TYPE_CENTER; break; + default: if (element_info[element].explosion_type == EXPLODES_CROSS) - Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS); + explosion_type = EX_TYPE_CROSS; else if (element_info[element].explosion_type == EXPLODES_1X1) - Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER); - else - Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL); + explosion_type = EX_TYPE_CENTER; break; } + if (explosion_type == EX_TYPE_DYNA) + DynaExplode(x, y); + else + Explode(x, y, EX_PHASE_START, explosion_type); + CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X); } @@ -3421,25 +3860,26 @@ static void InitBeltMovement() } } - for (y = 0; y < lev_fieldy; y++) +#if 1 + SCAN_PLAYFIELD(x, y) +#else + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { - for (x = 0; x < lev_fieldx; x++) - { - int element = Feld[x][y]; + int element = Feld[x][y]; - for (i = 0; i < NUM_BELTS; i++) + for (i = 0; i < NUM_BELTS; i++) + { + if (IS_BELT(element) && game.belt_dir[i] != MV_NONE) { - if (IS_BELT(element) && game.belt_dir[i] != MV_NONE) - { - int e_belt_nr = getBeltNrFromBeltElement(element); - int belt_nr = i; + int e_belt_nr = getBeltNrFromBeltElement(element); + int belt_nr = i; - if (e_belt_nr == belt_nr) - { - int belt_part = Feld[x][y] - belt_base_element[belt_nr]; + if (e_belt_nr == belt_nr) + { + int belt_part = Feld[x][y] - belt_base_element[belt_nr]; - Feld[x][y] = belt_base_active_element[belt_nr] + belt_part; - } + Feld[x][y] = belt_base_active_element[belt_nr] + belt_part; } } } @@ -3504,45 +3944,46 @@ static void ToggleBeltSwitch(int x, int y) graphic_info[graphic].anim_mode |= ANIM_REVERSE; } - for (yy = 0; yy < lev_fieldy; yy++) +#if 1 + SCAN_PLAYFIELD(xx, yy) +#else + for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++) +#endif { - for (xx = 0; xx < lev_fieldx; xx++) + int element = Feld[xx][yy]; + + if (IS_BELT_SWITCH(element)) { - int element = Feld[xx][yy]; + int e_belt_nr = getBeltNrFromBeltSwitchElement(element); - if (IS_BELT_SWITCH(element)) + if (e_belt_nr == belt_nr) { - int e_belt_nr = getBeltNrFromBeltSwitchElement(element); - - if (e_belt_nr == belt_nr) - { - Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr; - DrawLevelField(xx, yy); - } + Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr; + DrawLevelField(xx, yy); } - else if (IS_BELT(element) && belt_dir != MV_NONE) - { - int e_belt_nr = getBeltNrFromBeltElement(element); + } + else if (IS_BELT(element) && belt_dir != MV_NONE) + { + int e_belt_nr = getBeltNrFromBeltElement(element); - if (e_belt_nr == belt_nr) - { - int belt_part = Feld[xx][yy] - belt_base_element[belt_nr]; + if (e_belt_nr == belt_nr) + { + int belt_part = Feld[xx][yy] - belt_base_element[belt_nr]; - Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part; - DrawLevelField(xx, yy); - } + Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part; + DrawLevelField(xx, yy); } - else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE) - { - int e_belt_nr = getBeltNrFromBeltActiveElement(element); + } + else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE) + { + int e_belt_nr = getBeltNrFromBeltActiveElement(element); - if (e_belt_nr == belt_nr) - { - int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr]; + if (e_belt_nr == belt_nr) + { + int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr]; - Feld[xx][yy] = belt_base_element[belt_nr] + belt_part; - DrawLevelField(xx, yy); - } + Feld[xx][yy] = belt_base_element[belt_nr] + belt_part; + DrawLevelField(xx, yy); } } } @@ -3554,32 +3995,33 @@ static void ToggleSwitchgateSwitch(int x, int y) game.switchgate_pos = !game.switchgate_pos; - for (yy = 0; yy < lev_fieldy; yy++) +#if 1 + SCAN_PLAYFIELD(xx, yy) +#else + for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++) +#endif { - for (xx = 0; xx < lev_fieldx; xx++) - { - int element = Feld[xx][yy]; + int element = Feld[xx][yy]; - if (element == EL_SWITCHGATE_SWITCH_UP || - element == EL_SWITCHGATE_SWITCH_DOWN) - { - Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos; - DrawLevelField(xx, yy); - } - else if (element == EL_SWITCHGATE_OPEN || - element == EL_SWITCHGATE_OPENING) - { - Feld[xx][yy] = EL_SWITCHGATE_CLOSING; + if (element == EL_SWITCHGATE_SWITCH_UP || + element == EL_SWITCHGATE_SWITCH_DOWN) + { + Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos; + DrawLevelField(xx, yy); + } + else if (element == EL_SWITCHGATE_OPEN || + element == EL_SWITCHGATE_OPENING) + { + Feld[xx][yy] = EL_SWITCHGATE_CLOSING; - PlayLevelSoundAction(xx, yy, ACTION_CLOSING); - } - else if (element == EL_SWITCHGATE_CLOSED || - element == EL_SWITCHGATE_CLOSING) - { - Feld[xx][yy] = EL_SWITCHGATE_OPENING; + PlayLevelSoundAction(xx, yy, ACTION_CLOSING); + } + else if (element == EL_SWITCHGATE_CLOSED || + element == EL_SWITCHGATE_CLOSING) + { + Feld[xx][yy] = EL_SWITCHGATE_OPENING; - PlayLevelSoundAction(xx, yy, ACTION_OPENING); - } + PlayLevelSoundAction(xx, yy, ACTION_OPENING); } } } @@ -3604,50 +4046,167 @@ static void RedrawAllLightSwitchesAndInvisibleElements() { int x, y; - for (y = 0; y < lev_fieldy; y++) +#if 1 + SCAN_PLAYFIELD(x, y) +#else + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { - for (x = 0; x < lev_fieldx; x++) + int element = Feld[x][y]; + + if (element == EL_LIGHT_SWITCH && + game.light_time_left > 0) { - int element = Feld[x][y]; + Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE; + DrawLevelField(x, y); + } + else if (element == EL_LIGHT_SWITCH_ACTIVE && + game.light_time_left == 0) + { + Feld[x][y] = EL_LIGHT_SWITCH; + DrawLevelField(x, y); + } + else if (element == EL_EMC_DRIPPER && + game.light_time_left > 0) + { + Feld[x][y] = EL_EMC_DRIPPER_ACTIVE; + DrawLevelField(x, y); + } + else if (element == EL_EMC_DRIPPER_ACTIVE && + game.light_time_left == 0) + { + Feld[x][y] = EL_EMC_DRIPPER; + DrawLevelField(x, y); + } + else if (element == EL_INVISIBLE_STEELWALL || + element == EL_INVISIBLE_WALL || + element == EL_INVISIBLE_SAND) + { + if (game.light_time_left > 0) + Feld[x][y] = getInvisibleActiveFromInvisibleElement(element); - if (element == EL_LIGHT_SWITCH && - game.light_time_left > 0) - { - Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE; - DrawLevelField(x, y); - } - else if (element == EL_LIGHT_SWITCH_ACTIVE && - game.light_time_left == 0) - { - Feld[x][y] = EL_LIGHT_SWITCH; - DrawLevelField(x, y); - } - else if (element == EL_INVISIBLE_STEELWALL || - element == EL_INVISIBLE_WALL || - element == EL_INVISIBLE_SAND) - { - if (game.light_time_left > 0) - Feld[x][y] = getInvisibleActiveFromInvisibleElement(element); + DrawLevelField(x, y); - DrawLevelField(x, y); + /* uncrumble neighbour fields, if needed */ + if (element == EL_INVISIBLE_SAND) + DrawLevelFieldCrumbledSandNeighbours(x, y); + } + else if (element == EL_INVISIBLE_STEELWALL_ACTIVE || + element == EL_INVISIBLE_WALL_ACTIVE || + element == EL_INVISIBLE_SAND_ACTIVE) + { + if (game.light_time_left == 0) + Feld[x][y] = getInvisibleFromInvisibleActiveElement(element); - /* uncrumble neighbour fields, if needed */ - if (element == EL_INVISIBLE_SAND) - DrawLevelFieldCrumbledSandNeighbours(x, y); - } - else if (element == EL_INVISIBLE_STEELWALL_ACTIVE || - element == EL_INVISIBLE_WALL_ACTIVE || - element == EL_INVISIBLE_SAND_ACTIVE) - { - if (game.light_time_left == 0) - Feld[x][y] = getInvisibleFromInvisibleActiveElement(element); + DrawLevelField(x, y); - DrawLevelField(x, y); + /* re-crumble neighbour fields, if needed */ + if (element == EL_INVISIBLE_SAND) + DrawLevelFieldCrumbledSandNeighbours(x, y); + } + } +} - /* re-crumble neighbour fields, if needed */ - if (element == EL_INVISIBLE_SAND) - DrawLevelFieldCrumbledSandNeighbours(x, y); - } +static void RedrawAllInvisibleElementsForLenses() +{ + int x, y; + +#if 1 + SCAN_PLAYFIELD(x, y) +#else + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif + { + int element = Feld[x][y]; + + if (element == EL_EMC_DRIPPER && + game.lenses_time_left > 0) + { + Feld[x][y] = EL_EMC_DRIPPER_ACTIVE; + DrawLevelField(x, y); + } + else if (element == EL_EMC_DRIPPER_ACTIVE && + game.lenses_time_left == 0) + { + Feld[x][y] = EL_EMC_DRIPPER; + DrawLevelField(x, y); + } + else if (element == EL_INVISIBLE_STEELWALL || + element == EL_INVISIBLE_WALL || + element == EL_INVISIBLE_SAND) + { + if (game.lenses_time_left > 0) + Feld[x][y] = getInvisibleActiveFromInvisibleElement(element); + + DrawLevelField(x, y); + + /* uncrumble neighbour fields, if needed */ + if (element == EL_INVISIBLE_SAND) + DrawLevelFieldCrumbledSandNeighbours(x, y); + } + else if (element == EL_INVISIBLE_STEELWALL_ACTIVE || + element == EL_INVISIBLE_WALL_ACTIVE || + element == EL_INVISIBLE_SAND_ACTIVE) + { + if (game.lenses_time_left == 0) + Feld[x][y] = getInvisibleFromInvisibleActiveElement(element); + + DrawLevelField(x, y); + + /* re-crumble neighbour fields, if needed */ + if (element == EL_INVISIBLE_SAND) + DrawLevelFieldCrumbledSandNeighbours(x, y); + } + } +} + +static void RedrawAllInvisibleElementsForMagnifier() +{ + int x, y; + +#if 1 + SCAN_PLAYFIELD(x, y) +#else + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif + { + int element = Feld[x][y]; + + if (element == EL_EMC_FAKE_GRASS && + game.magnify_time_left > 0) + { + Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE; + DrawLevelField(x, y); + } + else if (element == EL_EMC_FAKE_GRASS_ACTIVE && + game.magnify_time_left == 0) + { + Feld[x][y] = EL_EMC_FAKE_GRASS; + DrawLevelField(x, y); + } + else if (IS_GATE_GRAY(element) && + game.magnify_time_left > 0) + { + Feld[x][y] = (IS_RND_GATE_GRAY(element) ? + element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE : + IS_EM_GATE_GRAY(element) ? + element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE : + IS_EMC_GATE_GRAY(element) ? + element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE : + element); + DrawLevelField(x, y); + } + else if (IS_GATE_GRAY_ACTIVE(element) && + game.magnify_time_left == 0) + { + Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ? + element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY : + IS_EM_GATE_GRAY_ACTIVE(element) ? + element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY : + IS_EMC_GATE_GRAY_ACTIVE(element) ? + element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY : + element); + DrawLevelField(x, y); } } } @@ -3669,28 +4228,29 @@ static void ActivateTimegateSwitch(int x, int y) game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND; - for (yy = 0; yy < lev_fieldy; yy++) +#if 1 + SCAN_PLAYFIELD(xx, yy) +#else + for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++) +#endif { - for (xx = 0; xx < lev_fieldx; xx++) - { - int element = Feld[xx][yy]; + int element = Feld[xx][yy]; - if (element == EL_TIMEGATE_CLOSED || - element == EL_TIMEGATE_CLOSING) - { - Feld[xx][yy] = EL_TIMEGATE_OPENING; - PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING); - } - - /* - else if (element == EL_TIMEGATE_SWITCH_ACTIVE) - { - Feld[xx][yy] = EL_TIMEGATE_SWITCH; - DrawLevelField(xx, yy); - } - */ + if (element == EL_TIMEGATE_CLOSED || + element == EL_TIMEGATE_CLOSING) + { + Feld[xx][yy] = EL_TIMEGATE_OPENING; + PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING); + } + /* + else if (element == EL_TIMEGATE_SWITCH_ACTIVE) + { + Feld[xx][yy] = EL_TIMEGATE_SWITCH; + DrawLevelField(xx, yy); } + */ + } Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE; @@ -3718,6 +4278,18 @@ void Impact(int x, int y) ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX) object_hit = FALSE; +#if USE_QUICKSAND_IMPACT_BUGFIX + if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE) + { + RemoveMovingField(x, y + 1); + Feld[x][y + 1] = EL_QUICKSAND_EMPTY; + Feld[x][y + 2] = EL_ROCK; + DrawLevelField(x, y + 2); + + object_hit = TRUE; + } +#endif + if (object_hit) smashed = MovingOrBlocked2Element(x, y + 1); @@ -3787,10 +4359,13 @@ void Impact(int x, int y) EL_BD_MAGIC_WALL_ACTIVE); /* activate magic wall / mill */ - for (yy = 0; yy < lev_fieldy; yy++) - for (xx = 0; xx < lev_fieldx; xx++) - if (Feld[xx][yy] == smashed) - Feld[xx][yy] = activated_magic_wall; +#if 1 + SCAN_PLAYFIELD(xx, yy) +#else + for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++) +#endif + 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; @@ -3929,7 +4504,7 @@ inline static void TurnRoundExt(int x, int y) { static struct { - int x, y; + int dx, dy; } move_xy[] = { { 0, 0 }, @@ -3964,10 +4539,10 @@ inline static void TurnRoundExt(int x, int y) int right_dir = turn[old_move_dir].right; int back_dir = turn[old_move_dir].back; - int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y; - int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y; - int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y; - int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y; + int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy; + int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy; + int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy; + int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy; int left_x = x + left_dx, left_y = y + left_dy; int right_x = x + right_dx, right_y = y + right_dy; @@ -4120,8 +4695,8 @@ inline static void TurnRoundExt(int x, int y) else MovDir[x][y] = back_dir; - xx = x + move_xy[MovDir[x][y]].x; - yy = y + move_xy[MovDir[x][y]].y; + xx = x + move_xy[MovDir[x][y]].dx; + yy = y + move_xy[MovDir[x][y]].dy; if (!IN_LEV_FIELD(xx, yy) || (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))) @@ -4148,8 +4723,8 @@ inline static void TurnRoundExt(int x, int y) else MovDir[x][y] = back_dir; - xx = x + move_xy[MovDir[x][y]].x; - yy = y + move_xy[MovDir[x][y]].y; + xx = x + move_xy[MovDir[x][y]].dx; + yy = y + move_xy[MovDir[x][y]].dy; if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy)) MovDir[x][y] = old_move_dir; @@ -4190,16 +4765,35 @@ inline static void TurnRoundExt(int x, int y) } else if (element == EL_SPRING) { +#if USE_NEW_SPRING_BUMPER + if (MovDir[x][y] & MV_HORIZONTAL) + { + if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) && + !SPRING_CAN_ENTER_FIELD(element, x, y + 1)) + { + Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE; + ResetGfxAnimation(move_x, move_y); + DrawLevelField(move_x, move_y); + + MovDir[x][y] = back_dir; + } + else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) || + SPRING_CAN_ENTER_FIELD(element, x, y + 1)) + MovDir[x][y] = MV_NONE; + } +#else if (MovDir[x][y] & MV_HORIZONTAL && (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) || SPRING_CAN_ENTER_FIELD(element, x, y + 1))) MovDir[x][y] = MV_NONE; +#endif MovDelay[x][y] = 0; } else if (element == EL_ROBOT || element == EL_SATELLITE || - element == EL_PENGUIN) + element == EL_PENGUIN || + element == EL_EMC_ANDROID) { int attr_x = -1, attr_y = -1; @@ -4300,21 +4894,21 @@ inline static void TurnRoundExt(int x, int y) new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy)) + if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy)) return; MovDir[x][y] = new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy)) + if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy)) return; MovDir[x][y] = old_move_dir; return; } } - else /* (element == EL_SATELLITE) */ + else if (element == EL_SATELLITE) { int newx, newy; @@ -4343,6 +4937,144 @@ inline static void TurnRoundExt(int x, int y) return; } } + else if (element == EL_EMC_ANDROID) + { + static int check_pos[16] = + { + -1, /* 0 => (invalid) */ + 7, /* 1 => MV_LEFT */ + 3, /* 2 => MV_RIGHT */ + -1, /* 3 => (invalid) */ + 1, /* 4 => MV_UP */ + 0, /* 5 => MV_LEFT | MV_UP */ + 2, /* 6 => MV_RIGHT | MV_UP */ + -1, /* 7 => (invalid) */ + 5, /* 8 => MV_DOWN */ + 6, /* 9 => MV_LEFT | MV_DOWN */ + 4, /* 10 => MV_RIGHT | MV_DOWN */ + -1, /* 11 => (invalid) */ + -1, /* 12 => (invalid) */ + -1, /* 13 => (invalid) */ + -1, /* 14 => (invalid) */ + -1, /* 15 => (invalid) */ + }; + static struct + { + int dx, dy; + int dir; + } check_xy[8] = + { + { -1, -1, MV_LEFT | MV_UP }, + { 0, -1, MV_UP }, + { +1, -1, MV_RIGHT | MV_UP }, + { +1, 0, MV_RIGHT }, + { +1, +1, MV_RIGHT | MV_DOWN }, + { 0, +1, MV_DOWN }, + { -1, +1, MV_LEFT | MV_DOWN }, + { -1, 0, MV_LEFT }, + }; + int start_pos, check_order; + boolean can_clone = FALSE; + int i; + + /* check if there is any free field around current position */ + for (i = 0; i < 8; i++) + { + int newx = x + check_xy[i].dx; + int newy = y + check_xy[i].dy; + + if (IN_LEV_FIELD_AND_IS_FREE(newx, newy)) + { + can_clone = TRUE; + + break; + } + } + + if (can_clone) /* randomly find an element to clone */ + { + can_clone = FALSE; + + start_pos = check_pos[RND(8)]; + check_order = (RND(2) ? -1 : +1); + + for (i = 0; i < 8; i++) + { + int pos_raw = start_pos + i * check_order; + int pos = (pos_raw + 8) % 8; + int newx = x + check_xy[pos].dx; + int newy = y + check_xy[pos].dy; + + if (ANDROID_CAN_CLONE_FIELD(newx, newy)) + { + element_info[element].move_leave_type = LEAVE_TYPE_LIMITED; + element_info[element].move_leave_element = EL_TRIGGER_ELEMENT; + + Store[x][y] = Feld[newx][newy]; + + can_clone = TRUE; + + break; + } + } + } + + if (can_clone) /* randomly find a direction to move */ + { + can_clone = FALSE; + + start_pos = check_pos[RND(8)]; + check_order = (RND(2) ? -1 : +1); + + for (i = 0; i < 8; i++) + { + int pos_raw = start_pos + i * check_order; + int pos = (pos_raw + 8) % 8; + int newx = x + check_xy[pos].dx; + int newy = y + check_xy[pos].dy; + int new_move_dir = check_xy[pos].dir; + + if (IN_LEV_FIELD_AND_IS_FREE(newx, newy)) + { + MovDir[x][y] = new_move_dir; + MovDelay[x][y] = level.android_clone_time * 8 + 1; + + can_clone = TRUE; + + break; + } + } + } + + if (can_clone) /* cloning and moving successful */ + return; + + /* cannot clone -- try to move towards player */ + + start_pos = check_pos[MovDir[x][y] & 0x0f]; + check_order = (RND(2) ? -1 : +1); + + for (i = 0; i < 3; i++) + { + /* first check start_pos, then previous/next or (next/previous) pos */ + int pos_raw = start_pos + (i < 2 ? i : -1) * check_order; + int pos = (pos_raw + 8) % 8; + int newx = x + check_xy[pos].dx; + int newy = y + check_xy[pos].dy; + int new_move_dir = check_xy[pos].dir; + + if (IS_PLAYER(newx, newy)) + break; + + if (ANDROID_CAN_ENTER_FIELD(element, newx, newy)) + { + MovDir[x][y] = new_move_dir; + MovDelay[x][y] = level.android_move_time * 8 + 1; + + break; + } + } + } } else if (move_pattern == MV_TURNING_LEFT || move_pattern == MV_TURNING_RIGHT || @@ -4582,6 +5314,9 @@ inline static void TurnRoundExt(int x, int y) static void TurnRound(int x, int y) { int direction = MovDir[x][y]; +#if 1 + int element, graphic; +#endif TurnRoundExt(x, y); @@ -4591,7 +5326,19 @@ static void TurnRound(int x, int y) GfxFrame[x][y] = 0; if (MovDelay[x][y]) - GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction); + GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction); + +#if 1 + element = Feld[x][y]; + graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + + if (graphic_info[graphic].anim_global_sync) + GfxFrame[x][y] = FrameCounter; + else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) + GfxFrame[x][y] = CustomValue[x][y]; + else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + GfxFrame[x][y] = element_info[element].collect_score; +#endif } static boolean JustBeingPushed(int x, int y) @@ -5090,6 +5837,7 @@ void StartMoving(int x, int y) else if (CAN_MOVE_INTO_ACID(element) && IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID && + !IS_MV_DIAGONAL(MovDir[x][y]) && (MovDir[x][y] == MV_DOWN || game.engine_version >= VERSION_IDENT(3,1,0,0))) { @@ -5116,7 +5864,7 @@ void StartMoving(int x, int y) } else if (IS_FOOD_PENGUIN(Feld[newx][newy])) { - if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING) + if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING) DrawLevelField(newx, newy); else GfxDir[x][y] = MovDir[x][y] = MV_NONE; @@ -5157,6 +5905,97 @@ void StartMoving(int x, int y) return; } } + else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy)) + { + if (Store[x][y] != EL_EMPTY) + { + boolean can_clone = FALSE; + int xx, yy; + + /* check if element to clone is still there */ + for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++) + { + if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y]) + { + can_clone = TRUE; + + break; + } + } + + /* cannot clone or target field not free anymore -- do not clone */ + if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy)) + Store[x][y] = EL_EMPTY; + } + + if (ANDROID_CAN_ENTER_FIELD(element, newx, newy)) + { + if (IS_MV_DIAGONAL(MovDir[x][y])) + { + int diagonal_move_dir = MovDir[x][y]; + int stored = Store[x][y]; + int change_delay = 8; + int graphic; + + /* android is moving diagonally */ + + CreateField(x, y, EL_DIAGONAL_SHRINKING); + + Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored); + GfxElement[x][y] = EL_EMC_ANDROID; + GfxAction[x][y] = ACTION_SHRINKING; + GfxDir[x][y] = diagonal_move_dir; + ChangeDelay[x][y] = change_delay; + + graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], + GfxDir[x][y]); + + DrawLevelGraphicAnimation(x, y, graphic); + PlayLevelSoundAction(x, y, ACTION_SHRINKING); + + if (Feld[newx][newy] == EL_ACID) + { + SplashAcid(newx, newy); + + return; + } + + CreateField(newx, newy, EL_DIAGONAL_GROWING); + + Store[newx][newy] = EL_EMC_ANDROID; + GfxElement[newx][newy] = EL_EMC_ANDROID; + GfxAction[newx][newy] = ACTION_GROWING; + GfxDir[newx][newy] = diagonal_move_dir; + ChangeDelay[newx][newy] = change_delay; + + graphic = el_act_dir2img(GfxElement[newx][newy], + GfxAction[newx][newy], GfxDir[newx][newy]); + + DrawLevelGraphicAnimation(newx, newy, graphic); + PlayLevelSoundAction(newx, newy, ACTION_GROWING); + + return; + } + else + { + Feld[newx][newy] = EL_EMPTY; + DrawLevelField(newx, newy); + + PlayLevelSoundAction(x, y, ACTION_DIGGING); + } + } + else if (!IS_FREE(newx, newy)) + { +#if 0 + if (IS_PLAYER(x, y)) + DrawPlayerField(x, y); + else + DrawLevelField(x, y); +#endif + + return; + } + } else if (IS_CUSTOM_ELEMENT(element) && CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)) { @@ -5198,6 +6037,11 @@ void StartMoving(int x, int y) } Store[newx][newy] = EL_EMPTY; +#if 1 + /* this makes it possible to leave the removed element again */ + if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element))) + Store[newx][newy] = new_element; +#else if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element))) { int move_leave_element = element_info[element].move_leave_element; @@ -5206,6 +6050,7 @@ void StartMoving(int x, int y) Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ? new_element : move_leave_element); } +#endif if (move_pattern & MV_MAZE_RUNNER_STYLE) { @@ -5401,12 +6246,13 @@ void StartMoving(int x, int y) void ContinueMoving(int x, int y) { int element = Feld[x][y]; - int stored = Store[x][y]; struct ElementInfo *ei = &element_info[element]; int direction = MovDir[x][y]; int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); int newx = x + dx, newy = y + dy; + int stored = Store[x][y]; + int stored_new = Store[newx][newy]; boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y)); boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y)); boolean last_line = (newy == lev_fieldy - 1); @@ -5508,22 +6354,34 @@ void ContinueMoving(int x, int y) MovDelay[newx][newy] = 0; +#if 1 + if (CAN_CHANGE_OR_HAS_ACTION(element)) +#else if (CAN_CHANGE(element)) +#endif { /* copy element change control values to new field */ ChangeDelay[newx][newy] = ChangeDelay[x][y]; ChangePage[newx][newy] = ChangePage[x][y]; - Changed[newx][newy] = Changed[x][y]; + ChangeCount[newx][newy] = ChangeCount[x][y]; ChangeEvent[newx][newy] = ChangeEvent[x][y]; +#if 0 #if USE_NEW_CUSTOM_VALUE CustomValue[newx][newy] = CustomValue[x][y]; +#endif #endif } +#if 1 +#if USE_NEW_CUSTOM_VALUE + CustomValue[newx][newy] = CustomValue[x][y]; +#endif +#endif + ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; - Changed[x][y] = FALSE; + ChangeCount[x][y] = 0; ChangeEvent[x][y] = -1; #if USE_NEW_CUSTOM_VALUE @@ -5539,16 +6397,34 @@ void ContinueMoving(int x, int y) Pushed[x][y] = Pushed[newx][newy] = FALSE; /* some elements can leave other elements behind after moving */ +#if 1 + if (ei->move_leave_element != EL_EMPTY && + (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) && + (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element))) +#else if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY && (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) && (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element))) +#endif { int move_leave_element = ei->move_leave_element; +#if 1 +#if 1 + /* this makes it possible to leave the removed element again */ + if (ei->move_leave_element == EL_TRIGGER_ELEMENT) + move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored); +#else + /* this makes it possible to leave the removed element again */ + if (ei->move_leave_element == EL_TRIGGER_ELEMENT) + move_leave_element = stored; +#endif +#else /* this makes it possible to leave the removed element again */ if (ei->move_leave_type == LEAVE_TYPE_LIMITED && ei->move_leave_element == EL_TRIGGER_ELEMENT) move_leave_element = stored; +#endif Feld[x][y] = move_leave_element; @@ -5624,20 +6500,54 @@ void ContinueMoving(int x, int y) if (pushed_by_player && !game.use_change_when_pushing_bug) { - int dig_side = MV_DIR_OPPOSITE(direction); + int push_side = MV_DIR_OPPOSITE(direction); struct PlayerInfo *player = PLAYERINFO(x, y); CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER, - player->index_bit, dig_side); + player->index_bit, push_side); CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X, - player->index_bit, dig_side); + player->index_bit, push_side); } + if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */ + MovDelay[newx][newy] = 1; + + CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction); + TestIfElementTouchesCustomElement(x, y); /* empty or new element */ +#if 0 + if (ChangePage[newx][newy] != -1) /* delayed change */ + { + int page = ChangePage[newx][newy]; + struct ElementChangeInfo *change = &ei->change_page[page]; + + ChangePage[newx][newy] = -1; + + if (change->can_change) + { + if (ChangeElement(newx, newy, element, page)) + { + if (change->post_change_function) + change->post_change_function(newx, newy); + } + } + + if (change->has_action) + ExecuteCustomElementAction(newx, newy, element, page); + } +#endif + TestIfElementHitsCustomElement(newx, newy, direction); TestIfPlayerTouchesCustomElement(newx, newy); TestIfElementTouchesCustomElement(newx, newy); + +#if 1 + 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)); +#endif } int AmoebeNachbarNr(int ax, int ay) @@ -5706,13 +6616,14 @@ void AmoebenVereinigen(int ax, int ay) AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr]; AmoebaCnt2[old_group_nr] = 0; - for (yy = 0; yy < lev_fieldy; yy++) +#if 1 + SCAN_PLAYFIELD(xx, yy) +#else + for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++) +#endif { - for (xx = 0; xx < lev_fieldx; xx++) - { - if (AmoebaNr[xx][yy] == old_group_nr) - AmoebaNr[xx][yy] = new_group_nr; - } + if (AmoebaNr[xx][yy] == old_group_nr) + AmoebaNr[xx][yy] = new_group_nr; } } } @@ -5735,17 +6646,19 @@ void AmoebeUmwandeln(int ax, int ay) } #endif - for (y = 0; y < lev_fieldy; y++) +#if 1 + SCAN_PLAYFIELD(x, y) +#else + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { - for (x = 0; x < lev_fieldx; x++) + if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr) { - if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr) - { - AmoebaNr[x][y] = 0; - Feld[x][y] = EL_AMOEBA_TO_DIAMOND; - } + AmoebaNr[x][y] = 0; + Feld[x][y] = EL_AMOEBA_TO_DIAMOND; } } + PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ? SND_AMOEBA_TURNING_TO_GEM : SND_AMOEBA_TURNING_TO_ROCK)); @@ -5795,21 +6708,22 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element) } #endif - for (y = 0; y < lev_fieldy; y++) +#if 1 + SCAN_PLAYFIELD(x, y) +#else + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { - for (x = 0; x < lev_fieldx; x++) + if (AmoebaNr[x][y] == group_nr && + (Feld[x][y] == EL_AMOEBA_DEAD || + Feld[x][y] == EL_BD_AMOEBA || + Feld[x][y] == EL_AMOEBA_GROWING)) { - if (AmoebaNr[x][y] == group_nr && - (Feld[x][y] == EL_AMOEBA_DEAD || - Feld[x][y] == EL_BD_AMOEBA || - Feld[x][y] == EL_AMOEBA_GROWING)) - { - AmoebaNr[x][y] = 0; - Feld[x][y] = new_element; - InitField(x, y, FALSE); - DrawLevelField(x, y); - done = TRUE; - } + AmoebaNr[x][y] = 0; + Feld[x][y] = new_element; + InitField(x, y, FALSE); + DrawLevelField(x, y); + done = TRUE; } } @@ -5897,6 +6811,7 @@ void AmoebeAbleger(int ax, int ay) int element = Feld[ax][ay]; int graphic = el2img(element); int newax = ax, neway = ay; + boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER); static int xy[4][2] = { { 0, -1 }, @@ -5905,7 +6820,7 @@ void AmoebeAbleger(int ax, int ay) { 0, +1 } }; - if (!level.amoeba_speed) + if (!level.amoeba_speed && element != EL_EMC_DRIPPER) { Feld[ax][ay] = EL_AMOEBA_DEAD; DrawLevelField(ax, ay); @@ -5925,7 +6840,7 @@ void AmoebeAbleger(int ax, int ay) return; } - if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */ + if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */ { int start = RND(4); int x = ax + xy[start][0]; @@ -6019,13 +6934,13 @@ void AmoebeAbleger(int ax, int ay) } } - if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) || + if (!can_drop || neway < ay || !IS_FREE(newax, neway) || (neway == lev_fieldy - 1 && newax != ax)) { Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */ Store[newax][neway] = element; } - else if (neway == ay) + else if (neway == ay || element == EL_EMC_DRIPPER) { Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */ @@ -6152,6 +7067,54 @@ static void RunTimegateWheel(int x, int y) PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE); } +static void InitMagicBallDelay(int x, int y) +{ +#if 1 + ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1; +#else + ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1; +#endif +} + +static void ActivateMagicBall(int bx, int by) +{ + int x, y; + + if (level.ball_random) + { + int pos_border = RND(8); /* select one of the eight border elements */ + int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border); + int xx = pos_content % 3; + int yy = pos_content / 3; + + x = bx - 1 + xx; + y = by - 1 + yy; + + if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY) + CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]); + } + else + { + for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++) + { + int xx = x - bx + 1; + int yy = y - by + 1; + + if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY) + CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]); + } + } + + game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents; +} + +static void InitDiagonalMovingElement(int x, int y) +{ +#if 0 + MovDelay[x][y] = level.android_move_time; +#endif +} + void CheckExit(int x, int y) { if (local_player->gems_still_needed > 0 || @@ -6200,18 +7163,19 @@ static void CloseAllOpenTimegates() { int x, y; - for (y = 0; y < lev_fieldy; y++) +#if 1 + SCAN_PLAYFIELD(x, y) +#else + for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { - for (x = 0; x < lev_fieldx; x++) - { - int element = Feld[x][y]; + int element = Feld[x][y]; - if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING) - { - Feld[x][y] = EL_TIMEGATE_CLOSING; + if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING) + { + Feld[x][y] = EL_TIMEGATE_CLOSING; - PlayLevelSoundAction(x, y, ACTION_CLOSING); - } + PlayLevelSoundAction(x, y, ACTION_CLOSING); } } } @@ -6491,9 +7455,10 @@ static void WarnBuggyBase(int x, int y) for (i = 0; i < NUM_DIRECTIONS; i++) { - int xx = x + xy[i][0], yy = y + xy[i][1]; + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; - if (IS_PLAYER(xx, yy)) + if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy)) { PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE); @@ -6556,7 +7521,11 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) if (!change->has_action) return; - /* ---------- determine action paramater values ---------- */ + /* ---------- determine action paramater values -------------------------- */ + + int level_time_value = + (level.time > 0 ? TimeLeft : + TimePlayed); int action_arg_element = (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player : @@ -6564,12 +7533,21 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element : EL_EMPTY); + int action_arg_direction = + (action_arg >= CA_ARG_DIRECTION_LEFT && + action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION : + action_arg == CA_ARG_DIRECTION_TRIGGER ? + change->actual_trigger_side : + action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ? + MV_DIR_OPPOSITE(change->actual_trigger_side) : + MV_NONE); + int action_arg_number_min = - (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN : + (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING : CA_ARG_MIN); int action_arg_number_max = - (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX : + (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER : action_type == CA_SET_LEVEL_GEMS ? 999 : action_type == CA_SET_LEVEL_TIME ? 9999 : action_type == CA_SET_LEVEL_SCORE ? 99999 : @@ -6578,7 +7556,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) CA_ARG_MAX); int action_arg_number_reset = - (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value : + (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize : action_type == CA_SET_LEVEL_GEMS ? level.gems_needed : action_type == CA_SET_LEVEL_TIME ? level.time : action_type == CA_SET_LEVEL_SCORE ? 0 : @@ -6592,7 +7570,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) int action_arg_number = (action_arg <= CA_ARG_MAX ? action_arg : - action_arg >= CA_ARG_SPEED_VERY_SLOW && + action_arg >= CA_ARG_SPEED_NOT_MOVING && action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) : action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset : action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min : @@ -6604,9 +7582,14 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) #else action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial : #endif - action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) : - action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) : - action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value : + action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) : + action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value : + action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed : + action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score : + action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) : + action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value: + action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element : + action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element : -1); int action_arg_number_old = @@ -6622,24 +7605,19 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_mode, action_arg_number, action_arg_number_min, action_arg_number_max); - int action_arg_player_bits = - (action_arg == CA_ARG_PLAYER_ANY ? PLAYER_BITS_ANY : - action_arg >= CA_ARG_PLAYER_1 && - action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER : - action_arg >= CA_ARG_1 && - action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - CA_ARG_1)) : - action_arg_element >= EL_PLAYER_1 && - action_arg_element <= EL_PLAYER_4 ? - (1 << (action_arg_element - EL_PLAYER_1)) : - PLAYER_BITS_ANY); - int trigger_player_bits = (change->actual_trigger_player >= EL_PLAYER_1 && change->actual_trigger_player <= EL_PLAYER_4 ? (1 << (change->actual_trigger_player - EL_PLAYER_1)) : PLAYER_BITS_ANY); - /* ---------- execute action ---------- */ + int action_arg_player_bits = + (action_arg >= CA_ARG_PLAYER_1 && + action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER : + action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits : + PLAYER_BITS_ANY); + + /* ---------- execute action -------------------------------------------- */ switch(action_type) { @@ -6648,67 +7626,109 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) return; } - case CA_EXIT_PLAYER: + /* ---------- level actions ------------------------------------------- */ + + case CA_RESTART_LEVEL: { - for (i = 0; i < MAX_PLAYERS; i++) - if (action_arg_player_bits & (1 << i)) - stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE; + game.restart_level = TRUE; + + break; + } + + case CA_SHOW_ENVELOPE: + { + int element = getSpecialActionElement(action_arg_element, + action_arg_number, EL_ENVELOPE_1); + + if (IS_ENVELOPE(element)) + local_player->show_envelope = element; + + break; + } + + case CA_SET_LEVEL_TIME: + { + if (level.time > 0) /* only modify limited time value */ + { + TimeLeft = action_arg_number_new; + + DrawGameValue_Time(TimeLeft); + if (!TimeLeft && setup.time_limit) + for (i = 0; i < MAX_PLAYERS; i++) + KillPlayer(&stored_player[i]); + } + + break; + } + + case CA_SET_LEVEL_SCORE: + { + local_player->score = action_arg_number_new; + + DrawGameValue_Score(local_player->score); + + break; + } + + case CA_SET_LEVEL_GEMS: + { + local_player->gems_still_needed = action_arg_number_new; + + DrawGameValue_Emeralds(local_player->gems_still_needed); + + break; + } + + case CA_SET_LEVEL_GRAVITY: + { + game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE : + action_arg == CA_ARG_GRAVITY_ON ? TRUE : + action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity : + game.gravity); break; } - case CA_KILL_PLAYER: + case CA_SET_LEVEL_WIND: { - for (i = 0; i < MAX_PLAYERS; i++) - if (action_arg_player_bits & (1 << i)) - KillPlayer(&stored_player[i]); + game.wind_direction = action_arg_direction; break; } - case CA_RESTART_LEVEL: + /* ---------- player actions ------------------------------------------ */ + + case CA_MOVE_PLAYER: { - game.restart_level = TRUE; + /* automatically move to the next field in specified direction */ + for (i = 0; i < MAX_PLAYERS; i++) + if (trigger_player_bits & (1 << i)) + stored_player[i].programmed_action = action_arg_direction; break; } - case CA_SHOW_ENVELOPE: + case CA_EXIT_PLAYER: { - int element = getSpecialActionElement(action_arg_element, - action_arg_number, EL_ENVELOPE_1); - - if (IS_ENVELOPE(element)) - local_player->show_envelope = element; + for (i = 0; i < MAX_PLAYERS; i++) + if (action_arg_player_bits & (1 << i)) + stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE; break; } - case CA_ADD_KEY: + case CA_KILL_PLAYER: { - int element = getSpecialActionElement(action_arg_element, - action_arg_number, EL_KEY_1); - - if (IS_KEY(element)) - { - for (i = 0; i < MAX_PLAYERS; i++) - { - if (trigger_player_bits & (1 << i)) - { - stored_player[i].key[KEY_NR(element)] = TRUE; - - DrawGameValue_Keys(stored_player[i].key); - - redraw_mask |= REDRAW_DOOR_1; - } - } - } + for (i = 0; i < MAX_PLAYERS; i++) + if (action_arg_player_bits & (1 << i)) + KillPlayer(&stored_player[i]); break; } - case CA_REMOVE_KEY: + case CA_SET_PLAYER_KEYS: { + int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE); int element = getSpecialActionElement(action_arg_element, action_arg_number, EL_KEY_1); @@ -6718,7 +7738,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { if (trigger_player_bits & (1 << i)) { - stored_player[i].key[KEY_NR(element)] = FALSE; + stored_player[i].key[KEY_NR(element)] = key_state; DrawGameValue_Keys(stored_player[i].key); @@ -6730,7 +7750,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } -#if 1 case CA_SET_PLAYER_SPEED: { for (i = 0; i < MAX_PLAYERS; i++) @@ -6739,8 +7758,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { int move_stepsize = TILEX / stored_player[i].move_delay_value; - if (action_arg == CA_ARG_SPEED_SLOWER || - action_arg == CA_ARG_SPEED_FASTER) + if (action_arg == CA_ARG_SPEED_FASTER && + stored_player[i].cannot_move) + { + action_arg_number = STEPSIZE_VERY_SLOW; + } + else if (action_arg == CA_ARG_SPEED_SLOWER || + action_arg == CA_ARG_SPEED_FASTER) { action_arg_number = 2; action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE : @@ -6754,116 +7778,81 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg_number_min, action_arg_number_max); +#if 1 + SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE); +#else /* make sure that value is power of 2 */ move_stepsize = (1 << log_2(move_stepsize)); /* do no immediately change -- the player might just be moving */ stored_player[i].move_delay_value_next = TILEX / move_stepsize; -#if 0 - printf("::: move_delay_value == %d [%d]\n", - stored_player[i].move_delay_value_next, action_arg_number); + stored_player[i].cannot_move = + (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE); #endif } } break; } -#else - case CA_SET_PLAYER_SPEED: + + case CA_SET_PLAYER_SHIELD: { for (i = 0; i < MAX_PLAYERS; i++) { if (trigger_player_bits & (1 << i)) { - int move_stepsize = TILEX / stored_player[i].move_delay_value; - - if (action_mode == CA_MODE_ADD || action_mode == CA_MODE_SUBTRACT) + if (action_arg == CA_ARG_SHIELD_OFF) { - /* translate "+" and "-" to "*" and "/" with powers of two */ - action_arg_number = 1 << action_arg_number; - action_mode = (action_mode == CA_MODE_ADD ? CA_MODE_MULTIPLY : - CA_MODE_DIVIDE); + stored_player[i].shield_normal_time_left = 0; + stored_player[i].shield_deadly_time_left = 0; + } + else if (action_arg == CA_ARG_SHIELD_NORMAL) + { + stored_player[i].shield_normal_time_left = 999999; + } + else if (action_arg == CA_ARG_SHIELD_DEADLY) + { + stored_player[i].shield_normal_time_left = 999999; + stored_player[i].shield_deadly_time_left = 999999; } - - move_stepsize = - getModifiedActionNumber(move_stepsize, - action_mode, - action_arg_number, - action_arg_number_min, - action_arg_number_max); - - /* make sure that value is power of 2 */ - move_stepsize = (1 << log_2(move_stepsize)); - - /* do no immediately change -- the player might just be moving */ - stored_player[i].move_delay_value_next = TILEX / move_stepsize; - -#if 0 - printf("::: move_delay_value == %d [%d]\n", - stored_player[i].move_delay_value_next, action_arg_number); -#endif } } break; } -#endif - - case CA_SET_PLAYER_GRAVITY: - { - game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE : - action_arg == CA_ARG_GRAVITY_ON ? TRUE : - action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity : - game.gravity); - break; - } - - case CA_SET_WIND_DIRECTION: - { - game.wind_direction = (action_arg >= CA_ARG_DIRECTION_NONE && - action_arg <= CA_ARG_DIRECTION_DOWN ? - action_arg - CA_ARG_DIRECTION : - action_arg == CA_ARG_DIRECTION_TRIGGER ? - MV_DIR_OPPOSITE(change->actual_trigger_side) : - game.wind_direction); - - break; - } - case CA_SET_LEVEL_GEMS: + case CA_SET_PLAYER_ARTWORK: { - local_player->gems_still_needed = action_arg_number_new; - - DrawGameValue_Emeralds(local_player->gems_still_needed); + for (i = 0; i < MAX_PLAYERS; i++) + { + if (trigger_player_bits & (1 << i)) + { + int artwork_element = action_arg_element; - break; - } + if (action_arg == CA_ARG_ELEMENT_RESET) + artwork_element = + (level.use_artwork_element[i] ? level.artwork_element[i] : + stored_player[i].element_nr); - case CA_SET_LEVEL_TIME: - { - if (level.time > 0) /* only modify limited time value */ - { - TimeLeft = action_arg_number_new; + stored_player[i].artwork_element = artwork_element; - DrawGameValue_Time(TimeLeft); + SetPlayerWaiting(&stored_player[i], FALSE); - if (!TimeLeft && setup.time_limit) - for (i = 0; i < MAX_PLAYERS; i++) - KillPlayer(&stored_player[i]); + /* set number of special actions for bored and sleeping animation */ + stored_player[i].num_special_action_bored = + get_num_special_action(artwork_element, + ACTION_BORING_1, ACTION_BORING_LAST); + stored_player[i].num_special_action_sleeping = + get_num_special_action(artwork_element, + ACTION_SLEEPING_1, ACTION_SLEEPING_LAST); + } } break; } - case CA_SET_LEVEL_SCORE: - { - local_player->score = action_arg_number_new; - - DrawGameValue_Score(local_player->score); - - break; - } + /* ---------- CE actions ---------------------------------------------- */ case CA_SET_CE_SCORE: { @@ -6891,53 +7880,46 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO); CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X); - } -#endif - - break; - } #if 0 - case CA_SET_DYNABOMB_NUMBER: - { - printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n"); + printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]); +#endif + } +#endif break; } - case CA_SET_DYNABOMB_SIZE: - { - printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n"); - - break; - } + /* ---------- engine actions ------------------------------------------ */ - case CA_SET_DYNABOMB_POWER: + case CA_SET_ENGINE_SCAN_MODE: { - printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n"); + InitPlayfieldScanMode(action_arg); break; } -#endif default: break; } } -static void ChangeElementNowExt(struct ElementChangeInfo *change, - int x, int y, int target_element) +static void CreateFieldExt(int x, int y, int element, boolean is_change) { int previous_move_direction = MovDir[x][y]; - boolean add_player = (ELEM_IS_PLAYER(target_element) && +#if USE_NEW_CUSTOM_VALUE + int last_ce_value = CustomValue[x][y]; +#endif + boolean add_player = (ELEM_IS_PLAYER(element) && IS_WALKABLE(Feld[x][y])); /* check if element under player changes from accessible to unaccessible (needed for special case of dropping element which then changes) */ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && - IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element)) + IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element)) { Bang(x, y); + return; } @@ -6948,7 +7930,7 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change, else RemoveField(x, y); - Feld[x][y] = target_element; + Feld[x][y] = element; ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); @@ -6956,6 +7938,11 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change, if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS) MovDir[x][y] = previous_move_direction; +#if USE_NEW_CUSTOM_VALUE + if (element_info[Feld[x][y]].use_last_ce_value) + CustomValue[x][y] = last_ce_value; +#endif + InitField_WithBug1(x, y, FALSE); DrawLevelField(x, y); @@ -6964,22 +7951,44 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change, DrawLevelFieldCrumbledSandNeighbours(x, y); } - /* "Changed[][]" not set yet to allow "entered by player" change one time */ - if (ELEM_IS_PLAYER(target_element)) - RelocatePlayer(x, y, target_element); + /* "ChangeCount" not set yet to allow "entered by player" change one time */ + if (ELEM_IS_PLAYER(element)) + RelocatePlayer(x, y, element); -#if 1 - Changed[x][y] = TRUE; /* ignore all further changes in this frame */ -#else - Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */ -#endif + if (is_change) + ChangeCount[x][y]++; /* count number of changes in the same frame */ TestIfBadThingTouchesPlayer(x, y); TestIfPlayerTouchesCustomElement(x, y); TestIfElementTouchesCustomElement(x, y); } -static boolean ChangeElementNow(int x, int y, int element, int page) +static void CreateField(int x, int y, int element) +{ + CreateFieldExt(x, y, element, FALSE); +} + +static void CreateElementFromChange(int x, int y, int element) +{ + element = GET_VALID_RUNTIME_ELEMENT(element); + +#if USE_STOP_CHANGED_ELEMENTS + if (game.engine_version >= VERSION_IDENT(3,2,0,7)) + { + int old_element = Feld[x][y]; + + /* prevent changed element from moving in same engine frame + unless both old and new element can either fall or move */ + if ((!CAN_FALL(old_element) || !CAN_FALL(element)) && + (!CAN_MOVE(old_element) || !CAN_MOVE(element))) + Stop[x][y] = TRUE; + } +#endif + + CreateFieldExt(x, y, element, TRUE); +} + +static boolean ChangeElement(int x, int y, int element, int page) { struct ElementChangeInfo *change = &element_info[element].change_page[page]; int target_element; @@ -6998,21 +8007,11 @@ static boolean ChangeElementNow(int x, int y, int element, int page) change->actual_trigger_ce_value = 0; } -#if 1 - /* do not change any elements that have already changed in this frame */ - if (Changed[x][y]) - return FALSE; -#else - /* do not change already changed elements with same change event */ - if (Changed[x][y] & ChangeEvent[x][y]) + /* do not change elements more than a specified maximum number of changes */ + if (ChangeCount[x][y] >= game.max_num_changes_per_frame) return FALSE; -#endif -#if 1 - Changed[x][y] = TRUE; /* ignore all further changes in this frame */ -#else - Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */ -#endif + ChangeCount[x][y]++; /* count number of changes in the same frame */ if (change->explode) { @@ -7112,7 +8111,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page) content_element = change->target_content.e[xx][yy]; target_element = GET_TARGET_ELEMENT(content_element, change); - ChangeElementNowExt(change, ex, ey, target_element); + CreateElementFromChange(ex, ey, target_element); something_has_changed = TRUE; @@ -7133,7 +8132,15 @@ static boolean ChangeElementNow(int x, int y, int element, int page) { target_element = GET_TARGET_ELEMENT(change->target_element, change); - ChangeElementNowExt(change, x, y, target_element); + if (element == EL_DIAGONAL_GROWING || + element == EL_DIAGONAL_SHRINKING) + { + target_element = Store[x][y]; + + Store[x][y] = EL_EMPTY; + } + + CreateElementFromChange(x, y, target_element); PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING); PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page); @@ -7147,7 +8154,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page) #if USE_NEW_DELAYED_ACTION -static void ChangeElement(int x, int y, int page) +static void HandleElementChange(int x, int y, int page) { int element = MovingOrBlocked2Element(x, y); struct ElementInfo *ei = &element_info[element]; @@ -7158,9 +8165,9 @@ static void ChangeElement(int x, int y, int page) !CAN_CHANGE_OR_HAS_ACTION(Back[x][y])) { printf("\n\n"); - printf("ChangeElement(): %d,%d: element = %d ('%s')\n", + printf("HandleElementChange(): %d,%d: element = %d ('%s')\n", x, y, element, element_info[element].token_name); - printf("ChangeElement(): This should never happen!\n"); + printf("HandleElementChange(): This should never happen!\n"); printf("\n\n"); } #endif @@ -7225,7 +8232,7 @@ static void ChangeElement(int x, int y, int page) if (change->can_change) { - if (ChangeElementNow(x, y, element, page)) + if (ChangeElement(x, y, element, page)) { if (change->post_change_function) change->post_change_function(x, y); @@ -7239,7 +8246,7 @@ static void ChangeElement(int x, int y, int page) #else -static void ChangeElement(int x, int y, int page) +static void HandleElementChange(int x, int y, int page) { int element = MovingOrBlocked2Element(x, y); struct ElementInfo *ei = &element_info[element]; @@ -7249,9 +8256,9 @@ static void ChangeElement(int x, int y, int page) if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y])) { printf("\n\n"); - printf("ChangeElement(): %d,%d: element = %d ('%s')\n", + printf("HandleElementChange(): %d,%d: element = %d ('%s')\n", x, y, element, element_info[element].token_name); - printf("ChangeElement(): This should never happen!\n"); + printf("HandleElementChange(): This should never happen!\n"); printf("\n\n"); } #endif @@ -7308,7 +8315,7 @@ static void ChangeElement(int x, int y, int page) return; } - if (ChangeElementNow(x, y, element, page)) + if (ChangeElement(x, y, element, page)) { if (change->post_change_function) change->post_change_function(x, y); @@ -7318,7 +8325,7 @@ static void ChangeElement(int x, int y, int page) #endif -static boolean CheckTriggeredElementChangeExt(int x, int y, +static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, int trigger_element, int trigger_event, int trigger_player, @@ -7356,13 +8363,17 @@ static boolean CheckTriggeredElementChangeExt(int x, int y, change->actual_trigger_element = trigger_element; change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player); change->actual_trigger_side = trigger_side; - change->actual_trigger_ce_value = CustomValue[x][y]; + change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y]; if ((change->can_change && !change_done) || change->has_action) { int x, y; +#if 1 + SCAN_PLAYFIELD(x, y) +#else for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { if (Feld[x][y] == element) { @@ -7370,7 +8381,8 @@ static boolean CheckTriggeredElementChangeExt(int x, int y, { ChangeDelay[x][y] = 1; ChangeEvent[x][y] = trigger_event; - ChangeElement(x, y, p); + + HandleElementChange(x, y, p); } #if USE_NEW_DELAYED_ACTION else if (change->has_action) @@ -7421,8 +8433,20 @@ static boolean CheckElementChangeExt(int x, int y, element = Feld[x][y]; } - if (Feld[x][y] != element) /* check if element has already changed */ +#if 0 + /* check if element has already changed */ + if (Feld[x][y] != element) + return FALSE; +#else + /* check if element has already changed or is about to change after moving */ + if ((game.engine_version < VERSION_IDENT(3,2,0,7) && + Feld[x][y] != element) || + + (game.engine_version >= VERSION_IDENT(3,2,0,7) && + (ChangeCount[x][y] >= game.max_num_changes_per_frame || + ChangePage[x][y] != -1))) return FALSE; +#endif for (p = 0; p < element_info[element].num_change_pages; p++) { @@ -7445,11 +8469,35 @@ static boolean CheckElementChangeExt(int x, int y, change->actual_trigger_side = trigger_side; change->actual_trigger_ce_value = CustomValue[x][y]; + /* special case: trigger element not at (x,y) position for some events */ + if (check_trigger_element) + { + static struct + { + int dx, dy; + } move_xy[] = + { + { 0, 0 }, + { -1, 0 }, + { +1, 0 }, + { 0, 0 }, + { 0, -1 }, + { 0, 0 }, { 0, 0 }, { 0, 0 }, + { 0, +1 } + }; + + int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx; + int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy; + + change->actual_trigger_ce_value = CustomValue[xx][yy]; + } + if (change->can_change && !change_done) { ChangeDelay[x][y] = 1; ChangeEvent[x][y] = trigger_event; - ChangeElement(x, y, p); + + HandleElementChange(x, y, p); change_done = TRUE; } @@ -7475,24 +8523,24 @@ static boolean CheckElementChangeExt(int x, int y, static void PlayPlayerSound(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; - int element = player->element_nr; + int sound_element = player->artwork_element; int last_action = player->last_action_waiting; int action = player->action_waiting; if (player->is_waiting) { if (action != last_action) - PlayLevelSoundElementAction(jx, jy, element, action); + PlayLevelSoundElementAction(jx, jy, sound_element, action); else - PlayLevelSoundElementActionIfLoop(jx, jy, element, action); + PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action); } else { if (action != last_action) - StopSound(element_info[element].sound[last_action]); + StopSound(element_info[sound_element].sound[last_action]); if (last_action == ACTION_SLEEPING) - PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING); + PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING); } } @@ -7559,7 +8607,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ? last_special_action + 1 : ACTION_SLEEPING); int special_graphic = - el_act_dir2img(player->element_nr, special_action, move_dir); + el_act_dir2img(player->artwork_element, special_action, move_dir); player->anim_delay_counter = graphic_info[special_graphic].anim_delay_fixed + @@ -7591,7 +8639,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) int special_action = ACTION_BORING_1 + SimpleRND(player->num_special_action_bored); int special_graphic = - el_act_dir2img(player->element_nr, special_action, move_dir); + el_act_dir2img(player->artwork_element, special_action, move_dir); player->anim_delay_counter = graphic_info[special_graphic].anim_delay_fixed + @@ -7765,6 +8813,40 @@ void GameActions() WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value); + InitPlayfieldScanModeVars(); + + if (ScreenMovPos == 0) /* screen currently aligned at tile position */ + { + struct PlayerInfo *player; + int player_nr = game.centered_player_nr_next; + + if (game.centered_player_nr_next == -1) + player_nr = local_player->index_nr; + + player = &stored_player[player_nr]; + + if (!player->active) + game.centered_player_nr_next = game.centered_player_nr; + + if (game.centered_player_nr != game.centered_player_nr_next) + { + DrawRelocatePlayer(player, setup.quick_switch); + + game.centered_player_nr = game.centered_player_nr_next; + } + } + +#if USE_ONE_MORE_CHANGE_PER_FRAME + if (game.engine_version >= VERSION_IDENT(3,2,0,7)) + { + SCAN_PLAYFIELD(x, y) + { + ChangeCount[x][y] = 0; + ChangeEvent[x][y] = -1; + } + } +#endif + if (network_playing && !network_player_action_received) { /* try to get network player actions in time */ @@ -7892,9 +8974,13 @@ void GameActions() } } +#if 1 + SCAN_PLAYFIELD(x, y) +#else for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { - Changed[x][y] = FALSE; + ChangeCount[x][y] = 0; ChangeEvent[x][y] = -1; /* this must be handled before main playfield loop */ @@ -7905,6 +8991,20 @@ void GameActions() RemoveField(x, y); } +#if USE_NEW_SNAP_DELAY + if (Feld[x][y] == EL_ELEMENT_SNAPPING) + { + MovDelay[x][y]--; + if (MovDelay[x][y] <= 0) + { + RemoveField(x, y); + DrawLevelField(x, y); + + TestIfElementTouchesCustomElement(x, y); /* for empty space */ + } + } +#endif + #if DEBUG if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1) { @@ -7926,7 +9026,7 @@ void GameActions() GfxFrame[x][y]++; /* reset finished pushing action (not done in ContinueMoving() to allow - continous pushing animation for elements with zero push delay) */ + continuous pushing animation for elements with zero push delay) */ if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y)) { ResetGfxAnimation(x, y); @@ -7950,13 +9050,46 @@ void GameActions() #endif } +#if 1 + SCAN_PLAYFIELD(x, y) +#else for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { element = Feld[x][y]; graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); +#if 0 + printf("::: %d,%d\n", x, y); + + if (element == EL_ROCK) + printf("::: Yo man! Rocks can fall!\n"); +#endif + if (graphic_info[graphic].anim_global_sync) GfxFrame[x][y] = FrameCounter; + else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) + { + int old_gfx_frame = GfxFrame[x][y]; + + GfxFrame[x][y] = CustomValue[x][y]; + +#if 1 + if (GfxFrame[x][y] != old_gfx_frame) +#endif + DrawLevelGraphicAnimation(x, y, graphic); + } + else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + { + int old_gfx_frame = GfxFrame[x][y]; + + GfxFrame[x][y] = element_info[element].collect_score; + +#if 1 + if (GfxFrame[x][y] != old_gfx_frame) +#endif + DrawLevelGraphicAnimation(x, y, graphic); + } if (ANIM_MODE(graphic) == ANIM_RANDOM && IS_NEXT_FRAME(GfxFrame[x][y], graphic)) @@ -7980,18 +9113,23 @@ void GameActions() { int page = element_info[element].event_page_nr[CE_DELAY]; #if 0 - ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page); + HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page); #else #if 0 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]); #endif +#if 0 + if (element == EL_CUSTOM_255) + printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]); +#endif + #if 1 - ChangeElement(x, y, page); + HandleElementChange(x, y, page); #else if (CAN_CHANGE(element)) - ChangeElement(x, y, page); + HandleElementChange(x, y, page); if (HAS_ACTION(element)) ExecuteCustomElementAction(x, y, element, page); @@ -8059,9 +9197,25 @@ void GameActions() CheckForDragon(x, y); else if (element == EL_EXPLOSION) ; /* drawing of correct explosion animation is handled separately */ + else if (element == EL_ELEMENT_SNAPPING || + element == EL_DIAGONAL_SHRINKING || + element == EL_DIAGONAL_GROWING) + { +#if 1 + graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]); + + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); +#endif + } else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y)) DrawLevelGraphicAnimationIfNeeded(x, y, graphic); +#if 0 + if (element == EL_CUSTOM_255 || + element == EL_CUSTOM_256) + DrawLevelGraphicAnimation(x, y, graphic); +#endif + if (IS_BELT_ACTIVE(element)) PlayLevelSoundAction(x, y, ACTION_ACTIVE); @@ -8121,7 +9275,11 @@ void GameActions() { game.explosions_delayed = FALSE; +#if 1 + SCAN_PLAYFIELD(x, y) +#else for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { element = Feld[x][y]; @@ -8155,7 +9313,11 @@ void GameActions() game.magic_wall_time_left--; if (!game.magic_wall_time_left) { +#if 1 + SCAN_PLAYFIELD(x, y) +#else for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++) +#endif { element = Feld[x][y]; @@ -8186,12 +9348,28 @@ void GameActions() RedrawAllLightSwitchesAndInvisibleElements(); } - if (game.timegate_time_left > 0) + if (game.timegate_time_left > 0) + { + game.timegate_time_left--; + + if (game.timegate_time_left == 0) + CloseAllOpenTimegates(); + } + + if (game.lenses_time_left > 0) + { + game.lenses_time_left--; + + if (game.lenses_time_left == 0) + RedrawAllInvisibleElementsForLenses(); + } + + if (game.magnify_time_left > 0) { - game.timegate_time_left--; + game.magnify_time_left--; - if (game.timegate_time_left == 0) - CloseAllOpenTimegates(); + if (game.magnify_time_left == 0) + RedrawAllInvisibleElementsForMagnifier(); } for (i = 0; i < MAX_PLAYERS; i++) @@ -8448,11 +9626,14 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, { int jx = player->jx, jy = player->jy; int new_jx = jx + dx, new_jy = jy + dy; +#if !USE_FIXED_DONT_RUN_INTO int element; +#endif int can_move; + boolean player_can_move = !player->cannot_move; if (!player->active || (!dx && !dy)) - return MF_NO_ACTION; + return MP_NO_ACTION; player->MovDir = (dx < 0 ? MV_LEFT : dx > 0 ? MV_RIGHT : @@ -8460,14 +9641,43 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, dy > 0 ? MV_DOWN : MV_NONE); if (!IN_LEV_FIELD(new_jx, new_jy)) - return MF_NO_ACTION; + return MP_NO_ACTION; + + if (!player_can_move) + { +#if 1 + if (player->MovPos == 0) + { + player->is_moving = FALSE; + player->is_digging = FALSE; + player->is_collecting = FALSE; + player->is_snapping = FALSE; + player->is_pushing = FALSE; + } +#else + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); + SnapField(player, 0, 0); +#endif + +#if 0 + return MP_NO_ACTION; +#endif + } +#if 1 + if (!options.network && game.centered_player_nr == -1 && + !AllPlayersInSight(player, new_jx, new_jy)) + return MP_NO_ACTION; +#else if (!options.network && !AllPlayersInSight(player, new_jx, new_jy)) - return MF_NO_ACTION; + return MP_NO_ACTION; +#endif +#if !USE_FIXED_DONT_RUN_INTO element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy); - if (DONT_RUN_INTO(element)) + /* (moved to DigField()) */ + if (player_can_move && DONT_RUN_INTO(element)) { if (element == EL_ACID && dx == 0 && dy == 1) { @@ -8481,16 +9691,26 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, else TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); - return MF_MOVING; + return MP_MOVING; } +#endif can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG); - if (can_move != MF_MOVING) +#if 0 +#if USE_FIXED_DONT_RUN_INTO + if (can_move == MP_DONT_RUN_INTO) + return MP_MOVING; +#endif +#endif + if (can_move != MP_MOVING) return can_move; +#if USE_FIXED_DONT_RUN_INTO +#endif + /* check if DigField() has caused relocation of the player */ if (player->jx != jx || player->jy != jy) - return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */ + return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */ StorePlayer[jx][jy] = 0; player->last_jx = jx; @@ -8514,14 +9734,14 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, ScrollPlayer(player, SCROLL_INIT); - return MF_MOVING; + return MP_MOVING; } boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) { int jx = player->jx, jy = player->jy; int old_jx = jx, old_jy = jy; - int moved = MF_NO_ACTION; + int moved = MP_NO_ACTION; if (!player->active) return FALSE; @@ -8594,8 +9814,14 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) jx = player->jx; jy = player->jy; - if (moved & MF_MOVING && !ScreenMovPos && +#if 1 + if (moved & MP_MOVING && !ScreenMovPos && + (player->index_nr == game.centered_player_nr || + game.centered_player_nr == -1)) +#else + if (moved & MP_MOVING && !ScreenMovPos && (player == local_player || !options.network)) +#endif { int old_scroll_x = scroll_x, old_scroll_y = scroll_y; int offset = (setup.scroll_delay ? 3 : 0); @@ -8650,12 +9876,22 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) if (scroll_x != old_scroll_x || scroll_y != old_scroll_y) { +#if 1 + if (!options.network && game.centered_player_nr == -1 && + !AllPlayersInVisibleScreen()) + { + scroll_x = old_scroll_x; + scroll_y = old_scroll_y; + } + else +#else if (!options.network && !AllPlayersInVisibleScreen()) { scroll_x = old_scroll_x; scroll_y = old_scroll_y; } else +#endif { ScrollScreen(player, SCROLL_INIT); ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y); @@ -8665,7 +9901,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->StepFrame = 0; - if (moved & MF_MOVING) + if (moved & MP_MOVING) { if (old_jx != jx && old_jy == jy) player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT); @@ -8851,6 +10087,9 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) CheckTriggeredElementChangeByPlayer(jx, jy, new_element, CE_PLAYER_ENTERS_X, player->index_bit, enter_side); + + CheckTriggeredElementChangeBySide(jx, jy, player->element_nr, + CE_MOVE_OF_X, move_direction); } if (game.engine_version >= VERSION_IDENT(3,0,7,0)) @@ -9001,7 +10240,94 @@ void TestIfPlayerTouchesCustomElement(int x, int y) } } +#if USE_ELEMENT_TOUCHING_BUGFIX + void TestIfElementTouchesCustomElement(int x, int y) +{ + static int xy[4][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } + }; + static int trigger_sides[4][2] = + { + /* center side border side */ + { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */ + { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */ + { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */ + { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */ + }; + static int touch_dir[4] = + { + MV_LEFT | MV_RIGHT, + MV_UP | MV_DOWN, + MV_UP | MV_DOWN, + MV_LEFT | MV_RIGHT + }; + boolean change_center_element = FALSE; + int center_element = Feld[x][y]; /* should always be non-moving! */ + int border_element_old[NUM_DIRECTIONS]; + int i; + + for (i = 0; i < NUM_DIRECTIONS; i++) + { + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; + int border_element; + + border_element_old[i] = -1; + + if (!IN_LEV_FIELD(xx, yy)) + continue; + + if (game.engine_version < VERSION_IDENT(3,0,7,0)) + border_element = Feld[xx][yy]; /* may be moving! */ + else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy)) + border_element = Feld[xx][yy]; + else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */ + border_element = MovingOrBlocked2Element(xx, yy); + else + continue; /* center and border element do not touch */ + + border_element_old[i] = border_element; + } + + for (i = 0; i < NUM_DIRECTIONS; i++) + { + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; + int center_side = trigger_sides[i][0]; + int border_element = border_element_old[i]; + + if (border_element == -1) + continue; + + /* check for change of border element */ + CheckElementChangeBySide(xx, yy, border_element, center_element, + CE_TOUCHING_X, center_side); + } + + for (i = 0; i < NUM_DIRECTIONS; i++) + { + int border_side = trigger_sides[i][1]; + int border_element = border_element_old[i]; + + if (border_element == -1) + continue; + + /* check for change of center element (but change it only once) */ + if (!change_center_element) + change_center_element = + CheckElementChangeBySide(x, y, center_element, border_element, + CE_TOUCHING_X, border_side); + } +} + +#else + +void TestIfElementTouchesCustomElement_OLD(int x, int y) { static int xy[4][2] = { @@ -9061,6 +10387,8 @@ void TestIfElementTouchesCustomElement(int x, int y) } } +#endif + void TestIfElementHitsCustomElement(int x, int y, int direction) { int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); @@ -9170,6 +10498,7 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction) void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) { int i, kill_x = -1, kill_y = -1; + int bad_element = -1; static int test_xy[4][2] = { @@ -9426,7 +10755,7 @@ void BuryPlayer(struct PlayerInfo *player) if (!player->active) return; - PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING); + PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING); PlayLevelSound(jx, jy, SND_GAME_LOSING); player->GameOver = TRUE; @@ -9458,6 +10787,27 @@ void RemovePlayer(struct PlayerInfo *player) ExitY = ZY = jy; } +#if USE_NEW_SNAP_DELAY +static void setFieldForSnapping(int x, int y, int element, int direction) +{ + struct ElementInfo *ei = &element_info[element]; + int direction_bit = MV_DIR_TO_BIT(direction); + int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit]; + int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING : + IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING); + + Feld[x][y] = EL_ELEMENT_SNAPPING; + MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1; + + ResetGfxAnimation(x, y); + + GfxElement[x][y] = element; + GfxAction[x][y] = action; + GfxDir[x][y] = direction; + GfxFrame[x][y] = -1; +} +#endif + /* ============================================================================= checkDiagonalPushing() @@ -9501,6 +10851,8 @@ int DigField(struct PlayerInfo *player, { boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG); boolean player_was_pushing = player->is_pushing; + boolean player_can_move = (!player->cannot_move && mode != DF_SNAP); + boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP); int jx = oldx, jy = oldy; int dx = x - jx, dy = y - jy; int nextx = x + dx, nexty = y + dy; @@ -9511,7 +10863,11 @@ int DigField(struct PlayerInfo *player, int opposite_direction = MV_DIR_OPPOSITE(move_direction); int dig_side = MV_DIR_OPPOSITE(move_direction); int old_element = Feld[jx][jy]; +#if USE_FIXED_DONT_RUN_INTO + int element = MovingOrBlocked2ElementIfNotLeaving(x, y); +#else int element; +#endif int collect_count; if (is_player) /* function can also be called by EL_PENGUIN */ @@ -9530,12 +10886,14 @@ int DigField(struct PlayerInfo *player, player->is_switching = FALSE; player->push_delay = -1; - return MF_NO_ACTION; + return MP_NO_ACTION; } } +#if !USE_FIXED_DONT_RUN_INTO if (IS_MOVING(x, y) || IS_PLAYER(x, y)) - return MF_NO_ACTION; + return MP_NO_ACTION; +#endif if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)) old_element = Back[jx][jy]; @@ -9545,47 +10903,119 @@ int DigField(struct PlayerInfo *player, game.engine_version >= VERSION_IDENT(2,2,0,0)) old_element = Back[jx][jy]; +#if 0 +#if USE_FIXED_DONT_RUN_INTO + if (player_can_move && DONT_RUN_INTO(element)) + { + if (element == EL_ACID && dx == 0 && dy == 1) + { + SplashAcid(x, y); + Feld[jx][jy] = EL_PLAYER_1; + InitMovingField(jx, jy, MV_DOWN); + Store[jx][jy] = EL_ACID; + ContinueMoving(jx, jy); + BuryPlayer(player); + } + else + TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); + + return MP_DONT_RUN_INTO; + } +#endif +#endif + +#if 1 +#if USE_FIXED_DONT_RUN_INTO + if (player_can_move && DONT_RUN_INTO(element)) + { + TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); + + return MP_DONT_RUN_INTO; + } +#endif +#endif + if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction)) - return MF_NO_ACTION; /* field has no opening in this direction */ + return MP_NO_ACTION; /* field has no opening in this direction */ if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction)) - return MF_NO_ACTION; /* field has no opening in this direction */ - - element = Feld[x][y]; -#if USE_NEW_CUSTOM_VALUE + return MP_NO_ACTION; /* field has no opening in this direction */ #if 1 - collect_count = element_info[element].collect_count_initial; -#else - collect_count = CustomValue[x][y]; -#endif +#if USE_FIXED_DONT_RUN_INTO + if (player_can_move && element == EL_ACID && move_direction == MV_DOWN) + { + SplashAcid(x, y); + Feld[jx][jy] = EL_PLAYER_1; + InitMovingField(jx, jy, MV_DOWN); + Store[jx][jy] = EL_ACID; + ContinueMoving(jx, jy); + BuryPlayer(player); -#else - collect_count = element_info[element].collect_count_initial; + return MP_DONT_RUN_INTO; + } +#endif #endif #if 0 - if (element != EL_BLOCKED && - CustomValue[x][y] != element_info[element].collect_count_initial) - printf("::: %d: %d != %d\n", - element, - CustomValue[x][y], - element_info[element].collect_count_initial); +#if USE_FIXED_DONT_RUN_INTO + if (player_can_move && DONT_RUN_INTO(element)) + { + if (element == EL_ACID && dx == 0 && dy == 1) + { + SplashAcid(x, y); + Feld[jx][jy] = EL_PLAYER_1; + InitMovingField(jx, jy, MV_DOWN); + Store[jx][jy] = EL_ACID; + ContinueMoving(jx, jy); + BuryPlayer(player); + } + else + TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); + + return MP_DONT_RUN_INTO; + } +#endif +#endif + +#if USE_FIXED_DONT_RUN_INTO + if (IS_MOVING(x, y) || IS_PLAYER(x, y)) + return MP_NO_ACTION; +#endif + +#if !USE_FIXED_DONT_RUN_INTO + element = Feld[x][y]; #endif + collect_count = element_info[element].collect_count_initial; + if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */ - return MF_NO_ACTION; + return MP_NO_ACTION; + + if (game.engine_version < VERSION_IDENT(2,2,0,0)) + player_can_move = player_can_move_or_snap; if (mode == DF_SNAP && !IS_SNAPPABLE(element) && game.engine_version >= VERSION_IDENT(2,2,0,0)) - return MF_NO_ACTION; + { + CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER, + player->index_bit, dig_side); + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); + + if (Feld[x][y] != element) /* field changed by snapping */ + return MP_ACTION; + + return MP_NO_ACTION; + } if (game.gravity && is_player && !player->is_auto_moving && canFallDown(player) && move_direction != MV_DOWN && !canMoveToValidFieldWithGravity(jx, jy, move_direction)) - return MF_NO_ACTION; /* player cannot walk here due to gravity */ + return MP_NO_ACTION; /* player cannot walk here due to gravity */ - if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction)) + if (player_can_move && + IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction)) { int sound_element = SND_ELEMENT(element); int sound_action = ACTION_WALKING; @@ -9593,12 +11023,17 @@ int DigField(struct PlayerInfo *player, if (IS_RND_GATE(element)) { if (!player->key[RND_GATE_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; } else if (IS_RND_GATE_GRAY(element)) { if (!player->key[RND_GATE_GRAY_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; + } + else if (IS_RND_GATE_GRAY_ACTIVE(element)) + { + if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)]) + return MP_NO_ACTION; } else if (element == EL_EXIT_OPEN || element == EL_SP_EXIT_OPEN || @@ -9615,25 +11050,31 @@ int DigField(struct PlayerInfo *player, if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED) PlayLevelSoundElementAction(x, y, sound_element, sound_action); else - PlayLevelSoundElementAction(x, y, player->element_nr, sound_action); + PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action); } - else if (IS_PASSABLE(element) && canPassField(x, y, move_direction)) + else if (player_can_move && + IS_PASSABLE(element) && canPassField(x, y, move_direction)) { if (!ACCESS_FROM(element, opposite_direction)) - return MF_NO_ACTION; /* field not accessible from this direction */ + return MP_NO_ACTION; /* field not accessible from this direction */ if (CAN_MOVE(element)) /* only fixed elements can be passed! */ - return MF_NO_ACTION; + return MP_NO_ACTION; if (IS_EM_GATE(element)) { if (!player->key[EM_GATE_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; } else if (IS_EM_GATE_GRAY(element)) { if (!player->key[EM_GATE_GRAY_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; + } + else if (IS_EM_GATE_GRAY_ACTIVE(element)) + { + if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)]) + return MP_NO_ACTION; } else if (IS_SP_PORT(element)) { @@ -9666,7 +11107,7 @@ int DigField(struct PlayerInfo *player, PlayLevelSoundAction(x, y, ACTION_PASSING); } - else if (IS_DIGGABLE(element)) + else if (player_can_move_or_snap && IS_DIGGABLE(element)) { RemoveField(x, y); @@ -9682,9 +11123,21 @@ int DigField(struct PlayerInfo *player, player->index_bit, dig_side); if (mode == DF_SNAP) - TestIfElementTouchesCustomElement(x, y); /* for empty space */ + { +#if USE_NEW_SNAP_DELAY + if (level.block_snap_field) + setFieldForSnapping(x, y, element, move_direction); + else + TestIfElementTouchesCustomElement(x, y); /* for empty space */ +#else + TestIfElementTouchesCustomElement(x, y); /* for empty space */ +#endif + + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); + } } - else if (IS_COLLECTIBLE(element)) + else if (player_can_move_or_snap && IS_COLLECTIBLE(element)) { RemoveField(x, y); @@ -9709,7 +11162,9 @@ int DigField(struct PlayerInfo *player, if (element == EL_SHIELD_DEADLY) player->shield_deadly_time_left += level.shield_deadly_time; } - else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED) + else if (element == EL_DYNAMITE || + element == EL_EM_DYNAMITE || + element == EL_SP_DISK_RED) { if (player->inventory_size < MAX_INVENTORY_SIZE) player->inventory_element[player->inventory_size++] = element; @@ -9741,6 +11196,18 @@ int DigField(struct PlayerInfo *player, { player->show_envelope = element; } + else if (element == EL_EMC_LENSES) + { + game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND; + + RedrawAllInvisibleElementsForLenses(); + } + else if (element == EL_EMC_MAGNIFIER) + { + game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND; + + RedrawAllInvisibleElementsForMagnifier(); + } else if (IS_DROPPABLE(element) || IS_THROWABLE(element)) /* can be collected and dropped */ { @@ -9772,19 +11239,31 @@ int DigField(struct PlayerInfo *player, player->index_bit, dig_side); if (mode == DF_SNAP) - TestIfElementTouchesCustomElement(x, y); /* for empty space */ + { +#if USE_NEW_SNAP_DELAY + if (level.block_snap_field) + setFieldForSnapping(x, y, element, move_direction); + else + TestIfElementTouchesCustomElement(x, y); /* for empty space */ +#else + TestIfElementTouchesCustomElement(x, y); /* for empty space */ +#endif + + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); + } } - else if (IS_PUSHABLE(element)) + else if (player_can_move_or_snap && IS_PUSHABLE(element)) { if (mode == DF_SNAP && element != EL_BD_ROCK) - return MF_NO_ACTION; + return MP_NO_ACTION; if (CAN_FALL(element) && dy) - return MF_NO_ACTION; + return MP_NO_ACTION; if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) && !(element == EL_SPRING && level.use_spring_bug)) - return MF_NO_ACTION; + return MP_NO_ACTION; if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 && ((move_direction & MV_VERTICAL && @@ -9797,12 +11276,12 @@ int DigField(struct PlayerInfo *player, IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) || (element_info[element].move_pattern & MV_DOWN && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1)))))) - return MF_NO_ACTION; + return MP_NO_ACTION; /* do not push elements already moving away faster than player */ if (CAN_MOVE(element) && MovDir[x][y] == move_direction && ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL) - return MF_NO_ACTION; + return MP_NO_ACTION; if (game.engine_version >= VERSION_IDENT(3,1,0,0)) { @@ -9826,10 +11305,10 @@ int DigField(struct PlayerInfo *player, (IS_FREE(nextx, nexty) || (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY && IS_SB_ELEMENT(element))))) - return MF_NO_ACTION; + return MP_NO_ACTION; if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) - return MF_NO_ACTION; + return MP_NO_ACTION; if (player->push_delay == -1) /* new pushing; restart delay */ player->push_delay = 0; @@ -9842,7 +11321,7 @@ int DigField(struct PlayerInfo *player, if (game.engine_version >= VERSION_IDENT(3,0,7,1)) player->move_delay = 0; - return MF_NO_ACTION; + return MP_NO_ACTION; } if (IS_SB_ELEMENT(element)) @@ -9912,7 +11391,7 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); - return MF_ACTION; + return MP_ACTION; } player->is_switching = TRUE; @@ -9933,7 +11412,11 @@ int DigField(struct PlayerInfo *player, { int xx, yy; - for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++) +#if 1 + SCAN_PLAYFIELD(xx, yy) +#else + for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++) +#endif { if (Feld[xx][yy] == EL_SP_DISK_YELLOW) Bang(xx, yy); @@ -9984,20 +11467,58 @@ int DigField(struct PlayerInfo *player, else if (element == EL_TIME_ORB_FULL) { Feld[x][y] = EL_TIME_ORB_EMPTY; - TimeLeft += level.time_orb_time; - DrawGameValue_Time(TimeLeft); + + if (level.time > 0 || level.use_time_orb_bug) + { + TimeLeft += level.time_orb_time; + DrawGameValue_Time(TimeLeft); + } ResetGfxAnimation(x, y); DrawLevelField(x, y); } + else if (element == EL_EMC_MAGIC_BALL_SWITCH || + element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE) + { + int xx, yy; + + game.ball_state = !game.ball_state; + +#if 1 + SCAN_PLAYFIELD(xx, yy) +#else + for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++) +#endif + { + int e = Feld[xx][yy]; + + if (game.ball_state) + { + if (e == EL_EMC_MAGIC_BALL) + CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE); + else if (e == EL_EMC_MAGIC_BALL_SWITCH) + CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE); + } + else + { + if (e == EL_EMC_MAGIC_BALL_ACTIVE) + CreateField(xx, yy, EL_EMC_MAGIC_BALL); + else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE) + CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH); + } + } + } CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X, player->index_bit, dig_side); + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X, + player->index_bit, dig_side); + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); - return MF_ACTION; + return MP_ACTION; } else { @@ -10011,6 +11532,11 @@ int DigField(struct PlayerInfo *player, player->index_bit, dig_side); CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X, player->index_bit, dig_side); + + CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER, + player->index_bit, dig_side); + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X, + player->index_bit, dig_side); } CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER, @@ -10018,7 +11544,7 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); - return MF_NO_ACTION; + return MP_NO_ACTION; } player->push_delay = -1; @@ -10029,7 +11555,7 @@ int DigField(struct PlayerInfo *player, player->is_collecting = !player->is_digging; } - return MF_MOVING; + return MP_MOVING; } boolean SnapField(struct PlayerInfo *player, int dx, int dy) @@ -10081,7 +11607,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) player->is_dropping = FALSE; - if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION) + if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION) return FALSE; player->is_snapping = TRUE; @@ -10120,7 +11646,7 @@ boolean DropElement(struct PlayerInfo *player) element can be dropped (this is especially important if the next element is dynamite, which can be placed on background for historical reasons) */ if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY) - return MF_ACTION; + return MP_ACTION; if (IS_THROWABLE(drop_element)) { @@ -10167,6 +11693,8 @@ boolean DropElement(struct PlayerInfo *player) if (new_element == EL_DYNAMITE) new_element = EL_DYNAMITE_ACTIVE; + else if (new_element == EL_EM_DYNAMITE) + new_element = EL_EM_DYNAMITE_ACTIVE; else if (new_element == EL_SP_DISK_RED) new_element = EL_SP_DISK_RED_ACTIVE; } @@ -10180,7 +11708,7 @@ boolean DropElement(struct PlayerInfo *player) PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING); /* needed if previous element just changed to "empty" in the last frame */ - Changed[dropx][dropy] = FALSE; /* allow another change */ + ChangeCount[dropx][dropy] = 0; /* allow at least one more change */ CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER, player->index_bit, drop_side); @@ -10220,7 +11748,7 @@ boolean DropElement(struct PlayerInfo *player) nextx = dropx + GET_DX_FROM_DIR(move_direction); nexty = dropy + GET_DY_FROM_DIR(move_direction); - Changed[dropx][dropy] = FALSE; /* allow another change */ + ChangeCount[dropx][dropy] = 0; /* allow at least one more change */ CheckCollision[dropx][dropy] = 2; } @@ -10398,7 +11926,7 @@ void PlayLevelSound_EM(int x, int y, int element_em, int sample) break; case SAMPLE_slurp: - PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING); + PlayLevelSoundElementAction(x, y, element, ACTION_EATING); break; case SAMPLE_eater: @@ -10560,6 +12088,7 @@ void RaiseScoreElement(int element) RaiseScore(level.score[SC_NUT]); break; case EL_DYNAMITE: + case EL_EM_DYNAMITE: case EL_SP_DISK_RED: case EL_DYNABOMB_INCREASE_NUMBER: case EL_DYNABOMB_INCREASE_SIZE: @@ -10571,7 +12100,7 @@ void RaiseScoreElement(int element) RaiseScore(level.score[SC_SHIELD]); break; case EL_EXTRA_TIME: - RaiseScore(level.score[SC_TIME_BONUS]); + RaiseScore(level.extra_time_score); break; case EL_KEY_1: case EL_KEY_2: