bool Rect::contains(const Int2 &point) const { return (point.getX() >= this->getLeft()) && (point.getY() >= this->getTop()) && (point.getX() < this->getRight()) && (point.getY() < this->getBottom()); }
int ListBox::getClickedIndex(const Int2 &point) const { // Only the Y component of the point really matters here. const int index = this->scrollIndex + ((point.getY() - this->point.getY()) / this->font.getCharacterHeight()); return index; }
Renderer::Renderer(int width, int height, bool fullscreen, double letterboxAspect) { Debug::mention("Renderer", "Initializing."); assert(width > 0); assert(height > 0); this->letterboxAspect = letterboxAspect; // Initialize window. The SDL_Surface is obtained from this window. this->window = [width, height, fullscreen]() { std::string title = "OpenTESArena"; return fullscreen ? SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP) : SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_RESIZABLE); }(); Debug::check(this->window != nullptr, "Renderer", "SDL_CreateWindow"); // Initialize renderer context. this->renderer = this->createRenderer(); // Use window dimensions, just in case it's fullscreen and the given width and // height are ignored. Int2 windowDimensions = this->getWindowDimensions(); // Initialize native frame buffer. this->nativeTexture = this->createTexture(Renderer::DEFAULT_PIXELFORMAT, SDL_TEXTUREACCESS_TARGET, windowDimensions.getX(), windowDimensions.getY()); Debug::check(this->nativeTexture != nullptr, "Renderer", "Couldn't create native frame buffer, " + std::string(SDL_GetError())); // Initialize 320x200 frame buffer. this->originalTexture = this->createTexture(Renderer::DEFAULT_PIXELFORMAT, SDL_TEXTUREACCESS_TARGET, Renderer::ORIGINAL_WIDTH, Renderer::ORIGINAL_HEIGHT); // Don't initialize the game world buffer until the 3D renderer is initialized. this->clProgram = nullptr; this->gameWorldTexture = nullptr; this->fullGameWindow = false; // Set the original frame buffer to not use transparency by default. this->useTransparencyBlending(false); }
Int2 Renderer::originalPointToNative(const Int2 &originalPoint) const { // From original point to letterbox point. double originalXPercent = static_cast<double>(originalPoint.getX()) / static_cast<double>(Renderer::ORIGINAL_WIDTH); double originalYPercent = static_cast<double>(originalPoint.getY()) / static_cast<double>(Renderer::ORIGINAL_HEIGHT); const auto letterbox = this->getLetterboxDimensions(); Int2 letterboxPoint( static_cast<double>(letterbox.w) * originalXPercent, static_cast<double>(letterbox.h) * originalYPercent); // Then from letterbox point to native point. Int2 nativePoint( letterboxPoint.getX() + letterbox.x, letterboxPoint.getY() + letterbox.y); return nativePoint; }
Int2 Renderer::nativePointToOriginal(const Int2 &nativePoint) const { // From native point to letterbox point. Int2 windowDimensions = this->getWindowDimensions(); const auto letterbox = this->getLetterboxDimensions(); Int2 letterboxPoint( nativePoint.getX() - letterbox.x, nativePoint.getY() - letterbox.y); // Then from letterbox point to original point. double letterboxXPercent = static_cast<double>(letterboxPoint.getX()) / static_cast<double>(letterbox.w); double letterboxYPercent = static_cast<double>(letterboxPoint.getY()) / static_cast<double>(letterbox.h); Int2 originalPoint( static_cast<double>(Renderer::ORIGINAL_WIDTH) * letterboxXPercent, static_cast<double>(Renderer::ORIGINAL_HEIGHT) * letterboxYPercent); return originalPoint; }
SDL_Surface *Renderer::getScreenshot() const { const Int2 dimensions = this->getWindowDimensions(); SDL_Surface *screenshot = Surface::createSurfaceWithFormat( dimensions.getX(), dimensions.getY(), Renderer::DEFAULT_BPP, Renderer::DEFAULT_PIXELFORMAT); int status = SDL_RenderReadPixels(this->renderer, nullptr, screenshot->format->format, screenshot->pixels, screenshot->pitch); if (status == 0) { Debug::mention("Renderer", "Screenshot taken."); } else { Debug::crash("Renderer", "Couldn't take screenshot, " + std::string(SDL_GetError())); } return screenshot; }
GameWorldPanel::GameWorldPanel(GameState *gameState) : Panel(gameState) { assert(gameState->gameDataIsActive()); this->playerNameTextBox = [gameState]() { int x = 17; int y = 154; Color color(215, 121, 8); std::string text = gameState->getGameData()->getPlayer().getFirstName(); auto &font = gameState->getFontManager().getFont(FontName::Char); auto alignment = TextAlignment::Left; return std::unique_ptr<TextBox>(new TextBox( x, y, color, text, font, alignment, gameState->getRenderer())); }(); this->automapButton = []() { auto function = [](GameState *gameState) { std::unique_ptr<Panel> automapPanel(new AutomapPanel(gameState)); gameState->setPanel(std::move(automapPanel)); }; return std::unique_ptr<Button>(new Button(function)); }(); this->characterSheetButton = []() { auto function = [](GameState *gameState) { std::unique_ptr<Panel> sheetPanel(new CharacterPanel(gameState)); gameState->setPanel(std::move(sheetPanel)); }; return std::unique_ptr<Button>(new Button(function)); }(); this->logbookButton = []() { auto function = [](GameState *gameState) { std::unique_ptr<Panel> logbookPanel(new LogbookPanel(gameState)); gameState->setPanel(std::move(logbookPanel)); }; return std::unique_ptr<Button>(new Button(function)); }(); this->pauseButton = []() { auto function = [](GameState *gameState) { std::unique_ptr<Panel> pausePanel(new PauseMenuPanel(gameState)); gameState->setPanel(std::move(pausePanel)); }; return std::unique_ptr<Button>(new Button(function)); }(); this->worldMapButton = []() { auto function = [](GameState *gameState) { std::unique_ptr<Panel> mapPanel(new WorldMapPanel(gameState)); gameState->setPanel(std::move(mapPanel)); }; return std::unique_ptr<Button>(new Button(function)); }(); // Set all of the cursor regions relative to the current window. const Int2 screenDims = gameState->getRenderer().getWindowDimensions(); this->updateCursorRegions(screenDims.getX(), screenDims.getY()); }
void GameWorldPanel::render(Renderer &renderer) { assert(this->getGameState()->gameDataIsActive()); // Clear full screen. renderer.clearNative(); renderer.clearOriginal(); // Draw game world onto the native frame buffer. The game world buffer // might not completely fill up the native buffer (bottom corners), so // clearing the native buffer beforehand is still necessary. renderer.renderWorld(); // Set screen palette. auto &textureManager = this->getGameState()->getTextureManager(); textureManager.setPalette(PaletteFile::fromName(PaletteName::Default)); // Set original frame buffer blending to true. renderer.useTransparencyBlending(true); // Draw game world interface. const auto &gameInterface = textureManager.getTexture( TextureFile::fromName(TextureName::GameWorldInterface)); renderer.drawToOriginal(gameInterface.get(), 0, Renderer::ORIGINAL_HEIGHT - gameInterface.getHeight()); // Draw player portrait. const auto &player = this->getGameState()->getGameData()->getPlayer(); const auto &headsFilename = PortraitFile::getHeads( player.getGenderName(), player.getRaceName(), true); const auto &portrait = textureManager.getTextures(headsFilename) .at(player.getPortraitID()); const auto &status = textureManager.getTextures( TextureFile::fromName(TextureName::StatusGradients)).at(0); renderer.drawToOriginal(status.get(), 14, 166); renderer.drawToOriginal(portrait.get(), 14, 166); // Draw compass slider (the actual headings). +X is north, +Z is east. // Should do some sin() and cos() functions to get the pixel offset. auto *compassSlider = textureManager.getSurface( TextureFile::fromName(TextureName::CompassSlider)); Texture compassSliderSegment = [&renderer, &compassSlider]() { SDL_Surface *segmentTemp = Surface::createSurfaceWithFormat(32, 7, Renderer::DEFAULT_BPP, Renderer::DEFAULT_PIXELFORMAT); SDL_Rect clipRect; clipRect.x = 60; // Arbitrary offset until compass rotation works. clipRect.y = 0; clipRect.w = segmentTemp->w; clipRect.h = segmentTemp->h; SDL_BlitSurface(compassSlider, &clipRect, segmentTemp, nullptr); SDL_Texture *segment = renderer.createTextureFromSurface(segmentTemp); SDL_FreeSurface(segmentTemp); return Texture(segment); }(); renderer.drawToOriginal(compassSliderSegment.get(), (Renderer::ORIGINAL_WIDTH / 2) - (compassSliderSegment.getWidth() / 2), compassSliderSegment.getHeight()); // Draw compass frame over the headings. const auto &compassFrame = textureManager.getTexture( TextureFile::fromName(TextureName::CompassFrame)); renderer.drawToOriginal(compassFrame.get(), (Renderer::ORIGINAL_WIDTH / 2) - (compassFrame.getWidth() / 2), 0); // If the player's class can't use magic, show the darkened spell icon. if (!player.getCharacterClass().canCastMagic()) { const auto &nonMagicIcon = textureManager.getTexture( TextureFile::fromName(TextureName::NoSpell)); renderer.drawToOriginal(nonMagicIcon.get(), 91, 177); } // Draw text: player name. renderer.drawToOriginal(this->playerNameTextBox->getTexture(), this->playerNameTextBox->getX(), this->playerNameTextBox->getY()); // Scale the original frame buffer onto the native one. // This shouldn't be done for the game world interface because it needs to // clamp to the screen edges, not the letterbox edges. // Fix this eventually... again. renderer.drawOriginalToNative(); // Draw cursor, depending on its position on the screen. const Int2 mousePosition = this->getMousePosition(); const Texture &cursor = [this, &mousePosition, &textureManager]() -> const Texture& // Interesting how this return type isn't deduced in MSVC. { // See which arrow cursor region the native mouse is in. for (int i = 0; i < this->nativeCursorRegions.size(); ++i) { if (this->nativeCursorRegions.at(i)->contains(mousePosition)) { return textureManager.getTextures( TextureFile::fromName(TextureName::ArrowCursors)).at(i); } } // If not in any of the arrow regions, use the default sword cursor. return textureManager.getTexture( TextureFile::fromName(TextureName::SwordCursor)); }(); renderer.drawToNative(cursor.get(), mousePosition.getX(), mousePosition.getY(), static_cast<int>(cursor.getWidth() * this->getCursorScale()), static_cast<int>(cursor.getHeight() * this->getCursorScale())); // Set the transparency blending back to normal (off). renderer.useTransparencyBlending(false); }