示例#1
0
void GuiManager::runNewMatch(const char *stageName, char **teamNames,
                             int numUserTeams) {
  tpsFactor_ = 0.5;
  nextDrawTime_ = 1;
  showedResults_ = false;

  sf::RenderWindow *window;
  bool maintainWindowScale = false;
  double prevScale = 1.0;
  double backingScale = getBackingScaleFactor();
  int screenWidth =
      backingScale * sf::VideoMode::getDesktopMode().width;
  int screenHeight =
      backingScale * sf::VideoMode::getDesktopMode().height;
  int dockSize = backingScale * DOCK_SIZE;

  if (window_ != 0) {
    if (restarting_) {
      prevScale = ((double) (window_->getSize().x - dockSize)) / viewWidth_;
      maintainWindowScale = true;
    }
  }
  
  // On Mac OS X, we need to init SFML before the wxWidgets stuff below or we
  // hit some unexplainable crashes when we delete an SFML window. I don't know
  // why, I've merely devised a work-around. Judging from some SFML forum
  // threads, it sounds likely to be an issue with nightmare-ish video drivers.
  if (window_ == 0) {
    window = initMainWindow(backingScale * screenWidth,
                            backingScale * (screenHeight - 75));
  } else {
    window = window_;
  }

  if (!restarting_) {
    saveCurrentMatchSettings(stageName, teamNames, numUserTeams);
  }
  if (engine_ != 0) {
    delete engine_;
    engine_ = 0;
  }

  if (restarting_) {
    stageConsole_->clear();
    guiPrintHandler_->restartMode();
  } else {
    destroyStageConsole();
    stageConsole_ = new OutputConsole(stageName, CONSOLE_SHIP_STAGE,
                                      menuBarMaker_);
    stageConsole_->Hide();

    if (guiPrintHandler_ != 0) {
      delete guiPrintHandler_;
      guiPrintHandler_ = 0;
    }
    guiPrintHandler_ = new GuiPrintHandler(stageConsole_, 0, menuBarMaker_);
  }

  engine_ = new BerryBotsEngine(guiPrintHandler_, fileManager_,
                                resourcePath().c_str());

  Stage *stage = engine_->getStage();
  if (restarting_) {
    stage->setGfxEnabled(stageConsole_->isChecked());
  }
  stageConsole_->setListener(new StageConsoleListener(stage));
  try {
    engine_->initStage(getStagesDir().c_str(), stageName,
                       getCacheDir().c_str());
    engine_->initShips(getShipsDir().c_str(), teamNames, numUserTeams,
                       getCacheDir().c_str());
    teamConsoles_ = guiPrintHandler_->getTeamConsoles();
  } catch (EngineException *e) {
    errorConsole_->print(stageName);
    errorConsole_->print(": ");
    errorConsole_->println(e->what());
    wxMessageDialog errorMessage(NULL, e->what(),
        "BerryBots engine init failed", wxOK | wxICON_EXCLAMATION);
    errorMessage.ShowModal();
    delete engine_;
    engine_ = 0;
    restarting_ = false;
    newMatchDialog_->Show();
    delete e;
    return;
  }

  viewWidth_ = stage->getWidth() + (STAGE_MARGIN * 2);
  viewHeight_ = stage->getHeight() + (STAGE_MARGIN * 2);
  double windowScale;
  if (restarting_ && maintainWindowScale) {
    windowScale = prevScale;
  } else {
    windowScale =
        std::min(backingScale, std::min(
            ((double) screenWidth - dockSize) / viewWidth_,
            ((double) screenHeight) / viewHeight_));
  }
  unsigned int targetWidth = round(windowScale * viewWidth_) + dockSize;
  unsigned int targetHeight = round(windowScale * viewHeight_);

  window->setSize(sf::Vector2u(targetWidth, targetHeight));

  interrupted_ = false;
  paused_ = false;
  newMatchDialog_->Hide();
  packageStageDialog_->Hide();
  packageShipDialog_->Hide();
  gfxHandler_ = new GfxEventHandler();
  stage->addEventHandler((EventHandler*) gfxHandler_);

  // TODO: If/when SFML getPosition() works, adjust the window position to
  //       keep the whole window on the screen (if necessary). Might be worth
  //       platform-specific implementations using getSystemHandle() if that
  //       doesn't happen in a reasonable timeframe.
  //       We could just set it to (0, 0) or centered on screen every time, but
  //       that seems potentially super annoying to a user - less annoying than
  //       having to move the window occasionally if you switch to a bigger
  //       stage that goes off-screen.

  gfxManager_->initBbGfx(window, backingScale, viewHeight_, stage,
                         engine_->getTeams(), engine_->getNumTeams(),
                         engine_->getShips(), engine_->getNumShips());
  gfxManager_->initViews(window, viewWidth_, viewHeight_);
  window->setVisible(true);
  drawFrame(window);

#ifdef __WXOSX__
  // SFML 2.1+ has a weird scaling issue. Some of it is documented here:
  // https://github.com/SFML/SFML/issues/474
  // Basically, OS X caps the window height, but SFML reports the requested
  // height instead of the actual height. My work-around is to manually trigger
  // a resize after drawing one frame.
  processMainWindowEvents(window, gfxManager_, viewWidth_, viewHeight_);
  gfxManager_->onResize(window, viewWidth_, viewHeight_);
#endif

  runCurrentMatch();
}
示例#2
0
int main(int argc, char *argv[]) {
  Zipper *zipper = new GuiZipper();
  FileManager *fileManager = new FileManager(zipper);
  CliPackageReporter *packageReporter = new CliPackageReporter();
  fileManager->setListener(packageReporter);

  char *shipsBaseDir = fileManager->getAbsFilePath(SHIPS_SUBDIR);
  char *stagesBaseDir = fileManager->getAbsFilePath(STAGES_SUBDIR);

  if (flagExists(argc, argv, "packstage")) {
    char **stageInfo = parseFlag(argc, argv, "packstage", 2);
    if (stageInfo == 0) {
      printUsage();
    } else {
      // TODO: add a new flag for obfuscating source code
      bool obfuscate = false;
      try {
        char *stageAbsName = fileManager->getAbsFilePath(stageInfo[0]);
        char *stageName =
            fileManager->parseRelativeFilePath(stagesBaseDir, stageAbsName);
        if (stageName == 0) {
          std::cout << "Stage must be located under " << STAGES_SUBDIR
                    << "/ subdirectory: " << stageInfo[0] << std::endl;
        } else {
          fileManager->packageStage(stagesBaseDir, stageName, stageInfo[1],
                                    CACHE_SUBDIR, TMP_SUBDIR, obfuscate, true);
          delete stageName;
        }
        delete stageAbsName;
      } catch (std::exception *e) {
        std::cout << "BerryBots encountered an error:" << std::endl;
        std::cout << "  " << e->what() << std::endl;
        delete e;
      }
      delete stageInfo;
    }
    return 0;
  }

  if (flagExists(argc, argv, "packbot")) {
    char **shipInfo = parseFlag(argc, argv, "packbot", 2);
    if (shipInfo == 0) {
      printUsage();
    } else {
      // TODO: add a new flag for obfuscating source code
      bool obfuscate = false;
      try {
        char *shipAbsName = fileManager->getAbsFilePath(shipInfo[0]);
        char *shipName =
           fileManager->parseRelativeFilePath(shipsBaseDir, shipAbsName);
        if (shipName == 0) {
          std::cout << "Ship must be located under " << SHIPS_SUBDIR
                    << "/ subdirectory: " << shipInfo[0] << std::endl;
        } else {
          fileManager->packageShip(shipsBaseDir, shipName, shipInfo[1],
                                   CACHE_SUBDIR, TMP_SUBDIR, obfuscate, true);
          delete shipName;
        }
        delete shipAbsName;
      } catch (std::exception *e) {
        std::cout << "BerryBots encountered an error:" << std::endl;
        std::cout << "  " << e->what() << std::endl;
        delete e;
      }
      delete shipInfo;
    }
    return 0;
  }
  
  bool nodisplay = flagExists(argc, argv, "nodisplay");
  bool saveReplay = flagExists(argc, argv, "savereplay");
  int optArgsOffset = (nodisplay ? 1 : 0) + (saveReplay ? 1 : 0);
  if (argc < 3 + optArgsOffset) {
    printUsage();
  }

  srand(time(NULL));
  CliPrintHandler *printHandler = new CliPrintHandler();
  BerryBotsEngine *engine =
      new BerryBotsEngine(printHandler, fileManager, resourcePath().c_str());
  Stage *stage = engine->getStage();

  char *stageAbsName = fileManager->getAbsFilePath(argv[1 + optArgsOffset]);
  char *stageName =
      fileManager->parseRelativeFilePath(stagesBaseDir, stageAbsName);
  if (stageName == 0) {
    std::cout << "Stage must be located under " << STAGES_SUBDIR
              << "/ subdirectory: " << argv[1 + optArgsOffset] << std::endl;
    return 0;
  }
  try {
    engine->initStage(stagesBaseDir, stageName, CACHE_SUBDIR);
  } catch (EngineException *e) {
    delete stageAbsName;
    delete stageName;
    std::cout << "BerryBots initialization failed:" << std::endl;
    std::cout << "  " << e->what() << std::endl;
    delete e;
    return 0;
  }
  delete stageAbsName;
  delete stageName;

  int firstTeam = (2 + optArgsOffset);
  int numTeams = argc - firstTeam;
  char **teams = new char*[numTeams];
  for (int x = 0; x < numTeams; x++) {
    char *teamAbsName = fileManager->getAbsFilePath(argv[x + firstTeam]);
    char *teamName =
        fileManager->parseRelativeFilePath(shipsBaseDir, teamAbsName);
    if (teamName == 0) {
      std::cout << "Ship must be located under " << SHIPS_SUBDIR
                << "/ subdirectory: " << argv[x + firstTeam] << std::endl;
      return 0;
    }
    teams[x] = teamName;
    delete teamAbsName;
  }

  printHandler->setNumTeams(numTeams);
  try {
    engine->initShips(shipsBaseDir, teams, numTeams, CACHE_SUBDIR);
  } catch (EngineException *e) {
    std::cout << "BerryBots initialization failed:" << std::endl;
    std::cout << "  " << e->what() << std::endl;
    delete e;
    return 0;
  }
  printHandler->updateTeams(engine->getTeams());

  GfxManager *gfxManager;
  sf::RenderWindow *window = 0;
  GfxEventHandler *gfxHandler = 0;
  unsigned int viewWidth = stage->getWidth() + (STAGE_MARGIN * 2);
  unsigned int viewHeight = stage->getHeight() + (STAGE_MARGIN * 2);
  if (!nodisplay) {
    gfxHandler = new GfxEventHandler();
    stage->addEventHandler((EventHandler*) gfxHandler);

    unsigned int screenWidth = sf::VideoMode::getDesktopMode().width;
    unsigned int screenHeight = sf::VideoMode::getDesktopMode().height;
    double windowScale = std::min(1.0,
        std::min(((double) screenWidth) / viewWidth,
                 ((double) screenHeight) / viewHeight));
    unsigned int targetWidth = round(windowScale * viewWidth);
    unsigned int targetHeight = round(windowScale * viewHeight);

    gfxManager = new GfxManager(false);
    window = new sf::RenderWindow(sf::VideoMode(targetWidth, targetHeight),
        "BerryBots", sf::Style::Default, sf::ContextSettings(0, 0, 16, 2, 0));
    gfxManager->initViews(window, viewWidth, viewHeight);
    gfxManager->initBbGfx(window, viewHeight, stage, engine->getTeams(),
        engine->getNumTeams(), engine->getShips(), engine->getNumShips(),
        resourcePath());
    window->clear();
    gfxManager->drawGame(window, stage, engine->getShips(),
        engine->getNumShips(), engine->getGameTime(), gfxHandler, false, false,
        0);
    window->display();
  }
  
  time_t realTime1;
  time_t realTime2;
  time(&realTime1);
  int realSeconds = 0;
  
  try {
    while ((nodisplay || window->isOpen()) && !engine->isGameOver()) {
      engine->processTick();
  
      if (!nodisplay) {
        sf::Event event;
        bool resized = false;
        while (window->pollEvent(event)) {
          if (event.type == sf::Event::Closed) {
            window->close();
          }
          if (event.type == sf::Event::KeyPressed
              && event.key.code == sf::Keyboard::Escape) {
            window->close();
          }
          if (event.type == sf::Event::Resized && !resized) {
            resized = true;
            gfxManager->onResize(window, viewWidth, viewHeight);
          }
        }
    
        window->clear();
        gfxManager->drawGame(window, stage, engine->getShips(),
            engine->getNumShips(), engine->getGameTime(), gfxHandler, false,
            false, 0);
        window->display();
      }
  
      time(&realTime2);
      if (realTime2 - realTime1 > 0) {
        realSeconds++;
        if (realSeconds % 10 == 0) {
          std::cout << "TPS: "
                    << (((double) engine->getGameTime()) / realSeconds)
                    << std::endl;
        }
      }
      realTime1 = realTime2;
    }
  } catch (EngineException *e) {
    std::cout << "BerryBots encountered an error:" << std::endl;
    std::cout << "  " << e->what() << std::endl;
    delete e;
    return 0;
  }
  
  if (!nodisplay) {
    gfxManager->destroyBbGfx();
    delete window;
  }
  
  const char* winnerName = engine->getWinnerName();
  if (winnerName != 0) {
    std::cout << std::endl<< winnerName << " wins! Congratulations!"
              << std::endl;
  }

  std::cout << std::endl << "Results:" << std::endl;
  Team **rankedTeams = engine->getRankedTeams();
  bool hasScores = false;
  for (int x = 0; x < numTeams; x++) {
    if (rankedTeams[x]->result.score != 0) {
      hasScores = true;
      break;
    }
  }
  TeamResult *firstResult = &(rankedTeams[0]->result);
  int numStats = firstResult->numStats;
  char **statKeys = 0;
  if (numStats > 0) {
    statKeys = new char*[firstResult->numStats];
    for (int x = 0; x < numStats; x++) {
      statKeys[x] = new char[strlen(firstResult->stats[x]->key) + 1];
      strcpy(statKeys[x], firstResult->stats[x]->key);
    }
  }

  for (int x = 0; x < engine->getNumTeams(); x++) {
    TeamResult *result = &(rankedTeams[x]->result);
    if (result->showResult) {
      std::cout << "    " << rankedTeams[x]->name << ":" << std::endl;
      std::cout << "        Rank: ";
      if (result->rank == 0) {
        std::cout << "-";
      } else {
        std::cout << result->rank;
      }
      std::cout << std::endl;
      if (hasScores) {
        std::cout << "        Score: " << round(result->score, 2) << std::endl;
      }

      for (int y = 0; y < numStats; y++) {
        char *key = statKeys[y];
        bool found = false;
        for (int z = 0; z < result->numStats; z++) {
        char *resultKey = result->stats[z]->key;
        if (strcmp(key, resultKey) == 0) {
          std::cout << "        " << key << ": "
              << round(result->stats[z]->value, 2) << std::endl;
          found = true;
          break;
        }
        }
        if (!found) {
        std::cout << "        " << key << ": -" << std::endl;
        }
      }
    }
  }

  std::cout << std::endl << "CPU time used per tick (microseconds):"
            << std::endl;
  for (int x = 0; x < engine->getNumTeams(); x++) {
    Team *team = engine->getTeam(x);
    if (!team->stageShip && !team->disabled) {
      std::cout << "  " << team->name << ": "
                << (team->totalCpuTime / team->totalCpuTicks) << std::endl;
    }
  }

  if (realSeconds > 0) {
    std::cout << std::endl << "TPS: "
              << (((double) engine->getGameTime()) / realSeconds) << std::endl;
  }

  if (saveReplay) {
    ReplayBuilder *replayBuilder = engine->getReplayBuilder();

    // TODO: move this into a function in the engine
    Team **rankedTeams = engine->getRankedTeams();
    replayBuilder->setResults(rankedTeams, engine->getNumTeams());

    char *filename = 0;
    char *absFilename = 0;
    do {
      if (filename != 0) {
        delete filename;
      }
      if (absFilename != 0) {
        delete absFilename;
      }
      filename = replayFilename(stage->getName());
      char *filePath = fileManager->getFilePath(REPLAYS_SUBDIR, filename);
      absFilename = fileManager->getAbsFilePath(filePath);
      delete filePath;
    } while (fileManager->fileExists(absFilename));
    replayBuilder->saveReplay(filename);
    std::cout << std::endl << "Saved replay to: " << REPLAYS_SUBDIR << "/"
              << filename << std::endl;
    delete filename;
    delete absFilename;
  }

  std::cout << std::endl;

  delete engine;
  for (int x = 0; x < numTeams; x++) {
    delete teams[x];
  }
  delete teams;
  delete rankedTeams;
  for (int x = 0; x < numStats; x++) {
    delete statKeys[x];
  }
  delete statKeys;
  delete printHandler;
  if (!nodisplay) {
    delete gfxManager;
  }
  delete packageReporter;
  delete fileManager;
  delete zipper;
  delete shipsBaseDir;
  delete stagesBaseDir;

  return 0;
}
示例#3
0
char* StagePreview::savePreviewImage(sf::RenderWindow *window,
    BerryBotsEngine *engine, unsigned int &targetWidth,
    unsigned int &targetHeight) {
  Stage *stage = engine->getStage();
  double backingScale = getBackingScaleFactor();
  unsigned int viewWidth = stage->getWidth() + (2 * STAGE_MARGIN);
  unsigned int viewHeight = stage->getHeight() + (2 * STAGE_MARGIN);
  unsigned int screenWidth = backingScale * MAX_PREVIEW_WIDTH;
  unsigned int screenHeight = backingScale * MAX_PREVIEW_HEIGHT;
  double windowScale =
      std::min(backingScale, std::min(((double) screenWidth) / viewWidth,
                                      ((double) screenHeight) / viewHeight));
  targetWidth = round(windowScale * viewWidth);
  targetHeight = round(windowScale * viewHeight);

#ifdef __WXGTK__
  // Since setSize() doesn't work reliably, we create it inline on Linux.
  window = new sf::RenderWindow(
      sf::VideoMode(targetWidth, targetHeight), "Preview",
      sf::Style::None,
      sf::ContextSettings(0, 0, (isAaDisabled() ? 0 : 4), 2, 0));
  window->setVisible(false);
#else
  window->setSize(sf::Vector2u(targetWidth, targetHeight));
#endif

  Team **teams = new Team*[1];
  teams[0] = new Team;
  strcpy(teams[0]->name, "PreviewTeam");
  teams[0]->numRectangles = 0;
  teams[0]->numLines = 0;
  teams[0]->numCircles = 0;
  teams[0]->numTexts = 0;
  Ship **ships = new Ship*[1];
  Ship *ship = new Ship;
  ShipProperties *properties = new ShipProperties;
  properties->shipR = properties->shipG = properties->shipB = 255;
  properties->laserR = properties->laserB = 0;
  properties->laserG = 255;
  properties->thrusterR = 255;
  properties->thrusterG = properties->thrusterB = 0;
  strcpy(properties->name, "PreviewShip");
  ship->properties = properties;
  ship->thrusterAngle = ship->thrusterForce = 0;
  Point2D *start = stage->getStart();
  ship->x = start->getX();
  ship->y = start->getY();
  ship->alive = true;
  ship->showName = ship->energyEnabled = false;
  ships[0] = ship;
  teams[0]->numTexts = 0;
  stage->setTeamsAndShips(teams, 1, ships, 1);

  previewGfxManager_->initBbGfx(window, backingScale, viewHeight, stage, teams,
                                1, ships, 1);
  previewGfxManager_->initViews(window, viewWidth, viewHeight);

  GfxEventHandler *gfxHandler = new GfxEventHandler();
  window->clear();
  previewGfxManager_->drawGame(window, stage, ships, 1, 0, gfxHandler, false,
                               false, 0);

  std::stringstream filenameStream;
  filenameStream << (rand() % 10000000) << ".png";
  char *filename = fileManager_->getFilePath(getTmpDir().c_str(),
                                             filenameStream.str().c_str());
  char *absFilename = fileManager_->getAbsFilePath(filename);
  delete filename;

  sf::Image previewImage = window->capture();
  fileManager_->createDirectoryIfNecessary(getTmpDir().c_str());
  previewImage.saveToFile(absFilename);
#ifdef __WXGTK__
  delete window;
#endif
  previewGfxManager_->destroyBbGfx();

  delete gfxHandler;
  delete properties;
  delete teams[0];
  delete teams;

  return absFilename;
}
示例#4
0
GameGUI* GameGUIBuilder::create() {
    FILE_LOG(logDEBUG) << "CONFIGURATION INITIALIZED";
    GameGUI *gameGUI = GameGUI::getInstance();

    Json::Value root;
    Json::Reader reader;

    ifstream gameConfig(this->configFilePath.c_str(), std::ifstream::binary);

    if (!gameConfig.good()) {
        MessageError fileNotFound(ERROR_FILE_NOT_FOUND, FILE_CONFIG_NOT_FOUND, LOG_LEVEL_ERROR);
        Log<Output2FILE>::logMsgError(fileNotFound);
        return createDefault();
    }
    bool parsingSuccessful = reader.parse(gameConfig, root, false);

    if (!parsingSuccessful) {
        MessageError parseException(ERROR_PARSER_JSON, reader.getFormattedErrorMessages(), LOG_LEVEL_ERROR);
        Log<Output2FILE>::logMsgError(parseException);
        return createDefault();
    }

    try {
        const Json::Value value1 = root.get(JSON_KEY_PERSONAJES, 0);
        const Json::Value value2 = root.get(JSON_KEY_CAPAS,0);
        Json::Value value3 = root[JSON_KEY_ESCENARIO];
        Json::Value value14 = root[JSON_KEY_VENTANA];
        Json::Value value5 = root[JSON_KEY_PELEA];
        Json::Value value6 = root[JSON_KEY_JOYSTICKS];
        Json::Value value7 = root[JSON_KEY_SECUENCES];
    } catch(std::exception const & e) {
        FILE_LOG(logDEBUG) << "Corrupt JSON File. Exception: "<<e.what();
        return createDefault(); //TODO: Validate create default
    }
    Window* window = jsonGetWindow(root);
    Stage* stage = jsonGetStage(root,window->width);

    gameGUI->setWindow(window);
    gameGUI->setStage(stage);

    float ratioX = getRatio(window->widthPx, window->width,"X");
    float ratioY = getRatio(window->heightPx, stage->getHeight(),"Y");

    TextureManager::Instance()->ratioHeight = ratioY;
    TextureManager::Instance()->ratioWidth = ratioX;

    vector<Layer*> layers = jsonGetLayers(root, ratioX, ratioY, window, stage);
    if (layers.size()==0) {
        layers = buildLayersByDefault(ratioX, ratioY,window,stage);
    }

    vector<Character*> characters = jsonGetCharacters(root, ratioX, ratioY, stage);
    gameGUI->setCharacters(characters);

    //The fight is now being triggered by the MENU
    /*Fight* fight = jsonGetFight(root);
    gameGUI->setFight(fight);
    if (fight->getFighterOne()->getName() == fight->getFighterTwo()->getName()) {
    	fight->getFighterTwo()->setIsAlternativePlayer(true);
    }
    fight->getFighterOne()->setIsRightOriented(true);
    MKGame::Instance()->getObjectList().push_back(fight->getFighterOne());
    MKGame::Instance()->getObjectList().push_back(fight->getFighterTwo());*/

    gameGUI->setLayers(layers);

    //The fight is now being triggered by the MENU
    //vector<Character*> fightingCharacters;
    //fightingCharacters.push_back(fight->getFighterOne());
    //fightingCharacters.push_back(fight->getFighterTwo());
    /*gameGUI->setCharacters(fightingCharacters);
    createGameInfo(window, fightingCharacters, ratioX, ratioY);
    createThrowableObject(fightingCharacters, window, ratioX, ratioY);*/


    jsonGetJoysticks(root);

    jsonGetSecuences(root);

    vector<VisualEffect*> visualEffects = createVisualEffects(ratioX, ratioY, window, stage);

    gameGUI->setVisualEffects(visualEffects);

    FILE_LOG(logDEBUG) << "CONFIGURATION FINISHED";

    return gameGUI;
}
示例#5
0
void StagePreview::showPreview(sf::RenderWindow *window, const char *stageName,
                               int x, int y) {
  if (stageName_ != 0) {
    delete stageName_;
  }
  stageName_ = new char[strlen(stageName) + 1];
  strcpy(stageName_, stageName);

  SetPosition(wxPoint(x, y));
  infoSizer_->Clear(true);
  descSizer_->Clear(true);
  
  BerryBotsEngine *engine = new BerryBotsEngine(0, fileManager_, 0);
  Stage *stage = engine->getStage();
  try {
    engine->initStage(getStagesDir().c_str(), stageName, getCacheDir().c_str());
  } catch (EngineException *e) {
    wxMessageDialog errorMessage(NULL, e->what(), "Preview failure",
                                 wxOK | wxICON_EXCLAMATION);
    errorMessage.ShowModal();
    delete engine;
    delete e;
    return;
  }
  SetTitle(wxString::Format(wxT("%s"), stage->getName()));

  unsigned int targetWidth;
  unsigned int targetHeight;
  char *previewFilename =
      savePreviewImage(window, engine, targetWidth, targetHeight);
  wxImage previewImage(previewFilename);
  delete previewFilename;

  double backingScale = getBackingScaleFactor();
  if (backingScale > 1) {
    targetWidth /= backingScale;
    targetHeight /= backingScale;
  }
  visualPreview_->SetMinSize(wxSize(targetWidth, targetHeight));
  visualPreview_->SetMaxSize(wxSize(targetWidth, targetHeight));

#ifdef __WXOSX__
  int padding = 4;
#else
  int padding = 8;
#endif

  wxSizer *infoGrid = new wxFlexGridSizer(2, 0, padding);
  addInfo(infoGrid, "Name:", stage->getName());
  addInfo(infoGrid, "Size:",
      wxString::Format(wxT("%i x %i"), stage->getWidth(), stage->getHeight()));
  if (engine->getTeamSize() > 1) {
    addInfo(infoGrid, "Team size:", engine->getTeamSize());
  }
  addInfo(infoGrid, "Walls:", (stage->getWallCount() - 4));
  addInfo(infoGrid, "Zones:", stage->getZoneCount());
  addInfo(infoGrid, "Starts:", stage->getStartCount());
  int numStageShips = stage->getStageShipCount();
  if (numStageShips > 0) {
    char **stageShips = stage->getStageShips();
    for (int x = 0; x < numStageShips; x++) {
      const char *shipName = stageShips[x];
      if (shipName != 0) {
        int count = 1;
        for (int y = x + 1; y < numStageShips; y++) {
          const char *shipName2 = stageShips[y];
          if (shipName2 != 0 && strcmp(shipName, shipName2) == 0) {
            count++;
            stageShips[y] = 0;
          }
        }
        wxString wxShipName = (count == 1) ? wxString(stageShips[x])
            : wxString::Format(wxT("%s x%i"), shipName, count);
        addInfo(infoGrid, (x == 0 ? "Ships:" : ""), wxShipName);
      }
    }
  }
  infoSizer_->Add(infoGrid);

  char *description = fileManager_->getStageDescription(
      getStagesDir().c_str(), stageName, getCacheDir().c_str());
  if (description == 0) {
    std::string descstr("<No description>");
    description = new char[descstr.length() + 1];
    strcpy(description, descstr.c_str());
  }
  wxStaticText *descCtrl = new wxStaticText(mainPanel_, wxID_ANY, description);
  descSizer_->Add(descCtrl);
  delete description;

  mainPanel_->GetSizer()->SetSizeHints(mainPanel_);
  mainPanel_->Layout();
  Fit();
  mainPanel_->SetFocus();

  wxBitmap bitmap;
#ifdef __WINDOWS__
  bitmap = wxBitmap(previewImage);
#else
  bitmap.CreateScaled(targetWidth, targetHeight, wxBITMAP_SCREEN_DEPTH,
                      backingScale);
  wxMemoryDC dc(bitmap);
  double logicalScale = (backingScale > 1) ? (1.0 / backingScale) : 1;
  dc.SetLogicalScale(logicalScale, logicalScale);
  dc.DrawBitmap(wxBitmap(previewImage), 0, 0);
#endif

  // On Windows, if we set the bitmap before the Layout/Fit stuff, we get visual
  // artifacts when paging through the stages with up/down keys.
  visualPreview_->SetBitmap(bitmap);

  delete engine;
}