text: /*  Starshatter OpenSource Distribution
      Copyright (c) 1997-2004, Destroyer Studios LLC.
      All Rights Reserved.
  
      Redistribution and use in source and binary forms, with or without
      modification, are permitted provided that the following conditions are met:
  
      * Redistributions of source code must retain the above copyright notice,
        this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above copyright notice,
        this list of conditions and the following disclaimer in the documentation
        and/or other materials provided with the distribution.
      * Neither the name "Destroyer Studios" nor the names of its contributors
        may be used to endorse or promote products derived from this software
        without specific prior written permission.
  
      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
      AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
      LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
      CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      POSSIBILITY OF SUCH DAMAGE.
  
      SUBSYSTEM:    Stars.exe
      FILE:         Starshatter.cpp
      AUTHOR:       John DiCamillo
  
  */
  
  
  #include "MemDebug.h"
  #include "Starshatter.h"
  
  #include "MenuScreen.h"
  #include "LoadScreen.h"
  #include "PlanScreen.h"
  #include "CmpnScreen.h"
  
  #include "AudioConfig.h"
  #include "MusicDirector.h"
  #include "HUDSounds.h"
  #include "Player.h"
  
  #include "Shot.h"
  #include "Drive.h"
  #include "LandingGear.h"
  #include "Explosion.h"
  #include "FlightDeck.h"
  #include "NavLight.h"
  #include "Debris.h"
  #include "Contact.h"
  #include "QuantumDrive.h"
  #include "Sensor.h"
  #include "Power.h"
  #include "SystemDesign.h"
  #include "WeaponDesign.h"
  
  #include "Campaign.h"
  #include "CampaignSaveGame.h"
  #include "CombatRoster.h"
  #include "CombatZone.h"
  #include "CampaignPlan.h"
  
  #include "Galaxy.h"
  #include "StarSystem.h"
  #include "Mission.h"
  #include "Sim.h"
  #include "SimEvent.h"
  #include "Element.h"
  #include "Ship.h"
  #include "ShipCtrl.h"
  #include "ShipDesign.h"
  #include "HUDView.h"
  #include "MFD.h"
  #include "RadioMessage.h"
  #include "RadioTraffic.h"
  #include "RadioVox.h"
  #include "CameraDirector.h"
  #include "ModConfig.h"
  #include "KeyMap.h"
  
  #include "GameScreen.h"
  #include "QuantumView.h"
  #include "QuitView.h"
  #include "RadioView.h"
  #include "TacticalView.h"
  #include "DisplayView.h"
  
  #include "LoadDlg.h"
  #include "TacRefDlg.h"
  #include "CmpLoadDlg.h"
  #include "Terrain.h"
  
  #include "NetClientConfig.h"
  #include "NetServerConfig.h"
  #include "NetLayer.h"
  #include "NetLobbyClient.h"
  #include "NetLobbyServer.h"
  #include "NetGame.h"
  #include "NetUtil.h"
  
  #include "ParseUtil.h"
  #include "Token.h"
  
  #include "MachineInfo.h"
  #include "Game.h"
  #include "VideoFactory.h"
  #include "Screen.h"
  #include "Window.h"
  #include "ActiveWindow.h"
  #include "Button.h"
  #include "CameraView.h"
  #include "ImgView.h"
  #include "FadeView.h"
  #include "Color.h"
  #include "Bitmap.h"
  #include "Font.h"
  #include "FontMgr.h"
  #include "Keyboard.h"
  #include "Joystick.h"
  #include "MouseController.h"
  #include "Mouse.h"
  #include "TrackIR.h"
  #include "EventDispatch.h"
  #include "MultiController.h"
  #include "Archive.h"
  #include "DataLoader.h"
  #include "Random.h"
  #include "Resource.h"
  #include "Universe.h"
  #include "Video.h"
  #include "VideoSettings.h"
  #include "WebBrowser.h"
  
  // +--------------------------------------------------------------------+
  
  int            quick_mode = 0;
  char           quick_mission_name[64];
  Mission*       quick_mission = 0;
  
  int            Starshatter::keymap[256];
  int            Starshatter::keyalt[256];
  Starshatter*   Starshatter::instance = 0;
  
  static Mission*   current_mission   = 0;
  static Mission*   cutscene_mission  = 0;
  static double     cutscene_basetime = 0;
  static int        cut_efx_volume    = 100;
  static int        cut_wrn_volume    = 100;
  static double     time_til_change   = 0;
  static bool       exit_latch        = true;
  static bool       show_missions     = false;
  static bool       use_file_system   = false;
  static bool       no_splash         = false;
  
  enum CHAT_MODES {
      CHAT_BROADCAST = 1,
      CHAT_TEAM      = 2,
      CHAT_WING      = 3,
      CHAT_UNIT      = 4
  };
  
  // +--------------------------------------------------------------------+
  
  Starshatter::Starshatter()
  : gamewin(0), menuscreen(0), loadscreen(0), planscreen(0),
  cmpnscreen(0), gamescreen(0), splash(0), splash_index(0), 
  input(0), loader(0), cam_dir(0), music_dir(0),
  field_of_view(2), time_mark(0), minutes(0), 
  player_ship(0), net_lobby(0),
  spinning(false), tactical(false), mouse_x(0), mouse_y(0),
  game_mode(MENU_MODE), mouse_input(0), head_tracker(0),
  terminal(0), verdana(0), limerick18(0), limerick12(0),
  HUDfont(0), GUIfont(0), GUI_small_font(0), title_font(0),
  ocrb(0), req_change_video(0), video_changed(0),
  lens_flare(true), corona(true), nebula(true), dust(0),
  load_step(0), load_progress(0),
  chat_mode(0), exit_time(1.2), cutscene(0)
  {
      if (!instance)
      instance = this;
  
      app_name     = "Starshatter: The Gathering Storm";
      title_text   = "STARSHATTER";
      palette_name = "alpha";
  
      gamma        = 128; // default - flat gamma ramp
  
      if (!DataLoader::GetLoader())
      DataLoader::Initialize();
  
      loader       = DataLoader::GetLoader();
      int loadstat = loader->EnableDatafile("shatter.dat");
  
      if (loadstat != DataLoader::DATAFILE_OK) {
          const char* err_msg = loadstat == DataLoader::DATAFILE_INVALID ?
          "The file 'shatter.dat' appears to have been damaged.  Please re-install Starshatter." :
          "Starshatter cannot open the file 'shatter.dat'.  Please re-install Starshatter.";
  
          ::MessageBox(hwnd, err_msg, "Starshatter - Error", MB_OK);
          ::Print(err_msg);
          ::Print("\n\nFATAL ERROR: EXIT.");
          exit(-1);
      }
  
      if (loader->FindFile("vox.dat"))
      loader->EnableDatafile("vox.dat");
  
      if (loader->FindFile("start.dat"))
      loader->EnableDatafile("start.dat");
  
      if (loader->FindFile("content.dat"))
      loader->EnableDatafile("content.dat");
  
      //if (loadstat != DataLoader::DATAFILE_OK) {
      //    const char* err_msg = loadstat == DataLoader::DATAFILE_INVALID ?
      //    "The file 'content.dat' appears to have been damaged.  Please re-install the latest Starshatter update." :
      //    "Starshatter cannot open the file 'content.dat'.  Please re-install the latest Starshatter update.";
  
      //    ::MessageBox(hwnd, err_msg, "Starshatter - Error", MB_OK);
      //    ::Print(err_msg);
      //    ::Print("\n\nFATAL ERROR: EXIT.");
      //    exit(-1);
      //}
  
      LoadVideoConfig("video.cfg");
  
      // create the fonts
      loader->SetDataPath("Fonts/");
  
      HUDfont = new(__FILE__,__LINE__) Font("HUDfont");
      FontMgr::Register("HUD", HUDfont);
  
      GUIfont = new(__FILE__,__LINE__) Font("GUIfont");
      FontMgr::Register("GUI", GUIfont);
  
      GUI_small_font = new(__FILE__,__LINE__) Font("GUIsmall");
      FontMgr::Register("GUIsmall", GUI_small_font);
  
      limerick12  = new(__FILE__,__LINE__) Font("Limerick12");
      limerick18  = new(__FILE__,__LINE__) Font("Limerick18");
      terminal    = new(__FILE__,__LINE__) Font("Terminal");
      verdana     = new(__FILE__,__LINE__) Font("Verdana");
      ocrb        = new(__FILE__,__LINE__) Font("OCRB");
  
      FontMgr::Register("Limerick12",  limerick12);
      FontMgr::Register("Limerick18",  limerick18);
      FontMgr::Register("Terminal",    terminal);
      FontMgr::Register("Verdana",     verdana);
      FontMgr::Register("OCRB",        ocrb);
  
      loader->SetDataPath(0);
  
      ZeroMemory(keymap, sizeof(keymap));
      ZeroMemory(keyalt, sizeof(keyalt));
  }
  
  Starshatter::~Starshatter()
  {
      if (video_changed) {
          SaveVideoConfig("video.cfg");
      }
  
      DeleteFile("video2.cfg");
      StopLobby();
  
      if (Status() <= EXIT)
      Player::Save();
  
      delete menuscreen;
      delete loadscreen;
      delete planscreen;
      delete gamescreen;
      delete cmpnscreen;
  
      menuscreen = 0;
      loadscreen = 0;
      planscreen = 0;
      gamescreen = 0;
      cmpnscreen = 0;
  
      music_dir  = 0;
  
      // delete all the ships and stuff
      // BEFORE getting rid of the system
      // and weapons catalogs!
      delete world;
      world = 0; // don't let base class double delete the world
  
      delete quick_mission;
  
      AudioConfig::Close();
      HUDSounds::Close();
      MusicDirector::Close();
  
      Player::Close();
      Drive::Close();
      LandingGear::Close();
      MFD::Close();
      Explosion::Close();
      FlightDeck::Close();
      Campaign::Close();
      CombatRoster::Close();
      Galaxy::Close();
      RadioTraffic::Close();
      RadioVox::Close();
      Ship::Close();
      WeaponDesign::Close();
      SystemDesign::Close();
      ModConfig::Close();
      NetClientConfig::Close();
      TacticalView::Close();
      QuantumView::Close();
      QuitView::Close();
      RadioView::Close();
      NetServerConfig::Close();
  
      Mouse::Close();
      EventDispatch::Close();
      FontMgr::Close();
      Button::Close();
      DataLoader::Close();
  
      delete ocrb;
      delete limerick12;
      delete limerick18;
      delete verdana;
      delete terminal;
      delete HUDfont;
      delete GUIfont;
      delete GUI_small_font;
      delete input;
      delete head_tracker;
  
      instance = 0;
  }
  
  void
  Starshatter::Exit()
  {
      MusicDirector::SetMode(MusicDirector::NONE);
      SetGameMode(EXIT_MODE);
  }
  
  // +--------------------------------------------------------------------+
  
  bool
  Starshatter::OnHelp()
  {
      WebBrowser browser;
      browser.OpenURL("http://matrixgames.com/support");
      return true;
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::MapKeys()
  {
      int nkeys = keycfg.GetNumKeys();
  
      if (nkeys > 0) {
          Starshatter::MapKeys(&keycfg, nkeys);
          input->MapKeys(keycfg.GetMapping(), nkeys);
      }
  }
  
  void
  Starshatter::MapKeys(KeyMap* mapping, int nkeys)
  {
      for (int i = 0; i < nkeys; i++) {
          KeyMapEntry* k = mapping->GetKeyMap(i);
          
          if (k->act >= KEY_MAP_FIRST && k->act <= KEY_MAP_LAST)
          MapKey(k->act, k->key, k->alt);
      }
  }
  
  void
  Starshatter::MapKey(int act, int key, int alt)
  {
      keymap[act] = key;
      keyalt[act] = alt;
  
      GetAsyncKeyState(key);
      GetAsyncKeyState(alt);
  }
  
  // +--------------------------------------------------------------------+
  
  bool
  Starshatter::Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow)
  {
      if (strstr(cmdline, "-win") || strstr(cmdline, "-dbg")) {
          if (video_settings) {
              video_settings->is_windowed   = true;
  
              Print("   STARSHATTER RUNNING IN WINDOW MODE\n");
          }
      }
  
      if (strstr(cmdline, "-filesys")) {
          use_file_system = true;
          Print("   FILE SYSTEM ENABLED\n");
      }
  
      if (strstr(cmdline, "-nosplash")) {
          no_splash = true;
      }
  
      if (loader)
      loader->UseFileSystem(use_file_system);
  
      return Game::Init(hi, hpi, cmdline, nCmdShow);
  }
  
  // +--------------------------------------------------------------------+
  
  bool
  Starshatter::InitGame()
  {
      if (!Game::InitGame())
      return false;
  
      RandomInit();
  
      AudioConfig::Initialize();
      ModConfig::Initialize();
  
      InitMouse();
  
      Button::Initialize();
      EventDispatch::Create();
      NetClientConfig::Initialize();
      Player::Initialize();
      HUDSounds::Initialize();
  
      int nkeys = keycfg.LoadKeyMap("key.cfg", 256);
  
      if (nkeys)
      Print("  Loaded key.cfg\n\n");
  
      // create the appropriate motion controller and player_ship
      input = new(__FILE__,__LINE__) MultiController;
      Keyboard* k = new(__FILE__,__LINE__) Keyboard;
      input->AddController(k);
      ActivateKeyboardLayout(GetKeyboardLayout(0), 0);
  
      mouse_input = new(__FILE__,__LINE__) MouseController;
      input->AddController(mouse_input);
  
      ::Print("\nStarshatter::InitGame() create joystick\n");
      Joystick* j = new(__FILE__,__LINE__) Joystick;
      j->SetSensitivity(15, 5000);
      input->AddController(j);
  
      Joystick::EnumerateDevices();
      ::Print("\n");
  
      head_tracker = new(__FILE__,__LINE__) TrackIR();
      MapKeys();
  
      SystemDesign::Initialize("sys.def");
      WeaponDesign::Initialize("wep.def");
      MusicDirector::Initialize();
  
      // if no splashes, we need to initialize the campaign engine now
      if (no_splash) {
          Ship::Initialize();
          Galaxy::Initialize();
          CombatRoster::Initialize();
          Campaign::Initialize();
      }
  
      // otherwise, the campaign engine will get initialized during the splashes
      else {
          SetupSplash();
      }
  
      time_mark = Game::GameTime();
      minutes   = 0;
  
      return true;
  }
  
  void
  Starshatter::InitMouse()
  {
      if (loader) {
          loader->SetDataPath(0);
  
          Mouse::Create(screen);
          Mouse::Show(false);
          Mouse::LoadCursor(Mouse::ARROW, "MouseArrow.pcx", Mouse::HOTSPOT_NW);
          Mouse::LoadCursor(Mouse::CROSS, "MouseCross.pcx", Mouse::HOTSPOT_CTR);
          Mouse::LoadCursor(Mouse::DRAG,  "MouseDrag.pcx",  Mouse::HOTSPOT_NW);
          Mouse::SetCursor(Mouse::ARROW);
      }
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::RequestChangeVideo()
  {
      req_change_video = true;
  }
  
  int
  Starshatter::GetScreenWidth()
  {
      if (video_settings)
      return video_settings->GetWidth();
  
      return 0;
  }
  
  // +--------------------------------------------------------------------+
  
  int
  Starshatter::GetScreenHeight()
  {
      if (video_settings)
      return video_settings->GetHeight();
  
      return 0;
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::StartOrResumeGame()
  {
      if (game_mode != MENU_MODE && game_mode != CMPN_MODE)
      return;
  
      Player* p = Player::GetCurrentPlayer();
      if (!p)
      return;
  
      List<Campaign>& list    = Campaign::GetAllCampaigns();
      Campaign*       c       = 0;
      Text            saved   = CampaignSaveGame::GetResumeFile();
  
  
      // resume saved game?
      if (saved.length()) {
          CampaignSaveGame  savegame;
          savegame.Load(saved);
          c = savegame.GetCampaign();
      }
  
      // start training campaign?
      else if (p->Trained() < 255) {
          c = list[0];
          c->Load();
      }
  
      // start new dynamic campaign sequence?
      else {
          c = list[1];
          c->Load();
      }
  
      if (c)
      Campaign::SelectCampaign(c->Name());
  
      Mouse::Show(false);
      SetGameMode(CLOD_MODE);
  }
  
  // +--------------------------------------------------------------------+
  
  bool
  Starshatter::UseFileSystem()
  {
      return use_file_system;
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::OpenTacticalReference()
  {
      if (menuscreen && game_mode == MENU_MODE) {
          menuscreen->ShowLoadDlg();
  
          LoadDlg* load_dlg = menuscreen->GetLoadDlg();
  
          if (load_dlg && load_dlg->IsShown()) {
              load_activity = Game::GetText("Starshatter.load.tac-ref");
              load_progress = 1;
              catalog_index = 0;
          }
          else {
              menuscreen->ShowTacRefDlg();
          }
      }
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::SetGameMode(int m)
  {
      if (game_mode == m)
      return;
  
      const char* mode_name[] = {
          "MENU_MODE",  // main menu
          "CLOD_MODE",  // loading campaign
          "CMPN_MODE",  // operational command for dynamic campaign
          "PREP_MODE",  // loading mission info for planning
          "PLAN_MODE",  // mission briefing
          "LOAD_MODE",  // loading mission into simulator
          "PLAY_MODE",  // active simulation
          "EXIT_MODE"   // shutting down
      };
  
      if (m >= MENU_MODE && m <= EXIT_MODE)
      Print(">>> Starshatter::SetGameMode(%d) (%s)\n", m, mode_name[m]);
      else
      Print(">>> Starshatter::SetGameMode(%d) (UNKNOWN MODE)\n", m);
  
      MouseController* mouse_con = MouseController::GetInstance();
      if (mouse_con)
      mouse_con->SetActive(false);
  
      if (m == CLOD_MODE || m == PREP_MODE || m == LOAD_MODE) {
          load_step     = 0;
          load_progress = 0;
          load_activity = Game::GetText("Starshatter.load.general");
          paused = true;
      }
  
      else if (m == CMPN_MODE) {
          load_step     = 0;
          load_progress = 100;
          load_activity = Game::GetText("Starshatter.load.complete");
          paused = false;
      }
  
      else if (m == PLAY_MODE) {
          Print("  Starting Game...\n");
  
          player_ship   = 0;
          load_progress = 100;
          load_activity = Game::GetText("Starshatter.load.complete");
  
          if (!world) {
              CreateWorld();
              InstantiateMission();
          }
  
          if (gamescreen)
          gamescreen->SetFieldOfView(field_of_view);
  
          HUDView::ClearMessages();
          RadioView::ClearMessages();
  
          SetTimeCompression(1);
          Pause(false);
  
          Print("  Stardate: %.1f\n", StarSystem::GetBaseTime());
      }
  
      else if (m == PLAN_MODE) {
          if (game_mode == PLAY_MODE) {
              Print("  Returning to Plan Mode...\n");
              if (soundcard)
              soundcard->StopSoundEffects();
  
              StopNetGame();
              Pause(true);
              Print("  Stardate: %.1f\n", StarSystem::GetBaseTime());
          }
      }
  
      else if (m == MENU_MODE) {
          Print("  Returning to Main Menu...\n");
  
          if (game_mode == PLAN_MODE || game_mode == PLAY_MODE) {
              if (soundcard)
              soundcard->StopSoundEffects();
  
              StopNetGame();
          }
  
          paused = true;
      }
  
      if (m == EXIT_MODE) {
          Print("  Shutting Down (Returning to Windows)...\n");
  
          if (game_mode == PLAN_MODE || game_mode == PLAY_MODE) {
              if (soundcard)
              soundcard->StopSoundEffects();
  
              StopNetGame();
          }
  
          Print("  Stardate: %.1f\n", StarSystem::GetBaseTime());
          Print("  Bitmap Cache Footprint: %d KB\n", Bitmap::CacheMemoryFootprint() / 1024);
  
          paused = true;
      }
  
      FlushKeys();
      game_mode = m;
  }
  
  // +--------------------------------------------------------------------+
  
  bool
  Starshatter::ChangeVideo()
  {
      bool  result = false;
  
      if (menuscreen) {
          delete menuscreen;
          menuscreen = 0;
      }
  
      if (loadscreen) {
          delete loadscreen;
          loadscreen = 0;
      }
  
      if (planscreen) {
          delete planscreen;
          planscreen = 0;
      }
  
      if (gamescreen) {
          delete gamescreen;
          gamescreen = 0;
      }
  
      if (cmpnscreen) {
          delete cmpnscreen;
          cmpnscreen = 0;
      }
  
      loader->SetDataPath(0);
  
      LoadVideoConfig("video2.cfg");
  
      result = ResetVideo();
  
      InitMouse();
  
      req_change_video = false;
      video_changed    = true;
  
      return result;
  }
  
  bool
  Starshatter::ResizeVideo()
  {
      if (Game::ResizeVideo()) {
          InitMouse();
          Mouse::Show(true);
  
          return true;
      }
  
      return false;
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::CreateWorld()
  {
      RadioTraffic::Initialize();
      RadioView::Initialize();
      RadioVox::Initialize();
      QuantumView::Initialize();
      QuitView::Initialize();
      TacticalView::Initialize();
  
      // create world
      if (!world) {
          Sim* sim = new(__FILE__,__LINE__) Sim(input);
          world = sim;
          Print("  World Created.\n");
      }
  
      cam_dir = CameraDirector::GetInstance();
  }
  
  void
  Starshatter::InstantiateMission()
  {
      Memory::Check();
  
      current_mission = 0;
  
      if (Campaign::GetCampaign()) {
          current_mission = Campaign::GetCampaign()->GetMission();
      }
  
      Sim* sim = (Sim*) world;
  
      if (sim) {
          bool        dynamic  = false;
          Campaign*   campaign = Campaign::GetCampaign();
  
          if (campaign && campaign->IsDynamic())
          dynamic = true;
  
          sim->UnloadMission();
          sim->LoadMission(current_mission);
          sim->ExecMission();
          sim->SetTestMode(test_mode && !dynamic ? true : false);
  
          Print("  Mission Instantiated.\n");
      }
  
      Memory::Check();
  }
  
  // +--------------------------------------------------------------------+
  
  int
  Starshatter::KeyDown(int action) const
  {
      int k = Joystick::KeyDownMap(action) ||
      Keyboard::KeyDownMap(action);
  
      return k;
  }
  
  // +--------------------------------------------------------------------+
  
  bool
  Starshatter::GameLoop()
  {
      cam_dir = CameraDirector::GetInstance();
  
      if (active && paused) {
          // Route Events to EventTargets
          EventDispatch* ed = EventDispatch::GetInstance();
          if (ed)
          ed->Dispatch();
  
          UpdateWorld();
          GameState();
          UpdateScreen();
          CollectStats();
  
          /***
      static DWORD vmf_time = 0;
  
      if (real_time() - vmf_time > 5000) {
          vmf_time = real_time();
          DWORD vmf = video->VidMemFree() / (1024 * 1024);
          ::Print("\n###### %02d:%02d - Video Memory Free: %d MB\n\n",
                  vmf_time / 60000,
                  vmf_time /  1000,
                  vmf);
      }
      ***/
      }
  
      Game::GameLoop();
      return false;  // must return false to keep processing
      // true tells the outer loop to sleep until a
      // windows event is available
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::UpdateWorld()
  {
      long   new_time      = real_time;
      double delta         = new_time - frame_time; // in milliseconds
      seconds       = max_frame_length;      // in seconds
      gui_seconds   = delta * 0.001;
  
      if (frame_time == 0)
      gui_seconds = 0;
  
      if (delta < time_comp * max_frame_length * 1000) {
          seconds = time_comp * delta * 0.001;
      }
      else {
          seconds = time_comp * max_frame_length;
      }
  
      frame_time = new_time;
  
      Galaxy* galaxy = Galaxy::GetInstance();
      if (galaxy) galaxy->ExecFrame();
  
      // Cutscene missions have a tendency to mess with the stardate
      // to manage time-of-day and camera effects.  It's a bad idea to
      // evaluate campaign actions and events while the cutscene is
      // changing the time base.
      if (!cutscene_mission) {
          Campaign* campaign = Campaign::GetCampaign();
          if (campaign) campaign->ExecFrame();
      }
  
      if (paused) {
          if (world)
          world->ExecFrame(0);
      }
  
      else {
          game_time += (DWORD) (seconds * 1000);
  
          Drive::StartFrame();
  
          if (world)
          world->ExecFrame(seconds);
      }
  
      if (game_mode == PLAY_MODE || InCutscene()) {
          if (cam_dir) {
              if (head_tracker && head_tracker->IsRunning() && !InCutscene()) {
                  head_tracker->ExecFrame();
                  cam_dir->VirtualHead(head_tracker->GetAzimuth(), head_tracker->GetElevation());
                  cam_dir->VirtualHeadOffset(head_tracker->GetX(), head_tracker->GetY(), head_tracker->GetZ());
              }
  
              cam_dir->ExecFrame(gui_seconds);
          }
  
          Sim*        sim   = Sim::GetSim();
          SimRegion*  rgn   = sim ? sim->GetActiveRegion() : 0;
  
          if (rgn) {
              ListIter<Ship> iter = rgn->Ships();
              while (++iter) {
                  Ship* s = iter.value();
                  s->SelectDetail(seconds);
              }
          }
      }
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::GameState()
  {
      if (splash) {
          static bool quick_splash = false;
  
          if (GetKey() != 0)
          quick_splash = true;
  
          if (quick_splash) {
              splash->FadeIn(0);
              splash->StopHold();
              splash->FadeOut(0);
          }
  
          if (splash->Done()) {
              splash = 0; // this will get deleted along with gamewin
              splash_index++;
  
              if (gamewin) {
                  screen->DelWindow(gamewin);
                  delete gamewin;
                  gamewin = 0;
              }
  
              if (splash_index < 2) {
                  Ship::Initialize();
                  Galaxy::Initialize();
                  SetupSplash();
              }
              else {
                  CombatRoster::Initialize();
                  Campaign::Initialize();
                  SetupMenuScreen();
              }
  
              FlushKeys();
          }
      }
  
      else if (game_mode == MENU_MODE) {
          bool campaign_select = false;
  
          if (cmpnscreen) {
              campaign_select = cmpnscreen->IsShown();
              cmpnscreen->Hide();
          }
  
          if (gamescreen)
          gamescreen->Hide();
  
          if (planscreen)
          planscreen->Hide();
  
          if (loadscreen)
          loadscreen->Hide();
  
          if (!menuscreen) {
              SetupMenuScreen();
          }
          else {
              menuscreen->Show();
  
              if (campaign_select)
              menuscreen->ShowCmpSelectDlg();
          }
  
          if (MusicDirector::GetInstance() &&
                  MusicDirector::GetInstance()->GetMode() != MusicDirector::CREDITS)
          MusicDirector::SetMode(MusicDirector::MENU);
  
          DoMenuScreenFrame();
      }
  
      else if (game_mode == CLOD_MODE ||
              game_mode == PREP_MODE ||
              game_mode == LOAD_MODE) {
          if (menuscreen)
          menuscreen->Hide();
  
          if (planscreen)
          planscreen->Hide();
  
          if (cmpnscreen)
          cmpnscreen->Hide();
  
          if (!loadscreen)
          SetupLoadScreen();
          else
          loadscreen->Show();
  
          if (game_mode == CLOD_MODE)
          MusicDirector::SetMode(MusicDirector::MENU);
          else
          MusicDirector::SetMode(MusicDirector::BRIEFING);
  
          DoLoadScreenFrame();
      }
  
      else if (game_mode == PLAN_MODE) {
          if (menuscreen)
          menuscreen->Hide();
  
          if (cmpnscreen)
          menuscreen->Hide();
  
          if (loadscreen)
          loadscreen->Hide();
  
          if (gamescreen)
          gamescreen->Hide();
  
          if (!planscreen)
          SetupPlanScreen();
          else
          planscreen->Show();
  
          Player* p = Player::GetCurrentPlayer();
          if (p && p->ShowAward()) {
              if (!planscreen->IsAwardShown())
              planscreen->ShowAwardDlg();
          }
  
          else if (ShipStats::NumStats()) {
              if (!planscreen->IsDebriefShown()) {
                  planscreen->ShowDebriefDlg();
                  show_missions = true;
              }
          }
  
          else {
              if (!planscreen->IsMsnShown() && !planscreen->IsNavShown())
              planscreen->ShowMsnDlg();
          }
  
          MusicDirector::SetMode(MusicDirector::BRIEFING);
  
          DoPlanScreenFrame();
      }
  
  
      else if (game_mode == CMPN_MODE) {
          if (menuscreen)
          menuscreen->Hide();
  
          if (planscreen)
          planscreen->Hide();
  
          if (loadscreen)
          loadscreen->Hide();
  
          if (gamescreen)
          gamescreen->Hide();
  
          if (!cmpnscreen)
          SetupCmpnScreen();
          else
          cmpnscreen->Show();
  
          DoCmpnScreenFrame();
      }
  
      else if (game_mode == PLAY_MODE) {
          if (menuscreen)
          menuscreen->Hide();
  
          if (cmpnscreen)
          cmpnscreen->Hide();
  
          if (planscreen)
          planscreen->Hide();
  
          if (loadscreen)
          loadscreen->Hide();
  
          if (!gamescreen)
          SetupGameScreen();
          else
          gamescreen->Show();
  
          DoGameScreenFrame();
      }
  
      if (game_mode == EXIT_MODE) {
          exit_time -= Game::GUITime();
  
          if (exit_time <= 0)
          Game::Exit();
      }
  
      if (net_lobby)
      net_lobby->ExecFrame();
  
      if (music_dir)
      music_dir->ExecFrame();
  
      Memory::Check();
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::DoMenuScreenFrame()
  {
      if (!Mouse::RButton()) {
          Mouse::SetCursor(Mouse::ARROW);
          Mouse::Show(true);
      }
  
      if (time_til_change > 0)
      time_til_change -= Game::GUITime();
  
      if (!menuscreen)
      return;
  
      if (KeyDown(KEY_EXIT)) {
          if (time_til_change <= 0) {
              time_til_change = 0.5;
  
              if (!exit_latch && !menuscreen->CloseTopmost()) {
                  menuscreen->ShowExitDlg();
              }
          }
  
          exit_latch = true;
      }
      else {
          exit_latch = false;
      }
  
      LoadDlg* load_dlg = menuscreen->GetLoadDlg();
  
      if (load_dlg && load_dlg->IsShown()) {
          // load all ship designs in the standard catalog
          if (catalog_index < ShipDesign::StandardCatalogSize()) {
              ShipDesign::PreloadCatalog(catalog_index++);
              load_activity = Game::GetText("Starshatter.load.tac-ref");
  
              if (load_progress < 95)
              load_progress++;
          }
  
          else {
              menuscreen->ShowTacRefDlg();
          }
      }
  
      if (show_missions) {
          if (net_lobby)
          menuscreen->ShowNetLobbyDlg();
          else
          menuscreen->ShowMsnSelectDlg();
  
          show_missions = false;
      }
  
      menuscreen->ExecFrame();
  
      if (req_change_video) {
          ChangeVideo();
          SetupMenuScreen();
      }
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::DoPlanScreenFrame()
  {
      Mouse::SetCursor(Mouse::ARROW);
  
      if (time_til_change > 0)
      time_til_change -= Game::GUITime();
  
      if (KeyDown(KEY_EXIT)) {
          if (time_til_change <= 0) {
              time_til_change = 1;
  
              if (!exit_latch && !planscreen->CloseTopmost()) {
                  Campaign* campaign = Campaign::GetCampaign();
                  if (campaign && (campaign->IsDynamic() || campaign->IsTraining()))
                  SetGameMode(CMPN_MODE);
                  else
                  SetGameMode(MENU_MODE);
              }
          }
  
          exit_latch = true;
      }
      else {
          exit_latch = false;
      }
  
      planscreen->ExecFrame();
      show_missions = true;
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::DoCmpnScreenFrame()
  {
      Mouse::SetCursor(Mouse::ARROW);
  
      if (time_til_change > 0)
      time_til_change -= Game::GUITime();
  
      exit_latch = KeyDown(KEY_EXIT) ? true : false;
  
      if (InCutscene() && player_ship) {
          // warp effect:
          if (player_ship->WarpFactor() > 1) {
              if (player_ship->WarpFactor() > field_of_view)
              cmpnscreen->SetFieldOfView(player_ship->WarpFactor());
              else
              cmpnscreen->SetFieldOfView(field_of_view);
          }
  
          else {
              if (cmpnscreen->GetFieldOfView() != field_of_view)
              cmpnscreen->SetFieldOfView(field_of_view);
          }
      }
  
      if (InCutscene() && exit_latch) {
          time_til_change = 1;
          EndCutscene();
          EndMission();
          cmpnscreen->SetFieldOfView(field_of_view);
      }
  
      else if (time_til_change <= 0 && exit_latch) {
          time_til_change = 1;
  
          if (!cmpnscreen || !cmpnscreen->CloseTopmost()) {
              SetGameMode(MENU_MODE);
          }
      }
  
      // time control for campaign mode:
      else if (game_mode == CMPN_MODE) {
          if (time_til_change <= 0) {
              if (KeyDown(KEY_PAUSE)) {
                  time_til_change = 1;
                  Pause(!paused);
              }
  
              else if (KeyDown(KEY_TIME_COMPRESS)) {
                  time_til_change = 1;
  
                  switch (TimeCompression()) {
                  case 1:  SetTimeCompression(2);  break;
                  case 2:  SetTimeCompression(4);  break;
                  case 4:  SetTimeCompression(8);  break;
                  }
              }
  
              else if (KeyDown(KEY_TIME_EXPAND)) {
                  time_til_change = 1;
  
                  switch (TimeCompression()) {
                  case  8: SetTimeCompression( 4); break;
                  case  4: SetTimeCompression( 2); break;
                  default: SetTimeCompression( 1); break;
                  }
              }
          }
      }
  
      if (show_missions && !InCutscene()) {
          cmpnscreen->ShowCmdMissionsDlg();
          show_missions = false;
      }
  
      cmpnscreen->ExecFrame();
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::DoLoadScreenFrame()
  {
      Mouse::Show(false);
      Mouse::SetCursor(Mouse::ARROW);
  
      if (game_mode == CLOD_MODE) {
          CmpLoadDlg* dlg = loadscreen->GetCmpLoadDlg();
  
          switch (load_step) {
          case 0:
              load_activity = Game::GetText("Starshatter.load.campaign");
              load_progress = 1;
              catalog_index = 0;
              break;
  
          case 1:
              // load all ship designs in the standard catalog
              if (catalog_index < ShipDesign::StandardCatalogSize()) {
                  ShipDesign::PreloadCatalog(catalog_index++);
                  load_activity = Game::GetText("Starshatter.load.campaign");
  
                  if (load_progress < 80)
                  load_progress++;
  
                  load_step = 0; // force return to current step on next frame
              }
              else {
                  load_activity = Game::GetText("Starshatter.load.start");
                  load_progress = 80;
              }
              break;
  
          case 2:
              if (Campaign::GetCampaign())
              Campaign::GetCampaign()->Start();
              break;
  
          default:
              if (dlg && load_progress < 100) {
                  load_progress++;
              }
              else {
                  load_activity = Game::GetText("Starshatter.load.ready");
                  load_progress = 100;
                  SetGameMode(CMPN_MODE);
              }
              break;
          }
      }
      else {
          switch (load_step) {
          case 0:
              load_activity = Game::GetText("Starshatter.load.drives");
              load_progress = 0;
              break;
  
          case 1:
              Drive::Initialize();
              LandingGear::Initialize();
              load_activity = Game::GetText("Starshatter.load.explosions");
              load_progress = 5;
              break;
  
          case 2:
              Explosion::Initialize();
              load_activity = Game::GetText("Starshatter.load.systems");
              load_progress = 10;
              break;
  
          case 3:
              FlightDeck::Initialize();
              NavLight::Initialize();
              load_activity = Game::GetText("Starshatter.load.ships");
              load_progress = 15;
              break;
  
          case 4:
              Ship::Initialize();
              Shot::Initialize();
              load_activity = Game::GetText("Starshatter.load.hud");
              load_progress = 20;
              break;
  
          case 5:
              MFD::Initialize();
              load_activity = Game::GetText("Starshatter.load.menus");
              load_progress = 25;
              break;
  
          case 6:
              RadioTraffic::Initialize();
              RadioView::Initialize();
              TacticalView::Initialize();
              
              if (!gamescreen && game_mode != PREP_MODE)
              SetupGameScreen();
  
              load_activity = Game::GetText("Starshatter.load.mission");
              load_progress = 65;
              break;
  
          case 7:
              if (game_mode == PREP_MODE) {
                  if (Campaign::GetCampaign())
                  Campaign::GetCampaign()->GetMission();
                  SetGameMode(PLAN_MODE);
                  load_activity = Game::GetText("Starshatter.load.loaded");
                  load_progress = 100;
                  Button::PlaySound(4);
              }
              else {
                  CreateWorld();
                  load_activity = Game::GetText("Starshatter.load.simulation");
                  load_progress = 75;
              }
              break;
  
          case 8:
              InstantiateMission();
              load_activity = Game::GetText("Starshatter.load.viewscreen");
              load_progress = 90;
              break;
  
          default:
              SetGameMode(PLAY_MODE);
              load_activity = Game::GetText("Starshatter.load.ready");
              load_progress = 100;
              break;
          }
      }
  
      load_step++;
      loadscreen->ExecFrame();
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::PlayerCam(int mode)
  {
      if (player_ship && !player_ship->InTransition() && player_ship->GetFlightPhase() >= Ship::LOCKED) {
          gamescreen->CloseTopmost();
          if (mode == CameraDirector::MODE_DROP) {
              player_ship->DropCam(15, 0);
              cam_dir->SetShip(player_ship);
              cam_dir->SetMode(CameraDirector::MODE_DROP, 0);
          }
  
          else {
              cam_dir->SetMode(mode);
  
              if (mode == CameraDirector::MODE_ORBIT) {
                  MouseController* mouse_con = MouseController::GetInstance();
                  if (mouse_con)
                  mouse_con->SetActive(false);
              }
          }
      }
  }
  
  void
  Starshatter::ViewSelection()
  {
      Sim* sim = (Sim*) world;
      List<Ship>& seln = sim->GetSelection().container();
  
      if (cam_dir) {
          if (seln.isEmpty())
          cam_dir->SetViewObject(player_ship);
  
          else if (seln.size() == 1)
          cam_dir->SetViewObject(seln[0]);
  
          else
          cam_dir->SetViewObjectGroup(seln);
      }
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::DoGameScreenFrame()
  {
      Sim* sim = (Sim*) world;
  
      if (!gamescreen || !sim)
      return;
  
      if (InCutscene()) {
          if (player_ship) {
              // warp effect:
              if (player_ship->WarpFactor() > 1) {
                  if (player_ship->WarpFactor() > field_of_view)
                  gamescreen->SetFieldOfView(player_ship->WarpFactor());
                  else
                  gamescreen->SetFieldOfView(field_of_view);
              }
  
              else {
                  if (gamescreen->GetFieldOfView() != field_of_view)
                  gamescreen->SetFieldOfView(field_of_view);
              }
          }
  
          gamescreen->FrameRate(frame_rate);
          gamescreen->ExecFrame();
  
          if (KeyDown(KEY_EXIT)) {
              gamescreen->SetFieldOfView(field_of_view);
              exit_latch      = true;
              time_til_change = 1;
  
              sim->SkipCutscene();
          }
  
          return;
      }
  
      if (time_til_change > 0)
      time_til_change -= Game::GUITime();
  
      if (exit_latch && !KeyDown(KEY_EXIT))
      exit_latch = false;
  
      DoMouseFrame();
  
      HUDView*       hud_view = HUDView::GetInstance();
  
      // changing to a new ship?
      if (player_ship != sim->GetPlayerShip()) {
          gamescreen->HideNavDlg();
          gamescreen->HideFltDlg();
          gamescreen->HideEngDlg();
  
          Ship* new_player = sim->GetPlayerShip();
  
          if (new_player) {
              if (new_player->IsDropship() && Ship::GetFlightModel() < 2 && Ship::GetControlModel() < 1)
              input->SwapYawRoll(true);
              else
              input->SwapYawRoll(false);
  
              if (hud_view) {
                  hud_view->SetHUDMode(HUDView::HUD_MODE_TAC);
                  hud_view->HideHUDWarn();
              }
          }
      }
  
      player_ship = sim->GetPlayerShip();
      
      if (player_ship) {
          // warp effect:
          if (player_ship->WarpFactor() > 1) {
              if (player_ship->WarpFactor() > field_of_view)
              gamescreen->SetFieldOfView(player_ship->WarpFactor());
              else
              gamescreen->SetFieldOfView(field_of_view);
          }
  
          else {
              if (gamescreen->GetFieldOfView() != field_of_view)
              gamescreen->SetFieldOfView(field_of_view);
          }
  
          gamescreen->ShowExternal();   
          
          if (CameraDirector::GetCameraMode() >= CameraDirector::MODE_ORBIT && !player_ship->InTransition())
          tactical = true;
          else
          tactical = false;
  
          if (player_ship->InTransition())
          gamescreen->CloseTopmost();
      }
  
      if (chat_mode) {
          DoChatMode();
      }
  
      else {
          DoGameKeys();
      }
  
      gamescreen->FrameRate(frame_rate);
      gamescreen->ExecFrame();
  
      if (Game::GameTime() - time_mark > 60000) {
          time_mark = Game::GameTime();
          minutes++;
          if (minutes > 60)
          Print("  TIME %2d:%02d:00\n", minutes/60, minutes%60);
          else
          Print("  TIME    %2d:00\n", minutes);
      }
  }
  
  void
  Starshatter::DoGameKeys()
  {
      Sim*     sim      = (Sim*) world;
      HUDView* hud_view = HUDView::GetInstance();
  
      if (time_til_change <= 0) {
          if (KeyDown(KEY_CAM_BRIDGE)) {
              PlayerCam(CameraDirector::MODE_COCKPIT);
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_CAM_VIRT)) {
              PlayerCam(CameraDirector::MODE_VIRTUAL);
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_CAM_CHASE)) {
              PlayerCam(CameraDirector::MODE_CHASE);
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_CAM_DROP)) {
              PlayerCam(CameraDirector::MODE_DROP);
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_CAM_EXTERN)) {
              PlayerCam(CameraDirector::MODE_ORBIT);
              time_til_change = 0.5;
          }
          
          else if (KeyDown(KEY_TARGET_PADLOCK)) {
              PlayerCam(CameraDirector::MODE_TARGET);
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_SWAP_ROLL_YAW)) {
              input->SwapYawRoll(!input->GetSwapYawRoll());
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_ZOOM_WIDE)) {
              time_til_change = 0.5;
              if (gamescreen->GetFieldOfView() <= 2)
              field_of_view = 3; // wide
              else
              field_of_view = 2; // normal
  
              // don't mess with fov during warp:
              if (player_ship && player_ship->WarpFactor() <= 1)
              gamescreen->SetFieldOfView(field_of_view);
          }
          
          else if (!exit_latch && KeyDown(KEY_EXIT)) {
              exit_latch      = true;
              time_til_change = 0.5;
  
              if (!gamescreen->CloseTopmost()) {
                  QuitView* quit = QuitView::GetInstance();
                  if (quit)
                  quit->ShowMenu();
                  else
                  SetGameMode(Starshatter::PLAN_MODE);
              }
          }
  
          else if (KeyDown(KEY_PAUSE)) {
              Pause(!paused);
              time_til_change = 0.5;
          }
  
  
          else if (KeyDown(KEY_TIME_COMPRESS)) {
              time_til_change = 0.5;
              if (NetGame::IsNetGame())
              SetTimeCompression(1);
              else
              switch (TimeCompression()) {
              case 1:  SetTimeCompression(2);  break;
              default: SetTimeCompression(4);  break;
              }
          }
  
          else if (KeyDown(KEY_TIME_EXPAND)) {
              time_til_change = 0.5;
  
              if (NetGame::IsNetGame())
              SetTimeCompression(1);
              else
              switch (TimeCompression()) {
              case  4: SetTimeCompression( 2); break;
              default: SetTimeCompression( 1); break;
              }
          }
  
          else if (KeyDown(KEY_TIME_SKIP)) {
              time_til_change = 0.5;
  
              if (player_ship && !NetGame::IsNetGame()) {
                  player_ship->TimeSkip();
              }
          }
  
          else if (KeyDown(KEY_COMMAND_MODE)) {
              if (player_ship) {
                  time_til_change = 0.5;
                  player_ship->CommandMode();
              }
          }
  
          /*** For Debug Convenience Only: ***/
          else if (KeyDown(KEY_INC_STARDATE)) {
              StarSystem::SetBaseTime(StarSystem::GetBaseTime() + 600, true);
          }
  
          else if (KeyDown(KEY_DEC_STARDATE)) {
              StarSystem::SetBaseTime(StarSystem::GetBaseTime() - 600, true);
          }
          /***/
      }
  
      if (gamescreen && time_til_change <= 0) {
          if (KeyDown(KEY_MFD1)) {
              gamescreen->CycleMFDMode(0);
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_MFD2)) {
              gamescreen->CycleMFDMode(1);
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_MFD3)) {
              gamescreen->CycleMFDMode(2);
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_MFD4)) {
              gamescreen->CycleMFDMode(3);
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_RADIO_MENU)) {
              RadioView* radio_view = RadioView::GetInstance();
              if (radio_view) {
                  if (radio_view->IsMenuShown())
                  radio_view->CloseMenu();
                  else
                  radio_view->ShowMenu();
              }
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_QUANTUM_MENU)) {
              QuantumView* quantum_view = QuantumView::GetInstance();
              if (quantum_view) {
                  if (quantum_view->IsMenuShown())
                  quantum_view->CloseMenu();
                  else
                  quantum_view->ShowMenu();
              }
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_CAM_VIEW_SELECTION)) {
              time_til_change = 0.5;
              ViewSelection();
          }
  
          else if (KeyDown(KEY_HUD_MODE)) {
              time_til_change = 0.5;
              if (hud_view)
              hud_view->CycleHUDMode();
          }
  
          else if (KeyDown(KEY_HUD_COLOR)) {
              time_til_change = 0.5;
              if (hud_view)
              hud_view->CycleHUDColor();
          }
  
          else if (KeyDown(KEY_HUD_WARN)) {
              time_til_change = 0.5;
              if (hud_view)
              hud_view->CycleHUDWarn();
          }
  
          else if (KeyDown(KEY_HUD_INST)) {
              time_til_change = 0.5;
              if (hud_view)
              hud_view->CycleHUDInst();
          }
  
          else if (KeyDown(KEY_SELF_DESTRUCT)) {
              time_til_change = 0.5;
              
              if (player_ship && !player_ship->InTransition()) {
                  double damage = player_ship->Design()->scuttle;
  
                  if (NetGame::IsNetGameClient()) {
                      NetUtil::SendSelfDestruct(player_ship, damage);
                  }
                  else {
                      Point scuttle_loc = player_ship->Location() + RandomDirection() * player_ship->Radius();
                      player_ship->InflictDamage(damage, 0, 1, scuttle_loc);
                  }
  
                  if (player_ship->Integrity() < 1) {
                      ::Print("  %s 0-0-0-Destruct-0\n\n", player_ship->Name());
  
                      ShipStats* s = ShipStats::Find(player_ship->Name());
                      if (s)
                      s->AddEvent(SimEvent::DESTROYED, player_ship->Name());
  
                      player_ship->DeathSpiral();
                  }
              }
          }
      }
  
      if (gamescreen && player_ship && time_til_change <= 0 && !::GetAsyncKeyState(VK_SHIFT) && !::GetAsyncKeyState(VK_MENU)) {
          if (KeyDown(KEY_NAV_DLG)) {
              gamescreen->ShowNavDlg();
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_WEP_DLG)) {
              if (player_ship && player_ship->Design()->wep_screen)
              gamescreen->ShowWeaponsOverlay();
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_FLT_DLG)) {
              if (player_ship && player_ship->NumFlightDecks() > 0)
              gamescreen->ShowFltDlg();
              time_til_change = 0.5;
          }
  
          else if (KeyDown(KEY_ENG_DLG)) {
              if (player_ship && player_ship->Design()->repair_screen)
              gamescreen->ShowEngDlg();
              time_til_change = 0.5;
          }
      }
  
      if (cam_dir) {
          double spin = (PI/2) * Game::FrameTime(); // Game::GUITime();
  
          if (avi_file)
          spin /= 6;
  
          if (KeyDown(KEY_CAM_EXT_PLUS_AZ))
          cam_dir->ExternalAzimuth(spin);
  
          else if (KeyDown(KEY_CAM_EXT_MINUS_AZ))
          cam_dir->ExternalAzimuth(-spin);
  
          if (KeyDown(KEY_CAM_EXT_PLUS_EL))
          cam_dir->ExternalElevation(spin);
  
          else if (KeyDown(KEY_CAM_EXT_MINUS_EL))
          cam_dir->ExternalElevation(-spin);
  
          if (KeyDown(KEY_CAM_VIRT_PLUS_AZ))
          cam_dir->VirtualAzimuth(-spin);
  
          else if (KeyDown(KEY_CAM_VIRT_MINUS_AZ))
          cam_dir->VirtualAzimuth(spin);
  
          if (KeyDown(KEY_CAM_VIRT_PLUS_EL))
          cam_dir->VirtualElevation(spin);
  
          else if (KeyDown(KEY_CAM_VIRT_MINUS_EL))
          cam_dir->VirtualElevation(-spin);
  
          if (KeyDown(KEY_CAM_EXT_PLUS_RANGE)){
              if (!gamescreen->IsNavShown()) {
                  cam_dir->ExternalRange((float) (1 + 1.5 * Game::FrameTime())); // 1.1f);
              }
          }
  
          else if (KeyDown(KEY_CAM_EXT_MINUS_RANGE)) {
              if (!gamescreen->IsNavShown()) {
                  cam_dir->ExternalRange((float) (1 - 1.5 * Game::FrameTime())); // 0.9f);
              }
          }
  
          if (tactical && !gamescreen->IsFormShown()) {
              if (Mouse::Wheel()) {
                  int w = Mouse::Wheel();
  
                  if (w < 0) {
                      while (w < 0) {
                          cam_dir->ExternalRange(1.25f);
                          w += 120;
                      }
                  }
                  else {
                      while (w > 0) {
                          cam_dir->ExternalRange(0.75f);
                          w -= 120;
                      }
                  }
              }
  
              else if (Mouse::LButton() && Mouse::RButton()) {
                  if (mouse_dy < 0)
                  cam_dir->ExternalRange(0.85f);
                  else if (mouse_dy > 0)
                  cam_dir->ExternalRange(1.15f);
              }
  
              else if (Mouse::MButton() && time_til_change <= 0) {
                  time_til_change = 0.5;
                  ViewSelection();
              }
  
              else {
                  if (mouse_dx || mouse_dy) {
                      if (CameraDirector::GetCameraMode() == CameraDirector::MODE_VIRTUAL) {
                          cam_dir->VirtualAzimuth(  mouse_dx * 0.2 * DEGREES);
                          cam_dir->VirtualElevation(mouse_dy * 0.2 * DEGREES);
                      }
                      else {
                          cam_dir->ExternalAzimuth(  mouse_dx * 0.2 * DEGREES);
                          cam_dir->ExternalElevation(mouse_dy * 0.2 * DEGREES);
                      }
                  }
              }
          }
      }
  
      // radio message hot keys:
      static bool comm_key = false;
  
      if (KeyDown(KEY_COMM_ATTACK_TGT)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::ATTACK);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_COMM_ESCORT_TGT)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::ESCORT);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_COMM_WEP_FREE)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::WEP_FREE);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_COMM_WEP_HOLD)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::FORM_UP);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_COMM_COVER_ME)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::COVER_ME);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_COMM_SKIP_NAV)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::SKIP_NAVPOINT);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_COMM_RETURN_TO_BASE)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::RTB);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_COMM_CALL_INBOUND)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::CALL_INBOUND);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_COMM_REQUEST_PICTURE)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::REQUEST_PICTURE);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_COMM_REQUEST_SUPPORT)) {
          if (!comm_key)
          RadioTraffic::SendQuickMessage(player_ship, RadioMessage::REQUEST_SUPPORT);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_CHAT_BROADCAST)) {
          if (!comm_key)
          SetChatMode(CHAT_BROADCAST);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_CHAT_TEAM)) {
          if (!comm_key)
          SetChatMode(CHAT_TEAM);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_CHAT_WING)) {
          if (!comm_key)
          SetChatMode(CHAT_WING);
          comm_key = true;
      }
  
      else if (KeyDown(KEY_CHAT_UNIT) && sim->GetSelection().container().size()) {
          if (!comm_key)
          SetChatMode(CHAT_UNIT);
          comm_key = true;
      }
  
      else {
          comm_key = false;
      }
  }
  
  void
  Starshatter::DoChatMode()
  {
      Player*  p         = Player::GetCurrentPlayer();
      bool     send_chat = false;
      Text     name      = "Player";
  
      if (player_ship)
      name = player_ship->Name();
  
      if (p)
      name = p->Name();
  
      if (chat_text.length()) {
          if (chat_text[0] >= '0' && chat_text[0] <= '9') {
              if (p) {
                  chat_text = p->ChatMacro(chat_text[0] - '0');
                  send_chat = true;
              }
          }
      }
  
      if (KeyDown(KEY_EXIT)) {
          SetChatMode(0);
          time_til_change = 0.5;
      }
  
      else if (send_chat || Keyboard::KeyDown(VK_RETURN)) {
          switch (chat_mode) {
          default:
          case CHAT_BROADCAST:
              {
                  NetUtil::SendChat(0, name, chat_text);
              }
              break;
  
          case CHAT_TEAM:
              if (player_ship) {
                  NetUtil::SendChat(player_ship->GetIFF()+1, name, chat_text);
              }
              break;
              
          case CHAT_WING:
              if (player_ship) {
                  Element* elem = player_ship->GetElement();
                  if (elem) {
                      for (int i = 1; i <= elem->NumShips(); i++) {
                          Ship* s = elem->GetShip(i);
                          if (s && s != player_ship)
                          NetUtil::SendChat(s->GetObjID(), name, chat_text);
                      }
                  }
              }
              break;
  
          case CHAT_UNIT:
              {
                  Sim*           sim  = Sim::GetSim();
                  ListIter<Ship> seln = sim->GetSelection();
  
                  while (++seln) {
                      Ship* s = seln.value();
                      if (s != player_ship)
                      NetUtil::SendChat(s->GetObjID(), name, chat_text);
                  }
              }
              break;
          }
  
          HUDView::Message("%s> %s", name.data(), chat_text.data());
  
          SetChatMode(0);
          time_til_change = 0.5;
      }
  
      else {
          int key = 0;
          int shift = 0;
  
          while (GetKeyPlus(key, shift)) {
              if (key >= 'A' && key <= 'Z') {
                  if (shift & 1)
                  chat_text += (char) key;
                  else
                  chat_text += (char) tolower(key);
              }
              else {
                  switch (key) {
                  case VK_BACK:
                      chat_text = chat_text.substring(0, chat_text.length()-1);
                      break;
  
                  case VK_SPACE: chat_text += ' '; break;
                  case '0': if (shift & 1) chat_text += ')'; else chat_text += '0'; break;
                  case '1': if (shift & 1) chat_text += '!'; else chat_text += '1'; break;
                  case '2': if (shift & 1) chat_text += '@'; else chat_text += '2'; break;
                  case '3': if (shift & 1) chat_text += '#'; else chat_text += '3'; break;
                  case '4': if (shift & 1) chat_text += '$'; else chat_text += '4'; break;
                  case '5': if (shift & 1) chat_text += '%'; else chat_text += '5'; break;
                  case '6': if (shift & 1) chat_text += '^'; else chat_text += '6'; break;
                  case '7': if (shift & 1) chat_text += '&'; else chat_text += '7'; break;
                  case '8': if (shift & 1) chat_text += '*'; else chat_text += '8'; break;
                  case '9': if (shift & 1) chat_text += '('; else chat_text += '9'; break;
                  case 186: if (shift & 1) chat_text += ':'; else chat_text += ';'; break;
                  case 187: if (shift & 1) chat_text += '+'; else chat_text += '='; break;
                  case 188: if (shift & 1) chat_text += '<'; else chat_text += ','; break;
                  case 189: if (shift & 1) chat_text += '_'; else chat_text += '-'; break;
                  case 190: if (shift & 1) chat_text += '>'; else chat_text += '.'; break;
                  case 191: if (shift & 1) chat_text += '?'; else chat_text += '/'; break;
                  case 192: if (shift & 1) chat_text += '~'; else chat_text += '`'; break;
                  case 219: if (shift & 1) chat_text += '{'; else chat_text += '['; break;
                  case 221: if (shift & 1) chat_text += '}'; else chat_text += ']'; break;
                  case 220: if (shift & 1) chat_text += '|'; else chat_text += '\\'; break;
                  case 222: if (shift & 1) chat_text += '"'; else chat_text += '\''; break;
                  }
              }
          }
      }
  }
  
  void
  Starshatter::SetChatMode(int mode)
  {
      if (mode >= 0 && mode <= 4 && mode != chat_mode) {
          chat_text = "";
  
          if (!NetGame::IsNetGame()) {
              chat_mode = 0;
          }
          else {
              chat_mode = mode;
  
              // flush input before reading chat message:
              if (chat_mode) {
                  FlushKeys();
              }
  
              // flush input before sampling flight controls:
              else {
                  for (int i = 1; i < 255; i++) {
                      Keyboard::KeyDown(i);
                  }
              }
          }
      }
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::DoMouseFrame()
  {
      EventDispatch* event_dispatch = EventDispatch::GetInstance();
      if (event_dispatch && event_dispatch->GetCapture())
      return;
  
      mouse_dx = 0;
      mouse_dy = 0;
  
      static int old_mouse_x = 0;
      static int old_mouse_y = 0;
  
      if (!spinning && Mouse::RButton()) {
          spinning = true;
          old_mouse_x = Mouse::X();
          old_mouse_y = Mouse::Y();
          mouse_x     = Mouse::X();
          mouse_y     = Mouse::Y();
          Mouse::Show(false);
      }
  
      else if (spinning) {
          if (!Mouse::RButton()) {
              spinning = false;
  
              if (tactical) {
                  Mouse::Show(true);
                  Mouse::SetCursor(Mouse::ARROW);
              }
  
              Mouse::SetCursorPos(old_mouse_x, old_mouse_y);
          }
  
          else {
              mouse_dx = Mouse::X() - mouse_x;
              mouse_dy = Mouse::Y() - mouse_y;
  
              Mouse::SetCursorPos(mouse_x, mouse_y);
          }
      }
  
      else if (cutscene || !player_ship) {
          Mouse::Show(false);
          return;
      }
  
      // hide mouse cursor when mouse controller is actively steering:
      else if (mouse_input && mouse_input->Active()) {
          if (mouse_input->Selector() == 1) {
              Mouse::Show(false);
          }
  
          else if (mouse_input->Selector() == 2) {
              Mouse::Show(true);
              Mouse::SetCursor(Mouse::CROSS);
          }
      }
  
      else {
          HUDView* hud_view = HUDView::GetInstance();
  
          if (hud_view && hud_view->GetHUDMode() != HUDView::HUD_MODE_OFF) {
              Mouse::Show(true);
              Mouse::SetCursor(Mouse::ARROW);
          }
      }
  
      if (gamescreen && gamescreen->IsFormShown())
      return;
  
      TacticalView*  tac_view = TacticalView::GetInstance();
  
      if (tac_view)
      tac_view->DoMouseFrame();
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::SetupSplash()
  {
      Color::SetFade(0);
  
      if (gamewin) {
          screen->DelWindow(gamewin);
          delete gamewin;
          gamewin = 0;
      }
  
      switch (splash_index) {
      case 0:
          splash_image.ClearImage();
          loader->SetDataPath(0);
          loader->LoadBitmap("matrix.pcx",   splash_image);
          break;
  
      case 1:
      default:
          splash_image.ClearImage();
          loader->SetDataPath(0);
          loader->LoadBitmap("studio.pcx",   splash_image);
          break;
      }
  
      int screen_width  = GetScreenWidth();
      int screen_height = GetScreenHeight();
  
      gamewin = new(__FILE__,__LINE__) Window(screen, 0, 0, screen_width, screen_height);
      splash  = new(__FILE__,__LINE__) FadeView(gamewin, 2, 2, 2);
  
      gamewin->AddView(splash);
      gamewin->AddView(new(__FILE__,__LINE__) ImgView(gamewin, &splash_image));
      screen->AddWindow(gamewin);
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::SetupMenuScreen()
  {
      if (menuscreen) {
          delete menuscreen;
          menuscreen = 0;
      }
  
      menuscreen = new(__FILE__,__LINE__) MenuScreen();
      menuscreen->Setup(screen);
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::SetupCmpnScreen()
  {
      if (cmpnscreen) {
          delete cmpnscreen;
          cmpnscreen = 0;
      }
  
      cmpnscreen = new(__FILE__,__LINE__) CmpnScreen();
      cmpnscreen->Setup(screen);
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::SetupPlanScreen()
  {
      if (planscreen) {
          delete planscreen;
          planscreen = 0;
      }
  
      planscreen = new(__FILE__,__LINE__) PlanScreen();
      planscreen->Setup(screen);
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::SetupLoadScreen()
  {
      if (loadscreen) {
          delete loadscreen;
          loadscreen = 0;
      }
  
      loadscreen = new(__FILE__,__LINE__) LoadScreen();
      loadscreen->Setup(screen);
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::SetupGameScreen()
  {
      if (gamescreen) {
          delete gamescreen;
          gamescreen = 0;
      }
  
      gamescreen = new(__FILE__,__LINE__) GameScreen();
      gamescreen->Setup(screen);
      gamescreen->SetFieldOfView(field_of_view);
  
      // initialize player_ship's MFD choices:
      Player::SelectPlayer(Player::GetCurrentPlayer());
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::LoadVideoConfig(const char* filename)
  {
      // set up defaults:
      int   screen_width         = 1024;
      int   screen_height        =  768;
      int   screen_depth         =   32;
      int   terrain_detail_level =    3;
      bool  shadows_enabled      = true;
      bool  spec_maps_enabled    = true;
      bool  bump_maps_enabled    = true;
      bool  vertex_shader        = true;
      bool  pixel_shader         = true;
      float depth_bias           = video_settings->depth_bias;
  
      max_tex_size   = 2048;
  
      if (MachineInfo::GetCpuSpeed() >= 1000 && MachineInfo::GetTotalRam() > 128)
      terrain_detail_level = 4;
  
      Terrain::SetDetailLevel(terrain_detail_level);
  
      // read the config file:
      BYTE*       block    = 0;
      int         blocklen = 0;
  
      FILE* f = ::fopen(filename, "rb");
  
      if (f) {
          ::fseek(f, 0, SEEK_END);
          blocklen = ftell(f);
          ::fseek(f, 0, SEEK_SET);
  
          block = new(__FILE__,__LINE__) BYTE[blocklen+1];
          block[blocklen] = 0;
  
          ::fread(block, blocklen, 1, f);
          ::fclose(f);
      }
  
      if (blocklen == 0)
      return;
  
      Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
      Term*  term = parser.ParseTerm();
  
      if (!term) {
          Print("ERROR: could not parse '%s'.\n", filename);
          exit(-3);
      }
      else {
          TermText* file_type = term->isText();
          if (!file_type || file_type->value() != "VIDEO") {
              Print("WARNING: invalid %s file.  Using defaults\n", filename);
              return;
          }
      }
  
      do {
          delete term;
  
          term = parser.ParseTerm();
          
          if (term) {
              TermDef* def = term->isDef();
              if (def) {
                  if (def->name()->value() == "width") {
                      int w;
                      GetDefNumber(w, def, filename);
  
                      switch (w) {
                      case 800:
                          screen_width  =  800;
                          screen_height =  600;
                          break;
  
                      case 1024:
                          screen_width  = 1024;
                          screen_height =  768;
                          break;
  
                      case 1152:
                          screen_width  = 1152;
                          screen_height =  864;
                          break;
  
                      case 1280:
                          screen_width  = 1280;
                          screen_height =  960;
                          break;
  
                      case 1440:
                          screen_width  = 1440;
                          screen_height =  900;
                          break;
  
                      default:
                          screen_width  = w;
                          screen_height = (w*3)/4;
                          break;
                      }
                  }
  
                  else if (def->name()->value() == "height") {
                      int h;
                      GetDefNumber(h, def, filename);
  
                      if (screen_width == 1280 && (h == 800 || h == 1024))
                      screen_height = h;
                      else if (screen_width == 1600 && (h == 900 || h == 1200))
                      screen_height = h;
                  }
  
                  else if (def->name()->value() == "depth" ||
                          def->name()->value() == "bpp") {
  
                      int bpp;
                      GetDefNumber(bpp, def, filename);
  
                      switch (bpp) {
                      case  8:
                      case 16:
                      case 24:
                      case 32:
                          screen_depth = bpp;
                          break;
  
                      default:
                          Print("WARNING: Invalid screen bpp (%d) in '%s'\n", bpp, filename);
                          screen_depth = 8;
                          break;
                      }
                  }
  
                  else if (def->name()->value() == "max_tex") {
                      int n;
                      GetDefNumber(n, def, filename);
                      if (n >= 64 && n <= 4096)
                      max_tex_size = n;
                  }
  
                  else if (def->name()->value() == "gamma") {
                      int level;
                      GetDefNumber(level, def, filename);
                      if (level >= 0 && level <= 255)
                      gamma = level;
                  }
  
                  else if (def->name()->value() == "terrain_detail_level") {
                      GetDefNumber(terrain_detail_level, def, filename);
                      Terrain::SetDetailLevel(terrain_detail_level);
                  }
  
                  else if (def->name()->value() == "terrain_texture_enable") {
                      bool enable;
                      GetDefBool(enable, def, filename);
  
                      // no longer supported!
                  }
  
                  else if (def->name()->value() == "shadows") {
                      GetDefBool(shadows_enabled, def, filename);
                  }
  
                  else if (def->name()->value() == "spec_maps") {
                      GetDefBool(spec_maps_enabled, def, filename);
                  }
  
                  else if (def->name()->value() == "bump_maps") {
                      GetDefBool(bump_maps_enabled, def, filename);
                  }
  
                  else if (def->name()->value() == "vertex_shader") {
                      GetDefBool(vertex_shader, def, filename);
                  }
  
                  else if (def->name()->value() == "pixel_shader") {
                      GetDefBool(pixel_shader, def, filename);
                  }
  
                  else if (def->name()->value().contains("bias")) {
                      GetDefNumber(depth_bias, def, filename);
                  }
  
                  else if (def->name()->value() == "flare") {
                      bool b;
                      GetDefBool(b, def, filename);
                      lens_flare = b;
                  }
  
                  else if (def->name()->value() == "corona") {
                      bool b;
                      GetDefBool(b, def, filename);
                      corona = b;
                  }
  
                  else if (def->name()->value() == "nebula") {
                      bool b;
                      GetDefBool(b, def, filename);
                      nebula = b;
                  }
  
                  else if (def->name()->value() == "dust") {
                      GetDefNumber(dust, def, filename);
                  }
  
                  else if (def->name()->value().indexOf("cam_range") == 0) {
                      double range_max = 0;
                      GetDefNumber(range_max, def, filename);
                      CameraDirector::SetRangeLimit(range_max);
                  }
              }
              else {
                  Print("WARNING: term ignored in '%s'\n", filename);
                  term->print();
              }
          }
      }
      while (term);
  
      loader->ReleaseBuffer(block);
  
      if (video_settings) {
          video_settings->fullscreen_mode.width  = screen_width;
          video_settings->fullscreen_mode.height = screen_height;
  
          if (screen_depth == 16)
          video_settings->fullscreen_mode.format = VideoMode::FMT_R5G6B5;
          else
          video_settings->fullscreen_mode.format = VideoMode::FMT_X8R8G8B8;
  
          video_settings->shadows    = shadows_enabled;
          video_settings->specmaps   = spec_maps_enabled;
          video_settings->bumpmaps   = bump_maps_enabled;
          video_settings->enable_vs  = vertex_shader;
          video_settings->enable_ps  = pixel_shader;
          video_settings->depth_bias = depth_bias;
      }
  
      if (video) {
          video->SetShadowEnabled(shadows_enabled);
          video->SetSpecMapEnabled(spec_maps_enabled);
          video->SetBumpMapEnabled(bump_maps_enabled);
      }
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::SaveVideoConfig(const char* filename)
  {
      if (!video_settings)
      return;
  
      FILE* f = fopen(filename, "w");
      if (f) {
          fprintf(f, "VIDEO\n\n");
          fprintf(f, "width:     %4d\n", video_settings->fullscreen_mode.width);
          fprintf(f, "height:    %4d\n", video_settings->fullscreen_mode.height);
          fprintf(f, "depth:     %4d\n", video_settings->fullscreen_mode.format == VideoMode::FMT_R5G6B5 ? 16 : 32);
          fprintf(f, "\n");
          fprintf(f, "max_tex:   %4d\n", max_tex_size);
          fprintf(f, "primary3D: %s\n",  "true");
          fprintf(f, "gamma:     %4d\n", gamma);
          fprintf(f, "\n");
          fprintf(f, "terrain_detail_level:   %d\n", Terrain::DetailLevel());
          fprintf(f, "terrain_texture_enable: %true\n");
          fprintf(f, "\n");
  
          fprintf(f, "shadows:   %s\n",  video_settings->shadows  ? "true" : "false");
          fprintf(f, "spec_maps: %s\n",  video_settings->specmaps ? "true" : "false");
          fprintf(f, "bump_maps: %s\n",  video_settings->bumpmaps ? "true" : "false");
          fprintf(f, "bias:      %f\n",  video_settings->depth_bias);
          fprintf(f, "\n");
  
          fprintf(f, "flare:     %s\n",  lens_flare ? "true" : "false");
          fprintf(f, "corona:    %s\n",  corona     ? "true" : "false");
          fprintf(f, "nebula:    %s\n",  nebula     ? "true" : "false");
          fprintf(f, "dust:      %d\n",  dust);
  
          if (CameraDirector::GetRangeLimit() != 300e3)
          fprintf(f, "   cam_range_max: %f,\n", CameraDirector::GetRangeLimit());
  
          fclose(f);
      }
  
      video_changed = false;
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::InvalidateTextureCache()
  {
      if (video)
      video->InvalidateCache();
  }
  
  // +--------------------------------------------------------------------+
  
  void
  Starshatter::ExecCutscene(const char* msn_file, const char* path)
  {
      if (InCutscene() || !msn_file || !*msn_file) return;
  
      if (!world)
      CreateWorld();
  
      cutscene_mission  = new(__FILE__,__LINE__) Mission(0);
      cutscene_basetime = StarSystem::GetBaseTime();
  
      if (cutscene_mission->Load(msn_file, path)) {
          Sim* sim = (Sim*) world;
  
          if (sim) {
              bool        dynamic  = false;
              Campaign*   campaign = Campaign::GetCampaign();
  
              if (campaign && campaign->IsDynamic())
              dynamic = true;
  
              sim->UnloadMission();
              sim->LoadMission(cutscene_mission, true); // attempt to preload the tex cache
              sim->ExecMission();
              sim->ShowGrid(false);
              player_ship = sim->GetPlayerShip();
  
              Print("  Cutscene Instantiated.\n");
  
              UpdateWorld();
          }
      }
      else {
          delete cutscene_mission;
          cutscene_mission  = 0;
          cutscene_basetime = 0;
      }
  }
  
  void
  Starshatter::BeginCutscene()
  {
      Sim* sim = Sim::GetSim();
  
      if (cutscene == 0 && !sim->IsNetGame()) {
          HUDView* hud_view = HUDView::GetInstance();
          if (hud_view)
          hud_view->SetHUDMode(HUDView::HUD_MODE_OFF);
  
          if (sim->GetPlayerShip())
          sim->GetPlayerShip()->SetControls(0);
  
          AudioConfig* audio_cfg = AudioConfig::GetInstance();
  
          if (audio_cfg) {
              cut_efx_volume = audio_cfg->GetEfxVolume();
              cut_wrn_volume = audio_cfg->GetWrnVolume();
          }
  
          Ship::SetFlightModel(Ship::FM_ARCADE);
      }
  
      cutscene++;
  }
  
  void
  Starshatter::EndCutscene()
  {
      cutscene--;
  
      if (cutscene == 0) {
          DisplayView* disp_view = DisplayView::GetInstance();
          if (disp_view)
          disp_view->ClearDisplay();
  
          HUDView* hud_view = HUDView::GetInstance();
          if (hud_view)
          hud_view->SetHUDMode(HUDView::HUD_MODE_TAC);
  
          Sim* sim = Sim::GetSim();
          if (sim->GetPlayerShip())
          sim->GetPlayerShip()->SetControls(sim->GetControls());
  
          if (cam_dir) {
              cam_dir->SetViewOrbital(0);
              CameraDirector::SetRangeLimits(10, CameraDirector::GetRangeLimit());
              cam_dir->SetOrbitPoint(PI/4,PI/4,1);
              cam_dir->SetOrbitRates(0,0,0);
          }
  
          AudioConfig* audio_cfg = AudioConfig::GetInstance();
  
          if (audio_cfg) {
              audio_cfg->SetEfxVolume(cut_efx_volume);
              audio_cfg->SetWrnVolume(cut_wrn_volume);
          }
  
          Player* p = Player::GetCurrentPlayer();
          if (p)
          Ship::SetFlightModel(p->FlightModel());
      }
  }
  
  void
  Starshatter::EndMission()
  {
      if (cutscene_mission) {
          Sim* sim = Sim::GetSim();
  
          if (sim && sim->GetMission() == cutscene_mission) {
              ShipStats::Initialize();
              sim->UnloadMission();
  
              // restore world clock (true => absolute time reference)
              if (cutscene_basetime != 0)
              StarSystem::SetBaseTime(cutscene_basetime, true);
  
              delete cutscene_mission;
              cutscene_mission  = 0;
              cutscene_basetime = 0;
  
              return;
          }
      }
  
      SetGameMode(Starshatter::PLAN_MODE);
  }
  
  Mission*
  Starshatter::GetCutsceneMission() const
  {
      return cutscene_mission;
  }
  
  const char*
  Starshatter::GetSubtitles() const
  {
      if (cutscene_mission)
      return cutscene_mission->Subtitles();
  
      return "";
  }
  
  // +--------------------------------------------------------------------+
  
  int
  Starshatter::GetLobbyMode()
  {
      return lobby_mode;
  }
  
  void
  Starshatter::SetLobbyMode(int mode)
  {
      lobby_mode = mode;
  }
  
  void
  Starshatter::StartLobby()
  {
      if (!net_lobby) {
          if (lobby_mode == NET_LOBBY_SERVER) {
              NetServerConfig::Initialize();
              NetServerConfig* server_config = NetServerConfig::GetInstance();
  
              if (server_config)
              net_lobby = new(__FILE__,__LINE__) NetLobbyServer;
          }
  
          else {
              NetClientConfig* client_config = NetClientConfig::GetInstance();
              if (client_config)
              client_config->Login();
  
              net_lobby = NetLobby::GetInstance();
          }
      }
  
      lobby_mode = NET_LOBBY_CLIENT;
  }
  
  void
  Starshatter::StopLobby()
  {
      if (net_lobby) {
          if (net_lobby->IsServer()) {
              delete net_lobby;
              NetServerConfig::Close();
          }
  
          else {
              NetClientConfig* client_config = NetClientConfig::GetInstance();
              if (client_config)
              client_config->Logout();
          }
  
          net_lobby = 0;
      }
  
      lobby_mode = NET_LOBBY_CLIENT;
  }
  
  void
  Starshatter::StopNetGame()
  {
      // local server:
      NetLobby* lobby = NetLobby::GetInstance();
  
      if (lobby && lobby->IsServer()) {
          lobby->GameStop();
      }
  
      // client connected to remote server:
      else {
          NetClientConfig* config = NetClientConfig::GetInstance();
          if (config && config->GetHostRequest()) {
              config->Login();
  
              NetLobbyClient* conn = config->GetConnection();
  
              if (conn) {
                  conn->GameStop();
                  conn->SelectMission(0);
              }
          }
      }
  }
  