X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=a18c80bdd1b8d0793488ec55b1d87aa943cdd52b;hb=88c9b68c44a16e7df62557b63cc7e86731e028c9;hp=d2e58439281ec3d5b4b6386f6af5baba3b4f8673;hpb=37a06df577bbfd00f4b361f92cacb0d97036ba93;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index d2e58439..a18c80bd 100644 --- a/src/game.c +++ b/src/game.c @@ -35,6 +35,11 @@ #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1) #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1) #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1) +#define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1) +#define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1) +#define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1) +#define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1) + #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0) /* for DigField() */ @@ -43,9 +48,10 @@ #define DF_SNAP 2 /* for MovePlayer() */ -#define MF_NO_ACTION 0 -#define MF_MOVING 1 -#define MF_ACTION 2 +#define MP_NO_ACTION 0 +#define MP_MOVING 1 +#define MP_ACTION 2 +#define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION) /* for ScrollPlayer() */ #define SCROLL_INIT 0 @@ -136,9 +142,18 @@ #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \ RND((c)->delay_random)) +#if 1 +#define GET_VALID_RUNTIME_ELEMENT(e) \ + ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e)) +#else +#define GET_VALID_FILE_ELEMENT(e) \ + ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e)) +#endif + #define GET_TARGET_ELEMENT(e, ch) \ - ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \ - (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e)) + ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \ + (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \ + (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e)) #define CAN_GROW_INTO(e) \ ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable)) @@ -174,6 +189,13 @@ #define SATELLITE_CAN_ENTER_FIELD(x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0) +#define ANDROID_CAN_ENTER_FIELD(e, x, y) \ + ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT) + +#define ANDROID_CAN_CLONE_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \ + CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT))) + #define ENEMY_CAN_ENTER_FIELD(e, x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0) @@ -201,13 +223,20 @@ #define SPRING_CAN_ENTER_FIELD(e, x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0) +#define SPRING_CAN_BUMP_FROM_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \ + Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE)) + +#if 0 #define GROUP_NR(e) ((e) - EL_GROUP_START) -#define MOVE_ENTER_EL(e) (element_info[e].move_enter_element) #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE) #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START)) #define IS_EQUAL_OR_IN_GROUP(e, ge) \ (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge)) +#endif + +#define MOVE_ENTER_EL(e) (element_info[e].move_enter_element) #define CE_ENTER_FIELD_COND(e, x, y) \ (!IS_PLAYER(x, y) && \ @@ -264,6 +293,8 @@ static void TestIfElementSmashesCustomElement(int, int, int); #endif static void HandleElementChange(int, int, int); +static void ExecuteCustomElementAction(int, int, int, int); +static boolean ChangeElement(int, int, int, int); static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int); #define CheckTriggeredElementChange(x, y, e, ev) \ @@ -321,6 +352,8 @@ static void RunTimegateWheel(int, int); static void InitMagicBallDelay(int, int); static void ActivateMagicBall(int, int); +static void InitDiagonalMovingElement(int, int); + struct ChangingElementInfo { int element; @@ -494,6 +527,30 @@ static struct ChangingElementInfo change_delay_list[] = NULL, ActivateMagicBall }, + { + EL_EMC_SPRING_BUMPER_ACTIVE, + EL_EMC_SPRING_BUMPER, + 8, + NULL, + NULL, + NULL + }, + { + EL_DIAGONAL_SHRINKING, + EL_UNDEFINED, + 0, + NULL, + NULL, + NULL + }, + { + EL_DIAGONAL_GROWING, + EL_UNDEFINED, + 0, + NULL, + NULL, + InitDiagonalMovingElement + }, { EL_UNDEFINED, @@ -1189,6 +1246,7 @@ void DrawGameDoorValues() DrawGameValue_Keys(stored_player[i].key); } +#if 0 static void resolve_group_element(int group_element, int recursion_depth) { static int group_nr; @@ -1232,7 +1290,7 @@ static void resolve_group_element(int group_element, int recursion_depth) } } } - +#endif /* ============================================================================= @@ -1364,6 +1422,7 @@ static void InitGameEngine() printf(" => game.engine_version == %06d\n", game.engine_version); #endif +#if 0 /* ---------- recursively resolve group elements ------------------------- */ for (i = 0; i < MAX_NUM_ELEMENTS; i++) @@ -1372,6 +1431,7 @@ static void InitGameEngine() for (i = 0; i < NUM_GROUP_ELEMENTS; i++) resolve_group_element(EL_GROUP_START + i, 0); +#endif /* ---------- initialize player's initial move delay --------------------- */ @@ -2756,14 +2816,33 @@ static void ResetRandomAnimationValue(int x, int y) static void ResetGfxAnimation(int x, int y) { +#if 0 + int element, graphic; +#endif + GfxFrame[x][y] = 0; GfxAction[x][y] = ACTION_DEFAULT; GfxDir[x][y] = MovDir[x][y]; + +#if 0 + element = Feld[x][y]; + graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + + if (graphic_info[graphic].anim_global_sync) + GfxFrame[x][y] = FrameCounter; + else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) + GfxFrame[x][y] = CustomValue[x][y]; + else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + GfxFrame[x][y] = element_info[element].collect_score; +#endif } void InitMovingField(int x, int y, int direction) { int element = Feld[x][y]; +#if 0 + int graphic; +#endif 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; @@ -2777,6 +2856,17 @@ void InitMovingField(int x, int y, int direction) GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ? ACTION_FALLING : ACTION_MOVING); +#if 0 + graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + + if (graphic_info[graphic].anim_global_sync) + GfxFrame[x][y] = FrameCounter; + else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) + GfxFrame[x][y] = CustomValue[x][y]; + else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + GfxFrame[x][y] = element_info[element].collect_score; +#endif + /* this is needed for CEs with property "can move" / "not moving" */ if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */ @@ -2800,8 +2890,13 @@ void InitMovingField(int x, int y, int direction) void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y) { int direction = MovDir[x][y]; +#if 1 + int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0); + int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0); +#else int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); +#endif *goes_to_x = newx; *goes_to_y = newy; @@ -4650,16 +4745,35 @@ inline static void TurnRoundExt(int x, int y) } else if (element == EL_SPRING) { +#if USE_NEW_SPRING_BUMPER + if (MovDir[x][y] & MV_HORIZONTAL) + { + if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) && + !SPRING_CAN_ENTER_FIELD(element, x, y + 1)) + { + Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE; + ResetGfxAnimation(move_x, move_y); + DrawLevelField(move_x, move_y); + + MovDir[x][y] = back_dir; + } + else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) || + SPRING_CAN_ENTER_FIELD(element, x, y + 1)) + MovDir[x][y] = MV_NONE; + } +#else if (MovDir[x][y] & MV_HORIZONTAL && (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) || SPRING_CAN_ENTER_FIELD(element, x, y + 1))) MovDir[x][y] = MV_NONE; +#endif MovDelay[x][y] = 0; } else if (element == EL_ROBOT || element == EL_SATELLITE || - element == EL_PENGUIN) + element == EL_PENGUIN || + element == EL_EMC_ANDROID) { int attr_x = -1, attr_y = -1; @@ -4760,21 +4874,21 @@ inline static void TurnRoundExt(int x, int y) new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy)) + if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy)) return; MovDir[x][y] = new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy)) + if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy)) return; MovDir[x][y] = old_move_dir; return; } } - else /* (element == EL_SATELLITE) */ + else if (element == EL_SATELLITE) { int newx, newy; @@ -4803,6 +4917,144 @@ inline static void TurnRoundExt(int x, int y) return; } } + else if (element == EL_EMC_ANDROID) + { + static int check_pos[16] = + { + -1, /* 0 => (invalid) */ + 7, /* 1 => MV_LEFT */ + 3, /* 2 => MV_RIGHT */ + -1, /* 3 => (invalid) */ + 1, /* 4 => MV_UP */ + 0, /* 5 => MV_LEFT | MV_UP */ + 2, /* 6 => MV_RIGHT | MV_UP */ + -1, /* 7 => (invalid) */ + 5, /* 8 => MV_DOWN */ + 6, /* 9 => MV_LEFT | MV_DOWN */ + 4, /* 10 => MV_RIGHT | MV_DOWN */ + -1, /* 11 => (invalid) */ + -1, /* 12 => (invalid) */ + -1, /* 13 => (invalid) */ + -1, /* 14 => (invalid) */ + -1, /* 15 => (invalid) */ + }; + static struct + { + int dx, dy; + int dir; + } check_xy[8] = + { + { -1, -1, MV_LEFT | MV_UP }, + { 0, -1, MV_UP }, + { +1, -1, MV_RIGHT | MV_UP }, + { +1, 0, MV_RIGHT }, + { +1, +1, MV_RIGHT | MV_DOWN }, + { 0, +1, MV_DOWN }, + { -1, +1, MV_LEFT | MV_DOWN }, + { -1, 0, MV_LEFT }, + }; + int start_pos, check_order; + boolean can_clone = FALSE; + int i; + + /* check if there is any free field around current position */ + for (i = 0; i < 8; i++) + { + int newx = x + check_xy[i].dx; + int newy = y + check_xy[i].dy; + + if (IN_LEV_FIELD_AND_IS_FREE(newx, newy)) + { + can_clone = TRUE; + + break; + } + } + + if (can_clone) /* randomly find an element to clone */ + { + can_clone = FALSE; + + start_pos = check_pos[RND(8)]; + check_order = (RND(2) ? -1 : +1); + + for (i = 0; i < 8; i++) + { + int pos_raw = start_pos + i * check_order; + int pos = (pos_raw + 8) % 8; + int newx = x + check_xy[pos].dx; + int newy = y + check_xy[pos].dy; + + if (ANDROID_CAN_CLONE_FIELD(newx, newy)) + { + element_info[element].move_leave_type = LEAVE_TYPE_LIMITED; + element_info[element].move_leave_element = EL_TRIGGER_ELEMENT; + + Store[x][y] = Feld[newx][newy]; + + can_clone = TRUE; + + break; + } + } + } + + if (can_clone) /* randomly find a direction to move */ + { + can_clone = FALSE; + + start_pos = check_pos[RND(8)]; + check_order = (RND(2) ? -1 : +1); + + for (i = 0; i < 8; i++) + { + int pos_raw = start_pos + i * check_order; + int pos = (pos_raw + 8) % 8; + int newx = x + check_xy[pos].dx; + int newy = y + check_xy[pos].dy; + int new_move_dir = check_xy[pos].dir; + + if (IN_LEV_FIELD_AND_IS_FREE(newx, newy)) + { + MovDir[x][y] = new_move_dir; + MovDelay[x][y] = level.android_clone_time * 8 + 1; + + can_clone = TRUE; + + break; + } + } + } + + if (can_clone) /* cloning and moving successful */ + return; + + /* cannot clone -- try to move towards player */ + + start_pos = check_pos[MovDir[x][y] & 0x0f]; + check_order = (RND(2) ? -1 : +1); + + for (i = 0; i < 3; i++) + { + /* first check start_pos, then previous/next or (next/previous) pos */ + int pos_raw = start_pos + (i < 2 ? i : -1) * check_order; + int pos = (pos_raw + 8) % 8; + int newx = x + check_xy[pos].dx; + int newy = y + check_xy[pos].dy; + int new_move_dir = check_xy[pos].dir; + + if (IS_PLAYER(newx, newy)) + break; + + if (ANDROID_CAN_ENTER_FIELD(element, newx, newy)) + { + MovDir[x][y] = new_move_dir; + MovDelay[x][y] = level.android_move_time * 8 + 1; + + break; + } + } + } } else if (move_pattern == MV_TURNING_LEFT || move_pattern == MV_TURNING_RIGHT || @@ -5042,6 +5294,9 @@ inline static void TurnRoundExt(int x, int y) static void TurnRound(int x, int y) { int direction = MovDir[x][y]; +#if 1 + int element, graphic; +#endif TurnRoundExt(x, y); @@ -5051,7 +5306,19 @@ static void TurnRound(int x, int y) GfxFrame[x][y] = 0; if (MovDelay[x][y]) - GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction); + GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction); + +#if 1 + element = Feld[x][y]; + graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + + if (graphic_info[graphic].anim_global_sync) + GfxFrame[x][y] = FrameCounter; + else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) + GfxFrame[x][y] = CustomValue[x][y]; + else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) + GfxFrame[x][y] = element_info[element].collect_score; +#endif } static boolean JustBeingPushed(int x, int y) @@ -5550,6 +5817,7 @@ void StartMoving(int x, int y) else if (CAN_MOVE_INTO_ACID(element) && IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID && + !IS_MV_DIAGONAL(MovDir[x][y]) && (MovDir[x][y] == MV_DOWN || game.engine_version >= VERSION_IDENT(3,1,0,0))) { @@ -5576,7 +5844,7 @@ void StartMoving(int x, int y) } else if (IS_FOOD_PENGUIN(Feld[newx][newy])) { - if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING) + if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING) DrawLevelField(newx, newy); else GfxDir[x][y] = MovDir[x][y] = MV_NONE; @@ -5617,6 +5885,97 @@ void StartMoving(int x, int y) return; } } + else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy)) + { + if (Store[x][y] != EL_EMPTY) + { + boolean can_clone = FALSE; + int xx, yy; + + /* check if element to clone is still there */ + for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++) + { + if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y]) + { + can_clone = TRUE; + + break; + } + } + + /* cannot clone or target field not free anymore -- do not clone */ + if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy)) + Store[x][y] = EL_EMPTY; + } + + if (ANDROID_CAN_ENTER_FIELD(element, newx, newy)) + { + if (IS_MV_DIAGONAL(MovDir[x][y])) + { + int diagonal_move_dir = MovDir[x][y]; + int stored = Store[x][y]; + int change_delay = 8; + int graphic; + + /* android is moving diagonally */ + + CreateField(x, y, EL_DIAGONAL_SHRINKING); + + Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored); + GfxElement[x][y] = EL_EMC_ANDROID; + GfxAction[x][y] = ACTION_SHRINKING; + GfxDir[x][y] = diagonal_move_dir; + ChangeDelay[x][y] = change_delay; + + graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], + GfxDir[x][y]); + + DrawLevelGraphicAnimation(x, y, graphic); + PlayLevelSoundAction(x, y, ACTION_SHRINKING); + + if (Feld[newx][newy] == EL_ACID) + { + SplashAcid(newx, newy); + + return; + } + + CreateField(newx, newy, EL_DIAGONAL_GROWING); + + Store[newx][newy] = EL_EMC_ANDROID; + GfxElement[newx][newy] = EL_EMC_ANDROID; + GfxAction[newx][newy] = ACTION_GROWING; + GfxDir[newx][newy] = diagonal_move_dir; + ChangeDelay[newx][newy] = change_delay; + + graphic = el_act_dir2img(GfxElement[newx][newy], + GfxAction[newx][newy], GfxDir[newx][newy]); + + DrawLevelGraphicAnimation(newx, newy, graphic); + PlayLevelSoundAction(newx, newy, ACTION_GROWING); + + return; + } + else + { + Feld[newx][newy] = EL_EMPTY; + DrawLevelField(newx, newy); + + PlayLevelSoundAction(x, y, ACTION_DIGGING); + } + } + else if (!IS_FREE(newx, newy)) + { +#if 0 + if (IS_PLAYER(x, y)) + DrawPlayerField(x, y); + else + DrawLevelField(x, y); +#endif + + return; + } + } else if (IS_CUSTOM_ELEMENT(element) && CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)) { @@ -6018,16 +6377,28 @@ void ContinueMoving(int x, int y) Pushed[x][y] = Pushed[newx][newy] = FALSE; /* some elements can leave other elements behind after moving */ +#if 1 + if (ei->move_leave_element != EL_EMPTY && + (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) && + (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element))) +#else if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY && (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) && (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element))) +#endif { int move_leave_element = ei->move_leave_element; +#if 1 #if 1 + /* this makes it possible to leave the removed element again */ + if (ei->move_leave_element == EL_TRIGGER_ELEMENT) + move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored); +#else /* this makes it possible to leave the removed element again */ if (ei->move_leave_element == EL_TRIGGER_ELEMENT) move_leave_element = stored; +#endif #else /* this makes it possible to leave the removed element again */ if (ei->move_leave_type == LEAVE_TYPE_LIMITED && @@ -6118,10 +6489,35 @@ void ContinueMoving(int x, int y) player->index_bit, push_side); } + if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */ + MovDelay[newx][newy] = 1; + CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction); TestIfElementTouchesCustomElement(x, y); /* empty or new element */ +#if 0 + if (ChangePage[newx][newy] != -1) /* delayed change */ + { + int page = ChangePage[newx][newy]; + struct ElementChangeInfo *change = &ei->change_page[page]; + + ChangePage[newx][newy] = -1; + + if (change->can_change) + { + if (ChangeElement(newx, newy, element, page)) + { + if (change->post_change_function) + change->post_change_function(newx, newy); + } + } + + if (change->has_action) + ExecuteCustomElementAction(newx, newy, element, page); + } +#endif + TestIfElementHitsCustomElement(newx, newy, direction); TestIfPlayerTouchesCustomElement(newx, newy); TestIfElementTouchesCustomElement(newx, newy); @@ -6395,6 +6791,7 @@ void AmoebeAbleger(int ax, int ay) int element = Feld[ax][ay]; int graphic = el2img(element); int newax = ax, neway = ay; + boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER); static int xy[4][2] = { { 0, -1 }, @@ -6403,7 +6800,7 @@ void AmoebeAbleger(int ax, int ay) { 0, +1 } }; - if (!level.amoeba_speed) + if (!level.amoeba_speed && element != EL_EMC_DRIPPER) { Feld[ax][ay] = EL_AMOEBA_DEAD; DrawLevelField(ax, ay); @@ -6423,7 +6820,7 @@ void AmoebeAbleger(int ax, int ay) return; } - if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */ + if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */ { int start = RND(4); int x = ax + xy[start][0]; @@ -6517,13 +6914,13 @@ void AmoebeAbleger(int ax, int ay) } } - if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) || + if (!can_drop || neway < ay || !IS_FREE(newax, neway) || (neway == lev_fieldy - 1 && newax != ax)) { Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */ Store[newax][neway] = element; } - else if (neway == ay) + else if (neway == ay || element == EL_EMC_DRIPPER) { Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */ @@ -6691,6 +7088,13 @@ static void ActivateMagicBall(int bx, int by) game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents; } +static void InitDiagonalMovingElement(int x, int y) +{ +#if 0 + MovDelay[x][y] = level.android_move_time; +#endif +} + void CheckExit(int x, int y) { if (local_player->gems_still_needed > 0 || @@ -7161,8 +7565,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value : action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed : action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score : - action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) : - action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value : + action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) : + action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value: + action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element : + action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element : -1); int action_arg_number_old = @@ -7453,6 +7859,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO); CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X); + +#if 0 + printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]); +#endif } #endif @@ -7539,6 +7949,21 @@ static void CreateField(int x, int y, int element) static void CreateElementFromChange(int x, int y, int element) { + element = GET_VALID_RUNTIME_ELEMENT(element); + +#if USE_STOP_CHANGED_ELEMENTS + if (game.engine_version >= VERSION_IDENT(3,2,0,7)) + { + int old_element = Feld[x][y]; + + /* prevent changed element from moving in same engine frame + unless both old and new element can either fall or move */ + if ((!CAN_FALL(old_element) || !CAN_FALL(element)) && + (!CAN_MOVE(old_element) || !CAN_MOVE(element))) + Stop[x][y] = TRUE; + } +#endif + CreateFieldExt(x, y, element, TRUE); } @@ -7686,6 +8111,14 @@ static boolean ChangeElement(int x, int y, int element, int page) { target_element = GET_TARGET_ELEMENT(change->target_element, change); + if (element == EL_DIAGONAL_GROWING || + element == EL_DIAGONAL_SHRINKING) + { + target_element = Store[x][y]; + + Store[x][y] = EL_EMPTY; + } + CreateElementFromChange(x, y, target_element); PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING); @@ -7979,8 +8412,20 @@ static boolean CheckElementChangeExt(int x, int y, element = Feld[x][y]; } - if (Feld[x][y] != element) /* check if element has already changed */ +#if 0 + /* check if element has already changed */ + if (Feld[x][y] != element) return FALSE; +#else + /* check if element has already changed or is about to change after moving */ + if ((game.engine_version < VERSION_IDENT(3,2,0,7) && + Feld[x][y] != element) || + + (game.engine_version >= VERSION_IDENT(3,2,0,7) && + (ChangeCount[x][y] >= game.max_num_changes_per_frame || + ChangePage[x][y] != -1))) + return FALSE; +#endif for (p = 0; p < element_info[element].num_change_pages; p++) { @@ -8345,9 +8790,20 @@ void GameActions() /* ---------- main game synchronization point ---------- */ + WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value); + InitPlayfieldScanModeVars(); - WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value); +#if USE_ONE_MORE_CHANGE_PER_FRAME + if (game.engine_version >= VERSION_IDENT(3,2,0,7)) + { + SCAN_PLAYFIELD(x, y) + { + ChangeCount[x][y] = 0; + ChangeEvent[x][y] = -1; + } + } +#endif if (network_playing && !network_player_action_received) { @@ -8528,7 +8984,7 @@ void GameActions() GfxFrame[x][y]++; /* reset finished pushing action (not done in ContinueMoving() to allow - continous pushing animation for elements with zero push delay) */ + continuous pushing animation for elements with zero push delay) */ if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y)) { ResetGfxAnimation(x, y); @@ -8699,7 +9155,9 @@ void GameActions() CheckForDragon(x, y); else if (element == EL_EXPLOSION) ; /* drawing of correct explosion animation is handled separately */ - else if (element == EL_ELEMENT_SNAPPING) + else if (element == EL_ELEMENT_SNAPPING || + element == EL_DIAGONAL_SHRINKING || + element == EL_DIAGONAL_GROWING) { #if 1 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]); @@ -9126,12 +9584,14 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, { int jx = player->jx, jy = player->jy; int new_jx = jx + dx, new_jy = jy + dy; +#if !USE_FIXED_DONT_RUN_INTO int element; +#endif int can_move; boolean player_can_move = !player->cannot_move; if (!player->active || (!dx && !dy)) - return MF_NO_ACTION; + return MP_NO_ACTION; player->MovDir = (dx < 0 ? MV_LEFT : dx > 0 ? MV_RIGHT : @@ -9139,7 +9599,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, dy > 0 ? MV_DOWN : MV_NONE); if (!IN_LEV_FIELD(new_jx, new_jy)) - return MF_NO_ACTION; + return MP_NO_ACTION; if (!player_can_move) { @@ -9158,15 +9618,17 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, #endif #if 0 - return MF_NO_ACTION; + return MP_NO_ACTION; #endif } if (!options.network && !AllPlayersInSight(player, new_jx, new_jy)) - return MF_NO_ACTION; + return MP_NO_ACTION; +#if !USE_FIXED_DONT_RUN_INTO element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy); + /* (moved to DigField()) */ if (player_can_move && DONT_RUN_INTO(element)) { if (element == EL_ACID && dx == 0 && dy == 1) @@ -9181,16 +9643,26 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, else TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); - return MF_MOVING; + return MP_MOVING; } +#endif can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG); - if (can_move != MF_MOVING) +#if 0 +#if USE_FIXED_DONT_RUN_INTO + if (can_move == MP_DONT_RUN_INTO) + return MP_MOVING; +#endif +#endif + if (can_move != MP_MOVING) return can_move; +#if USE_FIXED_DONT_RUN_INTO +#endif + /* check if DigField() has caused relocation of the player */ if (player->jx != jx || player->jy != jy) - return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */ + return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */ StorePlayer[jx][jy] = 0; player->last_jx = jx; @@ -9214,14 +9686,14 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, ScrollPlayer(player, SCROLL_INIT); - return MF_MOVING; + return MP_MOVING; } boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) { int jx = player->jx, jy = player->jy; int old_jx = jx, old_jy = jy; - int moved = MF_NO_ACTION; + int moved = MP_NO_ACTION; if (!player->active) return FALSE; @@ -9294,7 +9766,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) jx = player->jx; jy = player->jy; - if (moved & MF_MOVING && !ScreenMovPos && + if (moved & MP_MOVING && !ScreenMovPos && (player == local_player || !options.network)) { int old_scroll_x = scroll_x, old_scroll_y = scroll_y; @@ -9365,7 +9837,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->StepFrame = 0; - if (moved & MF_MOVING) + if (moved & MP_MOVING) { if (old_jx != jx && old_jy == jy) player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT); @@ -9873,6 +10345,7 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction) void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) { int i, kill_x = -1, kill_y = -1; + int bad_element = -1; static int test_xy[4][2] = { @@ -10165,7 +10638,7 @@ void RemovePlayer(struct PlayerInfo *player) static void setFieldForSnapping(int x, int y, int element, int direction) { struct ElementInfo *ei = &element_info[element]; - int direction_bit = MV_DIR_BIT(direction); + int direction_bit = MV_DIR_TO_BIT(direction); int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit]; int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING : IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING); @@ -10225,7 +10698,8 @@ int DigField(struct PlayerInfo *player, { boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG); boolean player_was_pushing = player->is_pushing; - boolean player_can_enter = (!player->cannot_move || mode == DF_SNAP); + boolean player_can_move = (!player->cannot_move && mode != DF_SNAP); + boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP); int jx = oldx, jy = oldy; int dx = x - jx, dy = y - jy; int nextx = x + dx, nexty = y + dy; @@ -10236,7 +10710,11 @@ int DigField(struct PlayerInfo *player, int opposite_direction = MV_DIR_OPPOSITE(move_direction); int dig_side = MV_DIR_OPPOSITE(move_direction); int old_element = Feld[jx][jy]; +#if USE_FIXED_DONT_RUN_INTO + int element = MovingOrBlocked2ElementIfNotLeaving(x, y); +#else int element; +#endif int collect_count; if (is_player) /* function can also be called by EL_PENGUIN */ @@ -10255,12 +10733,14 @@ int DigField(struct PlayerInfo *player, player->is_switching = FALSE; player->push_delay = -1; - return MF_NO_ACTION; + return MP_NO_ACTION; } } +#if !USE_FIXED_DONT_RUN_INTO if (IS_MOVING(x, y) || IS_PLAYER(x, y)) - return MF_NO_ACTION; + return MP_NO_ACTION; +#endif if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)) old_element = Back[jx][jy]; @@ -10270,36 +10750,97 @@ int DigField(struct PlayerInfo *player, game.engine_version >= VERSION_IDENT(2,2,0,0)) old_element = Back[jx][jy]; +#if 0 +#if USE_FIXED_DONT_RUN_INTO + if (player_can_move && DONT_RUN_INTO(element)) + { + if (element == EL_ACID && dx == 0 && dy == 1) + { + SplashAcid(x, y); + Feld[jx][jy] = EL_PLAYER_1; + InitMovingField(jx, jy, MV_DOWN); + Store[jx][jy] = EL_ACID; + ContinueMoving(jx, jy); + BuryPlayer(player); + } + else + TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); + + return MP_DONT_RUN_INTO; + } +#endif +#endif + +#if 1 +#if USE_FIXED_DONT_RUN_INTO + if (player_can_move && DONT_RUN_INTO(element)) + { + TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); + + return MP_DONT_RUN_INTO; + } +#endif +#endif + if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction)) - return MF_NO_ACTION; /* field has no opening in this direction */ + return MP_NO_ACTION; /* field has no opening in this direction */ if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction)) - return MF_NO_ACTION; /* field has no opening in this direction */ - - element = Feld[x][y]; -#if USE_NEW_CUSTOM_VALUE + return MP_NO_ACTION; /* field has no opening in this direction */ #if 1 - collect_count = element_info[element].collect_count_initial; -#else - collect_count = CustomValue[x][y]; -#endif +#if USE_FIXED_DONT_RUN_INTO + if (player_can_move && element == EL_ACID && move_direction == MV_DOWN) + { + SplashAcid(x, y); + Feld[jx][jy] = EL_PLAYER_1; + InitMovingField(jx, jy, MV_DOWN); + Store[jx][jy] = EL_ACID; + ContinueMoving(jx, jy); + BuryPlayer(player); -#else - collect_count = element_info[element].collect_count_initial; + return MP_DONT_RUN_INTO; + } +#endif #endif #if 0 - if (element != EL_BLOCKED && - CustomValue[x][y] != element_info[element].collect_count_initial) - printf("::: %d: %d != %d\n", - element, - CustomValue[x][y], - element_info[element].collect_count_initial); +#if USE_FIXED_DONT_RUN_INTO + if (player_can_move && DONT_RUN_INTO(element)) + { + if (element == EL_ACID && dx == 0 && dy == 1) + { + SplashAcid(x, y); + Feld[jx][jy] = EL_PLAYER_1; + InitMovingField(jx, jy, MV_DOWN); + Store[jx][jy] = EL_ACID; + ContinueMoving(jx, jy); + BuryPlayer(player); + } + else + TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); + + return MP_DONT_RUN_INTO; + } +#endif #endif +#if USE_FIXED_DONT_RUN_INTO + if (IS_MOVING(x, y) || IS_PLAYER(x, y)) + return MP_NO_ACTION; +#endif + +#if !USE_FIXED_DONT_RUN_INTO + element = Feld[x][y]; +#endif + + collect_count = element_info[element].collect_count_initial; + if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */ - return MF_NO_ACTION; + return MP_NO_ACTION; + + if (game.engine_version < VERSION_IDENT(2,2,0,0)) + player_can_move = player_can_move_or_snap; if (mode == DF_SNAP && !IS_SNAPPABLE(element) && game.engine_version >= VERSION_IDENT(2,2,0,0)) @@ -10310,17 +10851,17 @@ int DigField(struct PlayerInfo *player, player->index_bit, dig_side); if (Feld[x][y] != element) /* field changed by snapping */ - return MF_ACTION; + return MP_ACTION; - return MF_NO_ACTION; + return MP_NO_ACTION; } if (game.gravity && is_player && !player->is_auto_moving && canFallDown(player) && move_direction != MV_DOWN && !canMoveToValidFieldWithGravity(jx, jy, move_direction)) - return MF_NO_ACTION; /* player cannot walk here due to gravity */ + return MP_NO_ACTION; /* player cannot walk here due to gravity */ - if (player_can_enter && + if (player_can_move && IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction)) { int sound_element = SND_ELEMENT(element); @@ -10329,17 +10870,17 @@ int DigField(struct PlayerInfo *player, if (IS_RND_GATE(element)) { if (!player->key[RND_GATE_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; } else if (IS_RND_GATE_GRAY(element)) { if (!player->key[RND_GATE_GRAY_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; } else if (IS_RND_GATE_GRAY_ACTIVE(element)) { if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; } else if (element == EL_EXIT_OPEN || element == EL_SP_EXIT_OPEN || @@ -10358,29 +10899,29 @@ int DigField(struct PlayerInfo *player, else PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action); } - else if (player_can_enter && + else if (player_can_move && IS_PASSABLE(element) && canPassField(x, y, move_direction)) { if (!ACCESS_FROM(element, opposite_direction)) - return MF_NO_ACTION; /* field not accessible from this direction */ + return MP_NO_ACTION; /* field not accessible from this direction */ if (CAN_MOVE(element)) /* only fixed elements can be passed! */ - return MF_NO_ACTION; + return MP_NO_ACTION; if (IS_EM_GATE(element)) { if (!player->key[EM_GATE_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; } else if (IS_EM_GATE_GRAY(element)) { if (!player->key[EM_GATE_GRAY_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; } else if (IS_EM_GATE_GRAY_ACTIVE(element)) { if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)]) - return MF_NO_ACTION; + return MP_NO_ACTION; } else if (IS_SP_PORT(element)) { @@ -10413,7 +10954,7 @@ int DigField(struct PlayerInfo *player, PlayLevelSoundAction(x, y, ACTION_PASSING); } - else if (player_can_enter && IS_DIGGABLE(element)) + else if (player_can_move_or_snap && IS_DIGGABLE(element)) { RemoveField(x, y); @@ -10443,7 +10984,7 @@ int DigField(struct PlayerInfo *player, player->index_bit, dig_side); } } - else if (player_can_enter && IS_COLLECTIBLE(element)) + else if (player_can_move_or_snap && IS_COLLECTIBLE(element)) { RemoveField(x, y); @@ -10557,17 +11098,17 @@ int DigField(struct PlayerInfo *player, player->index_bit, dig_side); } } - else if (player_can_enter && IS_PUSHABLE(element)) + else if (player_can_move_or_snap && IS_PUSHABLE(element)) { if (mode == DF_SNAP && element != EL_BD_ROCK) - return MF_NO_ACTION; + return MP_NO_ACTION; if (CAN_FALL(element) && dy) - return MF_NO_ACTION; + return MP_NO_ACTION; if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) && !(element == EL_SPRING && level.use_spring_bug)) - return MF_NO_ACTION; + return MP_NO_ACTION; if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 && ((move_direction & MV_VERTICAL && @@ -10580,12 +11121,12 @@ int DigField(struct PlayerInfo *player, IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) || (element_info[element].move_pattern & MV_DOWN && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1)))))) - return MF_NO_ACTION; + return MP_NO_ACTION; /* do not push elements already moving away faster than player */ if (CAN_MOVE(element) && MovDir[x][y] == move_direction && ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL) - return MF_NO_ACTION; + return MP_NO_ACTION; if (game.engine_version >= VERSION_IDENT(3,1,0,0)) { @@ -10609,10 +11150,10 @@ int DigField(struct PlayerInfo *player, (IS_FREE(nextx, nexty) || (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY && IS_SB_ELEMENT(element))))) - return MF_NO_ACTION; + return MP_NO_ACTION; if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) - return MF_NO_ACTION; + return MP_NO_ACTION; if (player->push_delay == -1) /* new pushing; restart delay */ player->push_delay = 0; @@ -10625,7 +11166,7 @@ int DigField(struct PlayerInfo *player, if (game.engine_version >= VERSION_IDENT(3,0,7,1)) player->move_delay = 0; - return MF_NO_ACTION; + return MP_NO_ACTION; } if (IS_SB_ELEMENT(element)) @@ -10695,7 +11236,7 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); - return MF_ACTION; + return MP_ACTION; } player->is_switching = TRUE; @@ -10822,7 +11363,7 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); - return MF_ACTION; + return MP_ACTION; } else { @@ -10848,7 +11389,7 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X, player->index_bit, dig_side); - return MF_NO_ACTION; + return MP_NO_ACTION; } player->push_delay = -1; @@ -10859,7 +11400,7 @@ int DigField(struct PlayerInfo *player, player->is_collecting = !player->is_digging; } - return MF_MOVING; + return MP_MOVING; } boolean SnapField(struct PlayerInfo *player, int dx, int dy) @@ -10911,7 +11452,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) player->is_dropping = FALSE; - if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION) + if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION) return FALSE; player->is_snapping = TRUE; @@ -10950,7 +11491,7 @@ boolean DropElement(struct PlayerInfo *player) element can be dropped (this is especially important if the next element is dynamite, which can be placed on background for historical reasons) */ if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY) - return MF_ACTION; + return MP_ACTION; if (IS_THROWABLE(drop_element)) {