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(); }
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; }
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; }
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; }