#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_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
/* for DigField() */
#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
#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 ENEMY_CAN_ENTER_FIELD(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
#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))
+
#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)
/* forward declaration for internal use */
+static void CreateField(int, int, int);
+
static void SetPlayerWaiting(struct PlayerInfo *, boolean);
static void AdvanceFrameAndPlayerCounters(int);
static void TestIfElementSmashesCustomElement(int, int, int);
#endif
-static void ChangeElement(int, int, int);
+static void HandleElementChange(int, int, int);
static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
#define CheckTriggeredElementChange(x, y, e, ev) \
/* ------------------------------------------------------------------------- */
/* forward declaration for changer functions */
-static void InitBuggyBase(int x, int y);
-static void WarnBuggyBase(int x, int y);
+static void InitBuggyBase(int, int);
+static void WarnBuggyBase(int, int);
+
+static void InitTrap(int, int);
+static void ActivateTrap(int, int);
+static void ChangeActiveTrap(int, int);
-static void InitTrap(int x, int y);
-static void ActivateTrap(int x, int y);
-static void ChangeActiveTrap(int x, int y);
+static void InitRobotWheel(int, int);
+static void RunRobotWheel(int, int);
+static void StopRobotWheel(int, int);
-static void InitRobotWheel(int x, int y);
-static void RunRobotWheel(int x, int y);
-static void StopRobotWheel(int x, int y);
+static void InitTimegateWheel(int, int);
+static void RunTimegateWheel(int, int);
-static void InitTimegateWheel(int x, int y);
-static void RunTimegateWheel(int x, int y);
+static void InitMagicBallDelay(int, int);
+static void ActivateMagicBall(int, int);
+
+static void InitDiagonalMovingElement(int, int);
struct ChangingElementInfo
{
RunTimegateWheel,
NULL
},
+ {
+ EL_EMC_MAGIC_BALL_ACTIVE,
+ EL_EMC_MAGIC_BALL_ACTIVE,
+ 0,
+ InitMagicBallDelay,
+ NULL,
+ ActivateMagicBall
+ },
+ {
+ EL_EMC_SPRING_BUMPER_ACTIVE,
+ EL_EMC_SPRING_BUMPER,
+ 8,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_DIAGONAL_SHRINKING,
+ EL_EMPTY,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_DIAGONAL_GROWING,
+ EL_EMPTY,
+ 0,
+ NULL,
+ NULL,
+ InitDiagonalMovingElement
+ },
{
EL_UNDEFINED,
InitPlayfieldScanModeVars();
}
+static int get_move_delay_from_stepsize(int move_stepsize)
+{
+ move_stepsize =
+ MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
+
+ /* make sure that stepsize value is always a power of 2 */
+ move_stepsize = (1 << log_2(move_stepsize));
+
+ return TILEX / move_stepsize;
+}
+
+static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
+ boolean init_game)
+{
+ int move_delay = get_move_delay_from_stepsize(move_stepsize);
+ boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
+
+ /* do no immediately change move delay -- the player might just be moving */
+ player->move_delay_value_next = move_delay;
+
+ /* information if player can move must be set separately */
+ player->cannot_move = cannot_move;
+
+ if (init_game)
+ {
+ player->move_delay = game.initial_move_delay;
+ player->move_delay_value = game.initial_move_delay_value;
+
+ player->move_delay_value_next = -1;
+
+ player->move_delay_reset_counter = 0;
+ }
+}
+
void GetPlayerConfig()
{
if (!audio.sound_available)
game.light_time_left = level.time_light * FRAMES_PER_SECOND;
break;
+ case EL_EMC_MAGIC_BALL:
+ if (game.ball_state)
+ Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
+ break;
+
+ case EL_EMC_MAGIC_BALL_SWITCH:
+ if (game.ball_state)
+ Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
+ break;
+
default:
#if 1
if (IS_CUSTOM_ELEMENT(element))
/* ---------- initialize player's initial move delay --------------------- */
+#if 1
+ /* dynamically adjust player properties according to level information */
+ game.initial_move_delay_value =
+ get_move_delay_from_stepsize(level.initial_player_stepsize);
+#else
/* dynamically adjust player properties according to level information */
game.initial_move_delay_value =
(level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+#endif
/* dynamically adjust player properties according to game engine version */
game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
player->is_bored = FALSE;
player->is_sleeping = FALSE;
- player->cannot_move = FALSE;
-
player->frame_counter_bored = -1;
player->frame_counter_sleeping = -1;
player->show_envelope = 0;
+#if 1
+ SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
+#else
player->move_delay = game.initial_move_delay;
player->move_delay_value = game.initial_move_delay_value;
player->move_delay_reset_counter = 0;
+ player->cannot_move = FALSE;
+#endif
+
player->push_delay = -1; /* initialized when pushing starts */
player->push_delay_value = game.initial_push_delay_value;
game.lenses_time_left = 0;
game.magnify_time_left = 0;
+ game.ball_state = level.ball_state_initial;
+ game.ball_content_nr = 0;
+
game.envelope_active = FALSE;
for (i = 0; i < NUM_BELTS; i++)
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;
}
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;
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;
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 check_order = (RND(2) ? -1 : +1);
+ int start_pos = check_pos[MovDir[x][y] & 0x0f];
+ int i;
+
+ MovDelay[x][y] = level.android_move_time * 8 + 1;
+
+ if (start_pos < 0) /* (should never happen) */
+ return;
+
+ 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))
+ return;
+
+ if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+ {
+ MovDir[x][y] = new_move_dir;
+
+ return;
+ }
+ }
+ }
}
else if (move_pattern == MV_TURNING_LEFT ||
move_pattern == MV_TURNING_RIGHT ||
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);
}
static boolean JustBeingPushed(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;
return;
}
}
+ else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
+ {
+ if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL &&
+ ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+ {
+ int diagonal_move_dir = MovDir[x][y];
+ int change_delay = 8;
+ int graphic;
+
+ /* android is moving diagonally */
+
+ CreateField(x, y, EL_DIAGONAL_SHRINKING);
+
+ 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 0
+ CheckTriggeredElementChangeBySide(x, y, element,
+ CE_MOVE_OF_X, new_move_dir);
+
+ TestIfElementTouchesCustomElement(x, y); /* empty or new element */
+
+ TestIfElementHitsCustomElement(newx, newy, new_move_dir);
+ TestIfElementTouchesCustomElement(newx, newy);
+#endif
+
+ 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 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+ {
+ 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))
{
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 */
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 },
{ 0, +1 }
};
- if (!level.amoeba_speed)
+ if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
{
Feld[ax][ay] = EL_AMOEBA_DEAD;
DrawLevelField(ax, 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];
}
}
- 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 */
PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
}
+static void InitMagicBallDelay(int x, int y)
+{
+#if 1
+ ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
+#else
+ ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
+#endif
+}
+
+static void ActivateMagicBall(int bx, int by)
+{
+ int x, y;
+
+ if (level.ball_random)
+ {
+ int pos_border = RND(8); /* select one of the eight border elements */
+ int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
+ int xx = pos_content % 3;
+ int yy = pos_content / 3;
+
+ x = bx - 1 + xx;
+ y = by - 1 + yy;
+
+ if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
+ CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
+ }
+ else
+ {
+ for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
+ {
+ int xx = x - bx + 1;
+ int yy = y - by + 1;
+
+ if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
+ CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
+ }
+ }
+
+ 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 ||
MV_NONE);
int action_arg_number_min =
- (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
+ (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
CA_ARG_MIN);
int action_arg_number_max =
- (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
+ (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
action_type == CA_SET_LEVEL_GEMS ? 999 :
action_type == CA_SET_LEVEL_TIME ? 9999 :
action_type == CA_SET_LEVEL_SCORE ? 99999 :
CA_ARG_MAX);
int action_arg_number_reset =
- (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
+ (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
action_type == CA_SET_LEVEL_TIME ? level.time :
action_type == CA_SET_LEVEL_SCORE ? 0 :
{
int move_stepsize = TILEX / stored_player[i].move_delay_value;
- if (action_arg == CA_ARG_SPEED_SLOWER ||
- action_arg == CA_ARG_SPEED_FASTER)
+ if (action_arg == CA_ARG_SPEED_FASTER &&
+ stored_player[i].cannot_move)
+ {
+ action_arg_number = STEPSIZE_VERY_SLOW;
+ }
+ else if (action_arg == CA_ARG_SPEED_SLOWER ||
+ action_arg == CA_ARG_SPEED_FASTER)
{
action_arg_number = 2;
action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
action_arg_number_min,
action_arg_number_max);
+#if 1
+ SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
+#else
/* make sure that value is power of 2 */
move_stepsize = (1 << log_2(move_stepsize));
stored_player[i].cannot_move =
(action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
+#endif
}
}
}
}
-static void ChangeElementNowExt(struct ElementChangeInfo *change,
- int x, int y, int target_element)
+static void CreateFieldExt(int x, int y, int element, boolean is_change)
{
int previous_move_direction = MovDir[x][y];
#if USE_NEW_CUSTOM_VALUE
int last_ce_value = CustomValue[x][y];
#endif
- boolean add_player = (ELEM_IS_PLAYER(target_element) &&
+ boolean add_player = (ELEM_IS_PLAYER(element) &&
IS_WALKABLE(Feld[x][y]));
/* check if element under player changes from accessible to unaccessible
(needed for special case of dropping element which then changes) */
if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
- IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
+ IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
{
Bang(x, y);
+
return;
}
else
RemoveField(x, y);
- Feld[x][y] = target_element;
+ Feld[x][y] = element;
ResetGfxAnimation(x, y);
ResetRandomAnimationValue(x, y);
}
/* "ChangeCount" not set yet to allow "entered by player" change one time */
- if (ELEM_IS_PLAYER(target_element))
- RelocatePlayer(x, y, target_element);
+ if (ELEM_IS_PLAYER(element))
+ RelocatePlayer(x, y, element);
- ChangeCount[x][y]++; /* count number of changes in the same frame */
+ if (is_change)
+ ChangeCount[x][y]++; /* count number of changes in the same frame */
TestIfBadThingTouchesPlayer(x, y);
TestIfPlayerTouchesCustomElement(x, y);
TestIfElementTouchesCustomElement(x, y);
}
-static boolean ChangeElementNow(int x, int y, int element, int page)
+static void CreateField(int x, int y, int element)
+{
+ CreateFieldExt(x, y, element, FALSE);
+}
+
+static void CreateElementFromChange(int x, int y, int element)
+{
+ CreateFieldExt(x, y, element, TRUE);
+}
+
+static boolean ChangeElement(int x, int y, int element, int page)
{
struct ElementChangeInfo *change = &element_info[element].change_page[page];
int target_element;
content_element = change->target_content.e[xx][yy];
target_element = GET_TARGET_ELEMENT(content_element, change);
- ChangeElementNowExt(change, ex, ey, target_element);
+ CreateElementFromChange(ex, ey, target_element);
something_has_changed = TRUE;
{
target_element = GET_TARGET_ELEMENT(change->target_element, change);
- ChangeElementNowExt(change, x, y, target_element);
+ if (element == EL_DIAGONAL_GROWING)
+ target_element = Store[x][y];
+
+ CreateElementFromChange(x, y, target_element);
PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
#if USE_NEW_DELAYED_ACTION
-static void ChangeElement(int x, int y, int page)
+static void HandleElementChange(int x, int y, int page)
{
int element = MovingOrBlocked2Element(x, y);
struct ElementInfo *ei = &element_info[element];
!CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
{
printf("\n\n");
- printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
+ printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
x, y, element, element_info[element].token_name);
- printf("ChangeElement(): This should never happen!\n");
+ printf("HandleElementChange(): This should never happen!\n");
printf("\n\n");
}
#endif
if (change->can_change)
{
- if (ChangeElementNow(x, y, element, page))
+ if (ChangeElement(x, y, element, page))
{
if (change->post_change_function)
change->post_change_function(x, y);
#else
-static void ChangeElement(int x, int y, int page)
+static void HandleElementChange(int x, int y, int page)
{
int element = MovingOrBlocked2Element(x, y);
struct ElementInfo *ei = &element_info[element];
if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
{
printf("\n\n");
- printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
+ printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
x, y, element, element_info[element].token_name);
- printf("ChangeElement(): This should never happen!\n");
+ printf("HandleElementChange(): This should never happen!\n");
printf("\n\n");
}
#endif
return;
}
- if (ChangeElementNow(x, y, element, page))
+ if (ChangeElement(x, y, element, page))
{
if (change->post_change_function)
change->post_change_function(x, y);
{
ChangeDelay[x][y] = 1;
ChangeEvent[x][y] = trigger_event;
- ChangeElement(x, y, p);
+
+ HandleElementChange(x, y, p);
}
#if USE_NEW_DELAYED_ACTION
else if (change->has_action)
{
ChangeDelay[x][y] = 1;
ChangeEvent[x][y] = trigger_event;
- ChangeElement(x, y, p);
+
+ HandleElementChange(x, y, p);
change_done = TRUE;
}
/* ---------- 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)
{
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);
{
int page = element_info[element].event_page_nr[CE_DELAY];
#if 0
- ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
+ HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
#else
#if 0
#endif
#if 1
- ChangeElement(x, y, page);
+ HandleElementChange(x, y, page);
#else
if (CAN_CHANGE(element))
- ChangeElement(x, y, page);
+ HandleElementChange(x, y, page);
if (HAS_ACTION(element))
ExecuteCustomElementAction(x, y, element, page);
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]);
{
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 :
dy > 0 ? MV_DOWN : MV_NONE);
if (!IN_LEV_FIELD(new_jx, new_jy))
- return MF_NO_ACTION;
+ return MP_NO_ACTION;
- if (player->cannot_move)
+ if (!player_can_move)
{
#if 1
if (player->MovPos == 0)
SnapField(player, 0, 0);
#endif
- return MF_NO_ACTION;
+#if 0
+ 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);
- if (DONT_RUN_INTO(element))
+ /* (moved to DigField()) */
+ if (player_can_move && DONT_RUN_INTO(element))
{
if (element == EL_ACID && dx == 0 && dy == 1)
{
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;
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;
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;
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);
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] =
{
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);
{
boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
boolean player_was_pushing = player->is_pushing;
+ 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;
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 */
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];
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))
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 (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
+ if (player_can_move &&
+ IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
{
int sound_element = SND_ELEMENT(element);
int sound_action = ACTION_WALKING;
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 ||
else
PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
}
- else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
+ 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))
{
PlayLevelSoundAction(x, y, ACTION_PASSING);
}
- else if (IS_DIGGABLE(element))
+ else if (player_can_move_or_snap && IS_DIGGABLE(element))
{
RemoveField(x, y);
player->index_bit, dig_side);
}
}
- else if (IS_COLLECTIBLE(element))
+ else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
{
RemoveField(x, y);
player->index_bit, dig_side);
}
}
- else if (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 &&
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))
{
(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;
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))
CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
player->index_bit, dig_side);
- return MF_ACTION;
+ return MP_ACTION;
}
player->is_switching = TRUE;
ResetGfxAnimation(x, y);
DrawLevelField(x, y);
}
+ else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
+ element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
+ {
+ int xx, yy;
+
+ game.ball_state = !game.ball_state;
+
+#if 1
+ SCAN_PLAYFIELD(xx, yy)
+#else
+ for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
+#endif
+ {
+ int e = Feld[xx][yy];
+
+ if (game.ball_state)
+ {
+ if (e == EL_EMC_MAGIC_BALL)
+ CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
+ else if (e == EL_EMC_MAGIC_BALL_SWITCH)
+ CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
+ }
+ else
+ {
+ if (e == EL_EMC_MAGIC_BALL_ACTIVE)
+ CreateField(xx, yy, EL_EMC_MAGIC_BALL);
+ else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
+ CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
+ }
+ }
+ }
CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
player->index_bit, dig_side);
CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
player->index_bit, dig_side);
- return MF_ACTION;
+ return MP_ACTION;
}
else
{
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;
player->is_collecting = !player->is_digging;
}
- return MF_MOVING;
+ return MP_MOVING;
}
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;
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))
{