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_STEP_DELAY(e) ( (element_info[e].step_delay_fixed) + \
+ RND(element_info[e].step_delay_random))
+#define GET_MAX_STEP_DELAY(e) ( (element_info[e].step_delay_fixed) + \
+ (element_info[e].step_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))
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)
+#define CheckTriggeredElementChangeByMouse(x, y, e, ev, s) \
+ CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
#define CheckElementChange(x, y, e, te, ev) \
CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
#define CheckElementChangeBySide(x, y, e, te, ev, s) \
CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
+#define CheckElementChangeByMouse(x, y, e, ev, s) \
+ CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
static void PlayLevelSound(int, int, int);
static void PlayLevelSoundNearest(int, int, int);
static int getInvisibleActiveFromInvisibleElement(int);
static int getInvisibleFromInvisibleActiveElement(int);
+static void TestFieldAfterSnapping(int, int, int, int, int);
+
static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
// for detection of endless loops, caused by custom element programming
level.game_engine_type == GAME_ENGINE_TYPE_MM ?
MM_HEALTH(game_mm.laser_overload_value) :
game.health);
+ int sync_random_frame = INIT_GFX_RANDOM(); // random, but synchronized
UpdatePlayfieldElementCount();
stored_player[player_nr].num_white_keys;
}
+ // re-arrange keys on game panel, if needed or if defined by style settings
+ for (i = 0; i < MAX_NUM_KEYS + 1; i++) // all normal keys + white key
+ {
+ int nr = GAME_PANEL_KEY_1 + i;
+ struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
+ struct TextPosInfo *pos = gpc->pos;
+
+ // skip check if key is not in the player's inventory
+ if (gpc->value == EL_EMPTY)
+ continue;
+
+ // check if keys should be arranged on panel from left to right
+ if (pos->style == STYLE_LEFTMOST_POSITION)
+ {
+ // check previous key positions (left from current key)
+ for (k = 0; k < i; k++)
+ {
+ int nr_new = GAME_PANEL_KEY_1 + k;
+
+ if (game_panel_controls[nr_new].value == EL_EMPTY)
+ {
+ game_panel_controls[nr_new].value = gpc->value;
+ gpc->value = EL_EMPTY;
+
+ break;
+ }
+ }
+ }
+
+ // check if "undefined" keys can be placed at some other position
+ if (pos->x == -1 && pos->y == -1)
+ {
+ int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
+
+ // 1st try: display key at the same position as normal or EM keys
+ if (game_panel_controls[nr_new].value == EL_EMPTY)
+ {
+ game_panel_controls[nr_new].value = gpc->value;
+ }
+ else
+ {
+ // 2nd try: display key at the next free position in the key panel
+ for (k = 0; k < STD_NUM_KEYS; k++)
+ {
+ nr_new = GAME_PANEL_KEY_1 + k;
+
+ if (game_panel_controls[nr_new].value == EL_EMPTY)
+ {
+ game_panel_controls[nr_new].value = gpc->value;
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
for (i = 0; i < NUM_PANEL_INVENTORY; i++)
{
game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
}
game_panel_controls[GAME_PANEL_SCORE].value = score;
- game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
+ game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
game_panel_controls[GAME_PANEL_TIME].value = time;
int last_anim_random_frame = gfx.anim_random_frame;
int element = gpc->value;
int graphic = el2panelimg(element);
+ int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
+ sync_random_frame : INIT_GFX_RANDOM());
if (gpc->value != gpc->last_value)
{
gpc->gfx_frame = 0;
- gpc->gfx_random = INIT_GFX_RANDOM();
+ gpc->gfx_random = init_gfx_random;
}
else
{
if (ANIM_MODE(graphic) == ANIM_RANDOM &&
IS_NEXT_FRAME(gpc->gfx_frame, graphic))
- gpc->gfx_random = INIT_GFX_RANDOM();
+ gpc->gfx_random = init_gfx_random;
}
if (ANIM_MODE(graphic) == ANIM_RANDOM)
if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
gpc->gfx_frame = element_info[element].collect_score;
- gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
- gpc->gfx_frame);
+ gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
if (ANIM_MODE(graphic) == ANIM_RANDOM)
gfx.anim_random_frame = last_anim_random_frame;
{
int last_anim_random_frame = gfx.anim_random_frame;
int graphic = gpc->graphic;
+ int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
+ sync_random_frame : INIT_GFX_RANDOM());
if (gpc->value != gpc->last_value)
{
gpc->gfx_frame = 0;
- gpc->gfx_random = INIT_GFX_RANDOM();
+ gpc->gfx_random = init_gfx_random;
}
else
{
if (ANIM_MODE(graphic) == ANIM_RANDOM &&
IS_NEXT_FRAME(gpc->gfx_frame, graphic))
- gpc->gfx_random = INIT_GFX_RANDOM();
+ gpc->gfx_random = init_gfx_random;
}
if (ANIM_MODE(graphic) == ANIM_RANDOM)
if (PANEL_DEACTIVATED(pos))
continue;
+ if (pos->class == get_hash_from_key("extra_panel_items") &&
+ !setup.prefer_extra_panel_items)
+ continue;
+
gpc->last_value = value;
gpc->last_frame = frame;
DisplayGameControlValues();
}
-#if 0
-static void UpdateGameDoorValues(void)
+void UpdateGameDoorValues(void)
{
UpdateGameControlValues();
}
-#endif
void DrawGameDoorValues(void)
{
InitGameEngine();
InitGameControlValues();
- // initialize tape actions from game when recording tape
if (tape.recording)
{
+ // initialize tape actions from game when recording tape
tape.use_key_actions = game.use_key_actions;
tape.use_mouse_actions = game.use_mouse_actions;
+
+ // initialize visible playfield size when recording tape (for team mode)
+ tape.scr_fieldx = SCR_FIELDX;
+ tape.scr_fieldy = SCR_FIELDY;
}
// don't play tapes over network
player->shield_normal_time_left = 0;
player->shield_deadly_time_left = 0;
+ player->last_removed_element = EL_UNDEFINED;
+
player->inventory_infinite_element = EL_UNDEFINED;
player->inventory_size = 0;
game.switchgate_pos = 0;
game.wind_direction = level.wind_direction_initial;
+ game.time_final = 0;
+ game.score_time_final = 0;
+
game.score = 0;
game.score_final = 0;
game.restart_level = FALSE;
game.restart_game_message = NULL;
+
game.request_active = FALSE;
+ game.request_active_or_moving = FALSE;
if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
InitGameActions_MM();
game.LevelSolved = TRUE;
game.GameOver = TRUE;
-
- game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
- game_em.lev->score :
- level.game_engine_type == GAME_ENGINE_TYPE_MM ?
- game_mm.score :
- game.score);
- game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
- MM_HEALTH(game_mm.laser_overload_value) :
- game.health);
-
- game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
- game.LevelSolved_CountingScore = game.score_final;
- game.LevelSolved_CountingHealth = game.health_final;
}
void GameWon(void)
{
static int time_count_steps;
static int time, time_final;
- static int score, score_final;
+ static float score, score_final; // needed for time score < 10 for 10 seconds
static int health, health_final;
static int game_over_delay_1 = 0;
static int game_over_delay_2 = 0;
static int game_over_delay_3 = 0;
- int game_over_delay_value_1 = 50;
- int game_over_delay_value_2 = 25;
- int game_over_delay_value_3 = 50;
+ int time_score_base = MIN(MAX(1, level.time_score_base), 10);
+ float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
if (!game.LevelSolved_GameWon)
{
if (local_player->active && local_player->MovPos)
return;
+ game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
+ game.score_time_final = (level.use_step_counter ? TimePlayed :
+ TimePlayed * FRAMES_PER_SECOND + TimeFrames);
+
+ game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ game_em.lev->score :
+ level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+ game_mm.score :
+ game.score);
+
+ game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+ MM_HEALTH(game_mm.laser_overload_value) :
+ game.health);
+
+ game.LevelSolved_CountingTime = game.time_final;
+ game.LevelSolved_CountingScore = game.score_final;
+ game.LevelSolved_CountingHealth = game.health_final;
+
game.LevelSolved_GameWon = TRUE;
game.LevelSolved_SaveTape = tape.recording;
game.LevelSolved_SaveScore = !tape.playing;
TapeStop();
- game_over_delay_1 = 0;
- game_over_delay_2 = 0;
- game_over_delay_3 = game_over_delay_value_3;
+ game_over_delay_1 = FRAMES_PER_SECOND; // delay before counting time
+ game_over_delay_2 = FRAMES_PER_SECOND / 2; // delay before counting health
+ game_over_delay_3 = FRAMES_PER_SECOND; // delay before ending the game
- time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
+ time = time_final = game.time_final;
score = score_final = game.score_final;
health = health_final = game.health_final;
- if (level.score[SC_TIME_BONUS] > 0)
+ if (time_score > 0)
{
+ int time_final_max = 999;
+ int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
+ int time_frames = 0;
+ int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
+ int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
+
if (TimeLeft > 0)
{
time_final = 0;
- score_final += TimeLeft * level.score[SC_TIME_BONUS];
+ time_frames = time_frames_left;
}
- else if (game.no_time_limit && TimePlayed < 999)
+ else if (game.no_time_limit && TimePlayed < time_final_max)
{
- time_final = 999;
- score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
+ time_final = time_final_max;
+ time_frames = time_frames_final_max - time_frames_played;
}
- time_count_steps = MAX(1, ABS(time_final - time) / 100);
+ score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
- game_over_delay_1 = game_over_delay_value_1;
+ time_count_steps = MAX(1, ABS(time_final - time) / 100);
if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
{
health_final = 0;
- score_final += health * level.score[SC_TIME_BONUS];
-
- game_over_delay_2 = game_over_delay_value_2;
+ score_final += health * time_score;
}
game.score_final = score_final;
game.health_final = health_final;
}
- if (level_editor_test_game)
+ if (level_editor_test_game || !setup.count_score_after_game)
{
time = time_final;
score = score_final;
PlaySound(SND_GAME_WINNING);
}
- if (game_over_delay_1 > 0)
+ if (setup.count_score_after_game)
{
- game_over_delay_1--;
+ if (time != time_final)
+ {
+ if (game_over_delay_1 > 0)
+ {
+ game_over_delay_1--;
- return;
- }
+ return;
+ }
- if (time != time_final)
- {
- int time_to_go = ABS(time_final - time);
- int time_count_dir = (time < time_final ? +1 : -1);
+ int time_to_go = ABS(time_final - time);
+ int time_count_dir = (time < time_final ? +1 : -1);
- if (time_to_go < time_count_steps)
- time_count_steps = 1;
+ if (time_to_go < time_count_steps)
+ time_count_steps = 1;
- time += time_count_steps * time_count_dir;
- score += time_count_steps * level.score[SC_TIME_BONUS];
+ time += time_count_steps * time_count_dir;
+ score += time_count_steps * time_score;
- game.LevelSolved_CountingTime = time;
- game.LevelSolved_CountingScore = score;
+ // set final score to correct rounding differences after counting score
+ if (time == time_final)
+ score = score_final;
- game_panel_controls[GAME_PANEL_TIME].value = time;
- game_panel_controls[GAME_PANEL_SCORE].value = score;
+ game.LevelSolved_CountingTime = time;
+ game.LevelSolved_CountingScore = score;
- DisplayGameControlValues();
+ game_panel_controls[GAME_PANEL_TIME].value = time;
+ game_panel_controls[GAME_PANEL_SCORE].value = score;
- if (time == time_final)
- StopSound(SND_GAME_LEVELTIME_BONUS);
- else if (setup.sound_loops)
- PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
- else
- PlaySound(SND_GAME_LEVELTIME_BONUS);
+ DisplayGameControlValues();
- return;
- }
+ if (time == time_final)
+ StopSound(SND_GAME_LEVELTIME_BONUS);
+ else if (setup.sound_loops)
+ PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
+ else
+ PlaySound(SND_GAME_LEVELTIME_BONUS);
- if (game_over_delay_2 > 0)
- {
- game_over_delay_2--;
+ return;
+ }
- return;
- }
+ if (health != health_final)
+ {
+ if (game_over_delay_2 > 0)
+ {
+ game_over_delay_2--;
- if (health != health_final)
- {
- int health_count_dir = (health < health_final ? +1 : -1);
+ return;
+ }
- health += health_count_dir;
- score += level.score[SC_TIME_BONUS];
+ int health_count_dir = (health < health_final ? +1 : -1);
- game.LevelSolved_CountingHealth = health;
- game.LevelSolved_CountingScore = score;
+ health += health_count_dir;
+ score += time_score;
- game_panel_controls[GAME_PANEL_HEALTH].value = health;
- game_panel_controls[GAME_PANEL_SCORE].value = score;
+ game.LevelSolved_CountingHealth = health;
+ game.LevelSolved_CountingScore = score;
- DisplayGameControlValues();
+ game_panel_controls[GAME_PANEL_HEALTH].value = health;
+ game_panel_controls[GAME_PANEL_SCORE].value = score;
- if (health == health_final)
- StopSound(SND_GAME_LEVELTIME_BONUS);
- else if (setup.sound_loops)
- PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
- else
- PlaySound(SND_GAME_LEVELTIME_BONUS);
+ DisplayGameControlValues();
- return;
+ if (health == health_final)
+ StopSound(SND_GAME_LEVELTIME_BONUS);
+ else if (setup.sound_loops)
+ PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
+ else
+ PlaySound(SND_GAME_LEVELTIME_BONUS);
+
+ return;
+ }
}
game.panel.active = FALSE;
CloseDoor(DOOR_CLOSE_1);
SaveTapeChecked_LevelSolved(tape.level_nr); // ask to save tape
+
+ // set unique basename for score tape (also saved in high score table)
+ strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
}
// if no tape is to be saved, close both doors simultaneously
hi_pos = NewHiScore(last_level_nr);
- if (hi_pos >= 0 && !setup.skip_scores_after_game)
+ if (hi_pos >= 0 && setup.show_scores_after_game)
{
SetGameStatus(GAME_MODE_SCORES);
LoadScore(level_nr);
if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
- game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
+ game.score_final < scores.entry[MAX_SCORE_ENTRIES - 1].score)
return -1;
for (k = 0; k < MAX_SCORE_ENTRIES; k++)
{
- if (game.score_final > highscore[k].Score)
+ struct ScoreEntry *entry = &scores.entry[k];
+ boolean score_is_better = (game.score_final > entry->score);
+ boolean score_is_equal = (game.score_final == entry->score);
+ boolean time_is_better = (game.score_time_final < entry->time);
+ boolean time_is_equal = (game.score_time_final == entry->time);
+ boolean better_by_score = (score_is_better ||
+ (score_is_equal && time_is_better));
+ boolean better_by_time = (time_is_better ||
+ (time_is_equal && score_is_better));
+ boolean is_better = (level.rate_time_over_score ? better_by_time :
+ better_by_score);
+ boolean entry_is_empty = (entry->score == 0 &&
+ entry->time == 0);
+
+ if (is_better || entry_is_empty)
{
// player has made it to the hall of fame
if (one_score_entry_per_name)
{
for (l = k; l < MAX_SCORE_ENTRIES; l++)
- if (strEqual(setup.player_name, highscore[l].Name))
+ if (strEqual(setup.player_name, scores.entry[l].name))
m = l;
if (m == k) // player's new highscore overwrites his old one
for (l = m; l > k; l--)
{
- strcpy(highscore[l].Name, highscore[l - 1].Name);
- highscore[l].Score = highscore[l - 1].Score;
+ strcpy(scores.entry[l].name, scores.entry[l - 1].name);
+ scores.entry[l].score = scores.entry[l - 1].score;
+ scores.entry[l].time = scores.entry[l - 1].time;
}
}
put_into_list:
- strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
- highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
- highscore[k].Score = game.score_final;
+ strcpy(entry->tape_basename, tape.score_tape_basename);
+ strncpy(entry->name, setup.player_name, MAX_PLAYER_NAME_LEN);
+ entry->name[MAX_PLAYER_NAME_LEN] = '\0';
+ entry->score = game.score_final;
+ entry->time = game.score_time_final;
position = k;
break;
}
else if (one_score_entry_per_name &&
- !strncmp(setup.player_name, highscore[k].Name,
- MAX_PLAYER_NAME_LEN))
+ !strncmp(setup.player_name, entry->name, MAX_PLAYER_NAME_LEN))
break; // player already there with a higher score
}
- if (position >= 0)
+ if (position >= 0)
+ {
+ SaveScoreTape(level_nr);
SaveScore(level_nr);
+ }
return position;
}
return;
if (Back[x][y])
- DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
+ DrawLevelElement(x, y, Back[x][y]);
else if (Store[x][y])
- DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
+ DrawLevelElement(x, y, Store[x][y]);
+ else if (game.use_masked_elements)
+ DrawLevelElement(x, y, EL_EMPTY);
frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
- if (Back[x][y] || Store[x][y])
+ if (Back[x][y] || Store[x][y] || game.use_masked_elements)
DrawGraphicThruMask(sx, sy, graphic, frame);
else
DrawGraphic(sx, sy, graphic, frame);
DrawLevelElementThruMask(x, y, Back[x][y]);
}
else if (!IS_WALKABLE_INSIDE(Back[x][y]))
- DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
+ DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
}
}
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);
+ boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
- MovPos[x][y] += getElementMoveStepsize(x, y);
-
- if (pushed_by_player) // special case: moving object pushed by player
+ if (pushed_by_player) // special case: moving object pushed by player
+ {
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
+ {
+ if (!MovDelay[x][y])
+ MovPos[x][y] += getElementMoveStepsize(x, y);
+
+ if (MovDelay[x][y])
+ MovDelay[x][y]--;
+ else
+ MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
+
+ if (MovDelay[x][y])
+ {
+ TEST_DrawLevelField(x, y);
+
+ return; // element is still waiting
+ }
+ }
+ else // normal case: generically moving object
+ {
+ MovPos[x][y] += getElementMoveStepsize(x, y);
+ }
if (ABS(MovPos[x][y]) < TILEX)
{
if (!player->is_dropping)
player->was_dropping = FALSE;
}
+
+ static struct MouseActionInfo mouse_action_last = { 0 };
+ struct MouseActionInfo mouse_action = player->effective_mouse_action;
+ boolean new_released = (!mouse_action.button && mouse_action_last.button);
+
+ if (new_released)
+ CheckSaveEngineSnapshotToList();
+
+ mouse_action_last = mouse_action;
}
static void CheckSingleStepMode(struct PlayerInfo *player)
{
if (tape.single_step && tape.recording && !tape.pausing)
{
- /* as it is called "single step mode", just return to pause mode when the
- player stopped moving after one tile (or never starts moving at all) */
- if (!player->is_moving &&
- !player->is_pushing &&
- !player->is_dropping_pressed &&
- !player->effective_mouse_action.button)
- TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+ // as it is called "single step mode", just return to pause mode when the
+ // player stopped moving after one tile (or never starts moving at all)
+ // (reverse logic needed here in case single step mode used in team mode)
+ if (player->is_moving ||
+ player->is_pushing ||
+ player->is_dropping_pressed ||
+ player->effective_mouse_action.button)
+ game.enter_single_step_mode = FALSE;
}
CheckSaveEngineSnapshot(player);
DrawGameDoorValues();
}
+ // check single step mode (set flag and clear again if any player is active)
+ game.enter_single_step_mode =
+ (tape.single_step && tape.recording && !tape.pausing);
+
for (i = 0; i < MAX_PLAYERS; i++)
{
int actual_player_action = stored_player[i].effective_action;
ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
}
+ // single step pause mode may already have been toggled by "ScrollPlayer()"
+ if (game.enter_single_step_mode && !tape.pausing)
+ TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
ScrollScreen(NULL, SCROLL_GO_ON);
/* for backwards compatibility, the following code emulates a fixed bug that
MovDelay[x][y]--;
if (MovDelay[x][y] <= 0)
{
+ int element = Store[x][y];
+ int move_direction = MovDir[x][y];
+ int player_index_bit = Store2[x][y];
+
+ Store[x][y] = 0;
+ Store2[x][y] = 0;
+
RemoveField(x, y);
TEST_DrawLevelField(x, y);
- TestIfElementTouchesCustomElement(x, y); // for empty space
+ TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
}
}
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;
if (new_button)
{
- CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
- CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
+ CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
+ CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
+ ch_button);
}
- CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
- CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
+ CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
+ CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
+ ch_button);
}
SCAN_PLAYFIELD(x, y)
static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
{
int min_x = x, min_y = y, max_x = x, max_y = y;
+ int scr_fieldx = getScreenFieldSizeX();
+ int scr_fieldy = getScreenFieldSizeY();
int i;
for (i = 0; i < MAX_PLAYERS; i++)
max_y = MAX(max_y, jy);
}
- return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
+ return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
}
static boolean AllPlayersInVisibleScreen(void)
if (!player->is_pushing)
TestIfElementTouchesCustomElement(jx, jy); // for empty space
+ if (level.finish_dig_collect &&
+ (player->is_digging || player->is_collecting))
+ {
+ int last_element = player->last_removed_element;
+ int move_direction = player->MovDir;
+ int enter_side = MV_DIR_OPPOSITE(move_direction);
+ int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
+ CE_PLAYER_COLLECTS_X);
+
+ CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
+ player->index_bit, enter_side);
+
+ player->last_removed_element = EL_UNDEFINED;
+ }
+
if (!player->active)
RemovePlayer(player);
}
- if (!game.LevelSolved && level.use_step_counter)
+ if (level.use_step_counter)
{
int i;
{
TimeLeft--;
- if (TimeLeft <= 10 && setup.time_limit)
+ if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
DisplayGameControlValues();
- if (!TimeLeft && setup.time_limit)
+ if (!TimeLeft && setup.time_limit && !game.LevelSolved)
for (i = 0; i < MAX_PLAYERS; i++)
KillPlayer(&stored_player[i]);
}
game.players_still_needed--;
}
-static void setFieldForSnapping(int x, int y, int element, int direction)
+static void SetFieldForSnapping(int x, int y, int element, int direction,
+ int player_index_bit)
{
struct ElementInfo *ei = &element_info[element];
int direction_bit = MV_DIR_TO_BIT(direction);
Tile[x][y] = EL_ELEMENT_SNAPPING;
MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
+ MovDir[x][y] = direction;
+ Store[x][y] = element;
+ Store2[x][y] = player_index_bit;
ResetGfxAnimation(x, y);
GfxFrame[x][y] = -1;
}
+static void TestFieldAfterSnapping(int x, int y, int element, int direction,
+ int player_index_bit)
+{
+ TestIfElementTouchesCustomElement(x, y); // for empty space
+
+ if (level.finish_dig_collect)
+ {
+ int dig_side = MV_DIR_OPPOSITE(direction);
+
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+ player_index_bit, dig_side);
+ }
+}
+
/*
=============================================================================
checkDiagonalPushing()
PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
- CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
- player->index_bit, dig_side);
+ // use old behaviour for old levels (digging)
+ if (!level.finish_dig_collect)
+ {
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
+ player->index_bit, dig_side);
+
+ // if digging triggered player relocation, finish digging tile
+ if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
+ SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
+ }
if (mode == DF_SNAP)
{
if (level.block_snap_field)
- setFieldForSnapping(x, y, element, move_direction);
+ SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
else
- TestIfElementTouchesCustomElement(x, y); // for empty space
+ TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
- CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
- player->index_bit, dig_side);
+ // use old behaviour for old levels (snapping)
+ if (!level.finish_dig_collect)
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+ player->index_bit, dig_side);
}
}
else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
RaiseScoreElement(element);
PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
- if (is_player)
+ // use old behaviour for old levels (collecting)
+ if (!level.finish_dig_collect && is_player)
+ {
CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
player->index_bit, dig_side);
+ // if collecting triggered player relocation, finish collecting tile
+ if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
+ SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
+ }
+
if (mode == DF_SNAP)
{
if (level.block_snap_field)
- setFieldForSnapping(x, y, element, move_direction);
+ SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
else
- TestIfElementTouchesCustomElement(x, y); // for empty space
+ TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
- CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
- player->index_bit, dig_side);
+ // use old behaviour for old levels (snapping)
+ if (!level.finish_dig_collect)
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+ player->index_bit, dig_side);
}
}
else if (player_can_move_or_snap && IS_PUSHABLE(element))
{
player->is_collecting = !player->is_digging;
player->is_active = TRUE;
+
+ player->last_removed_element = element;
}
}
{
if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
{
- // closing door required in case of envelope style request dialogs
- if (!skip_request)
+ if (!quick_quit)
{
// prevent short reactivation of overlay buttons while closing door
SetOverlayActive(FALSE);
+ // door may still be open due to skipped or envelope style request
CloseDoor(DOOR_CLOSE_1);
}
}
}
-void RequestQuitGame(boolean ask_if_really_quit)
+void RequestQuitGame(boolean escape_key_pressed)
{
- boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
- boolean skip_request = game.all_players_gone || quick_quit;
+ boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
+ boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
+ level_editor_test_game);
+ boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
+ quick_quit);
RequestQuitGameExt(skip_request, quick_quit,
"Do you really want to quit the game?");
}
else
{
+ // needed in case of envelope request to close game panel
+ CloseDoor(DOOR_CLOSE_1);
+
SetGameStatus(GAME_MODE_MAIN);
DrawMainMenu();
if (tape.playing)
TapeStop();
else
- RequestQuitGame(TRUE);
+ RequestQuitGame(FALSE);
break;