X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=d3ac230748cb7b01f3bb4d65969f7789fbd378fd;hb=d42e0a36609c7990f8b9f6fcd80f37f65bcf5149;hp=75508923566c856c7708a3ab612ab9006189a287;hpb=ba68c676a24c697907748c2653d77752363170fe;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 75508923..d3ac2307 100644 --- a/src/game.c +++ b/src/game.c @@ -59,7 +59,8 @@ #define YY_KEYS 123 #define XX_SCORE 15 #define YY_SCORE 159 -#define XX_TIME 29 +#define XX_TIME1 29 +#define XX_TIME2 30 #define YY_TIME 194 /* special positions in the game control window (relative to main window) */ @@ -73,7 +74,8 @@ #define DY_KEYS (DY + YY_KEYS) #define DX_SCORE (DX + XX_SCORE) #define DY_SCORE (DY + YY_SCORE) -#define DX_TIME (DX + XX_TIME) +#define DX_TIME1 (DX + XX_TIME1) +#define DX_TIME2 (DX + XX_TIME2) #define DY_TIME (DY + YY_TIME) /* values for initial player move delay (initial delay counter value) */ @@ -101,14 +103,21 @@ #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \ (element_info[e].move_delay_random)) +#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_ENEMY_PROTECTED(x, y)))) - -#define ELEMENT_CAN_ENTER_FIELD_GENERIC_X(e, x, y, condition) \ +#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) && \ @@ -116,6 +125,7 @@ (DONT_COLLIDE_WITH(e) && \ IS_PLAYER(x, y) && \ !PLAYER_ENEMY_PROTECTED(x, y)))) +#endif #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ @@ -130,29 +140,58 @@ #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) || \ + (CAN_MOVE_INTO_ACID(EL_YAMYAM) && \ + Feld[x][y] == EL_ACID) || \ Feld[x][y] == EL_DIAMOND)) #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \ (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \ + (CAN_MOVE_INTO_ACID(EL_DARK_YAMYAM) &&\ + Feld[x][y] == EL_ACID) || \ IS_FOOD_DARK_YAMYAM(Feld[x][y]))) #define PACMAN_CAN_ENTER_FIELD(x, y) \ (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \ + (CAN_MOVE_INTO_ACID(EL_PACMAN) && \ + Feld[x][y] == EL_ACID) || \ IS_AMOEBOID(Feld[x][y]))) #define PIG_CAN_ENTER_FIELD(x, y) \ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + (CAN_MOVE_INTO_ACID(EL_PIG) && \ + Feld[x][y] == EL_ACID) || \ IS_FOOD_PIG(Feld[x][y]))) #define PENGUIN_CAN_ENTER_FIELD(x, y) \ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + (CAN_MOVE_INTO_ACID(EL_PENGUIN) && \ + Feld[x][y] == EL_ACID) || \ IS_FOOD_PENGUIN(Feld[x][y]) || \ - Feld[x][y] == EL_EXIT_OPEN || \ - Feld[x][y] == EL_ACID)) + Feld[x][y] == EL_EXIT_OPEN)) + +#define DRAGON_CAN_ENTER_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + (CAN_MOVE_INTO_ACID(EL_DRAGON) && \ + Feld[x][y] == EL_ACID))) + +#define MOLE_CAN_ENTER_FIELD(x, y, condition) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + (CAN_MOVE_INTO_ACID(EL_MOLE) && \ + Feld[x][y] == EL_ACID) || \ + (condition))) + +#define SPRING_CAN_ENTER_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + (CAN_MOVE_INTO_ACID(EL_SPRING) && \ + Feld[x][y] == EL_ACID))) #define GROUP_NR(e) ((e) - EL_GROUP_START) #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element) @@ -162,20 +201,19 @@ #define IS_EQUAL_OR_IN_GROUP(e, ge) \ (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge)) +#if 0 #define CE_ENTER_FIELD_COND(e, x, y) \ (!IS_PLAYER(x, y) && \ (Feld[x][y] == EL_ACID || \ IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))) - -#define CE_ENTER_FIELD_COND_X(e, x, y) \ +#else +#define CE_ENTER_FIELD_COND(e, x, y) \ (!IS_PLAYER(x, y) && \ IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))) +#endif #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \ - ELEMENT_CAN_ENTER_FIELD_GENERIC_X(e, x, y, CE_ENTER_FIELD_COND_X(e, x, y)) - -#define MOLE_CAN_ENTER_FIELD(x, y, condition) \ - (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition))) + ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y)) #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y)) #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y)) @@ -203,6 +241,7 @@ 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 CheckGravityMovementWhenNotMoving(struct PlayerInfo *); static void KillHeroUnlessEnemyProtected(int, int); static void KillHeroUnlessExplosionProtected(int, int); @@ -211,10 +250,26 @@ static void TestIfElementTouchesCustomElement(int, int); static void TestIfElementHitsCustomElement(int, int, int); static void ChangeElement(int, int, int); -static boolean CheckTriggeredElementSideChange(int, int, int, int, int); -static boolean CheckTriggeredElementChange(int, int, int, int); -static boolean CheckElementSideChange(int, int, int, int, int, int); -static boolean CheckElementChange(int, int, int, int); + +static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int); +#define CheckTriggeredElementChange(x, y, e, ev) \ + CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1) +#define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s) \ + CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1) +#define CheckTriggeredElementChangeSide(x, y, e, ev, s) \ + CheckTriggeredElementChangeExt(x, y, e, ev, -1, s, -1) +#define CheckTriggeredElementChangePage(x, y, e, ev, p) \ + CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p) + +static boolean CheckElementChangeExt(int, int, int, int, int, int, int); +#define CheckElementChange(x, y, e, ev) \ + CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1) +#define CheckElementChangePlayer(x, y, e, ev, p, s) \ + CheckElementChangeExt(x, y, e, ev, p, s, -1) +#define CheckElementChangeSide(x, y, e, ev, s) \ + CheckElementChangeExt(x, y, e, ev, -1, s, -1) +#define CheckElementChangePage(x, y, e, ev, p) \ + CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p) static void PlayLevelSound(int, int, int); static void PlayLevelSoundNearest(int, int, int); @@ -877,24 +932,76 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game) */ } +inline void DrawGameValue_Emeralds(int value) +{ + DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2); +} + +inline void DrawGameValue_Dynamite(int value) +{ + DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2); +} + +inline void DrawGameValue_Keys(struct PlayerInfo *player) +{ + int i; + + for (i = 0; i < MAX_KEYS; i++) + if (player->key[i]) + DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS, + el2edimg(EL_KEY_1 + i)); +} + +inline void DrawGameValue_Score(int value) +{ + DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2); +} + +inline void DrawGameValue_Time(int value) +{ + if (value < 1000) + DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2); + else + DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER); +} + +inline void DrawGameValue_Level(int value) +{ + if (level_nr < 100) + DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2); + else + { + /* misuse area for displaying emeralds to draw bigger level number */ + DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS, + int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE); + + /* now copy it to the area for displaying level number */ + BlitBitmap(drawto, drawto, + DX_EMERALDS, DY_EMERALDS + 1, + getFontWidth(FONT_LEVEL_NUMBER) * 3, + getFontHeight(FONT_LEVEL_NUMBER) - 1, + DX_LEVEL - 1, DY_LEVEL + 1); + + /* restore the area for displaying emeralds */ + DrawGameValue_Emeralds(local_player->gems_still_needed); + + /* yes, this is all really ugly :-) */ + } +} + void DrawGameDoorValues() { - int i, j; + int i; + + DrawGameValue_Level(level_nr); for (i = 0; i < MAX_PLAYERS; i++) - for (j = 0; j < 4; j++) - if (stored_player[i].key[j]) - DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS, - el2edimg(EL_KEY_1 + j)); + DrawGameValue_Keys(&stored_player[i]); - DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS, - int2str(local_player->gems_still_needed, 3), FONT_TEXT_2); - DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE, - int2str(local_player->inventory_size, 3), FONT_TEXT_2); - DrawText(DX + XX_SCORE, DY + YY_SCORE, - int2str(local_player->score, 5), FONT_TEXT_2); - DrawText(DX + XX_TIME, DY + YY_TIME, - int2str(TimeLeft, 3), FONT_TEXT_2); + DrawGameValue_Emeralds(local_player->gems_still_needed); + DrawGameValue_Dynamite(local_player->inventory_size); + DrawGameValue_Score(local_player->score); + DrawGameValue_Time(TimeLeft); } static void resolve_group_element(int group_element, int recursion_depth) @@ -1246,6 +1353,7 @@ void InitGame() struct PlayerInfo *player = &stored_player[i]; player->index_nr = i; + player->index_bit = (1 << i); player->element_nr = EL_PLAYER_1 + i; player->present = FALSE; @@ -1261,7 +1369,7 @@ void InitGame() player->lights_still_needed = 0; player->friends_still_needed = 0; - for (j = 0; j < 4; j++) + for (j = 0; j < MAX_KEYS; j++) player->key[j] = FALSE; player->dynabomb_count = 0; @@ -1364,6 +1472,7 @@ void InitGame() player->shield_normal_time_left = 0; player->shield_deadly_time_left = 0; + player->inventory_infinite_element = EL_UNDEFINED; player->inventory_size = 0; DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); @@ -1387,6 +1496,7 @@ void InitGame() TimeFrames = 0; TimePlayed = 0; TimeLeft = level.time; + TapeTime = 0; ScreenMovDir = MV_NO_MOVING; ScreenMovPos = 0; @@ -1408,7 +1518,7 @@ void InitGame() game.envelope_active = FALSE; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_BELTS; i++) { game.belt_dir[i] = MV_NO_MOVING; game.belt_dir_nr[i] = 3; /* not moving, next moving left */ @@ -1496,7 +1606,7 @@ void InitGame() } /* correct non-moving belts to start moving left */ - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_BELTS; i++) if (game.belt_dir[i] == MV_NO_MOVING) game.belt_dir_nr[i] = 3; /* not moving, next moving left */ @@ -1740,19 +1850,6 @@ void InitGame() BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY); - if (level_nr < 100) - DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2); - else - { - DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS, - int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE); - BlitBitmap(drawto, drawto, - DX + XX_EMERALDS, DY + YY_EMERALDS + 1, - getFontWidth(FONT_LEVEL_NUMBER) * 3, - getFontHeight(FONT_LEVEL_NUMBER) - 1, - DX + XX_LEVEL - 1, DY + YY_LEVEL + 1); - } - DrawGameDoorValues(); UnmapGameButtons(); @@ -1778,7 +1875,7 @@ void InitGame() if (options.debug) { - for (i = 0; i < 4; i++) + for (i = 0; i < MAX_PLAYERS; i++) printf("Player %d %sactive.\n", i + 1, (stored_player[i].active ? "" : "not ")); } @@ -1898,7 +1995,7 @@ void InitMovDir(int x, int y) else if (move_pattern == MV_ALONG_LEFT_SIDE || move_pattern == MV_ALONG_RIGHT_SIDE) { - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int x1 = x + xy[i][0]; int y1 = y + xy[i][1]; @@ -1925,7 +2022,7 @@ void InitMovDir(int x, int y) element != EL_BD_FIREFLY) break; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int x1 = x + xy[i][0]; int y1 = y + xy[i][1]; @@ -2010,7 +2107,9 @@ void GameWon() TimeLeft -= 10; else TimeLeft--; - DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2); + + DrawGameValue_Time(TimeLeft); + BackToFront(); if (!tape.playing) @@ -2036,7 +2135,9 @@ void GameWon() TimePlayed += 10; else TimePlayed++; - DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2); + + DrawGameValue_Time(TimePlayed); + BackToFront(); if (!tape.playing) @@ -2321,8 +2422,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) { @@ -2555,10 +2673,15 @@ void Explode(int ex, int ey, int phase, int mode) int yy = y - ey + 1; int element; +#if 1 + if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey))) + continue; +#else if (!IN_LEV_FIELD(x, y) || ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) && (x != ex || y != ey))) continue; +#endif element = Feld[x][y]; @@ -2975,12 +3098,12 @@ void DynaExplode(int ex, int ey) Explode(ex, ey, EX_PHASE_START, EX_CENTER); - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { for (j = 1; j <= dynabomb_size; j++) { - int x = ex + j * xy[i % 4][0]; - int y = ey + j * xy[i % 4][1]; + int x = ex + j * xy[i][0]; + int y = ey + j * xy[i][1]; int element; if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y])) @@ -3066,6 +3189,9 @@ void Bang(int x, int y) case EL_PENGUIN: case EL_LAMP: case EL_LAMP_ACTIVE: +#if 1 + case EL_AMOEBA_TO_DIAMOND: +#endif if (IS_PLAYER(x, y)) Explode(x, y, EX_PHASE_START, EX_NORMAL); else @@ -3086,23 +3212,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() @@ -3125,11 +3270,11 @@ static void InitBeltMovement() int x, y, i, j; /* set frame order for belt animation graphic according to belt direction */ - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_BELTS; i++) { int belt_nr = i; - for (j = 0; j < 3; j++) + for (j = 0; j < NUM_BELT_PARTS; j++) { int element = belt_base_active_element[belt_nr] + j; int graphic = el2img(element); @@ -3147,7 +3292,7 @@ static void InitBeltMovement() { int element = Feld[x][y]; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_BELTS; i++) { if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING) { @@ -3213,7 +3358,7 @@ static void ToggleBeltSwitch(int x, int y) belt_dir_nr = 1; /* set frame order for belt animation graphic according to belt direction */ - for (i = 0; i < 3; i++) + for (i = 0; i < NUM_BELT_PARTS; i++) { int element = belt_base_active_element[belt_nr] + i; int graphic = el2img(element); @@ -3479,7 +3624,7 @@ void Impact(int x, int y) if (!lastline && smashed == EL_ACID) /* element falls into acid */ { - SplashAcid(x, y); + SplashAcid(x, y + 1); return; } @@ -3645,10 +3790,9 @@ void Impact(int x, int y) { CheckElementChange(x, y + 1, smashed, CE_SMASHED); - CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP, - CE_OTHER_IS_SWITCHING); - CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP, - CE_SWITCHED, -1); + CheckTriggeredElementChangeSide(x, y + 1, smashed, + CE_OTHER_IS_SWITCHING, CH_SIDE_TOP); + CheckElementChangeSide(x, y + 1, smashed, CE_SWITCHED, CH_SIDE_TOP); } } else @@ -3730,9 +3874,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) @@ -3745,9 +3889,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 || @@ -3871,9 +4015,9 @@ inline static void TurnRoundExt(int x, int y) } else if (element == EL_DRAGON) { - boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y); - boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y); - boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y); + boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(left_x, left_y); + boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y); + boolean can_move_on = DRAGON_CAN_ENTER_FIELD(move_x, move_y); int rnd_value = 24; int rnd = RND(rnd_value); @@ -3952,10 +4096,16 @@ inline static void TurnRoundExt(int x, int y) } else if (element == EL_SPRING) { +#if 0 if (MovDir[x][y] & MV_HORIZONTAL && - (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) || - IN_LEV_FIELD_AND_IS_FREE(x, y + 1))) + !SPRING_CAN_ENTER_FIELD(move_x, move_y)) MovDir[x][y] = MV_NO_MOVING; +#else + if (MovDir[x][y] & MV_HORIZONTAL && + (!SPRING_CAN_ENTER_FIELD(move_x, move_y) || + SPRING_CAN_ENTER_FIELD(x, y + 1))) + MovDir[x][y] = MV_NO_MOVING; +#endif MovDelay[x][y] = 0; } @@ -4008,10 +4158,10 @@ inline static void TurnRoundExt(int x, int y) { 0, +1 } }; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { - int ex = x + xy[i % 4][0]; - int ey = y + xy[i % 4][1]; + int ex = x + xy[i][0]; + int ey = y + xy[i][1]; if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN) { @@ -4246,7 +4396,7 @@ inline static void TurnRoundExt(int x, int y) else if (move_pattern == MV_WHEN_PUSHED || move_pattern == MV_WHEN_DROPPED) { - if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y)) + if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y)) MovDir[x][y] = MV_NO_MOVING; MovDelay[x][y] = 0; @@ -4279,7 +4429,7 @@ inline static void TurnRoundExt(int x, int y) int start_test = RND(4); int i; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int move_dir = test_dir[start_test + i]; int move_dir_preference; @@ -4523,7 +4673,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; @@ -4929,9 +5079,15 @@ void StartMoving(int x, int y) } #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 || @@ -4943,7 +5099,7 @@ void StartMoving(int x, int y) 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)) @@ -5070,7 +5226,7 @@ void StartMoving(int x, int y) PlayLevelSoundAction(x, y, action); } - if (new_element == element_info[element].move_enter_element) + if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element))) element_info[element].can_leave_element = TRUE; if (move_pattern & MV_MAZE_RUNNER_STYLE) @@ -5158,8 +5314,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; @@ -5486,8 +5649,8 @@ void ContinueMoving(int x, int y) int hitting_element = Feld[newx][newy]; /* !!! fix side (direction) orientation here and elsewhere !!! */ - CheckElementSideChange(newx, newy, hitting_element, - direction, CE_HITTING_SOMETHING, -1); + CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING, + direction); #if 0 if (IN_LEV_FIELD(nextx, nexty)) @@ -5504,8 +5667,8 @@ void ContinueMoving(int x, int y) { int i; - CheckElementSideChange(nextx, nexty, touched_element, - opposite_direction, CE_HIT_BY_SOMETHING, -1); + CheckElementChangeSide(nextx, nexty, touched_element, + CE_HIT_BY_SOMETHING, opposite_direction); if (IS_CUSTOM_ELEMENT(hitting_element) && HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING)) @@ -5517,11 +5680,11 @@ void ContinueMoving(int x, int y) if (change->can_change && change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) && - change->sides & touched_side && + change->trigger_side & touched_side && change->trigger_element == touched_element) { - CheckElementSideChange(newx, newy, hitting_element, - CH_SIDE_ANY, CE_OTHER_IS_HITTING, i); + CheckElementChangePage(newx, newy, hitting_element, + CE_OTHER_IS_HITTING, i); break; } } @@ -5537,11 +5700,11 @@ void ContinueMoving(int x, int y) if (change->can_change && change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) && - change->sides & hitting_side && + change->trigger_side & hitting_side && change->trigger_element == hitting_element) { - CheckElementSideChange(nextx, nexty, touched_element, - CH_SIDE_ANY, CE_OTHER_GETS_HIT, i); + CheckElementChangePage(nextx, nexty, touched_element, + CE_OTHER_GETS_HIT, i); break; } } @@ -5569,7 +5732,7 @@ int AmoebeNachbarNr(int ax, int ay) { 0, +1 } }; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int x = ax + xy[i][0]; int y = ay + xy[i][1]; @@ -5599,7 +5762,7 @@ void AmoebenVereinigen(int ax, int ay) if (new_group_nr == 0) return; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { x = ax + xy[i][0]; y = ay + xy[i][1]; @@ -5677,7 +5840,7 @@ void AmoebeUmwandeln(int ax, int ay) { 0, +1 } }; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { x = ax + xy[i][0]; y = ay + xy[i][1]; @@ -5873,7 +6036,7 @@ void AmoebeAbleger(int ax, int ay) int start = RND(4); boolean waiting_for_player = FALSE; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int j = (start + i) % 4; int x = ax + xy[j][0]; @@ -6355,11 +6518,11 @@ void CheckForDragon(int x, int y) { 0, +1 } }; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { for (j = 0; j < 4; j++) { - int xx = x + j*xy[i][0], yy = y + j*xy[i][1]; + int xx = x + j * xy[i][0], yy = y + j * xy[i][1]; if (IN_LEV_FIELD(xx, yy) && (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON)) @@ -6374,11 +6537,11 @@ void CheckForDragon(int x, int y) if (!dragon_found) { - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { for (j = 0; j < 3; j++) { - int xx = x + j*xy[i][0], yy = y + j*xy[i][1]; + int xx = x + j * xy[i][0], yy = y + j * xy[i][1]; if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES) { @@ -6417,7 +6580,7 @@ static void WarnBuggyBase(int x, int y) { 0, +1 } }; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int xx = x + xy[i][0], yy = y + xy[i][1]; @@ -6513,7 +6676,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page) Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */ - CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING); + CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page); if (change->explode) { @@ -6687,10 +6850,12 @@ static void ChangeElement(int x, int y, int page) } } -static boolean CheckTriggeredElementSideChange(int lx, int ly, - int trigger_element, - int trigger_side, - int trigger_event) +static boolean CheckTriggeredElementChangeExt(int lx, int ly, + int trigger_element, + int trigger_event, + int trigger_player, + int trigger_side, + int trigger_page) { int i, j, x, y; @@ -6712,16 +6877,11 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly, struct ElementChangeInfo *change = &element_info[element].change_page[j]; if (change->can_change && -#if 1 change->events & CH_EVENT_BIT(trigger_event) && -#endif - change->sides & trigger_side && -#if 1 - IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element) -#else - change->trigger_element == trigger_element -#endif - ) + change->trigger_side & trigger_side && + change->trigger_player & trigger_player && + change->trigger_page & (1 << trigger_page) && + IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)) { #if 0 if (!(change->events & CH_EVENT_BIT(trigger_event))) @@ -6758,15 +6918,12 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly, return TRUE; } -static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element, - int trigger_event) -{ - return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY, - trigger_event); -} - -static boolean CheckElementSideChange(int x, int y, int element, int side, - int trigger_event, int page) +static boolean CheckElementChangeExt(int x, int y, + int element, + int trigger_event, + int trigger_player, + int trigger_side, + int trigger_page) { if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event)) return FALSE; @@ -6778,7 +6935,7 @@ static boolean CheckElementSideChange(int x, int y, int element, int side, } #if 1 - if (page < 0) + if (trigger_page < 0) { boolean change_element = FALSE; int i; @@ -6789,10 +6946,11 @@ static boolean CheckElementSideChange(int x, int y, int element, int side, if (change->can_change && change->events & CH_EVENT_BIT(trigger_event) && - change->sides & side) + change->trigger_side & trigger_side && + change->trigger_player & trigger_player) { change_element = TRUE; - page = i; + trigger_page = i; break; } @@ -6806,25 +6964,20 @@ static boolean CheckElementSideChange(int x, int y, int element, int side, /* !!! this check misses pages with same event, but different side !!! */ - if (page < 0) - page = element_info[element].event_page_nr[trigger_event]; + if (trigger_page < 0) + trigger_page = element_info[element].event_page_nr[trigger_event]; - if (!(element_info[element].change_page[page].sides & side)) + if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side)) return FALSE; #endif ChangeDelay[x][y] = 1; ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event); - ChangeElement(x, y, page); + ChangeElement(x, y, trigger_page); return TRUE; } -static boolean CheckElementChange(int x, int y, int element, int trigger_event) -{ - return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1); -} - static void PlayPlayerSound(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; @@ -7027,6 +7180,10 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter); #endif +#if 0 + /* !!! TEST !!! */ + CheckGravityMovement(player); +#endif if (button1) snapped = SnapField(player, dx, dy); else @@ -7064,7 +7221,7 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); - CheckGravityMovement(player); + CheckGravityMovementWhenNotMoving(player); if (player->MovPos == 0) SetPlayerWaiting(player, TRUE); @@ -7145,7 +7302,7 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action) DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); - CheckGravityMovement(player); + CheckGravityMovementWhenNotMoving(player); if (player->MovPos == 0) InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir); @@ -7225,6 +7382,12 @@ void GameActions() recorded_player_action = (tape.playing ? TapePlayAction() : NULL); +#if 1 + if (recorded_player_action != NULL) + for (i = 0; i < MAX_PLAYERS; i++) + stored_player[i].action = recorded_player_action[i]; +#endif + for (i = 0; i < MAX_PLAYERS; i++) { summarized_player_action |= stored_player[i].action; @@ -7245,11 +7408,31 @@ void GameActions() { int actual_player_action = stored_player[i].effective_action; +#if 1 + /* OLD: overwrite programmed action with tape action (BAD!!!) */ if (stored_player[i].programmed_action) actual_player_action = stored_player[i].programmed_action; +#endif if (recorded_player_action) + { +#if 0 + if (stored_player[i].programmed_action && + stored_player[i].programmed_action != recorded_player_action[i]) + printf("::: %d: %d <-> %d\n", i, + stored_player[i].programmed_action, recorded_player_action[i]); +#endif + +#if 0 actual_player_action = recorded_player_action[i]; +#endif + } + +#if 0 + /* NEW: overwrite tape action with programmed action */ + if (stored_player[i].programmed_action) + actual_player_action = stored_player[i].programmed_action; +#endif tape_action[i] = PlayerActions(&stored_player[i], actual_player_action); @@ -7683,39 +7866,44 @@ void GameActions() if (TimeFrames >= FRAMES_PER_SECOND) { TimeFrames = 0; - TimePlayed++; + TapeTime++; - for (i = 0; i < MAX_PLAYERS; i++) + if (!level.use_step_counter) { - struct PlayerInfo *player = &stored_player[i]; + TimePlayed++; - if (SHIELD_ON(player)) + for (i = 0; i < MAX_PLAYERS; i++) { - player->shield_normal_time_left--; + struct PlayerInfo *player = &stored_player[i]; - if (player->shield_deadly_time_left > 0) - player->shield_deadly_time_left--; - } - } + if (SHIELD_ON(player)) + { + player->shield_normal_time_left--; - if (tape.recording || tape.playing) - DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed); + if (player->shield_deadly_time_left > 0) + player->shield_deadly_time_left--; + } + } - if (TimeLeft > 0) - { - TimeLeft--; + if (TimeLeft > 0) + { + TimeLeft--; - if (TimeLeft <= 10 && setup.time_limit) - PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE); + if (TimeLeft <= 10 && setup.time_limit) + PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE); - DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2); + DrawGameValue_Time(TimeLeft); - if (!TimeLeft && setup.time_limit) - for (i = 0; i < MAX_PLAYERS; i++) - KillHero(&stored_player[i]); + if (!TimeLeft && setup.time_limit) + for (i = 0; i < MAX_PLAYERS; i++) + KillHero(&stored_player[i]); + } + else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ + DrawGameValue_Time(TimePlayed); } - else if (level.time == 0 && !AllPlayersGone) /* level without time limit */ - DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2); + + if (tape.recording || tape.playing) + DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); } DrawAllPlayers(); @@ -7885,20 +8073,25 @@ static void CheckGravityMovement(struct PlayerInfo *player) { if (game.gravity && !player->programmed_action) { - int move_dir_vertical = player->action & (MV_UP | MV_DOWN); - int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT); + int move_dir_horizontal = player->action & MV_HORIZONTAL; + int move_dir_vertical = player->action & MV_VERTICAL; int move_dir = - (player->last_move_dir & (MV_LEFT | MV_RIGHT) ? + (player->last_move_dir & MV_HORIZONTAL ? (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) : (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical)); int jx = player->jx, jy = player->jy; int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0); int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0); int new_jx = jx + dx, new_jy = jy + dy; + boolean player_is_snapping = player->action & JOY_BUTTON_1; boolean field_under_player_is_free = (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1)); boolean player_is_moving_to_valid_field = - (IN_LEV_FIELD(new_jx, new_jy) && + ( +#if 1 + !player_is_snapping && +#endif + IN_LEV_FIELD(new_jx, new_jy) && (Feld[new_jx][new_jy] == EL_SP_BASE || Feld[new_jx][new_jy] == EL_SAND || (IS_SP_PORT(Feld[new_jx][new_jy]) && @@ -7910,9 +8103,44 @@ static void CheckGravityMovement(struct PlayerInfo *player) (IS_WALKABLE(Feld[jx][jy]) && !(element_info[Feld[jx][jy]].access_direction & MV_DOWN))); +#if 0 + printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n", + field_under_player_is_free, + player_is_standing_on_valid_field, + player_is_moving_to_valid_field, + (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1)); +#endif + if (field_under_player_is_free && !player_is_standing_on_valid_field && !player_is_moving_to_valid_field) + { +#if 0 + printf("::: setting programmed_action to MV_DOWN ...\n"); +#endif + + player->programmed_action = MV_DOWN; + } + } +} + +static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player) +{ +#if 1 + return CheckGravityMovement(player); +#endif + + if (game.gravity && !player->programmed_action) + { + int jx = player->jx, jy = player->jy; + boolean field_under_player_is_free = + (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1)); + 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_standing_on_valid_field) player->programmed_action = MV_DOWN; } } @@ -7928,7 +8156,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, int dx, int dy, int real_dx, int real_dy) { #if 0 - static int change_sides[4][2] = + static int trigger_sides[4][2] = { /* enter side leave side */ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */ @@ -7940,8 +8168,8 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, dx == +1 ? MV_RIGHT : dy == -1 ? MV_UP : dy == +1 ? MV_DOWN : MV_NO_MOVING); - int enter_side = change_sides[MV_DIR_BIT(move_direction)][0]; - int leave_side = change_sides[MV_DIR_BIT(move_direction)][1]; + int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0]; + int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1]; #endif int jx = player->jx, jy = player->jy; int new_jx = jx + dx, new_jy = jy + dy; @@ -7972,7 +8200,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; @@ -8014,18 +8242,17 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, #if 0 if (IS_CUSTOM_ELEMENT(Feld[jx][jy])) { - CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side, - CE_OTHER_GETS_LEFT); - CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side, - CE_LEFT_BY_PLAYER, -1); + CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT, + leave_side); + CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side); } if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy])) { - CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], - enter_side, CE_OTHER_GETS_ENTERED); - CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side, - CE_ENTERED_BY_PLAYER, -1); + CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy], + CE_OTHER_GETS_ENTERED, enter_side); + CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy], + CE_ENTERED_BY_PLAYER, enter_side); } #endif @@ -8065,9 +8292,16 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) !tape.playing) return FALSE; #else + +#if 1 + if (!FrameReached(&player->move_delay, player->move_delay_value)) + return FALSE; +#else if (!FrameReached(&player->move_delay, player->move_delay_value) && !(tape.playing && tape.file_version < FILE_VERSION_2_0)) return FALSE; +#endif + #endif /* remove the last programmed player action */ @@ -8100,7 +8334,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->move_delay_value = original_move_delay_value; } - if (player->last_move_dir & (MV_LEFT | MV_RIGHT)) + if (player->last_move_dir & MV_HORIZONTAL) { if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy))) moved |= MovePlayerOneStep(player, dx, 0, dx, dy); @@ -8218,7 +8452,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) #if 1 { - static int change_sides[4][2] = + static int trigger_sides[4][2] = { /* enter side leave side */ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */ @@ -8227,24 +8461,27 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */ }; int move_direction = player->MovDir; - int enter_side = change_sides[MV_DIR_BIT(move_direction)][0]; - int leave_side = change_sides[MV_DIR_BIT(move_direction)][1]; + int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0]; + int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1]; #if 1 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy])) { - CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy], - leave_side, CE_OTHER_GETS_LEFT); - CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy], - leave_side, CE_LEFT_BY_PLAYER, -1); + CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy], + CE_OTHER_GETS_LEFT, + player->index_bit, leave_side); + CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy], + CE_LEFT_BY_PLAYER, + player->index_bit, leave_side); } if (IS_CUSTOM_ELEMENT(Feld[jx][jy])) { - CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], - enter_side, CE_OTHER_GETS_ENTERED); - CheckElementSideChange(jx, jy, Feld[jx][jy], - enter_side, CE_ENTERED_BY_PLAYER, -1); + CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy], + CE_OTHER_GETS_ENTERED, + player->index_bit, enter_side); + CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER, + player->index_bit, enter_side); } #endif @@ -8255,7 +8492,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) } else { - CheckGravityMovement(player); + CheckGravityMovementWhenNotMoving(player); /* player->last_move_dir = MV_NO_MOVING; @@ -8374,6 +8611,42 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) RemoveHero(player); } + if (level.use_step_counter) + { + int i; + + TimePlayed++; + + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + if (SHIELD_ON(player)) + { + player->shield_normal_time_left--; + + if (player->shield_deadly_time_left > 0) + player->shield_deadly_time_left--; + } + } + + if (TimeLeft > 0) + { + TimeLeft--; + + if (TimeLeft <= 10 && setup.time_limit) + PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE); + + DrawGameValue_Time(TimeLeft); + + if (!TimeLeft && setup.time_limit) + for (i = 0; i < MAX_PLAYERS; i++) + KillHero(&stored_player[i]); + } + else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ + DrawGameValue_Time(TimePlayed); + } + if (tape.single_step && tape.recording && !tape.pausing && !player->programmed_action) TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); @@ -8417,7 +8690,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y) { +1, 0 }, { 0, +1 } }; - static int change_sides[4][2] = + static int trigger_sides[4][2] = { /* center side border side */ { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */ @@ -8435,12 +8708,12 @@ void TestIfPlayerTouchesCustomElement(int x, int y) int center_element = Feld[x][y]; /* should always be non-moving! */ int i; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int xx = x + xy[i][0]; int yy = y + xy[i][1]; - int center_side = change_sides[i][0]; - int border_side = change_sides[i][1]; + int center_side = trigger_sides[i][0]; + int border_side = trigger_sides[i][1]; int border_element; if (!IN_LEV_FIELD(xx, yy)) @@ -8448,6 +8721,8 @@ void TestIfPlayerTouchesCustomElement(int x, int y) if (IS_PLAYER(x, y)) { + struct PlayerInfo *player = PLAYERINFO(x, y); + if (game.engine_version < VERSION_IDENT(3,0,7,0)) border_element = Feld[xx][yy]; /* may be moving! */ else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy)) @@ -8457,25 +8732,27 @@ void TestIfPlayerTouchesCustomElement(int x, int y) else continue; /* center and border element do not touch */ - CheckTriggeredElementSideChange(xx, yy, border_element, border_side, - CE_OTHER_GETS_TOUCHED); - CheckElementSideChange(xx, yy, border_element, border_side, - CE_TOUCHED_BY_PLAYER, -1); + CheckTriggeredElementChangePlayer(xx, yy, border_element, + CE_OTHER_GETS_TOUCHED, + player->index_bit, border_side); + CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER, + player->index_bit, border_side); } else if (IS_PLAYER(xx, yy)) { + struct PlayerInfo *player = PLAYERINFO(xx, yy); + if (game.engine_version >= VERSION_IDENT(3,0,7,0)) { - struct PlayerInfo *player = PLAYERINFO(xx, yy); - if (player->MovPos != 0 && !(player->MovDir & touch_dir[i])) continue; /* center and border element do not touch */ } - CheckTriggeredElementSideChange(x, y, center_element, center_side, - CE_OTHER_GETS_TOUCHED); - CheckElementSideChange(x, y, center_element, center_side, - CE_TOUCHED_BY_PLAYER, -1); + CheckTriggeredElementChangePlayer(x, y, center_element, + CE_OTHER_GETS_TOUCHED, + player->index_bit, center_side); + CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER, + player->index_bit, center_side); break; } @@ -8491,7 +8768,7 @@ void TestIfElementTouchesCustomElement(int x, int y) { +1, 0 }, { 0, +1 } }; - static int change_sides[4][2] = + static int trigger_sides[4][2] = { /* center side border side */ { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */ @@ -8511,12 +8788,12 @@ void TestIfElementTouchesCustomElement(int x, int y) int center_element = Feld[x][y]; /* should always be non-moving! */ int i, j; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int xx = x + xy[i][0]; int yy = y + xy[i][1]; - int center_side = change_sides[i][0]; - int border_side = change_sides[i][1]; + int center_side = trigger_sides[i][0]; + int border_side = trigger_sides[i][1]; int border_element; if (!IN_LEV_FIELD(xx, yy)) @@ -8543,7 +8820,7 @@ void TestIfElementTouchesCustomElement(int x, int y) if (change->can_change && change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) && - change->sides & border_side && + change->trigger_side & border_side && #if 1 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element) #else @@ -8570,7 +8847,7 @@ void TestIfElementTouchesCustomElement(int x, int y) if (change->can_change && change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) && - change->sides & center_side && + change->trigger_side & center_side && #if 1 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element) #else @@ -8578,8 +8855,8 @@ void TestIfElementTouchesCustomElement(int x, int y) #endif ) { - CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY, - CE_OTHER_IS_TOUCHING, j); + CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING, + j); break; } } @@ -8587,8 +8864,8 @@ void TestIfElementTouchesCustomElement(int x, int y) } if (change_center_element) - CheckElementSideChange(x, y, center_element, CH_SIDE_ANY, - CE_OTHER_IS_TOUCHING, center_element_change_page); + CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING, + center_element_change_page); } void TestIfElementHitsCustomElement(int x, int y, int direction) @@ -8613,8 +8890,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) return; #endif - CheckElementSideChange(x, y, hitting_element, - direction, CE_HITTING_SOMETHING, -1); + CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING, + direction); if (IN_LEV_FIELD(hitx, hity)) { @@ -8634,8 +8911,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) { int i; - CheckElementSideChange(hitx, hity, touched_element, - opposite_direction, CE_HIT_BY_SOMETHING, -1); + CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING, + opposite_direction); if (IS_CUSTOM_ELEMENT(hitting_element) && HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING)) @@ -8647,7 +8924,7 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) if (change->can_change && change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) && - change->sides & touched_side && + change->trigger_side & touched_side && #if 1 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element) @@ -8656,8 +8933,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) #endif ) { - CheckElementSideChange(x, y, hitting_element, - CH_SIDE_ANY, CE_OTHER_IS_HITTING, i); + CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING, + i); break; } } @@ -8673,7 +8950,7 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) if (change->can_change && change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) && - change->sides & hitting_side && + change->trigger_side & hitting_side && #if 1 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element) #else @@ -8681,8 +8958,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) #endif ) { - CheckElementSideChange(hitx, hity, touched_element, - CH_SIDE_ANY, CE_OTHER_GETS_HIT, i); + CheckElementChangePage(hitx, hity, touched_element, + CE_OTHER_GETS_HIT, i); break; } } @@ -8709,7 +8986,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) MV_DOWN }; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int test_x, test_y, test_move_dir, test_element; @@ -8784,7 +9061,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */ return; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int test_x, test_y, test_move_dir, test_element; @@ -8888,7 +9165,7 @@ void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y) { 0, +1 } }; - for (i = 0; i < 4; i++) + for (i = 0; i < NUM_DIRECTIONS; i++) { int x, y, element; @@ -9022,7 +9299,7 @@ int DigField(struct PlayerInfo *player, int oldx, int oldy, int x, int y, int real_dx, int real_dy, int mode) { - static int change_sides[4] = + static int trigger_sides[4] = { CH_SIDE_RIGHT, /* moving left */ CH_SIDE_LEFT, /* moving right */ @@ -9040,7 +9317,7 @@ int DigField(struct PlayerInfo *player, 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 dig_side = trigger_sides[MV_DIR_BIT(move_direction)]; int old_element = Feld[jx][jy]; int element; @@ -9329,7 +9606,8 @@ int DigField(struct PlayerInfo *player, PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING); - CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED); + CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED, + player->index_bit, CH_SIDE_ANY); #if 1 if (mode == DF_SNAP) @@ -9353,7 +9631,7 @@ int DigField(struct PlayerInfo *player, else if (element == EL_EXTRA_TIME && level.time > 0) { TimeLeft += 10; - DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2); + DrawGameValue_Time(TimeLeft); } else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY) { @@ -9366,8 +9644,7 @@ int DigField(struct PlayerInfo *player, if (player->inventory_size < MAX_INVENTORY_SIZE) player->inventory_element[player->inventory_size++] = element; - DrawText(DX_DYNAMITE, DY_DYNAMITE, - int2str(local_player->inventory_size, 3), FONT_TEXT_2); + DrawGameValue_Dynamite(local_player->inventory_size); } else if (element == EL_DYNABOMB_INCREASE_NUMBER) { @@ -9390,8 +9667,8 @@ int DigField(struct PlayerInfo *player, player->key[key_nr] = TRUE; - DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS, - el2edimg(EL_KEY_1 + key_nr)); + DrawGameValue_Keys(player); + redraw_mask |= REDRAW_DOOR_1; } else if (IS_ENVELOPE(element)) @@ -9406,12 +9683,14 @@ int DigField(struct PlayerInfo *player, { int i; - for (i = 0; i < element_info[element].collect_count; i++) - if (player->inventory_size < MAX_INVENTORY_SIZE) - player->inventory_element[player->inventory_size++] = element; + if (element_info[element].collect_count == 0) + player->inventory_infinite_element = element; + else + for (i = 0; i < element_info[element].collect_count; i++) + if (player->inventory_size < MAX_INVENTORY_SIZE) + player->inventory_element[player->inventory_size++] = element; - DrawText(DX_DYNAMITE, DY_DYNAMITE, - int2str(local_player->inventory_size, 3), FONT_TEXT_2); + DrawGameValue_Dynamite(local_player->inventory_size); } else if (element_info[element].collect_count > 0) { @@ -9420,14 +9699,15 @@ int DigField(struct PlayerInfo *player, if (local_player->gems_still_needed < 0) local_player->gems_still_needed = 0; - DrawText(DX_EMERALDS, DY_EMERALDS, - int2str(local_player->gems_still_needed, 3), FONT_TEXT_2); + DrawGameValue_Emeralds(local_player->gems_still_needed); } RaiseScoreElement(element); PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING); - CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED); + CheckTriggeredElementChangePlayer(x, y, element, + CE_OTHER_GETS_COLLECTED, + player->index_bit, CH_SIDE_ANY); #if 1 if (mode == DF_SNAP) @@ -9583,10 +9863,10 @@ int DigField(struct PlayerInfo *player, else player->push_delay_value = -1; /* get new value later */ - CheckTriggeredElementSideChange(x, y, element, dig_side, - CE_OTHER_GETS_PUSHED); - CheckElementSideChange(x, y, element, dig_side, - CE_PUSHED_BY_PLAYER, -1); + CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED, + player->index_bit, dig_side); + CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER, + player->index_bit, dig_side); break; } @@ -9671,7 +9951,7 @@ int DigField(struct PlayerInfo *player, { Feld[x][y] = EL_TIME_ORB_EMPTY; TimeLeft += 10; - DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2); + DrawGameValue_Time(TimeLeft); DrawLevelField(x, y); @@ -9690,15 +9970,17 @@ int DigField(struct PlayerInfo *player, player->switch_x = x; player->switch_y = y; - CheckTriggeredElementSideChange(x, y, element, dig_side, - CE_OTHER_IS_SWITCHING); - CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1); + CheckTriggeredElementChangePlayer(x, y, element, + CE_OTHER_IS_SWITCHING, + player->index_bit, dig_side); + CheckElementChangePlayer(x, y, element, CE_SWITCHED, + player->index_bit, dig_side); } - CheckTriggeredElementSideChange(x, y, element, dig_side, - CE_OTHER_GETS_PRESSED); - CheckElementSideChange(x, y, element, dig_side, - CE_PRESSED_BY_PLAYER, -1); + CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED, + player->index_bit, dig_side); + CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER, + player->index_bit, dig_side); } return MF_NO_ACTION; @@ -9787,25 +10069,43 @@ boolean DropElement(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; int old_element = Feld[jx][jy]; - int new_element; + int new_element = (player->inventory_size > 0 ? + player->inventory_element[player->inventory_size - 1] : + player->inventory_infinite_element != EL_UNDEFINED ? + player->inventory_infinite_element : + player->dynabombs_left > 0 ? + EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr : + EL_UNDEFINED); /* check if player is active, not moving and ready to drop */ if (!player->active || player->MovPos || player->drop_delay > 0) return FALSE; /* check if player has anything that can be dropped */ - if (player->inventory_size == 0 && player->dynabombs_left == 0) +#if 1 + if (new_element == EL_UNDEFINED) + return FALSE; +#else + if (player->inventory_size == 0 && + player->inventory_infinite_element == EL_UNDEFINED && + player->dynabombs_left == 0) return FALSE; +#endif /* check if anything can be dropped at the current position */ if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION) return FALSE; /* collected custom elements can only be dropped on empty fields */ +#if 1 + if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY) + return FALSE; +#else if (player->inventory_size > 0 && IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1]) && old_element != EL_EMPTY) return FALSE; +#endif if (old_element != EL_EMPTY) Back[jx][jy] = old_element; /* store old element on this field */ @@ -9813,20 +10113,26 @@ boolean DropElement(struct PlayerInfo *player) ResetGfxAnimation(jx, jy); ResetRandomAnimationValue(jx, jy); - if (player->inventory_size > 0) + if (player->inventory_size > 0 || + player->inventory_infinite_element != EL_UNDEFINED) { - player->inventory_size--; - new_element = player->inventory_element[player->inventory_size]; + if (player->inventory_size > 0) + { + player->inventory_size--; - if (new_element == EL_DYNAMITE) - new_element = EL_DYNAMITE_ACTIVE; - else if (new_element == EL_SP_DISK_RED) - new_element = EL_SP_DISK_RED_ACTIVE; +#if 0 + new_element = player->inventory_element[player->inventory_size]; +#endif - Feld[jx][jy] = new_element; + DrawGameValue_Dynamite(local_player->inventory_size); - DrawText(DX_DYNAMITE, DY_DYNAMITE, - int2str(local_player->inventory_size, 3), FONT_TEXT_2); + if (new_element == EL_DYNAMITE) + new_element = EL_DYNAMITE_ACTIVE; + else if (new_element == EL_SP_DISK_RED) + new_element = EL_SP_DISK_RED_ACTIVE; + } + + Feld[jx][jy] = new_element; if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy))) DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0); @@ -9838,15 +10144,21 @@ boolean DropElement(struct PlayerInfo *player) Changed[jx][jy] = 0; /* allow another change */ #endif - CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED); - CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER); + CheckTriggeredElementChangePlayer(jx, jy, new_element, + CE_OTHER_GETS_DROPPED, + player->index_bit, CH_SIDE_ANY); + CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER, + player->index_bit, CH_SIDE_ANY); TestIfElementTouchesCustomElement(jx, jy); } else /* player is dropping a dyna bomb */ { player->dynabombs_left--; + +#if 0 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr; +#endif Feld[jx][jy] = new_element; @@ -9904,8 +10216,8 @@ boolean DropElement(struct PlayerInfo *player) #if 1 TestIfElementHitsCustomElement(jx, jy, direction); #else - CheckElementSideChange(jx, jy, new_element, - direction, CE_HITTING_SOMETHING, -1); + CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING, + direction); #endif } @@ -10045,7 +10357,8 @@ static void PlayLevelMusic() void RaiseScore(int value) { local_player->score += value; - DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2); + + DrawGameValue_Score(local_player->score); } void RaiseScoreElement(int element)