diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..6ca03a0 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,5 @@ +Smeti random premikanje - ok +vnos imena kot text box +neko sledenje igralcu (nasprotnikov) +zavezniki naj nekaj delajo +fullscreen \ No newline at end of file diff --git a/include/game/agame/background.hpp b/include/game/agame/background.hpp index d32fbfd..c359a22 100644 --- a/include/game/agame/background.hpp +++ b/include/game/agame/background.hpp @@ -27,7 +27,7 @@ GAME_ENTITY(Background) int mPendingLevelStage = 0; // Periodic friendly spawn settings float mFriendlySpawnAvgInterval = 6.f; // average seconds between spawns - int mMaxAutoFriendlies = 7; // hard cap for total active friendlies + int mMaxAutoFriendlies = 2; // hard cap for total active friendlies std::shared_ptr mSeaTex; std::shared_ptr mEnemyTex; std::shared_ptr mTrashTex; diff --git a/include/game/agame/enemy.hpp b/include/game/agame/enemy.hpp index 4369637..2169cee 100644 --- a/include/game/agame/enemy.hpp +++ b/include/game/agame/enemy.hpp @@ -16,5 +16,8 @@ namespace Game::AGame { float mMoveSpeedY = 0.f; float mDirectionChangeTimer = 0.f; float mShoreSpawnCooldown = 0.f; + bool mFollowingPlayer = false; + static constexpr float FOLLOW_DISTANCE = 300.f; + static constexpr float FOLLOW_SPEED = 35.f; END_GAME_ENTITY() } \ No newline at end of file diff --git a/include/game/agame/friendly.hpp b/include/game/agame/friendly.hpp index fb82fbc..6d97078 100644 --- a/include/game/agame/friendly.hpp +++ b/include/game/agame/friendly.hpp @@ -14,5 +14,11 @@ namespace Game::AGame { float mMoveSpeedX = 0.f; float mMoveSpeedY = 0.f; float mDirectionChangeTimer = 0.f; + bool mOnSea = false; + static constexpr float CLEANUP_RADIUS = 50.f; + static constexpr int CLEANUP_SCORE_BONUS = 5; + static constexpr float LAND_SPEED_MIN = 20.f; + static constexpr float LAND_SPEED_MAX = 50.f; + static constexpr float SEA_SPEED = 14.f; END_GAME_ENTITY() } diff --git a/include/game/gamemanager.hpp b/include/game/gamemanager.hpp index e12f438..7b49afc 100644 --- a/include/game/gamemanager.hpp +++ b/include/game/gamemanager.hpp @@ -17,6 +17,7 @@ namespace Game { using clock = std::chrono::steady_clock; enum class GameStateEnum { + MENU, RUNNING, PAUSED, STOPPED diff --git a/include/renderer/renderer.hpp b/include/renderer/renderer.hpp index cf5ed34..39b18eb 100644 --- a/include/renderer/renderer.hpp +++ b/include/renderer/renderer.hpp @@ -11,6 +11,8 @@ namespace Game::Renderer { float camY; int screenW; int screenH; + float scaleX; // Scale from logical (1280) to actual screen width + float scaleY; // Scale from logical (720) to actual screen height } RendererConfig; class Renderer { diff --git a/src/game/agame/background.cpp b/src/game/agame/background.cpp index a1fa864..08e209d 100644 --- a/src/game/agame/background.cpp +++ b/src/game/agame/background.cpp @@ -57,7 +57,10 @@ namespace Game::AGame { mSeaTex->setTiled(true); } mTiledScale = 0.5f; - SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &mW, &mH); + + // Use logical world dimensions (1280×720) not actual screen size + mW = 1280; + mH = 720; // Land boundary: left 1/3 of map in centered coordinates // For 1280px window: -640 (left) + 426.67 (1/3) = -213.33 @@ -128,9 +131,11 @@ namespace Game::AGame { const float viewBottom = mH / 2.f; Object::Transform tS; + tS.x = 0.f; + tS.y = 0.f; tS.rotation = 0.f; - tS.scaleX = 6.f; - tS.scaleY = 6.f; + tS.scaleX = 4.0f; + tS.scaleY = 4.0f; const float halfFriendlyW = mFriendlyTex->getWidth() * tS.adjustedScaleX() / 2.f; const float halfFriendlyH = mFriendlyTex->getHeight() * tS.adjustedScaleY() / 2.f; @@ -194,8 +199,8 @@ namespace Game::AGame { Object::Transform tS; tS.rotation = 0.f; - tS.scaleX = 7.f; - tS.scaleY = 7.f; + tS.scaleX = 4.7f; + tS.scaleY = 4.7f; const float halfEnemyW = mEnemyTex->getWidth() * tS.adjustedScaleX() / 2.f; const float halfEnemyH = mEnemyTex->getHeight() * tS.adjustedScaleY() / 2.f; for (int i = 0; i < enemyCount; ++i) { @@ -268,9 +273,11 @@ namespace Game::AGame { const bool spawnSea = sideRoll < static_cast(seaProb * 100.f); Object::Transform tS; + tS.x = 0.f; + tS.y = 0.f; tS.rotation = 0.f; - tS.scaleX = 6.f; - tS.scaleY = 6.f; + tS.scaleX = 4.0f; + tS.scaleY = 4.0f; const float viewLeft = -mW / 2.f; const float viewRight = mW / 2.f; const float viewTop = -mH / 2.f; @@ -320,7 +327,8 @@ namespace Game::AGame { } void Background::onWindowResized(int newWidth, int newHeight) { - mW = newWidth; - mH = newHeight; + // Always maintain logical world dimensions (1280×720) + mW = 1280; + mH = 720; } } \ No newline at end of file diff --git a/src/game/agame/enemy.cpp b/src/game/agame/enemy.cpp index af3d381..4c7108e 100644 --- a/src/game/agame/enemy.cpp +++ b/src/game/agame/enemy.cpp @@ -44,14 +44,31 @@ namespace Game::AGame { const float dyv = py - ey; mIsVisible = (dxv * dxv + dyv * dyv) <= (revealRadius * revealRadius); - // Semi-random movement with periodic direction changes - mDirectionChangeTimer += deltaTime; - if (mDirectionChangeTimer > 2.0f) { - const float angle = static_cast(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f; - const float speed = 20.f + static_cast(Utils::getUtils().rirng32(0, 30)); - mMoveSpeedX = std::cos(angle) * speed; - mMoveSpeedY = std::sin(angle) * speed; - mDirectionChangeTimer = 0.f; + // Check if player is on land (not in ship mode) and within follow distance + const float distanceToPlayer = std::sqrt(dxv * dxv + dyv * dyv); + const bool playerOnLand = !player->isShipMode(); + const bool withinFollowRange = distanceToPlayer <= FOLLOW_DISTANCE; + + if (playerOnLand && withinFollowRange) { + // Follow player: calculate direction and move at constant speed + mFollowingPlayer = true; + if (distanceToPlayer > 1.f) { // Avoid division by zero + mMoveSpeedX = (dxv / distanceToPlayer) * FOLLOW_SPEED; + mMoveSpeedY = (dyv / distanceToPlayer) * FOLLOW_SPEED; + } + } else { + // Revert to random movement when player is on sea or out of range + mFollowingPlayer = false; + + // Semi-random movement with periodic direction changes + mDirectionChangeTimer += deltaTime; + if (mDirectionChangeTimer > 2.0f) { + const float angle = static_cast(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f; + const float speed = 20.f + static_cast(Utils::getUtils().rirng32(0, 30)); + mMoveSpeedX = std::cos(angle) * speed; + mMoveSpeedY = std::sin(angle) * speed; + mDirectionChangeTimer = 0.f; + } } // Move enemy @@ -68,9 +85,9 @@ namespace Game::AGame { const float halfWidth = entityWidth / 2.f; const float halfHeight = entityHeight / 2.f; - // Get window dimensions for boundary calculations - int w = 0, h = 0; - SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); + // Use logical world dimensions (1280×720) not actual screen size + constexpr int w = 1280; + constexpr int h = 720; const float leftEdge = -w / 2.f + 25.f; if (mTransform.x - halfWidth < leftEdge) { diff --git a/src/game/agame/friendly.cpp b/src/game/agame/friendly.cpp index 9c7c395..7cd7f1d 100644 --- a/src/game/agame/friendly.cpp +++ b/src/game/agame/friendly.cpp @@ -1,9 +1,12 @@ #include #include #include +#include #include +#include #include #include +#include #include #include @@ -12,26 +15,105 @@ namespace Game::AGame { mZIndex = 20; addComponent(); LOG("Zaveznik zagnan: " << getName()); + + const float landBoundaryX = GameManager::getSharedData("terrainLandBoundaryX"); + const float entityWidth = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() : 0.f; + mOnSea = (mTransform.x + entityWidth / 2.f) > landBoundaryX; - // Initialize random movement - const float angle = static_cast(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f; - const float speed = 20.f + static_cast(Utils::getUtils().rirng32(0, 30)); - mMoveSpeedX = std::cos(angle) * speed; - mMoveSpeedY = std::sin(angle) * speed; + if (!mOnSea) { + // Initialize random movement for land friendlies + const float angle = static_cast(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f; + const float speed = LAND_SPEED_MIN + static_cast(Utils::getUtils().rirng32(0, static_cast(LAND_SPEED_MAX - LAND_SPEED_MIN))); + mMoveSpeedX = std::cos(angle) * speed; + mMoveSpeedY = std::sin(angle) * speed; + } else { + mMoveSpeedX = 0.f; + mMoveSpeedY = 0.f; + } mDirectionChangeTimer = 0.f; } void Friendly::update(float deltaTime) { - (void)deltaTime; + // Auto-cleanup nearby trash + const float fw = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() : 0.f; + const float fh = getTexture() ? getTexture()->getHeight() * mTransform.adjustedScaleY() : 0.f; + const float friendlyCenterX = mTransform.x + fw / 2.f; + const float friendlyCenterY = mTransform.y + fh / 2.f; + + const float landBoundaryX = GameManager::getSharedData("terrainLandBoundaryX"); + mOnSea = friendlyCenterX > landBoundaryX; + + if (mOnSea) { + auto snapshot = State::GameState::getInstance().getEntitiesSnapshot(); + Trash* nearestTrash = nullptr; + float nearestDistanceSquared = std::numeric_limits::max(); + + for (auto* entity : snapshot) { + if (!entity) continue; + auto* trash = dynamic_cast(entity); + if (!trash) continue; + + const float tw = trash->getTexture() ? trash->getTexture()->getWidth() * trash->getTransform()->adjustedScaleX() : 0.f; + const float th = trash->getTexture() ? trash->getTexture()->getHeight() * trash->getTransform()->adjustedScaleY() : 0.f; + const float trashCenterX = trash->getTransform()->x + tw / 2.f; + const float trashCenterY = trash->getTransform()->y + th / 2.f; + const float dx = trashCenterX - friendlyCenterX; + const float dy = trashCenterY - friendlyCenterY; + const float distanceSquared = dx * dx + dy * dy; + + if (distanceSquared < nearestDistanceSquared) { + nearestDistanceSquared = distanceSquared; + nearestTrash = trash; + } + } + + if (nearestTrash) { + const float tw = nearestTrash->getTexture() ? nearestTrash->getTexture()->getWidth() * nearestTrash->getTransform()->adjustedScaleX() : 0.f; + const float th = nearestTrash->getTexture() ? nearestTrash->getTexture()->getHeight() * nearestTrash->getTransform()->adjustedScaleY() : 0.f; + const float trashCenterX = nearestTrash->getTransform()->x + tw / 2.f; + const float trashCenterY = nearestTrash->getTransform()->y + th / 2.f; + const float dx = trashCenterX - friendlyCenterX; + const float dy = trashCenterY - friendlyCenterY; + const float distance = std::sqrt(dx * dx + dy * dy); + + if (distance > 0.0001f) { + mMoveSpeedX = (dx / distance) * SEA_SPEED; + mMoveSpeedY = (dy / distance) * SEA_SPEED; + } + } + } else { + // Semi-random movement with periodic direction changes on land + mDirectionChangeTimer += deltaTime; + if (mDirectionChangeTimer > 2.0f) { + const float angle = static_cast(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f; + const float speed = LAND_SPEED_MIN + static_cast(Utils::getUtils().rirng32(0, static_cast(LAND_SPEED_MAX - LAND_SPEED_MIN))); + mMoveSpeedX = std::cos(angle) * speed; + mMoveSpeedY = std::sin(angle) * speed; + mDirectionChangeTimer = 0.f; + } + } + + auto snapshot = State::GameState::getInstance().getEntitiesSnapshot(); + for (auto* entity : snapshot) { + if (!entity) continue; + auto* trash = dynamic_cast(entity); + if (!trash) continue; + + // Calculate distance to trash + const float tw = trash->getTexture() ? trash->getTexture()->getWidth() * trash->getTransform()->adjustedScaleX() : 0.f; + const float th = trash->getTexture() ? trash->getTexture()->getHeight() * trash->getTransform()->adjustedScaleY() : 0.f; + const float trashCenterX = trash->getTransform()->x + tw / 2.f; + const float trashCenterY = trash->getTransform()->y + th / 2.f; + const float dx = friendlyCenterX - trashCenterX; + const float dy = friendlyCenterY - trashCenterY; + const float distanceSquared = dx * dx + dy * dy; - // Semi-random movement with periodic direction changes - mDirectionChangeTimer += deltaTime; - if (mDirectionChangeTimer > 2.0f) { - const float angle = static_cast(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f; - const float speed = 20.f + static_cast(Utils::getUtils().rirng32(0, 30)); - mMoveSpeedX = std::cos(angle) * speed; - mMoveSpeedY = std::sin(angle) * speed; - mDirectionChangeTimer = 0.f; + if (distanceSquared <= CLEANUP_RADIUS * CLEANUP_RADIUS) { + // Clean up this trash: award points and remove it + GameManager::setSharedData("gameScore", GameManager::getSharedData("gameScore") + CLEANUP_SCORE_BONUS); + GameManager::setSharedData("trashActiveCount", std::max(0, GameManager::getSharedData("trashActiveCount") - 1)); + GameManager::destroyEntity(trash); + } } // Move friendly @@ -39,25 +121,26 @@ namespace Game::AGame { mTransform.y += mMoveSpeedY * deltaTime; // Clamp to land section - const float landBoundaryX = GameManager::getSharedData("terrainLandBoundaryX"); - const float entityWidth = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() : 0.f; - const float entityHeight = getTexture() ? getTexture()->getHeight() * mTransform.adjustedScaleY() : 0.f; - const float halfWidth = entityWidth / 2.f; - const float halfHeight = entityHeight / 2.f; + const float halfWidth = fw / 2.f; + const float halfHeight = fh / 2.f; - // Get window dimensions for boundary calculations - int w = 0, h = 0; - SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); + // Use logical world dimensions (1280×720) not actual screen size + constexpr int w = 1280; + constexpr int h = 720; const float leftEdge = -w / 2.f + 25.f; if (mTransform.x - halfWidth < leftEdge) { mTransform.x = leftEdge + halfWidth; mMoveSpeedX = std::abs(mMoveSpeedX); } - if (mTransform.x + halfWidth > landBoundaryX - 25.f) { + if (!mOnSea && mTransform.x + halfWidth > landBoundaryX - 25.f) { mTransform.x = landBoundaryX - 25.f - halfWidth; mMoveSpeedX = -std::abs(mMoveSpeedX); } + if (mOnSea && mTransform.x - halfWidth < landBoundaryX + 25.f) { + mTransform.x = landBoundaryX + 25.f + halfWidth; + mMoveSpeedX = std::abs(mMoveSpeedX); + } if (mTransform.y - halfHeight < -h / 2.f + 25.f) { mTransform.y = -h / 2.f + 25.f + halfHeight; mMoveSpeedY = std::abs(mMoveSpeedY); diff --git a/src/game/agame/hudtext.cpp b/src/game/agame/hudtext.cpp index 2906fac..f814fe5 100644 --- a/src/game/agame/hudtext.cpp +++ b/src/game/agame/hudtext.cpp @@ -13,9 +13,9 @@ namespace Game::AGame { void HUDText::update(float deltaTime) { (void)deltaTime; - int windowW = 0; - int windowH = 0; - SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &windowW, &windowH); + // Use logical world dimensions (1280×720) not actual screen size + constexpr int windowW = 1280; + constexpr int windowH = 720; float camX = 0.f; float camY = 0.f; Object::Camera::getInstance().getPosition(camX, camY); diff --git a/src/game/agame/player.cpp b/src/game/agame/player.cpp index 7a2f5ef..46e0130 100644 --- a/src/game/agame/player.cpp +++ b/src/game/agame/player.cpp @@ -26,9 +26,9 @@ namespace Game::AGame { return; } - int w = 0; - int h = 0; - SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); + // Use logical world dimensions (1280×720) not actual screen size + constexpr int w = 1280; + constexpr int h = 720; const float halfWidth = spawnTex->getWidth() * mTransform.adjustedScaleX() / 2.f; const float halfHeight = spawnTex->getHeight() * mTransform.adjustedScaleY() / 2.f; @@ -64,8 +64,8 @@ namespace Game::AGame { Game::GameManager::setSharedData("gameStage", 1); Game::GameManager::setSharedData("gameScore", 0); - mTransform.scaleX = 8.f; - mTransform.scaleY = 8.f; + mTransform.scaleX = 5.3f; + mTransform.scaleY = 5.3f; if (!mShipTex) { mShipTex = mTex; @@ -74,9 +74,9 @@ namespace Game::AGame { mGroundTex = mTex; } - int w = 0; - int h = 0; - SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); + // Use logical world dimensions (1280×720) not actual screen size + constexpr int w = 1280; + constexpr int h = 720; const float halfWidth = mTex->getWidth() * mTransform.adjustedScaleX() / 2.f; const float halfHeight = mTex->getHeight() * mTransform.adjustedScaleY() / 2.f; @@ -134,9 +134,9 @@ namespace Game::AGame { if (Input::isKeyPressed(SDL_SCANCODE_D)) { mTransform.x += mSpeed * deltaTime; mIsFlipped = true; } mSpeed = Input::isKeyPressed(SDL_SCANCODE_LSHIFT) ? 400.f : 200.f; - int w = 0; - int h = 0; - SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &w, &h); + // Use logical world dimensions (1280×720) not actual screen size + constexpr int w = 1280; + constexpr int h = 720; const float entityWidth = mTex ? mTex->getWidth() * mTransform.adjustedScaleX() : 0.f; const float entityHeight = mTex ? mTex->getHeight() * mTransform.adjustedScaleY() : 0.f; const float minX = -w / 2.f; diff --git a/src/game/agame/trash.cpp b/src/game/agame/trash.cpp index df23123..a492595 100644 --- a/src/game/agame/trash.cpp +++ b/src/game/agame/trash.cpp @@ -11,15 +11,20 @@ namespace Game::AGame { void Trash::update(float deltaTime) { (void)deltaTime; - if (mSeaOnly) { + /*if (mSeaOnly) { const float landBoundaryX = GameManager::getSharedData("terrainLandBoundaryX"); const float margin = 25.f; const float halfWidth = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() / 2.f : 0.f; if (mTransform.x - halfWidth < landBoundaryX + margin) { mTransform.x = landBoundaryX + margin + halfWidth; } - } - return; + }*/ + + // Naključno premikanje + mTransform.x += static_cast(Utils::getUtils().rirng32(-50, 50)) * deltaTime; + mTransform.y += static_cast(Utils::getUtils().rirng32(-50, 50)) * deltaTime; + + //return; } void Trash::onCollisionEnter(Object::Entity* other) { diff --git a/src/game/gamemanager.cpp b/src/game/gamemanager.cpp index e4b0a5d..2cdbe28 100644 --- a/src/game/gamemanager.cpp +++ b/src/game/gamemanager.cpp @@ -2,7 +2,7 @@ #include namespace Game { - GameStateEnum GameManager::mCurrentGameState = GameStateEnum::RUNNING; + GameStateEnum GameManager::mCurrentGameState = GameStateEnum::MENU; void GameManager::run(std::stop_token stopToken) { using namespace std::chrono_literals; diff --git a/src/main.cpp b/src/main.cpp index 043e155..4fa284e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,14 +30,25 @@ int main() { State::GameState::getInstance().addEntity(std::make_unique("BG", std::make_shared("../resources/bgtest.png", window.getRenderer()->getSDLRenderer()), Object::DEFAULT_TRANSFORM)); - auto* player = dynamic_cast(State::GameState::getInstance().addEntity(std::make_unique("Player", std::make_shared("../resources/l3ladja.png", window.getRenderer()->getSDLRenderer()), Object::DEFAULT_TRANSFORM))); + auto* player = dynamic_cast( + State::GameState::getInstance().addEntity( + std::make_unique( + "Player", + std::make_shared( + "../resources/l3ladja.png", + window.getRenderer()->getSDLRenderer() + ), + Object::DEFAULT_TRANSFORM + ) + ) + ); if (player) { player->addComponent(); player->setShipTexture(std::make_shared("../resources/l3ladja.png", window.getRenderer()->getSDLRenderer())); player->setGroundTexture(std::make_shared("../resources/l3player.png", window.getRenderer()->getSDLRenderer())); } - State::GameState::getInstance().addEntity(std::make_unique("HUD", std::make_shared("../resources/roboto.ttf", window.getRenderer()->getSDLRenderer(), 72, "HUDFont"), Object::Transform{0.f, 0.f, 0.f, 1.f, 1.f}, 320.f, 40.f)); + State::GameState::getInstance().addEntity(std::make_unique("HUD", std::make_shared("../resources/roboto.ttf", window.getRenderer()->getSDLRenderer(), 60, "HUDFont"), Object::Transform{0.f, 0.f, 0.f, 1.f, 1.f}, 320.f, 40.f)); window.run(); diff --git a/src/object/entity.cpp b/src/object/entity.cpp index 0fda1fd..39c902b 100644 --- a/src/object/entity.cpp +++ b/src/object/entity.cpp @@ -46,7 +46,7 @@ namespace Game::Object { SDL_GetTextureSize(mTex->getSDLTexture(), &w, &h); SDL_FRect dst; - dst.w = w * mTransform.scaleX * UNIVERSAL_SCALE_COEFFICIENT; // 1.f is HUGE, so this is just a constant to make the default scale more reasonable + dst.w = w * mTransform.scaleX * UNIVERSAL_SCALE_COEFFICIENT; dst.h = h * mTransform.scaleY * UNIVERSAL_SCALE_COEFFICIENT; // Top-left origin; Account for camera position (center the camera on the screen) @@ -73,7 +73,7 @@ namespace Game::Object { SDL_GetTextureSize(mTex->getSDLTexture(), &tileW, &tileH); SDL_FRect dst; - dst.w = tileW * mTiledScale; // Tile size is the original texture size multiplied by the universal scale coefficient (ignoring the entity's scale, since that only affects how many times the texture is tiled, not the size of each tile) + dst.w = tileW * mTiledScale; dst.h = tileH * mTiledScale; // Top-left origin; Account for camera position (center the camera on the screen) diff --git a/src/object/ui/uitextbox.cpp b/src/object/ui/uitextbox.cpp index d8085eb..e1ad491 100644 --- a/src/object/ui/uitextbox.cpp +++ b/src/object/ui/uitextbox.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace Game::Object { @@ -112,15 +113,29 @@ namespace Game::Object { bool UITextBox::isFocused() const { return mIsFocused; } bool UITextBox::isMouseInsideBox() const { - const float mouseX = Input::getMouseX(); - const float mouseY = Input::getMouseY(); + // Get screen-space mouse coordinates + const float screenMouseX = Input::getMouseX(); + const float screenMouseY = Input::getMouseY(); + // Get window dimensions + int windowW = 0, windowH = 0; + SDL_GetWindowSizeInPixels(SDL_GetMouseFocus(), &windowW, &windowH); + + // Get camera position + float camX = 0.f, camY = 0.f; + Object::Camera::getInstance().getPosition(camX, camY); + + // Convert screen coordinates to world coordinates + const float worldMouseX = screenMouseX - windowW / 2.f + camX; + const float worldMouseY = screenMouseY - windowH / 2.f + camY; + + // Check bounds in world space const float left = mTransform.x - mConfig.paddingX; const float right = left + mBoxWidth + 2.f * mConfig.paddingX; const float top = mTransform.y - mConfig.paddingY; - const float bottom = top + mBoxHeight + 2.f * mConfig.paddingY; + const float bottom = top + mBoxHeight + 2.f * mConfig.paddingY; - return mouseX >= left && mouseX <= right && mouseY >= top && mouseY <= bottom; + return worldMouseX >= left && worldMouseX <= right && worldMouseY >= top && worldMouseY <= bottom; } void UITextBox::refreshVisualText() { diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp index 94375a6..096fd37 100644 --- a/src/renderer/renderer.cpp +++ b/src/renderer/renderer.cpp @@ -60,8 +60,18 @@ namespace Game::Renderer { Object::Camera::getInstance().getPosition(camX, camY); int screenW, screenH; SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &screenW, &screenH); - // Pass the config to avoid wasting time recalculating it for every entity, since it's not gonna change during the frame - RendererConfig config{ camX, camY, screenW, screenH }; + + // Logical game world is 1280x720; scale rendering if fullscreen is at different resolution + static constexpr int LOGICAL_WIDTH = 1280; + static constexpr int LOGICAL_HEIGHT = 720; + const float scaleX = static_cast(screenW) / LOGICAL_WIDTH; + const float scaleY = static_cast(screenH) / LOGICAL_HEIGHT; + + // Apply SDL render scale to handle fullscreen scaling + SDL_SetRenderScale(mRenderer, scaleX, scaleY); + + // Pass the config WITHOUT scaling factors (SDL handles it now) + RendererConfig config{ camX, camY, LOGICAL_WIDTH, LOGICAL_HEIGHT, 1.0f, 1.0f }; try { Game::State::GameState::getInstance().withEntitiesLocked([&](auto& entities) { @@ -89,6 +99,9 @@ namespace Game::Renderer { } mPresent(); + + // Reset render scale for next frame + SDL_SetRenderScale(mRenderer, 1.0f, 1.0f); } void Renderer::mClear() { diff --git a/src/window/window.cpp b/src/window/window.cpp index 83d4247..fde5809 100644 --- a/src/window/window.cpp +++ b/src/window/window.cpp @@ -41,7 +41,7 @@ namespace Game::Window { Audio::Audio::getInstance().init(); - mWindow = SDL_CreateWindow(title.c_str(), width, height, 0); + mWindow = SDL_CreateWindow(title.c_str(), width, height, SDL_WINDOW_FULLSCREEN); if (!mWindow) { ERROR("Failed to create window: " << SDL_GetError()); SDL_Quit(); @@ -95,10 +95,6 @@ namespace Game::Window { mRunning = false; } - if (event.type == SDL_EVENT_WINDOW_ENTER_FULLSCREEN) { - SDL_SetWindowFullscreen(mWindow, false); - } - // Window resize event - update the renderer's viewport if (event.type == SDL_EVENT_WINDOW_RESIZED) { std::scoped_lock lock(mMutex);