ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
#define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
- ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
+ ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
#define PACMAN_CAN_ENTER_FIELD(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
static void ExecuteCustomElementAction(int, int, int, int);
static boolean ChangeElement(int, int, int, int);
-static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
+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)
+ 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) \
SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
}
+ // ---------- initialize if element can trigger global animations -----------
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[i];
+
+ ei->has_anim_event = FALSE;
+ }
+
+ InitGlobalAnimEventsForCustomElements();
+
// ---------- initialize internal run-time variables ------------------------
for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
for (j = 0; j < ei->num_change_pages; j++)
{
- ei->change_page[j].actual_trigger_element = EL_EMPTY;
- ei->change_page[j].actual_trigger_player = EL_EMPTY;
- ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
- 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;
+ struct ElementChangeInfo *change = &ei->change_page[j];
+
+ change->actual_trigger_element = EL_EMPTY;
+ change->actual_trigger_player = EL_EMPTY;
+ change->actual_trigger_player_bits = CH_PLAYER_NONE;
+ change->actual_trigger_side = CH_SIDE_NONE;
+ change->actual_trigger_ce_value = 0;
+ change->actual_trigger_ce_score = 0;
}
}
for (j = 0; j < ei->num_change_pages; j++)
{
- if (!ei->change_page[j].can_change_or_has_action)
+ struct ElementChangeInfo *change = &ei->change_page[j];
+
+ if (!change->can_change_or_has_action)
continue;
- if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
+ if (change->has_event[CE_BY_OTHER_ACTION])
{
- int trigger_element = ei->change_page[j].trigger_element;
+ int trigger_element = change->trigger_element;
for (k = 0; k < NUM_CHANGE_EVENTS; k++)
{
- if (ei->change_page[j].has_event[k])
+ if (change->has_event[k])
{
if (IS_GROUP_ELEMENT(trigger_element))
{
{
int element = EL_CUSTOM_START + i;
- if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
- HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
- HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
- HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
+ if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
+ HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
+ HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
+ HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
game.use_mouse_actions = TRUE;
}
}
// force restarting global animations displayed during game play
RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
+ // this is required for "transforming" fade modes like cross-fading
+ // (else global animations will be stopped, but not restarted here)
+ SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
+
SetGameStatus(GAME_MODE_PLAYING);
}
void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
{
- int oldx = x, oldy = y;
int direction = MovDir[x][y];
-
- if (direction == MV_LEFT)
- oldx++;
- else if (direction == MV_RIGHT)
- oldx--;
- else if (direction == MV_UP)
- oldy++;
- else if (direction == MV_DOWN)
- oldy--;
+ int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
+ int oldy = y + (direction & MV_UP ? +1 : direction & MV_DOWN ? -1 : 0);
*comes_from_x = oldx;
*comes_from_y = oldy;
int oldx, oldy;
Blocked2Moving(x, y, &oldx, &oldy);
+
return Tile[oldx][oldy];
}
- else
- return element;
+
+ return element;
}
static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
{
// like MovingOrBlocked2Element(), but if element is moving
- // and (x,y) is the field the moving element is just leaving,
+ // and (x, y) is the field the moving element is just leaving,
// return EL_BLOCKED instead of the element value
int element = Tile[x][y];
int last_phase;
int border_element;
- // !!! eliminate this variable !!!
- int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
-
if (game.explosions_delayed)
{
ExplodeField[ex][ey] = mode;
if (phase == EX_PHASE_START) // initialize 'Store[][]' field
{
int center_element = Tile[ex][ey];
+ int ce_value = CustomValue[ex][ey];
+ int ce_score = element_info[center_element].collect_score;
int artwork_element, explosion_element; // set these values later
// remove things displayed in background while burning dynamite
else
Store[x][y] = EL_EMPTY;
+ if (IS_CUSTOM_ELEMENT(center_element))
+ Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
+ Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
+ Store[x][y] >= EL_PREV_CE_8 &&
+ Store[x][y] <= EL_NEXT_CE_8 ?
+ RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
+ Store[x][y]);
+
if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
center_element == EL_AMOEBA_TO_DIAMOND)
Store2[x][y] = element;
int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
int frame = getGraphicAnimationFrameXY(graphic, x, y);
- if (phase == delay)
+ if (phase == 1)
TEST_DrawLevelFieldCrumbled(x, y);
if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
boolean can_turn_left =
CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
boolean can_turn_right =
- CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
if (element_info[element].move_stepsize == 0) // "not moving"
return;
PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
- DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
+ DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
game.friends_still_needed--;
if (!game.friends_still_needed &&
}
else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
{
- if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
+ if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
TEST_DrawLevelField(newx, newy);
else
GfxDir[x][y] = MovDir[x][y] = MV_NONE;
if (pushed_by_player) // special case: moving object pushed by player
{
- MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
+ MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
}
else if (use_step_delay) // special case: moving object has step delay
{
CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
player->index_bit, push_side);
- CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
+ CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
player->index_bit, push_side);
}
if (GFX_CRUMBLED(new_element))
TEST_DrawLevelFieldCrumbledNeighbours(x, y);
- }
- // check if element under the player changes from accessible to unaccessible
- // (needed for special case of dropping element which then changes)
- // (must be checked after creating new element for walkable group elements)
- if (IS_PLAYER(x, y) && !player_explosion_protected &&
- IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
- {
- Bang(x, y);
+ if (old_element == EL_EXPLOSION)
+ {
+ Store[x][y] = Store2[x][y] = 0;
- return;
+ // check if new element replaces an exploding player, requiring cleanup
+ if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
+ StorePlayer[x][y] = 0;
+ }
+
+ // check if element under the player changes from accessible to unaccessible
+ // (needed for special case of dropping element which then changes)
+ // (must be checked after creating new element for walkable group elements)
+ if (IS_PLAYER(x, y) && !player_explosion_protected &&
+ IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+ {
+ KillPlayer(PLAYERINFO(x, y));
+
+ return;
+ }
}
// "ChangeCount" not set yet to allow "entered by player" change one time
ChangeCount[x][y]++; // count number of changes in the same frame
+ if (ei->has_anim_event)
+ HandleGlobalAnimEventByElementChange(element, page, x, y);
+
if (change->explode)
{
Bang(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]);
+ int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
- if (IS_ANIMATED(graphic))
- DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ // also needed if CE can not change, but has CE delay with CE action
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ if (change->can_change)
+ {
if (change->change_function)
change->change_function(x, y);
}
}
}
+static void HandleMouseAction(struct MouseActionInfo *mouse_action,
+ struct MouseActionInfo *mouse_action_last)
+{
+ if (mouse_action->button)
+ {
+ int new_button = (mouse_action->button && mouse_action_last->button == 0);
+ int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
+ int x = mouse_action->lx;
+ int y = mouse_action->ly;
+ int element = Tile[x][y];
+
+ if (new_button)
+ {
+ CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
+ CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
+ ch_button);
+ }
+
+ CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
+ CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
+ ch_button);
+
+ if (level.use_step_counter)
+ {
+ boolean counted_click = FALSE;
+
+ // element clicked that can change when clicked/pressed
+ if (CAN_CHANGE_OR_HAS_ACTION(element) &&
+ (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
+ HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
+ counted_click = TRUE;
+
+ // element clicked that can trigger change when clicked/pressed
+ if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
+ trigger_events[element][CE_MOUSE_PRESSED_ON_X])
+ counted_click = TRUE;
+
+ if (new_button && counted_click)
+ CheckLevelTime_StepCounter();
+ }
+ }
+}
+
void StartGameActions(boolean init_network_game, boolean record_tape,
int random_seed)
{
#endif
}
- if (mouse_action.button)
- {
- int new_button = (mouse_action.button && mouse_action_last.button == 0);
- int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
-
- x = mouse_action.lx;
- y = mouse_action.ly;
- element = Tile[x][y];
-
- if (new_button)
- {
- CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
- CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
- ch_button);
- }
-
- CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
- CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
- ch_button);
-
- if (level.use_step_counter)
- {
- boolean counted_click = FALSE;
-
- // element clicked that can change when clicked/pressed
- if (CAN_CHANGE_OR_HAS_ACTION(element) &&
- (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
- HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
- counted_click = TRUE;
-
- // element clicked that can trigger change when clicked/pressed
- if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
- trigger_events[element][CE_MOUSE_PRESSED_ON_X])
- counted_click = TRUE;
-
- if (new_button && counted_click)
- CheckLevelTime_StepCounter();
- }
- }
+ HandleMouseAction(&mouse_action, &mouse_action_last);
SCAN_PLAYFIELD(x, y)
{
element == EL_DIAGONAL_SHRINKING ||
element == EL_DIAGONAL_GROWING)
{
- graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
+ graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
}
y = RND(lev_fieldy);
element = Tile[x][y];
- if (!IS_PLAYER(x,y) &&
+ if (!IS_PLAYER(x, y) &&
(element == EL_EMPTY ||
CAN_GROW_INTO(element) ||
element == EL_QUICKSAND_EMPTY ||
!AllPlayersInSight(player, new_jx, new_jy))
return MP_NO_ACTION;
- can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
+ can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
if (can_move != MP_MOVING)
return can_move;
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);
+ // needed because pushed element has not yet reached its destination,
+ // so it would trigger a change event at its previous field location
+ if (!player->is_pushing)
+ {
+ if (IS_CUSTOM_ELEMENT(new_element))
+ CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
+ player->index_bit, enter_side);
- CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
- CE_PLAYER_ENTERS_X,
- player->index_bit, enter_side);
+ CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
+ CE_PLAYER_ENTERS_X,
+ player->index_bit, enter_side);
+ }
CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
CE_MOVE_OF_X, move_direction);
TestIfPlayerTouchesBadThing(jx, jy);
TestIfPlayerTouchesCustomElement(jx, jy);
- /* needed because pushed element has not yet reached its destination,
- so it would trigger a change event at its previous field location */
+ // needed because pushed element has not yet reached its destination,
+ // so it would trigger a change event at its previous field location
if (!player->is_pushing)
TestIfElementTouchesCustomElement(jx, jy); // for empty space
incorrectly give EL_PLAYER_1 for "player->element_nr") */
int player_element = PLAYERINFO(x, y)->initial_element;
+ // as element "X" is the player here, check opposite (center) side
CheckElementChangeBySide(xx, yy, border_element, player_element,
- CE_TOUCHING_X, border_side);
+ CE_TOUCHING_X, center_side);
}
}
else if (IS_PLAYER(xx, yy)) // player found at border element
incorrectly give EL_PLAYER_1 for "player->element_nr") */
int player_element = PLAYERINFO(xx, yy)->initial_element;
+ // as element "X" is the player here, check opposite (border) side
CheckElementChangeBySide(x, y, center_element, player_element,
- CE_TOUCHING_X, center_side);
+ CE_TOUCHING_X, border_side);
}
break;
CheckElementChangeBySide(xx, yy, border_element, center_element,
CE_TOUCHING_X, center_side);
- // (center element cannot be player, so we dont have to check this here)
+ // (center element cannot be player, so we don't have to check this here)
}
for (i = 0; i < NUM_DIRECTIONS; i++)
incorrectly give EL_PLAYER_1 for "player->element_nr") */
int player_element = PLAYERINFO(xx, yy)->initial_element;
+ // as element "X" is the player here, check opposite (border) side
CheckElementChangeBySide(x, y, center_element, player_element,
CE_TOUCHING_X, border_side);
}
player->killed = TRUE;
// remove accessible field at the player's position
- Tile[jx][jy] = EL_EMPTY;
+ RemoveField(jx, jy);
// deactivate shield (else Bang()/Explode() would not work right)
player->shield_normal_time_left = 0;
if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
return MP_NO_ACTION; // field has no opening in this direction
- if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
+ if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
return MP_NO_ACTION; // field has no opening in this direction
if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
static int getLevelMusicNr(void)
{
+ int level_pos = level_nr - leveldir_current->first_level;
+
if (levelset.music[level_nr] != MUS_UNDEFINED)
return levelset.music[level_nr]; // from config file
else
- return MAP_NOCONF_MUSIC(level_nr); // from music dir
+ return MAP_NOCONF_MUSIC(level_pos); // from music dir
}
static void FadeLevelSounds(void)
"Do you really want to quit the game?");
}
-static void RequestRestartGame(char *message)
+static char *getRestartGameMessage(void)
+{
+ boolean play_again = hasStartedNetworkGame();
+ static char message[MAX_OUTPUT_LINESIZE];
+ char *game_over_text = "Game over!";
+ char *play_again_text = " Play it again?";
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
+ game_mm.game_over_message != NULL)
+ game_over_text = game_mm.game_over_message;
+
+ snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
+ (play_again ? play_again_text : ""));
+
+ return message;
+}
+
+static void RequestRestartGame(void)
{
+ char *message = getRestartGameMessage();
boolean has_started_game = hasStartedNetworkGame();
int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
int door_state = DOOR_CLOSE_1;
}
}
-static char *getRestartGameMessage(void)
-{
- boolean play_again = hasStartedNetworkGame();
- static char message[MAX_OUTPUT_LINESIZE];
- char *game_over_text = "Game over!";
- char *play_again_text = " Play it again?";
-
- if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
- game_mm.game_over_message != NULL)
- game_over_text = game_mm.game_over_message;
-
- snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
- (play_again ? play_again_text : ""));
-
- return message;
-}
-
boolean CheckRestartGame(void)
{
static int game_over_delay = 0;
if (!setup.ask_on_game_over)
return FALSE;
- RequestRestartGame(getRestartGameMessage());
+ RequestRestartGame();
return TRUE;
}