static void MapGameButtons();
static void HandleGameButtons(struct GadgetInfo *);
+int AmoebeNachbarNr(int, int);
+void AmoebeUmwandeln(int, int);
+void ContinueMoving(int, int);
+void Bang(int, int);
+void InitMovDir(int, int);
+void InitAmoebaNr(int, int);
+int NewHiScore(void);
+
+void TestIfGoodThingHitsBadThing(int, int, int);
+void TestIfBadThingHitsGoodThing(int, int, int);
+void TestIfPlayerTouchesBadThing(int, int);
+void TestIfPlayerRunsIntoBadThing(int, int, int);
+void TestIfBadThingTouchesPlayer(int, int);
+void TestIfBadThingRunsIntoPlayer(int, int, int);
+void TestIfFriendTouchesBadThing(int, int);
+void TestIfBadThingTouchesFriend(int, int);
+void TestIfBadThingTouchesOtherBadThing(int, int);
+
+void KillPlayer(struct PlayerInfo *);
+void BuryPlayer(struct PlayerInfo *);
+void RemovePlayer(struct PlayerInfo *);
+
+boolean SnapField(struct PlayerInfo *, int, int);
+boolean DropElement(struct PlayerInfo *);
+
+
static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
(x) >= 0 && (x) <= lev_fieldx - 1; \
(x) += playfield_scan_delta_x) \
+#ifdef DEBUG
+void DEBUG_SetMaximumDynamite()
+{
+ int i;
+
+ for (i = 0; i < MAX_INVENTORY_SIZE; i++)
+ if (local_player->inventory_size < MAX_INVENTORY_SIZE)
+ local_player->inventory_element[local_player->inventory_size++] =
+ EL_DYNAMITE;
+}
+#endif
+
static void InitPlayfieldScanModeVars()
{
if (game.use_reverse_scan_direction)
Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
break;
+ case EL_BUG:
case EL_BUG_RIGHT:
case EL_BUG_UP:
case EL_BUG_LEFT:
case EL_BUG_DOWN:
- case EL_BUG:
+ case EL_SPACESHIP:
case EL_SPACESHIP_RIGHT:
case EL_SPACESHIP_UP:
case EL_SPACESHIP_LEFT:
case EL_SPACESHIP_DOWN:
- case EL_SPACESHIP:
+ case EL_BD_BUTTERFLY:
case EL_BD_BUTTERFLY_RIGHT:
case EL_BD_BUTTERFLY_UP:
case EL_BD_BUTTERFLY_LEFT:
case EL_BD_BUTTERFLY_DOWN:
- case EL_BD_BUTTERFLY:
+ case EL_BD_FIREFLY:
case EL_BD_FIREFLY_RIGHT:
case EL_BD_FIREFLY_UP:
case EL_BD_FIREFLY_LEFT:
case EL_BD_FIREFLY_DOWN:
- case EL_BD_FIREFLY:
case EL_PACMAN_RIGHT:
case EL_PACMAN_UP:
case EL_PACMAN_LEFT:
case EL_PACMAN_DOWN:
case EL_YAMYAM:
+ case EL_YAMYAM_LEFT:
+ case EL_YAMYAM_RIGHT:
+ case EL_YAMYAM_UP:
+ case EL_YAMYAM_DOWN:
case EL_DARK_YAMYAM:
case EL_ROBOT:
case EL_PACMAN:
case EL_SP_SNIKSNAK:
case EL_SP_ELECTRON:
+ case EL_MOLE:
case EL_MOLE_LEFT:
case EL_MOLE_RIGHT:
case EL_MOLE_UP:
case EL_MOLE_DOWN:
- case EL_MOLE:
InitMovDir(x, y);
break;
break;
}
+#if 0
+ printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
+#endif
+
return num_special_action;
}
player->is_pushing = FALSE;
player->is_switching = FALSE;
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
player->is_bored = FALSE;
player->is_sleeping = FALSE;
player->anim_delay_counter = 0;
player->post_delay_counter = 0;
+ player->dir_waiting = MV_NONE;
player->action_waiting = ACTION_DEFAULT;
player->last_action_waiting = ACTION_DEFAULT;
player->special_action_bored = ACTION_DEFAULT;
player->special_action_sleeping = ACTION_DEFAULT;
+#if 1
+ /* cannot be set here -- could be modified in Init[Player]Field() below */
+#else
/* set number of special actions for bored and sleeping animation */
player->num_special_action_bored =
get_num_special_action(player->artwork_element,
player->num_special_action_sleeping =
get_num_special_action(player->artwork_element,
ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+#endif
player->switch_x = -1;
player->switch_y = -1;
player->push_delay_value = game.initial_push_delay_value;
player->drop_delay = 0;
+ player->drop_pressed_delay = 0;
player->last_jx = player->last_jy = 0;
player->jx = player->jy = 0;
game.envelope_active = FALSE;
game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
+ game.set_centered_player = FALSE;
for (i = 0; i < NUM_BELTS; i++)
{
InitBeltMovement();
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+#if 1
+ /* set number of special actions for bored and sleeping animation */
+ player->num_special_action_bored =
+ get_num_special_action(player->artwork_element,
+ ACTION_BORING_1, ACTION_BORING_LAST);
+ player->num_special_action_sleeping =
+ get_num_special_action(player->artwork_element,
+ ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+#endif
+
+ }
+
game.emulation = (emulate_bd ? EMU_BOULDERDASH :
emulate_sb ? EMU_SOKOBAN :
emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
break;
+ case EL_YAMYAM_LEFT:
+ case EL_YAMYAM_RIGHT:
+ case EL_YAMYAM_UP:
+ case EL_YAMYAM_DOWN:
+ Feld[x][y] = EL_YAMYAM;
+ MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
+ break;
+
case EL_SP_SNIKSNAK:
MovDir[x][y] = MV_UP;
break;
Bang(x, y);
}
+#if 1
+
+static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
+{
+ boolean num_checked_players = 0;
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active)
+ {
+ int sx = stored_player[i].jx;
+ int sy = stored_player[i].jy;
+
+ if (num_checked_players == 0)
+ {
+ *sx1 = *sx2 = sx;
+ *sy1 = *sy2 = sy;
+ }
+ else
+ {
+ *sx1 = MIN(*sx1, sx);
+ *sy1 = MIN(*sy1, sy);
+ *sx2 = MAX(*sx2, sx);
+ *sy2 = MAX(*sy2, sy);
+ }
+
+ num_checked_players++;
+ }
+ }
+}
+
+static boolean checkIfAllPlayersFitToScreen_RND()
+{
+ int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ return (sx2 - sx1 < SCR_FIELDX &&
+ sy2 - sy1 < SCR_FIELDY);
+}
+
+static void setScreenCenteredToAllPlayers(int *sx, int *sy)
+{
+ int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ *sx = (sx1 + sx2) / 2;
+ *sy = (sy1 + sy2) / 2;
+}
+
+#if 0
+static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
+ int center_x, int center_y)
+{
+ int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
+ *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
+}
+
+static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
+{
+ int max_dx, max_dy;
+
+ setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
+
+ return (max_dx <= SCR_FIELDX / 2 &&
+ max_dy <= SCR_FIELDY / 2);
+}
+#endif
+
+#endif
+
+#if 1
+
+void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
+ boolean quick_relocation)
+{
+ boolean ffwd_delay = (tape.playing && tape.fast_forward);
+ boolean no_delay = (tape.warp_forward);
+ int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+ int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+
+ if (quick_relocation)
+ {
+ int offset = (setup.scroll_delay ? 3 : 0);
+
+#if 0
+ if (center_screen)
+ offset = 0;
+#endif
+
+ if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
+ {
+ scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
+ x > SBX_Right + MIDPOSX ? SBX_Right :
+ x - MIDPOSX);
+
+ scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ y - MIDPOSY);
+ }
+ else
+ {
+ if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
+ (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
+ scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
+
+ if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
+ (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
+ scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
+
+ /* 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);
+
+ /* 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);
+ }
+
+ RedrawPlayfield(TRUE, 0,0,0,0);
+ }
+ else
+ {
+ int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
+ x > SBX_Right + MIDPOSX ? SBX_Right :
+ x - MIDPOSX);
+
+ int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ y - MIDPOSY);
+
+ ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
+
+ while (scroll_x != scroll_xx || scroll_y != scroll_yy)
+ {
+ int dx = 0, dy = 0;
+ int fx = FX, fy = FY;
+
+ dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
+ dy = (scroll_yy < scroll_y ? +1 : scroll_yy > 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;
+
+ 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);
+ FlushDisplay();
+ Delay(wait_delay_value);
+
+ /* scroll second step to align at full tile size */
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+
+ DrawAllPlayers();
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+}
+
+#else
+
void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
{
boolean ffwd_delay = (tape.playing && tape.fast_forward);
}
}
+#endif
+
void RelocatePlayer(int jx, int jy, int el_player_raw)
{
int el_player = GET_PLAYER_ELEMENT(el_player_raw);
#if 1
/* only visually relocate centered player */
+#if 1
+ DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
+ level.instant_relocation);
+#else
if (player->index_nr == game.centered_player_nr)
DrawRelocatePlayer(player, level.instant_relocation);
+#endif
#else
if (player == local_player) /* only visually relocate local player */
DrawRelocatePlayer(player, level.instant_relocation);
boolean last_waiting = player->is_waiting;
int move_dir = player->MovDir;
+ player->dir_waiting = move_dir;
player->last_action_waiting = player->action_waiting;
if (is_waiting)
game.player_sleeping_delay_fixed +
SimpleRND(game.player_sleeping_delay_random);
+#if 1
+ InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
+#else
InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
+#endif
}
if (game.player_sleeping_delay_fixed +
player->is_bored ? ACTION_BORING :
ACTION_WAITING);
+#if 1
+ if (player->is_sleeping && player->use_murphy)
+ {
+ /* special case for sleeping Murphy when leaning against non-free tile */
+
+ if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
+ (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
+ !IS_MOVING(player->jx - 1, player->jy)))
+ move_dir = MV_LEFT;
+ else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
+ (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
+ !IS_MOVING(player->jx + 1, player->jy)))
+ move_dir = MV_RIGHT;
+ else
+ player->is_sleeping = FALSE;
+
+ player->dir_waiting = move_dir;
+ }
+#endif
+
if (player->is_sleeping)
{
if (player->num_special_action_sleeping > 0)
player->anim_delay_counter = 0;
player->post_delay_counter = 0;
+ player->dir_waiting = player->MovDir;
player->action_waiting = ACTION_DEFAULT;
player->special_action_bored = ACTION_DEFAULT;
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);
+ int dx = (left ? -1 : right ? 1 : 0);
+ int dy = (up ? -1 : down ? 1 : 0);
if (!player->active || tape.pausing)
return 0;
player->is_moving = FALSE;
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
return 0;
}
if (stored_player[i].drop_delay > 0)
stored_player[i].drop_delay--;
+
+ if (stored_player[i].is_dropping_pressed)
+ stored_player[i].drop_pressed_delay++;
}
}
InitPlayfieldScanModeVars();
- if (ScreenMovPos == 0) /* screen currently aligned at tile position */
+#if 0
+ if (game.set_centered_player)
{
+ boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
+
+ /* switching to "all players" only possible if all players fit to screen */
+ if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+
+ /* do not switch focus to non-existing (or non-active) player */
+ if (game.centered_player_nr_next >= 0 &&
+ !stored_player[game.centered_player_nr_next].active)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+ }
+
+ if (game.set_centered_player &&
+ ScreenMovPos == 0) /* screen currently aligned at tile position */
+ {
+#if 0
struct PlayerInfo *player;
int player_nr = game.centered_player_nr_next;
+#endif
+ int sx, sy;
if (game.centered_player_nr_next == -1)
- player_nr = local_player->index_nr;
+ {
+ setScreenCenteredToAllPlayers(&sx, &sy);
+ }
+ else
+ {
+ sx = stored_player[game.centered_player_nr_next].jx;
+ sy = stored_player[game.centered_player_nr_next].jy;
+ }
+#if 0
player = &stored_player[player_nr];
if (!player->active)
game.centered_player_nr_next = game.centered_player_nr;
+ sx = player->jx;
+ sy = player->jy;
+#endif
+
+#if 0
if (game.centered_player_nr != game.centered_player_nr_next)
+#endif
{
+#if 1
+ DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
+#else
DrawRelocatePlayer(player, setup.quick_switch);
+#endif
game.centered_player_nr = game.centered_player_nr_next;
}
+
+ game.set_centered_player = FALSE;
}
+#endif
#if USE_ONE_MORE_CHANGE_PER_FRAME
if (game.engine_version >= VERSION_IDENT(3,2,0,7))
recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
+#if 1
+ if (tape.set_centered_player)
+ {
+ game.centered_player_nr_next = tape.centered_player_nr_next;
+ game.set_centered_player = TRUE;
+ }
+
+ if (game.set_centered_player)
+ {
+ boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
+
+ /* switching to "all players" only possible if all players fit to screen */
+ if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+
+ /* do not switch focus to non-existing (or non-active) player */
+ if (game.centered_player_nr_next >= 0 &&
+ !stored_player[game.centered_player_nr_next].active)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+ }
+
+ if (game.set_centered_player &&
+ ScreenMovPos == 0) /* screen currently aligned at tile position */
+ {
+#if 0
+ struct PlayerInfo *player;
+ int player_nr = game.centered_player_nr_next;
+#endif
+ int sx, sy;
+
+ if (game.centered_player_nr_next == -1)
+ {
+ setScreenCenteredToAllPlayers(&sx, &sy);
+ }
+ else
+ {
+ sx = stored_player[game.centered_player_nr_next].jx;
+ sy = stored_player[game.centered_player_nr_next].jy;
+ }
+
+#if 0
+ player = &stored_player[player_nr];
+
+ if (!player->active)
+ game.centered_player_nr_next = game.centered_player_nr;
+
+ sx = player->jx;
+ sy = player->jy;
+#endif
+
+#if 0
+ if (game.centered_player_nr != game.centered_player_nr_next)
+#endif
+ {
+#if 1
+ DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
+#else
+ DrawRelocatePlayer(player, setup.quick_switch);
+#endif
+
+ game.centered_player_nr = game.centered_player_nr_next;
+ }
+
+ game.set_centered_player = FALSE;
+ }
+#endif
+
#if 1
/* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
if (recorded_player_action == NULL && tape.pausing)
player->is_snapping = FALSE;
player->is_switching = FALSE;
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
}
else
{
}
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
return FALSE;
EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
EL_UNDEFINED);
+ player->is_dropping_pressed = TRUE;
+
/* do not drop an element on top of another element; when holding drop key
pressed without moving, dropped element must move away before the next
element can be dropped (this is especially important if the next element
if (new_element == EL_UNDEFINED)
return FALSE;
+ /* check if drop key was pressed long enough for EM style dynamite */
+ if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
+ return FALSE;
+
/* check if anything can be dropped at the current position */
if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
return FALSE;
player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
player->is_dropping = TRUE;
+ player->drop_pressed_delay = 0;
+ player->is_dropping_pressed = FALSE;
+
player->drop_x = dropx;
player->drop_y = dropy;