#define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
(element_info[e].move_delay_random))
+#if 1
+#define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
+ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
+ (condition) || \
+ (DONT_COLLIDE_WITH(e) && \
+ IS_PLAYER(x, y) && \
+ !PLAYER_PROTECTED(x, y))))
+#else
#define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
(condition) || \
(DONT_COLLIDE_WITH(e) && \
IS_FREE_OR_PLAYER(x, y))))
+#endif
#define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
Feld[x][y] == EL_EXIT_OPEN || \
Feld[x][y] == EL_ACID))
+#if 0
#if 1
#define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
(IN_LEV_FIELD(x, y) && IS_FREE(x, y))
(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
IS_FOOD_DARK_YAMYAM(Feld[x][y])))
#endif
+#endif
+
+#define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
+#define IS_IN_GROUP(e, g) (element_info[e].in_group[g] == TRUE)
+#define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
+
+#define CE_ENTER_FIELD_COND(e, x, y) \
+ (!IS_PLAYER(x, y) && \
+ (Feld[x][y] == EL_ACID || \
+ Feld[x][y] == MOVE_ENTER_EL(e) || \
+ (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) && \
+ IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
+
+#define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
+ ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
#define MOLE_CAN_ENTER_FIELD(x, y, condition) \
(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
InitPlayerField(x, y, element, init_game);
break;
+ case EL_SOKOBAN_FIELD_PLAYER:
+ element = Feld[x][y] = EL_PLAYER_1;
+ InitField(x, y, init_game);
+
+ element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
+ InitField(x, y, init_game);
+ break;
+
+ case EL_SOKOBAN_FIELD_EMPTY:
+ local_player->sokobanfields_still_needed++;
+ break;
+
case EL_STONEBLOCK:
if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
Feld[x][y] = EL_ACID_POOL_TOPLEFT;
local_player->lights_still_needed++;
break;
- case EL_SOKOBAN_FIELD_EMPTY:
- local_player->sokobanfields_still_needed++;
- break;
-
case EL_PENGUIN:
local_player->friends_still_needed++;
break;
default:
if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
InitMovDir(x, y);
+ else if (IS_GROUP_ELEMENT(element))
+ {
+ struct ElementGroupInfo *group = element_info[element].group;
+ int random_pos = RND(group->num_elements_resolved);
+
+ Feld[x][y] = group->element_resolved[random_pos];
+
+ InitField(x, y, init_game);
+ }
break;
}
}
int2str(TimeLeft, 3), FONT_TEXT_2);
}
+static void resolve_group_element(int group_element, int recursion_depth)
+{
+ static int group_nr;
+ static struct ElementGroupInfo *group;
+ struct ElementGroupInfo *actual_group = element_info[group_element].group;
+ int i;
+
+ if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
+ {
+ Error(ERR_WARN, "recursion too deep when resolving group element %d",
+ group_element - EL_GROUP_START + 1);
+
+ /* replace element which caused too deep recursion by question mark */
+ group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
+
+ return;
+ }
+
+ if (recursion_depth == 0) /* initialization */
+ {
+ group = element_info[group_element].group;
+ group->num_elements_resolved = 0;
+ group_nr = group_element - EL_GROUP_START;
+ }
+
+ for (i = 0; i < actual_group->num_elements; i++)
+ {
+ int element = actual_group->element[i];
+
+ if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
+ break;
+
+ if (IS_GROUP_ELEMENT(element))
+ resolve_group_element(element, recursion_depth + 1);
+ else
+ {
+ group->element_resolved[group->num_elements_resolved++] = element;
+ element_info[element].in_group[group_nr] = TRUE;
+ }
+ }
+
+#if 0
+ if (recursion_depth == 0 && group_element <= EL_GROUP_4)
+ {
+ printf("::: group %d: %d resolved elements\n",
+ group_element - EL_GROUP_START, group->num_elements_resolved);
+ for (i = 0; i < group->num_elements_resolved; i++)
+ printf("::: - %d ['%s']\n", group->element_resolved[i],
+ element_info[group->element_resolved[i]].token_name);
+ }
+#endif
+}
+
/*
=============================================================================
printf(" => game.engine_version == %06d\n", game.engine_version);
#endif
+ /* ---------- recursively resolve group elements ------------------------- */
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
+ element_info[i].in_group[j] = FALSE;
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ resolve_group_element(EL_GROUP_START + i, 0);
+
/* ---------- initialize player's initial move delay --------------------- */
/* dynamically adjust player properties according to game engine version */
default:
if (IS_CUSTOM_ELEMENT(element))
{
- 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 ||
- element_info[element].move_pattern == MV_TURNING_LEFT ||
- element_info[element].move_pattern == MV_TURNING_RIGHT ||
- element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
- element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
- element_info[element].move_pattern == MV_TURNING_RANDOM)
+ struct ElementInfo *ei = &element_info[element];
+ int move_direction_initial = ei->move_direction_initial;
+ int move_pattern = ei->move_pattern;
+
+ if (move_direction_initial == MV_PREVIOUS)
+ {
+ if (MovDir[x][y] != MV_NO_MOVING)
+ return;
+
+ move_direction_initial = MV_AUTOMATIC;
+ }
+
+ if (move_direction_initial & MV_ANY_DIRECTION)
+ MovDir[x][y] = move_direction_initial;
+ else if (move_direction_initial == MV_RANDOM ||
+ move_pattern == MV_ALL_DIRECTIONS ||
+ move_pattern == MV_TURNING_LEFT ||
+ move_pattern == MV_TURNING_RIGHT ||
+ move_pattern == MV_TURNING_LEFT_RIGHT ||
+ move_pattern == MV_TURNING_RIGHT_LEFT ||
+ move_pattern == MV_TURNING_RANDOM)
MovDir[x][y] = 1 << RND(4);
- else if (element_info[element].move_pattern == MV_HORIZONTAL)
+ else if (move_pattern == MV_HORIZONTAL)
MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
- else if (element_info[element].move_pattern == MV_VERTICAL)
+ else if (move_pattern == MV_VERTICAL)
MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
- else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
+ else if (move_pattern & MV_ANY_DIRECTION)
MovDir[x][y] = element_info[element].move_pattern;
- else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
- element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+ else if (move_pattern == MV_ALONG_LEFT_SIDE ||
+ move_pattern == MV_ALONG_RIGHT_SIDE)
{
for (i = 0; i < 4; i++)
{
if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
{
- if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+ if (move_pattern == MV_ALONG_RIGHT_SIDE)
MovDir[x][y] = direction[0][i];
else
MovDir[x][y] = direction[1][i];
move_pattern == MV_TURNING_RANDOM ||
move_pattern == MV_ALL_DIRECTIONS)
{
- 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);
+ boolean can_turn_left =
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
+ boolean can_turn_right =
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
if (move_pattern == MV_TURNING_LEFT)
MovDir[x][y] = left_dir;
}
else if (move_pattern == MV_ALONG_LEFT_SIDE)
{
- if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
MovDir[x][y] = left_dir;
- else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+ else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
MovDir[x][y] = right_dir;
if (MovDir[x][y] != old_move_dir)
}
else if (move_pattern == MV_ALONG_RIGHT_SIDE)
{
- if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
MovDir[x][y] = right_dir;
- else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+ else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
MovDir[x][y] = left_dir;
if (MovDir[x][y] != old_move_dir)
new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
Moving2Blocked(x, y, &newx, &newy);
- if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
+ if (CUSTOM_ELEMENT_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 (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
return;
MovDir[x][y] = old_move_dir;
MovDelay[x][y] = 0;
}
- else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
- element == EL_MAZE_RUNNER)
+ else if (move_pattern & MV_MAZE_RUNNER_STYLE)
{
static int test_xy[7][2] =
{
break;
}
- if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
+ if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
continue;
move_dir_preference = -1 * RunnerVisit[xx][yy];
{
#if 1
TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
+
return;
#else
/* player killed by element which is deadly when colliding with */
DrawPlayerField(x, y);
else
DrawLevelField(x, y);
+
return;
}
}
DrawPlayerField(x, y);
else
DrawLevelField(x, y);
+
return;
}
}
- else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
- element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
- {
+
#if 1
- if (IS_FREE(newx, newy))
-#else
- if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
+
+ /*
+ else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
+ */
+
+ else if (IS_CUSTOM_ELEMENT(element) &&
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
+
+#if 0
+ &&
+ !IS_FREE(newx, newy)
+#endif
+
+)
+ {
+#if 0
+ printf("::: '%s' digs '%s' [%d]\n",
+ element_info[element].token_name,
+ element_info[Feld[newx][newy]].token_name,
+ StorePlayer[newx][newy]);
#endif
+
+ if (!IS_FREE(newx, newy))
{
+ int new_element = Feld[newx][newy];
+ int sound;
+
+ /* no element can dig solid indestructible elements */
+ if (IS_INDESTRUCTIBLE(new_element) &&
+ !IS_DIGGABLE(new_element) &&
+ !IS_COLLECTIBLE(new_element))
+ return;
+
+ if (AmoebaNr[newx][newy] &&
+ (new_element == EL_AMOEBA_FULL ||
+ new_element == EL_BD_AMOEBA ||
+ new_element == EL_AMOEBA_GROWING))
+ {
+ AmoebaCnt[AmoebaNr[newx][newy]]--;
+ AmoebaCnt2[AmoebaNr[newx][newy]]--;
+ }
+
if (IS_MOVING(newx, newy))
RemoveMovingField(newx, newy);
else
{
- Feld[newx][newy] = EL_EMPTY;
+ RemoveField(newx, newy);
DrawLevelField(newx, newy);
}
- PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
+ sound = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
+ IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
+ ACTION_BREAKING);
+
+ PlayLevelSoundAction(x, y, sound);
}
- else if (!IS_FREE(newx, newy))
+
+ if (move_pattern & MV_MAZE_RUNNER_STYLE)
{
-#if 0
- if (IS_PLAYER(x, y))
- DrawPlayerField(x, y);
- else
- DrawLevelField(x, y);
-#endif
- return;
+ RunnerVisit[x][y] = FrameCounter;
+ PlayerVisit[x][y] /= 8; /* expire player visit path */
}
-
- RunnerVisit[x][y] = FrameCounter;
- PlayerVisit[x][y] /= 8; /* expire player visit path */
}
+
+#endif
+
else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
{
if (!IS_FREE(newx, newy))
DrawLevelField(x, y);
MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
+
return; /* wait for shrinking amoeba */
}
else /* element == EL_PACMAN */
ResetGfxAnimation(x, y); /* reset animation values for old field */
+#if 1
+ if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y))
+ {
+ int new_element = element_info[element].move_leave_element;
+
+ Feld[x][y] = new_element;
+
+ if (new_element != EL_EMPTY)
+ {
+ InitField(x, y, FALSE);
+
+ TestIfElementTouchesCustomElement(x, y);
+
+ if (GFX_CRUMBLED(new_element))
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+ }
+#endif
+
#if 0
/* 2.1.1 (does not work correctly for spring) */
if (!CAN_MOVE(element))
static void ChangeElementNowExt(int x, int y, int target_element)
{
+ int previous_move_direction = MovDir[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_PROTECTED(x, y) &&
ResetGfxAnimation(x, y);
ResetRandomAnimationValue(x, y);
+ if (element_info[Feld[x][y]].move_direction_initial == MV_PREVIOUS)
+ MovDir[x][y] = previous_move_direction;
+
InitField(x, y, FALSE);
if (CAN_MOVE(Feld[x][y]))
InitMovDir(x, y);
if (!player->active || tape.pausing)
return 0;
+#if 0
+ printf("::: [%d %d %d %d] [%d %d]\n",
+ left, right, up, down, button1, button2);
+#endif
+
if (player_action)
{
#if 0
/* automatically move to the next field with double speed */
player->programmed_action = move_direction;
-#if 0
+#if 1
if (player->move_delay_reset_counter == 0)
{
player->move_delay_reset_counter = 2; /* two double speed steps */
DOUBLE_PLAYER_SPEED(player);
}
#else
- DOUBLE_PLAYER_SPEED(player);
-
player->move_delay_reset_counter = 2;
+
+ DOUBLE_PLAYER_SPEED(player);
#endif
PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
DOUBLE_PLAYER_SPEED(player);
}
#else
- DOUBLE_PLAYER_SPEED(player);
-
player->move_delay_reset_counter = 2;
+
+ DOUBLE_PLAYER_SPEED(player);
#endif
PlayLevelSoundAction(x, y, ACTION_PASSING);
player->MovDir = snap_direction;
- player->is_moving = FALSE;
- player->is_digging = FALSE;
- player->is_collecting = FALSE;
+#if 1
+ if (player->MovPos == 0)
+#endif
+ {
+ player->is_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+ }
player->is_dropping = FALSE;
player->is_snapping = TRUE;
- player->is_moving = FALSE;
- player->is_digging = FALSE;
- player->is_collecting = FALSE;
+#if 1
+ if (player->MovPos == 0)
+#endif
+ {
+ player->is_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+ }
DrawLevelField(x, y);
BackToFront();
int move_stepsize = element_info[new_element].move_stepsize;
int direction, dx, dy, nextx, nexty;
- if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
+ if (element_info[new_element].move_direction_initial == MV_AUTOMATIC)
MovDir[jx][jy] = player->MovDir;
direction = MovDir[jx][jy];