#define GAME_CTRL_ID_SAVE 5
#define GAME_CTRL_ID_PAUSE2 6
#define GAME_CTRL_ID_LOAD 7
-#define GAME_CTRL_ID_PANEL_STOP 8
-#define GAME_CTRL_ID_PANEL_PAUSE 9
-#define GAME_CTRL_ID_PANEL_PLAY 10
-#define GAME_CTRL_ID_TOUCH_STOP 11
-#define GAME_CTRL_ID_TOUCH_PAUSE 12
-#define SOUND_CTRL_ID_MUSIC 13
-#define SOUND_CTRL_ID_LOOPS 14
-#define SOUND_CTRL_ID_SIMPLE 15
-#define SOUND_CTRL_ID_PANEL_MUSIC 16
-#define SOUND_CTRL_ID_PANEL_LOOPS 17
-#define SOUND_CTRL_ID_PANEL_SIMPLE 18
-
-#define NUM_GAME_BUTTONS 19
+#define GAME_CTRL_ID_RESTART 8
+#define GAME_CTRL_ID_PANEL_STOP 9
+#define GAME_CTRL_ID_PANEL_PAUSE 10
+#define GAME_CTRL_ID_PANEL_PLAY 11
+#define GAME_CTRL_ID_PANEL_RESTART 12
+#define GAME_CTRL_ID_TOUCH_STOP 13
+#define GAME_CTRL_ID_TOUCH_PAUSE 14
+#define GAME_CTRL_ID_TOUCH_RESTART 15
+#define SOUND_CTRL_ID_MUSIC 16
+#define SOUND_CTRL_ID_LOOPS 17
+#define SOUND_CTRL_ID_SIMPLE 18
+#define SOUND_CTRL_ID_PANEL_MUSIC 19
+#define SOUND_CTRL_ID_PANEL_LOOPS 20
+#define SOUND_CTRL_ID_PANEL_SIMPLE 21
+
+#define NUM_GAME_BUTTONS 22
// forward declaration for internal use
game_em.use_single_button =
(game.engine_version > VERSION_IDENT(4,0,0,2));
+ game_em.use_push_delay =
+ (game.engine_version > VERSION_IDENT(4,3,7,1));
+
game_em.use_snap_key_bug =
(game.engine_version < VERSION_IDENT(4,0,1,0));
change->actual_trigger_side = CH_SIDE_NONE;
change->actual_trigger_ce_value = 0;
change->actual_trigger_ce_score = 0;
+ change->actual_trigger_x = -1;
+ change->actual_trigger_y = -1;
}
}
level.game_engine_type == GAME_ENGINE_TYPE_EM &&
!setup.forced_scroll_delay ? 0 :
setup.scroll_delay ? setup.scroll_delay_value : 0);
- game.scroll_delay_value =
- MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
+ if (game.forced_scroll_delay_value == -1)
+ game.scroll_delay_value =
+ MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
// ---------- initialize game engine snapshots ------------------------------
for (i = 0; i < MAX_PLAYERS; i++)
game.LevelSolved_CountingScore = 0;
game.LevelSolved_CountingHealth = 0;
+ game.RestartGameRequested = FALSE;
+
game.panel.active = TRUE;
game.no_level_time_limit = (level.time == 0);
InitBeltMovement();
+ // required if level does not contain any "empty space" element
+ if (element_info[EL_EMPTY].use_gfx_element)
+ game.use_masked_elements = TRUE;
+
for (i = 0; i < MAX_PLAYERS; i++)
{
struct PlayerInfo *player = &stored_player[i];
scroll_y = SCROLL_POSITION_Y(local_player->jy);
}
+ if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
+ scroll_x = game.forced_scroll_x;
+ if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
+ scroll_y = game.forced_scroll_y;
+
// !!! FIX THIS (START) !!!
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
}
game.restart_level = FALSE;
-
game.request_active = FALSE;
- game.request_active_or_moving = FALSE;
if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
InitGameActions_MM();
{
// relocation _without_ centering of screen
- int center_scroll_x = SCROLL_POSITION_X(old_x);
- int center_scroll_y = SCROLL_POSITION_Y(old_y);
- int offset_x = x + (scroll_x - center_scroll_x);
- int offset_y = y + (scroll_y - center_scroll_y);
+ // apply distance between old and new player position to scroll position
+ int shifted_scroll_x = scroll_x + (x - old_x);
+ int shifted_scroll_y = scroll_y + (y - old_y);
- // for new screen position, apply previous offset to center position
- new_scroll_x = SCROLL_POSITION_X(offset_x);
- new_scroll_y = SCROLL_POSITION_Y(offset_y);
+ // make sure that shifted scroll position does not scroll beyond screen
+ new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
+ new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
+
+ // special case for teleporting from one end of the playfield to the other
+ // (this kludge prevents the destination area to be shifted by half a tile
+ // against the source destination for even screen width or screen height;
+ // probably most useful when used with high "game.forced_scroll_delay_value"
+ // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
+ if (quick_relocation)
+ {
+ if (EVEN(SCR_FIELDX))
+ {
+ // relocate (teleport) between left and right border (half or full)
+ if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
+ new_scroll_x = SBX_Right;
+ else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
+ new_scroll_x = SBX_Right - 1;
+ else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
+ new_scroll_x = SBX_Left;
+ else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
+ new_scroll_x = SBX_Left + 1;
+ }
+
+ if (EVEN(SCR_FIELDY))
+ {
+ // relocate (teleport) between top and bottom border (half or full)
+ if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
+ new_scroll_y = SBY_Lower;
+ else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
+ new_scroll_y = SBY_Lower - 1;
+ else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
+ new_scroll_y = SBY_Upper;
+ else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
+ new_scroll_y = SBY_Upper + 1;
+ }
+ }
}
if (quick_relocation)
if (GFX_CRUMBLED(new_element))
TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+ if (old_element == EL_EXPLOSION)
+ {
+ Store[x][y] = Store2[x][y] = 0;
+
+ // 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))
{
- Bang(x, y);
+ KillPlayer(PLAYERINFO(x, y));
return;
}
change->actual_trigger_side = CH_SIDE_NONE;
change->actual_trigger_ce_value = 0;
change->actual_trigger_ce_score = 0;
+ change->actual_trigger_x = -1;
+ change->actual_trigger_y = -1;
}
// do not change elements more than a specified maximum number of changes
ChangeCount[x][y]++; // count number of changes in the same frame
if (ei->has_anim_event)
- HandleGlobalAnimEventByElementChange(element, page, x, y);
+ HandleGlobalAnimEventByElementChange(element, page, x, y,
+ change->actual_trigger_x,
+ change->actual_trigger_y);
if (change->explode)
{
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);
+ change->actual_trigger_x = trigger_x;
+ change->actual_trigger_y = trigger_y;
if ((change->can_change && !change_done) || change->has_action)
{
change->actual_trigger_side = trigger_side;
change->actual_trigger_ce_value = CustomValue[x][y];
change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
+ change->actual_trigger_x = x;
+ change->actual_trigger_y = y;
// special case: trigger element not at (x,y) position for some events
if (check_trigger_element)
change->actual_trigger_ce_value = CustomValue[xx][yy];
change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
+ change->actual_trigger_x = xx;
+ change->actual_trigger_y = yy;
}
if (change->can_change && !change_done)
return FALSE;
}
- // do not handle game over if request dialog is already active
+ // do not ask to play again if request dialog is already active
if (game.request_active)
return FALSE;
+ // do not ask to play again if request dialog already handled
+ if (game.RestartGameRequested)
+ return FALSE;
+
// do not ask to play again if game was never actually played
if (!game.GamePlayed)
return FALSE;
if (!setup.ask_on_game_over)
return FALSE;
+ game.RestartGameRequested = TRUE;
+
RequestRestartGame();
return TRUE;
GAME_CTRL_ID_LOAD, NULL,
TRUE, FALSE, "load game"
},
+ {
+ IMG_GFX_GAME_BUTTON_RESTART, &game.button.restart,
+ GAME_CTRL_ID_RESTART, NULL,
+ TRUE, FALSE, "restart game"
+ },
{
IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
GAME_CTRL_ID_PANEL_STOP, NULL,
GAME_CTRL_ID_PANEL_PLAY, NULL,
FALSE, FALSE, "play game"
},
+ {
+ IMG_GFX_GAME_BUTTON_PANEL_RESTART, &game.button.panel_restart,
+ GAME_CTRL_ID_PANEL_RESTART, NULL,
+ FALSE, FALSE, "restart game"
+ },
{
IMG_GFX_GAME_BUTTON_TOUCH_STOP, &game.button.touch_stop,
GAME_CTRL_ID_TOUCH_STOP, NULL,
GAME_CTRL_ID_TOUCH_PAUSE, NULL,
FALSE, TRUE, "pause game"
},
+ {
+ IMG_GFX_GAME_BUTTON_TOUCH_RESTART, &game.button.touch_restart,
+ GAME_CTRL_ID_TOUCH_RESTART, NULL,
+ FALSE, TRUE, "restart game"
+ },
{
IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
SOUND_CTRL_ID_MUSIC, &setup.sound_music,
id == GAME_CTRL_ID_PLAY ||
id == GAME_CTRL_ID_PANEL_PLAY ||
id == GAME_CTRL_ID_SAVE ||
- id == GAME_CTRL_ID_LOAD)
+ id == GAME_CTRL_ID_LOAD ||
+ id == GAME_CTRL_ID_RESTART ||
+ id == GAME_CTRL_ID_PANEL_RESTART ||
+ id == GAME_CTRL_ID_TOUCH_RESTART)
{
button_type = GD_TYPE_NORMAL_BUTTON;
checked = FALSE;
};
int i;
+ // do not redraw pause button on closed door (may happen when restarting game)
+ if (!(GetDoorState() & DOOR_OPEN_1))
+ return;
+
for (i = 0; ids[i] > -1; i++)
ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
}
TapeQuickLoad();
break;
+ case GAME_CTRL_ID_RESTART:
+ case GAME_CTRL_ID_PANEL_RESTART:
+ case GAME_CTRL_ID_TOUCH_RESTART:
+ TapeRestartGame();
+
+ break;
+
case SOUND_CTRL_ID_MUSIC:
case SOUND_CTRL_ID_PANEL_MUSIC:
if (setup.sound_music)