X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=c321c01297597ea07e7e7a588717c5d616d361f7;hb=750338586791fbdc6d34ab20b5bd659573faf111;hp=2069cb1d35397dedc14306ddfea9fe845a658514;hpb=87c8124b5296a3f5debfcb1273f4aa3beb563e8e;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 2069cb1d..c321c012 100644 --- a/src/game.c +++ b/src/game.c @@ -107,7 +107,7 @@ (condition))) #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \ - ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 1) + ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0) #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \ ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID)) @@ -352,8 +352,47 @@ static struct ChangingElementInfo changing_element_list[] = } }; +struct +{ + int element; + int push_delay_fixed, push_delay_random; +} +push_delay_list[] = +{ + { EL_SPRING, 0, 0 }, + { EL_BALLOON, 0, 0 }, + + { EL_SOKOBAN_OBJECT, 2, 0 }, + { EL_SOKOBAN_FIELD_FULL, 2, 0 }, + { EL_SATELLITE, 2, 0 }, + { EL_SP_DISK_YELLOW, 2, 0 }, + + { EL_UNDEFINED, 0, 0 }, +}; + +struct +{ + int element; + int gem_count; +} +gem_count_list[] = +{ + { EL_EMERALD, 1 }, + { EL_BD_DIAMOND, 1 }, + { EL_EMERALD_YELLOW, 1 }, + { EL_EMERALD_RED, 1 }, + { EL_EMERALD_PURPLE, 1 }, + { EL_DIAMOND, 3 }, + { EL_SP_INFOTRON, 1 }, + { EL_PEARL, 5 }, + { EL_CRYSTAL, 8 }, + + { EL_UNDEFINED, 0 }, +}; + static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS]; static unsigned long trigger_events[MAX_NUM_ELEMENTS]; +static int gem_count[MAX_NUM_ELEMENTS]; #define IS_AUTO_CHANGING(e) (changing_element[e].base_element != EL_UNDEFINED) #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0) @@ -688,6 +727,8 @@ static void InitGameEngine() printf(" => game.engine_version == %06d\n", game.engine_version); #endif + /* ---------- initialize player's initial move delay --------------------- */ + /* dynamically adjust player properties according to game engine version */ game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1) ? INITIAL_MOVE_DELAY_ON : @@ -697,6 +738,8 @@ static void InitGameEngine() game.initial_move_delay_value = (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED); + /* ---------- initialize changing elements ------------------------------- */ + /* initialize changing elements information */ for (i=0; ibase_element; @@ -721,8 +763,6 @@ static void InitGameEngine() changing_element[element].pre_change_function = ce->pre_change_function; changing_element[element].change_function = ce->change_function; changing_element[element].post_change_function = ce->post_change_function; - - i++; } /* add changing elements from custom element configuration */ @@ -741,6 +781,8 @@ static void InitGameEngine() change->delay_frames); } + /* ---------- initialize trigger events ---------------------------------- */ + /* initialize trigger events information */ for (i=0; iactual_frame_counter = 0; player->last_move_dir = MV_NO_MOVING; - player->is_moving = FALSE; player->is_moving = FALSE; player->is_waiting = FALSE; @@ -908,6 +980,7 @@ void InitGame() AmoebaNr[x][y] = 0; JustStopped[x][y] = 0; Stop[x][y] = FALSE; + Pushed[x][y] = FALSE; ExplodePhase[x][y] = 0; ExplodeField[x][y] = EX_NO_EXPLOSION; @@ -1212,7 +1285,9 @@ void InitMovDir(int x, int y) { if (element_info[element].move_direction_initial != MV_NO_MOVING) MovDir[x][y] = element_info[element].move_direction_initial; - else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS) + else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS || + element_info[element].move_pattern == MV_TURNING_LEFT || + element_info[element].move_pattern == MV_TURNING_RIGHT) MovDir[x][y] = 1 << RND(4); else if (element_info[element].move_pattern == MV_HORIZONTAL) MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT); @@ -1505,8 +1580,10 @@ static void ResetGfxAnimation(int x, int y) void InitMovingField(int x, int y, int direction) { int element = Feld[x][y]; - int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); - int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); + int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); + int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); + int newx = x + dx; + int newy = y + dy; if (!JustStopped[x][y] || direction != MovDir[x][y]) ResetGfxAnimation(x, y); @@ -1600,6 +1677,7 @@ static void RemoveField(int x, int y) MovDir[x][y] = 0; MovDelay[x][y] = 0; ChangeDelay[x][y] = 0; + Pushed[x][y] = FALSE; } void RemoveMovingField(int x, int y) @@ -3015,12 +3093,18 @@ void TurnRound(int x, int y) } } } - else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS) + else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS || + element_info[element].move_pattern == MV_TURNING_LEFT || + element_info[element].move_pattern == MV_TURNING_RIGHT) { boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y); boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y); - if (can_turn_left && can_turn_right) + if (element_info[element].move_pattern == MV_TURNING_LEFT) + MovDir[x][y] = left_dir; + else if (element_info[element].move_pattern == MV_TURNING_RIGHT) + MovDir[x][y] = right_dir; + else if (can_turn_left && can_turn_right) MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir); else if (can_turn_left) MovDir[x][y] = (RND(2) ? left_dir : back_dir); @@ -3313,11 +3397,26 @@ void StartMoving(int x, int y) GfxAction[x][y + 1] = ACTION_ACTIVE; #endif } +#if 1 +#if 1 + else if (game.engine_version < RELEASE_IDENT(2,2,0,7) && + CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED && + JustStopped[x][y] && !Pushed[x][y + 1]) +#else else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED && JustStopped[x][y]) +#endif { + /* calling "Impact()" here is not only completely unneccessary + (because it already gets called from "ContinueMoving()" in + all relevant situations), but also completely bullshit, because + "JustStopped" also indicates a finished *horizontal* movement; + we must keep this trash for backwards compatibility with older + tapes */ + Impact(x, y); } +#endif else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug) { if (MovDir[x][y] == MV_NO_MOVING) @@ -3351,7 +3450,7 @@ void StartMoving(int x, int y) element != EL_DX_SUPABOMB) #endif #else - else if ((IS_SLIPPERY(Feld[x][y + 1]) || + else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) || (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) && !IS_FALLING(x, y + 1) && !JustStopped[x][y + 1] && element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE) @@ -3371,6 +3470,11 @@ void StartMoving(int x, int y) InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT); started_moving = TRUE; + +#if 0 + if (element == EL_BOMB) + printf("::: SLIP DOWN [%d]\n", FrameCounter); +#endif } } else if (IS_BELT_ACTIVE(Feld[x][y + 1])) @@ -3391,7 +3495,7 @@ void StartMoving(int x, int y) } } - /* not "else if" because of EL_SPRING */ + /* not "else if" because of elements that can fall and move (EL_SPRING) */ if (CAN_MOVE(element) && !started_moving) { int newx, newy; @@ -3424,7 +3528,9 @@ void StartMoving(int x, int y) if (element != EL_YAMYAM && element != EL_DARK_YAMYAM && element != EL_PACMAN && - !(element_info[element].move_pattern & MV_ANY_DIRECTION)) + !(element_info[element].move_pattern & MV_ANY_DIRECTION) && + element_info[element].move_pattern != MV_TURNING_LEFT && + element_info[element].move_pattern != MV_TURNING_RIGHT) { TurnRound(x, y); @@ -3778,6 +3884,21 @@ void ContinueMoving(int x, int y) int horiz_move = (dx != 0); int newx = x + dx, newy = y + dy; int step = (horiz_move ? dx : dy) * TILEX / MOVE_DELAY_NORMAL_SPEED; +#if 1 + boolean pushed = Pushed[x][y]; +#else + struct PlayerInfo *player = (IS_PLAYER(x, y) ? PLAYERINFO(x, y) : NULL); +#if 0 + boolean pushing = (player != NULL && player->Pushing && player->MovPos != 0); +#else + boolean pushing = (player != NULL && player->Pushing && player->is_moving); +#endif +#endif + +#if 0 + if (player && player->is_moving && player->MovPos == 0) + printf("::: !!!\n"); +#endif if (element == EL_AMOEBA_DROP || element == EL_AMOEBA_DROPPING) step /= 2; @@ -3804,6 +3925,30 @@ void ContinueMoving(int x, int y) MovPos[x][y] += step; +#if 1 +#if 1 + if (pushed) /* special case: moving object pushed by player */ +#else + if (pushing) /* special case: moving object pushed by player */ +#endif +#if 1 + MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); +#else + MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->GfxPos)); +#endif +#endif + +#if 0 + if (element == EL_SPRING) + printf("::: spring moves %d [%d: %d, %d, %d/%d]\n", + MovPos[x][y], + pushing, + (player?player->Pushing:-42), + (player?player->is_moving:-42), + (player?player->MovPos:-42), + (player?player->GfxPos:-42)); +#endif + if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */ { Feld[x][y] = EL_EMPTY; @@ -3879,6 +4024,16 @@ void ContinueMoving(int x, int y) Feld[x][y] = get_next_element(element); element = Feld[newx][newy] = Store[x][y]; } + else if (element == EL_SOKOBAN_OBJECT) + { + if (Back[x][y]) + Feld[x][y] = Back[x][y]; + + if (Back[newx][newy]) + Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL; + + Back[x][y] = Back[newx][newy] = 0; + } else if (Store[x][y] == EL_ACID) { element = Feld[newx][newy] = EL_ACID; @@ -3896,29 +4051,47 @@ void ContinueMoving(int x, int y) GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */ GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */ + Pushed[x][y] = Pushed[newx][newy] = FALSE; + ResetGfxAnimation(x, y); /* reset animation values for old field */ #if 1 #if 0 + /* 2.1.1 (does not work correctly for spring) */ if (!CAN_MOVE(element)) MovDir[newx][newy] = 0; #else - /* + +#if 0 + /* (does not work for falling objects that slide horizontally) */ if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN) MovDir[newx][newy] = 0; +#else + /* + if (!CAN_MOVE(element) || + (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN)) + MovDir[newx][newy] = 0; */ if (!CAN_MOVE(element) || - (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN)) + (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)) MovDir[newx][newy] = 0; #endif + +#endif #endif DrawLevelField(x, y); DrawLevelField(newx, newy); - Stop[newx][newy] = TRUE; - JustStopped[newx][newy] = 3; +#if 0 + if (game.engine_version >= RELEASE_IDENT(2,2,0,7) || !pushing) +#endif + Stop[newx][newy] = TRUE; /* ignore this element until the next frame */ +#if 1 + if (!pushed) /* special case: moving object pushed by player */ +#endif + JustStopped[newx][newy] = 3; if (DONT_TOUCH(element)) /* object may be nasty to player or others */ { @@ -4961,16 +5134,12 @@ static void CheckPlayerElementChange(int x, int y, int element, if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, trigger_event)) return; - if (trigger_event == CE_PUSHED_BY_PLAYER) - { -#if 0 - /* !!! does not work -- debug !!! */ - ChangeDelay[x][y] = 2; /* give one frame (+1) to start pushing */ - ChangeElement(x, y); +#if 1 + ChangeDelay[x][y] = 1; + ChangeElement(x, y); +#else + ChangeElementDoIt(x, y, element_info[element].change.successor); #endif - } - else - ChangeElementDoIt(x, y, element_info[element].change.successor); } static void PlayerActions(struct PlayerInfo *player, byte player_action) @@ -5139,6 +5308,30 @@ void GameActions() stored_player[i].Frame++; #endif +#if 1 + if (game.engine_version < RELEASE_IDENT(2,2,0,7)) + { + for (i=0; ijx; + int y = player->jy; + + if (player->active && player->Pushing && player->is_moving && + IS_MOVING(x, y)) + { + ContinueMoving(x, y); + + /* continue moving after pushing (this is actually a bug) */ + if (!IS_MOVING(x, y)) + { + Stop[x][y] = FALSE; + } + } + } + } +#endif + for (y=0; yjx, jy = player->jy; int dx = x - jx, dy = y - jy; int move_direction = (dx == -1 ? MV_LEFT : @@ -6328,7 +6522,14 @@ int DigField(struct PlayerInfo *player, } if (IS_MOVING(x, y) || IS_PLAYER(x, y)) + { +#if 0 + if (FrameCounter == 437) + printf("::: ---> IS_MOVING %d\n", MovDir[x][y]); +#endif + return MF_NO_ACTION; + } #if 0 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy])) @@ -6402,6 +6603,8 @@ int DigField(struct PlayerInfo *player, break; #endif +#if 0 + case EL_EMERALD: case EL_BD_DIAMOND: case EL_EMERALD_YELLOW: @@ -6431,6 +6634,10 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); break; +#endif + +#if 0 + case EL_SPEED_PILL: RemoveField(x, y); player->move_delay_value = MOVE_DELAY_HIGH_SPEED; @@ -6442,6 +6649,9 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); break; +#endif + + #if 0 case EL_ENVELOPE: Feld[x][y] = EL_EMPTY; @@ -6454,6 +6664,8 @@ int DigField(struct PlayerInfo *player, break; #endif +#if 0 + case EL_EXTRA_TIME: RemoveField(x, y); if (level.time > 0) @@ -6469,6 +6681,9 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); break; +#endif + +#if 0 case EL_SHIELD_NORMAL: RemoveField(x, y); player->shield_normal_time_left += 10; @@ -6491,7 +6706,9 @@ int DigField(struct PlayerInfo *player, #endif CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); break; +#endif +#if 0 case EL_DYNAMITE: case EL_SP_DISK_RED: RemoveField(x, y); @@ -6503,7 +6720,9 @@ int DigField(struct PlayerInfo *player, PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); break; +#endif +#if 0 case EL_DYNABOMB_INCREASE_NUMBER: RemoveField(x, y); player->dynabomb_count++; @@ -6540,7 +6759,9 @@ int DigField(struct PlayerInfo *player, #endif CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); break; +#endif +#if 0 case EL_KEY_1: case EL_KEY_2: case EL_KEY_3: @@ -6588,6 +6809,7 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); break; } +#endif case EL_ROBOT_WHEEL: Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE; @@ -6689,6 +6911,8 @@ int DigField(struct PlayerInfo *player, return MF_ACTION; break; +#if 0 + /* the following elements cannot be pushed by "snapping" */ case EL_ROCK: case EL_BOMB: @@ -6708,6 +6932,10 @@ int DigField(struct PlayerInfo *player, if (dy) return MF_NO_ACTION; + if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) && + !(element == EL_SPRING && use_spring_bug)) + return MF_NO_ACTION; + player->Pushing = TRUE; #if 0 @@ -6722,8 +6950,14 @@ int DigField(struct PlayerInfo *player, if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) return MF_NO_ACTION; + if (player->push_delay == 0) player->push_delay = FrameCounter; + +#if 0 + printf("want push... %d [%d]\n", FrameCounter, player->push_delay_value); +#endif + #if 0 if (!FrameReached(&player->push_delay, player->push_delay_value) && !tape.playing && @@ -6743,15 +6977,29 @@ int DigField(struct PlayerInfo *player, } else { +#if 1 + InitMovingField(x, y, (dx < 0 ? MV_LEFT : + dx > 0 ? MV_RIGHT : + dy < 0 ? MV_UP : MV_DOWN)); + MovPos[x][y] = (dx != 0 ? dx : dy); +#else RemoveField(x, y); Feld[x + dx][y + dy] = element; +#endif } +#if 0 + printf("pushing %d/%d ... %d [%d]\n", dx, dy, + FrameCounter, player->push_delay_value); +#endif + +#if 0 if (element == EL_SPRING) { Feld[x + dx][y + dy] = EL_SPRING; MovDir[x + dx][y + dy] = move_direction; } +#endif player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8)); @@ -6762,6 +7010,9 @@ int DigField(struct PlayerInfo *player, break; +#endif + +#if 0 case EL_GATE_1: case EL_GATE_2: case EL_GATE_3: @@ -6813,7 +7064,9 @@ int DigField(struct PlayerInfo *player, PlaySoundLevel(x, y, SND_GATE_PASSING); #endif break; +#endif +#if 0 case EL_SWITCHGATE_OPEN: case EL_TIMEGATE_OPEN: if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy)) @@ -6825,6 +7078,7 @@ int DigField(struct PlayerInfo *player, PlaySoundLevelElementAction(x, y, element, ACTION_PASSING); break; +#endif case EL_SP_PORT_LEFT: case EL_SP_PORT_RIGHT: @@ -6907,16 +7161,19 @@ int DigField(struct PlayerInfo *player, if (!(tube_enter_directions[i][1] & move_direction)) return MF_NO_ACTION; /* tube has no opening in this direction */ - PlaySoundLevel(x, y, SND_CLASS_TUBE_PASSING); + PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING); } break; +#if 0 case EL_EXIT_CLOSED: case EL_SP_EXIT_CLOSED: case EL_EXIT_OPENING: return MF_NO_ACTION; break; +#endif +#if 0 case EL_EXIT_OPEN: case EL_SP_EXIT_OPEN: if (mode == DF_SNAP) @@ -6928,6 +7185,7 @@ int DigField(struct PlayerInfo *player, PlaySoundLevel(x, y, SND_CLASS_SP_EXIT_PASSING); break; +#endif case EL_LAMP: Feld[x][y] = EL_LAMP_ACTIVE; @@ -6946,6 +7204,8 @@ int DigField(struct PlayerInfo *player, return MF_ACTION; break; +#if 0 + #if 0 case EL_SOKOBAN_FIELD_EMPTY: break; @@ -6985,6 +7245,44 @@ int DigField(struct PlayerInfo *player, if (IS_SB_ELEMENT(element)) { +#if 1 + if (element == EL_SOKOBAN_FIELD_FULL) + { + Back[x][y] = EL_SOKOBAN_FIELD_EMPTY; + local_player->sokobanfields_still_needed++; + } + + if (Feld[x + dx][y + dy] == EL_SOKOBAN_FIELD_EMPTY) + { + Back[x + dx][y + dy] = EL_SOKOBAN_FIELD_EMPTY; + local_player->sokobanfields_still_needed--; + } + + Feld[x][y] = EL_SOKOBAN_OBJECT; + + if (Back[x][y] == Back[x + dx][y + dy]) + PlaySoundLevelAction(x, y, ACTION_PUSHING); + else if (Back[x][y] != 0) + PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL, + ACTION_EMPTYING); + else + PlaySoundLevelElementAction(x + dx, y + dy, EL_SOKOBAN_FIELD_EMPTY, + ACTION_FILLING); + + InitMovingField(x, y, (dx < 0 ? MV_LEFT : + dx > 0 ? MV_RIGHT : + dy < 0 ? MV_UP : MV_DOWN)); + MovPos[x][y] = (dx != 0 ? dx : dy); + +#if 0 + printf("::: %s -> %s [%s -> %s]\n", + element_info[Feld[x][y]].token_name, + element_info[Feld[x + dx][y + dy]].token_name, + element_info[Back[x][y]].token_name, + element_info[Back[x + dx][y + dy]].token_name); +#endif + +#else if (element == EL_SOKOBAN_FIELD_FULL) { Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY; @@ -7026,11 +7324,19 @@ int DigField(struct PlayerInfo *player, PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING); #endif } +#endif } else { +#if 1 + InitMovingField(x, y, (dx < 0 ? MV_LEFT : + dx > 0 ? MV_RIGHT : + dy < 0 ? MV_UP : MV_DOWN)); + MovPos[x][y] = (dx != 0 ? dx : dy); +#else RemoveField(x, y); - Feld[x+dx][y+dy] = element; + Feld[x + dx][y + dy] = element; +#endif PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING); } @@ -7051,16 +7357,70 @@ int DigField(struct PlayerInfo *player, break; +#endif + +#if 0 case EL_PENGUIN: case EL_PIG: case EL_DRAGON: break; +#endif default: if (IS_WALKABLE(element)) { - PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING); + int sound_action = ACTION_WALKING; + + if (element >= EL_GATE_1 && element <= EL_GATE_4) + { + if (!player->key[element - EL_GATE_1]) + return MF_NO_ACTION; + } + else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY) + { + if (!player->key[element - EL_GATE_1_GRAY]) + return MF_NO_ACTION; + } + else if (element == EL_EXIT_OPEN || element == EL_SP_EXIT_OPEN) + { + sound_action = ACTION_PASSING; /* player is passing exit */ + } + else if (element == EL_EMPTY) + { + sound_action = ACTION_MOVING; /* nothing to walk on */ + } + + /* play sound from background or player, whatever is available */ + if (element_info[element].sound[sound_action] != SND_UNDEFINED) + PlaySoundLevelElementAction(x, y, element, sound_action); + else + PlaySoundLevelElementAction(x, y, player->element_nr, sound_action); + + break; + } + else if (IS_PASSABLE(element)) + { + if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy)) + return MF_NO_ACTION; + + if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4) + { + if (!player->key[element - EL_EM_GATE_1]) + return MF_NO_ACTION; + } + else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY) + { + if (!player->key[element - EL_EM_GATE_1_GRAY]) + return MF_NO_ACTION; + } + + /* automatically move to the next field with double speed */ + player->programmed_action = move_direction; + DOUBLE_PLAYER_SPEED(player); + + PlaySoundLevelAction(x, y, ACTION_PASSING); + break; } else if (IS_DIGGABLE(element)) @@ -7088,8 +7448,63 @@ int DigField(struct PlayerInfo *player, player->is_collecting = TRUE; } - RaiseScoreElement(element); + if (element == EL_SPEED_PILL) + player->move_delay_value = MOVE_DELAY_HIGH_SPEED; + else if (element == EL_EXTRA_TIME && level.time > 0) + { + TimeLeft += 10; + DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2); + } + else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY) + { + player->shield_normal_time_left += 10; + if (element == EL_SHIELD_DEADLY) + player->shield_deadly_time_left += 10; + } + else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED) + { + player->dynamite++; + player->use_disk_red_graphic = (element == EL_SP_DISK_RED); + DrawText(DX_DYNAMITE, DY_DYNAMITE, + int2str(local_player->dynamite, 3), FONT_TEXT_2); + } + else if (element == EL_DYNABOMB_INCREASE_NUMBER) + { + player->dynabomb_count++; + player->dynabombs_left++; + } + else if (element == EL_DYNABOMB_INCREASE_SIZE) + { + player->dynabomb_size++; + } + else if (element == EL_DYNABOMB_INCREASE_POWER) + { + player->dynabomb_xl = TRUE; + } + else if ((element >= EL_KEY_1 && element <= EL_KEY_4) || + (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4)) + { + int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ? + element - EL_KEY_1 : element - EL_EM_KEY_1); + + player->key[key_nr] = TRUE; + + DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS, + el2edimg(EL_KEY_1 + key_nr)); + redraw_mask |= REDRAW_DOOR_1; + } + else if (gem_count[element] > 0) + { + local_player->gems_still_needed -= gem_count[element]; + 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); + } + + RaiseScoreElement(element); PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); @@ -7098,19 +7513,29 @@ int DigField(struct PlayerInfo *player, } else if (IS_PUSHABLE(element)) { - if (mode == DF_SNAP) + if (mode == DF_SNAP && element != EL_BD_ROCK) return MF_NO_ACTION; if (CAN_FALL(element) && dy) return MF_NO_ACTION; + if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) && + !(element == EL_SPRING && use_spring_bug)) + return MF_NO_ACTION; + + if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING) + return MF_NO_ACTION; + if (!player->Pushing && game.engine_version >= RELEASE_IDENT(2,2,0,7)) player->push_delay_value = GET_NEW_PUSH_DELAY(element); player->Pushing = TRUE; - if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy)) + if (!(IN_LEV_FIELD(x + dx, y + dy) && + (IS_FREE(x + dx, y + dy) || + (Feld[x + dx][y + dy] == EL_SOKOBAN_FIELD_EMPTY && + IS_SB_ELEMENT(element))))) return MF_NO_ACTION; if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) @@ -7120,21 +7545,57 @@ int DigField(struct PlayerInfo *player, player->push_delay = FrameCounter; if (!FrameReached(&player->push_delay, player->push_delay_value) && - !(tape.playing && tape.file_version < FILE_VERSION_2_0)) + !(tape.playing && tape.file_version < FILE_VERSION_2_0) && + element != EL_SPRING && element != EL_BALLOON) return MF_NO_ACTION; - RemoveField(x, y); - Feld[x + dx][y + dy] = element; + if (IS_SB_ELEMENT(element)) + { + if (element == EL_SOKOBAN_FIELD_FULL) + { + Back[x][y] = EL_SOKOBAN_FIELD_EMPTY; + local_player->sokobanfields_still_needed++; + } + + if (Feld[x + dx][y + dy] == EL_SOKOBAN_FIELD_EMPTY) + { + Back[x + dx][y + dy] = EL_SOKOBAN_FIELD_EMPTY; + local_player->sokobanfields_still_needed--; + } + + Feld[x][y] = EL_SOKOBAN_OBJECT; + + if (Back[x][y] == Back[x + dx][y + dy]) + PlaySoundLevelAction(x, y, ACTION_PUSHING); + else if (Back[x][y] != 0) + PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL, + ACTION_EMPTYING); + else + PlaySoundLevelElementAction(x + dx, y + dy, EL_SOKOBAN_FIELD_EMPTY, + ACTION_FILLING); + + if (local_player->sokobanfields_still_needed == 0 && + game.emulation == EMU_SOKOBAN) + { + player->LevelSolved = player->GameOver = TRUE; + PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING); + } + } + else + PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING); + + InitMovingField(x, y, move_direction); + + if (mode == DF_SNAP) + ContinueMoving(x, y); + else + MovPos[x][y] = (dx != 0 ? dx : dy); + + Pushed[x][y] = TRUE; + Pushed[x + dx][y + dy] = TRUE; -#if 1 if (game.engine_version < RELEASE_IDENT(2,2,0,7)) player->push_delay_value = GET_NEW_PUSH_DELAY(element); -#else - player->push_delay_value = 2 + RND(8); -#endif - - DrawLevelField(x + dx, y + dy); - PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING); CheckTriggeredElementChange(element, CE_OTHER_PUSHING); CheckPlayerElementChange(x, y, element, CE_PUSHED_BY_PLAYER); @@ -7433,6 +7894,7 @@ void RaiseScoreElement(int element) RaiseScore(level.score[SC_NUT]); break; case EL_DYNAMITE: + case EL_SP_DISK_RED: case EL_DYNABOMB_INCREASE_NUMBER: case EL_DYNABOMB_INCREASE_SIZE: case EL_DYNABOMB_INCREASE_POWER: