+ if (!(trigger_events[trigger_element][trigger_event]))
+ return FALSE;
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+ boolean change_done = FALSE;
+ int p;
+
+ if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
+ !HAS_ANY_CHANGE_EVENT(element, trigger_event))
+ continue;
+
+ for (p = 0; p < element_info[element].num_change_pages; p++)
+ {
+ struct ElementChangeInfo *change = &element_info[element].change_page[p];
+
+ if (change->can_change_or_has_action &&
+ change->has_event[trigger_event] &&
+ change->trigger_side & trigger_side &&
+ change->trigger_player & trigger_player &&
+ change->trigger_page & trigger_page_bits &&
+ IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
+ {
+ 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];
+
+ 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)
+ {
+ if (change->can_change && !change_done)
+ {
+ ChangeDelay[x][y] = 1;
+ ChangeEvent[x][y] = trigger_event;
+
+ 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
+ }
+ }
+
+ if (change->can_change)
+ {
+ change_done = TRUE;
+ change_done_any = TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ return change_done_any;
+}
+
+static boolean CheckElementChangeExt(int x, int y,
+ int element,
+ int trigger_element,
+ int trigger_event,
+ int trigger_player,
+ int trigger_side)
+{
+ boolean change_done = FALSE;
+ int p;
+
+ if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
+ !HAS_ANY_CHANGE_EVENT(element, trigger_event))
+ return FALSE;
+
+ if (Feld[x][y] == EL_BLOCKED)
+ {
+ Blocked2Moving(x, y, &x, &y);
+ element = Feld[x][y];
+ }
+
+#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++)
+ {
+ struct ElementChangeInfo *change = &element_info[element].change_page[p];
+
+ boolean check_trigger_element =
+ (trigger_event == CE_TOUCHING_X ||
+ trigger_event == CE_HITTING_X ||
+ trigger_event == CE_HIT_BY_X);
+
+ if (change->can_change_or_has_action &&
+ change->has_event[trigger_event] &&
+ change->trigger_side & trigger_side &&
+ change->trigger_player & trigger_player &&
+ (!check_trigger_element ||
+ IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
+ {
+ 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];
+
+ /* 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];
+ }
+
+ if (change->can_change && !change_done)
+ {
+ ChangeDelay[x][y] = 1;
+ ChangeEvent[x][y] = trigger_event;
+
+ 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
+ }
+ }
+
+ return change_done;
+}
+
+static void PlayPlayerSound(struct PlayerInfo *player)
+{
+ int jx = player->jx, jy = player->jy;
+ 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, sound_element, action);
+ else
+ PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
+ }
+ else
+ {
+ if (action != last_action)
+ StopSound(element_info[sound_element].sound[last_action]);
+
+ if (last_action == ACTION_SLEEPING)
+ PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
+ }
+}
+
+static void PlayAllPlayersSound()
+{
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (stored_player[i].active)
+ PlayPlayerSound(&stored_player[i]);
+}
+
+static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
+{
+ boolean last_waiting = player->is_waiting;
+ int move_dir = player->MovDir;
+
+ player->last_action_waiting = player->action_waiting;
+
+ if (is_waiting)
+ {
+ if (!last_waiting) /* not waiting -> waiting */
+ {
+ player->is_waiting = TRUE;
+
+ player->frame_counter_bored =
+ FrameCounter +
+ game.player_boring_delay_fixed +
+ SimpleRND(game.player_boring_delay_random);
+ player->frame_counter_sleeping =
+ FrameCounter +
+ game.player_sleeping_delay_fixed +
+ SimpleRND(game.player_sleeping_delay_random);
+
+ InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
+ }
+
+ if (game.player_sleeping_delay_fixed +
+ game.player_sleeping_delay_random > 0 &&
+ player->anim_delay_counter == 0 &&
+ player->post_delay_counter == 0 &&
+ FrameCounter >= player->frame_counter_sleeping)
+ player->is_sleeping = TRUE;
+ else if (game.player_boring_delay_fixed +
+ game.player_boring_delay_random > 0 &&
+ FrameCounter >= player->frame_counter_bored)
+ player->is_bored = TRUE;
+
+ player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
+ player->is_bored ? ACTION_BORING :
+ ACTION_WAITING);
+
+ if (player->is_sleeping)
+ {
+ if (player->num_special_action_sleeping > 0)
+ {
+ if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
+ {
+ int last_special_action = player->special_action_sleeping;
+ int num_special_action = player->num_special_action_sleeping;
+ int special_action =
+ (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
+ last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
+ last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
+ last_special_action + 1 : ACTION_SLEEPING);
+ int special_graphic =
+ el_act_dir2img(player->artwork_element, special_action, move_dir);
+
+ player->anim_delay_counter =
+ graphic_info[special_graphic].anim_delay_fixed +
+ SimpleRND(graphic_info[special_graphic].anim_delay_random);
+ player->post_delay_counter =
+ graphic_info[special_graphic].post_delay_fixed +
+ SimpleRND(graphic_info[special_graphic].post_delay_random);
+
+ player->special_action_sleeping = special_action;
+ }
+
+ if (player->anim_delay_counter > 0)
+ {
+ player->action_waiting = player->special_action_sleeping;
+ player->anim_delay_counter--;
+ }
+ else if (player->post_delay_counter > 0)
+ {
+ player->post_delay_counter--;
+ }
+ }
+ }
+ else if (player->is_bored)
+ {
+ if (player->num_special_action_bored > 0)
+ {
+ if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
+ {
+ int special_action =
+ ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
+ int special_graphic =
+ el_act_dir2img(player->artwork_element, special_action, move_dir);
+
+ player->anim_delay_counter =
+ graphic_info[special_graphic].anim_delay_fixed +
+ SimpleRND(graphic_info[special_graphic].anim_delay_random);
+ player->post_delay_counter =
+ graphic_info[special_graphic].post_delay_fixed +
+ SimpleRND(graphic_info[special_graphic].post_delay_random);
+
+ player->special_action_bored = special_action;
+ }
+
+ if (player->anim_delay_counter > 0)
+ {
+ player->action_waiting = player->special_action_bored;
+ player->anim_delay_counter--;
+ }
+ else if (player->post_delay_counter > 0)
+ {
+ player->post_delay_counter--;
+ }
+ }
+ }
+ }
+ else if (last_waiting) /* waiting -> not waiting */
+ {
+ player->is_waiting = FALSE;
+ player->is_bored = FALSE;
+ player->is_sleeping = FALSE;
+
+ player->frame_counter_bored = -1;
+ player->frame_counter_sleeping = -1;
+
+ player->anim_delay_counter = 0;
+ player->post_delay_counter = 0;
+
+ player->action_waiting = ACTION_DEFAULT;
+
+ player->special_action_bored = ACTION_DEFAULT;
+ player->special_action_sleeping = ACTION_DEFAULT;
+ }
+}
+
+static byte PlayerActions(struct PlayerInfo *player, byte player_action)
+{
+ boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
+ int left = player_action & JOY_LEFT;
+ int right = player_action & JOY_RIGHT;
+ int up = player_action & JOY_UP;
+ 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);
+
+ if (!player->active || tape.pausing)
+ return 0;
+
+ if (player_action)
+ {
+ if (button1)
+ snapped = SnapField(player, dx, dy);
+ else
+ {
+ if (button2)
+ dropped = DropElement(player);
+
+ moved = MovePlayer(player, dx, dy);
+ }
+
+ if (tape.single_step && tape.recording && !tape.pausing)
+ {
+ if (button1 || (dropped && !moved))
+ {
+ TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+ SnapField(player, 0, 0); /* stop snapping */
+ }
+ }
+
+ SetPlayerWaiting(player, FALSE);
+
+ return player_action;
+ }
+ else
+ {
+ /* no actions for this player (no input at player's configured device) */
+
+ DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
+ SnapField(player, 0, 0);
+ CheckGravityMovementWhenNotMoving(player);
+
+ if (player->MovPos == 0)
+ SetPlayerWaiting(player, TRUE);
+
+ if (player->MovPos == 0) /* needed for tape.playing */
+ player->is_moving = FALSE;
+
+ player->is_dropping = FALSE;
+
+ return 0;
+ }
+}
+
+void AdvanceFrameAndPlayerCounters(int player_nr)
+{
+ int i;
+
+ /* advance frame counters (global frame counter and time frame counter) */
+ FrameCounter++;
+ TimeFrames++;
+
+ /* advance player counters (counters for move delay, move animation etc.) */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ boolean advance_player_counters = (player_nr == -1 || player_nr == i);
+ int move_delay_value = stored_player[i].move_delay_value;
+ int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
+
+ if (!advance_player_counters) /* not all players may be affected */
+ continue;
+
+#if USE_NEW_PLAYER_ANIM
+ if (move_frames == 0) /* less than one move per game frame */
+ {
+ int stepsize = TILEX / move_delay_value;
+ int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
+ int count = (stored_player[i].is_moving ?
+ ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
+
+ if (count % delay == 0)
+ move_frames = 1;
+ }
+#endif
+
+ stored_player[i].Frame += move_frames;
+
+ if (stored_player[i].MovPos != 0)
+ stored_player[i].StepFrame += move_frames;
+
+ if (stored_player[i].move_delay > 0)
+ stored_player[i].move_delay--;
+
+ /* due to bugs in previous versions, counter must count up, not down */
+ if (stored_player[i].push_delay != -1)
+ stored_player[i].push_delay++;
+
+ if (stored_player[i].drop_delay > 0)
+ stored_player[i].drop_delay--;
+ }
+}
+
+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];
+
+ if (game_status != GAME_MODE_PLAYING)