X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=c75171ba1306cd893467e33428f739e204f7a2ce;hb=adaabca253f2e76597725cdc86fc1fff6f0de1d2;hp=2fd57d065386332b48ac485ef92cf707fec8edd2;hpb=12d604d7378a05f91df4b061195a9943f2214da8;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 2fd57d06..c75171ba 100644 --- a/src/game.c +++ b/src/game.c @@ -101,7 +101,9 @@ #define NUM_GAME_BUTTONS 6 /* forward declaration for internal use */ +static void CloseAllOpenTimegates(void); static void CheckGravityMovement(struct PlayerInfo *); +static void KillHeroUnlessProtected(int, int); static void MapGameButtons(); static void HandleGameButtons(struct GadgetInfo *); @@ -188,19 +190,19 @@ static int getBeltNrFromElement(int element) static int getBeltNrFromSwitchElement(int element) { - return (element < EL_BELT2_SWITCH_L ? 0 : - element < EL_BELT3_SWITCH_L ? 1 : - element < EL_BELT4_SWITCH_L ? 2 : 3); + return (element < EL_BELT2_SWITCH_LEFT ? 0 : + element < EL_BELT3_SWITCH_LEFT ? 1 : + element < EL_BELT4_SWITCH_LEFT ? 2 : 3); } static int getBeltDirNrFromSwitchElement(int element) { static int belt_base_element[4] = { - EL_BELT1_SWITCH_L, - EL_BELT2_SWITCH_L, - EL_BELT3_SWITCH_L, - EL_BELT4_SWITCH_L + EL_BELT1_SWITCH_LEFT, + EL_BELT2_SWITCH_LEFT, + EL_BELT3_SWITCH_LEFT, + EL_BELT4_SWITCH_LEFT }; int belt_nr = getBeltNrFromSwitchElement(element); @@ -281,36 +283,41 @@ static void InitField(int x, int y, boolean init_game) Feld[x][y] = EL_BADEWANNE5; break; - case EL_KAEFER_R: - case EL_KAEFER_O: - case EL_KAEFER_L: - case EL_KAEFER_U: + case EL_KAEFER_RIGHT: + case EL_KAEFER_UP: + case EL_KAEFER_LEFT: + case EL_KAEFER_DOWN: case EL_KAEFER: - case EL_FLIEGER_R: - case EL_FLIEGER_O: - case EL_FLIEGER_L: - case EL_FLIEGER_U: + case EL_FLIEGER_RIGHT: + case EL_FLIEGER_UP: + case EL_FLIEGER_LEFT: + case EL_FLIEGER_DOWN: case EL_FLIEGER: - case EL_BUTTERFLY_R: - case EL_BUTTERFLY_O: - case EL_BUTTERFLY_L: - case EL_BUTTERFLY_U: + case EL_BUTTERFLY_RIGHT: + case EL_BUTTERFLY_UP: + case EL_BUTTERFLY_LEFT: + case EL_BUTTERFLY_DOWN: case EL_BUTTERFLY: - case EL_FIREFLY_R: - case EL_FIREFLY_O: - case EL_FIREFLY_L: - case EL_FIREFLY_U: + case EL_FIREFLY_RIGHT: + case EL_FIREFLY_UP: + case EL_FIREFLY_LEFT: + case EL_FIREFLY_DOWN: case EL_FIREFLY: - case EL_PACMAN_R: - case EL_PACMAN_O: - case EL_PACMAN_L: - case EL_PACMAN_U: + case EL_PACMAN_RIGHT: + case EL_PACMAN_UP: + case EL_PACMAN_LEFT: + case EL_PACMAN_DOWN: case EL_MAMPFER: case EL_MAMPFER2: case EL_ROBOT: case EL_PACMAN: case EL_SP_SNIKSNAK: case EL_SP_ELECTRON: + case EL_MOLE_LEFT: + case EL_MOLE_RIGHT: + case EL_MOLE_UP: + case EL_MOLE_DOWN: + case EL_MOLE: InitMovDir(x, y); break; @@ -339,7 +346,6 @@ static void InitField(int x, int y, boolean init_game) local_player->sokobanfields_still_needed++; break; - case EL_MAULWURF: case EL_PINGUIN: local_player->friends_still_needed++; break; @@ -366,18 +372,18 @@ static void InitField(int x, int y, boolean init_game) Feld[x][y] = EL_EM_KEY_4; break; - case EL_BELT1_SWITCH_L: - case EL_BELT1_SWITCH_M: - case EL_BELT1_SWITCH_R: - case EL_BELT2_SWITCH_L: - case EL_BELT2_SWITCH_M: - case EL_BELT2_SWITCH_R: - case EL_BELT3_SWITCH_L: - case EL_BELT3_SWITCH_M: - case EL_BELT3_SWITCH_R: - case EL_BELT4_SWITCH_L: - case EL_BELT4_SWITCH_M: - case EL_BELT4_SWITCH_R: + case EL_BELT1_SWITCH_LEFT: + case EL_BELT1_SWITCH_MIDDLE: + case EL_BELT1_SWITCH_RIGHT: + case EL_BELT2_SWITCH_LEFT: + case EL_BELT2_SWITCH_MIDDLE: + case EL_BELT2_SWITCH_RIGHT: + case EL_BELT3_SWITCH_LEFT: + case EL_BELT3_SWITCH_MIDDLE: + case EL_BELT3_SWITCH_RIGHT: + case EL_BELT4_SWITCH_LEFT: + case EL_BELT4_SWITCH_MIDDLE: + case EL_BELT4_SWITCH_RIGHT: if (init_game) { int belt_nr = getBeltNrFromSwitchElement(Feld[x][y]); @@ -394,6 +400,18 @@ static void InitField(int x, int y, boolean init_game) Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr]; } } + break; + + case EL_SWITCHGATE_SWITCH_2: /* always start with same switch pos */ + if (init_game) + Feld[x][y] = EL_SWITCHGATE_SWITCH_1; + break; + + case EL_LIGHT_SWITCH_ON: + if (init_game) + game.light_time_left = level.time_light * FRAMES_PER_SECOND; + break; + default: break; } @@ -463,6 +481,9 @@ void InitGame() player->last_jx = player->last_jy = 0; player->jx = player->jy = 0; + player->shield_passive_time_left = 0; + player->shield_active_time_left = 0; + DigField(player, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); @@ -495,10 +516,15 @@ void InitGame() AllPlayersGone = FALSE; game.magic_wall_active = FALSE; game.magic_wall_time_left = 0; + game.light_time_left = 0; + game.timegate_time_left = 0; + game.switchgate_pos = 0; + game.balloon_dir = MV_NO_MOVING; + for (i=0; i<4; i++) { game.belt_dir[i] = MV_NO_MOVING; - game.belt_dir_nr[i] = 3; /* no moving, next switch left */ + game.belt_dir_nr[i] = 3; /* not moving, next moving left */ } for (i=0; iMovPos) return; @@ -921,17 +966,26 @@ void GameWon() SaveTape(tape.level_nr); /* Ask to save tape */ } + if (level_nr == leveldir_current->handicap_level) + { + leveldir_current->handicap_level++; + SaveLevelSetup_SeriesInfo(); + + if (level_nr < leveldir_current->last_level) + raise_level = TRUE; + } + if ((hi_pos = NewHiScore()) >= 0) { game_status = HALLOFFAME; DrawHallOfFame(hi_pos); - if (bumplevel && TAPE_IS_EMPTY(tape)) + if (raise_level) level_nr++; } else { game_status = MAINMENU; - if (bumplevel && TAPE_IS_EMPTY(tape)) + if (raise_level) level_nr++; DrawMainMenu(); } @@ -1221,10 +1275,22 @@ void Explode(int ex, int ey, int phase, int mode) if (IS_MASSIVE(element) || element == EL_BURNING) continue; + if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y))) + { + if (IS_ACTIVE_BOMB(element)) + { + /* re-activate things under the bomb like gate or penguin */ + Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_LEERRAUM); + Store[x][y] = 0; + } + + continue; + } + if (element == EL_EXPLODING) element = Store2[x][y]; - if (IS_PLAYER(ex, ey)) + if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey)) { switch(StorePlayer[ex][ey]) { @@ -1246,7 +1312,7 @@ void Explode(int ex, int ey, int phase, int mode) if (game.emulation == EMU_SUPAPLEX) Store[x][y] = EL_LEERRAUM; } - else if (center_element == EL_MAULWURF) + else if (center_element == EL_MOLE) Store[x][y] = EL_EDELSTEIN_ROT; else if (center_element == EL_PINGUIN) Store[x][y] = EL_EDELSTEIN_LILA; @@ -1272,6 +1338,10 @@ void Explode(int ex, int ey, int phase, int mode) Store[x][y] = EL_EDELSTEIN_ROT; else if (element == EL_ERZ_EDEL_LILA) Store[x][y] = EL_EDELSTEIN_LILA; + else if (element == EL_WALL_PEARL) + Store[x][y] = EL_PEARL; + else if (element == EL_WALL_CRYSTAL) + Store[x][y] = EL_CRYSTAL; else if (!IS_PFORTE(Store[x][y])) Store[x][y] = EL_LEERRAUM; @@ -1325,7 +1395,7 @@ void Explode(int ex, int ey, int phase, int mode) int element = Store2[x][y]; if (IS_PLAYER(x, y)) - KillHero(PLAYERINFO(x, y)); + KillHeroUnlessProtected(x, y); else if (IS_EXPLOSIVE(element)) { Feld[x][y] = Store2[x][y]; @@ -1425,8 +1495,10 @@ void Bang(int x, int y) else PlaySoundLevel(x, y, SND_ROAAAR); +#if 0 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */ element = EL_LEERRAUM; +#endif switch(element) { @@ -1438,6 +1510,7 @@ void Bang(int x, int y) case EL_MAMPFER2: case EL_ROBOT: case EL_PACMAN: + case EL_MOLE: RaiseScoreElement(element); Explode(x, y, EX_PHASE_START, EX_NORMAL); break; @@ -1450,11 +1523,13 @@ void Bang(int x, int y) case EL_DYNABOMB_XL: DynaExplode(x, y); break; - case EL_MAULWURF: case EL_PINGUIN: case EL_BIRNE_AUS: case EL_BIRNE_EIN: - Explode(x, y, EX_PHASE_START, EX_CENTER); + if (IS_PLAYER(x, y)) + Explode(x, y, EX_PHASE_START, EX_NORMAL); + else + Explode(x, y, EX_PHASE_START, EX_CENTER); break; default: Explode(x, y, EX_PHASE_START, EX_NORMAL); @@ -1504,6 +1579,174 @@ void Blurb(int x, int y) } } +static void ToggleBeltSwitch(int x, int y) +{ + static int belt_base_element[4] = + { + EL_BELT1_SWITCH_LEFT, + EL_BELT2_SWITCH_LEFT, + EL_BELT3_SWITCH_LEFT, + EL_BELT4_SWITCH_LEFT + }; + static int belt_move_dir[4] = + { + MV_LEFT, + MV_NO_MOVING, + MV_RIGHT, + MV_NO_MOVING, + }; + + int element = Feld[x][y]; + int belt_nr = getBeltNrFromSwitchElement(element); + int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4; + int belt_dir = belt_move_dir[belt_dir_nr]; + int xx, yy; + + if (!IS_BELT_SWITCH(element)) + return; + + game.belt_dir_nr[belt_nr] = belt_dir_nr; + game.belt_dir[belt_nr] = belt_dir; + + if (belt_dir_nr == 3) + belt_dir_nr = 1; + + for (yy=0; yy 0) + { + Feld[x][y] = EL_LIGHT_SWITCH_ON; + DrawLevelField(x, y); + } + else if (element == EL_LIGHT_SWITCH_ON && + game.light_time_left == 0) + { + Feld[x][y] = EL_LIGHT_SWITCH_OFF; + DrawLevelField(x, y); + } + + if (element == EL_INVISIBLE_STEEL || + element == EL_UNSICHTBAR || + element == EL_SAND_INVISIBLE) + DrawLevelField(x, y); + } + } +} + +static void ToggleLightSwitch(int x, int y) +{ + int element = Feld[x][y]; + + game.light_time_left = + (element == EL_LIGHT_SWITCH_OFF ? + level.time_light * FRAMES_PER_SECOND : 0); + + RedrawAllLightSwitchesAndInvisibleElements(); +} + +static void ActivateTimegateSwitch(int x, int y) +{ + int xx, yy; + + game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND; + + for (yy=0; yy=0) + if (sound >= 0) PlaySoundLevel(x, y, sound); } } @@ -1728,10 +2004,10 @@ void TurnRound(int x, int y) TestIfBadThingHitsOtherBadThing(x, y); if (IN_LEV_FIELD(right_x, right_y) && - IS_FREE_OR_PLAYER(right_x, right_y)) + IS_FREE(right_x, right_y)) MovDir[x][y] = right_dir; else if (!IN_LEV_FIELD(move_x, move_y) || - !IS_FREE_OR_PLAYER(move_x, move_y)) + !IS_FREE(move_x, move_y)) MovDir[x][y] = left_dir; if (element == EL_KAEFER && MovDir[x][y] != old_move_dir) @@ -1745,10 +2021,10 @@ void TurnRound(int x, int y) TestIfBadThingHitsOtherBadThing(x, y); if (IN_LEV_FIELD(left_x, left_y) && - IS_FREE_OR_PLAYER(left_x, left_y)) + IS_FREE(left_x, left_y)) MovDir[x][y] = left_dir; else if (!IN_LEV_FIELD(move_x, move_y) || - !IS_FREE_OR_PLAYER(move_x, move_y)) + !IS_FREE(move_x, move_y)) MovDir[x][y] = right_dir; if ((element == EL_FLIEGER || @@ -1931,8 +2207,51 @@ void TurnRound(int x, int y) MovDelay[x][y] = 0; } - else if (element == EL_ROBOT || element == EL_SONDE || - element == EL_MAULWURF || element == EL_PINGUIN) + else if (element == EL_MOLE) + { + boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE; + + if (IN_LEV_FIELD(move_x, move_y) && + (IS_FREE(move_x, move_y) || IS_AMOEBOID(Feld[move_x][move_y]) || + Feld[move_x][move_y] == EL_DEAMOEBING)) + can_move_on = TRUE; + + if (!can_move_on) + { + if (IN_LEV_FIELD(left_x, left_y) && + (IS_FREE(left_x, left_y) || IS_AMOEBOID(Feld[left_x][left_y]))) + can_turn_left = TRUE; + if (IN_LEV_FIELD(right_x, right_y) && + (IS_FREE(right_x, right_y) || IS_AMOEBOID(Feld[right_x][right_y]))) + can_turn_right = TRUE; + + if (can_turn_left && can_turn_right) + MovDir[x][y] = (RND(2) ? left_dir : right_dir); + else if (can_turn_left) + MovDir[x][y] = left_dir; + else + MovDir[x][y] = right_dir; + } + + if (MovDir[x][y] != old_move_dir) + MovDelay[x][y] = 9; + } + else if (element == EL_BALLOON) + { + MovDir[x][y] = game.balloon_dir; + MovDelay[x][y] = 0; + } + else if (element == EL_SPRING_MOVING) + { + if (!IN_LEV_FIELD(move_x, move_y) || !IS_FREE(move_x, move_y) || + (IN_LEV_FIELD(x, y+1) && IS_FREE(x, y+1))) + { + Feld[x][y] = EL_SPRING; + MovDir[x][y] = MV_NO_MOVING; + } + MovDelay[x][y] = 0; + } + else if (element == EL_ROBOT || element == EL_SONDE || element == EL_PINGUIN) { int attr_x = -1, attr_y = -1; @@ -1967,7 +2286,7 @@ void TurnRound(int x, int y) attr_y = ZY; } - if (element == EL_MAULWURF || element == EL_PINGUIN) + if (element == EL_PINGUIN) { int i; static int xy[4][2] = @@ -2033,7 +2352,7 @@ void TurnRound(int x, int y) if (IN_LEV_FIELD(newx, newy) && (IS_FREE(newx, newy) || Feld[newx][newy] == EL_SALZSAEURE || - ((element == EL_MAULWURF || element == EL_PINGUIN) && + (element == EL_PINGUIN && (Feld[newx][newy] == EL_AUSGANG_AUF || IS_MAMPF3(Feld[newx][newy]))))) return; @@ -2045,7 +2364,7 @@ void TurnRound(int x, int y) if (IN_LEV_FIELD(newx, newy) && (IS_FREE(newx, newy) || Feld[newx][newy] == EL_SALZSAEURE || - ((element == EL_MAULWURF || element == EL_PINGUIN) && + (element == EL_PINGUIN && (Feld[newx][newy] == EL_AUSGANG_AUF || IS_MAMPF3(Feld[newx][newy]))))) return; @@ -2115,7 +2434,8 @@ void StartMoving(int x, int y) Feld[x][y+1] = EL_MORAST_VOLL; } } - else if (element == EL_FELSBROCKEN && Feld[x][y+1] == EL_MORAST_LEER) + else if ((element == EL_FELSBROCKEN || element == EL_BD_ROCK) && + Feld[x][y+1] == EL_MORAST_LEER) { InitMovingField(x, y, MV_DOWN); Store[x][y] = EL_MORAST_VOLL; @@ -2204,7 +2524,8 @@ void StartMoving(int x, int y) else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1]) #else else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1] && - !IS_FALLING(x, y+1) && !JustStopped[x][y+1]) + !IS_FALLING(x, y+1) && !JustStopped[x][y+1] && + element != EL_DX_SUPABOMB) #endif { boolean left = (x>0 && IS_FREE(x-1, y) && @@ -2214,7 +2535,9 @@ void StartMoving(int x, int y) if (left || right) { - if (left && right && game.emulation != EMU_BOULDERDASH) + if (left && right && + (game.emulation != EMU_BOULDERDASH && + element != EL_BD_ROCK && element != EL_EDELSTEIN_BD)) left = !(right = RND(2)); InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT); @@ -2236,7 +2559,9 @@ void StartMoving(int x, int y) { int newx, newy; - if (element == EL_SONDE && JustBeingPushed(x, y)) + if ((element == EL_SONDE || element == EL_BALLOON || + element == EL_SPRING_MOVING) + && JustBeingPushed(x, y)) return; if (!MovDelay[x][y]) /* start new movement phase */ @@ -2247,9 +2572,11 @@ void StartMoving(int x, int y) if (element!=EL_MAMPFER && element!=EL_MAMPFER2 && element!=EL_PACMAN) { TurnRound(x, y); - if (MovDelay[x][y] && (element == EL_KAEFER || element == EL_FLIEGER || + if (MovDelay[x][y] && (element == EL_KAEFER || + element == EL_FLIEGER || element == EL_SP_SNIKSNAK || - element == EL_SP_ELECTRON)) + element == EL_SP_ELECTRON || + element == EL_MOLE)) DrawLevelField(x, y); } } @@ -2335,23 +2662,30 @@ void StartMoving(int x, int y) Moving2Blocked(x, y, &newx, &newy); /* get next screen position */ - if (IS_ENEMY(element) && IS_PLAYER(newx, newy)) + if (IS_ENEMY(element) && IS_PLAYER(newx, newy) && + !PLAYER_PROTECTED(newx, newy)) { + +#if 1 + TestIfBadThingHitsHero(x, y); + return; +#else /* enemy got the player */ MovDir[x][y] = 0; KillHero(PLAYERINFO(newx, newy)); return; +#endif + } - else if ((element == EL_MAULWURF || element == EL_PINGUIN || - element == EL_ROBOT || element == EL_SONDE) && + else if ((element == EL_PINGUIN || element == EL_ROBOT || + element == EL_SONDE || element == EL_BALLOON) && IN_LEV_FIELD(newx, newy) && MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_SALZSAEURE) { Blurb(x, y); Store[x][y] = EL_SALZSAEURE; } - else if ((element == EL_MAULWURF || element == EL_PINGUIN) && - IN_LEV_FIELD(newx, newy)) + else if (element == EL_PINGUIN && IN_LEV_FIELD(newx, newy)) { if (Feld[newx][newy] == EL_AUSGANG_AUF) { @@ -2476,8 +2810,8 @@ void StartMoving(int x, int y) DrawLevelField(newx, newy); } } - else if (element == EL_PACMAN && IN_LEV_FIELD(newx, newy) && - IS_AMOEBOID(Feld[newx][newy])) + else if ((element == EL_PACMAN || element == EL_MOLE) + && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy])) { if (AmoebaNr[newx][newy]) { @@ -2487,8 +2821,24 @@ void StartMoving(int x, int y) AmoebaCnt[AmoebaNr[newx][newy]]--; } - Feld[newx][newy] = EL_LEERRAUM; - DrawLevelField(newx, newy); + if (element == EL_MOLE) + { + Feld[newx][newy] = EL_DEAMOEBING; + MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */ + return; /* wait for shrinking amoeba */ + } + else /* element == EL_PACMAN */ + { + Feld[newx][newy] = EL_LEERRAUM; + DrawLevelField(newx, newy); + } + } + else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) && + (Feld[newx][newy] == EL_DEAMOEBING || + (Feld[newx][newy] == EL_LEERRAUM && Stop[newx][newy]))) + { + /* wait for shrinking amoeba to completely disappear */ + return; } else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy)) { @@ -2497,7 +2847,7 @@ void StartMoving(int x, int y) TurnRound(x, y); if (element == EL_KAEFER || element == EL_FLIEGER || - element == EL_SP_SNIKSNAK) + element == EL_SP_SNIKSNAK || element == EL_MOLE) DrawLevelField(x, y); else if (element == EL_BUTTERFLY || element == EL_FIREFLY) DrawGraphicAnimation(x, y, el2gfx(element), 2, 4, ANIM_NORMAL); @@ -2506,6 +2856,9 @@ void StartMoving(int x, int y) else if (element == EL_SP_ELECTRON) DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL); + if (DONT_TOUCH(element)) + TestIfBadThingHitsHero(x, y); + return; } @@ -2536,6 +2889,8 @@ void ContinueMoving(int x, int y) else if (CAN_FALL(element) && horiz_move && y < lev_fieldy-1 && IS_BELT(Feld[x][y+1])) step /= 2; + else if (element == EL_SPRING_MOVING) + step*=2; #if OLD_GAME_BEHAVIOUR else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element)) @@ -2549,6 +2904,32 @@ void ContinueMoving(int x, int y) Feld[x][y] = EL_LEERRAUM; Feld[newx][newy] = element; + if (element == EL_MOLE) + { + int i; + static int xy[4][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } + }; + + Feld[x][y] = EL_ERDREICH; + DrawLevelField(x, y); + + for(i=0; i<4; i++) + { + int xx, yy; + + xx = x + xy[i][0]; + yy = y + xy[i][1]; + + if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_ERDREICH) + DrawLevelField(xx, yy); /* for "ErdreichAnbroeckeln()" */ + } + } + if (Store[x][y] == EL_MORAST_VOLL) { Store[x][y] = 0; @@ -2790,7 +3171,7 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element) if (done) PlaySoundLevel(ax, ay, - (new_element == EL_FELSBROCKEN ? SND_KLOPF : SND_PLING)); + (new_element == EL_BD_ROCK ? SND_KLOPF : SND_PLING)); } void AmoebeWaechst(int x, int y) @@ -2824,6 +3205,40 @@ void AmoebeWaechst(int x, int y) } } +void AmoebeSchrumpft(int x, int y) +{ + static unsigned long sound_delay = 0; + static unsigned long sound_delay_value = 0; + + if (!MovDelay[x][y]) /* start new shrinking cycle */ + { + MovDelay[x][y] = 7; + + if (DelayReached(&sound_delay, sound_delay_value)) + { + PlaySoundLevel(x, y, SND_BLURB); + sound_delay_value = 30; + } + } + + if (MovDelay[x][y]) /* wait some time before shrinking */ + { + MovDelay[x][y]--; + if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + DrawGraphic(SCREENX(x), SCREENY(y), GFX_AMOEBING + MovDelay[x][y]/2); + + if (!MovDelay[x][y]) + { + Feld[x][y] = EL_LEERRAUM; + DrawLevelField(x, y); + + /* don't let mole enter this field in this cycle; + (give priority to objects falling to this field from above) */ + Stop[x][y] = TRUE; + } + } +} + void AmoebeAbleger(int ax, int ay) { int i; @@ -2940,7 +3355,7 @@ void AmoebeAbleger(int ax, int ay) if (element == EL_AMOEBE_BD && AmoebaCnt2[new_group_nr] >= 200) { - AmoebeUmwandelnBD(newax, neway, EL_FELSBROCKEN); + AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK); return; } } @@ -3056,6 +3471,31 @@ void Ablenk(int x, int y) ZX = ZY = -1; } +void TimegateWheel(int x, int y) +{ + if (!MovDelay[x][y]) /* next animation frame */ + MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND; + + if (MovDelay[x][y]) /* wait some time before next frame */ + { + MovDelay[x][y]--; + if (MovDelay[x][y]) + { + if (IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + DrawGraphic(SCREENX(x), SCREENY(y), + GFX_TIMEGATE_SWITCH + MovDelay[x][y]%4); + if (!(MovDelay[x][y]%4)) + PlaySoundLevel(x, y, SND_MIEP); + return; + } + } + + Feld[x][y] = EL_TIMEGATE_SWITCH_OFF; + DrawLevelField(x, y); + if (ZX == x && ZY == y) + ZX = ZY = -1; +} + void Birne(int x, int y) { if (!MovDelay[x][y]) /* next animation frame */ @@ -3102,7 +3542,8 @@ void NussKnacken(int x, int y) { MovDelay[x][y]--; if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) - DrawGraphic(SCREENX(x), SCREENY(y), GFX_CRACKINGNUT+3-MovDelay[x][y]/2); + DrawGraphic(SCREENX(x), SCREENY(y), + GFX_CRACKINGNUT + 3 - MovDelay[x][y]/2); if (!MovDelay[x][y]) { @@ -3112,14 +3553,34 @@ void NussKnacken(int x, int y) } } -void SiebAktivieren(int x, int y, int typ) +void BreakingPearl(int x, int y) { - int graphic = (typ == 1 ? GFX_SIEB_VOLL : GFX_SIEB2_VOLL) + 3; + if (!MovDelay[x][y]) /* next animation frame */ + MovDelay[x][y] = 9; - DrawGraphicAnimation(x, y, graphic, 4, 4, ANIM_REVERSE); -} + if (MovDelay[x][y]) /* wait some time before next frame */ + { + MovDelay[x][y]--; + if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + DrawGraphic(SCREENX(x), SCREENY(y), + GFX_PEARL_BREAKING + 4 - MovDelay[x][y]/2); -void AusgangstuerPruefen(int x, int y) + if (!MovDelay[x][y]) + { + Feld[x][y] = EL_LEERRAUM; + DrawLevelField(x, y); + } + } +} + +void SiebAktivieren(int x, int y, int typ) +{ + int graphic = (typ == 1 ? GFX_SIEB_VOLL : GFX_SIEB2_VOLL) + 3; + + DrawGraphicAnimation(x, y, graphic, 4, 4, ANIM_REVERSE); +} + +void AusgangstuerPruefen(int x, int y) { if (!local_player->gems_still_needed && !local_player->sokobanfields_still_needed && @@ -3164,6 +3625,121 @@ void AusgangstuerBlinken(int x, int y) DrawGraphicAnimation(x, y, GFX_AUSGANG_AUF, 4, 4, ANIM_OSCILLATE); } +void OpenSwitchgate(int x, int y) +{ + int delay = 6; + + if (!MovDelay[x][y]) /* next animation frame */ + MovDelay[x][y] = 5 * delay; + + if (MovDelay[x][y]) /* wait some time before next frame */ + { + int phase; + + MovDelay[x][y]--; + phase = MovDelay[x][y] / delay; + if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + DrawGraphic(SCREENX(x), SCREENY(y), GFX_SWITCHGATE_OPEN - phase); + + if (!MovDelay[x][y]) + { + Feld[x][y] = EL_SWITCHGATE_OPEN; + DrawLevelField(x, y); + } + } +} + +void CloseSwitchgate(int x, int y) +{ + int delay = 6; + + if (!MovDelay[x][y]) /* next animation frame */ + MovDelay[x][y] = 5 * delay; + + if (MovDelay[x][y]) /* wait some time before next frame */ + { + int phase; + + MovDelay[x][y]--; + phase = MovDelay[x][y] / delay; + if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + DrawGraphic(SCREENX(x), SCREENY(y), GFX_SWITCHGATE_CLOSED + phase); + + if (!MovDelay[x][y]) + { + Feld[x][y] = EL_SWITCHGATE_CLOSED; + DrawLevelField(x, y); + } + } +} + +void OpenTimegate(int x, int y) +{ + int delay = 6; + + if (!MovDelay[x][y]) /* next animation frame */ + MovDelay[x][y] = 5 * delay; + + if (MovDelay[x][y]) /* wait some time before next frame */ + { + int phase; + + MovDelay[x][y]--; + phase = MovDelay[x][y] / delay; + if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + DrawGraphic(SCREENX(x), SCREENY(y), GFX_TIMEGATE_OPEN - phase); + + if (!MovDelay[x][y]) + { + Feld[x][y] = EL_TIMEGATE_OPEN; + DrawLevelField(x, y); + } + } +} + +void CloseTimegate(int x, int y) +{ + int delay = 6; + + if (!MovDelay[x][y]) /* next animation frame */ + MovDelay[x][y] = 5 * delay; + + if (MovDelay[x][y]) /* wait some time before next frame */ + { + int phase; + + MovDelay[x][y]--; + phase = MovDelay[x][y] / delay; + if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + DrawGraphic(SCREENX(x), SCREENY(y), GFX_TIMEGATE_CLOSED + phase); + + if (!MovDelay[x][y]) + { + Feld[x][y] = EL_TIMEGATE_CLOSED; + DrawLevelField(x, y); + } + } +} + +static void CloseAllOpenTimegates() +{ + int x, y; + + for (y=0; y= num_frames/2) + phase = num_frames - phase; + + if (IN_SCR_FIELD(SCREENX(x), SCREENY(y))) + { + DrawGraphic(SCREENX(x),SCREENY(y), GFX_TRAP_INACTIVE + phase - 1); + ErdreichAnbroeckeln(SCREENX(x), SCREENY(y)); + } + } + + return; + } + + Feld[x][y] = EL_TRAP_INACTIVE; + DrawLevelField(x, y); + } + } +} + static void DrawBeltAnimation(int x, int y, int element) { int belt_nr = getBeltNrFromElement(element); @@ -3553,7 +4184,8 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action) if (IN_LEV_FIELD(jx+dx, jy) && IS_PUSHABLE(Feld[jx+dx][jy])) { int el = Feld[jx+dx][jy]; - int push_delay = (IS_SB_ELEMENT(el) || el == EL_SONDE ? 2 : 10); + int push_delay = (IS_SB_ELEMENT(el) || el == EL_SONDE ? 2 : + (el == EL_BALLOON || el == EL_SPRING) ? 0 : 10); if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay) { @@ -3727,18 +4359,24 @@ void GameActions() Explode(x, y, Frame[x][y], EX_NORMAL); else if (element == EL_AMOEBING) AmoebeWaechst(x, y); + else if (element == EL_DEAMOEBING) + AmoebeSchrumpft(x, y); else if (IS_AMOEBALIVE(element)) AmoebeAbleger(x, y); else if (element == EL_LIFE || element == EL_LIFE_ASYNC) Life(x, y); else if (element == EL_ABLENK_EIN) Ablenk(x, y); + else if (element == EL_TIMEGATE_SWITCH_ON) + TimegateWheel(x, y); else if (element == EL_SALZSAEURE) Blubber(x, y); else if (element == EL_BLURB_LEFT || element == EL_BLURB_RIGHT) Blurb(x, y); else if (element == EL_CRACKINGNUT) NussKnacken(x, y); + else if (element == EL_PEARL_BREAKING) + BreakingPearl(x, y); else if (element == EL_AUSGANG_ZU) AusgangstuerPruefen(x, y); else if (element == EL_AUSGANG_ACT) @@ -3756,12 +4394,28 @@ void GameActions() CheckForDragon(x, y); else if (element == EL_SP_BUG || element == EL_SP_BUG_ACTIVE) CheckBuggyBase(x, y); + else if (element == EL_TRAP_INACTIVE || element == EL_TRAP_ACTIVE) + CheckTrap(x, y); else if (element == EL_SP_TERMINAL) DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 12, ANIM_NORMAL); else if (element == EL_SP_TERMINAL_ACTIVE) DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 4, ANIM_NORMAL); else if (IS_BELT(element)) DrawBeltAnimation(x, y, element); + else if (element == EL_SWITCHGATE_OPENING) + OpenSwitchgate(x, y); + else if (element == EL_SWITCHGATE_CLOSING) + CloseSwitchgate(x, y); + else if (element == EL_TIMEGATE_OPENING) + OpenTimegate(x, y); + else if (element == EL_TIMEGATE_CLOSING) + CloseTimegate(x, y); + else if (element == EL_EXTRA_TIME) + DrawGraphicAnimation(x, y, GFX_EXTRA_TIME, 6, 4, ANIM_NORMAL); + else if (element == EL_SHIELD_PASSIVE) + DrawGraphicAnimation(x, y, GFX_SHIELD_PASSIVE, 6, 4, ANIM_NORMAL); + else if (element == EL_SHIELD_ACTIVE) + DrawGraphicAnimation(x, y, GFX_SHIELD_ACTIVE, 6, 4, ANIM_NORMAL); if (game.magic_wall_active) { @@ -3803,6 +4457,7 @@ void GameActions() for (y=0; y= (1000 / GameFrameDelay) && !tape.pausing) + if (game.light_time_left > 0) + { + game.light_time_left--; + + if (game.light_time_left == 0) + { + for (y=0; y 0) + { + game.timegate_time_left--; + + if (game.timegate_time_left == 0) + CloseAllOpenTimegates(); + } + + if (TimeFrames >= (1000 / GameFrameDelay)) { TimeFrames = 0; TimePlayed++; + for (i=0; i 0) + stored_player[i].shield_active_time_left--; + } + } + if (tape.recording || tape.playing) DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed); @@ -3832,12 +4529,12 @@ void GameActions() { TimeLeft--; - if (TimeLeft <= 10) + if (TimeLeft <= 10 && setup.time_limit) PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT); DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW); - if (!TimeLeft) + if (!TimeLeft && setup.time_limit) for (i=0; ishield_active_time_left > 0) + Bang(killx, killy); + else if (!PLAYER_PROTECTED(goodx, goody)) + KillHero(player); + } else Bang(goodx, goody); } @@ -4320,6 +5024,9 @@ void TestIfBadThingHitsGoodThing(int badx, int bady) MV_DOWN }; + if (Feld[badx][bady] == EL_EXPLODING) /* skip just exploding bad things */ + return; + for (i=0; i<4; i++) { int x, y, element; @@ -4351,7 +5058,14 @@ void TestIfBadThingHitsGoodThing(int badx, int bady) if (killx != badx || killy != bady) { if (IS_PLAYER(killx, killy)) - KillHero(PLAYERINFO(killx, killy)); + { + struct PlayerInfo *player = PLAYERINFO(killx, killy); + + if (player->shield_active_time_left > 0) + Bang(badx, bady); + else if (!PLAYER_PROTECTED(killx, killy)) + KillHero(player); + } else Bang(killx, killy); } @@ -4421,10 +5135,20 @@ void KillHero(struct PlayerInfo *player) if (IS_PFORTE(Feld[jx][jy])) Feld[jx][jy] = EL_LEERRAUM; + /* deactivate shield (else Bang()/Explode() would not work right) */ + player->shield_passive_time_left = 0; + player->shield_active_time_left = 0; + Bang(jx, jy); BuryHero(player); } +static void KillHeroUnlessProtected(int x, int y) +{ + if (!PLAYER_PROTECTED(x, y)) + KillHero(PLAYERINFO(x, y)); +} + void BuryHero(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; @@ -4484,6 +5208,36 @@ int DigField(struct PlayerInfo *player, if (IS_MOVING(x, y) || IS_PLAYER(x, y)) return MF_NO_ACTION; + if (IS_TUBE(Feld[jx][jy])) + { + int i = 0; + int tube_leave_directions[][2] = + { + { EL_TUBE_CROSS, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }, + { EL_TUBE_VERTICAL, MV_UP | MV_DOWN }, + { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT }, + { EL_TUBE_VERT_LEFT, MV_LEFT | MV_UP | MV_DOWN }, + { EL_TUBE_VERT_RIGHT, MV_RIGHT | MV_UP | MV_DOWN }, + { EL_TUBE_HORIZ_UP, MV_LEFT | MV_RIGHT | MV_UP }, + { EL_TUBE_HORIZ_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 }, + { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN } + }; + + while (tube_leave_directions[i][0] != Feld[jx][jy]) + { + i++; + if (tube_leave_directions[i][0] == -1) /* should not happen */ + break; + } + + if (!(tube_leave_directions[i][1] & move_direction)) + return MF_NO_ACTION; /* tube has no opening in this direction */ + } + element = Feld[x][y]; switch (element) @@ -4493,6 +5247,8 @@ int DigField(struct PlayerInfo *player, break; case EL_ERDREICH: + case EL_SAND_INVISIBLE: + case EL_TRAP_INACTIVE: Feld[x][y] = EL_LEERRAUM; PlaySoundLevel(x, y, SND_SCHLURF); break; @@ -4510,8 +5266,12 @@ int DigField(struct PlayerInfo *player, case EL_EDELSTEIN_LILA: case EL_DIAMANT: case EL_SP_INFOTRON: + case EL_PEARL: + case EL_CRYSTAL: RemoveField(x, y); - local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 : 1); + local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 : + element == EL_PEARL ? 5 : + element == EL_CRYSTAL ? 8 : 1); if (local_player->gems_still_needed < 0) local_player->gems_still_needed = 0; RaiseScoreElement(element); @@ -4530,6 +5290,34 @@ int DigField(struct PlayerInfo *player, PlaySoundLevel(x, y, SND_PONG); break; + case EL_ENVELOPE: + Feld[x][y] = EL_LEERRAUM; + PlaySoundLevel(x, y, SND_PONG); + break; + + case EL_EXTRA_TIME: + RemoveField(x, y); + if (level.time > 0) + { + TimeLeft += 10; + DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW); + } + PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT); + break; + + case EL_SHIELD_PASSIVE: + RemoveField(x, y); + player->shield_passive_time_left += 10; + PlaySoundLevel(x, y, SND_PONG); + break; + + case EL_SHIELD_ACTIVE: + RemoveField(x, y); + player->shield_passive_time_left += 10; + player->shield_active_time_left += 10; + PlaySoundLevel(x, y, SND_PONG); + break; + case EL_DYNAMITE_INACTIVE: case EL_SP_DISK_RED: RemoveField(x, y); @@ -4633,76 +5421,67 @@ int DigField(struct PlayerInfo *player, } break; - case EL_BELT1_SWITCH_L: - case EL_BELT1_SWITCH_M: - case EL_BELT1_SWITCH_R: - case EL_BELT2_SWITCH_L: - case EL_BELT2_SWITCH_M: - case EL_BELT2_SWITCH_R: - case EL_BELT3_SWITCH_L: - case EL_BELT3_SWITCH_M: - case EL_BELT3_SWITCH_R: - case EL_BELT4_SWITCH_L: - case EL_BELT4_SWITCH_M: - case EL_BELT4_SWITCH_R: + case EL_BELT1_SWITCH_LEFT: + case EL_BELT1_SWITCH_MIDDLE: + case EL_BELT1_SWITCH_RIGHT: + case EL_BELT2_SWITCH_LEFT: + case EL_BELT2_SWITCH_MIDDLE: + case EL_BELT2_SWITCH_RIGHT: + case EL_BELT3_SWITCH_LEFT: + case EL_BELT3_SWITCH_MIDDLE: + case EL_BELT3_SWITCH_RIGHT: + case EL_BELT4_SWITCH_LEFT: + case EL_BELT4_SWITCH_MIDDLE: + case EL_BELT4_SWITCH_RIGHT: + if (!player->Switching) { - static int belt_base_element[4] = - { - EL_BELT1_SWITCH_L, - EL_BELT2_SWITCH_L, - EL_BELT3_SWITCH_L, - EL_BELT4_SWITCH_L - }; - static int belt_move_dir[4] = - { - MV_LEFT, - MV_NO_MOVING, - MV_RIGHT, - MV_NO_MOVING, - }; - - int belt_nr = getBeltNrFromSwitchElement(element); - int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4; - int belt_dir = belt_move_dir[belt_dir_nr]; - int xx, yy; + player->Switching = TRUE; + ToggleBeltSwitch(x, y); + } + return MF_ACTION; + break; - if (player->Switching) - return MF_ACTION; + case EL_SWITCHGATE_SWITCH_1: + case EL_SWITCHGATE_SWITCH_2: + if (!player->Switching) + { + player->Switching = TRUE; + ToggleSwitchgateSwitch(x, y); + } + return MF_ACTION; + break; - game.belt_dir_nr[belt_nr] = belt_dir_nr; - game.belt_dir[belt_nr] = belt_dir; + case EL_LIGHT_SWITCH_OFF: + case EL_LIGHT_SWITCH_ON: + if (!player->Switching) + { + player->Switching = TRUE; + ToggleLightSwitch(x, y); + } + return MF_ACTION; + break; - if (belt_dir_nr == 3) - belt_dir_nr = 1; + case EL_TIMEGATE_SWITCH_OFF: + ActivateTimegateSwitch(x, y); - player->Switching = TRUE; + return MF_ACTION; + break; - for (yy=0; yypush_delay == 0) player->push_delay = FrameCounter; if (!FrameReached(&player->push_delay, player->push_delay_value) && - !tape.playing) + !tape.playing && element != EL_SPRING) return MF_NO_ACTION; RemoveField(x, y); Feld[x+dx][y+dy] = element; - player->push_delay_value = 2+RND(8); + if (element == EL_SPRING) + { + Feld[x+dx][y+dy] = EL_SPRING_MOVING; + MovDir[x+dx][y+dy] = move_direction; + } + + player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8)); DrawLevelField(x+dx, y+dy); - if (element == EL_FELSBROCKEN) + if (element == EL_FELSBROCKEN || element == EL_BD_ROCK) PlaySoundLevel(x+dx, y+dy, SND_PUSCH); else if (element == EL_KOKOSNUSS) PlaySoundLevel(x+dx, y+dy, SND_KNURK); else if (IS_SP_ELEMENT(element)) PlaySoundLevel(x+dx, y+dy, SND_SP_ZONKPUSH); else - PlaySoundLevel(x+dx, y+dy, SND_KLOPF); + PlaySoundLevel(x+dx, y+dy, SND_PUSCH); /* better than "SND_KLOPF" */ break; case EL_PFORTE1: @@ -4805,6 +5593,19 @@ int DigField(struct PlayerInfo *player, break; + case EL_SWITCHGATE_OPEN: + case EL_TIMEGATE_OPEN: + if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy)) + return MF_NO_ACTION; + + /* automatically move to the next field with double speed */ + player->programmed_action = move_direction; + DOUBLE_PLAYER_SPEED(player); + + PlaySoundLevel(x, y, SND_GATE); + + break; + case EL_SP_PORT1_LEFT: case EL_SP_PORT2_LEFT: case EL_SP_PORT1_RIGHT: @@ -4845,7 +5646,47 @@ int DigField(struct PlayerInfo *player, DOUBLE_PLAYER_SPEED(player); PlaySoundLevel(x, y, SND_GATE); + break; + + case EL_TUBE_CROSS: + case EL_TUBE_VERTICAL: + case EL_TUBE_HORIZONTAL: + case EL_TUBE_VERT_LEFT: + case EL_TUBE_VERT_RIGHT: + case EL_TUBE_HORIZ_UP: + case EL_TUBE_HORIZ_DOWN: + case EL_TUBE_LEFT_UP: + case EL_TUBE_LEFT_DOWN: + case EL_TUBE_RIGHT_UP: + case EL_TUBE_RIGHT_DOWN: + { + int i = 0; + int tube_enter_directions[][2] = + { + { EL_TUBE_CROSS, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }, + { EL_TUBE_VERTICAL, MV_UP | MV_DOWN }, + { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT }, + { EL_TUBE_VERT_LEFT, MV_RIGHT | MV_UP | MV_DOWN }, + { EL_TUBE_VERT_RIGHT, MV_LEFT | MV_UP | MV_DOWN }, + { EL_TUBE_HORIZ_UP, MV_LEFT | MV_RIGHT | MV_DOWN }, + { EL_TUBE_HORIZ_DOWN, MV_LEFT | MV_RIGHT | MV_UP }, + { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN }, + { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP }, + { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN }, + { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP }, + { -1, MV_NO_MOVING } + }; + + while (tube_enter_directions[i][0] != element) + { + i++; + if (tube_enter_directions[i][0] == -1) /* should not happen */ + break; + } + if (!(tube_enter_directions[i][1] & move_direction)) + return MF_NO_ACTION; /* tube has no opening in this direction */ + } break; case EL_AUSGANG_ZU: @@ -4886,6 +5727,7 @@ int DigField(struct PlayerInfo *player, case EL_SOKOBAN_OBJEKT: case EL_SONDE: case EL_SP_DISK_YELLOW: + case EL_BALLOON: if (mode == DF_SNAP) return MF_NO_ACTION; @@ -4911,7 +5753,7 @@ int DigField(struct PlayerInfo *player, if (player->push_delay == 0) player->push_delay = FrameCounter; if (!FrameReached(&player->push_delay, player->push_delay_value) && - !tape.playing) + !tape.playing && element != EL_BALLOON) return MF_NO_ACTION; if (IS_SB_ELEMENT(element)) @@ -4940,11 +5782,14 @@ int DigField(struct PlayerInfo *player, Feld[x+dx][y+dy] = element; } - player->push_delay_value = 2; + player->push_delay_value = (element == EL_BALLOON ? 0 : 2); DrawLevelField(x, y); DrawLevelField(x+dx, y+dy); - PlaySoundLevel(x+dx, y+dy, SND_PUSCH); + if (element == EL_BALLOON) + PlaySoundLevel(x+dx, y+dy, SND_SCHLURF); + else + PlaySoundLevel(x+dx, y+dy, SND_PUSCH); if (IS_SB_ELEMENT(element) && local_player->sokobanfields_still_needed == 0 && @@ -4956,7 +5801,6 @@ int DigField(struct PlayerInfo *player, break; - case EL_MAULWURF: case EL_PINGUIN: case EL_SCHWEIN: case EL_DRACHE: