#define USE_NEW_AMOEBA_CODE FALSE
/* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF (TRUE * 1)
-
-#define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1)
-#define USE_NEW_COLLECT_COUNT (TRUE * USE_NEW_STUFF * 1)
-#define USE_NEW_PLAYER_ANIM (TRUE * USE_NEW_STUFF * 1)
-
+#define USE_NEW_STUFF ( 1)
+
+#define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
+#define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
+#define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
+#define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
+#define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
+#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_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
+#define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
+
+#define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
/* for DigField() */
#define DF_NO_PUSH 0
#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 SCROLL_GO_ON 1
-/* for Explode() */
+/* for Bang()/Explode() */
#define EX_PHASE_START 0
#define EX_TYPE_NONE 0
#define EX_TYPE_NORMAL (1 << 0)
#define EX_TYPE_CENTER (1 << 1)
#define EX_TYPE_BORDER (1 << 2)
#define EX_TYPE_CROSS (1 << 3)
+#define EX_TYPE_DYNA (1 << 4)
#define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
/* special positions in the game control window (relative to control window) */
#define DX_TIME2 (DX + XX_TIME2)
#define DY_TIME (DY + YY_TIME)
+/* values for delayed check of falling and moving elements and for collision */
+#define CHECK_DELAY_MOVING 3
+#define CHECK_DELAY_FALLING 3
+#define CHECK_DELAY_COLLISION 2
+
/* values for initial player move delay (initial delay counter value) */
#define INITIAL_MOVE_DELAY_OFF -1
#define INITIAL_MOVE_DELAY_ON 0
RND(element_info[e].move_delay_random))
#define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
(element_info[e].move_delay_random))
+#define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
+ RND(element_info[e].ce_value_random_initial))
+#define GET_CE_SCORE(e) ( (element_info[e].collect_score))
#define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
RND((c)->delay_random * (c)->delay_frames))
+#define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
+ RND((c)->delay_random))
+
-#define GET_TARGET_ELEMENT(e, ch) \
- ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
- (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
+#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_VALID_PLAYER_ELEMENT(e) \
- ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
+#define GET_TARGET_ELEMENT(e, ch, cv, cs) \
+ ((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) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
+ (e) == EL_CURRENT_CE_VALUE ? (cv) : \
+ (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
#define CAN_GROW_INTO(e) \
((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
#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)
#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) && \
/* forward declaration for internal use */
+static void CreateField(int, int, int);
+
+static void SetPlayerWaiting(struct PlayerInfo *, boolean);
static void AdvanceFrameAndPlayerCounters(int);
static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
static void CloseAllOpenTimegates(void);
static void CheckGravityMovement(struct PlayerInfo *);
static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
-static void KillHeroUnlessEnemyProtected(int, int);
-static void KillHeroUnlessExplosionProtected(int, int);
+static void KillPlayerUnlessEnemyProtected(int, int);
+static void KillPlayerUnlessExplosionProtected(int, int);
static void TestIfPlayerTouchesCustomElement(int, int);
static void TestIfElementTouchesCustomElement(int, int);
static void TestIfElementSmashesCustomElement(int, int, int);
#endif
-static void ChangeElement(int, int, int);
+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);
-#define CheckTriggeredElementChange(e, ev) \
- CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
-#define CheckTriggeredElementChangeByPlayer(e, ev, p, s) \
- CheckTriggeredElementChangeExt(e, ev, p, s, -1)
-#define CheckTriggeredElementChangeBySide(e, ev, s) \
- CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, s, -1)
-#define CheckTriggeredElementChangeByPage(e, ev, p) \
- CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
+static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
+#define CheckTriggeredElementChange(x, y, e, ev) \
+ CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
+#define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
+ CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
+#define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
+ CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
+#define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
+ CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
#define CheckElementChange(x, y, e, te, ev) \
static void MapGameButtons();
static void HandleGameButtons(struct GadgetInfo *);
+int AmoebeNachbarNr(int, int);
+void AmoebeUmwandeln(int, int);
+void ContinueMoving(int, int);
+void Bang(int, int);
+void InitMovDir(int, int);
+void InitAmoebaNr(int, int);
+int NewHiScore(void);
+
+void TestIfGoodThingHitsBadThing(int, int, int);
+void TestIfBadThingHitsGoodThing(int, int, int);
+void TestIfPlayerTouchesBadThing(int, int);
+void TestIfPlayerRunsIntoBadThing(int, int, int);
+void TestIfBadThingTouchesPlayer(int, int);
+void TestIfBadThingRunsIntoPlayer(int, int, int);
+void TestIfFriendTouchesBadThing(int, int);
+void TestIfBadThingTouchesFriend(int, int);
+void TestIfBadThingTouchesOtherBadThing(int, int);
+
+void KillPlayer(struct PlayerInfo *);
+void BuryPlayer(struct PlayerInfo *);
+void RemovePlayer(struct PlayerInfo *);
+
+boolean SnapField(struct PlayerInfo *, int, int);
+boolean DropElement(struct PlayerInfo *);
+
+
static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
/* ------------------------------------------------------------------------- */
/* 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_UNDEFINED,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_DIAGONAL_GROWING,
+ EL_UNDEFINED,
+ 0,
+ NULL,
+ NULL,
+ InitDiagonalMovingElement
+ },
{
EL_UNDEFINED,
{ EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
{ EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
- { EL_UNDEFINED, MV_NO_MOVING }
+ { EL_UNDEFINED, MV_NONE }
};
static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
#define CE_PAGE(e, ce) (element_info[e].event_page[ce])
+/* static variables for playfield scan mode (scanning forward or backward) */
+static int playfield_scan_start_x = 0;
+static int playfield_scan_start_y = 0;
+static int playfield_scan_delta_x = 1;
+static int playfield_scan_delta_y = 1;
+
+#define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
+ (y) >= 0 && (y) <= lev_fieldy - 1; \
+ (y) += playfield_scan_delta_y) \
+ for ((x) = playfield_scan_start_x; \
+ (x) >= 0 && (x) <= lev_fieldx - 1; \
+ (x) += playfield_scan_delta_x) \
+
+#ifdef DEBUG
+void DEBUG_SetMaximumDynamite()
+{
+ int i;
+
+ for (i = 0; i < MAX_INVENTORY_SIZE; i++)
+ if (local_player->inventory_size < MAX_INVENTORY_SIZE)
+ local_player->inventory_element[local_player->inventory_size++] =
+ EL_DYNAMITE;
+}
+#endif
+
+static void InitPlayfieldScanModeVars()
+{
+ if (game.use_reverse_scan_direction)
+ {
+ playfield_scan_start_x = lev_fieldx - 1;
+ playfield_scan_start_y = lev_fieldy - 1;
+
+ playfield_scan_delta_x = -1;
+ playfield_scan_delta_y = -1;
+ }
+ else
+ {
+ playfield_scan_start_x = 0;
+ playfield_scan_start_y = 0;
+
+ playfield_scan_delta_x = 1;
+ playfield_scan_delta_y = 1;
+ }
+}
+
+static void InitPlayfieldScanMode(int mode)
+{
+ game.use_reverse_scan_direction =
+ (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
+
+ 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()
{
static int belt_move_dir[3] =
{
MV_LEFT,
- MV_NO_MOVING,
+ MV_NONE,
MV_RIGHT
};
return belt_move_dir[belt_dir_nr];
}
+static int get_element_from_group_element(int element)
+{
+ if (IS_GROUP_ELEMENT(element))
+ {
+ struct ElementGroupInfo *group = element_info[element].group;
+ int last_anim_random_frame = gfx.anim_random_frame;
+ int element_pos;
+
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = RND(group->num_elements_resolved);
+
+ element_pos = getAnimationFrame(group->num_elements_resolved, 1,
+ group->choice_mode, 0,
+ group->choice_pos);
+
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = last_anim_random_frame;
+
+ group->choice_pos++;
+
+ element = group->element_resolved[element_pos];
+ }
+
+ return element;
+}
+
static void InitPlayerField(int x, int y, int element, boolean init_game)
{
if (element == EL_SP_MURPHY)
}
else
{
- stored_player[0].use_murphy_graphic = TRUE;
+ stored_player[0].use_murphy = TRUE;
+
+ if (!level.use_artwork_element[0])
+ stored_player[0].artwork_element = EL_SP_MURPHY;
}
Feld[x][y] = EL_PLAYER_1;
Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
break;
+ case EL_BUG:
case EL_BUG_RIGHT:
case EL_BUG_UP:
case EL_BUG_LEFT:
case EL_BUG_DOWN:
- case EL_BUG:
+ case EL_SPACESHIP:
case EL_SPACESHIP_RIGHT:
case EL_SPACESHIP_UP:
case EL_SPACESHIP_LEFT:
case EL_SPACESHIP_DOWN:
- case EL_SPACESHIP:
+ case EL_BD_BUTTERFLY:
case EL_BD_BUTTERFLY_RIGHT:
case EL_BD_BUTTERFLY_UP:
case EL_BD_BUTTERFLY_LEFT:
case EL_BD_BUTTERFLY_DOWN:
- case EL_BD_BUTTERFLY:
+ case EL_BD_FIREFLY:
case EL_BD_FIREFLY_RIGHT:
case EL_BD_FIREFLY_UP:
case EL_BD_FIREFLY_LEFT:
case EL_BD_FIREFLY_DOWN:
- case EL_BD_FIREFLY:
case EL_PACMAN_RIGHT:
case EL_PACMAN_UP:
case EL_PACMAN_LEFT:
case EL_PACMAN_DOWN:
case EL_YAMYAM:
+ case EL_YAMYAM_LEFT:
+ case EL_YAMYAM_RIGHT:
+ case EL_YAMYAM_UP:
+ case EL_YAMYAM_DOWN:
case EL_DARK_YAMYAM:
case EL_ROBOT:
case EL_PACMAN:
case EL_SP_SNIKSNAK:
case EL_SP_ELECTRON:
+ case EL_MOLE:
case EL_MOLE_LEFT:
case EL_MOLE_RIGHT:
case EL_MOLE_UP:
case EL_MOLE_DOWN:
- case EL_MOLE:
InitMovDir(x, y);
break;
MovDelay[x][y] = 96;
break;
+ case EL_EM_DYNAMITE_ACTIVE:
+ MovDelay[x][y] = 32;
+ break;
+
case EL_LAMP:
local_player->lights_still_needed++;
break;
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))
+ {
+ if (CAN_MOVE(element))
+ InitMovDir(x, y);
+
+#if USE_NEW_CUSTOM_VALUE
+ if (!element_info[element].use_last_ce_value || init_game)
+ CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
+#endif
+ }
+#else
if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
InitMovDir(x, y);
+#endif
else if (IS_GROUP_ELEMENT(element))
{
+#if 1
+ Feld[x][y] = get_element_from_group_element(element);
+
+ InitField(x, y, init_game);
+#else
struct ElementGroupInfo *group = element_info[element].group;
int last_anim_random_frame = gfx.anim_random_frame;
int element_pos;
Feld[x][y] = group->element_resolved[element_pos];
InitField(x, y, init_game);
+#endif
}
break;
}
-#if USE_NEW_COLLECT_COUNT
- Count[x][y] = element_info[Feld[x][y]].collect_count_initial;
+#if 1
+ if (!init_game)
+ CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
+#endif
+
+#if 0
+
+#if USE_NEW_CUSTOM_VALUE
+
+#if 1
+ CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
+#else
+ CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
+#endif
+
+#endif
+
#endif
}
inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
{
+ int base_key_graphic = EL_KEY_1;
int i;
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ base_key_graphic = EL_EM_KEY_1;
+
/* currently only 4 of 8 possible keys are displayed */
for (i = 0; i < STD_NUM_KEYS; i++)
{
if (key[i])
DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
- el2edimg(EL_KEY_1 + i));
+ el2edimg(base_key_graphic + i));
else
BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
void DrawGameDoorValues()
{
- int i;
+ int dynamite_state = 0;
+ int key_bits = 0;
+ int i, j;
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
return;
}
+#if 0
DrawGameValue_Level(level_nr);
DrawGameValue_Emeralds(local_player->gems_still_needed);
DrawGameValue_Score(local_player->score);
DrawGameValue_Time(TimeLeft);
- for (i = 0; i < MAX_PLAYERS; i++)
+#else
+
+ if (game.centered_player_nr == -1)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ for (j = 0; j < MAX_NUM_KEYS; j++)
+ if (stored_player[i].key[j])
+ key_bits |= (1 << j);
+
+ dynamite_state += stored_player[i].inventory_size;
+ }
+
+#if 0
DrawGameValue_Keys(stored_player[i].key);
+#endif
+ }
+ else
+ {
+ int player_nr = game.centered_player_nr;
+
+ for (i = 0; i < MAX_NUM_KEYS; i++)
+ if (stored_player[player_nr].key[i])
+ key_bits |= (1 << i);
+
+ dynamite_state = stored_player[player_nr].inventory_size;
+ }
+
+ DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
+ local_player->score, TimeLeft, key_bits);
+#endif
}
+#if 0
static void resolve_group_element(int group_element, int recursion_depth)
{
static int group_nr;
}
}
}
-
+#endif
/*
=============================================================================
static void InitGameEngine()
{
- int i, j, k, l;
+ int i, j, k, l, x, y;
/* set game engine from tape file when re-playing, else from level file */
game.engine_version = (tape.playing ? tape.engine_version :
game.use_block_last_field_bug =
(game.engine_version < VERSION_IDENT(3,1,1,0));
+ /*
+ Summary of bugfix/change:
+ Changed behaviour of CE changes with multiple changes per single frame.
+
+ Fixed/changed in version:
+ 3.2.0-6
+
+ Description:
+ Before 3.2.0-6, only one single CE change was allowed in each engine frame.
+ This resulted in race conditions where CEs seem to behave strange in some
+ situations (where triggered CE changes were just skipped because there was
+ already a CE change on that tile in the playfield in that engine frame).
+ Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
+ (The number of changes per frame must be limited in any case, because else
+ it is easily possible to define CE changes that would result in an infinite
+ loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
+ should be set large enough so that it would only be reached in cases where
+ the corresponding CE change conditions run into a loop. Therefore, it seems
+ to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
+ maximal number of change pages for custom elements.)
+
+ Affected levels/tapes:
+ Probably many.
+ */
+
+#if USE_ONLY_ONE_CHANGE_PER_FRAME
+ game.max_num_changes_per_frame = 1;
+#else
+ game.max_num_changes_per_frame =
+ (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
+#endif
+
/* ---------------------------------------------------------------------- */
+ /* default scan direction: scan playfield from top/left to bottom/right */
+ InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
+
/* dynamically adjust element properties according to game engine version */
InitElementPropertiesEngine(game.engine_version);
printf(" => game.engine_version == %06d\n", game.engine_version);
#endif
+#if 0
/* ---------- recursively resolve group elements ------------------------- */
for (i = 0; i < MAX_NUM_ELEMENTS; i++)
for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
resolve_group_element(EL_GROUP_START + i, 0);
+#endif
/* ---------- 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) ?
ei->change->change_function = ch_delay->change_function;
ei->change->post_change_function = ch_delay->post_change_function;
+ ei->change->can_change = TRUE;
+ ei->change->can_change_or_has_action = TRUE;
+
ei->has_change_event[CE_DELAY] = TRUE;
SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
{
ei->change_page[j].actual_trigger_element = EL_EMPTY;
ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
+ ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
+ ei->change_page[j].actual_trigger_ce_value = 0;
+ ei->change_page[j].actual_trigger_ce_score = 0;
}
}
for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
element_info[access_direction_list[i].element].access_direction =
access_direction_list[i].direction;
+
+ /* ---------- initialize explosion content ------------------------------- */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (IS_CUSTOM_ELEMENT(i))
+ continue;
+
+ for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+ {
+ /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
+
+ element_info[i].content.e[x][y] =
+ (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
+ i == EL_PLAYER_2 ? EL_EMERALD_RED :
+ i == EL_PLAYER_3 ? EL_EMERALD :
+ i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
+ i == EL_MOLE ? EL_EMERALD_RED :
+ i == EL_PENGUIN ? EL_EMERALD_PURPLE :
+ i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
+ i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
+ i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
+ i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
+ i == EL_WALL_EMERALD ? EL_EMERALD :
+ i == EL_WALL_DIAMOND ? EL_DIAMOND :
+ i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
+ i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
+ i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
+ i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
+ i == EL_WALL_PEARL ? EL_PEARL :
+ i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
+ EL_EMPTY);
+ }
+ }
}
+int get_num_special_action(int element, int action_first, int action_last)
+{
+ int num_special_action = 0;
+ int i, j;
+
+ for (i = action_first; i <= action_last; i++)
+ {
+ boolean found = FALSE;
+
+ for (j = 0; j < NUM_DIRECTIONS; j++)
+ if (el_act_dir2img(element, i, j) !=
+ el_act_dir2img(element, ACTION_DEFAULT, j))
+ found = TRUE;
+
+ if (found)
+ num_special_action++;
+ else
+ break;
+ }
+
+#if 0
+ printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
+#endif
+
+ return num_special_action;
+}
/*
=============================================================================
boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
- int i, j, k, x, y;
+ int i, j, x, y;
InitGameEngine();
player->dynabombs_left = 0;
player->dynabomb_xl = FALSE;
- player->MovDir = MV_NO_MOVING;
+ player->MovDir = MV_NONE;
player->MovPos = 0;
player->GfxPos = 0;
- player->GfxDir = MV_NO_MOVING;
+ player->GfxDir = MV_NONE;
player->GfxAction = ACTION_DEFAULT;
player->Frame = 0;
player->StepFrame = 0;
- player->use_murphy_graphic = FALSE;
+ player->use_murphy = FALSE;
+ player->artwork_element =
+ (level.use_artwork_element[i] ? level.artwork_element[i] :
+ player->element_nr);
player->block_last_field = FALSE; /* initialized in InitPlayerField() */
player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
player->step_counter = 0;
- player->last_move_dir = MV_NO_MOVING;
+ player->last_move_dir = MV_NONE;
player->is_waiting = FALSE;
player->is_moving = FALSE;
player->is_pushing = FALSE;
player->is_switching = FALSE;
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
player->is_bored = FALSE;
player->is_sleeping = FALSE;
player->anim_delay_counter = 0;
player->post_delay_counter = 0;
+ player->dir_waiting = MV_NONE;
player->action_waiting = ACTION_DEFAULT;
player->last_action_waiting = ACTION_DEFAULT;
player->special_action_bored = ACTION_DEFAULT;
player->special_action_sleeping = ACTION_DEFAULT;
- player->num_special_action_bored = 0;
- player->num_special_action_sleeping = 0;
-
- /* determine number of special actions for bored and sleeping animation */
- for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
- {
- boolean found = FALSE;
-
- for (k = 0; k < NUM_DIRECTIONS; k++)
- if (el_act_dir2img(player->element_nr, j, k) !=
- el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
- found = TRUE;
-
- if (found)
- player->num_special_action_bored++;
- else
- break;
- }
- for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
- {
- boolean found = FALSE;
-
- for (k = 0; k < NUM_DIRECTIONS; k++)
- if (el_act_dir2img(player->element_nr, j, k) !=
- el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
- found = TRUE;
-
- if (found)
- player->num_special_action_sleeping++;
- else
- break;
- }
+#if 1
+ /* cannot be set here -- could be modified in Init[Player]Field() below */
+#else
+ /* set number of special actions for bored and sleeping animation */
+ player->num_special_action_bored =
+ get_num_special_action(player->artwork_element,
+ ACTION_BORING_1, ACTION_BORING_LAST);
+ player->num_special_action_sleeping =
+ get_num_special_action(player->artwork_element,
+ ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+#endif
player->switch_x = -1;
player->switch_y = -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_value_next = -1;
+
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;
player->drop_delay = 0;
+ player->drop_pressed_delay = 0;
player->last_jx = player->last_jy = 0;
player->jx = player->jy = 0;
#if defined(NETWORK_AVALIABLE)
/* initial null action */
if (network_playing)
- SendToServer_MovePlayer(MV_NO_MOVING);
+ SendToServer_MovePlayer(MV_NONE);
#endif
ZX = ZY = -1;
TimeLeft = level.time;
TapeTime = 0;
- ScreenMovDir = MV_NO_MOVING;
+ ScreenMovDir = MV_NONE;
ScreenMovPos = 0;
ScreenGfxPos = 0;
game.light_time_left = 0;
game.timegate_time_left = 0;
game.switchgate_pos = 0;
- game.balloon_dir = MV_NO_MOVING;
+ game.wind_direction = level.wind_direction_initial;
game.gravity = level.initial_gravity;
game.explosions_delayed = TRUE;
+ 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;
+ /* set focus to local player for network games, else to all players */
+ game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+
+ if (network_playing && tape.recording)
+ {
+ /* store client dependent player focus when recording network games */
+ tape.centered_player_nr_next = game.centered_player_nr_next;
+ tape.set_centered_player = TRUE;
+ }
+
+#if 0
+ printf("::: focus set to player %d [%d]\n",
+ game.centered_player_nr, local_player->index_nr);
+#endif
+
for (i = 0; i < NUM_BELTS; i++)
{
- game.belt_dir[i] = MV_NO_MOVING;
+ game.belt_dir[i] = MV_NONE;
game.belt_dir_nr[i] = 3; /* not moving, next moving left */
}
for (i = 0; i < MAX_NUM_AMOEBA; i++)
AmoebaCnt[i] = AmoebaCnt2[i] = 0;
- for (x = 0; x < lev_fieldx; x++)
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
+ for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
+#endif
{
- for (y = 0; y < lev_fieldy; y++)
- {
- Feld[x][y] = level.field[x][y];
- MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
- ChangeDelay[x][y] = 0;
- ChangePage[x][y] = -1;
-#if USE_NEW_COLLECT_COUNT
- Count[x][y] = 0; /* initialized in InitField() */
+ Feld[x][y] = level.field[x][y];
+ MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+ ChangeDelay[x][y] = 0;
+ ChangePage[x][y] = -1;
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[x][y] = 0; /* initialized in InitField() */
#endif
- Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
- AmoebaNr[x][y] = 0;
- WasJustMoving[x][y] = 0;
- WasJustFalling[x][y] = 0;
- CheckCollision[x][y] = 0;
- Stop[x][y] = FALSE;
- Pushed[x][y] = FALSE;
+ Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
+ AmoebaNr[x][y] = 0;
+ WasJustMoving[x][y] = 0;
+ WasJustFalling[x][y] = 0;
+ CheckCollision[x][y] = 0;
+ Stop[x][y] = FALSE;
+ Pushed[x][y] = FALSE;
- Changed[x][y] = FALSE;
- ChangeEvent[x][y] = -1;
+ ChangeCount[x][y] = 0;
+ ChangeEvent[x][y] = -1;
- ExplodePhase[x][y] = 0;
- ExplodeDelay[x][y] = 0;
- ExplodeField[x][y] = EX_TYPE_NONE;
+ ExplodePhase[x][y] = 0;
+ ExplodeDelay[x][y] = 0;
+ ExplodeField[x][y] = EX_TYPE_NONE;
- RunnerVisit[x][y] = 0;
- PlayerVisit[x][y] = 0;
+ RunnerVisit[x][y] = 0;
+ PlayerVisit[x][y] = 0;
- GfxFrame[x][y] = 0;
- GfxRandom[x][y] = INIT_GFX_RANDOM();
- GfxElement[x][y] = EL_UNDEFINED;
- GfxAction[x][y] = ACTION_DEFAULT;
- GfxDir[x][y] = MV_NO_MOVING;
- }
+ GfxFrame[x][y] = 0;
+ GfxRandom[x][y] = INIT_GFX_RANDOM();
+ GfxElement[x][y] = EL_UNDEFINED;
+ GfxAction[x][y] = ACTION_DEFAULT;
+ GfxDir[x][y] = MV_NONE;
}
- for (y = 0; y < lev_fieldy; y++)
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
- for (x = 0; x < lev_fieldx; x++)
- {
- if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
- emulate_bd = FALSE;
- if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
- emulate_sb = FALSE;
- if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
- emulate_sp = FALSE;
+ if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
+ emulate_bd = FALSE;
+ if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
+ emulate_sb = FALSE;
+ if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
+ emulate_sp = FALSE;
- InitField(x, y, TRUE);
- }
+ InitField(x, y, TRUE);
}
InitBeltMovement();
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+#if 1
+ /* set number of special actions for bored and sleeping animation */
+ player->num_special_action_bored =
+ get_num_special_action(player->artwork_element,
+ ACTION_BORING_1, ACTION_BORING_LAST);
+ player->num_special_action_sleeping =
+ get_num_special_action(player->artwork_element,
+ ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+#endif
+
+ }
+
game.emulation = (emulate_bd ? EMU_BOULDERDASH :
emulate_sb ? EMU_SOKOBAN :
emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+#if USE_NEW_ALL_SLIPPERY
+ /* initialize type of slippery elements */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (!IS_CUSTOM_ELEMENT(i))
+ {
+ /* default: elements slip down either to the left or right randomly */
+ element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
+
+ /* SP style elements prefer to slip down on the left side */
+ if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
+ element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+
+ /* BD style elements prefer to slip down on the left side */
+ if (game.emulation == EMU_BOULDERDASH)
+ element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+ }
+ }
+#endif
+
/* initialize explosion and ignition delay */
for (i = 0; i < MAX_NUM_ELEMENTS; i++)
{
/* correct non-moving belts to start moving left */
for (i = 0; i < NUM_BELTS; i++)
- if (game.belt_dir[i] == MV_NO_MOVING)
+ if (game.belt_dir[i] == MV_NONE)
game.belt_dir_nr[i] = 3; /* not moving, next moving left */
/* check if any connected player was not found in playfield */
player->element_nr = some_player->element_nr;
#endif
+ player->artwork_element = some_player->artwork_element;
+
player->block_last_field = some_player->block_last_field;
player->block_delay_adjustment = some_player->block_delay_adjustment;
if (tape.playing)
{
- /* when playing a tape, eliminate all players which do not participate */
+ /* when playing a tape, eliminate all players who do not participate */
for (i = 0; i < MAX_PLAYERS; i++)
{
int start_x = 0, start_y = 0;
int found_rating = 0;
int found_element = EL_UNDEFINED;
+ int player_nr = local_player->index_nr;
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
int element = Feld[x][y];
int content;
int xx, yy;
boolean is_player;
+ if (level.use_start_element[player_nr] &&
+ level.start_element[player_nr] == element &&
+ found_rating < 4)
+ {
+ start_x = x;
+ start_y = y;
+
+ found_rating = 4;
+ found_element = element;
+ }
+
if (!IS_CUSTOM_ELEMENT(element))
continue;
{
for (i = 0; i < element_info[element].num_change_pages; i++)
{
+ /* check for player created from custom element as single target */
content = element_info[element].change_page[i].target_element;
is_player = ELEM_IS_PLAYER(content);
for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
{
- content = element_info[element].content[xx][yy];
+ /* check for player created from custom element as explosion content */
+ content = element_info[element].content.e[xx][yy];
is_player = ELEM_IS_PLAYER(content);
if (is_player && (found_rating < 2 || element < found_element))
for (i = 0; i < element_info[element].num_change_pages; i++)
{
- content= element_info[element].change_page[i].target_content[xx][yy];
+ /* check for player created from custom element as extended target */
+ content =
+ element_info[element].change_page[i].target_content.e[xx][yy];
+
is_player = ELEM_IS_PLAYER(content);
if (is_player && (found_rating < 1 || element < found_element))
MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
break;
+ case EL_YAMYAM_LEFT:
+ case EL_YAMYAM_RIGHT:
+ case EL_YAMYAM_UP:
+ case EL_YAMYAM_DOWN:
+ Feld[x][y] = EL_YAMYAM;
+ MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
+ break;
+
case EL_SP_SNIKSNAK:
MovDir[x][y] = MV_UP;
break;
if (move_direction_initial == MV_START_PREVIOUS)
{
- if (MovDir[x][y] != MV_NO_MOVING)
+ if (MovDir[x][y] != MV_NONE)
return;
move_direction_initial = MV_START_AUTOMATIC;
{
if (!tape.playing && !setup.sound_loops)
PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
- if (TimeLeft > 0 && !(TimeLeft % 10))
- RaiseScore(level.score[SC_TIME_BONUS]);
- if (TimeLeft > 100 && !(TimeLeft % 10))
+
+ if (TimeLeft > 100 && TimeLeft % 10 == 0)
+ {
TimeLeft -= 10;
+ RaiseScore(level.score[SC_TIME_BONUS] * 10);
+ }
else
+ {
TimeLeft--;
+ RaiseScore(level.score[SC_TIME_BONUS]);
+ }
DrawGameValue_Time(TimeLeft);
{
if (!tape.playing && !setup.sound_loops)
PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
- if (TimePlayed < 999 && !(TimePlayed % 10))
- RaiseScore(level.score[SC_TIME_BONUS]);
- if (TimePlayed < 900 && !(TimePlayed % 10))
+
+ if (TimePlayed < 900 && TimePlayed % 10 == 0)
+ {
TimePlayed += 10;
+ RaiseScore(level.score[SC_TIME_BONUS] * 10);
+ }
else
+ {
TimePlayed++;
+ RaiseScore(level.score[SC_TIME_BONUS]);
+ }
DrawGameValue_Time(TimePlayed);
PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
}
- /* Hero disappears */
+ /* player disappears */
if (ExitX >= 0 && ExitY >= 0)
DrawLevelField(ExitX, ExitY);
BackToFront();
+#if 0
+ if (tape.playing)
+ printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
+ else
+ printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
+#endif
+
if (tape.playing)
return;
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;
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 */
MovDir[newx][newy] = MovDir[x][y];
-#if USE_NEW_COLLECT_COUNT
- Count[newx][newy] = Count[x][y];
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[newx][newy] = CustomValue[x][y];
#endif
GfxFrame[newx][newy] = GfxFrame[x][y];
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;
MovDir[x][y] = 0;
MovDelay[x][y] = 0;
-#if USE_NEW_COLLECT_COUNT
- Count[x][y] = 0;
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[x][y] = 0;
#endif
AmoebaNr[x][y] = 0;
GfxElement[x][y] = EL_UNDEFINED;
GfxAction[x][y] = ACTION_DEFAULT;
- GfxDir[x][y] = MV_NO_MOVING;
+ GfxDir[x][y] = MV_NONE;
}
void RemoveMovingField(int x, int y)
Bang(x, y);
}
-void DrawRelocatePlayer(struct PlayerInfo *player)
+#if 1
+
+static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
+{
+ boolean num_checked_players = 0;
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active)
+ {
+ int sx = stored_player[i].jx;
+ int sy = stored_player[i].jy;
+
+ if (num_checked_players == 0)
+ {
+ *sx1 = *sx2 = sx;
+ *sy1 = *sy2 = sy;
+ }
+ else
+ {
+ *sx1 = MIN(*sx1, sx);
+ *sy1 = MIN(*sy1, sy);
+ *sx2 = MAX(*sx2, sx);
+ *sy2 = MAX(*sy2, sy);
+ }
+
+ num_checked_players++;
+ }
+ }
+}
+
+static boolean checkIfAllPlayersFitToScreen_RND()
+{
+ int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ return (sx2 - sx1 < SCR_FIELDX &&
+ sy2 - sy1 < SCR_FIELDY);
+}
+
+static void setScreenCenteredToAllPlayers(int *sx, int *sy)
+{
+ int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ *sx = (sx1 + sx2) / 2;
+ *sy = (sy1 + sy2) / 2;
+}
+
+#if 0
+static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
+ int center_x, int center_y)
+{
+ int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
+ *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
+}
+
+static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
+{
+ int max_dx, max_dy;
+
+ setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
+
+ return (max_dx <= SCR_FIELDX / 2 &&
+ max_dy <= SCR_FIELDY / 2);
+}
+#endif
+
+#endif
+
+#if 1
+
+void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
+ boolean quick_relocation)
+{
+ boolean ffwd_delay = (tape.playing && tape.fast_forward);
+ boolean no_delay = (tape.warp_forward);
+ int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+ int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+
+ if (quick_relocation)
+ {
+ int offset = (setup.scroll_delay ? 3 : 0);
+
+#if 0
+ if (center_screen)
+ offset = 0;
+#endif
+
+ if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
+ {
+ scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
+ x > SBX_Right + MIDPOSX ? SBX_Right :
+ x - MIDPOSX);
+
+ scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ y - MIDPOSY);
+ }
+ else
+ {
+ if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
+ (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
+ scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
+
+ if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
+ (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
+ scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
+
+ /* don't scroll over playfield boundaries */
+ if (scroll_x < SBX_Left || scroll_x > SBX_Right)
+ scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+
+ /* don't scroll over playfield boundaries */
+ if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
+ scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+ }
+
+ RedrawPlayfield(TRUE, 0,0,0,0);
+ }
+ else
+ {
+ int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
+ x > SBX_Right + MIDPOSX ? SBX_Right :
+ x - MIDPOSX);
+
+ int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ y - MIDPOSY);
+
+ ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
+
+ while (scroll_x != scroll_xx || scroll_y != scroll_yy)
+ {
+ int dx = 0, dy = 0;
+ int fx = FX, fy = FY;
+
+ dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
+ dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
+
+ if (dx == 0 && dy == 0) /* no scrolling needed at all */
+ break;
+
+ scroll_x -= dx;
+ scroll_y -= dy;
+
+ fx += dx * TILEX / 2;
+ fy += dy * TILEY / 2;
+
+ ScrollLevel(dx, dy);
+ DrawAllPlayers();
+
+ /* scroll in two steps of half tile size to make things smoother */
+ BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+ FlushDisplay();
+ Delay(wait_delay_value);
+
+ /* scroll second step to align at full tile size */
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+
+ DrawAllPlayers();
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+}
+
+#else
+
+void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
{
boolean ffwd_delay = (tape.playing && tape.fast_forward);
boolean no_delay = (tape.warp_forward);
int jx = player->jx;
int jy = player->jy;
- if (level.instant_relocation)
+ if (quick_relocation)
{
int offset = (setup.scroll_delay ? 3 : 0);
if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
{
- scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
- local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
- local_player->jx - MIDPOSX);
+ scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
+ player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+ player->jx - MIDPOSX);
- scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
- local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
- local_player->jy - MIDPOSY);
+ scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+ player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+ player->jy - MIDPOSY);
}
else
{
}
else
{
- int scroll_xx = -999, scroll_yy = -999;
+ int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
+ player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+ player->jx - MIDPOSX);
+
+ int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+ player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+ player->jy - MIDPOSY);
ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
- while (scroll_xx != scroll_x || scroll_yy != scroll_y)
+ while (scroll_x != scroll_xx || scroll_y != scroll_yy)
{
int dx = 0, dy = 0;
int fx = FX, fy = FY;
- scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
- local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
- local_player->jx - MIDPOSX);
-
- scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
- local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
- local_player->jy - MIDPOSY);
-
dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
}
}
+#endif
+
void RelocatePlayer(int jx, int jy, int el_player_raw)
{
- int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
- struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
+ int el_player = GET_PLAYER_ELEMENT(el_player_raw);
+ int player_nr = GET_PLAYER_NR(el_player);
+ struct PlayerInfo *player = &stored_player[player_nr];
boolean ffwd_delay = (tape.playing && tape.fast_forward);
boolean no_delay = (tape.warp_forward);
int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
CE_LEFT_BY_PLAYER,
player->index_bit, leave_side);
- CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
+ CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
+ CE_PLAYER_LEAVES_X,
player->index_bit, leave_side);
Feld[jx][jy] = el_player;
InitField(jx, jy, FALSE);
}
+#if 1
+ /* only visually relocate centered player */
+#if 1
+ DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
+ level.instant_relocation);
+#else
+ if (player->index_nr == game.centered_player_nr)
+ DrawRelocatePlayer(player, level.instant_relocation);
+#endif
+#else
if (player == local_player) /* only visually relocate local player */
- DrawRelocatePlayer(player);
+ DrawRelocatePlayer(player, level.instant_relocation);
+#endif
- TestIfHeroTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesBadThing(jx, jy);
TestIfPlayerTouchesCustomElement(jx, jy);
if (IS_CUSTOM_ELEMENT(element))
CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
player->index_bit, enter_side);
- CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_ENTERS_X,
+ CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
player->index_bit, enter_side);
}
if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
{
int center_element = Feld[ex][ey];
+ int artwork_element, explosion_element; /* set these values later */
#if 0
/* --- This is only really needed (and now handled) in "Impact()". --- */
return;
#endif
+#if 0
+ /* !!! at this place, the center element may be EL_BLOCKED !!! */
if (mode == EX_TYPE_NORMAL ||
mode == EX_TYPE_CENTER ||
mode == EX_TYPE_CROSS)
- PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
+ PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
+#endif
/* remove things displayed in background while burning dynamite */
if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
Feld[ex][ey] = center_element;
}
+ /* now "center_element" is finally determined -- set related values now */
+ artwork_element = center_element; /* for custom player artwork */
+ explosion_element = center_element; /* for custom player artwork */
+
+ if (IS_PLAYER(ex, ey))
+ {
+ int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
+
+ artwork_element = stored_player[player_nr].artwork_element;
+
+ if (level.use_explosion_element[player_nr])
+ {
+ explosion_element = level.explosion_element[player_nr];
+ artwork_element = explosion_element;
+ }
+ }
+
+#if 1
+ if (mode == EX_TYPE_NORMAL ||
+ mode == EX_TYPE_CENTER ||
+ mode == EX_TYPE_CROSS)
+ PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
+#endif
+
+#if 1
+ last_phase = element_info[explosion_element].explosion_delay + 1;
+#else
last_phase = element_info[center_element].explosion_delay + 1;
+#endif
for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
{
if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
{
+#if 1
+ int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
+
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
+#else
switch(StorePlayer[ex][ey])
{
case EL_PLAYER_2:
Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
break;
}
+#endif
- if (PLAYERINFO(ex, ey)->use_murphy_graphic)
+ if (PLAYERINFO(ex, ey)->use_murphy)
Store[x][y] = EL_EMPTY;
}
+#if 1
+ /* !!! check this case -- currently needed for rnd_rado_negundo_v,
+ !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
+ else if (ELEM_IS_PLAYER(center_element))
+ Store[x][y] = EL_EMPTY;
+ else if (center_element == EL_YAMYAM)
+ Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
+ else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
+ Store[x][y] = element_info[center_element].content.e[xx][yy];
+#if 1
+ /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
+ (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
+ otherwise) -- FIX THIS !!! */
+ else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
+ Store[x][y] = element_info[element].content.e[1][1];
+#else
+ else if (!CAN_EXPLODE(element))
+ Store[x][y] = element_info[element].content.e[1][1];
+#endif
+ else
+ Store[x][y] = EL_EMPTY;
+#else
else if (center_element == EL_MOLE)
Store[x][y] = EL_EMERALD_RED;
else if (center_element == EL_PENGUIN)
else if (center_element == EL_AMOEBA_TO_DIAMOND)
Store[x][y] = level.amoeba_content;
else if (center_element == EL_YAMYAM)
- Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
+ Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
else if (IS_CUSTOM_ELEMENT(center_element) &&
- element_info[center_element].content[xx][yy] != EL_EMPTY)
- Store[x][y] = element_info[center_element].content[xx][yy];
+ element_info[center_element].content.e[xx][yy] != EL_EMPTY)
+ Store[x][y] = element_info[center_element].content.e[xx][yy];
else if (element == EL_WALL_EMERALD)
Store[x][y] = EL_EMERALD;
else if (element == EL_WALL_DIAMOND)
else if (element == EL_WALL_CRYSTAL)
Store[x][y] = EL_CRYSTAL;
else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
- Store[x][y] = element_info[element].content[1][1];
+ Store[x][y] = element_info[element].content.e[1][1];
else
Store[x][y] = EL_EMPTY;
+#endif
if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
center_element == EL_AMOEBA_TO_DIAMOND)
Store2[x][y] = element;
Feld[x][y] = EL_EXPLOSION;
- GfxElement[x][y] = center_element;
+ GfxElement[x][y] = artwork_element;
+
+#if 0
+ printf(":: setting gfx(%d,%d) to %d ['%s']\n",
+ x, y, artwork_element, EL_NAME(artwork_element));
+#endif
ExplodePhase[x][y] = 1;
ExplodeDelay[x][y] = last_phase;
#endif
#if 1
+#if 1
+ /* this can happen if the player leaves an explosion just in time */
+ if (GfxElement[x][y] == EL_UNDEFINED)
+ GfxElement[x][y] = EL_EMPTY;
+#else
if (GfxElement[x][y] == EL_UNDEFINED)
{
printf("\n\n");
GfxElement[x][y] = EL_EMPTY;
}
+#endif
+
#endif
border_element = Store2[x][y];
if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
!PLAYER_EXPLOSION_PROTECTED(x, y))
{
- KillHeroUnlessExplosionProtected(x, y);
+ KillPlayerUnlessExplosionProtected(x, y);
border_explosion = TRUE;
}
else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
/* player can escape from explosions and might therefore be still alive */
if (element >= EL_PLAYER_IS_EXPLODING_1 &&
element <= EL_PLAYER_IS_EXPLODING_4)
- Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
- EL_EMPTY :
- element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
- element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
- element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
- EL_EMERALD_PURPLE);
+ {
+ int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
+ int explosion_element = EL_PLAYER_1 + player_nr;
+ int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
+ int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
+
+ if (level.use_explosion_element[player_nr])
+ explosion_element = level.explosion_element[player_nr];
+
+ Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
+ element_info[explosion_element].content.e[xx][yy]);
+ }
/* restore probably existing indestructible background element */
if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
Back[x][y] = 0;
MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
- GfxDir[x][y] = MV_NO_MOVING;
+ GfxDir[x][y] = MV_NONE;
ChangeDelay[x][y] = 0;
ChangePage[x][y] = -1;
-#if USE_NEW_COLLECT_COUNT
- Count[x][y] = 0;
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[x][y] = 0;
#endif
InitField_WithBug2(x, y, FALSE);
void Bang(int x, int y)
{
int element = MovingOrBlocked2Element(x, y);
+ int explosion_type = EX_TYPE_NORMAL;
if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
{
struct PlayerInfo *player = PLAYERINFO(x, y);
- element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
+ element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
player->element_nr);
+
+ if (level.use_explosion_element[player->index_nr])
+ {
+ int explosion_element = level.explosion_element[player->index_nr];
+
+ if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
+ explosion_type = EX_TYPE_CROSS;
+ else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
+ explosion_type = EX_TYPE_CENTER;
+ }
}
switch(element)
case EL_PACMAN:
case EL_MOLE:
RaiseScoreElement(element);
- Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
break;
+
case EL_DYNABOMB_PLAYER_1_ACTIVE:
case EL_DYNABOMB_PLAYER_2_ACTIVE:
case EL_DYNABOMB_PLAYER_3_ACTIVE:
case EL_DYNABOMB_INCREASE_NUMBER:
case EL_DYNABOMB_INCREASE_SIZE:
case EL_DYNABOMB_INCREASE_POWER:
- DynaExplode(x, y);
+ explosion_type = EX_TYPE_DYNA;
break;
+
case EL_PENGUIN:
case EL_LAMP:
case EL_LAMP_ACTIVE:
case EL_AMOEBA_TO_DIAMOND:
- if (IS_PLAYER(x, y))
- Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
- else
- Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
+ if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
+ explosion_type = EX_TYPE_CENTER;
break;
+
default:
if (element_info[element].explosion_type == EXPLODES_CROSS)
- Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
+ explosion_type = EX_TYPE_CROSS;
else if (element_info[element].explosion_type == EXPLODES_1X1)
- Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
- else
- Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
+ explosion_type = EX_TYPE_CENTER;
break;
}
- CheckTriggeredElementChange(element, CE_EXPLOSION_OF_X);
+ if (explosion_type == EX_TYPE_DYNA)
+ DynaExplode(x, y);
+ else
+ Explode(x, y, EX_PHASE_START, explosion_type);
+
+ CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
}
void SplashAcid(int x, int y)
}
}
- for (y = 0; y < lev_fieldy; y++)
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
- for (x = 0; x < lev_fieldx; x++)
- {
- int element = Feld[x][y];
+ int element = Feld[x][y];
- for (i = 0; i < NUM_BELTS; i++)
+ for (i = 0; i < NUM_BELTS; i++)
+ {
+ if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
{
- if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
- {
- int e_belt_nr = getBeltNrFromBeltElement(element);
- int belt_nr = i;
+ int e_belt_nr = getBeltNrFromBeltElement(element);
+ int belt_nr = i;
- if (e_belt_nr == belt_nr)
- {
- int belt_part = Feld[x][y] - belt_base_element[belt_nr];
+ if (e_belt_nr == belt_nr)
+ {
+ int belt_part = Feld[x][y] - belt_base_element[belt_nr];
- Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
- }
+ Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
}
}
}
static int belt_move_dir[4] =
{
MV_LEFT,
- MV_NO_MOVING,
+ MV_NONE,
MV_RIGHT,
- MV_NO_MOVING,
+ MV_NONE,
};
int element = Feld[x][y];
graphic_info[graphic].anim_mode |= ANIM_REVERSE;
}
- for (yy = 0; yy < lev_fieldy; yy++)
+#if 1
+ SCAN_PLAYFIELD(xx, yy)
+#else
+ for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
+#endif
{
- for (xx = 0; xx < lev_fieldx; xx++)
+ int element = Feld[xx][yy];
+
+ if (IS_BELT_SWITCH(element))
{
- int element = Feld[xx][yy];
+ int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
- if (IS_BELT_SWITCH(element))
+ if (e_belt_nr == belt_nr)
{
- int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
-
- if (e_belt_nr == belt_nr)
- {
- Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
- DrawLevelField(xx, yy);
- }
+ Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
+ DrawLevelField(xx, yy);
}
- else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
- {
- int e_belt_nr = getBeltNrFromBeltElement(element);
+ }
+ else if (IS_BELT(element) && belt_dir != MV_NONE)
+ {
+ int e_belt_nr = getBeltNrFromBeltElement(element);
- if (e_belt_nr == belt_nr)
- {
- int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
+ if (e_belt_nr == belt_nr)
+ {
+ int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
- Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
- DrawLevelField(xx, yy);
- }
+ Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
+ DrawLevelField(xx, yy);
}
- else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
- {
- int e_belt_nr = getBeltNrFromBeltActiveElement(element);
+ }
+ else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
+ {
+ int e_belt_nr = getBeltNrFromBeltActiveElement(element);
- if (e_belt_nr == belt_nr)
- {
- int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
+ if (e_belt_nr == belt_nr)
+ {
+ int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
- Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
- DrawLevelField(xx, yy);
- }
+ Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
+ DrawLevelField(xx, yy);
}
}
}
game.switchgate_pos = !game.switchgate_pos;
- for (yy = 0; yy < lev_fieldy; yy++)
+#if 1
+ SCAN_PLAYFIELD(xx, yy)
+#else
+ for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
+#endif
{
- for (xx = 0; xx < lev_fieldx; xx++)
- {
- int element = Feld[xx][yy];
+ int element = Feld[xx][yy];
- if (element == EL_SWITCHGATE_SWITCH_UP ||
- element == EL_SWITCHGATE_SWITCH_DOWN)
- {
- Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
- DrawLevelField(xx, yy);
- }
- else if (element == EL_SWITCHGATE_OPEN ||
- element == EL_SWITCHGATE_OPENING)
- {
- Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
+ if (element == EL_SWITCHGATE_SWITCH_UP ||
+ element == EL_SWITCHGATE_SWITCH_DOWN)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
+ DrawLevelField(xx, yy);
+ }
+ else if (element == EL_SWITCHGATE_OPEN ||
+ element == EL_SWITCHGATE_OPENING)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
- PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
- }
- else if (element == EL_SWITCHGATE_CLOSED ||
- element == EL_SWITCHGATE_CLOSING)
- {
- Feld[xx][yy] = EL_SWITCHGATE_OPENING;
+ PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
+ }
+ else if (element == EL_SWITCHGATE_CLOSED ||
+ element == EL_SWITCHGATE_CLOSING)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_OPENING;
- PlayLevelSoundAction(xx, yy, ACTION_OPENING);
- }
+ PlayLevelSoundAction(xx, yy, ACTION_OPENING);
}
}
}
{
int x, y;
- for (y = 0; y < lev_fieldy; y++)
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
- for (x = 0; x < lev_fieldx; x++)
+ int element = Feld[x][y];
+
+ if (element == EL_LIGHT_SWITCH &&
+ game.light_time_left > 0)
{
- int element = Feld[x][y];
+ Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_LIGHT_SWITCH_ACTIVE &&
+ game.light_time_left == 0)
+ {
+ Feld[x][y] = EL_LIGHT_SWITCH;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_DRIPPER &&
+ game.light_time_left > 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_DRIPPER_ACTIVE &&
+ game.light_time_left == 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL ||
+ element == EL_INVISIBLE_WALL ||
+ element == EL_INVISIBLE_SAND)
+ {
+ if (game.light_time_left > 0)
+ Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
- if (element == EL_LIGHT_SWITCH &&
- game.light_time_left > 0)
- {
- Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
- DrawLevelField(x, y);
- }
- else if (element == EL_LIGHT_SWITCH_ACTIVE &&
- game.light_time_left == 0)
- {
- Feld[x][y] = EL_LIGHT_SWITCH;
- DrawLevelField(x, y);
- }
- else if (element == EL_INVISIBLE_STEELWALL ||
- element == EL_INVISIBLE_WALL ||
- element == EL_INVISIBLE_SAND)
- {
- if (game.light_time_left > 0)
- Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+ DrawLevelField(x, y);
- DrawLevelField(x, y);
+ /* uncrumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
+ element == EL_INVISIBLE_WALL_ACTIVE ||
+ element == EL_INVISIBLE_SAND_ACTIVE)
+ {
+ if (game.light_time_left == 0)
+ Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
- /* uncrumble neighbour fields, if needed */
- if (element == EL_INVISIBLE_SAND)
- DrawLevelFieldCrumbledSandNeighbours(x, y);
- }
- else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
- element == EL_INVISIBLE_WALL_ACTIVE ||
- element == EL_INVISIBLE_SAND_ACTIVE)
- {
- if (game.light_time_left == 0)
- Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+ DrawLevelField(x, y);
- DrawLevelField(x, y);
+ /* re-crumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+ }
+}
- /* re-crumble neighbour fields, if needed */
- if (element == EL_INVISIBLE_SAND)
- DrawLevelFieldCrumbledSandNeighbours(x, y);
- }
+static void RedrawAllInvisibleElementsForLenses()
+{
+ int x, y;
+
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
+ {
+ int element = Feld[x][y];
+
+ if (element == EL_EMC_DRIPPER &&
+ game.lenses_time_left > 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_DRIPPER_ACTIVE &&
+ game.lenses_time_left == 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL ||
+ element == EL_INVISIBLE_WALL ||
+ element == EL_INVISIBLE_SAND)
+ {
+ if (game.lenses_time_left > 0)
+ Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+
+ DrawLevelField(x, y);
+
+ /* uncrumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
+ element == EL_INVISIBLE_WALL_ACTIVE ||
+ element == EL_INVISIBLE_SAND_ACTIVE)
+ {
+ if (game.lenses_time_left == 0)
+ Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+
+ DrawLevelField(x, y);
+
+ /* re-crumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+ }
+}
+
+static void RedrawAllInvisibleElementsForMagnifier()
+{
+ int x, y;
+
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
+ {
+ int element = Feld[x][y];
+
+ if (element == EL_EMC_FAKE_GRASS &&
+ game.magnify_time_left > 0)
+ {
+ Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
+ game.magnify_time_left == 0)
+ {
+ Feld[x][y] = EL_EMC_FAKE_GRASS;
+ DrawLevelField(x, y);
+ }
+ else if (IS_GATE_GRAY(element) &&
+ game.magnify_time_left > 0)
+ {
+ Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
+ element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
+ IS_EM_GATE_GRAY(element) ?
+ element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
+ IS_EMC_GATE_GRAY(element) ?
+ element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
+ element);
+ DrawLevelField(x, y);
+ }
+ else if (IS_GATE_GRAY_ACTIVE(element) &&
+ game.magnify_time_left == 0)
+ {
+ Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
+ element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
+ IS_EM_GATE_GRAY_ACTIVE(element) ?
+ element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
+ IS_EMC_GATE_GRAY_ACTIVE(element) ?
+ element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
+ element);
+ DrawLevelField(x, y);
}
}
}
game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
- for (yy = 0; yy < lev_fieldy; yy++)
+#if 1
+ SCAN_PLAYFIELD(xx, yy)
+#else
+ for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
+#endif
{
- for (xx = 0; xx < lev_fieldx; xx++)
- {
- int element = Feld[xx][yy];
-
- if (element == EL_TIMEGATE_CLOSED ||
- element == EL_TIMEGATE_CLOSING)
- {
- Feld[xx][yy] = EL_TIMEGATE_OPENING;
- PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
- }
+ int element = Feld[xx][yy];
- /*
- else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
- {
- Feld[xx][yy] = EL_TIMEGATE_SWITCH;
- DrawLevelField(xx, yy);
- }
- */
+ if (element == EL_TIMEGATE_CLOSED ||
+ element == EL_TIMEGATE_CLOSING)
+ {
+ Feld[xx][yy] = EL_TIMEGATE_OPENING;
+ PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
+ }
+ /*
+ else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
+ {
+ Feld[xx][yy] = EL_TIMEGATE_SWITCH;
+ DrawLevelField(xx, yy);
}
+ */
+
}
Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
object_hit = FALSE;
+#if USE_QUICKSAND_IMPACT_BUGFIX
+ if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
+ {
+ RemoveMovingField(x, y + 1);
+ Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
+ Feld[x][y + 2] = EL_ROCK;
+ DrawLevelField(x, y + 2);
+
+ object_hit = TRUE;
+ }
+#endif
+
if (object_hit)
smashed = MovingOrBlocked2Element(x, y + 1);
if (impact && element == EL_AMOEBA_DROP)
{
if (object_hit && IS_PLAYER(x, y + 1))
- KillHeroUnlessEnemyProtected(x, y + 1);
+ KillPlayerUnlessEnemyProtected(x, y + 1);
else if (object_hit && smashed == EL_PENGUIN)
Bang(x, y + 1);
else
EL_BD_MAGIC_WALL_ACTIVE);
/* activate magic wall / mill */
- for (yy = 0; yy < lev_fieldy; yy++)
- for (xx = 0; xx < lev_fieldx; xx++)
- if (Feld[xx][yy] == smashed)
- Feld[xx][yy] = activated_magic_wall;
+#if 1
+ SCAN_PLAYFIELD(xx, yy)
+#else
+ for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
+#endif
+ if (Feld[xx][yy] == smashed)
+ Feld[xx][yy] = activated_magic_wall;
game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
game.magic_wall_active = TRUE;
{
if (CAN_SMASH_PLAYER(element))
{
- KillHeroUnlessEnemyProtected(x, y + 1);
+ KillPlayerUnlessEnemyProtected(x, y + 1);
return;
}
}
CheckElementChangeBySide(x, y + 1, smashed, element,
CE_SWITCHED, CH_SIDE_TOP);
- CheckTriggeredElementChangeBySide(smashed, CE_SWITCH_OF_X,
+ CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
CH_SIDE_TOP);
}
}
{
static struct
{
- int x, y;
+ int dx, dy;
} move_xy[] =
{
{ 0, 0 },
int right_dir = turn[old_move_dir].right;
int back_dir = turn[old_move_dir].back;
- int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
- int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
- int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
- int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
+ int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
+ int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
+ int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
+ int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
int left_x = x + left_dx, left_y = y + left_dy;
int right_x = x + right_dx, right_y = y + right_dy;
else
MovDir[x][y] = back_dir;
- xx = x + move_xy[MovDir[x][y]].x;
- yy = y + move_xy[MovDir[x][y]].y;
+ xx = x + move_xy[MovDir[x][y]].dx;
+ yy = y + move_xy[MovDir[x][y]].dy;
if (!IN_LEV_FIELD(xx, yy) ||
(!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
else
MovDir[x][y] = back_dir;
- xx = x + move_xy[MovDir[x][y]].x;
- yy = y + move_xy[MovDir[x][y]].y;
+ xx = x + move_xy[MovDir[x][y]].dx;
+ yy = y + move_xy[MovDir[x][y]].dy;
if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
MovDir[x][y] = old_move_dir;
}
else if (element == EL_BALLOON)
{
- MovDir[x][y] = game.balloon_dir;
+ MovDir[x][y] = game.wind_direction;
MovDelay[x][y] = 0;
}
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_NO_MOVING;
+ 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;
}
}
- MovDir[x][y] = MV_NO_MOVING;
+ MovDir[x][y] = MV_NONE;
if (attr_x < x)
MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
else if (attr_x > x)
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 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 ||
MovDir[x][y] = move_pattern;
MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
}
+ else if (move_pattern & MV_WIND_DIRECTION)
+ {
+ MovDir[x][y] = game.wind_direction;
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+ }
else if (move_pattern == MV_ALONG_LEFT_SIDE)
{
if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
}
}
- MovDir[x][y] = MV_NO_MOVING;
+ MovDir[x][y] = MV_NONE;
if (attr_x < x)
MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
else if (attr_x > x)
move_pattern == MV_WHEN_DROPPED)
{
if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
- MovDir[x][y] = MV_NO_MOVING;
+ MovDir[x][y] = MV_NONE;
MovDelay[x][y] = 0;
}
};
boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
int move_preference = -1000000; /* start with very low preference */
- int new_move_dir = MV_NO_MOVING;
+ int new_move_dir = MV_NONE;
int start_test = RND(4);
int i;
static void TurnRound(int x, int y)
{
int direction = MovDir[x][y];
+#if 1
+ int element, graphic;
+#endif
TurnRoundExt(x, 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)
}
else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
{
- if (MovDir[x][y] == MV_NO_MOVING)
+ if (MovDir[x][y] == MV_NONE)
{
InitMovingField(x, y, MV_DOWN);
started_moving = TRUE;
Feld[x + 1][y + 1] == EL_ACID));
boolean can_fall_any = (can_fall_left || can_fall_right);
boolean can_fall_both = (can_fall_left && can_fall_right);
+ int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
- if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
+#if USE_NEW_ALL_SLIPPERY
+ if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
{
- int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
-
- if (slippery_type == SLIPPERY_ONLY_LEFT)
+ if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
can_fall_right = FALSE;
- else if (slippery_type == SLIPPERY_ONLY_RIGHT)
+ else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
can_fall_left = FALSE;
- else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
+ else if (slippery_type == SLIPPERY_ONLY_LEFT)
can_fall_right = FALSE;
- else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
+ else if (slippery_type == SLIPPERY_ONLY_RIGHT)
can_fall_left = FALSE;
can_fall_any = (can_fall_left || can_fall_right);
- can_fall_both = (can_fall_left && can_fall_right);
+ can_fall_both = FALSE;
}
+#else
+ if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
+ {
+ if (slippery_type == SLIPPERY_ONLY_LEFT)
+ can_fall_right = FALSE;
+ else if (slippery_type == SLIPPERY_ONLY_RIGHT)
+ can_fall_left = FALSE;
+ else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
+ can_fall_right = FALSE;
+ else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
+ can_fall_left = FALSE;
+
+ can_fall_any = (can_fall_left || can_fall_right);
+ can_fall_both = (can_fall_left && can_fall_right);
+ }
+#endif
+#if USE_NEW_ALL_SLIPPERY
+#else
#if USE_NEW_SP_SLIPPERY
/* !!! better use the same properties as for custom elements here !!! */
else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
can_fall_both = FALSE;
}
#endif
+#endif
+
+#if USE_NEW_ALL_SLIPPERY
+ if (can_fall_both)
+ {
+ if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
+ can_fall_right = FALSE; /* slip down on left side */
+ else
+ can_fall_left = !(can_fall_right = RND(2));
+ can_fall_both = FALSE;
+ }
+#else
if (can_fall_both)
{
if (game.emulation == EMU_BOULDERDASH ||
can_fall_both = FALSE;
}
+#endif
if (can_fall_any)
{
/* not "else if" because of elements that can fall and move (EL_SPRING) */
#if 0
- if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
+ if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
#else
if (CAN_MOVE(element) && !started_moving)
#endif
#if 0
#if DEBUG
- if (MovDir[x][y] == MV_NO_MOVING)
+ if (MovDir[x][y] == MV_NONE)
{
printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
x, y, element, element_info[element].token_name);
IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
!PLAYER_ENEMY_PROTECTED(newx, newy))
{
- TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
+ TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
return;
}
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)))
{
}
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_NO_MOVING;
+ GfxDir[x][y] = MovDir[x][y] = MV_NONE;
}
else if (!IS_FREE(newx, newy))
{
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))
{
}
Store[newx][newy] = EL_EMPTY;
+#if 1
+ /* this makes it possible to leave the removed element again */
+ if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
+ Store[newx][newy] = new_element;
+#else
if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
{
int move_leave_element = element_info[element].move_leave_element;
Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
new_element : move_leave_element);
}
+#endif
if (move_pattern & MV_MAZE_RUNNER_STYLE)
{
DrawLevelElementAnimation(x, y, element);
if (DONT_TOUCH(element))
- TestIfBadThingTouchesHero(x, y);
+ TestIfBadThingTouchesPlayer(x, y);
return;
}
void ContinueMoving(int x, int y)
{
int element = Feld[x][y];
- int stored = Store[x][y];
struct ElementInfo *ei = &element_info[element];
int direction = MovDir[x][y];
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, newy = y + dy;
+ int stored = Store[x][y];
+ int stored_new = Store[newx][newy];
boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
boolean last_line = (newy == lev_fieldy - 1);
Feld[x][y] = EL_MAGIC_WALL_DEAD;
element = Feld[newx][newy] = Store[x][y];
-#if USE_NEW_COLLECT_COUNT
+#if USE_NEW_CUSTOM_VALUE
InitField(newx, newy, FALSE);
#endif
}
Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
element = Feld[newx][newy] = Store[x][y];
-#if USE_NEW_COLLECT_COUNT
+#if USE_NEW_CUSTOM_VALUE
InitField(newx, newy, FALSE);
#endif
}
MovDelay[newx][newy] = 0;
+#if 1
+ if (CAN_CHANGE_OR_HAS_ACTION(element))
+#else
if (CAN_CHANGE(element))
+#endif
{
/* copy element change control values to new field */
ChangeDelay[newx][newy] = ChangeDelay[x][y];
ChangePage[newx][newy] = ChangePage[x][y];
- Changed[newx][newy] = Changed[x][y];
+ ChangeCount[newx][newy] = ChangeCount[x][y];
ChangeEvent[newx][newy] = ChangeEvent[x][y];
-#if USE_NEW_COLLECT_COUNT
- Count[newx][newy] = Count[x][y];
+#if 0
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[newx][newy] = CustomValue[x][y];
+#endif
#endif
}
+#if 1
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[newx][newy] = CustomValue[x][y];
+#endif
+#endif
+
ChangeDelay[x][y] = 0;
ChangePage[x][y] = -1;
- Changed[x][y] = FALSE;
+ ChangeCount[x][y] = 0;
ChangeEvent[x][y] = -1;
-#if USE_NEW_COLLECT_COUNT
- Count[x][y] = 0;
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[x][y] = 0;
#endif
/* copy animation control values to new field */
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 &&
ei->move_leave_element == EL_TRIGGER_ELEMENT)
move_leave_element = stored;
+#endif
Feld[x][y] = move_leave_element;
int nextx = newx + dx, nexty = newy + dy;
boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
- WasJustMoving[newx][newy] = 3;
+ WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
if (CAN_FALL(element) && direction == MV_DOWN)
- WasJustFalling[newx][newy] = 3;
+ WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
- CheckCollision[newx][newy] = 2;
+ CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
}
if (DONT_TOUCH(element)) /* object may be nasty to player or others */
{
- TestIfBadThingTouchesHero(newx, newy);
+ TestIfBadThingTouchesPlayer(newx, newy);
TestIfBadThingTouchesFriend(newx, newy);
if (!IS_CUSTOM_ELEMENT(element))
if (pushed_by_player && !game.use_change_when_pushing_bug)
{
- int dig_side = MV_DIR_OPPOSITE(direction);
+ int push_side = MV_DIR_OPPOSITE(direction);
struct PlayerInfo *player = PLAYERINFO(x, y);
CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
- player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
- player->index_bit, dig_side);
+ player->index_bit, push_side);
+ CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
+ 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);
+
+#if 1
+ if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
+ IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
+ CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
+ MV_DIR_OPPOSITE(direction));
+#endif
}
int AmoebeNachbarNr(int ax, int ay)
AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
AmoebaCnt2[old_group_nr] = 0;
- for (yy = 0; yy < lev_fieldy; yy++)
+#if 1
+ SCAN_PLAYFIELD(xx, yy)
+#else
+ for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
+#endif
{
- for (xx = 0; xx < lev_fieldx; xx++)
- {
- if (AmoebaNr[xx][yy] == old_group_nr)
- AmoebaNr[xx][yy] = new_group_nr;
- }
+ if (AmoebaNr[xx][yy] == old_group_nr)
+ AmoebaNr[xx][yy] = new_group_nr;
}
}
}
}
#endif
- for (y = 0; y < lev_fieldy; y++)
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
- for (x = 0; x < lev_fieldx; x++)
+ if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
{
- if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
- {
- AmoebaNr[x][y] = 0;
- Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
- }
+ AmoebaNr[x][y] = 0;
+ Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
}
}
+
PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
SND_AMOEBA_TURNING_TO_GEM :
SND_AMOEBA_TURNING_TO_ROCK));
}
#endif
- for (y = 0; y < lev_fieldy; y++)
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
- for (x = 0; x < lev_fieldx; x++)
+ if (AmoebaNr[x][y] == group_nr &&
+ (Feld[x][y] == EL_AMOEBA_DEAD ||
+ Feld[x][y] == EL_BD_AMOEBA ||
+ Feld[x][y] == EL_AMOEBA_GROWING))
{
- if (AmoebaNr[x][y] == group_nr &&
- (Feld[x][y] == EL_AMOEBA_DEAD ||
- Feld[x][y] == EL_BD_AMOEBA ||
- Feld[x][y] == EL_AMOEBA_GROWING))
- {
- AmoebaNr[x][y] = 0;
- Feld[x][y] = new_element;
- InitField(x, y, FALSE);
- DrawLevelField(x, y);
- done = TRUE;
- }
+ AmoebaNr[x][y] = 0;
+ Feld[x][y] = new_element;
+ InitField(x, y, FALSE);
+ DrawLevelField(x, y);
+ done = TRUE;
}
}
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 */
void Life(int ax, int ay)
{
int x1, y1, x2, y2;
+#if 0
static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
+#endif
int life_time = 40;
int element = Feld[ax][ay];
int graphic = el2img(element);
+ int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
+ level.biomaze);
boolean changed = FALSE;
if (IS_ANIMATED(graphic))
if (xx == ax && yy == ay) /* field in the middle */
{
- if (nachbarn < life[0] || nachbarn > life[1])
+ if (nachbarn < life_parameter[0] ||
+ nachbarn > life_parameter[1])
{
Feld[xx][yy] = EL_EMPTY;
if (!Stop[xx][yy])
}
else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
{ /* free border field */
- if (nachbarn >= life[2] && nachbarn <= life[3])
+ if (nachbarn >= life_parameter[2] &&
+ nachbarn <= life_parameter[3])
{
Feld[xx][yy] = element;
MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
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 ||
{
int x, y;
- for (y = 0; y < lev_fieldy; y++)
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
- for (x = 0; x < lev_fieldx; x++)
- {
- int element = Feld[x][y];
+ int element = Feld[x][y];
- if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
- {
- Feld[x][y] = EL_TIMEGATE_CLOSING;
+ if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
+ {
+ Feld[x][y] = EL_TIMEGATE_CLOSING;
- PlayLevelSoundAction(x, y, ACTION_CLOSING);
- }
+ PlayLevelSoundAction(x, y, ACTION_CLOSING);
}
}
}
Feld[x][y] = Store[x][y];
Store[x][y] = 0;
- GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
+ GfxDir[x][y] = MovDir[x][y] = MV_NONE;
DrawLevelField(x, y);
}
}
for (i = 0; i < NUM_DIRECTIONS; i++)
{
- int xx = x + xy[i][0], yy = y + xy[i][1];
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
- if (IS_PLAYER(xx, yy))
+ if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
{
PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
static int getModifiedActionNumber(int value_old, int operator, int operand,
int value_min, int value_max)
{
- int value_new = (operator == CA_MODE_ADD ? value_old + operand :
+ int value_new = (operator == CA_MODE_SET ? operand :
+ operator == CA_MODE_ADD ? value_old + operand :
operator == CA_MODE_SUBTRACT ? value_old - operand :
operator == CA_MODE_MULTIPLY ? value_old * operand :
operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
- operator == CA_MODE_SET ? operand :
+ operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
value_old);
return (value_new < value_min ? value_min :
{
struct ElementInfo *ei = &element_info[element];
struct ElementChangeInfo *change = &ei->change_page[page];
+ int target_element = change->target_element;
int action_type = change->action_type;
int action_mode = change->action_mode;
int action_arg = change->action_arg;
if (!change->has_action)
return;
- /* ---------- determine action paramater values ---------- */
+ /* ---------- determine action paramater values -------------------------- */
+
+ int level_time_value =
+ (level.time > 0 ? TimeLeft :
+ TimePlayed);
int action_arg_element =
(action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
EL_EMPTY);
+ int action_arg_direction =
+ (action_arg >= CA_ARG_DIRECTION_LEFT &&
+ action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
+ action_arg == CA_ARG_DIRECTION_TRIGGER ?
+ change->actual_trigger_side :
+ action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
+ MV_DIR_OPPOSITE(change->actual_trigger_side) :
+ 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_GEMS ? 999 :
- action_type == CA_SET_TIME ? 9999 :
- action_type == CA_SET_SCORE ? 99999 :
+ (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 :
+ action_type == CA_SET_CE_VALUE ? 9999 :
action_type == CA_SET_CE_SCORE ? 9999 :
- action_type == CA_SET_CE_COUNT ? 9999 :
CA_ARG_MAX);
int action_arg_number_reset =
- (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
- action_type == CA_SET_GEMS ? level.gems_needed :
- action_type == CA_SET_TIME ? level.time :
- action_type == CA_SET_SCORE ? 0 :
+ (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 :
+#if 1
+ action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
+#else
+ action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
+#endif
action_type == CA_SET_CE_SCORE ? 0 :
- action_type == CA_SET_CE_COUNT ? ei->collect_count_initial :
0);
- int action_arg_number_normal =
- (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_NORMAL :
- action_arg_number_reset);
-
int action_arg_number =
(action_arg <= CA_ARG_MAX ? action_arg :
+ action_arg >= CA_ARG_SPEED_NOT_MOVING &&
+ action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
+ action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
- action_arg == CA_ARG_NUMBER_NORMAL ? action_arg_number_normal :
- action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
-#if USE_NEW_COLLECT_COUNT
- action_arg == CA_ARG_NUMBER_CE_COUNT ? Count[x][y] :
+#if USE_NEW_CUSTOM_VALUE
+ action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
#else
- action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count_initial :
+ action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
#endif
- action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
+ action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
+ action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
+ 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_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
+ action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
+ action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
+ action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
+ 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 =
- (action_type == CA_SET_GEMS ? local_player->gems_still_needed :
- action_type == CA_SET_TIME ? TimeLeft :
- action_type == CA_SET_SCORE ? local_player->score :
+ (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
+ action_type == CA_SET_LEVEL_TIME ? TimeLeft :
+ action_type == CA_SET_LEVEL_SCORE ? local_player->score :
+ action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
action_type == CA_SET_CE_SCORE ? ei->collect_score :
- action_type == CA_SET_CE_COUNT ? Count[x][y] :
0);
int action_arg_number_new =
action_mode, action_arg_number,
action_arg_number_min, action_arg_number_max);
- /* (for explicit player choice, set invalid value to "no player") */
- int action_arg_player_bits =
- (action_arg == CA_ARG_PLAYER_ANY ? action_arg - CA_ARG_PLAYER :
- action_arg >= CA_ARG_PLAYER_1 &&
- action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
- action_arg >= CA_ARG_1 &&
- action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - 1)) :
- action_arg_element >= EL_PLAYER_1 &&
- action_arg_element <= EL_PLAYER_4 ?
- (1 << (action_arg_element - EL_PLAYER_1)) :
- 0);
-
- /* (for implicit player choice, set invalid value to "all players") */
int trigger_player_bits =
(change->actual_trigger_player >= EL_PLAYER_1 &&
change->actual_trigger_player <= EL_PLAYER_4 ?
(1 << (change->actual_trigger_player - EL_PLAYER_1)) :
PLAYER_BITS_ANY);
- /* ---------- execute action ---------- */
+ int action_arg_player_bits =
+ (action_arg >= CA_ARG_PLAYER_1 &&
+ action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
+ action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
+ PLAYER_BITS_ANY);
+
+ /* ---------- execute action -------------------------------------------- */
switch(action_type)
{
return;
}
- case CA_EXIT_PLAYER:
- {
- for (i = 0; i < MAX_PLAYERS; i++)
- if (action_arg_player_bits & (1 << i))
- stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
-
- break;
- }
-
- case CA_KILL_PLAYER:
- {
- for (i = 0; i < MAX_PLAYERS; i++)
- if (action_arg_player_bits & (1 << i))
- KillHero(&stored_player[i]);
-
- break;
- }
+ /* ---------- level actions ------------------------------------------- */
case CA_RESTART_LEVEL:
{
break;
}
- case CA_ADD_KEY:
+ case CA_SET_LEVEL_TIME:
{
- int element = getSpecialActionElement(action_arg_element,
- action_arg_number, EL_KEY_1);
-
- if (IS_KEY(element))
+ if (level.time > 0) /* only modify limited time value */
{
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- if (trigger_player_bits & (1 << i))
- {
- stored_player[i].key[KEY_NR(element)] = TRUE;
+ TimeLeft = action_arg_number_new;
- DrawGameValue_Keys(stored_player[i].key);
+ DrawGameValue_Time(TimeLeft);
- redraw_mask |= REDRAW_DOOR_1;
- }
- }
+ if (!TimeLeft && setup.time_limit)
+ for (i = 0; i < MAX_PLAYERS; i++)
+ KillPlayer(&stored_player[i]);
}
break;
}
- case CA_DEL_KEY:
+ case CA_SET_LEVEL_SCORE:
{
- int element = getSpecialActionElement(action_arg_element,
- action_arg_number, EL_KEY_1);
+ local_player->score = action_arg_number_new;
- if (IS_KEY(element))
- {
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- if (trigger_player_bits & (1 << i))
- {
- stored_player[i].key[KEY_NR(element)] = FALSE;
+ DrawGameValue_Score(local_player->score);
- DrawGameValue_Keys(stored_player[i].key);
+ break;
+ }
- redraw_mask |= REDRAW_DOOR_1;
- }
+ case CA_SET_LEVEL_GEMS:
+ {
+ local_player->gems_still_needed = action_arg_number_new;
+
+ DrawGameValue_Emeralds(local_player->gems_still_needed);
+
+ break;
+ }
+
+ case CA_SET_LEVEL_GRAVITY:
+ {
+ game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
+ action_arg == CA_ARG_GRAVITY_ON ? TRUE :
+ action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
+ game.gravity);
+ break;
+ }
+
+ case CA_SET_LEVEL_WIND:
+ {
+ game.wind_direction = action_arg_direction;
+
+ break;
+ }
+
+ /* ---------- player actions ------------------------------------------ */
+
+ case CA_MOVE_PLAYER:
+ {
+ /* automatically move to the next field in specified direction */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (trigger_player_bits & (1 << i))
+ stored_player[i].programmed_action = action_arg_direction;
+
+ break;
+ }
+
+ case CA_EXIT_PLAYER:
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (action_arg_player_bits & (1 << i))
+ stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
+
+ break;
+ }
+
+ case CA_KILL_PLAYER:
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (action_arg_player_bits & (1 << i))
+ KillPlayer(&stored_player[i]);
+
+ break;
+ }
+
+ case CA_SET_PLAYER_KEYS:
+ {
+ int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
+ int element = getSpecialActionElement(action_arg_element,
+ action_arg_number, EL_KEY_1);
+
+ if (IS_KEY(element))
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (trigger_player_bits & (1 << i))
+ {
+ stored_player[i].key[KEY_NR(element)] = key_state;
+
+#if 1
+ DrawGameDoorValues();
+#else
+ DrawGameValue_Keys(stored_player[i].key);
+#endif
+
+ redraw_mask |= REDRAW_DOOR_1;
+ }
}
}
{
int move_stepsize = TILEX / stored_player[i].move_delay_value;
+ 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 :
+ CA_MODE_MULTIPLY);
+ }
+
move_stepsize =
getModifiedActionNumber(move_stepsize,
action_mode,
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].move_delay_value = TILEX / move_stepsize;
+ /* do no immediately change -- the player might just be moving */
+ stored_player[i].move_delay_value_next = TILEX / move_stepsize;
-#if 0
- printf("::: move_delay_value == %d\n",
- stored_player[i].move_delay_value);
+ stored_player[i].cannot_move =
+ (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
#endif
}
}
break;
}
- case CA_SET_GEMS:
+ case CA_SET_PLAYER_SHIELD:
{
- local_player->gems_still_needed = action_arg_number_new;
-
- DrawGameValue_Emeralds(local_player->gems_still_needed);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (trigger_player_bits & (1 << i))
+ {
+ if (action_arg == CA_ARG_SHIELD_OFF)
+ {
+ stored_player[i].shield_normal_time_left = 0;
+ stored_player[i].shield_deadly_time_left = 0;
+ }
+ else if (action_arg == CA_ARG_SHIELD_NORMAL)
+ {
+ stored_player[i].shield_normal_time_left = 999999;
+ }
+ else if (action_arg == CA_ARG_SHIELD_DEADLY)
+ {
+ stored_player[i].shield_normal_time_left = 999999;
+ stored_player[i].shield_deadly_time_left = 999999;
+ }
+ }
+ }
break;
}
- case CA_SET_TIME:
+ case CA_SET_PLAYER_ARTWORK:
{
- if (level.time > 0) /* only modify limited time value */
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- TimeLeft = action_arg_number_new;
-
- DrawGameValue_Time(TimeLeft);
+ if (trigger_player_bits & (1 << i))
+ {
+ int artwork_element = action_arg_element;
- if (!TimeLeft && setup.time_limit)
- for (i = 0; i < MAX_PLAYERS; i++)
- KillHero(&stored_player[i]);
- }
+ if (action_arg == CA_ARG_ELEMENT_RESET)
+ artwork_element =
+ (level.use_artwork_element[i] ? level.artwork_element[i] :
+ stored_player[i].element_nr);
- break;
- }
+ stored_player[i].artwork_element = artwork_element;
- case CA_SET_SCORE:
- {
- local_player->score = action_arg_number_new;
+ SetPlayerWaiting(&stored_player[i], FALSE);
- DrawGameValue_Score(local_player->score);
+ /* set number of special actions for bored and sleeping animation */
+ stored_player[i].num_special_action_bored =
+ get_num_special_action(artwork_element,
+ ACTION_BORING_1, ACTION_BORING_LAST);
+ stored_player[i].num_special_action_sleeping =
+ get_num_special_action(artwork_element,
+ ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+ }
+ }
break;
}
- case CA_SET_CE_SCORE:
- {
- ei->collect_score = action_arg_number_new;
+ /* ---------- CE actions ---------------------------------------------- */
- break;
- }
-
- case CA_SET_CE_COUNT:
+ case CA_SET_CE_VALUE:
{
-#if USE_NEW_COLLECT_COUNT
- int count_last = Count[x][y];
+#if USE_NEW_CUSTOM_VALUE
+ int last_custom_value = CustomValue[x][y];
- Count[x][y] = action_arg_number_new;
+ CustomValue[x][y] = action_arg_number_new;
#if 0
- printf("::: Count == %d\n", Count[x][y]);
+ printf("::: Count == %d\n", CustomValue[x][y]);
#endif
- if (Count[x][y] == 0 && count_last > 0)
+ if (CustomValue[x][y] == 0 && last_custom_value > 0)
{
#if 0
- printf("::: CE_COUNT_AT_ZERO\n");
+ printf("::: CE_VALUE_GETS_ZERO\n");
#endif
- CheckElementChange(x, y, element, EL_UNDEFINED, CE_COUNT_AT_ZERO);
- CheckTriggeredElementChange(element, CE_COUNT_AT_ZERO_OF_X);
+ 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
break;
}
- case CA_SET_DYNABOMB_NUMBER:
- {
- printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
-
- break;
- }
-
- case CA_SET_DYNABOMB_SIZE:
- {
- printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
-
- break;
- }
-
- case CA_SET_DYNABOMB_POWER:
- {
- printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
-
- break;
- }
-
- case CA_TOGGLE_PLAYER_GRAVITY:
+ case CA_SET_CE_SCORE:
{
- game.gravity = !game.gravity;
+ ei->collect_score = action_arg_number_new;
break;
}
- case CA_ENABLE_PLAYER_GRAVITY:
- {
- game.gravity = TRUE;
+ /* ---------- engine actions ------------------------------------------ */
- break;
- }
-
- case CA_DISABLE_PLAYER_GRAVITY:
+ case CA_SET_ENGINE_SCAN_MODE:
{
- game.gravity = FALSE;
+ InitPlayfieldScanMode(action_arg);
break;
}
}
}
-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 old_element = Feld[x][y];
+ int new_element = get_element_from_group_element(element);
int previous_move_direction = MovDir[x][y];
- boolean add_player = (ELEM_IS_PLAYER(target_element) &&
- IS_WALKABLE(Feld[x][y]));
+#if USE_NEW_CUSTOM_VALUE
+ int last_ce_value = CustomValue[x][y];
+#endif
+ boolean add_player = (ELEM_IS_PLAYER(new_element) &&
+ IS_WALKABLE(old_element));
+#if 0
/* 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(old_element) && !IS_ACCESSIBLE(new_element))
{
Bang(x, y);
+
return;
}
+#endif
if (!add_player)
{
else
RemoveField(x, y);
- Feld[x][y] = target_element;
+ Feld[x][y] = new_element;
ResetGfxAnimation(x, y);
ResetRandomAnimationValue(x, y);
- if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+ if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
MovDir[x][y] = previous_move_direction;
+#if USE_NEW_CUSTOM_VALUE
+ if (element_info[new_element].use_last_ce_value)
+ CustomValue[x][y] = last_ce_value;
+#endif
+
InitField_WithBug1(x, y, FALSE);
+ new_element = Feld[x][y]; /* element may have changed */
+
DrawLevelField(x, y);
- if (GFX_CRUMBLED(Feld[x][y]))
+ if (GFX_CRUMBLED(new_element))
DrawLevelFieldCrumbledSandNeighbours(x, y);
}
- /* "Changed[][]" not set yet to allow "entered by player" change one time */
- if (ELEM_IS_PLAYER(target_element))
- RelocatePlayer(x, y, target_element);
-
#if 1
- Changed[x][y] = TRUE; /* ignore all further changes in this frame */
-#else
- Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
+ /* 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(old_element) && !IS_ACCESSIBLE(new_element))
+ {
+ Bang(x, y);
+
+ return;
+ }
#endif
- TestIfBadThingTouchesHero(x, y);
+ /* "ChangeCount" not set yet to allow "entered by player" change one time */
+ if (ELEM_IS_PLAYER(new_element))
+ RelocatePlayer(x, y, new_element);
+
+ 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)
+{
+ 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);
+}
+
+static boolean ChangeElement(int x, int y, int element, int page)
{
- struct ElementChangeInfo *change = &element_info[element].change_page[page];
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[page];
+ int ce_value = CustomValue[x][y];
+ int ce_score = ei->collect_score;
int target_element;
int old_element = Feld[x][y];
/* reset actual trigger element, trigger player and action element */
change->actual_trigger_element = EL_EMPTY;
change->actual_trigger_player = EL_PLAYER_1;
+ change->actual_trigger_side = CH_SIDE_NONE;
+ change->actual_trigger_ce_value = 0;
+ change->actual_trigger_ce_score = 0;
}
-#if 1
- /* do not change any elements that have already changed in this frame */
- if (Changed[x][y])
+ /* do not change elements more than a specified maximum number of changes */
+ if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
return FALSE;
-#else
- /* do not change already changed elements with same change event */
- if (Changed[x][y] & ChangeEvent[x][y])
- return FALSE;
-#endif
-#if 1
- Changed[x][y] = TRUE; /* ignore all further changes in this frame */
-#else
- Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
-#endif
+ ChangeCount[x][y]++; /* count number of changes in the same frame */
if (change->explode)
{
boolean is_destructible;
int ex = x + xx - 1;
int ey = y + yy - 1;
- int content_element = change->target_content[xx][yy];
+ int content_element = change->target_content.e[xx][yy];
int e;
can_replace[xx][yy] = TRUE;
ChangeEvent[ex][ey] = ChangeEvent[x][y];
- content_element = change->target_content[xx][yy];
- target_element = GET_TARGET_ELEMENT(content_element, change);
+ content_element = change->target_content.e[xx][yy];
+ target_element = GET_TARGET_ELEMENT(content_element, change,
+ ce_value, ce_score);
- ChangeElementNowExt(change, ex, ey, target_element);
+ CreateElementFromChange(ex, ey, target_element);
something_has_changed = TRUE;
}
if (something_has_changed)
+ {
PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
+ }
}
}
else
{
- target_element = GET_TARGET_ELEMENT(change->target_element, change);
+ target_element = GET_TARGET_ELEMENT(change->target_element, change,
+ ce_value, ce_score);
- ChangeElementNowExt(change, x, y, target_element);
+ 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);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
}
/* this uses direct change before indirect change */
- CheckTriggeredElementChangeByPage(old_element, CE_CHANGE_OF_X, page);
+ CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
return TRUE;
}
-static void ChangeElement(int x, int y, int page)
+#if USE_NEW_DELAYED_ACTION
+
+static void HandleElementChange(int x, int y, int page)
+{
+ int element = MovingOrBlocked2Element(x, y);
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[page];
+
+#ifdef DEBUG
+ if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
+ !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
+ {
+ printf("\n\n");
+ printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
+ x, y, element, element_info[element].token_name);
+ printf("HandleElementChange(): This should never happen!\n");
+ printf("\n\n");
+ }
+#endif
+
+ /* this can happen with classic bombs on walkable, changing elements */
+ if (!CAN_CHANGE_OR_HAS_ACTION(element))
+ {
+#if 0
+ if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
+ ChangeDelay[x][y] = 0;
+#endif
+
+ return;
+ }
+
+ if (ChangeDelay[x][y] == 0) /* initialize element change */
+ {
+ ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
+
+ if (change->can_change)
+ {
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+
+ if (change->pre_change_function)
+ change->pre_change_function(x, y);
+ }
+ }
+
+ ChangeDelay[x][y]--;
+
+ if (ChangeDelay[x][y] != 0) /* continue element change */
+ {
+ if (change->can_change)
+ {
+ int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ if (change->change_function)
+ change->change_function(x, y);
+ }
+ }
+ else /* finish element change */
+ {
+ if (ChangePage[x][y] != -1) /* remember page from delayed change */
+ {
+ page = ChangePage[x][y];
+ ChangePage[x][y] = -1;
+
+ change = &ei->change_page[page];
+ }
+
+ if (IS_MOVING(x, y)) /* never change a running system ;-) */
+ {
+ ChangeDelay[x][y] = 1; /* try change after next move step */
+ ChangePage[x][y] = page; /* remember page to use for change */
+
+ return;
+ }
+
+ if (change->can_change)
+ {
+ if (ChangeElement(x, y, element, page))
+ {
+ if (change->post_change_function)
+ change->post_change_function(x, y);
+ }
+ }
+
+ if (change->has_action)
+ ExecuteCustomElementAction(x, y, element, page);
+ }
+}
+
+#else
+
+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);
}
}
-static boolean CheckTriggeredElementChangeExt(int trigger_element,
+#endif
+
+static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
+ int trigger_element,
int trigger_event,
int trigger_player,
int trigger_side,
{
change->actual_trigger_element = trigger_element;
change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+ change->actual_trigger_side = trigger_side;
+ change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
if ((change->can_change && !change_done) || change->has_action)
{
int x, y;
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
if (Feld[x][y] == element)
{
{
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)
+ {
+ ExecuteCustomElementAction(x, y, element, p);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+ }
+#else
if (change->has_action)
+ {
ExecuteCustomElementAction(x, y, element, p);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+ }
+#endif
}
}
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++)
{
{
change->actual_trigger_element = trigger_element;
change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+ change->actual_trigger_side = trigger_side;
+ change->actual_trigger_ce_value = CustomValue[x][y];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
+
+ /* special case: trigger element not at (x,y) position for some events */
+ if (check_trigger_element)
+ {
+ static struct
+ {
+ int dx, dy;
+ } move_xy[] =
+ {
+ { 0, 0 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, 0 },
+ { 0, -1 },
+ { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 0, +1 }
+ };
+
+ int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
+ int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
+
+ change->actual_trigger_ce_value = CustomValue[xx][yy];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
+ }
if (change->can_change && !change_done)
{
ChangeDelay[x][y] = 1;
ChangeEvent[x][y] = trigger_event;
- ChangeElement(x, y, p);
+
+ HandleElementChange(x, y, p);
change_done = TRUE;
}
-
+#if USE_NEW_DELAYED_ACTION
+ else if (change->has_action)
+ {
+ ExecuteCustomElementAction(x, y, element, p);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+ }
+#else
if (change->has_action)
+ {
ExecuteCustomElementAction(x, y, element, p);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+ }
+#endif
}
}
static void PlayPlayerSound(struct PlayerInfo *player)
{
int jx = player->jx, jy = player->jy;
- int element = player->element_nr;
+ int sound_element = player->artwork_element;
int last_action = player->last_action_waiting;
int action = player->action_waiting;
if (player->is_waiting)
{
if (action != last_action)
- PlayLevelSoundElementAction(jx, jy, element, action);
+ PlayLevelSoundElementAction(jx, jy, sound_element, action);
else
- PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
+ PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
}
else
{
if (action != last_action)
- StopSound(element_info[element].sound[last_action]);
+ StopSound(element_info[sound_element].sound[last_action]);
if (last_action == ACTION_SLEEPING)
- PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
+ PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
}
}
boolean last_waiting = player->is_waiting;
int move_dir = player->MovDir;
+ player->dir_waiting = move_dir;
player->last_action_waiting = player->action_waiting;
if (is_waiting)
game.player_sleeping_delay_fixed +
SimpleRND(game.player_sleeping_delay_random);
+#if 1
+ InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
+#else
InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
+#endif
}
if (game.player_sleeping_delay_fixed +
player->is_bored ? ACTION_BORING :
ACTION_WAITING);
+#if 1
+ if (player->is_sleeping && player->use_murphy)
+ {
+ /* special case for sleeping Murphy when leaning against non-free tile */
+
+ if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
+ (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
+ !IS_MOVING(player->jx - 1, player->jy)))
+ move_dir = MV_LEFT;
+ else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
+ (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
+ !IS_MOVING(player->jx + 1, player->jy)))
+ move_dir = MV_RIGHT;
+ else
+ player->is_sleeping = FALSE;
+
+ player->dir_waiting = move_dir;
+ }
+#endif
+
if (player->is_sleeping)
{
if (player->num_special_action_sleeping > 0)
last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
last_special_action + 1 : ACTION_SLEEPING);
int special_graphic =
- el_act_dir2img(player->element_nr, special_action, move_dir);
+ el_act_dir2img(player->artwork_element, special_action, move_dir);
player->anim_delay_counter =
graphic_info[special_graphic].anim_delay_fixed +
int special_action =
ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
int special_graphic =
- el_act_dir2img(player->element_nr, special_action, move_dir);
+ el_act_dir2img(player->artwork_element, special_action, move_dir);
player->anim_delay_counter =
graphic_info[special_graphic].anim_delay_fixed +
player->anim_delay_counter = 0;
player->post_delay_counter = 0;
+ player->dir_waiting = player->MovDir;
player->action_waiting = ACTION_DEFAULT;
player->special_action_bored = ACTION_DEFAULT;
int down = player_action & JOY_DOWN;
int button1 = player_action & JOY_BUTTON_1;
int button2 = player_action & JOY_BUTTON_2;
- int dx = (left ? -1 : right ? 1 : 0);
- int dy = (up ? -1 : down ? 1 : 0);
+ int dx = (left ? -1 : right ? 1 : 0);
+ int dy = (up ? -1 : down ? 1 : 0);
if (!player->active || tape.pausing)
return 0;
if (player->MovPos == 0) /* needed for tape.playing */
player->is_moving = FALSE;
- player->is_dropping = FALSE;
+ player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
+
+ return 0;
+ }
+}
+
+static void CheckLevelTime()
+{
+ int i;
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ if (level.native_em_level->lev->home == 0) /* all players at home */
+ {
+ local_player->LevelSolved = TRUE;
+ AllPlayersGone = TRUE;
+
+ level.native_em_level->lev->home = -1;
+ }
+
+ if (level.native_em_level->ply[0]->alive == 0 &&
+ level.native_em_level->ply[1]->alive == 0 &&
+ level.native_em_level->ply[2]->alive == 0 &&
+ level.native_em_level->ply[3]->alive == 0) /* all dead */
+ AllPlayersGone = TRUE;
+ }
+
+ if (TimeFrames >= FRAMES_PER_SECOND)
+ {
+ TimeFrames = 0;
+ TapeTime++;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ if (SHIELD_ON(player))
+ {
+ player->shield_normal_time_left--;
+
+ if (player->shield_deadly_time_left > 0)
+ player->shield_deadly_time_left--;
+ }
+ }
+
+ if (!level.use_step_counter)
+ {
+ TimePlayed++;
+
+ if (TimeLeft > 0)
+ {
+ TimeLeft--;
+
+ if (TimeLeft <= 10 && setup.time_limit)
+ PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+
+ DrawGameValue_Time(TimeLeft);
+
+ if (!TimeLeft && setup.time_limit)
+ {
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ level.native_em_level->lev->killed_out_of_time = TRUE;
+ else
+ for (i = 0; i < MAX_PLAYERS; i++)
+ KillPlayer(&stored_player[i]);
+ }
+ }
+ else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+ DrawGameValue_Time(TimePlayed);
+
+ level.native_em_level->lev->time =
+ (level.time == 0 ? TimePlayed : TimeLeft);
+ }
- return 0;
+ if (tape.recording || tape.playing)
+ DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
}
}
{
int i;
+#if 0
+ Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
+ FrameCounter, FrameCounter + 1);
+#endif
+
/* advance frame counters (global frame counter and time frame counter) */
FrameCounter++;
TimeFrames++;
if (stored_player[i].drop_delay > 0)
stored_player[i].drop_delay--;
+
+ if (stored_player[i].is_dropping_pressed)
+ stored_player[i].drop_pressed_delay++;
+ }
+}
+
+void StartGameActions(boolean init_network_game, boolean record_tape,
+ long random_seed)
+{
+ unsigned long new_random_seed = InitRND(random_seed);
+
+ if (record_tape)
+ TapeStartRecording(new_random_seed);
+
+#if defined(NETWORK_AVALIABLE)
+ if (init_network_game)
+ {
+ SendToServer_StartPlaying();
+
+ return;
}
+#endif
+
+ StopAnimation();
+
+ game_status = GAME_MODE_PLAYING;
+
+ InitGame();
}
void GameActions()
{
static unsigned long game_frame_delay = 0;
unsigned long game_frame_delay_value;
- int magic_wall_x = 0, magic_wall_y = 0;
- int i, x, y, element, graphic;
byte *recorded_player_action;
byte summarized_player_action = 0;
byte tape_action[MAX_PLAYERS];
+ int i;
- if (game_status != GAME_MODE_PLAYING)
+ if (game.restart_level)
+ StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ if (level.native_em_level->lev->home == 0) /* all players at home */
+ {
+ local_player->LevelSolved = TRUE;
+ AllPlayersGone = TRUE;
+
+ level.native_em_level->lev->home = -1;
+ }
+
+ if (level.native_em_level->ply[0]->alive == 0 &&
+ level.native_em_level->ply[1]->alive == 0 &&
+ level.native_em_level->ply[2]->alive == 0 &&
+ level.native_em_level->ply[3]->alive == 0) /* all dead */
+ AllPlayersGone = TRUE;
+ }
+
+ if (local_player->LevelSolved)
+ GameWon();
+
+ if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
+ TapeStop();
+
+ if (game_status != GAME_MODE_PLAYING) /* status might have changed */
return;
game_frame_delay_value =
if (!network_player_action_received)
return; /* failed to get network player actions in time */
+
+ /* do not yet reset "network_player_action_received" (for tape.pausing) */
}
if (tape.pausing)
return;
- recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
+ /* at this point we know that we really continue executing the game */
#if 1
- /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
- if (recorded_player_action == NULL && tape.pausing)
- return;
+ network_player_action_received = FALSE;
#endif
+ recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
+
+ if (tape.set_centered_player)
+ {
+ game.centered_player_nr_next = tape.centered_player_nr_next;
+ game.set_centered_player = TRUE;
+ }
+
for (i = 0; i < MAX_PLAYERS; i++)
{
summarized_player_action |= stored_player[i].action;
if (!options.network && !setup.team_mode)
local_player->effective_action = summarized_player_action;
+ if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].effective_action =
+ (i == game.centered_player_nr ? summarized_player_action : 0);
+ }
+
if (recorded_player_action != NULL)
for (i = 0; i < MAX_PLAYERS; i++)
stored_player[i].effective_action = recorded_player_action[i];
{
tape_action[i] = stored_player[i].effective_action;
+ /* (this can only happen in the R'n'D game engine) */
if (tape.recording && tape_action[i] && !tape.player_participates[i])
tape.player_participates[i] = TRUE; /* player just appeared from CE */
}
- /* only save actions from input devices, but not programmed actions */
+ /* only record actions from input devices, but not programmed actions */
if (tape.recording)
TapeRecordAction(tape_action);
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ GameActions_EM_Main();
+ }
+ else
+ {
+ GameActions_RND();
+ }
+}
+
+void GameActions_EM_Main()
+{
+ byte effective_action[MAX_PLAYERS];
+ boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ effective_action[i] = stored_player[i].effective_action;
+
+ GameActions_EM(effective_action, warp_mode);
+
+ CheckLevelTime();
+
+ AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
+}
+
+void GameActions_RND()
+{
+ int magic_wall_x = 0, magic_wall_y = 0;
+ int i, x, y, element, graphic;
+
+ InitPlayfieldScanModeVars();
+
+#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 1
+ if (game.set_centered_player)
+ {
+ boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
+
+ /* switching to "all players" only possible if all players fit to screen */
+ if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+
+ /* do not switch focus to non-existing (or non-active) player */
+ if (game.centered_player_nr_next >= 0 &&
+ !stored_player[game.centered_player_nr_next].active)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+ }
+
+ if (game.set_centered_player &&
+ ScreenMovPos == 0) /* screen currently aligned at tile position */
+ {
+ int sx, sy;
+
+ if (game.centered_player_nr_next == -1)
+ {
+ setScreenCenteredToAllPlayers(&sx, &sy);
+ }
+ else
+ {
+ sx = stored_player[game.centered_player_nr_next].jx;
+ sy = stored_player[game.centered_player_nr_next].jy;
+ }
+
+ game.centered_player_nr = game.centered_player_nr_next;
+ game.set_centered_player = FALSE;
+
+ DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
+ DrawGameDoorValues();
+ }
+#endif
+
for (i = 0; i < MAX_PLAYERS; i++)
{
int actual_player_action = stored_player[i].effective_action;
ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
}
+#if 0
network_player_action_received = FALSE;
+#endif
ScrollScreen(NULL, SCROLL_GO_ON);
}
}
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
- Changed[x][y] = FALSE;
+ ChangeCount[x][y] = 0;
ChangeEvent[x][y] = -1;
/* this must be handled before main playfield loop */
RemoveField(x, y);
}
+#if USE_NEW_SNAP_DELAY
+ if (Feld[x][y] == EL_ELEMENT_SNAPPING)
+ {
+ MovDelay[x][y]--;
+ if (MovDelay[x][y] <= 0)
+ {
+ RemoveField(x, y);
+ DrawLevelField(x, y);
+
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
+ }
+ }
+#endif
+
#if DEBUG
if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
{
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);
#endif
}
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
element = Feld[x][y];
graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+#if 0
+ printf("::: %d,%d\n", x, y);
+
+ if (element == EL_ROCK)
+ printf("::: Yo man! Rocks can fall!\n");
+#endif
+
if (graphic_info[graphic].anim_global_sync)
GfxFrame[x][y] = FrameCounter;
+ else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+ {
+ int old_gfx_frame = GfxFrame[x][y];
+
+ GfxFrame[x][y] = CustomValue[x][y];
+
+#if 1
+ if (GfxFrame[x][y] != old_gfx_frame)
+#endif
+ DrawLevelGraphicAnimation(x, y, graphic);
+ }
+ else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+ {
+ int old_gfx_frame = GfxFrame[x][y];
+
+ GfxFrame[x][y] = element_info[element].collect_score;
+
+#if 1
+ if (GfxFrame[x][y] != old_gfx_frame)
+#endif
+ DrawLevelGraphicAnimation(x, y, graphic);
+ }
if (ANIM_MODE(graphic) == ANIM_RANDOM &&
IS_NEXT_FRAME(GfxFrame[x][y], graphic))
{
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
printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
#endif
+#if 0
+ if (element == EL_CUSTOM_255)
+ printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
+#endif
+
+#if 1
+ HandleElementChange(x, y, page);
+#else
if (CAN_CHANGE(element))
- ChangeElement(x, y, page);
+ HandleElementChange(x, y, page);
- if (HAS_ACTION(element) && ChangeDelay[x][y] == 0)
+ if (HAS_ACTION(element))
ExecuteCustomElementAction(x, y, element, page);
+#endif
+
#endif
element = Feld[x][y];
CheckForDragon(x, y);
else if (element == EL_EXPLOSION)
; /* drawing of correct explosion animation is handled separately */
+ 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]);
+
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+#endif
+ }
else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+#if 0
+ if (element == EL_CUSTOM_255 ||
+ element == EL_CUSTOM_256)
+ DrawLevelGraphicAnimation(x, y, graphic);
+#endif
+
if (IS_BELT_ACTIVE(element))
PlayLevelSoundAction(x, y, ACTION_ACTIVE);
{
game.explosions_delayed = FALSE;
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
element = Feld[x][y];
game.magic_wall_time_left--;
if (!game.magic_wall_time_left)
{
+#if 1
+ SCAN_PLAYFIELD(x, y)
+#else
for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#endif
{
element = Feld[x][y];
CloseAllOpenTimegates();
}
+ if (game.lenses_time_left > 0)
+ {
+ game.lenses_time_left--;
+
+ if (game.lenses_time_left == 0)
+ RedrawAllInvisibleElementsForLenses();
+ }
+
+ if (game.magnify_time_left > 0)
+ {
+ game.magnify_time_left--;
+
+ if (game.magnify_time_left == 0)
+ RedrawAllInvisibleElementsForMagnifier();
+ }
+
for (i = 0; i < MAX_PLAYERS; i++)
{
struct PlayerInfo *player = &stored_player[i];
}
}
- if (TimeFrames >= FRAMES_PER_SECOND)
- {
- TimeFrames = 0;
- TapeTime++;
-
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- struct PlayerInfo *player = &stored_player[i];
-
- if (SHIELD_ON(player))
- {
- player->shield_normal_time_left--;
-
- if (player->shield_deadly_time_left > 0)
- player->shield_deadly_time_left--;
- }
- }
-
- if (!level.use_step_counter)
- {
- TimePlayed++;
-
- if (TimeLeft > 0)
- {
- TimeLeft--;
-
- if (TimeLeft <= 10 && setup.time_limit)
- PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
-
- DrawGameValue_Time(TimeLeft);
-
- if (!TimeLeft && setup.time_limit)
- for (i = 0; i < MAX_PLAYERS; i++)
- KillHero(&stored_player[i]);
- }
- else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
- DrawGameValue_Time(TimePlayed);
- }
-
- if (tape.recording || tape.playing)
- DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
- }
+ CheckLevelTime();
DrawAllPlayers();
PlayAllPlayersSound();
{
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_UP :
- dy > 0 ? MV_DOWN : MV_NO_MOVING);
+ 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)
+ {
+#if 1
+ if (player->MovPos == 0)
+ {
+ player->is_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+ player->is_snapping = FALSE;
+ player->is_pushing = FALSE;
+ }
+#else
+ DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
+ SnapField(player, 0, 0);
+#endif
+
+#if 0
+ return MP_NO_ACTION;
+#endif
+ }
+#if 1
+ if (!options.network && game.centered_player_nr == -1 &&
+ !AllPlayersInSight(player, new_jx, new_jy))
+ return MP_NO_ACTION;
+#else
if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
- return MF_NO_ACTION;
+ return MP_NO_ACTION;
+#endif
+#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)
{
InitMovingField(jx, jy, MV_DOWN);
Store[jx][jy] = EL_ACID;
ContinueMoving(jx, jy);
- BuryHero(player);
+ BuryPlayer(player);
}
else
- TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
+ 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;
player->jy = new_jy;
StorePlayer[new_jx][new_jy] = player->element_nr;
+ if (player->move_delay_value_next != -1)
+ {
+ player->move_delay_value = player->move_delay_value_next;
+ player->move_delay_value_next = -1;
+ }
+
player->MovPos =
(dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
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;
player->move_delay = -1; /* set to "uninitialized" value */
/* store if player is automatically moved to next field */
- player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
+ player->is_auto_moving = (player->programmed_action != MV_NONE);
/* remove the last programmed player action */
player->programmed_action = 0;
jx = player->jx;
jy = player->jy;
- if (moved & MF_MOVING && !ScreenMovPos &&
+#if 1
+ if (moved & MP_MOVING && !ScreenMovPos &&
+ (player->index_nr == game.centered_player_nr ||
+ game.centered_player_nr == -1))
+#else
+ if (moved & MP_MOVING && !ScreenMovPos &&
(player == local_player || !options.network))
+#endif
{
int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
int offset = (setup.scroll_delay ? 3 : 0);
(player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
scroll_y = old_scroll_y;
}
- }
-
- if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
- {
+ }
+
+ if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
+ {
+#if 1
+ if (!options.network && game.centered_player_nr == -1 &&
+ !AllPlayersInVisibleScreen())
+ {
+ scroll_x = old_scroll_x;
+ scroll_y = old_scroll_y;
+ }
+ else
+#else
if (!options.network && !AllPlayersInVisibleScreen())
{
scroll_x = old_scroll_x;
scroll_y = old_scroll_y;
}
else
+#endif
{
ScrollScreen(player, SCROLL_INIT);
ScrollLevel(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);
player->is_snapping = FALSE;
player->is_switching = FALSE;
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
}
else
{
if (game.engine_version < VERSION_IDENT(3,0,7,0))
{
- TestIfHeroTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesBadThing(jx, jy);
TestIfPlayerTouchesCustomElement(jx, jy);
}
if (!player->active)
- RemoveHero(player);
+ RemovePlayer(player);
return moved;
}
int last_jx = player->last_jx, last_jy = player->last_jy;
int move_stepsize = TILEX / player->move_delay_value;
- if (!player->active || !player->MovPos)
+#if USE_NEW_PLAYER_SPEED
+ if (!player->active)
+ return;
+
+ if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
+ return;
+#else
+ if (!player->active || player->MovPos == 0)
return;
+#endif
if (mode == SCROLL_INIT)
{
MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
}
+#if USE_NEW_PLAYER_SPEED
+ if (player->MovPos != 0) /* player has not yet reached destination */
+ return;
+#else
return;
+#endif
}
else if (!FrameReached(&player->actual_frame_counter, 1))
return;
+#if 0
+ printf("::: player->MovPos: %d -> %d\n",
+ player->MovPos,
+ player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
+#endif
+
+#if USE_NEW_PLAYER_SPEED
+ if (player->MovPos != 0)
+ {
+ player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
+ player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
+
+ /* before DrawPlayer() to draw correct player graphic for this case */
+ if (player->MovPos == 0)
+ CheckGravityMovement(player);
+ }
+#else
player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
/* before DrawPlayer() to draw correct player graphic for this case */
if (player->MovPos == 0)
CheckGravityMovement(player);
+#endif
if (player->MovPos == 0) /* player reached destination field */
{
+#if 0
+ printf("::: player reached destination field\n");
+#endif
+
if (player->move_delay_reset_counter > 0)
{
player->move_delay_reset_counter--;
Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
{
DrawPlayer(player); /* needed here only to cleanup last field */
- RemoveHero(player);
+ RemovePlayer(player);
if (local_player->friends_still_needed == 0 ||
IS_SP_ELEMENT(Feld[jx][jy]))
CE_LEFT_BY_PLAYER,
player->index_bit, leave_side);
- CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
+ CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
+ CE_PLAYER_LEAVES_X,
player->index_bit, leave_side);
if (IS_CUSTOM_ELEMENT(new_element))
CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
player->index_bit, enter_side);
- CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_ENTERS_X,
+ CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
+ CE_PLAYER_ENTERS_X,
player->index_bit, enter_side);
+
+ CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
+ CE_MOVE_OF_X, move_direction);
}
if (game.engine_version >= VERSION_IDENT(3,0,7,0))
{
- TestIfHeroTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesBadThing(jx, jy);
TestIfPlayerTouchesCustomElement(jx, jy);
/* needed because pushed element has not yet reached its destination,
TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
if (!player->active)
- RemoveHero(player);
+ RemovePlayer(player);
}
if (level.use_step_counter)
if (!TimeLeft && setup.time_limit)
for (i = 0; i < MAX_PLAYERS; i++)
- KillHero(&stored_player[i]);
+ KillPlayer(&stored_player[i]);
}
else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
DrawGameValue_Time(TimePlayed);
redraw_mask |= REDRAW_FIELD;
}
else
- ScreenMovDir = MV_NO_MOVING;
+ ScreenMovDir = MV_NONE;
}
void TestIfPlayerTouchesCustomElement(int x, int y)
CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
player->index_bit, border_side);
- CheckTriggeredElementChangeByPlayer(border_element, CE_PLAYER_TOUCHES_X,
+ CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
+ CE_PLAYER_TOUCHES_X,
player->index_bit, border_side);
}
else if (IS_PLAYER(xx, yy))
CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
player->index_bit, center_side);
- CheckTriggeredElementChangeByPlayer(center_element, CE_PLAYER_TOUCHES_X,
+ CheckTriggeredElementChangeByPlayer(x, y, center_element,
+ CE_PLAYER_TOUCHES_X,
player->index_bit, center_side);
break;
}
}
}
+#if USE_ELEMENT_TOUCHING_BUGFIX
+
void TestIfElementTouchesCustomElement(int x, int y)
+{
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+ static int trigger_sides[4][2] =
+ {
+ /* center side border side */
+ { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
+ { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
+ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
+ { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
+ };
+ static int touch_dir[4] =
+ {
+ MV_LEFT | MV_RIGHT,
+ MV_UP | MV_DOWN,
+ MV_UP | MV_DOWN,
+ MV_LEFT | MV_RIGHT
+ };
+ boolean change_center_element = FALSE;
+ int center_element = Feld[x][y]; /* should always be non-moving! */
+ int border_element_old[NUM_DIRECTIONS];
+ int i;
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
+ int border_element;
+
+ border_element_old[i] = -1;
+
+ if (!IN_LEV_FIELD(xx, yy))
+ continue;
+
+ if (game.engine_version < VERSION_IDENT(3,0,7,0))
+ border_element = Feld[xx][yy]; /* may be moving! */
+ else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
+ border_element = Feld[xx][yy];
+ else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
+ border_element = MovingOrBlocked2Element(xx, yy);
+ else
+ continue; /* center and border element do not touch */
+
+ border_element_old[i] = border_element;
+ }
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
+ int center_side = trigger_sides[i][0];
+ int border_element = border_element_old[i];
+
+ if (border_element == -1)
+ continue;
+
+ /* check for change of border element */
+ CheckElementChangeBySide(xx, yy, border_element, center_element,
+ CE_TOUCHING_X, center_side);
+ }
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int border_side = trigger_sides[i][1];
+ int border_element = border_element_old[i];
+
+ if (border_element == -1)
+ continue;
+
+ /* check for change of center element (but change it only once) */
+ if (!change_center_element)
+ change_center_element =
+ CheckElementChangeBySide(x, y, center_element, border_element,
+ CE_TOUCHING_X, border_side);
+ }
+}
+
+#else
+
+void TestIfElementTouchesCustomElement_OLD(int x, int y)
{
static int xy[4][2] =
{
}
}
+#endif
+
void TestIfElementHitsCustomElement(int x, int y, int direction)
{
int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
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] =
{
continue;
test_move_dir =
- (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
+ (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
!IS_INDESTRUCTIBLE(bad_element))
Bang(kill_x, kill_y);
else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
- KillHero(player);
+ KillPlayer(player);
}
else
Bang(good_x, good_y);
continue;
test_move_dir =
- (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
+ (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
test_element = Feld[test_x][test_y];
!IS_INDESTRUCTIBLE(bad_element))
Bang(bad_x, bad_y);
else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
- KillHero(player);
+ KillPlayer(player);
}
else
Bang(kill_x, kill_y);
}
}
-void TestIfHeroTouchesBadThing(int x, int y)
+void TestIfPlayerTouchesBadThing(int x, int y)
{
- TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+ TestIfGoodThingHitsBadThing(x, y, MV_NONE);
}
-void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
+void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
{
TestIfGoodThingHitsBadThing(x, y, move_dir);
}
-void TestIfBadThingTouchesHero(int x, int y)
+void TestIfBadThingTouchesPlayer(int x, int y)
{
- TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
+ TestIfBadThingHitsGoodThing(x, y, MV_NONE);
}
-void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
+void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
{
TestIfBadThingHitsGoodThing(x, y, move_dir);
}
void TestIfFriendTouchesBadThing(int x, int y)
{
- TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+ TestIfGoodThingHitsBadThing(x, y, MV_NONE);
}
void TestIfBadThingTouchesFriend(int x, int y)
{
- TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
+ TestIfBadThingHitsGoodThing(x, y, MV_NONE);
}
void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
Bang(bad_x, bad_y);
}
-void KillHero(struct PlayerInfo *player)
+void KillPlayer(struct PlayerInfo *player)
{
int jx = player->jx, jy = player->jy;
player->shield_deadly_time_left = 0;
Bang(jx, jy);
- BuryHero(player);
+ BuryPlayer(player);
}
-static void KillHeroUnlessEnemyProtected(int x, int y)
+static void KillPlayerUnlessEnemyProtected(int x, int y)
{
if (!PLAYER_ENEMY_PROTECTED(x, y))
- KillHero(PLAYERINFO(x, y));
+ KillPlayer(PLAYERINFO(x, y));
}
-static void KillHeroUnlessExplosionProtected(int x, int y)
+static void KillPlayerUnlessExplosionProtected(int x, int y)
{
if (!PLAYER_EXPLOSION_PROTECTED(x, y))
- KillHero(PLAYERINFO(x, y));
+ KillPlayer(PLAYERINFO(x, y));
}
-void BuryHero(struct PlayerInfo *player)
+void BuryPlayer(struct PlayerInfo *player)
{
int jx = player->jx, jy = player->jy;
if (!player->active)
return;
- PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
+ PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
PlayLevelSound(jx, jy, SND_GAME_LOSING);
player->GameOver = TRUE;
- RemoveHero(player);
+ RemovePlayer(player);
}
-void RemoveHero(struct PlayerInfo *player)
+void RemovePlayer(struct PlayerInfo *player)
{
int jx = player->jx, jy = player->jy;
int i, found = FALSE;
if (!ExplodeField[jx][jy])
StorePlayer[jx][jy] = 0;
+ if (player->is_moving)
+ DrawLevelField(player->last_jx, player->last_jy);
+
for (i = 0; i < MAX_PLAYERS; i++)
if (stored_player[i].active)
found = TRUE;
ExitY = ZY = jy;
}
+#if USE_NEW_SNAP_DELAY
+static void setFieldForSnapping(int x, int y, int element, int direction)
+{
+ struct ElementInfo *ei = &element_info[element];
+ 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);
+
+ Feld[x][y] = EL_ELEMENT_SNAPPING;
+ MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
+
+ ResetGfxAnimation(x, y);
+
+ GfxElement[x][y] = element;
+ GfxAction[x][y] = action;
+ GfxDir[x][y] = direction;
+ GfxFrame[x][y] = -1;
+}
+#endif
+
/*
=============================================================================
checkDiagonalPushing()
{
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 move_direction = (dx == -1 ? MV_LEFT :
+ int move_direction = (dx == -1 ? MV_LEFT :
dx == +1 ? MV_RIGHT :
- dy == -1 ? MV_UP :
- dy == +1 ? MV_DOWN : MV_NO_MOVING);
+ dy == -1 ? MV_UP :
+ dy == +1 ? MV_DOWN : MV_NONE);
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 */
+ return MP_NO_ACTION; /* field has no opening in this direction */
- element = Feld[x][y];
-#if USE_NEW_COLLECT_COUNT
- collect_count = Count[x][y];
-#else
- collect_count = element_info[element].collect_count_initial;
+#if 1
+#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);
+
+ return MP_DONT_RUN_INTO;
+ }
+#endif
#endif
#if 0
- if (element != EL_BLOCKED &&
- Count[x][y] != element_info[element].collect_count_initial)
- printf("::: %d: %d != %d\n",
- element,
- Count[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))
- return MF_NO_ACTION;
+ {
+ CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
+ player->index_bit, dig_side);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+ player->index_bit, dig_side);
+
+ if (Feld[x][y] != element) /* field changed by snapping */
+ return MP_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 MP_NO_ACTION;
}
else if (element == EL_EXIT_OPEN ||
element == EL_SP_EXIT_OPEN ||
if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
PlayLevelSoundElementAction(x, y, sound_element, sound_action);
else
- PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
+ 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 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);
PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
- CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_DIGS_X,
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
player->index_bit, dig_side);
if (mode == DF_SNAP)
- TestIfElementTouchesCustomElement(x, y); /* for empty space */
+ {
+#if USE_NEW_SNAP_DELAY
+ if (level.block_snap_field)
+ setFieldForSnapping(x, y, element, move_direction);
+ else
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
+#else
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
+#endif
+
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+ player->index_bit, dig_side);
+ }
}
- else if (IS_COLLECTIBLE(element))
+ else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
{
RemoveField(x, y);
}
else if (element == EL_EXTRA_TIME && level.time > 0)
{
- TimeLeft += 10;
+ TimeLeft += level.extra_time;
DrawGameValue_Time(TimeLeft);
}
else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
{
- player->shield_normal_time_left += 10;
+ player->shield_normal_time_left += level.shield_normal_time;
if (element == EL_SHIELD_DEADLY)
- player->shield_deadly_time_left += 10;
+ player->shield_deadly_time_left += level.shield_deadly_time;
}
- else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
+ else if (element == EL_DYNAMITE ||
+ element == EL_EM_DYNAMITE ||
+ element == EL_SP_DISK_RED)
{
if (player->inventory_size < MAX_INVENTORY_SIZE)
player->inventory_element[player->inventory_size++] = element;
+#if 1
+ DrawGameDoorValues();
+#else
DrawGameValue_Dynamite(local_player->inventory_size);
+#endif
}
else if (element == EL_DYNABOMB_INCREASE_NUMBER)
{
{
player->key[KEY_NR(element)] = TRUE;
+#if 1
+ DrawGameDoorValues();
+#else
DrawGameValue_Keys(player->key);
+#endif
redraw_mask |= REDRAW_DOOR_1;
}
{
player->show_envelope = element;
}
+ else if (element == EL_EMC_LENSES)
+ {
+ game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
+
+ RedrawAllInvisibleElementsForLenses();
+ }
+ else if (element == EL_EMC_MAGNIFIER)
+ {
+ game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
+
+ RedrawAllInvisibleElementsForMagnifier();
+ }
else if (IS_DROPPABLE(element) ||
IS_THROWABLE(element)) /* can be collected and dropped */
{
if (player->inventory_size < MAX_INVENTORY_SIZE)
player->inventory_element[player->inventory_size++] = element;
+#if 1
+ DrawGameDoorValues();
+#else
DrawGameValue_Dynamite(local_player->inventory_size);
+#endif
}
else if (collect_count > 0)
{
PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
if (is_player)
- CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_COLLECTS_X,
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
player->index_bit, dig_side);
if (mode == DF_SNAP)
- TestIfElementTouchesCustomElement(x, y); /* for empty space */
+ {
+#if USE_NEW_SNAP_DELAY
+ if (level.block_snap_field)
+ setFieldForSnapping(x, y, element, move_direction);
+ else
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
+#else
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
+#endif
+
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+ 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))
{
CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
player->index_bit, dig_side);
}
}
{
if (PLAYER_SWITCHING(player, x, y))
{
- CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
player->index_bit, dig_side);
- return MF_ACTION;
+ return MP_ACTION;
}
player->is_switching = TRUE;
{
int xx, yy;
- for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
+#if 1
+ SCAN_PLAYFIELD(xx, yy)
+#else
+ for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
+#endif
{
if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
Bang(xx, yy);
{
ActivateTimegateSwitch(x, y);
}
- else if (element == EL_BALLOON_SWITCH_LEFT ||
+ else if (element == EL_BALLOON_SWITCH_LEFT ||
element == EL_BALLOON_SWITCH_RIGHT ||
- element == EL_BALLOON_SWITCH_UP ||
- element == EL_BALLOON_SWITCH_DOWN ||
+ element == EL_BALLOON_SWITCH_UP ||
+ element == EL_BALLOON_SWITCH_DOWN ||
+ element == EL_BALLOON_SWITCH_NONE ||
element == EL_BALLOON_SWITCH_ANY)
{
- if (element == EL_BALLOON_SWITCH_ANY)
- game.balloon_dir = move_direction;
- else
- game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
- element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
- element == EL_BALLOON_SWITCH_UP ? MV_UP :
- element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
- MV_NO_MOVING);
+ game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
+ element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
+ element == EL_BALLOON_SWITCH_UP ? MV_UP :
+ element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
+ element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
+ move_direction);
}
else if (element == EL_LAMP)
{
else if (element == EL_TIME_ORB_FULL)
{
Feld[x][y] = EL_TIME_ORB_EMPTY;
- TimeLeft += 10;
- DrawGameValue_Time(TimeLeft);
+
+ if (level.time > 0 || level.use_time_orb_bug)
+ {
+ TimeLeft += level.time_orb_time;
+ DrawGameValue_Time(TimeLeft);
+ }
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(element, CE_SWITCH_OF_X,
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
player->index_bit, dig_side);
- return MF_ACTION;
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
+ player->index_bit, dig_side);
+
+ return MP_ACTION;
}
else
{
CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
+ player->index_bit, dig_side);
+
+ CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
+ player->index_bit, dig_side);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
player->index_bit, dig_side);
}
CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
+ 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)
{
int jx = player->jx, jy = player->jy;
int x = jx + dx, y = jy + dy;
- int snap_direction = (dx == -1 ? MV_LEFT :
+ int snap_direction = (dx == -1 ? MV_LEFT :
dx == +1 ? MV_RIGHT :
- dy == -1 ? MV_UP :
- dy == +1 ? MV_DOWN : MV_NO_MOVING);
+ dy == -1 ? MV_UP :
+ dy == +1 ? MV_DOWN : MV_NONE);
+ boolean can_continue_snapping = (level.continuous_snapping &&
+ WasJustFalling[x][y] < CHECK_DELAY_FALLING);
if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
return FALSE;
return FALSE;
}
+#if USE_NEW_CONTINUOUS_SNAPPING
+ /* prevent snapping with already pressed snap key when not allowed */
+ if (player->is_snapping && !can_continue_snapping)
+ return FALSE;
+#else
if (player->is_snapping)
return FALSE;
+#endif
player->MovDir = snap_direction;
}
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
- 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;
EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
EL_UNDEFINED);
+ player->is_dropping_pressed = TRUE;
+
/* do not drop an element on top of another element; when holding drop key
pressed without moving, dropped element must move away before the next
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))
{
if (new_element == EL_UNDEFINED)
return FALSE;
+ /* check if drop key was pressed long enough for EM style dynamite */
+ if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
+ return FALSE;
+
/* check if anything can be dropped at the current position */
if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
return FALSE;
{
player->inventory_size--;
+#if 1
+ DrawGameDoorValues();
+#else
DrawGameValue_Dynamite(local_player->inventory_size);
+#endif
if (new_element == EL_DYNAMITE)
new_element = EL_DYNAMITE_ACTIVE;
+ else if (new_element == EL_EM_DYNAMITE)
+ new_element = EL_EM_DYNAMITE_ACTIVE;
else if (new_element == EL_SP_DISK_RED)
new_element = EL_SP_DISK_RED_ACTIVE;
}
PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
/* needed if previous element just changed to "empty" in the last frame */
- Changed[dropx][dropy] = FALSE; /* allow another change */
+ ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
player->index_bit, drop_side);
- CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_DROPS_X,
+ CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
+ CE_PLAYER_DROPS_X,
player->index_bit, drop_side);
TestIfElementTouchesCustomElement(dropx, dropy);
nextx = dropx + GET_DX_FROM_DIR(move_direction);
nexty = dropy + GET_DY_FROM_DIR(move_direction);
- Changed[dropx][dropy] = FALSE; /* allow another change */
- CheckCollision[dropx][dropy] = 2;
+ ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
+ CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
}
player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
player->is_dropping = TRUE;
+ player->drop_pressed_delay = 0;
+ player->is_dropping_pressed = FALSE;
+
player->drop_x = dropx;
player->drop_y = dropy;
break;
case SAMPLE_slurp:
- PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
+ PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
break;
case SAMPLE_eater:
RaiseScore(level.score[SC_NUT]);
break;
case EL_DYNAMITE:
+ case EL_EM_DYNAMITE:
case EL_SP_DISK_RED:
case EL_DYNABOMB_INCREASE_NUMBER:
case EL_DYNABOMB_INCREASE_SIZE:
RaiseScore(level.score[SC_SHIELD]);
break;
case EL_EXTRA_TIME:
- RaiseScore(level.score[SC_TIME_BONUS]);
+ RaiseScore(level.extra_time_score);
break;
case EL_KEY_1:
case EL_KEY_2:
{
#if defined(NETWORK_AVALIABLE)
if (options.network)
- SendToServer_StopPlaying();
+ SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
else
#endif
{
switch (id)
{
case GAME_CTRL_ID_STOP:
- RequestQuitGame(TRUE);
+ if (tape.playing)
+ TapeStop();
+ else
+ RequestQuitGame(TRUE);
break;
case GAME_CTRL_ID_PAUSE:
#endif
{
tape.pausing = FALSE;
- DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+ DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
}
}
break;