+ for (j = 0; j < NUM_DIRECTIONS; j++)
+ if (el_act_dir2img(element, i, j) !=
+ el_act_dir2img(element, ACTION_DEFAULT, j))
+ found = TRUE;
+
+ if (found)
+ num_special_action++;
+ else
+ break;
+ }
+
+ return num_special_action;
+}
+
+
+/*
+ =============================================================================
+ InitGame()
+ -----------------------------------------------------------------------------
+ initialize and start new game
+ =============================================================================
+*/
+
+void InitGame()
+{
+ boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
+ boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
+ boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
+ boolean do_fading = (game_status == GAME_MODE_MAIN);
+ int i, j, x, y;
+
+ game_status = GAME_MODE_PLAYING;
+
+ InitGameEngine();
+
+ /* don't play tapes over network */
+ network_playing = (options.network && !tape.playing);
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ player->index_nr = i;
+ player->index_bit = (1 << i);
+ player->element_nr = EL_PLAYER_1 + i;
+
+ player->present = FALSE;
+ player->active = FALSE;
+ player->killed = FALSE;
+
+ player->action = 0;
+ player->effective_action = 0;
+ player->programmed_action = 0;
+
+ player->score = 0;
+ player->score_final = 0;
+
+ player->gems_still_needed = level.gems_needed;
+ player->sokobanfields_still_needed = 0;
+ player->lights_still_needed = 0;
+ player->friends_still_needed = 0;
+
+ for (j = 0; j < MAX_NUM_KEYS; j++)
+ player->key[j] = FALSE;
+
+ player->num_white_keys = 0;
+
+ player->dynabomb_count = 0;
+ player->dynabomb_size = 1;
+ player->dynabombs_left = 0;
+ player->dynabomb_xl = FALSE;
+
+ player->MovDir = MV_NONE;
+ player->MovPos = 0;
+ player->GfxPos = 0;
+ player->GfxDir = MV_NONE;
+ player->GfxAction = ACTION_DEFAULT;
+ player->Frame = 0;
+ player->StepFrame = 0;
+
+ player->use_murphy = FALSE;
+ player->artwork_element =
+ (level.use_artwork_element[i] ? level.artwork_element[i] :
+ player->element_nr);
+
+ player->block_last_field = FALSE; /* initialized in InitPlayerField() */
+ player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
+
+ player->gravity = level.initial_player_gravity[i];
+
+ player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
+
+ player->actual_frame_counter = 0;
+
+ player->step_counter = 0;
+
+ player->last_move_dir = MV_NONE;
+
+ player->is_active = FALSE;
+
+ player->is_waiting = FALSE;
+ player->is_moving = FALSE;
+ player->is_auto_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_snapping = FALSE;
+ player->is_collecting = FALSE;
+ 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->frame_counter_bored = -1;
+ player->frame_counter_sleeping = -1;
+
+ 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;
+
+ player->switch_x = -1;
+ player->switch_y = -1;
+
+ player->drop_x = -1;
+ player->drop_y = -1;
+
+ player->show_envelope = 0;
+
+ SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
+
+ player->push_delay = -1; /* initialized when pushing starts */
+ player->push_delay_value = game.initial_push_delay_value;
+
+ player->drop_delay = 0;
+ player->drop_pressed_delay = 0;
+
+ player->last_jx = -1;
+ player->last_jy = -1;
+ player->jx = -1;
+ player->jy = -1;
+
+ player->shield_normal_time_left = 0;
+ player->shield_deadly_time_left = 0;
+
+ player->inventory_infinite_element = EL_UNDEFINED;
+ player->inventory_size = 0;
+
+ DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
+ SnapField(player, 0, 0);
+
+ player->LevelSolved = FALSE;
+ player->GameOver = FALSE;
+
+ player->LevelSolved_GameWon = FALSE;
+ player->LevelSolved_GameEnd = FALSE;
+ player->LevelSolved_PanelOff = FALSE;
+ player->LevelSolved_SaveTape = FALSE;
+ player->LevelSolved_SaveScore = FALSE;
+ }
+
+ network_player_action_received = FALSE;
+
+#if defined(NETWORK_AVALIABLE)
+ /* initial null action */
+ if (network_playing)
+ SendToServer_MovePlayer(MV_NONE);
+#endif
+
+ ZX = ZY = -1;
+ ExitX = ExitY = -1;
+
+ FrameCounter = 0;
+ TimeFrames = 0;
+ TimePlayed = 0;
+ TimeLeft = level.time;
+ TapeTime = 0;
+
+ ScreenMovDir = MV_NONE;
+ ScreenMovPos = 0;
+ ScreenGfxPos = 0;
+
+ ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
+
+ AllPlayersGone = FALSE;
+
+ game.yamyam_content_nr = 0;
+ game.magic_wall_active = FALSE;
+ game.magic_wall_time_left = 0;
+ game.light_time_left = 0;
+ game.timegate_time_left = 0;
+ game.switchgate_pos = 0;
+ game.wind_direction = level.wind_direction_initial;
+
+#if !USE_PLAYER_GRAVITY
+ game.gravity = FALSE;
+ game.explosions_delayed = TRUE;
+#endif
+
+ game.lenses_time_left = 0;
+ game.magnify_time_left = 0;
+
+ game.ball_state = level.ball_state_initial;
+ game.ball_content_nr = 0;
+
+ game.envelope_active = FALSE;
+
+ /* set focus to local player for network games, else to all players */
+ 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;
+
+ if (network_playing && tape.recording)
+ {
+ /* store client dependent player focus when recording network games */
+ tape.centered_player_nr_next = game.centered_player_nr_next;
+ tape.set_centered_player = TRUE;
+ }
+
+ for (i = 0; i < NUM_BELTS; i++)
+ {
+ game.belt_dir[i] = MV_NONE;
+ game.belt_dir_nr[i] = 3; /* not moving, next moving left */
+ }
+
+ for (i = 0; i < MAX_NUM_AMOEBA; i++)
+ AmoebaCnt[i] = AmoebaCnt2[i] = 0;
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ Feld[x][y] = level.field[x][y];
+ MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+ ChangeDelay[x][y] = 0;
+ ChangePage[x][y] = -1;
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[x][y] = 0; /* initialized in InitField() */
+#endif
+ Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
+ AmoebaNr[x][y] = 0;
+ WasJustMoving[x][y] = 0;
+ WasJustFalling[x][y] = 0;
+ CheckCollision[x][y] = 0;
+ CheckImpact[x][y] = 0;
+ Stop[x][y] = FALSE;
+ Pushed[x][y] = FALSE;
+
+ ChangeCount[x][y] = 0;
+ ChangeEvent[x][y] = -1;
+
+ ExplodePhase[x][y] = 0;
+ ExplodeDelay[x][y] = 0;
+ ExplodeField[x][y] = EX_TYPE_NONE;
+
+ RunnerVisit[x][y] = 0;
+ PlayerVisit[x][y] = 0;
+
+ GfxFrame[x][y] = 0;
+ GfxRandom[x][y] = INIT_GFX_RANDOM();
+ GfxElement[x][y] = EL_UNDEFINED;
+ GfxAction[x][y] = ACTION_DEFAULT;
+ GfxDir[x][y] = MV_NONE;
+ }
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
+ emulate_bd = FALSE;
+ if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
+ emulate_sb = FALSE;
+ if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
+ emulate_sp = FALSE;
+
+ InitField(x, y, TRUE);
+ }
+
+ InitBeltMovement();
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ /* 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);
+ }
+
+ game.emulation = (emulate_bd ? EMU_BOULDERDASH :
+ emulate_sb ? EMU_SOKOBAN :
+ emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+
+#if USE_NEW_ALL_SLIPPERY
+ /* initialize type of slippery elements */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (!IS_CUSTOM_ELEMENT(i))
+ {
+ /* default: elements slip down either to the left or right randomly */
+ element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
+
+ /* SP style elements prefer to slip down on the left side */
+ if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
+ element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+
+ /* BD style elements prefer to slip down on the left side */
+ if (game.emulation == EMU_BOULDERDASH)
+ element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+ }
+ }
+#endif
+
+ /* initialize explosion and ignition delay */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (!IS_CUSTOM_ELEMENT(i))
+ {
+ int num_phase = 8;
+ int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
+ game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
+ game.emulation == EMU_SUPAPLEX ? 3 : 2);
+ int last_phase = (num_phase + 1) * delay;
+ int half_phase = (num_phase / 2) * delay;
+
+ element_info[i].explosion_delay = last_phase - 1;
+ element_info[i].ignition_delay = half_phase;
+
+ if (i == EL_BLACK_ORB)
+ element_info[i].ignition_delay = 1;
+ }
+
+#if 0
+ if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
+ element_info[i].explosion_delay = 1;
+
+ if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
+ element_info[i].ignition_delay = 1;
+#endif
+ }
+
+ /* correct non-moving belts to start moving left */
+ for (i = 0; i < NUM_BELTS; i++)
+ if (game.belt_dir[i] == MV_NONE)
+ game.belt_dir_nr[i] = 3; /* not moving, next moving left */
+
+ /* check if any connected player was not found in playfield */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ if (player->connected && !player->present)
+ {
+ for (j = 0; j < MAX_PLAYERS; j++)
+ {
+ struct PlayerInfo *some_player = &stored_player[j];
+ int jx = some_player->jx, jy = some_player->jy;
+
+ /* assign first free player found that is present in the playfield */
+ if (some_player->present && !some_player->connected)
+ {
+ player->present = TRUE;
+ player->active = TRUE;
+
+ some_player->present = FALSE;
+ some_player->active = FALSE;
+
+ player->artwork_element = some_player->artwork_element;
+
+ player->block_last_field = some_player->block_last_field;
+ player->block_delay_adjustment = some_player->block_delay_adjustment;
+
+ StorePlayer[jx][jy] = player->element_nr;
+ player->jx = player->last_jx = jx;
+ player->jy = player->last_jy = jy;
+
+ break;
+ }
+ }
+ }
+ }
+
+ if (tape.playing)
+ {
+ /* when playing a tape, eliminate all players who do not participate */
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active && !tape.player_participates[i])
+ {
+ struct PlayerInfo *player = &stored_player[i];
+ int jx = player->jx, jy = player->jy;
+
+ player->active = FALSE;
+ StorePlayer[jx][jy] = 0;
+ Feld[jx][jy] = EL_EMPTY;
+ }
+ }
+ }
+ else if (!options.network && !setup.team_mode) /* && !tape.playing */
+ {
+ /* when in single player mode, eliminate all but the first active player */
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active)
+ {
+ for (j = i + 1; j < MAX_PLAYERS; j++)
+ {
+ if (stored_player[j].active)
+ {
+ struct PlayerInfo *player = &stored_player[j];
+ int jx = player->jx, jy = player->jy;
+
+ player->active = FALSE;
+ player->present = FALSE;
+
+ StorePlayer[jx][jy] = 0;
+ Feld[jx][jy] = EL_EMPTY;
+ }
+ }
+ }
+ }
+ }
+
+ /* when recording the game, store which players take part in the game */
+ if (tape.recording)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (stored_player[i].active)
+ tape.player_participates[i] = TRUE;
+ }
+
+ if (options.debug)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ printf("Player %d: present == %d, connected == %d, active == %d.\n",
+ i+1,
+ player->present,
+ player->connected,
+ player->active);
+ if (local_player == player)
+ printf("Player %d is local player.\n", i+1);
+ }
+ }
+
+ if (BorderElement == EL_EMPTY)
+ {
+ SBX_Left = 0;
+ SBX_Right = lev_fieldx - SCR_FIELDX;
+ SBY_Upper = 0;
+ SBY_Lower = lev_fieldy - SCR_FIELDY;
+ }
+ else
+ {
+ SBX_Left = -1;
+ SBX_Right = lev_fieldx - SCR_FIELDX + 1;
+ SBY_Upper = -1;
+ SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
+ }
+
+ if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
+ SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
+
+ if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
+ SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+
+ /* if local player not found, look for custom element that might create
+ the player (make some assumptions about the right custom element) */
+ if (!local_player->present)
+ {
+ int start_x = 0, start_y = 0;
+ int found_rating = 0;
+ int found_element = EL_UNDEFINED;
+ int player_nr = local_player->index_nr;
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ int element = Feld[x][y];
+ int content;
+ int xx, yy;
+ boolean is_player;
+
+ if (level.use_start_element[player_nr] &&
+ level.start_element[player_nr] == element &&
+ found_rating < 4)
+ {
+ start_x = x;
+ start_y = y;
+
+ found_rating = 4;
+ found_element = element;
+ }
+
+ if (!IS_CUSTOM_ELEMENT(element))
+ continue;
+
+ if (CAN_CHANGE(element))
+ {
+ for (i = 0; i < element_info[element].num_change_pages; i++)
+ {
+ /* check for player created from custom element as single target */
+ content = element_info[element].change_page[i].target_element;
+ is_player = ELEM_IS_PLAYER(content);
+
+ if (is_player && (found_rating < 3 || element < found_element))
+ {
+ start_x = x;
+ start_y = y;
+
+ found_rating = 3;
+ found_element = element;
+ }
+ }
+ }
+
+ for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
+ {
+ /* check for player created from custom element as explosion content */
+ content = element_info[element].content.e[xx][yy];
+ is_player = ELEM_IS_PLAYER(content);
+
+ if (is_player && (found_rating < 2 || element < found_element))
+ {
+ start_x = x + xx - 1;
+ start_y = y + yy - 1;
+
+ found_rating = 2;
+ found_element = element;
+ }
+
+ if (!CAN_CHANGE(element))
+ continue;
+
+ for (i = 0; i < element_info[element].num_change_pages; i++)
+ {
+ /* check for player created from custom element as extended target */
+ content =
+ element_info[element].change_page[i].target_content.e[xx][yy];
+
+ is_player = ELEM_IS_PLAYER(content);
+
+ if (is_player && (found_rating < 1 || element < found_element))
+ {
+ start_x = x + xx - 1;
+ start_y = y + yy - 1;
+
+ found_rating = 1;
+ found_element = element;
+ }
+ }
+ }
+ }
+
+ scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
+ start_x > SBX_Right + MIDPOSX ? SBX_Right :
+ start_x - MIDPOSX);
+
+ scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ start_y - MIDPOSY);
+ }
+ else
+ {
+ scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
+ local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+ local_player->jx - MIDPOSX);
+
+ scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+ local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+ local_player->jy - MIDPOSY);
+ }
+
+ StopAnimation();
+
+ if (!game.restart_level)
+ CloseDoor(DOOR_CLOSE_1);
+
+ if (do_fading)
+ FadeOut(REDRAW_FIELD);
+
+ /* !!! FIX THIS (START) !!! */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ InitGameEngine_EM();
+
+ /* blit playfield from scroll buffer to normal back buffer for fading in */
+ BlitScreenToBitmap_EM(backbuffer);
+ }
+ else
+ {
+ DrawLevel();
+ DrawAllPlayers();
+
+ /* after drawing the level, correct some elements */
+ if (game.timegate_time_left == 0)
+ CloseAllOpenTimegates();
+
+ /* blit playfield from scroll buffer to normal back buffer for fading in */
+ if (setup.soft_scrolling)
+ BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+
+ redraw_mask |= REDRAW_FROM_BACKBUFFER;
+ }
+ /* !!! FIX THIS (END) !!! */
+
+ if (do_fading)
+ FadeIn(REDRAW_FIELD);
+
+ BackToFront();
+
+ if (!game.restart_level)
+ {
+ /* copy default game door content to main double buffer */
+ BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+ DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+ }
+
+ SetPanelBackground();
+ SetDrawBackgroundMask(REDRAW_DOOR_1);
+
+ DrawGameDoorValues();
+
+ if (!game.restart_level)
+ {
+ UnmapGameButtons();
+ UnmapTapeButtons();
+ game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
+ game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
+ game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
+ MapGameButtons();
+ MapTapeButtons();
+
+ /* copy actual game door content to door double buffer for OpenDoor() */
+ BlitBitmap(drawto, bitmap_db_door,
+ DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+
+ OpenDoor(DOOR_OPEN_ALL);
+
+ PlaySound(SND_GAME_STARTING);
+
+ if (setup.sound_music)
+ PlayLevelMusic();
+
+ KeyboardAutoRepeatOffUnlessAutoplay();
+
+ if (options.debug)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ printf("Player %d %sactive.\n",
+ i + 1, (stored_player[i].active ? "" : "not "));
+ }
+ }
+
+#if 1
+ UnmapAllGadgets();
+
+ MapGameButtons();
+ MapTapeButtons();
+#endif
+
+ game.restart_level = FALSE;
+}
+
+void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
+{
+ /* this is used for non-R'n'D game engines to update certain engine values */
+
+ /* needed to determine if sounds are played within the visible screen area */
+ scroll_x = actual_scroll_x;
+ scroll_y = actual_scroll_y;
+}
+
+void InitMovDir(int x, int y)
+{
+ int i, element = Feld[x][y];
+ static int xy[4][2] =
+ {
+ { 0, +1 },
+ { +1, 0 },
+ { 0, -1 },
+ { -1, 0 }
+ };
+ static int direction[3][4] =
+ {
+ { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
+ { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
+ { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
+ };
+
+ switch (element)
+ {
+ case EL_BUG_RIGHT:
+ case EL_BUG_UP:
+ case EL_BUG_LEFT:
+ case EL_BUG_DOWN:
+ Feld[x][y] = EL_BUG;
+ MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
+ break;
+
+ case EL_SPACESHIP_RIGHT:
+ case EL_SPACESHIP_UP:
+ case EL_SPACESHIP_LEFT:
+ case EL_SPACESHIP_DOWN:
+ Feld[x][y] = EL_SPACESHIP;
+ MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
+ break;