#define GAME_CTRL_ID_PANEL_STOP 8
#define GAME_CTRL_ID_PANEL_PAUSE 9
#define GAME_CTRL_ID_PANEL_PLAY 10
-#define SOUND_CTRL_ID_MUSIC 11
-#define SOUND_CTRL_ID_LOOPS 12
-#define SOUND_CTRL_ID_SIMPLE 13
-#define SOUND_CTRL_ID_PANEL_MUSIC 14
-#define SOUND_CTRL_ID_PANEL_LOOPS 15
-#define SOUND_CTRL_ID_PANEL_SIMPLE 16
+#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 17
+#define NUM_GAME_BUTTONS 19
// forward declaration for internal use
player->jy = player->last_jy = y;
}
- if (!init_game)
+ // always check if player was just killed and should be reanimated
{
int player_nr = GET_PLAYER_NR(element);
struct PlayerInfo *player = &stored_player[player_nr];
case EL_MOLE_RIGHT:
case EL_MOLE_UP:
case EL_MOLE_DOWN:
+ case EL_SPRING_LEFT:
+ case EL_SPRING_RIGHT:
InitMovDir(x, y);
break;
break;
case EL_EMC_MAGIC_BALL:
- if (game.ball_state)
+ if (game.ball_active)
Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
break;
case EL_EMC_MAGIC_BALL_SWITCH:
- if (game.ball_state)
+ if (game.ball_active)
Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
break;
int getPlayerInventorySize(int player_nr)
{
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
- return level.native_em_level->ply[player_nr]->dynamite;
+ return game_em.ply[player_nr]->dynamite;
else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
- return level.native_sp_level->game_sp->red_disk_count;
+ return game_sp.red_disk_count;
else
return stored_player[player_nr].inventory_size;
}
int time = (game.LevelSolved ?
game.LevelSolved_CountingTime :
level.game_engine_type == GAME_ENGINE_TYPE_EM ?
- level.native_em_level->lev->time :
+ game_em.lev->time :
level.game_engine_type == GAME_ENGINE_TYPE_SP ?
- level.native_sp_level->game_sp->time_played :
+ game_sp.time_played :
level.game_engine_type == GAME_ENGINE_TYPE_MM ?
game_mm.energy_left :
game.no_time_limit ? TimePlayed : TimeLeft);
int score = (game.LevelSolved ?
game.LevelSolved_CountingScore :
level.game_engine_type == GAME_ENGINE_TYPE_EM ?
- level.native_em_level->lev->score :
+ game_em.lev->score :
level.game_engine_type == GAME_ENGINE_TYPE_SP ?
- level.native_sp_level->game_sp->score :
+ game_sp.score :
level.game_engine_type == GAME_ENGINE_TYPE_MM ?
game_mm.score :
game.score);
int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
- level.native_em_level->lev->required :
+ game_em.lev->gems_needed :
level.game_engine_type == GAME_ENGINE_TYPE_SP ?
- level.native_sp_level->game_sp->infotrons_still_needed :
+ game_sp.infotrons_still_needed :
level.game_engine_type == GAME_ENGINE_TYPE_MM ?
game_mm.kettles_still_needed :
game.gems_still_needed);
int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
- level.native_em_level->lev->required > 0 :
+ game_em.lev->gems_needed > 0 :
level.game_engine_type == GAME_ENGINE_TYPE_SP ?
- level.native_sp_level->game_sp->infotrons_still_needed > 0 :
+ game_sp.infotrons_still_needed > 0 :
level.game_engine_type == GAME_ENGINE_TYPE_MM ?
game_mm.kettles_still_needed > 0 ||
game_mm.lights_still_needed > 0 :
{
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
- if (level.native_em_level->ply[i]->keys & (1 << k))
+ if (game_em.ply[i]->keys & (1 << k))
game_panel_controls[GAME_PANEL_KEY_1 + k].value =
get_key_element_from_nr(k);
}
{
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
- if (level.native_em_level->ply[player_nr]->keys & (1 << k))
+ if (game_em.ply[player_nr]->keys & (1 << k))
game_panel_controls[GAME_PANEL_KEY_1 + k].value =
get_key_element_from_nr(k);
}
(exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
- (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
+ (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
- (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
+ (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
EL_EMC_MAGIC_BALL_SWITCH);
game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
game.team_mode = (num_players > 1);
}
+#if 0
+ printf("level %d: level.game_version == %06d\n", level_nr,
+ level.game_version);
+ printf(" tape.file_version == %06d\n",
+ tape.file_version);
+ printf(" tape.game_version == %06d\n",
+ tape.game_version);
+ printf(" tape.engine_version == %06d\n",
+ tape.engine_version);
+ printf(" => game.engine_version == %06d [tape mode: %s]\n",
+ game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
+#endif
+
// --------------------------------------------------------------------------
// set flags for bugs and changes according to active game engine version
// --------------------------------------------------------------------------
+ /*
+ Summary of bugfix:
+ Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
+
+ Bug was introduced in version:
+ 2.0.1
+
+ Bug was fixed in version:
+ 4.1.4.2
+
+ Description:
+ In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
+ but the property "can fall" was missing, which caused some levels to be
+ unsolvable. This was fixed in version 4.1.4.2.
+
+ Affected levels/tapes:
+ An example for a tape that was fixed by this bugfix is tape 029 from the
+ level set "rnd_sam_bateman".
+ The wrong behaviour will still be used for all levels or tapes that were
+ created/recorded with it. An example for this is tape 023 from the level
+ set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
+ */
+
+ boolean use_amoeba_dropping_cannot_fall_bug =
+ ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
+ game.engine_version <= VERSION_IDENT(4,1,4,1)) ||
+ (tape.playing &&
+ tape.game_version >= VERSION_IDENT(2,0,1,0) &&
+ tape.game_version <= VERSION_IDENT(4,1,4,1)));
+
+ /*
+ Summary of bugfix/change:
+ Fixed move speed of elements entering or leaving magic wall.
+
+ Fixed/changed in version:
+ 2.0.1
+
+ Description:
+ Before 2.0.1, move speed of elements entering or leaving magic wall was
+ twice as fast as it is now.
+ Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
+
+ Affected levels/tapes:
+ The first condition is generally needed for all levels/tapes before version
+ 2.0.1, which might use the old behaviour before it was changed; known tapes
+ that are affected: Tape 014 from the level set "rnd_conor_mancone".
+ The second condition is an exception from the above case and is needed for
+ the special case of tapes recorded with game (not engine!) version 2.0.1 or
+ above, but before it was known that this change would break tapes like the
+ above and was fixed in 4.1.4.2, so that the changed behaviour was active
+ although the engine version while recording maybe was before 2.0.1. There
+ are a lot of tapes that are affected by this exception, like tape 006 from
+ the level set "rnd_conor_mancone".
+ */
+
+ boolean use_old_move_stepsize_for_magic_wall =
+ (game.engine_version < VERSION_IDENT(2,0,1,0) &&
+ !(tape.playing &&
+ tape.game_version >= VERSION_IDENT(2,0,1,0) &&
+ tape.game_version < VERSION_IDENT(4,1,4,2)));
+
/*
Summary of bugfix/change:
Fixed handling for custom elements that change when pushed by the player.
game.use_block_last_field_bug =
(game.engine_version < VERSION_IDENT(3,1,1,0));
+ /* various special flags and settings for native Emerald Mine game engine */
+
game_em.use_single_button =
(game.engine_version > VERSION_IDENT(4,0,0,2));
game_em.use_snap_key_bug =
(game.engine_version < VERSION_IDENT(4,0,1,0));
+ game_em.use_old_explosions =
+ (game.engine_version < VERSION_IDENT(4,1,4,2));
+
+ game_em.use_old_android =
+ (game.engine_version < VERSION_IDENT(4,1,4,2));
+
+ game_em.use_wrap_around =
+ (game.engine_version > VERSION_IDENT(4,1,4,1));
+
// --------------------------------------------------------------------------
// set maximal allowed number of custom element changes per game frame
// dynamically adjust element properties according to game engine version
InitElementPropertiesEngine(game.engine_version);
-#if 0
- printf("level %d: level version == %06d\n", level_nr, level.game_version);
- printf(" tape version == %06d [%s] [file: %06d]\n",
- tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
- tape.file_version);
- printf(" => game.engine_version == %06d\n", game.engine_version);
-#endif
+ // ---------- initialize special element properties -------------------------
+
+ // "EL_AMOEBA_DROPPING" missed property "can fall" between 2.0.1 and 4.1.4.1
+ if (use_amoeba_dropping_cannot_fall_bug)
+ SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
// ---------- initialize player's initial move delay ------------------------
int e = move_stepsize_list[i].element;
element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
+
+ // set move stepsize value for certain elements for older engine versions
+ if (use_old_move_stepsize_for_magic_wall)
+ {
+ if (e == EL_MAGIC_WALL_FILLING ||
+ e == EL_MAGIC_WALL_EMPTYING ||
+ e == EL_BD_MAGIC_WALL_FILLING ||
+ e == EL_BD_MAGIC_WALL_EMPTYING)
+ element_info[e].move_stepsize *= 2;
+ }
}
// ---------- initialize collect score --------------------------------------
// ---------- initialize graphics engine ------------------------------------
game.scroll_delay_value =
(game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
+ 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);
// Supaplex levels with time limit currently unsupported -- should be added
if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
level.time = 0;
+
+ // ---------- initialize flags for handling game actions --------------------
+
+ // set flags for game actions to default values
+ game.use_key_actions = TRUE;
+ game.use_mouse_actions = FALSE;
+
+ // when using Mirror Magic game engine, handle mouse events only
+ if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+ {
+ game.use_key_actions = FALSE;
+ game.use_mouse_actions = TRUE;
+ }
+
+ // check for custom elements with mouse click events
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ {
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ 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))
+ game.use_mouse_actions = TRUE;
+ }
+ }
}
static int get_num_special_action(int element, int action_first,
SetGameStatus(GAME_MODE_PLAYING);
if (level_editor_test_game)
- FadeSkipNextFadeIn();
+ FadeSkipNextFadeOut();
else
FadeSetEnterScreen();
ExpireSoundLoops(TRUE);
- if (!level_editor_test_game)
- FadeOut(fade_mask);
+ FadeOut(fade_mask);
+
+ if (level_editor_test_game)
+ FadeSkipNextFadeIn();
// needed if different viewport properties defined for playing
ChangeViewportPropertiesIfNeeded();
InitGameEngine();
InitGameControlValues();
+ // initialize tape actions from game when recording tape
+ if (tape.recording)
+ {
+ tape.use_key_actions = game.use_key_actions;
+ tape.use_mouse_actions = game.use_mouse_actions;
+ }
+
// don't play tapes over network
network_playing = (network.enabled && !tape.playing);
game.lenses_time_left = 0;
game.magnify_time_left = 0;
- game.ball_state = level.ball_state_initial;
+ game.ball_active = level.ball_active_initial;
game.ball_content_nr = 0;
game.explosions_delayed = TRUE;
// use preferred player also in local single-player mode
if (!network.enabled && !game.team_mode)
{
- int old_index_nr = local_player->index_nr;
int new_index_nr = setup.network_player_nr;
if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
{
- stored_player[old_index_nr].connected_locally = FALSE;
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].connected_locally = FALSE;
+
stored_player[new_index_nr].connected_locally = TRUE;
}
}
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;
+ game.set_centered_player_wrap = FALSE;
if (network_playing && tape.recording)
{
{
// this is used for non-R'n'D game engines to update certain engine values
- if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
- {
- actual_player_x = correctLevelPosX_EM(actual_player_x);
- actual_player_y = correctLevelPosY_EM(actual_player_y);
- }
-
// needed to determine if sounds are played within the visible screen area
scroll_x = actual_scroll_x;
scroll_y = actual_scroll_y;
MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
break;
+ case EL_SPRING_LEFT:
+ case EL_SPRING_RIGHT:
+ Feld[x][y] = EL_SPRING;
+ MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
+ break;
+
default:
if (IS_CUSTOM_ELEMENT(element))
{
game.GameOver = TRUE;
game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
- level.native_em_level->lev->score :
+ game_em.lev->score :
level.game_engine_type == GAME_ENGINE_TYPE_MM ?
game_mm.score :
game.score);
while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
{
- int dx = 0, dy = 0;
- int fx = FX, fy = FY;
-
- dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
- dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
+ int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
+ int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > 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;
+ // set values for horizontal/vertical screen scrolling (half tile size)
+ int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
+ int dir_y = (dy != 0 ? MV_VERTICAL : 0);
+ int pos_x = dx * TILEX / 2;
+ int pos_y = dy * TILEY / 2;
+ int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
+ int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
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);
+ BlitScreenToBitmapExt_RND(window, fx, fy);
// scroll second step to align at full tile size
BlitScreenToBitmap(window);
// ---------- player actions ---------------------------------------------
case CA_MOVE_PLAYER:
+ case CA_MOVE_PLAYER_NEW:
{
// 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;
+ if (action_type == CA_MOVE_PLAYER ||
+ stored_player[i].MovPos == 0)
+ stored_player[i].programmed_action = action_arg_direction;
break;
}
if (CustomValue[x][y] == 0)
{
+ // reset change counter (else CE_VALUE_GETS_ZERO would not work)
+ ChangeCount[x][y] = 0; // allow at least one more change
+
CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
}
{
int xx, yy;
+ // reset change counter (else CE_SCORE_GETS_ZERO would not work)
+ ChangeCount[x][y] = 0; // allow at least one more change
+
CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
byte *tape_action)
{
- if (!tape.use_mouse)
+ if (!tape.use_mouse_actions)
return;
mouse_action->lx = tape_action[TAPE_ACTION_LX];
static void SetTapeActionFromMouseAction(byte *tape_action,
struct MouseActionInfo *mouse_action)
{
- if (!tape.use_mouse)
+ if (!tape.use_mouse_actions)
return;
tape_action[TAPE_ACTION_LX] = mouse_action->lx;
if (!TimeLeft && setup.time_limit)
{
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
- level.native_em_level->lev->killed_out_of_time = TRUE;
+ game_em.lev->killed_out_of_time = TRUE;
else
for (i = 0; i < MAX_PLAYERS; i++)
KillPlayer(&stored_player[i]);
game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
}
- level.native_em_level->lev->time =
- (game.no_time_limit ? TimePlayed : TimeLeft);
+ game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
}
if (tape.recording || tape.playing)
unsigned int game_frame_delay_value;
byte *recorded_player_action;
byte summarized_player_action = 0;
- byte tape_action[MAX_PLAYERS];
+ byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
int i;
// detect endless loops, caused by custom element programming
stored_player[map_player_action[local_player->index_nr]].effective_action =
summarized_player_action;
+ // summarize all actions at centered player in local team mode
if (tape.recording &&
- setup.team_mode &&
+ setup.team_mode && !network.enabled &&
setup.input_on_focus &&
game.centered_player_nr != -1)
{
for (i = 0; i < MAX_PLAYERS; i++)
- stored_player[i].effective_action =
+ stored_player[map_player_action[i]].effective_action =
(i == game.centered_player_nr ? summarized_player_action : 0);
}
void GameActions_RND(void)
{
+ static struct MouseActionInfo mouse_action_last = { 0 };
+ struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
int magic_wall_x = 0, magic_wall_y = 0;
int i, x, y, element, graphic, last_gfx_frame;
#endif
}
+ if (mouse_action.button)
+ {
+ int new_button = (mouse_action.button && mouse_action_last.button == 0);
+
+ x = mouse_action.lx;
+ y = mouse_action.ly;
+ element = Feld[x][y];
+
+ if (new_button)
+ {
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
+ CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
+ }
+
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
+ CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
+ }
+
SCAN_PLAYFIELD(x, y)
{
element = Feld[x][y];
element == EL_DC_MAGIC_WALL_FULL ||
element == EL_DC_MAGIC_WALL_ACTIVE ||
element == EL_DC_MAGIC_WALL_EMPTYING) &&
- ABS(x - jx) + ABS(y - jy) < ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
+ ABS(x - jx) + ABS(y - jy) <
+ ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
{
magic_wall_x = x;
magic_wall_y = y;
DrawAllPlayers();
PlayAllPlayersSound();
- if (local_player->show_envelope != 0 && (!local_player->active ||
- local_player->MovPos == 0))
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
+ struct PlayerInfo *player = &stored_player[i];
- local_player->show_envelope = 0;
+ if (player->show_envelope != 0 && (!player->active ||
+ player->MovPos == 0))
+ {
+ ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
+
+ player->show_envelope = 0;
+ }
}
// use random number generator in every frame to make it less predictable
if (game.engine_version >= VERSION_IDENT(3,1,1,0))
RND(1);
+
+ mouse_action_last = mouse_action;
}
static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
game.centered_player_nr == -1))
{
int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
- int offset = game.scroll_delay_value;
if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
{
}
else
{
+ int offset_raw = game.scroll_delay_value;
+
if (jx != old_jx) // player has moved horizontally
{
- if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
- (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
- scroll_x = jx - MIDPOSX + (scroll_x < jx - MIDPOSX ? -offset : +offset);
+ int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
+ int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
+ int new_scroll_x = jx - MIDPOSX + offset_x;
+
+ if ((player->MovDir == MV_LEFT && scroll_x > new_scroll_x) ||
+ (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
+ scroll_x = new_scroll_x;
// 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);
+ scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
// don't scroll more than one field at a time
scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
}
else // player has moved vertically
{
- if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
- (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
- scroll_y = jy - MIDPOSY + (scroll_y < jy - MIDPOSY ? -offset : +offset);
+ int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
+ int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
+ int new_scroll_y = jy - MIDPOSY + offset_y;
+
+ if ((player->MovDir == MV_UP && scroll_y > new_scroll_y) ||
+ (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
+ scroll_y = new_scroll_y;
// 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);
+ scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
// don't scroll more than one field at a time
scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
{
int xx, yy;
- game.ball_state = !game.ball_state;
+ game.ball_active = !game.ball_active;
SCAN_PLAYFIELD(xx, yy)
{
int e = Feld[xx][yy];
- if (game.ball_state)
+ if (game.ball_active)
{
if (e == EL_EMC_MAGIC_BALL)
CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
{
- int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
- int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
- int x = xx - 1 - offset;
- int y = yy - 1 - offset;
+ int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
+ int offset = 0;
+ int x = xx - offset;
+ int y = yy - offset;
switch (sample)
{
- case SAMPLE_blank:
+ case SOUND_blank:
PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
break;
- case SAMPLE_roll:
+ case SOUND_roll:
PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
break;
- case SAMPLE_stone:
+ case SOUND_stone:
PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
break;
- case SAMPLE_nut:
+ case SOUND_nut:
PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
break;
- case SAMPLE_crack:
+ case SOUND_crack:
PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
break;
- case SAMPLE_bug:
+ case SOUND_bug:
PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
break;
- case SAMPLE_tank:
+ case SOUND_tank:
PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
break;
- case SAMPLE_android_clone:
+ case SOUND_android_clone:
PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
break;
- case SAMPLE_android_move:
+ case SOUND_android_move:
PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
break;
- case SAMPLE_spring:
+ case SOUND_spring:
PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
break;
- case SAMPLE_slurp:
+ case SOUND_slurp:
PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
break;
- case SAMPLE_eater:
+ case SOUND_eater:
PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
break;
- case SAMPLE_eater_eat:
+ case SOUND_eater_eat:
PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
break;
- case SAMPLE_alien:
+ case SOUND_alien:
PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
break;
- case SAMPLE_collect:
+ case SOUND_collect:
PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
break;
- case SAMPLE_diamond:
+ case SOUND_diamond:
PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
break;
- case SAMPLE_squash:
+ case SOUND_squash:
// !!! CHECK THIS !!!
#if 1
PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
#endif
break;
- case SAMPLE_wonderfall:
+ case SOUND_wonderfall:
PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
break;
- case SAMPLE_drip:
+ case SOUND_drip:
PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
break;
- case SAMPLE_push:
+ case SOUND_push:
PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
break;
- case SAMPLE_dirt:
+ case SOUND_dirt:
PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
break;
- case SAMPLE_acid:
+ case SOUND_acid:
PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
break;
- case SAMPLE_ball:
+ case SOUND_ball:
PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
break;
- case SAMPLE_grow:
+ case SOUND_slide:
PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
break;
- case SAMPLE_wonder:
+ case SOUND_wonder:
PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
break;
- case SAMPLE_door:
+ case SOUND_door:
PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
break;
- case SAMPLE_exit_open:
+ case SOUND_exit_open:
PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
break;
- case SAMPLE_exit_leave:
+ case SOUND_exit_leave:
PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
break;
- case SAMPLE_dynamite:
+ case SOUND_dynamite:
PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
break;
- case SAMPLE_tick:
+ case SOUND_tick:
PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
break;
- case SAMPLE_press:
+ case SOUND_press:
PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
break;
- case SAMPLE_wheel:
+ case SOUND_wheel:
PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
break;
- case SAMPLE_boom:
+ case SOUND_boom:
PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
break;
- case SAMPLE_die:
+ case SOUND_die:
PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
break;
- case SAMPLE_time:
+ case SOUND_time:
PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
break;
int gadget_id;
boolean *setup_value;
boolean allowed_on_tape;
+ boolean is_touch_button;
char *infotext;
} gamebutton_info[NUM_GAME_BUTTONS] =
{
{
IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
GAME_CTRL_ID_STOP, NULL,
- TRUE, "stop game"
+ TRUE, FALSE, "stop game"
},
{
IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
GAME_CTRL_ID_PAUSE, NULL,
- TRUE, "pause game"
+ TRUE, FALSE, "pause game"
},
{
IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
GAME_CTRL_ID_PLAY, NULL,
- TRUE, "play game"
+ TRUE, FALSE, "play game"
},
{
IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
GAME_CTRL_ID_UNDO, NULL,
- TRUE, "undo step"
+ TRUE, FALSE, "undo step"
},
{
IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
GAME_CTRL_ID_REDO, NULL,
- TRUE, "redo step"
+ TRUE, FALSE, "redo step"
},
{
IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
GAME_CTRL_ID_SAVE, NULL,
- TRUE, "save game"
+ TRUE, FALSE, "save game"
},
{
IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
GAME_CTRL_ID_PAUSE2, NULL,
- TRUE, "pause game"
+ TRUE, FALSE, "pause game"
},
{
IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
GAME_CTRL_ID_LOAD, NULL,
- TRUE, "load game"
+ TRUE, FALSE, "load game"
},
{
IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
GAME_CTRL_ID_PANEL_STOP, NULL,
- FALSE, "stop game"
+ FALSE, FALSE, "stop game"
},
{
IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
GAME_CTRL_ID_PANEL_PAUSE, NULL,
- FALSE, "pause game"
+ FALSE, FALSE, "pause game"
},
{
IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
GAME_CTRL_ID_PANEL_PLAY, NULL,
- FALSE, "play game"
+ FALSE, FALSE, "play game"
+ },
+ {
+ IMG_GFX_GAME_BUTTON_TOUCH_STOP, &game.button.touch_stop,
+ GAME_CTRL_ID_TOUCH_STOP, NULL,
+ FALSE, TRUE, "stop game"
+ },
+ {
+ IMG_GFX_GAME_BUTTON_TOUCH_PAUSE, &game.button.touch_pause,
+ GAME_CTRL_ID_TOUCH_PAUSE, NULL,
+ FALSE, TRUE, "pause game"
},
{
IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
SOUND_CTRL_ID_MUSIC, &setup.sound_music,
- TRUE, "background music on/off"
+ TRUE, FALSE, "background music on/off"
},
{
IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
- TRUE, "sound loops on/off"
+ TRUE, FALSE, "sound loops on/off"
},
{
IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
- TRUE, "normal sounds on/off"
+ TRUE, FALSE, "normal sounds on/off"
},
{
IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
- FALSE, "background music on/off"
+ FALSE, FALSE, "background music on/off"
},
{
IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
- FALSE, "sound loops on/off"
+ FALSE, FALSE, "sound loops on/off"
},
{
IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
- FALSE, "normal sounds on/off"
+ FALSE, FALSE, "normal sounds on/off"
}
};
int button_type;
boolean checked;
unsigned int event_mask;
+ boolean is_touch_button = gamebutton_info[i].is_touch_button;
boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
- int base_x = (on_tape ? VX : DX);
- int base_y = (on_tape ? VY : DY);
+ int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
+ int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
int gd_x = gfx->src_x;
int gd_y = gfx->src_y;
int gd_xp = gfx->src_x + gfx->pressed_xoffset;
int gd_ya = gfx->src_y + gfx->active_yoffset;
int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
+ int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
+ int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
int id = i;
if (gfx->bitmap == NULL)
if (id == GAME_CTRL_ID_STOP ||
id == GAME_CTRL_ID_PANEL_STOP ||
+ id == GAME_CTRL_ID_TOUCH_STOP ||
id == GAME_CTRL_ID_PLAY ||
id == GAME_CTRL_ID_PANEL_PLAY ||
id == GAME_CTRL_ID_SAVE ||
gi = CreateGadget(GDI_CUSTOM_ID, id,
GDI_IMAGE_ID, graphic,
GDI_INFO_TEXT, gamebutton_info[i].infotext,
- GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
- GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
+ GDI_X, base_x + x,
+ GDI_Y, base_y + y,
GDI_WIDTH, gfx->width,
GDI_HEIGHT, gfx->height,
GDI_TYPE, button_type,
GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
GDI_DIRECT_DRAW, FALSE,
+ GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
GDI_EVENT_MASK, event_mask,
GDI_CALLBACK_ACTION, HandleGameButtons,
GDI_END);
MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
-
- ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
}
void UnmapUndoRedoButtons(void)
MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
+}
- ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
+void ModifyPauseButtons(void)
+{
+ static int ids[] =
+ {
+ GAME_CTRL_ID_PAUSE,
+ GAME_CTRL_ID_PAUSE2,
+ GAME_CTRL_ID_PANEL_PAUSE,
+ GAME_CTRL_ID_TOUCH_PAUSE,
+ -1
+ };
+ int i;
+
+ for (i = 0; ids[i] > -1; i++)
+ ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
}
static void MapGameButtonsExt(boolean on_tape)
for (i = 0; i < NUM_GAME_BUTTONS; i++)
if (!on_tape || gamebutton_info[i].allowed_on_tape)
RedrawGadget(game_gadget[i]);
-
- // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
- redraw_mask &= ~REDRAW_ALL;
}
static void SetGadgetState(struct GadgetInfo *gi, boolean state)
{
case GAME_CTRL_ID_STOP:
case GAME_CTRL_ID_PANEL_STOP:
+ case GAME_CTRL_ID_TOUCH_STOP:
if (game_status == GAME_MODE_MAIN)
break;
case GAME_CTRL_ID_PAUSE:
case GAME_CTRL_ID_PAUSE2:
case GAME_CTRL_ID_PANEL_PAUSE:
+ case GAME_CTRL_ID_TOUCH_PAUSE:
if (network.enabled && game_status == GAME_MODE_PLAYING)
{
if (tape.pausing)