X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=f8289cc9138937767facf986edaa3b21fa908628;hb=564a563aee2c9fdb37116d41342b36eda63c1f6a;hp=d9771d8882976a9069efd0981dd0bb70e5380381;hpb=c64be0354d88b9776537a907f524993e29e074aa;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index d9771d88..f8289cc9 100644 --- a/src/game.c +++ b/src/game.c @@ -101,19 +101,28 @@ #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \ (element_info[e].move_delay_random)) -#if 1 +#define ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, condition) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + (CAN_MOVE_INTO_ACID(e) && \ + Feld[x][y] == EL_ACID) || \ + (condition))) + +#if 0 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ (condition) || \ (DONT_COLLIDE_WITH(e) && \ IS_PLAYER(x, y) && \ - !PLAYER_PROTECTED(x, y)))) + !PLAYER_ENEMY_PROTECTED(x, y)))) #else #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ (condition) || \ + (CAN_MOVE_INTO_ACID(e) && \ + Feld[x][y] == EL_ACID) || \ (DONT_COLLIDE_WITH(e) && \ - IS_FREE_OR_PLAYER(x, y)))) + IS_PLAYER(x, y) && \ + !PLAYER_ENEMY_PROTECTED(x, y)))) #endif #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \ @@ -129,7 +138,11 @@ #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \ ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID)) -#define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y)) +#if 0 +#define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y)) +#else +#define ENEMY_CAN_ENTER_FIELD(e, x, y) ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, 0) +#endif #define YAMYAM_CAN_ENTER_FIELD(x, y) \ (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \ @@ -153,17 +166,6 @@ Feld[x][y] == EL_EXIT_OPEN || \ Feld[x][y] == EL_ACID)) -#if 0 -#if 1 -#define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \ - (IN_LEV_FIELD(x, y) && IS_FREE(x, y)) -#else -#define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \ - (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ - IS_FOOD_DARK_YAMYAM(Feld[x][y]))) -#endif -#endif - #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) @@ -172,7 +174,7 @@ #define IS_EQUAL_OR_IN_GROUP(e, ge) \ (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge)) -#if 1 +#if 0 #define CE_ENTER_FIELD_COND(e, x, y) \ (!IS_PLAYER(x, y) && \ (Feld[x][y] == EL_ACID || \ @@ -180,10 +182,7 @@ #else #define CE_ENTER_FIELD_COND(e, x, y) \ (!IS_PLAYER(x, y) && \ - (Feld[x][y] == EL_ACID || \ - Feld[x][y] == MOVE_ENTER_EL(e) || \ - (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) && \ - IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e))))) + IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))) #endif #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \ @@ -213,10 +212,13 @@ static boolean MovePlayer(struct PlayerInfo *, int, int); static void ScrollPlayer(struct PlayerInfo *, int); static void ScrollScreen(struct PlayerInfo *, int); +int DigField(struct PlayerInfo *, int, int, int, int, int, int, int); + static void InitBeltMovement(void); static void CloseAllOpenTimegates(void); static void CheckGravityMovement(struct PlayerInfo *); -static void KillHeroUnlessProtected(int, int); +static void KillHeroUnlessEnemyProtected(int, int); +static void KillHeroUnlessExplosionProtected(int, int); static void TestIfPlayerTouchesCustomElement(int, int); static void TestIfElementTouchesCustomElement(int, int); @@ -496,6 +498,28 @@ collect_count_list[] = { EL_UNDEFINED, 0 }, }; +struct +{ + int element; + int direction; +} +tube_access[] = +{ + { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }, + { EL_TUBE_VERTICAL, MV_UP | MV_DOWN }, + { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT }, + { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN }, + { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN }, + { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP }, + { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN }, + { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP }, + { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN }, + { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP }, + { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN }, + + { EL_UNDEFINED, 0 } +}; + static unsigned long trigger_events[MAX_NUM_ELEMENTS]; #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \ @@ -837,6 +861,36 @@ static void InitField(int x, int y, boolean init_game) } } +static inline void InitField_WithBug1(int x, int y, boolean init_game) +{ + InitField(x, y, init_game); + + /* not needed to call InitMovDir() -- already done by InitField()! */ + if (game.engine_version < VERSION_IDENT(3,0,9,0) && + CAN_MOVE(Feld[x][y])) + InitMovDir(x, y); +} + +static inline void InitField_WithBug2(int x, int y, boolean init_game) +{ + int old_element = Feld[x][y]; + + InitField(x, y, init_game); + + /* not needed to call InitMovDir() -- already done by InitField()! */ + if (game.engine_version < VERSION_IDENT(3,0,9,0) && + CAN_MOVE(old_element) && + (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN)) + InitMovDir(x, y); + + /* this case is in fact a combination of not less than three bugs: + first, it calls InitMovDir() for elements that can move, although this is + already done by InitField(); then, it checks the element that was at this + field _before_ the call to InitField() (which can change it) + + */ +} + void DrawGameDoorValues() { int i, j; @@ -870,7 +924,7 @@ static void resolve_group_element(int group_element, int recursion_depth) group_element - EL_GROUP_START + 1); /* replace element which caused too deep recursion by question mark */ - group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION; + group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN; return; } @@ -1156,6 +1210,18 @@ static void InitGameEngine() for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++) element_info[collect_count_list[i].element].collect_count = collect_count_list[i].count; + + /* ---------- initialize access direction -------------------------------- */ + + /* initialize access direction values to default */ + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + if (!IS_CUSTOM_ELEMENT(i)) + element_info[i].access_direction = MV_ALL_DIRECTIONS; + + /* set access direction value for certain elements from pre-defined list */ + for (i = 0; tube_access[i].element != EL_UNDEFINED; i++) + element_info[tube_access[i].element].access_direction = + tube_access[i].direction; } @@ -1314,7 +1380,7 @@ void InitGame() player->inventory_size = 0; - DigField(player, 0, 0, 0, 0, DF_NO_PUSH); + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); player->LevelSolved = FALSE; @@ -1384,6 +1450,7 @@ void InitGame() ChangeEvent[x][y] = CE_BITMASK_DEFAULT; ExplodePhase[x][y] = 0; + ExplodeDelay[x][y] = 0; ExplodeField[x][y] = EX_NO_EXPLOSION; RunnerVisit[x][y] = 0; @@ -1418,6 +1485,30 @@ void InitGame() emulate_sb ? EMU_SOKOBAN : emulate_sp ? EMU_SUPAPLEX : EMU_NONE); + /* initialize explosion and ignition delay */ + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + { + if (!IS_CUSTOM_ELEMENT(i)) + { + int num_phase = 9; + int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2); + int last_phase = num_phase * delay; + int half_phase = (num_phase / 2) * delay; + + element_info[i].explosion_delay = last_phase; + element_info[i].ignition_delay = half_phase; + + if (i == EL_BLACK_ORB) + element_info[i].ignition_delay = 1; + } + + if (element_info[i].explosion_delay < 2) /* !!! check again !!! */ + element_info[i].explosion_delay = 2; + + if (element_info[i].ignition_delay < 1) /* !!! check again !!! */ + element_info[i].ignition_delay = 1; + } + /* correct non-moving belts to start moving left */ for (i = 0; i < 4; i++) if (game.belt_dir[i] == MV_NO_MOVING) @@ -1793,15 +1884,15 @@ void InitMovDir(int x, int y) int move_direction_initial = ei->move_direction_initial; int move_pattern = ei->move_pattern; - if (move_direction_initial == MV_PREVIOUS) + if (move_direction_initial == MV_START_PREVIOUS) { if (MovDir[x][y] != MV_NO_MOVING) return; - move_direction_initial = MV_AUTOMATIC; + move_direction_initial = MV_START_AUTOMATIC; } - if (move_direction_initial == MV_RANDOM) + if (move_direction_initial == MV_START_RANDOM) MovDir[x][y] = 1 << RND(4); else if (move_direction_initial & MV_ANY_DIRECTION) MovDir[x][y] = move_direction_initial; @@ -2244,8 +2335,25 @@ void RemoveMovingField(int x, int y) if (IS_MOVING(x, y)) { Moving2Blocked(x, y, &newx, &newy); +#if 0 if (Feld[newx][newy] != EL_BLOCKED) return; +#else + if (Feld[newx][newy] != EL_BLOCKED) + { + /* element is moving, but target field is not free (blocked), but + already occupied by something different (example: acid pool); + in this case, only remove the moving field, but not the target */ + + RemoveField(oldx, oldy); + + Store[oldx][oldy] = Store2[oldx][oldy] = 0; + + DrawLevelField(oldx, oldy); + + return; + } +#endif } else if (element == EL_BLOCKED) { @@ -2335,8 +2443,9 @@ void CheckDynamite(int x, int y) Bang(x, y); } -void RelocatePlayer(int x, int y, int element) +void RelocatePlayer(int x, int y, int element_raw) { + int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw); struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1]; boolean ffwd_delay = (tape.playing && tape.fast_forward); boolean no_delay = (tape.index_search); @@ -2418,11 +2527,21 @@ void RelocatePlayer(int x, int y, int element) void Explode(int ex, int ey, int phase, int mode) { int x, y; +#if 0 int num_phase = 9; +#endif + + /* !!! eliminate this variable !!! */ int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2); + +#if 1 + int last_phase; +#else int last_phase = num_phase * delay; int half_phase = (num_phase / 2) * delay; int first_phase_after_start = EX_PHASE_START + 1; +#endif + int border_element; if (game.explosions_delayed) { @@ -2457,6 +2576,10 @@ void Explode(int ex, int ey, int phase, int mode) Feld[ex][ey] = center_element; } +#if 1 + last_phase = element_info[center_element].explosion_delay; +#endif + for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++) { int xx = x - ex + 1; @@ -2536,7 +2659,7 @@ void Explode(int ex, int ey, int phase, int mode) RemoveField(x, y); #endif - if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey)) + if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey)) { switch(StorePlayer[ex][ey]) { @@ -2627,6 +2750,9 @@ void Explode(int ex, int ey, int phase, int mode) #endif ExplodePhase[x][y] = 1; +#if 1 + ExplodeDelay[x][y] = last_phase; +#endif Stop[x][y] = TRUE; } @@ -2643,6 +2769,10 @@ void Explode(int ex, int ey, int phase, int mode) x = ex; y = ey; +#if 1 + last_phase = ExplodeDelay[x][y]; +#endif + ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0); #ifdef DEBUG @@ -2663,6 +2793,69 @@ void Explode(int ex, int ey, int phase, int mode) } #endif +#if 1 + + border_element = Store2[x][y]; + if (IS_PLAYER(x, y)) + border_element = StorePlayer[x][y]; + + if (phase == element_info[border_element].ignition_delay || + phase == last_phase) + { + boolean border_explosion = FALSE; + +#if 1 + if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present) +#else + if (IS_PLAYER(x, y)) +#endif + { + KillHeroUnlessExplosionProtected(x, y); + border_explosion = TRUE; + +#if 0 + if (phase == last_phase) + printf("::: IS_PLAYER\n"); +#endif + } + else if (CAN_EXPLODE_BY_EXPLOSION(border_element)) + { + Feld[x][y] = Store2[x][y]; + Store2[x][y] = 0; + Bang(x, y); + border_explosion = TRUE; + +#if 0 + if (phase == last_phase) + printf("::: CAN_EXPLODE_BY_EXPLOSION\n"); +#endif + } + else if (border_element == EL_AMOEBA_TO_DIAMOND) + { + AmoebeUmwandeln(x, y); + Store2[x][y] = 0; + border_explosion = TRUE; + +#if 0 + if (phase == last_phase) + printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n", + element_info[border_element].explosion_delay, + element_info[border_element].ignition_delay, + phase); +#endif + } + +#if 1 + /* if an element just explodes due to another explosion (chain-reaction), + do not immediately end the new explosion when it was the last frame of + the explosion (as it would be done in the following "if"-statement!) */ + if (border_explosion && phase == last_phase) + return; +#endif + } + +#else + if (phase == first_phase_after_start) { int element = Store2[x][y]; @@ -2679,8 +2872,8 @@ void Explode(int ex, int ey, int phase, int mode) int element = Store2[x][y]; if (IS_PLAYER(x, y)) - KillHeroUnlessProtected(x, y); - else if (CAN_EXPLODE_BY_FIRE(element)) + KillHeroUnlessExplosionProtected(x, y); + else if (CAN_EXPLODE_BY_EXPLOSION(element)) { Feld[x][y] = Store2[x][y]; Store2[x][y] = 0; @@ -2689,6 +2882,7 @@ void Explode(int ex, int ey, int phase, int mode) else if (element == EL_AMOEBA_TO_DIAMOND) AmoebeUmwandeln(x, y); } +#endif if (phase == last_phase) { @@ -2718,11 +2912,21 @@ void Explode(int ex, int ey, int phase, int mode) ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; +#if 1 + InitField_WithBug2(x, y, FALSE); +#else InitField(x, y, FALSE); #if 1 /* !!! not needed !!! */ +#if 1 + if (game.engine_version < VERSION_IDENT(3,0,9,0) && + CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE) + InitMovDir(x, y); +#else if (CAN_MOVE(element)) InitMovDir(x, y); +#endif +#endif #endif DrawLevelField(x, y); @@ -2731,13 +2935,17 @@ void Explode(int ex, int ey, int phase, int mode) if (GFX_CRUMBLED(element)) DrawLevelFieldCrumbledSandNeighbours(x, y); - if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present) + if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present) StorePlayer[x][y] = 0; if (ELEM_IS_PLAYER(element)) RelocatePlayer(x, y, element); } +#if 1 + else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y))) +#else else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) +#endif { #if 1 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING); @@ -2836,7 +3044,7 @@ void Bang(int x, int y) #endif #if 1 - if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y)) + if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y)) #else if (IS_PLAYER(x, y)) #endif @@ -2909,23 +3117,42 @@ void Bang(int x, int y) void SplashAcid(int x, int y) { +#if 1 + if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) && + (!IN_LEV_FIELD(x - 1, y - 2) || + !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2)))) + Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT; + + if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) && + (!IN_LEV_FIELD(x + 1, y - 2) || + !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2)))) + Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT; + + PlayLevelSound(x, y, SND_ACID_SPLASHING); +#else + /* input: position of element entering acid (obsolete) */ + int element = Feld[x][y]; + if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID) + return; + if (element != EL_ACID_SPLASH_LEFT && element != EL_ACID_SPLASH_RIGHT) { PlayLevelSound(x, y, SND_ACID_SPLASHING); - if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) && - (!IN_LEV_FIELD(x-1, y-1) || - !CAN_FALL(MovingOrBlocked2Element(x-1, y-1)))) - Feld[x-1][y] = EL_ACID_SPLASH_LEFT; + if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) && + (!IN_LEV_FIELD(x - 1, y - 1) || + !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1)))) + Feld[x - 1][y] = EL_ACID_SPLASH_LEFT; - if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) && - (!IN_LEV_FIELD(x+1, y-1) || - !CAN_FALL(MovingOrBlocked2Element(x+1, y-1)))) - Feld[x+1][y] = EL_ACID_SPLASH_RIGHT; + if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) && + (!IN_LEV_FIELD(x + 1, y - 1) || + !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1)))) + Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT; } +#endif } static void InitBeltMovement() @@ -3250,11 +3477,19 @@ inline static int getElementMoveStepsize(int x, int y) /* special values for move stepsize for spring and things on conveyor belt */ if (horiz_move) { +#if 0 + if (element == EL_SPRING) + step = sign * MOVE_STEPSIZE_NORMAL * 2; + else if (CAN_FALL(element) && !CAN_MOVE(element) && + y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1])) + step = sign * MOVE_STEPSIZE_NORMAL / 2; +#else if (CAN_FALL(element) && y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1])) step = sign * MOVE_STEPSIZE_NORMAL / 2; else if (element == EL_SPRING) step = sign * MOVE_STEPSIZE_NORMAL * 2; +#endif } return step; @@ -3294,7 +3529,7 @@ void Impact(int x, int y) if (!lastline && smashed == EL_ACID) /* element falls into acid */ { - SplashAcid(x, y); + SplashAcid(x, y + 1); return; } @@ -3327,7 +3562,7 @@ void Impact(int x, int y) if (impact && element == EL_AMOEBA_DROP) { if (object_hit && IS_PLAYER(x, y + 1)) - KillHeroUnlessProtected(x, y + 1); + KillHeroUnlessEnemyProtected(x, y + 1); else if (object_hit && smashed == EL_PENGUIN) Bang(x, y + 1); else @@ -3369,7 +3604,7 @@ void Impact(int x, int y) { if (CAN_SMASH_PLAYER(element)) { - KillHeroUnlessProtected(x, y + 1); + KillHeroUnlessEnemyProtected(x, y + 1); return; } } @@ -3545,9 +3780,9 @@ inline static void TurnRoundExt(int x, int y) { TestIfBadThingTouchesOtherBadThing(x, y); - if (ENEMY_CAN_ENTER_FIELD(right_x, right_y)) + if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y)) MovDir[x][y] = right_dir; - else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y)) + else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y)) MovDir[x][y] = left_dir; if (element == EL_BUG && MovDir[x][y] != old_move_dir) @@ -3560,9 +3795,9 @@ inline static void TurnRoundExt(int x, int y) { TestIfBadThingTouchesOtherBadThing(x, y); - if (ENEMY_CAN_ENTER_FIELD(left_x, left_y)) + if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y)) MovDir[x][y] = left_dir; - else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y)) + else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y)) MovDir[x][y] = right_dir; if ((element == EL_SPACESHIP || @@ -4187,7 +4422,9 @@ static boolean JustBeingPushed(int x, int y) void StartMoving(int x, int y) { +#if 0 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0)); +#endif boolean started_moving = FALSE; /* some elements can fall _and_ move */ int element = Feld[x][y]; @@ -4205,8 +4442,8 @@ void StartMoving(int x, int y) if (CAN_FALL(element) && y < lev_fieldy - 1) { - if ((x > 0 && IS_PLAYER(x - 1, y)) || - (x < lev_fieldx-1 && IS_PLAYER(x + 1, y))) + if ((x > 0 && IS_PLAYER(x - 1, y)) || + (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y))) if (JustBeingPushed(x, y)) return; @@ -4336,7 +4573,7 @@ void StartMoving(int x, int y) else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID) #endif { - SplashAcid(x, y); + SplashAcid(x, y + 1); InitMovingField(x, y, MV_DOWN); started_moving = TRUE; @@ -4384,7 +4621,7 @@ void StartMoving(int x, int y) Impact(x, y); } - else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug) + else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug) { if (MovDir[x][y] == MV_NO_MOVING) { @@ -4460,7 +4697,11 @@ void StartMoving(int x, int y) started_moving = TRUE; } } +#if 0 + else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element)) +#else else if (IS_BELT_ACTIVE(Feld[x][y + 1])) +#endif { boolean left_is_free = (x > 0 && IS_FREE(x - 1, y)); boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y)); @@ -4470,11 +4711,24 @@ void StartMoving(int x, int y) if ((belt_dir == MV_LEFT && left_is_free) || (belt_dir == MV_RIGHT && right_is_free)) { +#if 1 + int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1); +#endif + InitMovingField(x, y, belt_dir); started_moving = TRUE; +#if 1 + Pushed[x][y] = TRUE; + Pushed[nextx][y] = TRUE; +#endif + GfxAction[x][y] = ACTION_DEFAULT; } + else + { + MovDir[x][y] = 0; /* if element was moving, stop it */ + } } } @@ -4668,7 +4922,7 @@ void StartMoving(int x, int y) { int flamed = MovingOrBlocked2Element(xx, yy); - if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed)) + if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed)) Bang(xx, yy); else RemoveMovingField(xx, yy); @@ -4710,7 +4964,7 @@ void StartMoving(int x, int y) if (DONT_COLLIDE_WITH(element) && IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) && - !PLAYER_PROTECTED(newx, newy)) + !PLAYER_ENEMY_PROTECTED(newx, newy)) { #if 1 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]); @@ -4724,6 +4978,18 @@ void StartMoving(int x, int y) #endif } +#if 1 +#if 1 + else if (CAN_MOVE_INTO_ACID(element) && + IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID && + (MovDir[x][y] == MV_DOWN || + game.engine_version > VERSION_IDENT(3,0,8,0))) +#else + else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN && + IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID) +#endif +#else + else if ((element == EL_PENGUIN || element == EL_ROBOT || element == EL_SATELLITE || @@ -4731,8 +4997,9 @@ void StartMoving(int x, int y) IS_CUSTOM_ELEMENT(element)) && IN_LEV_FIELD(newx, newy) && MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID) +#endif { - SplashAcid(x, y); + SplashAcid(newx, newy); Store[x][y] = EL_ACID; } else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy)) @@ -4760,7 +5027,7 @@ void StartMoving(int x, int y) } else if (IS_FOOD_PENGUIN(Feld[newx][newy])) { - if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING) + if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING) DrawLevelField(newx, newy); else GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING; @@ -4947,8 +5214,15 @@ void StartMoving(int x, int y) AmoebaCnt[AmoebaNr[newx][newy]]--; } +#if 0 + /* !!! test !!! */ + if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy)) +#else if (IS_MOVING(newx, newy)) +#endif + { RemoveMovingField(newx, newy); + } else { Feld[newx][newy] = EL_EMPTY; @@ -5050,12 +5324,25 @@ void ContinueMoving(int x, int y) #if 0 int nextx = newx + dx, nexty = newy + dy; #endif - boolean pushed = Pushed[x][y]; +#if 1 + boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y)); + boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y)); +#else + boolean pushed_by_player = Pushed[x][y]; +#endif MovPos[x][y] += getElementMoveStepsize(x, y); - if (pushed) /* special case: moving object pushed by player */ +#if 0 + if (pushed_by_player && IS_PLAYER(x, y)) + { + /* special case: moving object pushed by player */ + MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); + } +#else + if (pushed_by_player) /* special case: moving object pushed by player */ MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); +#endif if (ABS(MovPos[x][y]) < TILEX) { @@ -5207,12 +5494,19 @@ void ContinueMoving(int x, int y) Stop[newx][newy] = TRUE; /* ignore this element until the next frame */ /* prevent pushed element from moving on in pushed direction */ - if (pushed && CAN_MOVE(element) && + if (pushed_by_player && CAN_MOVE(element) && element_info[element].move_pattern & MV_ANY_DIRECTION && !(element_info[element].move_pattern & direction)) TurnRound(newx, newy); - if (!pushed) /* special case: moving object pushed by player */ +#if 1 + /* prevent elements on conveyor belt from moving on in last direction */ + if (pushed_by_conveyor && CAN_FALL(element) && + direction & MV_HORIZONTAL) + MovDir[newx][newy] = 0; +#endif + + if (!pushed_by_player) { WasJustMoving[newx][newy] = 3; @@ -5261,15 +5555,7 @@ void ContinueMoving(int x, int y) #if 0 if (IN_LEV_FIELD(nextx, nexty)) { - static int opposite_directions[] = - { - MV_RIGHT, - MV_LEFT, - MV_DOWN, - MV_UP - }; - int move_dir_bit = MV_DIR_BIT(direction); - int opposite_direction = opposite_directions[move_dir_bit]; + int opposite_direction = MV_DIR_OPPOSITE(direction); int hitting_side = direction; int touched_side = opposite_direction; int touched_element = MovingOrBlocked2Element(nextx, nexty); @@ -6232,7 +6518,7 @@ static void ChangeElementNowExt(int x, int y, int target_element) /* 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_PROTECTED(x, y) && + if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element)) { Bang(x, y); @@ -6247,12 +6533,16 @@ static void ChangeElementNowExt(int x, int y, int target_element) ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); - if (element_info[Feld[x][y]].move_direction_initial == MV_PREVIOUS) + if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS) MovDir[x][y] = previous_move_direction; +#if 1 + InitField_WithBug1(x, y, FALSE); +#else InitField(x, y, FALSE); if (CAN_MOVE(Feld[x][y])) InitMovDir(x, y); +#endif DrawLevelField(x, y); @@ -6550,11 +6840,41 @@ static boolean CheckElementSideChange(int x, int y, int element, int side, element = Feld[x][y]; } +#if 1 + if (page < 0) + { + boolean change_element = FALSE; + int i; + + for (i = 0; i < element_info[element].num_change_pages; i++) + { + struct ElementChangeInfo *change = &element_info[element].change_page[i]; + + if (change->can_change && + change->events & CH_EVENT_BIT(trigger_event) && + change->sides & side) + { + change_element = TRUE; + page = i; + + break; + } + } + + if (!change_element) + return FALSE; + } + +#else + + /* !!! this check misses pages with same event, but different side !!! */ + if (page < 0) page = element_info[element].event_page_nr[trigger_event]; if (!(element_info[element].change_page[page].sides & side)) return FALSE; +#endif ChangeDelay[x][y] = 1; ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event); @@ -6805,7 +7125,7 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) /* no actions for this player (no input at player's configured device) */ - DigField(player, 0, 0, 0, 0, DF_NO_PUSH); + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); CheckGravityMovement(player); @@ -6886,7 +7206,7 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action) /* no actions for this player (no input at player's configured device) */ - DigField(player, 0, 0, 0, 0, DF_NO_PUSH); + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); CheckGravityMovement(player); @@ -7020,7 +7340,19 @@ void GameActions() #endif #if 1 + /* for downwards compatibility, the following code emulates a fixed bug that + occured when pushing elements (causing elements that just made their last + pushing step to already (if possible) make their first falling step in the + same game frame, which is bad); this code is also needed to use the famous + "spring push bug" which is used in older levels and might be wanted to be + used also in newer levels, but in this case the buggy pushing code is only + affecting the "spring" element and no other elements */ + +#if 1 + if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug) +#else if (game.engine_version < VERSION_IDENT(2,2,0,7)) +#endif { for (i = 0; i < MAX_PLAYERS; i++) { @@ -7028,8 +7360,15 @@ void GameActions() int x = player->jx; int y = player->jy; +#if 1 + if (player->active && player->is_pushing && player->is_moving && + IS_MOVING(x, y) && + (game.engine_version < VERSION_IDENT(2,2,0,7) || + Feld[x][y] == EL_SPRING)) +#else if (player->active && player->is_pushing && player->is_moving && IS_MOVING(x, y)) +#endif { ContinueMoving(x, y); @@ -7629,9 +7968,14 @@ static void CheckGravityMovement(struct PlayerInfo *player) canEnterSupaplexPort(new_jx, new_jy, dx, dy)))); /* !!! extend EL_SAND to anything diggable !!! */ + boolean player_is_standing_on_valid_field = + (IS_WALKABLE_INSIDE(Feld[jx][jy]) || + (IS_WALKABLE(Feld[jx][jy]) && + !(element_info[Feld[jx][jy]].access_direction & MV_DOWN))); + if (field_under_player_is_free && - !player_is_moving_to_valid_field && - !IS_WALKABLE_INSIDE(Feld[jx][jy])) + !player_is_standing_on_valid_field && + !player_is_moving_to_valid_field) player->programmed_action = MV_DOWN; } } @@ -7691,7 +8035,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, { if (element == EL_ACID && dx == 0 && dy == 1) { - SplashAcid(jx, jy); + SplashAcid(new_jx, new_jy); Feld[jx][jy] = EL_PLAYER_1; InitMovingField(jx, jy, MV_DOWN); Store[jx][jy] = EL_ACID; @@ -7704,7 +8048,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, return MF_MOVING; } - can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG); + can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG); if (can_move != MF_MOVING) return can_move; @@ -8337,15 +8681,7 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) if (IN_LEV_FIELD(hitx, hity)) { - static int opposite_directions[] = - { - MV_RIGHT, - MV_LEFT, - MV_DOWN, - MV_UP - }; - int move_dir_bit = MV_DIR_BIT(direction); - int opposite_direction = opposite_directions[move_dir_bit]; + int opposite_direction = MV_DIR_OPPOSITE(direction); int hitting_side = direction; int touched_side = opposite_direction; int touched_element = MovingOrBlocked2Element(hitx, hity); @@ -8474,7 +8810,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) if (player->shield_deadly_time_left > 0) Bang(kill_x, kill_y); - else if (!PLAYER_PROTECTED(good_x, good_y)) + else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y)) KillHero(player); } else @@ -8566,7 +8902,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) if (player->shield_deadly_time_left > 0) Bang(bad_x, bad_y); - else if (!PLAYER_PROTECTED(kill_x, kill_y)) + else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y)) KillHero(player); } else @@ -8656,9 +8992,15 @@ void KillHero(struct PlayerInfo *player) BuryHero(player); } -static void KillHeroUnlessProtected(int x, int y) +static void KillHeroUnlessEnemyProtected(int x, int y) { - if (!PLAYER_PROTECTED(x, y)) + if (!PLAYER_ENEMY_PROTECTED(x, y)) + KillHero(PLAYERINFO(x, y)); +} + +static void KillHeroUnlessExplosionProtected(int x, int y) +{ + if (!PLAYER_EXPLOSION_PROTECTED(x, y)) KillHero(PLAYERINFO(x, y)); } @@ -8740,7 +9082,8 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player, */ int DigField(struct PlayerInfo *player, - int x, int y, int real_dx, int real_dy, int mode) + int oldx, int oldy, int x, int y, + int real_dx, int real_dy, int mode) { static int change_sides[4] = { @@ -8749,15 +9092,19 @@ int DigField(struct PlayerInfo *player, CH_SIDE_BOTTOM, /* moving up */ CH_SIDE_TOP, /* moving down */ }; +#if 0 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0)); - int jx = player->jx, jy = player->jy; +#endif + int jx = oldx, jy = oldy; int dx = x - jx, dy = y - jy; int nextx = x + dx, nexty = y + dy; int move_direction = (dx == -1 ? MV_LEFT : dx == +1 ? MV_RIGHT : dy == -1 ? MV_UP : dy == +1 ? MV_DOWN : MV_NO_MOVING); + int opposite_direction = MV_DIR_OPPOSITE(move_direction); int dig_side = change_sides[MV_DIR_BIT(move_direction)]; + int old_element = Feld[jx][jy]; int element; if (player->MovPos == 0) @@ -8780,6 +9127,8 @@ int DigField(struct PlayerInfo *player, if (IS_MOVING(x, y) || IS_PLAYER(x, y)) return MF_NO_ACTION; +#if 0 + #if 0 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy])) #else @@ -8816,6 +9165,17 @@ int DigField(struct PlayerInfo *player, return MF_NO_ACTION; /* tube has no opening in this direction */ } +#else + + if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)) + old_element = Back[jx][jy]; + +#endif + + if (IS_WALKABLE(old_element) && + !(element_info[old_element].access_direction & move_direction)) + return MF_NO_ACTION; /* field has no opening in this direction */ + element = Feld[x][y]; if (mode == DF_SNAP && !IS_SNAPPABLE(element) && @@ -8888,6 +9248,7 @@ int DigField(struct PlayerInfo *player, PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING); break; +#if 0 case EL_TUBE_ANY: case EL_TUBE_VERTICAL: case EL_TUBE_HORIZONTAL: @@ -8930,6 +9291,7 @@ int DigField(struct PlayerInfo *player, PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING); } break; +#endif default: @@ -8937,6 +9299,9 @@ int DigField(struct PlayerInfo *player, { int sound_action = ACTION_WALKING; + if (!(element_info[element].access_direction & opposite_direction)) + return MF_NO_ACTION; /* field not accessible from this direction */ + if (element >= EL_GATE_1 && element <= EL_GATE_4) { if (!player->key[element - EL_GATE_1]) @@ -8971,6 +9336,10 @@ int DigField(struct PlayerInfo *player, if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty)) return MF_NO_ACTION; + if (IS_CUSTOM_ELEMENT(element) && + !(element_info[element].access_direction & opposite_direction)) + return MF_NO_ACTION; /* field not accessible from this direction */ + #if 1 if (CAN_MOVE(element)) /* only fixed elements can be passed! */ return MF_NO_ACTION; @@ -9139,7 +9508,7 @@ int DigField(struct PlayerInfo *player, return MF_NO_ACTION; if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) && - !(element == EL_SPRING && use_spring_bug)) + !(element == EL_SPRING && level.use_spring_bug)) return MF_NO_ACTION; #if 1 @@ -9457,7 +9826,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) player->is_dropping = FALSE; - if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION) + if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION) return FALSE; player->is_snapping = TRUE; @@ -9556,9 +9925,13 @@ boolean DropElement(struct PlayerInfo *player) if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */ { +#if 1 + InitField_WithBug1(jx, jy, FALSE); +#else InitField(jx, jy, FALSE); if (CAN_MOVE(Feld[jx][jy])) InitMovDir(jx, jy); +#endif } new_element = Feld[jx][jy]; @@ -9569,7 +9942,7 @@ boolean DropElement(struct PlayerInfo *player) int move_stepsize = element_info[new_element].move_stepsize; int direction, dx, dy, nextx, nexty; - if (element_info[new_element].move_direction_initial == MV_AUTOMATIC) + if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC) MovDir[jx][jy] = player->MovDir; direction = MovDir[jx][jy];