+ InitGameEngine();
+
+#if 0
+#if DEBUG
+#if USE_NEW_AMOEBA_CODE
+ printf("Using new amoeba code.\n");
+#else
+ printf("Using old amoeba code.\n");
+#endif
+#endif
+#endif
+
+ /* 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->element_nr = EL_PLAYER_1 + i;
+
+ player->present = FALSE;
+ player->active = FALSE;
+
+ player->action = 0;
+ player->effective_action = 0;
+ player->programmed_action = 0;
+
+ player->score = 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<4; j++)
+ player->key[j] = FALSE;
+
+ player->dynamite = 0;
+ player->dynabomb_count = 0;
+ player->dynabomb_size = 1;
+ player->dynabombs_left = 0;
+ player->dynabomb_xl = FALSE;
+
+ player->MovDir = MV_NO_MOVING;
+ player->MovPos = 0;
+ player->Pushing = FALSE;
+ player->Switching = FALSE;
+ player->GfxPos = 0;
+ player->GfxDir = MV_NO_MOVING;
+ player->GfxAction = ACTION_DEFAULT;
+ player->Frame = 0;
+ player->StepFrame = 0;
+
+ player->use_murphy_graphic = FALSE;
+ player->use_disk_red_graphic = FALSE;
+
+ player->actual_frame_counter = 0;
+
+ player->last_move_dir = MV_NO_MOVING;
+ player->is_moving = FALSE;
+
+ player->is_moving = FALSE;
+ player->is_waiting = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+
+ player->move_delay = game.initial_move_delay;
+ player->move_delay_value = game.initial_move_delay_value;
+
+ player->push_delay = 0;
+ player->push_delay_value = 5;
+
+ player->snapped = FALSE;
+
+ player->last_jx = player->last_jy = 0;
+ player->jx = player->jy = 0;
+
+ player->shield_normal_time_left = 0;
+ player->shield_deadly_time_left = 0;
+
+ DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
+ SnapField(player, 0, 0);
+
+ player->LevelSolved = FALSE;
+ player->GameOver = FALSE;
+ }
+
+ network_player_action_received = FALSE;
+
+#if defined(PLATFORM_UNIX)
+ /* initial null action */
+ if (network_playing)
+ SendToServer_MovePlayer(MV_NO_MOVING);
+#endif
+
+ ZX = ZY = -1;
+
+ FrameCounter = 0;
+ TimeFrames = 0;
+ TimePlayed = 0;
+ TimeLeft = level.time;
+
+ ScreenMovDir = MV_NO_MOVING;
+ 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.balloon_dir = MV_NO_MOVING;
+ game.explosions_delayed = TRUE;
+
+ for (i=0; i<4; i++)
+ {
+ game.belt_dir[i] = MV_NO_MOVING;
+ game.belt_dir_nr[i] = 3; /* not moving, next moving left */
+ }
+
+ for (i=0; i<MAX_NUM_AMOEBA; i++)
+ AmoebaCnt[i] = AmoebaCnt2[i] = 0;
+
+ for (x=0; x<lev_fieldx; x++)
+ {
+ for (y=0; y<lev_fieldy; y++)
+ {
+ Feld[x][y] = Ur[x][y];
+ MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+ Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
+ AmoebaNr[x][y] = 0;
+ JustStopped[x][y] = 0;
+ Stop[x][y] = FALSE;
+ ExplodePhase[x][y] = 0;
+ ExplodeField[x][y] = EX_NO_EXPLOSION;
+
+ GfxFrame[x][y] = 0;
+ GfxAction[x][y] = ACTION_DEFAULT;
+ GfxRandom[x][y] = INIT_GFX_RANDOM();
+ GfxElement[x][y] = EL_UNDEFINED;
+ }
+ }
+
+ for(y=0; y<lev_fieldy; y++)
+ {
+ for(x=0; x<lev_fieldx; x++)
+ {
+ 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();
+
+ game.emulation = (emulate_bd ? EMU_BOULDERDASH :
+ emulate_sb ? EMU_SOKOBAN :
+ emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+
+ /* correct non-moving belts to start moving left */
+ for (i=0; i<4; i++)
+ if (game.belt_dir[i] == MV_NO_MOVING)
+ 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;
+
+ 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;
+ 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;
+
+ scroll_x = SBX_Left;
+ scroll_y = SBY_Upper;
+ if (local_player->jx >= SBX_Left + MIDPOSX)
+ scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
+ local_player->jx - MIDPOSX :
+ SBX_Right);
+ if (local_player->jy >= SBY_Upper + MIDPOSY)
+ scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
+ local_player->jy - MIDPOSY :
+ SBY_Lower);
+
+ CloseDoor(DOOR_CLOSE_1);
+
+ DrawLevel();
+ DrawAllPlayers();
+
+ /* after drawing the level, correct some elements */
+ if (game.timegate_time_left == 0)
+ CloseAllOpenTimegates();
+
+ if (setup.soft_scrolling)
+ BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+
+ redraw_mask |= REDRAW_FROM_BACKBUFFER;
+ FadeToFront();
+
+ /* 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);
+
+ if (level_nr < 100)
+ DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
+ else
+ {
+ DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
+ int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
+ BlitBitmap(drawto, drawto,
+ DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
+ getFontWidth(FONT_LEVEL_NUMBER) * 3,
+ getFontHeight(FONT_LEVEL_NUMBER) - 1,
+ DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
+ }
+
+ DrawGameDoorValues();
+
+ 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);
+
+ PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
+ if (setup.sound_music)
+ PlayMusic(level_nr);
+
+ KeyboardAutoRepeatOffUnlessAutoplay();
+
+ if (options.debug)
+ {
+ for (i=0; i<4; i++)
+ printf("Player %d %sactive.\n",
+ i + 1, (stored_player[i].active ? "" : "not "));
+ }
+}
+
+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;
+
+ case EL_BD_BUTTERFLY_RIGHT:
+ case EL_BD_BUTTERFLY_UP:
+ case EL_BD_BUTTERFLY_LEFT:
+ case EL_BD_BUTTERFLY_DOWN:
+ Feld[x][y] = EL_BD_BUTTERFLY;
+ MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
+ break;
+
+ case EL_BD_FIREFLY_RIGHT:
+ case EL_BD_FIREFLY_UP:
+ case EL_BD_FIREFLY_LEFT:
+ case EL_BD_FIREFLY_DOWN:
+ Feld[x][y] = EL_BD_FIREFLY;
+ MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
+ break;
+
+ case EL_PACMAN_RIGHT:
+ case EL_PACMAN_UP:
+ case EL_PACMAN_LEFT:
+ case EL_PACMAN_DOWN:
+ Feld[x][y] = EL_PACMAN;
+ MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
+ break;
+
+ case EL_SP_SNIKSNAK:
+ MovDir[x][y] = MV_UP;
+ break;
+
+ case EL_SP_ELECTRON:
+ MovDir[x][y] = MV_LEFT;
+ break;
+
+ case EL_MOLE_LEFT:
+ case EL_MOLE_RIGHT:
+ case EL_MOLE_UP:
+ case EL_MOLE_DOWN:
+ Feld[x][y] = EL_MOLE;
+ MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
+ break;
+
+ default:
+ if (IS_CUSTOM_ELEMENT(element))
+ {
+ if (element_info[element].move_direction_initial != MV_NO_MOVING)
+ MovDir[x][y] = element_info[element].move_direction_initial;
+ else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS)
+ MovDir[x][y] = 1 << RND(4);
+ else if (element_info[element].move_pattern == MV_HORIZONTAL)
+ MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
+ else if (element_info[element].move_pattern == MV_VERTICAL)
+ MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
+ else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
+ MovDir[x][y] = element_info[element].move_pattern;
+ else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
+ element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+ {
+ for (i=0; i<4; i++)
+ {
+ int x1 = x + xy[i][0];
+ int y1 = y + xy[i][1];
+
+ if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
+ {
+ if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+ MovDir[x][y] = direction[0][i];
+ else
+ MovDir[x][y] = direction[1][i];
+
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ MovDir[x][y] = 1 << RND(4);
+
+ if (element != EL_BUG &&
+ element != EL_SPACESHIP &&
+ element != EL_BD_BUTTERFLY &&
+ element != EL_BD_FIREFLY)
+ break;
+
+ for (i=0; i<4; i++)
+ {
+ int x1 = x + xy[i][0];
+ int y1 = y + xy[i][1];
+
+ if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
+ {
+ if (element == EL_BUG || element == EL_BD_BUTTERFLY)
+ {
+ MovDir[x][y] = direction[0][i];
+ break;
+ }
+ else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
+ element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
+ {
+ MovDir[x][y] = direction[1][i];
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+void InitAmoebaNr(int x, int y)
+{
+ int i;
+ int group_nr = AmoebeNachbarNr(x, y);
+
+ if (group_nr == 0)
+ {
+ for (i=1; i<MAX_NUM_AMOEBA; i++)
+ {
+ if (AmoebaCnt[i] == 0)
+ {
+ group_nr = i;
+ break;
+ }
+ }
+ }
+
+ AmoebaNr[x][y] = group_nr;
+ AmoebaCnt[group_nr]++;
+ AmoebaCnt2[group_nr]++;
+}
+
+void GameWon()
+{
+ int hi_pos;
+ boolean raise_level = FALSE;
+
+ if (local_player->MovPos)
+ return;
+
+ if (tape.playing && tape.auto_play)
+ tape.auto_play_level_solved = TRUE;
+
+ local_player->LevelSolved = FALSE;
+
+ PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
+
+ if (TimeLeft)
+ {
+ if (!tape.playing && setup.sound_loops)
+ PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
+ SND_CTRL_PLAY_LOOP);
+
+ while (TimeLeft > 0)
+ {
+ if (!tape.playing && !setup.sound_loops)
+ PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+ if (TimeLeft > 0 && !(TimeLeft % 10))
+ RaiseScore(level.score[SC_TIME_BONUS]);
+ if (TimeLeft > 100 && !(TimeLeft % 10))
+ TimeLeft -= 10;
+ else
+ TimeLeft--;
+ DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
+ BackToFront();
+
+ if (!tape.playing)
+ Delay(10);
+ }
+
+ if (!tape.playing && setup.sound_loops)
+ StopSound(SND_GAME_LEVELTIME_BONUS);
+ }
+ else if (level.time == 0) /* level without time limit */
+ {
+ if (!tape.playing && setup.sound_loops)
+ PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
+ SND_CTRL_PLAY_LOOP);
+
+ while (TimePlayed < 999)
+ {
+ if (!tape.playing && !setup.sound_loops)
+ PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+ if (TimePlayed < 999 && !(TimePlayed % 10))
+ RaiseScore(level.score[SC_TIME_BONUS]);
+ if (TimePlayed < 900 && !(TimePlayed % 10))
+ TimePlayed += 10;
+ else
+ TimePlayed++;
+ DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
+ BackToFront();
+
+ if (!tape.playing)
+ Delay(10);
+ }
+
+ if (!tape.playing && setup.sound_loops)
+ StopSound(SND_GAME_LEVELTIME_BONUS);
+ }
+
+ /* Hero disappears */
+ DrawLevelField(ExitX, ExitY);
+ BackToFront();
+
+ if (tape.playing)
+ return;
+
+ CloseDoor(DOOR_CLOSE_1);
+
+ if (tape.recording)
+ {
+ TapeStop();
+ SaveTape(tape.level_nr); /* Ask to save tape */
+ }
+
+ if (level_nr == leveldir_current->handicap_level)
+ {
+ leveldir_current->handicap_level++;
+ SaveLevelSetup_SeriesInfo();
+ }
+
+ if (level_editor_test_game)
+ local_player->score = -1; /* no highscore when playing from editor */
+ else if (level_nr < leveldir_current->last_level)
+ raise_level = TRUE; /* advance to next level */
+
+ if ((hi_pos = NewHiScore()) >= 0)
+ {
+ game_status = GAME_MODE_SCORES;
+ DrawHallOfFame(hi_pos);
+ if (raise_level)
+ {
+ level_nr++;
+ TapeErase();
+ }
+ }
+ else
+ {
+ game_status = GAME_MODE_MAIN;
+ if (raise_level)
+ {
+ level_nr++;
+ TapeErase();
+ }
+ DrawMainMenu();
+ }
+
+ BackToFront();
+}
+
+int NewHiScore()
+{
+ int k, l;
+ int position = -1;
+
+ LoadScore(level_nr);
+
+ if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
+ local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
+ return -1;
+
+ for (k=0; k<MAX_SCORE_ENTRIES; k++)