//-------------------------------------------------------------------
//
//  This code is copyright 2001 by G5 Software.
//  Any unauthorized usage, either in part or in whole of this code
//  is strictly prohibited. Violators WILL be prosecuted to the
//  maximum extent allowed by law.
//
//-------------------------------------------------------------------

class CMainMenu extends

  // base classes
  CBaseMenu, CCommonStrings, CPlayerSettings, CMoviePlayer,

  // Menu classes
  CMultiplayerMenu, CPlayerMenu, CSettingsMenu, CCampaignMenu, CEnvironmentMenu

{
  String g_StdFontName  = "Europe";
  float  g_StdFontSize  = 0.031;
  Color  g_StdFontColor = new Color(210.0 / 255.0, 210.0 / 255.0, 210.0 / 255.0);

  Color  StdFrameColor  = new Color(159.0 / 255.0, 96.0 / 255.0, 96.0 / 255.0);
  Color  StdHilitedFrameColor = new Color(1.0, 0.0, 0.0);
  Color  StdHilitedBitmapColor = new Color(1.0, 0.0, 0.0);
  Color  StdWhiteColor = new Color(1.0, 1.0, 1.0);
  Color  StdBitmapColor = StdWhiteColor;
  Color  StdDisabledColor = new Color(102.0 / 255.0, 102.0 / 255.0, 102.0 / 255.0);
  Color  InvisibleColor = new Color(0.0, 0.0, 0.0, 0.0);
  Color  StdBlackColor = new Color (1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
  Color  StdCompletedColor = new Color(1.0, 113.0 / 255.0, 113.0 / 255.0);

  Color  ObjectiveInProgressStatusColor = new Color(194.0 / 255.0, 121.0 / 255.0, 121.0 / 255.0);
  Color  ObjectiveCompleteStatusColor   = new Color(121.0 / 255.0, 194.0 / 255.0, 164.0 / 255.0);
  Color  ObjectiveFailedStatusColor     = new Color(1.0, 0.0, 0.0);

  Component m_MenuSoundMouseOver;
  Component m_MenuSoundNextScreen;
  Component m_MenuSoundPushButton;

  int       m_HilitedAudibleID = 0;

  int m_StartPlayersSlot    = 0;
  int m_SelectedPlayersSlot = 0;

  // name of the mission loaded last time
  String m_LastLoadedMission;

  // Current mission name
  WString m_MissionName;

  Array  m_Objectives;
  Array  m_Statuses;

  Array  m_BonusObjectives;
  Array  m_BonusStatuses;

  boolean m_bMissionStatus;

  boolean m_UserEscape = true;

  boolean m_bIsPlayerDied;

  CMainMenu()
  {
    CreateDefaultCursor();
    Init();

    m_MenuSoundMouseOver   = new #Sound2D<CMenuSoundMouseOver>();
    m_MenuSoundPushButton  = new #Sound2D<CMenuSoundPushButton>();
    //  Now sounds pretty strange if these sounds are different.
    //  Furthermore, it is too messed up to distingish 'next' buttons from others.
    m_MenuSoundNextScreen  = m_MenuSoundPushButton;
  }

  void CreateQuitWindow()
  {
    float X = 208.0;
    float Y = 277.0;

    CreateGroupBackground(
       "QUIT_MESSAGE",
       CCommonStrings::str_QuitMessage,
       X / 1024.0,
       Y / 768.0,
       609.0 / 1024.0,
       90.0  / 768.0
     );

    CreateAudibleButton(
        "ID_QUIT",
        "caption",
        "caption",
        CCommonStrings::YNSwitchPosition[1],
        (X + 269.0) / 1024.0,
        (Y + 48.0) / 768.0,
        138.0 / 1024.0,
        33.0 / 768.0
      );
    GetObject("ID_QUIT").EnableFrame(true);
    GetObject("ID_QUIT").SetFrameColors(StdFrameColor, StdFrameColor);
    GetObject("ID_QUIT").SetFont(g_StdFontName, g_StdFontSize);

    CreateAudibleButton(
        "QUIT_MESSAGE_NO",
        "caption",
        "caption",
        CCommonStrings::YNSwitchPosition[0],
        (X + 430.0) / 1024.0,
        (Y + 48.0) / 768.0,
        138.0 / 1024.0,
        33.0 / 768.0
      );
    GetObject("QUIT_MESSAGE_NO").EnableFrame(true);
    GetObject("QUIT_MESSAGE_NO").SetFrameColors(StdFrameColor, StdFrameColor);
    GetObject("QUIT_MESSAGE_NO").SetFont(g_StdFontName, g_StdFontSize);
  }

  boolean FirstStart = true;

  void Init()
  {
    // run intros if first start
    if (FirstStart)
    {
      FirstStart = false;
      Component Intros = new CIntroSettings();
      PlayMovieList("ID_INTROS", Intros.Movies);
    }

    (new #GameController()).ResetDifficultyLevel();

    new #MusicController().SetMenuState(CMusicController::MENU_Main);

    // Background

    CreateBackground("main menu background");

    // "Game" button background

    CreateInterlacedButtonBackground(
      "GAMEBACK",
      728.0 / 1024.0,
      421.0 / 768.0,
      285.0 / 1024.0,
      40.0  / 768.0,
      true
    );

    // "Multiplayer" button background

    CreateInterlacedButtonBackground(
      "MULTIPLAYERBACK",
      728.0 / 1024.0,
      478.0 / 768.0,
      285.0 / 1024.0,
      40.0  / 768.0,
      true
    );

    // "Settings" button background

    CreateInterlacedButtonBackground(
      "SETTINGSBACK",
      728.0 / 1024.0,
      535.0 / 768.0,
      285.0 / 1024.0,
      40.0  / 768.0,
      true
    );

    // "Credits" button background

    CreateInterlacedButtonBackground(
      "CREDITSBACK",
      728.0 / 1024.0,
      592.0 / 768.0,
      285.0 / 1024.0,
      40.0  / 768.0,
      true
    );

    // "Quit" button background

    CreateInterlacedButtonBackground(
      "QUITBACK",
      728.0 / 1024.0,
      687.0 / 768.0,
      285.0 / 1024.0,
      40.0  / 768.0,
      true
    );

    // "Game" button
    CreateAudibleButton(
      "GameMenu",
      "game button right aligned",
      "game button right aligned hilited",
      L"",
      (728.0 + 285.0 - float(MatManager.GetMaterialWidth("game button right aligned"))) / 1024.0,
      421.0 / 768.0,
      float(MatManager.GetMaterialWidth("game button right aligned")) / 1024.0,
      40.0  / 768.0
    );

    // "Multiplayer" button
    CreateAudibleButton(
      "MultiplayerMenu",
      "multiplayer button",
      "multiplayer button hilited",
      L"",
      (728.0 + 285.0 - float(MatManager.GetMaterialWidth("multiplayer button"))) / 1024.0,
      478.0 / 768.0,
      float(MatManager.GetMaterialWidth("multiplayer button")) / 1024.0,
      40.0  / 768.0
    );

    // "Settings" button
    CreateAudibleButton(
      "SettingsMenu",
      "settings button",
      "settings button hilited",
      L"",
      (728.0 + 285.0 - float(MatManager.GetMaterialWidth("settings button"))) / 1024.0,
      535.0 / 768.0,
      float(MatManager.GetMaterialWidth("settings button")) / 1024.0,
      40.0  / 768.0
    );

    // "Credits" button
    CreateAudibleButton(
      "CreditsMenu",
      "credits button",
      "credits button hilited",
      L"",
      (728.0 + 285.0 - float(MatManager.GetMaterialWidth("credits button"))) / 1024.0,
      592.0 / 768.0,
      float(MatManager.GetMaterialWidth("credits button")) / 1024.0,
      40.0  / 768.0
    );

    // "Quit" button
    CreateAudibleButton(
      "ID_QUIT_MESSAGE",
      "quit button",
      "quit button hilited",
      L"",
      (728.0 + 285.0 - float(MatManager.GetMaterialWidth("quit button"))) / 1024.0,
      687.0 / 768.0,
      float(MatManager.GetMaterialWidth("quit button")) / 1024.0,
      40.0  / 768.0
    );

    CreatePlayerInfo(true);
  }

  void LoadingMenu()
  {
    GetObject("Cursor").Enable(false);

    CreateBackground("loading menu background");

    CreateProgressBar(
      "ID_PROGRESS_BAR",
      590.0 / 1024.0,
      693.0 / 768.0,
      427.0 / 1024.0,
      32.0  / 768.0
     );

    GetObject("ID_PROGRESS_BAR").SetProgress(0);
  }

  void OnUpdateLoadProgress(
      int _Progress
    )
  {
    Component ProgressBar = GetObject("ID_PROGRESS_BAR");
    if (ProgressBar != null)
      ProgressBar.SetProgress(_Progress);
  }

  void CreditsMenu()
  {
    m_MenuSoundNextScreen.PlaySoundBuffer(false);

    new #MusicController().SetMenuState(CMusicController::MENU_Main);

    //CreateBackground("credits menu background");

    CreateBackground("main menu background");

    CreateItemBackground(
      "CREDITS_BACK",
      0.15,
      0.25,
      0.7,
      0.5
    );

    WString CreditsText  = new WString("");

    // Loading credits text from Common.rsr
    int nNumCreditsStrings = 18; // See crXXX entries in Common.rsr
    for (int i=0; i < nNumCreditsStrings; i++)
    {
      String CreditsEntry = "cr" + new String(i);
      CreditsText = CreditsText + getLocalized("Credits", CreditsEntry) + L"\r\n";
    }

    Component CinemaText = new #CinemaText();
    CinemaText.SetPosition(0.2, 0.3);
    CinemaText.SetDimensions(0.6, 0.4);
    CinemaText.SetFont(g_StdFontName, g_StdFontSize);
    CinemaText.SetColor(
        new Color(1.0, 1.0, 1.0),
        new Color(0.0, 0.0, 0.0, 0.0)
      );
    CinemaText.SetAlignment(0.0);
    CinemaText.SetPauses(0.03f, 0.0f, 2.0f, 0.05f); // char, space, dot, comma
    CinemaText.SetRenderLayer(PRIMLAYER_MENU_DIALOG);
    RegisterObject("CinemaText", CinemaText);
    CreateBackButton();

    CinemaText.SetText(CreditsText);
    CinemaText.StartScroll(60);
    // $TODO destroy cinema text on exit
  }

  void EscapeMenu()
  {
    GetObject("Cursor").Enable(true);

    Color TextColor = new Color(1.0, 1.0, 1.0);
    float TextShiftX = 3.0 / 1024.0;
    float TextShiftY = 3.0 / 768.0;

    CreateBackground("esc menu background");

    CreateInterlacedButtonBackground(
      "BUTTONBLOCKBACK1",
       10.0 / 1024.0,
      623.0 / 768.0,
      664.0 / 1024.0,
       40.0  / 768.0,
      false
    );

    String GameMode = (new #GameController()).GetGameMode();
    Component Session = (new #GameController()).GetGameSession();

    if (GameMode == "Single")
      CreateMissionStatistics(true);
    else
    if (CServerDescriptor::ST_Cooperative == Session.GetSessionInfo().GetSessionTypeID())
      CreateMissionStatistics(true);
    else
      CreateMissionMultiplayerStatistics();

    // VIDEO
    CreateAudibleButton(
      "SettingsVideoMenu",
      "video button left aligned",
      "video button left aligned hilited",
      L"",
       15.0 / 1024.0,
      623.0 / 768.0,
      float(MatManager.GetMaterialWidth("video button left aligned")) / 1024.0,
      40.0  / 768.0
    );

    // AUDIO
    CreateAudibleButton(
      "SettingsAudioMenu",
      "audio button left aligned",
      "audio button left aligned",
      L"",
      (15.0 + MatManager.GetMaterialWidth("video button left aligned")) / 1024.0,
      623.0 / 768.0,
      float(MatManager.GetMaterialWidth("audio button left aligned")) / 1024.0,
      40.0  / 768.0
    );

    // CONTROLS
    CreateAudibleButton(
      "SettingsControlsMenu",
      "controls button left aligned",
      "controls button left aligned hilited",
      L"",
      (15.0 + MatManager.GetMaterialWidth("video button left aligned") + MatManager.GetMaterialWidth("audio button left aligned")) / 1024.0,
      623.0 / 768.0,
      float(MatManager.GetMaterialWidth("controls button left aligned")) / 1024.0,
      40.0  / 768.0
    );

    // GAME
    CreateAudibleButton(
        "SettingsGameMenu",
        "game button left aligned",
        "game button left aligned hilited",
        L"",
        (15.0 + MatManager.GetMaterialWidth("video button left aligned") + MatManager.GetMaterialWidth("audio button left aligned") + MatManager.GetMaterialWidth("controls button left aligned")) / 1024.0,
        623.0 / 768.0,
        float(MatManager.GetMaterialWidth("game button left aligned")) / 1024.0,
        40.0  / 768.0
      );

    CreatePlayerInfo(false);

    Component Game = new #GameController();
    String GameMode = Game.GetGameMode();
    Component Session = Game.GetGameSession();

    CreateInterlacedButtonBackground(
        "BACKBACK",
        728.0 / 1024.0,
        687.0 / 768.0,
        285.0 / 1024.0,
        40.0  / 768.0,
        true
      );

    CreateAudibleButton(
        "ID_BACK_ESCMENU",
        "continue button",
        "continue button hilited",
        L"",
        (728.0 + 285.0 - float(MatManager.GetMaterialWidth("continue button"))) / 1024.0,
        687.0 / 768.0,
        float(MatManager.GetMaterialWidth("continue button")) / 1024.0,
        41.0  / 768.0
      );

    SetDefaultCommands("", "ID_BACK_ESCMENU");

    if (!m_UserEscape && (GameMode == "Client"))
    {
      if (CServerDescriptor::ST_Cooperative == Session.GetSessionInfo().GetSessionTypeID())
        m_UserEscape = false;
    }

    GetObject("ID_BACK_ESCMENU").SetDisabled(!m_UserEscape);
  }

  void BriefingMenu()
  {
    m_MenuSoundNextScreen.PlaySoundBuffer(false);

    new #MusicController().SetMissionState(CMusicController::MISSION_Briefing);

    Color TextColor = new Color(1.0, 1.0, 1.0);
    float TextShiftX = 5.0 / 1024.0;
    float TextShiftY = 5.0 / 768.0;

    GetObject("Cursor").Enable(true);

    CreateBackground("briefing menu background");

    CreateBitmapControl(
      "ID_BLACKLINES",
      "briefing menu background",
         0.0 / 1024.0,
         0.0 / 768.0,
      1024.0 / 1024.0,
       768.0 / 768.0
    );

    // back/next background/button

    CreateInterlacedButtonBackground(
      "BACKNEXTBACK",
       11.0 / 1024.0,
      687.0 / 768.0,
      420.0 / 1024.0,
       40.0 / 768.0,
      false
    );

    CreateAudibleButton(
      "ID_BRIEFING_PREV",
      "back button left aligned",
      "back button left aligned hilited",
      L"",
       15.0 / 1024.0,
      687.0 / 768.0,
      float(MatManager.GetMaterialWidth("back button left aligned")) / 1024.0,
      40.0  / 768.0
    );

    CreateAudibleButton(
      "ID_BRIEFING_NEXT",
      "next button left aligned",
      "next button left aligned hilited",
      L"",
      (15.0 + float(MatManager.GetMaterialWidth("back button left aligned"))) / 1024.0,
      687.0 / 768.0,
      float(MatManager.GetMaterialWidth("next button left aligned")) / 1024.0,
      40.0  / 768.0
    );

    // start background/button

    CreateInterlacedButtonBackground(
      "STARTBACK",
      728.0 / 1024.0,
      687.0 / 768.0,
      285.0 / 1024.0,
      40.0  / 768.0,
      true
    );

    CreateAudibleButton(
      "EnvironmentMenu",
      "start button",
      "start button hilited",
      L"",
      (728.0 + 285.0 - float(MatManager.GetMaterialWidth("start button"))) / 1024.0,
      687.0 / 768.0,
      float(MatManager.GetMaterialWidth("start button")) / 1024.0,
      40.0  / 768.0
    );

    // briefing picture
/*
    CreateBitmapControl(
      "ID_BRIEFING_PICTURE",
      "",
         0.0 / 1024.0,
        96.0 / 768.0,
      1024.0 / 1024.0,
       574.0 / 768.0
    );
*/
    // briefing text

    CreateItemBackground(
      "BRIEFING_TEXT_BACK",
       14.0 / 1024.0,
      522.0 / 768.0,
      993.0 / 1024.0,
      139.0 / 768.0
    );

    CreateTextControl(
      "ID_BRIEFING_TEXT",
      g_StdFontName,
      g_StdFontSize,
      L"",
      TextColor,
       14.0 / 1024.0 + TextShiftX,
      522.0 / 768.0  + TextShiftY,
      993.0 / 1024.0 - TextShiftX * 2,
      139.0 / 768.0 - TextShiftY * 2
    );

    SwitchToBriefingPage(0);
  }

  void SetMissionStatus(boolean _Status)
  {
    m_bMissionStatus = _Status;
  }

  void DebriefingMenu()
  {
    // select background
    if (!m_bMissionStatus || m_bIsPlayerDied)
    {
      // play failed music
      new #MusicController().SetImmidiateMissionState(CMusicController::MISSION_Killed);
      // create background
      CreateBackground("mission failed menu background");
    }
    else
    {
      // play complete music
      new #MusicController().SetImmidiateMissionState(CMusicController::MISSION_Complete);
      // create background
      CreateBackground("mission complete menu background");
    }

    boolean EntryMission = false;

    Component Game = new #GameController();
    if (Game.GetGameMode() != "Single")
    {
      Component Session = Game.GetGameSession();
      if (Session != null)
      {
        if (CServerDescriptor::ST_Cooperative == Session.GetSessionInfo().GetSessionTypeID())
          EntryMission = true;
      }
    }

    // draw mission
    CreateMissionStatistics(EntryMission);
  }

  void SetMissionObjectives(
      Array _Objectives,
      Array _Statuses,
      Array _VisibleFlag
    )
  {
    // $LOG
//    logWarning("SetMissionObjectives()");

    m_Objectives.clear();
    m_Statuses.clear();
    for (int obj = 0; obj < _Objectives.size(); obj++)
    {
      // $LOG
//      logMessage("Objecive " + new String(obj) + ", status " + new String(_Statuses[obj]) + ", visibility " + new String(_VisibleFlag[obj]));

      if (_VisibleFlag[obj])
      {
        m_Objectives.add(_Objectives[obj]);
        m_Statuses.add(_Statuses[obj]);
      }
    }
  }

  void SetBonusMissionObjectives(
      Array _Objectives,
      Array _Statuses,
      Array _VisibleFlag
    )
  {
    m_BonusObjectives.clear();
    m_BonusStatuses.clear();

    for (int obj = 0; obj < _Objectives.size(); obj++)
      if (_VisibleFlag[obj])
      {
        m_BonusObjectives.add(_Objectives[obj]);
        m_BonusStatuses.add(_Statuses[obj]);
      }
  }

  int MaximumObjectives = 9;

  void CreateMissionStatistics(
      boolean _EntryMission
    )
  {
    Color TextColor = new Color(1.0, 1.0, 1.0);
    float TextShiftX = 3.0 / 1024.0;
    float TextShiftY = 3.0 / 768.0;

    // primary objectives background
    CreateBitmapControl(
        "PRIMARY_OBJECTIVES_BACK",
        "caption",
        11.0 / 1024.0,
        140.0 / 768.0,
        653.0 / 1024.0,
        30.0  / 768.0
      );

    CreateTextControl(
        "PRIMARY_OBJECTIVES_TEXT",
        g_StdFontName,
        g_StdFontSize,
        CCommonStrings::str_PrimaryObjectives,
        g_StdFontColor,
        21.0 / 1024.0,
        (140.0 + 15.0) / 768.0 - g_StdFontSize * 0.5,
        200.0 / 1024.0,
        30.0 / 768.0
      );

    CreateTextControl(
        "MISSION_NAME_TEXT",
        g_StdFontName,
        g_StdFontSize,
        m_MissionName,
        g_StdFontColor,
        201.0 / 1024.0,
        (140.0 + 15.0) / 768.0 - g_StdFontSize * 0.5,
        453.0 / 1024.0,
        30.0  / 768.0
      );
    GetObject("MISSION_NAME_TEXT").SetAlignment(ALIGNMENT_RIGHT);

    CreateBitmapControl(
        "PRIMARY_OBJECTIVES_BACK1",
        "background",
        11.0 / 1024.0,
        170.0 / 768.0,
        653.0 / 1024.0,
        float(m_Objectives.size()) * (46.0 / 768.0)
      );

    // start bonus objectives y
    int MaximumBonusObjectives = MaximumObjectives - m_Objectives.size();
    float BonusY = (170.0 + float(m_Objectives.size()) * 46.0) / 768.0;
    // bonus objectives background
    CreateBitmapControl(
        "BONUS_OBJECTIVES_BACK",
        "caption",
        11.0 / 1024.0,
        BonusY,
        653.0 / 1024.0,
        30.0  / 768.0
      );
    CreateTextControl(
        "BONUS_OBJECTIVES_TEXT",
        g_StdFontName,
        g_StdFontSize,
        CCommonStrings::str_BonusObjectives,
        g_StdFontColor,
        21.0 / 1024.0,
        BonusY + (15.0 / 768.0) - g_StdFontSize * 0.5,
        200.0 / 1024.0,
        30.0 / 768.0
      );
    CreateBitmapControl(
        "BONUS_OBJECTIVES_BACK1",
        "background",
        11.0 / 1024.0,
        BonusY + (30.0 / 768.0),
        653.0 / 1024.0,
        float(MaximumBonusObjectives) * (46.0 / 768.0)
      );
    CreateStaticFrame(
        "OBJECTIVES_FRAME",
        StdFrameColor,
        11.0 / 1024.0,
        140.0 / 768.0,
        653.0 / 1024.0,
        474.0 / 768.0
      );

    // list primary objectives
    for (int obj = 0; obj < m_Objectives.size(); obj++)
    {
      float LineY = (170.0 / 768.0) + float(obj) * (46.0 / 768.0);

      CreateBitmapControl(
          "OBJECTIVE_BACK_" + new String(obj),
          "objectives status background",
          410.0 / 1024.0,
          171.0 / 768.0 + (46.0 / 768.0) * float(obj),
          251.0 / 1024.0,
          44.0 / 768.0
        );

      // status
      Color StatusColor;
      if (m_Statuses[obj] == str_ObjectiveComplete)
        StatusColor = ObjectiveCompleteStatusColor;
      else
      if (m_Statuses[obj] == str_ObjectiveInProgress)
        StatusColor = ObjectiveInProgressStatusColor;
      else
      if (m_Statuses[obj] == str_ObjectiveFailed)
        StatusColor = ObjectiveFailedStatusColor;

      // objective
      CreateTextControl(
          "OBJECTIVE_" + new String(obj),
          g_StdFontName,
          g_StdFontSize,
          new WString(obj + 1) + L". " + m_Objectives[obj],
          StatusColor,
          21.0 / 1024.0,
          LineY,
          376.0 / 1024.0,
          2.0 * g_StdFontSize
        );

      CreateTextButton(
          "STATUS_" + new String(obj),
          g_StdFontName,
          g_StdFontSize,
          m_Statuses[obj],
          21.0 / 1024.0,
          LineY,
          633.0 / 1024.0,
          44.0 / 768.0
        );
      GetObject("STATUS_" + new String(obj)).SetTextColors(StatusColor, StatusColor);
      GetObject("STATUS_" + new String(obj)).SetAlignment(ALIGNMENT_RIGHT);
    }

    // list bonus objectives
    float LineY = BonusY + (30.0 / 768.0);
    for (int obj = 0; obj < MaximumBonusObjectives; obj++)
    {
      CreateBitmapControl(
          "BOBJECTIVE_BACK_" + new String(obj),
          "objectives status background",
          410.0 / 1024.0,
          LineY + (1.0 / 768.0),
          251.0 / 1024.0,
          44.0 / 768.0
        );

      if (obj < m_BonusObjectives.size())
      {
        // status
        Color StatusColor;
        if (m_BonusStatuses[obj] == str_ObjectiveComplete)
          StatusColor = ObjectiveCompleteStatusColor;
        else
        if (m_BonusStatuses[obj] == str_ObjectiveInProgress)
          StatusColor = ObjectiveInProgressStatusColor;
        else
        if (m_BonusStatuses[obj] == str_ObjectiveFailed)
          StatusColor = ObjectiveFailedStatusColor;

        // objective
        CreateTextControl(
            "BOBJECTIVE_" + new String(obj),
            g_StdFontName,
            g_StdFontSize,
            new WString(m_Objectives.size() + obj + 1) + L". " + m_BonusObjectives[obj],
            StatusColor,
            21.0 / 1024.0,
            LineY,
            376.0 / 1024.0,
            2.0 * g_StdFontSize
          );
        CreateTextButton(
            "BSTATUS_" + new String(obj),
            g_StdFontName,
            g_StdFontSize,
            m_BonusStatuses[obj],
            21.0 / 1024.0,
            LineY,
            633.0 / 1024.0,
            2.0 * g_StdFontSize
          );
        GetObject("BSTATUS_" + new String(obj)).SetTextColors(StatusColor, StatusColor);
        GetObject("BSTATUS_" + new String(obj)).SetAlignment(ALIGNMENT_RIGHT);
      }

      LineY = LineY + (46.0 / 768.0);
    }

    // player statistics background
    CreateGroupBackground(
        "MISSION_STATISTICS_BACK",
        CCommonStrings::str_PlayerStatistics,
        671.0 / 1024.0,
        140.0 / 768.0,
        342.0 / 1024.0,
        474.0 / 768.0
      );

    // player rank
    CreateItemBackground(
        "PLAYER_RANK_BACKGROUND",
        680.0 / 1024.0,
        187.0 / 768.0,
        324.0 / 1024.0,
        130.0  / 768.0
      );
    CreateTextControl(
        "PLAYER_RANK_TITLE",
        g_StdFontName,
        g_StdFontSize,
        CCommonStrings::str_Rank,
        g_StdFontColor,
        686.0 / 1024.0,
        194.0 / 768.0,
        310.0 / 1024.0,
        25.0 / 768.0
      );
    CreateTextControl(
        "PLAYER_RANK",
        g_StdFontName,
        g_StdFontSize,
        GetCurrentPlayer().GetRank(),
        g_StdFontColor,
        686.0 / 1024.0,
        194.0 / 768.0,
        310.0 / 1024.0,
        25.0 / 768.0
      );
    GetObject("PLAYER_RANK").SetAlignment(ALIGNMENT_RIGHT);

    // medals
    for (int CmpIndex = 0; CmpIndex < 4; CmpIndex++)
    {
      Array Status = GetCurrentPlayer().GetCampaignStatus(CmpIndex);
      for (int DiffIndex = 0; DiffIndex < 4; DiffIndex++)
        if (Status[DiffIndex])
        {
          String MedalID = "PLAYER_MEDAL_" + new String(CmpIndex) + "_" + new String(DiffIndex);
          CreateBitmapControl(
              MedalID,
              m_Medals[CmpIndex][DiffIndex],
              (688.0 + 77.0 * float(CmpIndex)) / 1024.0,
              (227.0 + 21.0 * float(DiffIndex)) / 768.0,
              77.0 / 1024.0,
              21.0 / 768.0
            );
        }
    }

    Array PlayerStatTitle = CCommonStrings::StatisticsTitle;
    Array PlayerStatValue = [
        new WString(GetCurrentPlayer().GetMissionsCompleted()) + L"/30",
        new WString(GetCurrentPlayer().GetCampaignsCompleted()) + L"/4",
        new WString(GetCurrentPlayer().GetGroundUnitsDestroyed()),
        new WString(GetCurrentPlayer().GetAirUnitsDestroyed()),
        new WString(GetCurrentPlayer().GetNavalUnitsDestroyed()),
        new WString(GetCurrentPlayer().GetMultiplayerDeath()),
        new WString(GetCurrentPlayer().GetMultiplayerKills())
      ];


    float LineY = 336.0 / 768.0;

    for (int i = 0; i < PlayerStatTitle.size(); i++)
    {
      float  ItemHeight = 25.0 / 768.0;
      String LineID = new String(i);

      float  TextShiftX = 3.0 / 1024.0;

      //  stat value box
      CreateItemBackground(
        "PLAYER_STAT_BACK_" + LineID,
        928.0 / 1024.0,
        LineY,
        76.0 / 1024.0,
        ItemHeight + 2.0 / 768.0
      );

      CreateTextControl(
          "PLAYER_STAT_VALUE_" + LineID,
          g_StdFontName,
          g_StdFontSize,
          PlayerStatValue[i],
          g_StdFontColor,
          928.0 / 1024.0,
          LineY,
          76.0 / 1024.0,
          ItemHeight
        );
      GetObject("PLAYER_STAT_VALUE_" + LineID).SetAlignment(ALIGNMENT_CENTER);

      CreateTextControl(
          "PLAYER_STAT_TITLE_" + LineID,
          g_StdFontName,
          g_StdFontSize,
          PlayerStatTitle[i],
          g_StdFontColor,
          680.0 / 1024.0 + TextShiftX,
          LineY + (ItemHeight - g_StdFontSize) /2,
          235.0 / 1024.0,
          ItemHeight
        );

      LineY  = LineY + ItemHeight + 14.0 / 768.0;
    }

    CreateInterlacedButtonBackground(
      "BUTTONBLOCKBACK2",
      618.0 / 1024.0,
      623.0 / 768.0,
      396.0 / 1024.0,
       40.0  / 768.0,
      true
    );

    String ActionString        = "ID_ABORT";
    String ButtonName          = "abort button"; //"end mission button";
    String RestartActionString = "ID_RESTART_NEW";

    if (_EntryMission)
    {
      // check mission complete
      boolean bMissionComplete = true;
      for (int i = 0; i < m_Statuses.size(); i++)
        bMissionComplete &= (m_Statuses[i] == str_ObjectiveComplete);

      // draw end mission button if mission is complete
      if (bMissionComplete)
      {
        ActionString = "ID_END_MISSION";
        ButtonName   = "end mission button";
      }
      else
      {
        ActionString = "ID_ABORT";
        ButtonName   = "abort button";
      }

      RestartActionString = "ID_RESTART";
    }

    // abort / continue / end mission button
    CreateAudibleButton(
        ActionString,
        ButtonName,
        ButtonName + " hilited",
        L"",
        (1014.0 - float(MatManager.GetMaterialWidth(ButtonName))) / 1024.0,
        623.0 / 768.0,
        float(MatManager.GetMaterialWidth(ButtonName)) / 1024.0,
        40.0  / 768.0
      );

    Component Game = new #GameController();
    String GameMode = Game.GetGameMode();
    Component Session = Game.GetGameSession();

    boolean ShowRestart = true;

    if (GameMode == "Client")
    {
      if (CServerDescriptor::ST_Cooperative == Session.GetSessionInfo().GetSessionTypeID())
        ShowRestart = false;
    }

    // no restart button for client in cooperative
    if (ShowRestart)
    {
      // RESTART
      CreateAudibleButton(
          RestartActionString,
          "restart button",
          "restart button hilited",
          L"",
          (1014.0 - float(MatManager.GetMaterialWidth(ButtonName)) - float(MatManager.GetMaterialWidth("restart button"))) / 1024.0,
          623.0 / 768.0,
          float(MatManager.GetMaterialWidth("restart button")) / 1024.0,
          40.0  / 768.0
        );
    }
  }

  int m_CurrentBriefingPage;

  void SwitchToBriefingPage(int _Page)
  {
    Component Mission = new #GameController().GetMission();

    // Getting the name of a current mission
    m_MissionName = Mission.str_MapName;

    if (_Page < 0) _Page = 0;
    if (_Page > Mission.m_BriefingTexts.size() - 1)
      _Page = Mission.m_BriefingTexts.size() - 1;

    m_CurrentBriefingPage = _Page;
    GetObject("ID_BRIEFING_PREV").SetDisabled(_Page == 0);
    GetObject("ID_BRIEFING_NEXT").SetDisabled(_Page == Mission.m_BriefingTexts.size() - 1);
    GetObject("ID_BRIEFING_TEXT").SetText(Mission.m_BriefingTexts[_Page]);

    GetObject("Background").SetMaterial(Mission.m_BriefingMaterials[_Page]);
    //GetObject("ID_BRIEFING_PICTURE").SetMaterial(Mission.m_BriefingMaterials[_Page]);
  }

  void OnButtonActivated(String _ID)
  {
    int audibleID = 0;
    Component button = GetObject(_ID);
    if (button != null)
    {
      audibleID = button.GetAudibleID();
    }

    // handle system control interaction
    BaseMenu_OnButtonActivated(_ID);
    MultiplayerMenu_OnButtonActivated(_ID);
    EnvironmentMenu_OnButtonActivated(_ID);
    SettingsMenu_OnButtonActivated(_ID);
    PlayerMenu_OnButtonActivated(_ID);
    CampaignMenu_OnButtonActivated(_ID);

    //
    //  quit
    //
    if (_ID == "ID_QUIT_MESSAGE")
    {
//      MenuSoundToPlay = m_MenuSoundExit;
      OpenWindow("CreateQuitWindow");
    }

    if (_ID == "ID_QUIT")
    {
      new #GameController().QuitGame();
    }

    if (_ID == "QUIT_MESSAGE_NO")
      CloseWindow();

    if (_ID == "ID_BACK")
    {
      // return to prev window
      GoBack();
    }

    if (_ID == "ID_BACK_ESCMENU")
    {
      (new #GameController()).DisableMissionMenu();
    }

    //
    //  Escape menu
    //

    if (_ID == "ID_RESTART_NEW")
    {
      GoTo("LoadingMenu");
      new #GameController().LoadSinglePlayerMission(m_LastLoadedMission);
      setVisibleState(new #GameController().GetMission(), false);
      GoTo("BriefingMenu");
    }

    if (_ID == "ID_RESTART")
    {
      Component Game = new #GameController();

      Game.DisableMissionMenu();
      Game.EnableControl("MainMenu", true);
      GoTo("LoadingMenu");
      if (Game.ReloadMission())
      {
        setVisibleState(Game.GetMission(), false);
        GoTo("EnvironmentMenu");
      }
    }

    if (_ID == "ID_ABORT")
    {
      m_bIsCanAutoUpdate = false;

      // unload mission music
      new #MusicController().UnloadMissionMusic();

      Component Game = new #GameController();
      Game.DisableMissionMenu();

      Component Session = Game.GetGameSession();
      if (Session != null)
      {
        if (CServerDescriptor::ST_DeathMatch == Session.GetSessionInfo().GetSessionTypeID())
        {
          Component Mission = Game.GetMission();
          if (Mission != null)
            Mission.OnPlayerExitedGame();
        }

        Session.DeCachePlayingPlayersInfo();
        Session.CleanupPlayersInfo();
      }

      String GameMode = Game.GetGameMode();
      Game.UnloadMission();

      Game.EnableControl("MainMenu", true);

      if (GameMode == "Single")
      {
        GoTo("GameMenu");
        Invoke("Campaign" + m_LastLoadedMission.GetChar(2) + "MenuEntry2");
      }
      else
      {
        GoTo("MultiplayerMenu");
      }
    }

    if (_ID == "ID_END_MISSION")
    {
      (new #GameController()).DisableMissionMenu();

      String GameMode = (new #GameController()).GetGameMode();
      Component Mission = new #GameController().GetMission();
      if (Mission != null)
        Mission.CloseMission(false);
      else
        GoTo("MultiplayerMenu");
    }

    if (_ID == "ID_DEBRIEFING_ABORT")
    {
      GoTo("Init");
    }

    if (_ID == "ID_DEBRIEFING_AGAIN")
    {
      Component Session = (new #GameController()).GetGameSession();
      if (Session != null)
      {
        if ((new #GameController()).GetGameMode() == "Server")
          Session.FireNetEvent("Client_EnableTeamAssaultContinue", [true]);

        Session.DeCachePlayingPlayersInfo();
      }

      GoTo("EnvironmentMenu");
    }

    if (_ID == "ID_DEBRIEFING_RESTART")
    {
      GoTo("LoadingMenu");

      new #GameController().LoadSinglePlayerMission(m_LastLoadedMission);

      GoTo("EnvironmentMenu");
    }

    if (_ID.IsStartsWith("PLAYERS_FRAME_"))
    {
      String FieldId = _ID.GetPartFollowing("PLAYERS_FRAME_");
      if (GetObject("PLAYERS_NAME_" + FieldId).GetText() != L"")
      {
        m_SelectedPlayersSlot = m_StartPlayersSlot + FieldId.ConvertToInt();
        UpdatePlayersStatList();
        UpdateMultiPlayerStat();
      }
    }

    if (_ID == "PLAYERS_ARROW_UP")
    {
      m_StartPlayersSlot--;
      UpdatePlayersStatList();
    }

    if (_ID == "PLAYERS_ARROW_DOWN")
    {
      m_StartPlayersSlot++;
      UpdatePlayersStatList();
    }


    //
    //  Environment menu
    //

    if (_ID == "ID_DONE")
    {
      m_bIsPlayerDied    = false;
      m_bIsCanAutoUpdate = false;

      Component Game    = new #GameController();
      Component Mission = Game.GetMission();

      // unload menu music
      new #MusicController().UnloadMenuMusic();

      String PlayerID  = Mission.GetMainPlayerObjectID();

      // create player objects
      Mission.CreatePlayerObjects(PlayerID);

      // show mission
      setVisibleState(Mission, true);

      // activate start mission task
      sendEvent(0.5, SOID_MissionController, "StartMission", []);

      // send event to activate player object
      sendEvent(0.5, SOID_MissionController, "StartPlay", []);
    }

    //
    //  Briefing menu
    //

    if (_ID == "ID_BRIEFING_PREV")
    {
      m_CurrentBriefingPage--;
      SwitchToBriefingPage(m_CurrentBriefingPage);
    }

    if (_ID == "ID_BRIEFING_NEXT")
    {
      m_CurrentBriefingPage++;
      SwitchToBriefingPage(m_CurrentBriefingPage);
    }

    //  Play only if button is audible.
    if (audibleID != 0)
    {
      m_MenuSoundPushButton.PlaySoundBuffer(false);
    }
  }


  void ContinueRestart()
  {
    setVisibleState((new #GameController()).GetMission(), false);
    GoTo("EnvironmentMenu");
  }


  void OnButtonHilited(String _ID)
  {
    //  Seems that OnButtonHilited/OnButtonDehilited are sent every tick.
    Component button = GetObject(_ID);
    int audibleID = button.GetAudibleID();

    if ((audibleID != 0) && (m_HilitedAudibleID != audibleID) && (!button.IsHilited()))
    {
      m_MenuSoundMouseOver.PlaySoundBuffer(false);
      m_HilitedAudibleID = audibleID;
    }

    BaseMenu_OnButtonHilited(_ID);
    MultiplayerMenu_OnButtonHilited(_ID);
    PlayerMenu_OnButtonHilited(_ID);
    CampaignMenu_OnButtonHilited(_ID);
  }

  void OnButtonDehilited(String _ID)
  {
    m_HilitedAudibleID = 0;

    BaseMenu_OnButtonDehilited(_ID);
    MultiplayerMenu_OnButtonDehilited(_ID);
    PlayerMenu_OnButtonDehilited(_ID);
    CampaignMenu_OnButtonDehilited(_ID);
  }

  //
  //  helper methods
  //

  void CreateBackButton()
  {
    //
    //  Back button
    //

    CreateInterlacedButtonBackground(
      "BACKBACK",
      728.0 / 1024.0,
      687.0 / 768.0,
      285.0 / 1024.0,
      40.0  / 768.0,
      true
    );

    CreateAudibleButton(
      "ID_BACK",
      "back button right aligned",
      "back button right aligned hilited",
      L"",
      (728.0 + 285.0 - float(MatManager.GetMaterialWidth("back button right aligned"))) / 1024.0,
      687.0 / 768.0,
      float(MatManager.GetMaterialWidth("back button right aligned")) / 1024.0,
      40.0  / 768.0
    );
  }

  void CreateItemBackground(
         String  _ID,
         float   _X,
         float   _Y,
         float   _Width,
         float   _Height
       )
  {
    CreateBitmapControl(
      _ID + "_ITEM_BACKGROUND_LEFT",
      "edit box background",
      _X,
      _Y,
      _Width,
      _Height
    );

    CreateStaticFrame(
      _ID + "_ITEM_BACKGROUND_FRAME",
      StdFrameColor,
      _X,
      _Y,
      _Width,
      _Height
    );
  }

  void UnregisterItemBackground(
      String _ID
    )
  {
    UnregisterObject(_ID + "_ITEM_BACKGROUND_LEFT");
    UnregisterObject(_ID + "_ITEM_BACKGROUND_FRAME");
  }

  void CreateInterlacedButtonBackground(
         String  _ID,
         float   _X,
         float   _Y,
         float   _Width,
         float   _Height,
         boolean _LeftToRight
       )
  {
    float StubPartWidth     = 3.0 / 1024.0;
    float Delta             = 2.0 / 1024.0;
    float GradientPartWidth = 150.0 / 1024.0;

    if (_Width < GradientPartWidth + StubPartWidth)
    {
      GradientPartWidth = _Width / 2.0;
    }

    if (_LeftToRight)
    {
      // gradient part
      CreateBitmapControl(
        _ID + "_BUTTON_BACKGROUND_GRAD",
        "button background right",
        _X,
        _Y,
        GradientPartWidth + Delta,
        _Height,
        true,
        false
      );
      // scaled constant transparency part
      CreateBitmapControl(
        _ID + "BUTTON_BACKGROUND_CONST",
        "button background middle",
        _X + GradientPartWidth,
        _Y,
        _Width - GradientPartWidth - StubPartWidth,
        _Height
      );
      // stub part
      CreateBitmapControl(
        _ID + "BUTTON_BACKGROUND_STUB",
        "button background left",
        _X + _Width - StubPartWidth - Delta,
        _Y,
        StubPartWidth + Delta,
        _Height,
        true,
        false
      );
    }
    else
    {
      // stub part
      CreateBitmapControl(
        _ID + "_BUTTON_BACKGROUND_STUB",
        "button background left",
        _X,
        _Y,
        StubPartWidth + Delta,
        _Height
      );
      // scaled constant transparency part
      CreateBitmapControl(
        _ID + "BUTTON_BACKGROUND_CONST",
        "button background middle",
        _X + StubPartWidth,
        _Y,
        _Width - GradientPartWidth - StubPartWidth,
        _Height
      );
      // stub part
      CreateBitmapControl(
        _ID + "BUTTON_BACKGROUND_GRAD",
        "button background right",
        _X + _Width - GradientPartWidth - Delta,
        _Y,
        GradientPartWidth + Delta,
        _Height
      );
    }
  }

  void CreateGroupBackground(
         String  _ID,
         WString _Caption,
         float   _X,
         float   _Y,
         float   _Width,
         float   _Height
       )
  {
    float CaptionHeight     = 28.0 / 768.0;
    float CaptionTextShiftX = 10.0 / 1024.0;

    // caption
    CreateBitmapControl(
      _ID + "_CAPTION_BACK",
      "caption",
      _X,
      _Y,
      _Width,
      CaptionHeight
    );

    // group background

    CreateBitmapControl(
      _ID + "_GROUP_BACK",
      "background",
      _X,
      _Y + CaptionHeight,
      _Width,
      _Height - CaptionHeight
    );

    // caption text

    CreateTextControl(
      _ID + "_CAPTION_TEXT",
      g_StdFontName,
      g_StdFontSize,
      _Caption,
      g_StdFontColor,
      _X + CaptionTextShiftX,
      _Y + (CaptionHeight - g_StdFontSize) / 2.0,
      _Width,
      CaptionHeight
    );

    // group frame
    CreateStaticFrame(
        _ID + "_GROUP_FRAME",
        StdFrameColor,
        _X,
        _Y,
        _Width,
        _Height
      );
  }

  void UnregisterGroupBackground(
      String _ID
    )
  {
    UnregisterObject(_ID + "_CAPTION_BACK");
    UnregisterObject(_ID + "_GROUP_BACK");
    UnregisterObject(_ID + "_CAPTION_TEXT");
    UnregisterObject(_ID + "_GROUP_FRAME");
  }

  void CreatePlayersStatList()
  {
    float X = 12.0 / 1024.0;
    float Y = 194.0 / 768.0;

    // players list background
    CreateGroupBackground(
       "PLAYERS_LIST_BACK",
       getLocalized("Menu", "PlayerListBack"),
       X,
       Y,
       653.0 / 1024.0,
       370.0 / 768.0
     );

    CreateTextControl(
        "PLAYERS_KILLS_TITLE",
        g_StdFontName,
        g_StdFontSize,
        getLocalized("Menu", "PlayerKillsTitle"),
        g_StdFontColor,
        382.0 / 1024.0,
        209.0 / 768.0 - g_StdFontSize * 0.5,
        200.0 / 1024.0,
        30.0 / 768.0
      );

      CreateTextControl(
        "PLAYERS_DEATHS_TITLE",
        g_StdFontName,
        g_StdFontSize,
        getLocalized("Menu", "PlayerDeathsTitle"),
        g_StdFontColor,
        459.0 / 1024.0,
        209.0 / 768.0 - g_StdFontSize * 0.5,
        200.0 / 1024.0,
        30.0 / 768.0
      );

      CreateTextControl(
        "PLAYERS_FRAGS_TITLE",
        g_StdFontName,
        g_StdFontSize,
        getLocalized("Menu", "PlayerFragsTitle"),
        g_StdFontColor,
        560.0 / 1024.0,
        209.0 / 768.0 - g_StdFontSize * 0.5,
        200.0 / 1024.0,
        30.0 / 768.0
      );

    //
    //  player list
    //
    for (int i = 0; i < 5; i++)
    {
      String  FieldID = new String(i);
      float   LineY = Y + (36.0 + float(i) * 67.0) / 768.0;

      CreateBitmapControl(
          "PLAYERS_BACK_" + FieldID,
          "background",
          X + 6.0 / 1024.0,
          LineY,
          614.0 / 1024.0,
          22.0 / 768.0
        );

      CreateAudibleStaticFrame(
          "PLAYERS_FRAME_" + FieldID,
          StdFrameColor,
          X + 6.0 / 1024.0,
          LineY,
          614.0 / 1024.0,
          59.0 / 768.0
        );

      CreateBitmapControl(
          "PLAYERS_BOX_" + FieldID,
          "box",
          X + 6.0 / 1024.0,
          LineY - 6.0 / 768.0,
          23.0 / 1024.0,
          36.0 / 768.0
        );

      CreateTextButton(
          "PLAYERS_NAME_" + FieldID,
          g_StdFontName,
          g_StdFontSize,
          L"",
          X + 28.0 / 1024.0,
          LineY,
          220.0 / 1024.0,
          20.0 / 768.0
        );
      GetObject("PLAYERS_NAME_" + FieldID).SetAlignment(ALIGNMENT_LEFT);

      CreateTextControl(
          "PLAYERS_RANK_" + FieldID,
          g_StdFontName,
          g_StdFontSize,
          L"",
          g_StdFontColor,
          X + 6.0 / 1024.0,
          LineY - g_StdFontSize * 0.5 + 40.0 / 768.0,
          310.0 / 1024.0,
          20.0 / 768.0
          );
      GetObject("PLAYERS_RANK_" + FieldID).SetAlignment(ALIGNMENT_RIGHT);

      CreateButton(
          "PLAYERS_KILLS_" + FieldID,
          "edit box background",
          "edit box background",
          L"",
          X + 351.0 / 1024.0,
          LineY + 28.0 / 768.0,
          74.0 / 1024.0,
          24.0 / 768.0
        );
      GetObject("PLAYERS_KILLS_" + FieldID).SetFrameColors(StdFrameColor, StdFrameColor);
      CreateTextControl(
          "PLAYERS_KILLS_TEXT_" + FieldID,
          g_StdFontName,
          g_StdFontSize,
          L"",
          g_StdFontColor,
          X + 351.0 / 1024.0,
          LineY + 28.0 / 768.0,
          74.0 / 1024.0,
          24.0 / 768.0
        );
      GetObject("PLAYERS_KILLS_TEXT_" + FieldID).SetAlignment(ALIGNMENT_CENTER);
      CreateButton(
          "PLAYERS_DEATHS_" + FieldID,
          "edit box background",
          "edit box background",
          L"",
          X + 444.0 / 1024.0,
          LineY + 28.0 / 768.0,
          74.0 / 1024.0,
          24.0 / 768.0
        );
      GetObject("PLAYERS_DEATHS_" + FieldID).SetFrameColors(StdFrameColor, StdFrameColor);
      CreateTextControl(
          "PLAYERS_DEATHS_TEXT_" + FieldID,
          g_StdFontName,
          g_StdFontSize,
          L"",
          g_StdFontColor,
          X + 444.0 / 1024.0,
          LineY + 28.0 / 768.0,
          74.0 / 1024.0,
          24.0 / 768.0
        );
      GetObject("PLAYERS_DEATHS_TEXT_" + FieldID).SetAlignment(ALIGNMENT_CENTER);
      CreateButton(
          "PLAYERS_FRAGS_" + FieldID,
          "edit box background",
          "edit box background",
          L"",
          X + 537.0 / 1024.0,
          LineY + 28.0 / 768.0,
          74.0 / 1024.0,
          24.0 / 768.0
        );
      GetObject("PLAYERS_FRAGS_" + FieldID).SetFrameColors(StdFrameColor, StdFrameColor);
      CreateTextControl(
          "PLAYERS_FRAGS_TEXT_" + FieldID,
          g_StdFontName,
          g_StdFontSize,
          L"",
          g_StdFontColor,
          X + 537.0 / 1024.0,
          LineY + 28.0 / 768.0,
          74.0 / 1024.0,
          24.0 / 768.0
        );
      GetObject("PLAYERS_FRAGS_TEXT_" + FieldID).SetAlignment(ALIGNMENT_CENTER);

    }

    // create players combo box
    CreateBitmapControl(
        "PLAYERS_COMBO_BACKGROUND",
        "background",
        X + 624.0 / 1024.0,
        Y + 36.0 / 768.0,
        25.0 / 1024.0,
        326.0 / 768.0
      );

    CreateAudibleButton(
        "PLAYERS_ARROW_UP",
        "arrow up",
        "arrow up",
        L"",
        X + 618.0 / 1024.0,
        Y + 33.0 / 768.0,
        37.0 / 1024.0,
        41.0 / 768.0
      );

    CreateAudibleButton(
        "PLAYERS_ARROW_DOWN",
        "arrow down",
        "arrow down",
        L"",
        X + 618.0 / 1024.0,
        Y + 326.0 / 768.0,
        37.0 / 1024.0,
        41.0 / 768.0
      );

    // slider
    CreateBitmapControl(
        "PLAYERS_SLIDER",
        "scroll bar slider",
        X + 618.0 / 1024.0,
        Y + 324.0 / 768.0,
        37.0 / 1024.0,
        18.0 / 768.0
      );
  }

  void UpdatePlayersStatList()
  {
    float X = 12.0 / 1024.0;
    float Y = 194.0 / 768.0;

    Component GameSession = (new #GameController()).GetGameSession();
    Array PlayersInfo = GameSession.GetPlayingPlayersInfo();

    // correct m_StartPlayersSlot
    if (m_StartPlayersSlot + 5 >= PlayersInfo.size())
      m_StartPlayersSlot = PlayersInfo.size() - 5;
    if (m_StartPlayersSlot < 0)
      m_StartPlayersSlot = 0;

    // Массив номеров игроков, отсортированных по количеству фрагов
    Array PlayerIndices;
    for (int i = 0; i < PlayersInfo.size(); i++)
      PlayerIndices.add(i);

    // Сортируем его пузырьком:)
    for (int i = 0; i < PlayersInfo.size() - 1; i++)
    for (int j = 0; j < PlayersInfo.size() - 1 - i; j++)
    {
      int Frags1 = PlayersInfo[PlayerIndices[j + 1]].SessionFrags;
      int Frags2 = PlayersInfo[PlayerIndices[j]].SessionFrags;
      if (Frags1 > Frags2)
      {
        int nTemp = PlayerIndices[j];
        PlayerIndices[j] = PlayerIndices[j + 1];
        PlayerIndices[j + 1] = nTemp;
      }
    }

    for (int i = 0; i < 5; i++)
    {
      String  FieldID = new String(i);
      boolean IsEnabled = (i + m_StartPlayersSlot < PlayersInfo.size());
      boolean IsSelected = (i + m_StartPlayersSlot == m_SelectedPlayersSlot);

      // set default values
      WString PlayerName;
      WString PlayerRank;
      WString PlayerKills;
      WString PlayerDeaths;
      WString PlayerFrags;
      Color   BoxColor    = StdDisabledColor;
      Color   FrameColor  = StdDisabledColor;
      Color   HFrameColor = StdDisabledColor;
      Color   NameColor   = g_StdFontColor;

      int nPlayerIndex = PlayerIndices[i + m_StartPlayersSlot];

      if (IsEnabled)
      {
        PlayerName   = PlayersInfo[nPlayerIndex].Name;
        PlayerRank   = PlayersInfo[nPlayerIndex].Rank;
        PlayerKills  = new WString(PlayersInfo[nPlayerIndex].SessionKills);
        PlayerDeaths = new WString(PlayersInfo[nPlayerIndex].SessionDeaths);
        PlayerFrags  = new WString(PlayersInfo[nPlayerIndex].SessionFrags);
        BoxColor     = StdBitmapColor;
        FrameColor   = StdFrameColor;
        HFrameColor  = StdWhiteColor;
      }

      if (IsSelected)
      {
        FrameColor = StdHilitedBitmapColor;
        BoxColor = StdHilitedBitmapColor;
        NameColor = StdHilitedBitmapColor;
      }

      GetObject("PLAYERS_NAME_" + FieldID).SetText(PlayerName);
      GetObject("PLAYERS_NAME_" + FieldID).SetTextColors(NameColor, NameColor);
      GetObject("PLAYERS_BOX_" + FieldID).SetBitmapColor(BoxColor);
      GetObject("PLAYERS_RANK_" + FieldID).SetText(PlayerRank);
      GetObject("PLAYERS_FRAME_" + FieldID).SetFrameColors(FrameColor, HFrameColor);

      GetObject("PLAYERS_KILLS_" + FieldID).EnableBitmap(IsEnabled);
      GetObject("PLAYERS_KILLS_" + FieldID).EnableFrame(IsEnabled);
      GetObject("PLAYERS_KILLS_TEXT_" + FieldID).SetText(PlayerKills);
      GetObject("PLAYERS_DEATHS_" + FieldID).EnableBitmap(IsEnabled);
      GetObject("PLAYERS_DEATHS_" + FieldID).EnableFrame(IsEnabled);
      GetObject("PLAYERS_DEATHS_TEXT_" + FieldID).SetText(PlayerDeaths);
      GetObject("PLAYERS_FRAGS_" + FieldID).EnableBitmap(IsEnabled);
      GetObject("PLAYERS_FRAGS_" + FieldID).EnableFrame(IsEnabled);
      GetObject("PLAYERS_FRAGS_TEXT_" + FieldID).SetText(PlayerFrags);


      //  $TMP: These comments are temporary solution. Must check why this error is logged:
      //  Failed to invoke host function SetDisabled with 1 arguments.

      GetObject("PLAYERS_NAME_" + FieldID).SetDisabled(!IsEnabled);
//      GetObject("PLAYERS_BOX_" + FieldID).SetDisabled(!IsEnabled);
//      GetObject("PLAYERS_RANK_" + FieldID).SetDisabled(!IsEnabled);
      GetObject("PLAYERS_FRAME_" + FieldID).SetDisabled(!IsEnabled);
      GetObject("PLAYERS_KILLS_" + FieldID).SetDisabled(!IsEnabled);
//      GetObject("PLAYERS_KILLS_TEXT_" + FieldID).SetDisabled(!IsEnabled);
      GetObject("PLAYERS_DEATHS_" + FieldID).SetDisabled(!IsEnabled);
//      GetObject("PLAYERS_DEATHS_TEXT_" + FieldID).SetDisabled(!IsEnabled);
      GetObject("PLAYERS_FRAGS_" + FieldID).SetDisabled(!IsEnabled);
//      GetObject("PLAYERS_FRAGS_TEXT_" + FieldID).SetDisabled(!IsEnabled);
    }

    // $TODO fix buggy slider posn
    // update slider position
    if (PlayersInfo.size() < 6)
      GetObject("PLAYERS_SLIDER").SetPosition(X + 618.0 / 1024.0, Y + 63.0 / 768.0);
    else
    {
      float SliderY = Y + (63.0  + 260.0 * (float(m_StartPlayersSlot) / float(PlayersInfo.size() - 5))) / 768.0;
      GetObject("PLAYERS_SLIDER").SetPosition(X + 618.0 / 1024.0, SliderY);
    }
  }

  void UpdateMultiPlayerStat()
  {
    Component GameSession = (new #GameController()).GetGameSession();
    Array PlayersInfo = GameSession.GetPlayingPlayersInfo();

    // medals
    for (int CmpIndex = 0; CmpIndex < 4; CmpIndex++)
      for (int DiffIndex = 0; DiffIndex < 4; DiffIndex++)
      {
        String MedalID = "PLAYERS_MEDAL_" + new String(CmpIndex) + "_" + new String(DiffIndex);
        boolean IsEnabled = false;
        if (m_SelectedPlayersSlot != -1)
        {
          int Status = PlayersInfo[m_SelectedPlayersSlot].CampaignStatus & (1 << (CmpIndex * 4 + DiffIndex));
          IsEnabled = (Status != 0);
        }
        GetObject(MedalID).EnableBitmap(IsEnabled);
      }

    // fill statistics informations
    Array PlayerStatValue;
    WString PlayerRank = L"";
    if (m_SelectedPlayersSlot != -1)
    {
      PlayerStatValue.add(new WString(0) + L"/30");
      PlayerStatValue.add(new WString(0) + L"/4");
      PlayerStatValue.add(new WString(PlayersInfo[m_SelectedPlayersSlot].GroundUnitsDestroyed));
      PlayerStatValue.add(new WString(PlayersInfo[m_SelectedPlayersSlot].AirUnitsDestroyed));
      PlayerStatValue.add(new WString(PlayersInfo[m_SelectedPlayersSlot].NavalUnitsDestroyed));
      PlayerStatValue.add(new WString(PlayersInfo[m_SelectedPlayersSlot].MultiplayerDeath));
      PlayerStatValue.add(new WString(PlayersInfo[m_SelectedPlayersSlot].MultiplayerKills));
      PlayerRank = PlayersInfo[m_SelectedPlayersSlot].Rank;
    }
    else
    {
      PlayerStatValue.add(L"");
      PlayerStatValue.add(L"");
      PlayerStatValue.add(L"");
      PlayerStatValue.add(L"");
      PlayerStatValue.add(L"");
      PlayerStatValue.add(L"");
      PlayerStatValue.add(L"");
    }

    GetObject("PLAYERS_RANK").SetText(PlayerRank);
    for (int i = 2; i < PlayerStatValue.size(); i++)
      GetObject("PLAYERS_STAT_VALUE_" + new String(i)).SetText(PlayerStatValue[i]);
  }

  void MultiplayerDebriefingMenu()
  {
    GetObject("Cursor").Enable(true);

    // $TMP need set backgound
    CreateBackground("muliplayer debriefing menu background");

    boolean ShowContinue = false;

    Component Session = (new #GameController()).GetGameSession();
    if (Session != null)
    {
      if (CServerDescriptor::ST_CaptureTheFlag == Session.GetSessionInfo().GetSessionTypeID())
        ShowContinue = true;
    }

    CreateMissionMultiplayerStatistics(ShowContinue);

//    SetDefaultCommands("", "ID_BACK_ESCMENU");
  }

  void CreateMissionMultiplayerStatistics()
  {
    CreateMissionMultiplayerStatistics(false);
  }

  void CreateMissionMultiplayerStatistics(boolean _ShowContinue)
  {
    Component GameSession = (new #GameController()).GetGameSession();
    Component SessionInfo = GameSession.GetSessionInfo();
    Component MissionDescriptor = createScriptClass(SessionInfo.GetMissionName() + "Descriptor");

    // session information
    CreateItemBackground(
        "SESSION_INFORMATION_BACK",
        12.0 / 1024.0,
        149.0 / 768.0,
        1002.0 / 1024.0,
        36.0 / 768.0
      );
    CreateTextControl(
        "SESSION_NAME",
        g_StdFontName,
        g_StdFontSize,
        SessionInfo.GetSessionName(),
        g_StdFontColor,
        23.0 / 1024.0,
        167.0 / 768.0 - g_StdFontSize * 0.5,
        400.0 / 1024.0,
        36.0 / 768.0
      );
    CreateTextControl(
        "SESSION_TYPE_TEXT",
        g_StdFontName,
        g_StdFontSize,
        CCommonStrings::str_ServerType,
        g_StdFontColor,
        378.0 / 1024.0,
        167.0 / 768.0 - g_StdFontSize * 0.5,
        400.0 / 1024.0,
        36.0 / 768.0
      );
    CreateItemBackground(
        "SESSION_TYPE_BACK",
        431.0 / 1024.0,
        155.0 / 768.0,
        224.0 / 1024.0,
        24.0 / 768.0
      );
    CreateTextControl(
        "SESSION_TYPE",
        g_StdFontName,
        g_StdFontSize,
        CCommonStrings::MultiplayerGameType[SessionInfo.m_Descriptor.SessionType],
        g_StdFontColor,
        431.0 / 1024.0,
        167.0 / 768.0 - g_StdFontSize * 0.5,
        224.0 / 1024.0,
        24.0 / 768.0
      );
    GetObject("SESSION_TYPE").SetAlignment(ALIGNMENT_CENTER);
    CreateTextControl(
        "SESSION_MAP_TEXT",
        g_StdFontName,
        g_StdFontSize,
        CCommonStrings::Str_ServerMap,
        g_StdFontColor,
        682.0 / 1024.0,
        167.0 / 768.0 - g_StdFontSize * 0.5,
        400.0 / 1024.0,
        36.0 / 768.0
      );
    CreateItemBackground(
        "SESSION_MAP_BACK",
        735.0 / 1024.0,
        155.0 / 768.0,
        274.0 / 1024.0,
        24.0 / 768.0
      );
    CreateTextControl(
        "SESSION_MAP",
        g_StdFontName,
        g_StdFontSize,
        MissionDescriptor.GetMapName(),
        g_StdFontColor,
        735.0 / 1024.0,
        167.0 / 768.0 - g_StdFontSize * 0.5,
        274.0 / 1024.0,
        24.0 / 768.0
      );
    GetObject("SESSION_MAP").SetAlignment(ALIGNMENT_CENTER);

    m_StartPlayersSlot = 0;
    m_SelectedPlayersSlot = -1;

    CreatePlayersStatList();
    UpdatePlayersStatList();

    // player statistics background
    CreateGroupBackground(
        "PLAYERS_STATISTICS_BACK",
        CCommonStrings::str_PlayerStatistics,
        671.0 / 1024.0,
        194.0 / 768.0,
        342.0 / 1024.0,
        370.0 / 768.0
      );

    // player rank
    CreateItemBackground(
        "PLAYERS_RANK_BACKGROUND",
        680.0 / 1024.0,
        232.0 / 768.0,
        324.0 / 1024.0,
        130.0  / 768.0
      );
    CreateTextControl(
        "PLAYERS_RANK_TITLE",
        g_StdFontName,
        g_StdFontSize,
        CCommonStrings::str_Rank,
        g_StdFontColor,
        686.0 / 1024.0,
        239.0 / 768.0,
        310.0 / 1024.0,
        25.0 / 768.0
      );
    CreateTextControl(
        "PLAYERS_RANK",
        g_StdFontName,
        g_StdFontSize,
        L"",
        g_StdFontColor,
        686.0 / 1024.0,
        239.0 / 768.0,
        310.0 / 1024.0,
        25.0 / 768.0
      );
    GetObject("PLAYERS_RANK").SetAlignment(ALIGNMENT_RIGHT);

    // medals
    for (int CmpIndex = 0; CmpIndex < 4; CmpIndex++)
      for (int DiffIndex = 0; DiffIndex < 4; DiffIndex++)
      {
        String MedalID = "PLAYERS_MEDAL_" + new String(CmpIndex) + "_" + new String(DiffIndex);
        CreateButton(
            MedalID,
            m_Medals[CmpIndex][DiffIndex],
            m_Medals[CmpIndex][DiffIndex],
            L"",
            (688.0 + 77.0 * float(CmpIndex)) / 1024.0,
            (272.0 + 21.0 * float(DiffIndex)) / 768.0,
            77.0 / 1024.0,
            21.0 / 768.0
          );
        GetObject(MedalID).SetTextColors(StdBitmapColor, StdBitmapColor);
      }

    Array PlayerStatTitle = CCommonStrings::StatisticsTitle;

    float LineY = 373.0 / 768.0;

    for (int i = 2; i < PlayerStatTitle.size(); i++)
    {
      float  ItemHeight = 25.0 / 768.0;
      String LineID = new String(i);

      float  TextShiftX = 3.0 / 1024.0;

      //  stat value box
      CreateItemBackground(
        "PLAYERS_STAT_BACK_" + LineID,
        928.0 / 1024.0,
        LineY,
        76.0 / 1024.0,
        ItemHeight + 2.0 / 768.0
      );

      CreateTextControl(
          "PLAYERS_STAT_VALUE_" + LineID,
          g_StdFontName,
          g_StdFontSize,
          L"",
          g_StdFontColor,
          928.0 / 1024.0,
          LineY,
          76.0 / 1024.0,
          ItemHeight
        );
      GetObject("PLAYERS_STAT_VALUE_" + LineID).SetAlignment(ALIGNMENT_CENTER);

      CreateTextControl(
          "PLAYERS_STAT_TITLE_" + LineID,
          g_StdFontName,
          g_StdFontSize,
          PlayerStatTitle[i],
          g_StdFontColor,
          680.0 / 1024.0 + TextShiftX,
          LineY + (ItemHeight - g_StdFontSize) /2,
          235.0 / 1024.0,
          ItemHeight
        );

      LineY  = LineY + ItemHeight + 14.0 / 768.0;
    }

    UpdateMultiPlayerStat();

    if (_ShowContinue)
    {
      CreateInterlacedButtonBackground(
          "BACKBACK",
          728.0 / 1024.0,
          687.0 / 768.0,
          285.0 / 1024.0,
          40.0  / 768.0,
          true
        );

      CreateAudibleButton(
          "ID_DEBRIEFING_AGAIN",
          "continue button",
          "continue button hilited",
          L"",
          (728.0 + 285.0 - float(MatManager.GetMaterialWidth("continue button"))) / 1024.0,
          687.0 / 768.0,
          float(MatManager.GetMaterialWidth("continue button")) / 1024.0,
          41.0  / 768.0
        );

      if ((new #GameController()).GetGameMode() != "Server")
        GetObject("ID_DEBRIEFING_AGAIN").SetDisabled(true);
    }

    CreateInterlacedButtonBackground(
      "BUTTONBLOCKBACK2",
      780.0 / 1024.0,
      623.0 / 768.0,
      234.0 / 1024.0,
       40.0 / 768.0,
      true
    );

    // ABORT
    CreateAudibleButton(
      "ID_ABORT",
      "abort button",
      "abort button hilited",
      L"",
      (723.0 + MatManager.GetMaterialWidth("abort button")) / 1024.0,
      623.0 / 768.0,
      float(MatManager.GetMaterialWidth("abort button")) / 1024.0,
      40.0  / 768.0
    );

    CreateItemBackground(
        "ID_MESSAGES_BACK",
        12.0 / 1024.0,
        576.0 / 768.0,
        1002.0 / 1024.0,
        36.0 / 768.0
      );
  }

  void SetPlayerDied()
  {
    m_bIsPlayerDied = true;
  }
}
