X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=38cf088dd0f87ef0305e781d012d7b6a6de46ae8;hb=0f40b41943c6442963b3677ce3d18d1f04c61605;hp=bdd3059e469fdbed3da4f07043b79855cc798cca;hpb=6a066eac92bfa607cec653f6a97917891aee0d95;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index bdd3059e..38cf088d 100644 --- a/src/game.c +++ b/src/game.c @@ -14,9 +14,9 @@ #include "libgame/libgame.h" #include "game.h" +#include "init.h" #include "tools.h" #include "screens.h" -#include "init.h" #include "files.h" #include "tape.h" #include "network.h" @@ -154,38 +154,144 @@ struct ChangingElementInfo static struct ChangingElementInfo changing_element_list[] = { - { EL_NUT_BREAKING, EL_EMERALD, 6, NULL, NULL, NULL }, - { EL_PEARL_BREAKING, EL_EMPTY, 8, NULL, NULL, NULL }, - { EL_EXIT_OPENING, EL_EXIT_OPEN, 29, NULL, NULL, NULL }, - - { EL_SWITCHGATE_OPENING, EL_SWITCHGATE_OPEN, 29, NULL, NULL, NULL }, - { EL_SWITCHGATE_CLOSING, EL_SWITCHGATE_CLOSED, 29, NULL, NULL, NULL }, - - { EL_TIMEGATE_OPENING, EL_TIMEGATE_OPEN, 29, NULL, NULL, NULL }, - { EL_TIMEGATE_CLOSING, EL_TIMEGATE_CLOSED, 29, NULL, NULL, NULL }, - - { EL_ACID_SPLASH_LEFT, EL_EMPTY, 8, NULL, NULL, NULL }, - { EL_ACID_SPLASH_RIGHT, EL_EMPTY, 8, NULL, NULL, NULL }, - - { EL_SP_BUGGY_BASE, EL_SP_BUGGY_BASE_ACTIVATING, 0, - InitBuggyBase, NULL, NULL }, - { EL_SP_BUGGY_BASE_ACTIVATING,EL_SP_BUGGY_BASE_ACTIVE, 0, - InitBuggyBase, NULL, NULL }, - { EL_SP_BUGGY_BASE_ACTIVE, EL_SP_BUGGY_BASE, 0, - InitBuggyBase, WarnBuggyBase, NULL }, - - { EL_TRAP, EL_TRAP_ACTIVE, 0, - InitTrap, NULL, ActivateTrap }, - { EL_TRAP_ACTIVE, EL_TRAP, 31, - NULL, ChangeActiveTrap, NULL }, - - { EL_ROBOT_WHEEL_ACTIVE, EL_ROBOT_WHEEL, 0, - InitRobotWheel, RunRobotWheel, StopRobotWheel }, + { + EL_NUT_BREAKING, + EL_EMERALD, + 6, + NULL, + NULL, + NULL + }, + { + EL_PEARL_BREAKING, + EL_EMPTY, + 8, + NULL, + NULL, + NULL + }, + { + EL_EXIT_OPENING, + EL_EXIT_OPEN, + 29, + NULL, + NULL, + NULL + }, + { + EL_SWITCHGATE_OPENING, + EL_SWITCHGATE_OPEN, + 29, + NULL, + NULL, + NULL + }, + { + EL_SWITCHGATE_CLOSING, + EL_SWITCHGATE_CLOSED, + 29, + NULL, + NULL, + NULL + }, + { + EL_TIMEGATE_OPENING, + EL_TIMEGATE_OPEN, + 29, + NULL, + NULL, + NULL + }, + { + EL_TIMEGATE_CLOSING, + EL_TIMEGATE_CLOSED, + 29, + NULL, + NULL, + NULL + }, - { EL_TIMEGATE_SWITCH_ACTIVE, EL_TIMEGATE_SWITCH, 0, - InitTimegateWheel, RunTimegateWheel, NULL }, + { + EL_ACID_SPLASH_LEFT, + EL_EMPTY, + 8, + NULL, + NULL, + NULL + }, + { + EL_ACID_SPLASH_RIGHT, + EL_EMPTY, + 8, + NULL, + NULL, + NULL + }, + { + EL_SP_BUGGY_BASE, + EL_SP_BUGGY_BASE_ACTIVATING, + 0, + InitBuggyBase, + NULL, + NULL + }, + { + EL_SP_BUGGY_BASE_ACTIVATING, + EL_SP_BUGGY_BASE_ACTIVE, + 0, + InitBuggyBase, + NULL, + NULL + }, + { + EL_SP_BUGGY_BASE_ACTIVE, + EL_SP_BUGGY_BASE, + 0, + InitBuggyBase, + WarnBuggyBase, + NULL + }, + { + EL_TRAP, + EL_TRAP_ACTIVE, + 0, + InitTrap, + NULL, + ActivateTrap + }, + { + EL_TRAP_ACTIVE, + EL_TRAP, + 31, + NULL, + ChangeActiveTrap, + NULL + }, + { + EL_ROBOT_WHEEL_ACTIVE, + EL_ROBOT_WHEEL, + 0, + InitRobotWheel, + RunRobotWheel, + StopRobotWheel + }, + { + EL_TIMEGATE_SWITCH_ACTIVE, + EL_TIMEGATE_SWITCH, + 0, + InitTimegateWheel, + RunTimegateWheel, + NULL + }, - { EL_UNDEFINED, EL_UNDEFINED, -1, NULL } + { + EL_UNDEFINED, + EL_UNDEFINED, + -1, + NULL, + NULL, + NULL + } }; static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS]; @@ -500,9 +606,13 @@ static void InitGameEngine() { int i; + /* set game engine from tape file when re-playing, else from level file */ game.engine_version = (tape.playing ? tape.engine_version : level.game_version); + /* dynamically adjust element properties according to game engine version */ + InitElementPropertiesEngine(game.engine_version); + #if 0 printf("level %d: level version == %06d\n", level_nr, level.game_version); printf(" tape version == %06d [%s] [file: %06d]\n", @@ -520,6 +630,7 @@ static void InitGameEngine() game.initial_move_delay_value = (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED); +#if 0 /* dynamically adjust element properties according to game engine version */ { static int ep_em_slippery_wall[] = @@ -533,33 +644,17 @@ static void InitGameEngine() }; static int ep_em_slippery_wall_num = SIZEOF_ARRAY_INT(ep_em_slippery_wall); + /* special EM style gems behaviour */ for (i=0; i VERSION_IDENT(2,0,1))); -#else - if (level.em_slippery_gems && game.engine_version > VERSION_IDENT(2,0,1)) - Properties2[EL_EXPANDABLE_WALL_GROWING] |= EP_BIT_EM_SLIPPERY_WALL; - else - Properties2[EL_EXPANDABLE_WALL_GROWING] &=~EP_BIT_EM_SLIPPERY_WALL; -#endif } +#endif /* initialize changing elements information */ for (i=0; isuccessor; + changing_element[element].change_delay = 0; + + if (HAS_CHANGE_EVENT(element, CE_DELAY_FIXED)) + changing_element[element].change_delay += + change->delay_fixed * change->delay_frames; + + if (HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM)); + /* random frame delay added at runtime for each element individually */ + } } @@ -716,7 +836,7 @@ void InitGame() AllPlayersGone = FALSE; - game.yam_content_nr = 0; + game.yamyam_content_nr = 0; game.magic_wall_active = FALSE; game.magic_wall_time_left = 0; game.light_time_left = 0; @@ -740,7 +860,7 @@ void InitGame() { Feld[x][y] = Ur[x][y]; MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0; - Store[x][y] = Store2[x][y] = StorePlayer[x][y] = 0; + Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0; AmoebaNr[x][y] = 0; JustStopped[x][y] = 0; Stop[x][y] = FALSE; @@ -958,7 +1078,7 @@ void InitGame() if (setup.sound_music) PlayMusic(level_nr); - KeyboardAutoRepeatOff(); + KeyboardAutoRepeatOffUnlessAutoplay(); if (options.debug) { @@ -1123,7 +1243,7 @@ void GameWon() if (!tape.playing && !setup.sound_loops) PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE); if (TimeLeft > 0 && !(TimeLeft % 10)) - RaiseScore(level.score[SC_ZEITBONUS]); + RaiseScore(level.score[SC_TIME_BONUS]); if (TimeLeft > 100 && !(TimeLeft % 10)) TimeLeft -= 10; else @@ -1149,7 +1269,7 @@ void GameWon() if (!tape.playing && !setup.sound_loops) PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE); if (TimePlayed < 999 && !(TimePlayed % 10)) - RaiseScore(level.score[SC_ZEITBONUS]); + RaiseScore(level.score[SC_TIME_BONUS]); if (TimePlayed < 900 && !(TimePlayed % 10)) TimePlayed += 10; else @@ -1193,7 +1313,7 @@ void GameWon() if ((hi_pos = NewHiScore()) >= 0) { - game_status = HALLOFFAME; + game_status = GAME_MODE_SCORES; DrawHallOfFame(hi_pos); if (raise_level) { @@ -1203,7 +1323,7 @@ void GameWon() } else { - game_status = MAINMENU; + game_status = GAME_MODE_MAIN; if (raise_level) { level_nr++; @@ -1395,7 +1515,7 @@ static int MovingOrBlocked2ElementIfNotLeaving(int x, int y) static void RemoveField(int x, int y) { Feld[x][y] = EL_EMPTY; - GfxElement[x][y] = EL_EMPTY; + GfxElement[x][y] = EL_UNDEFINED; MovPos[x][y] = 0; MovDir[x][y] = 0; MovDelay[x][y] = 0; @@ -1450,13 +1570,18 @@ void DrawDynamite(int x, int y) if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y)) return; - if (Store[x][y]) + if (IS_WALKABLE_INSIDE(Back[x][y])) + return; + + if (Back[x][y]) + DrawGraphic(sx, sy, el2img(Back[x][y]), 0); + else if (Store[x][y]) DrawGraphic(sx, sy, el2img(Store[x][y]), 0); frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]); #if 1 - if (Store[x][y]) + if (Back[x][y] || Store[x][y]) DrawGraphicThruMask(sx, sy, graphic, frame); else DrawGraphic(sx, sy, graphic, frame); @@ -1517,6 +1642,10 @@ void Explode(int ex, int ey, int phase, int mode) { int center_element = Feld[ex][ey]; + /* remove things displayed in background while burning dynamite */ + if (!IS_INDESTRUCTIBLE(Back[ex][ey])) + Back[ex][ey] = 0; + if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey)) { /* put moving element to center field (and let it explode there) */ @@ -1525,7 +1654,7 @@ void Explode(int ex, int ey, int phase, int mode) Feld[ex][ey] = center_element; } - for (y=ey-1; y<=ey+1; y++) for(x=ex-1; x<=ex+1; x++) + for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++) { int element; @@ -1542,8 +1671,16 @@ void Explode(int ex, int ey, int phase, int mode) RemoveMovingField(x, y); } - if (IS_INDESTRUCTIBLE(element) || element == EL_FLAMES) +#if 1 + if (IS_EXPLOSION_PROOF(element)) continue; +#else + if ((IS_INDESTRUCTIBLE(element) && + (game.engine_version < VERSION_IDENT(2,2,0) || + (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) || + element == EL_FLAMES) + continue; +#endif if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y))) { @@ -1557,6 +1694,11 @@ void Explode(int ex, int ey, int phase, int mode) continue; } + /* save walkable background elements while explosion on same tile */ + if (IS_INDESTRUCTIBLE(element)) + Back[x][y] = element; + + /* ignite explodable elements reached by other explosion */ if (element == EL_EXPLOSION) element = Store2[x][y]; @@ -1592,10 +1734,14 @@ void Explode(int ex, int ey, int phase, int mode) Store[x][y] = EL_BD_DIAMOND; else if (center_element == EL_SP_ELECTRON) Store[x][y] = EL_SP_INFOTRON; - else if (center_element == EL_YAMYAM) - Store[x][y] = level.yam_content[game.yam_content_nr][x-ex+1][y-ey+1]; else if (center_element == EL_AMOEBA_TO_DIAMOND) Store[x][y] = level.amoeba_content; + else if (center_element == EL_YAMYAM) + Store[x][y] = + level.yamyam_content[game.yamyam_content_nr][x - ex + 1][y - ey + 1]; + else if (IS_CUSTOM_ELEMENT(center_element)) + Store[x][y] = + CUSTOM_ELEMENT_INFO(center_element).content[x - ex + 1][y - ey + 1]; else if (element == EL_WALL_EMERALD) Store[x][y] = EL_EMERALD; else if (element == EL_WALL_DIAMOND) @@ -1612,7 +1758,7 @@ void Explode(int ex, int ey, int phase, int mode) Store[x][y] = EL_PEARL; else if (element == EL_WALL_CRYSTAL) Store[x][y] = EL_CRYSTAL; - else if (!IS_PFORTE(Store[x][y])) + else Store[x][y] = EL_EMPTY; if (x != ex || y != ey || @@ -1629,7 +1775,7 @@ void Explode(int ex, int ey, int phase, int mode) } Feld[x][y] = EL_EXPLOSION; - GfxElement[x][y] = EL_EMPTY; + GfxElement[x][y] = EL_UNDEFINED; MovDir[x][y] = MovPos[x][y] = 0; AmoebaNr[x][y] = 0; ExplodePhase[x][y] = 1; @@ -1637,7 +1783,8 @@ void Explode(int ex, int ey, int phase, int mode) } if (center_element == EL_YAMYAM) - game.yam_content_nr = (game.yam_content_nr + 1) % level.num_yam_contents; + game.yamyam_content_nr = + (game.yamyam_content_nr + 1) % level.num_yamyam_contents; return; } @@ -1667,7 +1814,7 @@ void Explode(int ex, int ey, int phase, int mode) if (IS_PLAYER(x, y)) KillHeroUnlessProtected(x, y); - else if (IS_EXPLOSIVE(element)) + else if (CAN_EXPLODE(element)) { Feld[x][y] = Store2[x][y]; Store2[x][y] = 0; @@ -1683,9 +1830,14 @@ void Explode(int ex, int ey, int phase, int mode) element = Feld[x][y] = Store[x][y]; Store[x][y] = Store2[x][y] = 0; + + if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y])) + element = Feld[x][y] = Back[x][y]; + Back[x][y] = 0; + MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0; InitField(x, y, FALSE); - if (CAN_MOVE(element) || COULD_MOVE(element)) + if (CAN_MOVE(element)) InitMovDir(x, y); DrawLevelField(x, y); @@ -1703,12 +1855,17 @@ void Explode(int ex, int ey, int phase, int mode) if (phase == delay) DrawLevelFieldCrumbledSand(x, y); - if (IS_PFORTE(Store[x][y])) + if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY) { - DrawLevelElement(x, y, Store[x][y]); + DrawLevelElement(x, y, Back[x][y]); DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame); } - else + else if (IS_WALKABLE_UNDER(Back[x][y])) + { + DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame); + DrawLevelElementThruMask(x, y, Back[x][y]); + } + else if (!IS_WALKABLE_INSIDE(Back[x][y])) DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame); } } @@ -2208,7 +2365,7 @@ void Impact(int x, int y) if (!lastline && object_hit) /* check which object was hit */ { - if (CAN_CHANGE(element) && + if (CAN_PASS_MAGIC_WALL(element) && (smashed == EL_MAGIC_WALL || smashed == EL_BD_MAGIC_WALL)) { @@ -2446,11 +2603,11 @@ void TurnRound(int x, int y) if (IN_LEV_FIELD(left_x, left_y) && (IS_FREE_OR_PLAYER(left_x, left_y) || - IS_MAMPF2(Feld[left_x][left_y]))) + IS_FOOD_DARK_YAMYAM(Feld[left_x][left_y]))) can_turn_left = TRUE; if (IN_LEV_FIELD(right_x, right_y) && (IS_FREE_OR_PLAYER(right_x, right_y) || - IS_MAMPF2(Feld[right_x][right_y]))) + IS_FOOD_DARK_YAMYAM(Feld[right_x][right_y]))) can_turn_right = TRUE; if (can_turn_left && can_turn_right) @@ -2497,13 +2654,13 @@ void TurnRound(int x, int y) int rnd = RND(rnd_value); if (IN_LEV_FIELD(left_x, left_y) && - (IS_FREE(left_x, left_y) || IS_GEM(Feld[left_x][left_y]))) + (IS_FREE(left_x, left_y) || IS_FOOD_PIG(Feld[left_x][left_y]))) can_turn_left = TRUE; if (IN_LEV_FIELD(right_x, right_y) && - (IS_FREE(right_x, right_y) || IS_GEM(Feld[right_x][right_y]))) + (IS_FREE(right_x, right_y) || IS_FOOD_PIG(Feld[right_x][right_y]))) can_turn_right = TRUE; if (IN_LEV_FIELD(move_x, move_y) && - (IS_FREE(move_x, move_y) || IS_GEM(Feld[move_x][move_y]))) + (IS_FREE(move_x, move_y) || IS_FOOD_PIG(Feld[move_x][move_y]))) can_move_on = TRUE; if (can_turn_left && @@ -2555,7 +2712,7 @@ void TurnRound(int x, int y) MovDir[x][y] = back_dir; if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) && - !IS_GEM(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y])) + !IS_FOOD_PIG(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y])) MovDir[x][y] = old_move_dir; MovDelay[x][y] = 0; @@ -2737,7 +2894,7 @@ void TurnRound(int x, int y) Feld[newx][newy] == EL_ACID || (element == EL_PENGUIN && (Feld[newx][newy] == EL_EXIT_OPEN || - IS_MAMPF3(Feld[newx][newy]))))) + IS_FOOD_PENGUIN(Feld[newx][newy]))))) return; MovDir[x][y] = @@ -2749,7 +2906,7 @@ void TurnRound(int x, int y) Feld[newx][newy] == EL_ACID || (element == EL_PENGUIN && (Feld[newx][newy] == EL_EXIT_OPEN || - IS_MAMPF3(Feld[newx][newy]))))) + IS_FOOD_PENGUIN(Feld[newx][newy]))))) return; MovDir[x][y] = old_move_dir; @@ -2782,7 +2939,7 @@ static boolean JustBeingPushed(int x, int y) void StartMoving(int x, int y) { - static boolean use_spring_bug = TRUE; + boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0)); boolean started_moving = FALSE; /* some elements can fall _and_ move */ int element = Feld[x][y]; @@ -2905,7 +3062,7 @@ void StartMoving(int x, int y) Store[x][y] = 0; } } - else if (CAN_CHANGE(element) && + else if (CAN_PASS_MAGIC_WALL(element) && (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE || Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)) { @@ -3039,8 +3196,8 @@ void StartMoving(int x, int y) if (!MovDelay[x][y]) /* start new movement phase */ { - /* all objects that can change their move direction after each step */ - /* (MAMPFER, MAMPFER2 and PACMAN go straight until they hit a wall */ + /* all objects that can change their move direction after each step + (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */ if (element != EL_YAMYAM && element != EL_DARK_YAMYAM && @@ -3088,15 +3245,14 @@ void StartMoving(int x, int y) int sx = SCREENX(xx), sy = SCREENY(yy); int flame_graphic = graphic + (i - 1); - if (!IN_LEV_FIELD(xx, yy) || - IS_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLOSION) + if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy])) break; if (MovDelay[x][y]) { int flamed = MovingOrBlocked2Element(xx, yy); - if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed)) + if (IS_ENEMY(flamed) || CAN_EXPLODE(flamed)) Bang(xx, yy); else RemoveMovingField(xx, yy); @@ -3167,7 +3323,7 @@ void StartMoving(int x, int y) return; } - else if (IS_MAMPF3(Feld[newx][newy])) + else if (IS_FOOD_PENGUIN(Feld[newx][newy])) { if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING) DrawLevelField(newx, newy); @@ -3185,7 +3341,7 @@ void StartMoving(int x, int y) } else if (element == EL_PIG && IN_LEV_FIELD(newx, newy)) { - if (IS_GEM(Feld[newx][newy])) + if (IS_FOOD_PIG(Feld[newx][newy])) { if (IS_MOVING(newx, newy)) RemoveMovingField(newx, newy); @@ -3262,7 +3418,7 @@ void StartMoving(int x, int y) PlaySoundLevel(x, y, SND_YAMYAM_DIGGING); } else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) && - IS_MAMPF2(Feld[newx][newy])) + IS_FOOD_DARK_YAMYAM(Feld[newx][newy])) { if (AmoebaNr[newx][newy]) { @@ -4154,22 +4310,22 @@ void MauerWaechst(int x, int y) { if (MovDir[x][y] == MV_LEFT) { - if (IN_LEV_FIELD(x - 1, y) && IS_MAUER(Feld[x - 1][y])) + if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y])) DrawLevelField(x - 1, y); } else if (MovDir[x][y] == MV_RIGHT) { - if (IN_LEV_FIELD(x + 1, y) && IS_MAUER(Feld[x + 1][y])) + if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y])) DrawLevelField(x + 1, y); } else if (MovDir[x][y] == MV_UP) { - if (IN_LEV_FIELD(x, y - 1) && IS_MAUER(Feld[x][y - 1])) + if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1])) DrawLevelField(x, y - 1); } else { - if (IN_LEV_FIELD(x, y + 1) && IS_MAUER(Feld[x][y + 1])) + if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1])) DrawLevelField(x, y + 1); } @@ -4268,13 +4424,13 @@ void MauerAbleger(int ax, int ay) if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei)) DrawLevelField(ax, ay); - if (!IN_LEV_FIELD(ax, ay-1) || IS_MAUER(Feld[ax][ay-1])) + if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1])) oben_massiv = TRUE; - if (!IN_LEV_FIELD(ax, ay+1) || IS_MAUER(Feld[ax][ay+1])) + if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1])) unten_massiv = TRUE; - if (!IN_LEV_FIELD(ax-1, ay) || IS_MAUER(Feld[ax-1][ay])) + if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay])) links_massiv = TRUE; - if (!IN_LEV_FIELD(ax+1, ay) || IS_MAUER(Feld[ax+1][ay])) + if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay])) rechts_massiv = TRUE; if (((oben_massiv && unten_massiv) || @@ -4402,10 +4558,23 @@ static void ChangeElement(int x, int y) { int element = Feld[x][y]; + if (IS_MOVING(x, y)) /* never change a running system :-) */ + return; + if (MovDelay[x][y] == 0) /* initialize element change */ { MovDelay[x][y] = changing_element[element].change_delay + 1; + if (IS_CUSTOM_ELEMENT(element) && + HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM)) + { + int i = element - EL_CUSTOM_START; + int max_random_delay = level.custom_element[i].change.delay_random; + int delay_frames = level.custom_element[i].change.delay_frames; + + MovDelay[x][y] += RND(max_random_delay * delay_frames); + } + ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); @@ -4430,6 +4599,11 @@ static void ChangeElement(int x, int y) ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); +#if 1 + InitField(x, y, FALSE); + if (CAN_MOVE(element)) + InitMovDir(x, y); +#endif DrawLevelField(x, y); if (changing_element[element].post_change_function) @@ -4516,7 +4690,7 @@ void GameActions() byte *recorded_player_action; byte summarized_player_action = 0; - if (game_status != PLAYING) + if (game_status != GAME_MODE_PLAYING) return; action_delay_value = @@ -4542,7 +4716,7 @@ void GameActions() HandleNetworking(); #endif - if (game_status != PLAYING) + if (game_status != GAME_MODE_PLAYING) return; if (!network_player_action_received) @@ -4633,6 +4807,15 @@ void GameActions() element = Feld[x][y]; graphic = el2img(element); +#if 0 + if (element == -1) + { + printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter); + + element = graphic = 0; + } +#endif + if (graphic_info[graphic].anim_global_sync) GfxFrame[x][y] = FrameCounter; @@ -4709,13 +4892,21 @@ void GameActions() MauerAbleger(x, y); else if (element == EL_FLAMES) CheckForDragon(x, y); +#if 0 else if (IS_AUTO_CHANGING(element)) ChangeElement(x, y); +#endif else if (element == EL_EXPLOSION) ; /* drawing of correct explosion animation is handled separately */ - else if (IS_ANIMATED(graphic)) + else if (IS_ANIMATED(graphic) && !IS_AUTO_CHANGING(element)) DrawLevelGraphicAnimationIfNeeded(x, y, graphic); +#if 1 + /* this may take place after moving, therefore element may have changed */ + if (IS_AUTO_CHANGING(Feld[x][y])) + ChangeElement(x, y); +#endif + if (IS_BELT_ACTIVE(element)) PlaySoundLevelAction(x, y, ACTION_ACTIVE); @@ -5047,11 +5238,18 @@ static void CheckGravityMovement(struct PlayerInfo *player) if (field_under_player_is_free && !player_is_moving_to_valid_field && - !IS_TUBE(Feld[jx][jy])) + !IS_WALKABLE_INSIDE(Feld[jx][jy])) player->programmed_action = MV_DOWN; } } +/* + MoveFigureOneStep() + ----------------------------------------------------------------------------- + dx, dy: direction (non-diagonal) to try to move the player to + real_dx, real_dy: direction as read from input device (can be diagonal) +*/ + boolean MoveFigureOneStep(struct PlayerInfo *player, int dx, int dy, int real_dx, int real_dy) { @@ -5326,7 +5524,7 @@ void ScrollFigure(struct PlayerInfo *player, int mode) if (player->MovPos == 0) { - if (IS_QUICK_GATE(Feld[last_jx][last_jy])) + if (IS_PASSABLE(Feld[last_jx][last_jy])) { /* continue with normal speed after quickly moving through gate */ HALVE_PLAYER_SPEED(player); @@ -5614,8 +5812,8 @@ void KillHero(struct PlayerInfo *player) if (!player->active) return; - if (IS_PFORTE(Feld[jx][jy])) - Feld[jx][jy] = EL_EMPTY; + /* remove accessible field at the player's position */ + Feld[jx][jy] = EL_EMPTY; /* deactivate shield (else Bang()/Explode() would not work right) */ player->shield_normal_time_left = 0; @@ -5667,6 +5865,39 @@ void RemoveHero(struct PlayerInfo *player) ExitY = ZY = jy; } +/* + checkDiagonalPushing() + ----------------------------------------------------------------------------- + check if diagonal input device direction results in pushing of object + (by checking if the alternative direction is walkable, diggable, ...) +*/ + +static boolean checkDiagonalPushing(struct PlayerInfo *player, + int x, int y, int real_dx, int real_dy) +{ + int jx, jy, dx, dy, xx, yy; + + if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */ + return TRUE; + + /* diagonal direction: check alternative direction */ + jx = player->jx; + jy = player->jy; + dx = x - jx; + dy = y - jy; + xx = jx + (dx == 0 ? real_dx : 0); + yy = jy + (dy == 0 ? real_dy : 0); + + return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy])); +} + +/* + DigField() + ----------------------------------------------------------------------------- + x, y: field next to player (non-diagonal) to try to dig to + real_dx, real_dy: direction as read from input device (can be diagonal) +*/ + int DigField(struct PlayerInfo *player, int x, int y, int real_dx, int real_dy, int mode) { @@ -5691,15 +5922,22 @@ int DigField(struct PlayerInfo *player, { player->Switching = FALSE; player->push_delay = 0; + return MF_NO_ACTION; } if (IS_MOVING(x, y) || IS_PLAYER(x, y)) return MF_NO_ACTION; - if (IS_TUBE(Feld[jx][jy])) +#if 0 + if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy])) +#else + if (IS_TUBE(Feld[jx][jy]) || + (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0))) +#endif { int i = 0; + int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]); int tube_leave_directions[][2] = { { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }, @@ -5716,7 +5954,7 @@ int DigField(struct PlayerInfo *player, { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN } }; - while (tube_leave_directions[i][0] != Feld[jx][jy]) + while (tube_leave_directions[i][0] != tube_element) { i++; if (tube_leave_directions[i][0] == -1) /* should not happen */ @@ -5729,6 +5967,12 @@ int DigField(struct PlayerInfo *player, element = Feld[x][y]; +#if 1 + if (mode == DF_SNAP && !IS_SNAPPABLE(element) && + game.engine_version >= VERSION_IDENT(2,2,0)) + return MF_NO_ACTION; +#endif + switch (element) { case EL_EMPTY: @@ -6004,14 +6248,17 @@ int DigField(struct PlayerInfo *player, player->Pushing = TRUE; +#if 0 + if (element == EL_ROCK) + printf("::: wanna push [%d] [%d]\n", + FrameCounter, player->push_delay_value); +#endif + if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy)) return MF_NO_ACTION; - if (real_dy) - { - if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy])) - return MF_NO_ACTION; - } + if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) + return MF_NO_ACTION; if (player->push_delay == 0) player->push_delay = FrameCounter; @@ -6115,16 +6362,16 @@ int DigField(struct PlayerInfo *player, break; case EL_SP_PORT_LEFT: - case EL_SP_GRAVITY_PORT_LEFT: case EL_SP_PORT_RIGHT: - case EL_SP_GRAVITY_PORT_RIGHT: case EL_SP_PORT_UP: - case EL_SP_GRAVITY_PORT_UP: case EL_SP_PORT_DOWN: - case EL_SP_GRAVITY_PORT_DOWN: case EL_SP_PORT_HORIZONTAL: case EL_SP_PORT_VERTICAL: case EL_SP_PORT_ANY: + case EL_SP_GRAVITY_PORT_LEFT: + case EL_SP_GRAVITY_PORT_RIGHT: + case EL_SP_GRAVITY_PORT_UP: + case EL_SP_GRAVITY_PORT_DOWN: if ((dx == -1 && element != EL_SP_PORT_LEFT && element != EL_SP_GRAVITY_PORT_LEFT && @@ -6253,16 +6500,8 @@ int DigField(struct PlayerInfo *player, || !IS_SB_ELEMENT(element)))) return MF_NO_ACTION; - if (dx && real_dy) - { - if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy])) - return MF_NO_ACTION; - } - else if (dy && real_dx) - { - if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy])) - return MF_NO_ACTION; - } + if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) + return MF_NO_ACTION; if (player->push_delay == 0) player->push_delay = FrameCounter; @@ -6349,7 +6588,41 @@ int DigField(struct PlayerInfo *player, break; default: - if (IS_PUSHABLE(element)) + + if (IS_WALKABLE(element)) + { + break; + } + else if (IS_DIGGABLE(element)) + { + RemoveField(x, y); +#if 1 + if (mode != DF_SNAP) + { + GfxElement[x][y] = + (CAN_BE_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element)); + player->is_digging = TRUE; + } +#endif + PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING); + + break; + } + else if (IS_COLLECTIBLE(element)) + { + RemoveField(x, y); +#if 1 + if (mode != DF_SNAP) + { + GfxElement[x][y] = element; + player->is_collecting = TRUE; + } +#endif + PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); + + break; + } + else if (IS_PUSHABLE(element)) { if (mode == DF_SNAP) return MF_NO_ACTION; @@ -6362,16 +6635,8 @@ int DigField(struct PlayerInfo *player, if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy)) return MF_NO_ACTION; - if (dx && real_dy) - { - if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy])) - return MF_NO_ACTION; - } - else if (dy && real_dx) - { - if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy])) - return MF_NO_ACTION; - } + if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) + return MF_NO_ACTION; if (player->push_delay == 0) player->push_delay = FrameCounter; @@ -6407,6 +6672,9 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) int jx = player->jx, jy = player->jy; int x = jx + dx, y = jy + dy; + if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0)) + return FALSE; + if (!player->active || !IN_LEV_FIELD(x, y)) return FALSE; @@ -6437,7 +6705,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) dy < 0 ? MV_UP : dy > 0 ? MV_DOWN : MV_NO_MOVING); - if (!DigField(player, x, y, 0, 0, DF_SNAP)) + if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION) return FALSE; player->snapped = TRUE; @@ -6464,8 +6732,19 @@ boolean PlaceBomb(struct PlayerInfo *player) IS_ACTIVE_BOMB(element) || element == EL_EXPLOSION) return FALSE; +#if 0 if (element != EL_EMPTY) + return FALSE; +#endif + + if (element != EL_EMPTY) + { +#if 0 Store[jx][jy] = element; +#else + Back[jx][jy] = element; +#endif + } MovDelay[jx][jy] = 96; @@ -6628,22 +6907,31 @@ void RaiseScoreElement(int element) case EL_EMERALD_YELLOW: case EL_EMERALD_RED: case EL_EMERALD_PURPLE: - RaiseScore(level.score[SC_EDELSTEIN]); + case EL_SP_INFOTRON: + RaiseScore(level.score[SC_EMERALD]); break; case EL_DIAMOND: - RaiseScore(level.score[SC_DIAMANT]); + RaiseScore(level.score[SC_DIAMOND]); + break; + case EL_CRYSTAL: + RaiseScore(level.score[SC_CRYSTAL]); + break; + case EL_PEARL: + RaiseScore(level.score[SC_PEARL]); break; case EL_BUG: case EL_BD_BUTTERFLY: - RaiseScore(level.score[SC_KAEFER]); + case EL_SP_ELECTRON: + RaiseScore(level.score[SC_BUG]); break; case EL_SPACESHIP: case EL_BD_FIREFLY: - RaiseScore(level.score[SC_FLIEGER]); + case EL_SP_SNIKSNAK: + RaiseScore(level.score[SC_SPACESHIP]); break; case EL_YAMYAM: case EL_DARK_YAMYAM: - RaiseScore(level.score[SC_MAMPFER]); + RaiseScore(level.score[SC_YAMYAM]); break; case EL_ROBOT: RaiseScore(level.score[SC_ROBOT]); @@ -6652,16 +6940,26 @@ void RaiseScoreElement(int element) RaiseScore(level.score[SC_PACMAN]); break; case EL_NUT: - RaiseScore(level.score[SC_KOKOSNUSS]); + RaiseScore(level.score[SC_NUT]); break; case EL_DYNAMITE: - RaiseScore(level.score[SC_DYNAMIT]); + case EL_DYNABOMB_INCREASE_NUMBER: + case EL_DYNABOMB_INCREASE_SIZE: + case EL_DYNABOMB_INCREASE_POWER: + RaiseScore(level.score[SC_DYNAMITE]); + break; + case EL_SHIELD_NORMAL: + case EL_SHIELD_DEADLY: + RaiseScore(level.score[SC_SHIELD]); + break; + case EL_EXTRA_TIME: + RaiseScore(level.score[SC_TIME_BONUS]); break; case EL_KEY_1: case EL_KEY_2: case EL_KEY_3: case EL_KEY_4: - RaiseScore(level.score[SC_SCHLUESSEL]); + RaiseScore(level.score[SC_KEY]); break; default: break; @@ -6682,7 +6980,7 @@ void RequestQuitGame(boolean ask_if_really_quit) else #endif { - game_status = MAINMENU; + game_status = GAME_MODE_MAIN; DrawMainMenu(); } } @@ -6843,7 +7141,7 @@ static void HandleGameButtons(struct GadgetInfo *gi) { int id = gi->custom_id; - if (game_status != PLAYING) + if (game_status != GAME_MODE_PLAYING) return; switch (id)