This commit is contained in:
2026-05-02 15:18:39 +02:00
parent 8be2cea49a
commit 56d567b77d
19 changed files with 634 additions and 94 deletions

View File

@@ -1,19 +1,37 @@
#include <game/agame/background.hpp>
#include <game/agame/player.hpp>
#include <window/window.hpp>
#include <object/camera.hpp>
#include <algorithm>
#include <utils.hpp>
namespace Game::AGame {
void Background::start() {
mSeaTex = std::make_shared<Game::Renderer::Texture>("../resources/l3sea.png", SDL_GetRenderer(Window::Window::getSDLWindowBackend()), "seaTex");
mEnemyTex = std::make_shared<Game::Renderer::Texture>("../resources/l3enemy.png", SDL_GetRenderer(Window::Window::getSDLWindowBackend()), "enemyTex");
mTrashTex = std::make_shared<Game::Renderer::Texture>("../resources/l3trash.png", SDL_GetRenderer(Window::Window::getSDLWindowBackend()), "trashTex");
mFriendlyTex = std::make_shared<Game::Renderer::Texture>("../resources/l3friendly.png", SDL_GetRenderer(Window::Window::getSDLWindowBackend()), "friendlyTex");
GameManager::setSharedData("enemyActiveCount", 0);
GameManager::setSharedData("trashActiveCount", 0);
GameManager::setSharedData("friendlyActiveCount", 0);
GameManager::setSharedData("gameStage", 1);
GameManager::setSharedData("gameWon", false);
GameManager::setSharedData("gameLost", false);
mZIndex = -1; // Ensure background renders behind other entities
mTex->setTiled(true); // Set the background texture to be tiled
if (mSeaTex) {
mSeaTex->setTiled(true);
}
mTiledScale = 0.5f;
SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &mW, &mH);
// Land boundary: left 1/3 of map in centered coordinates
// For 1280px window: -640 (left) + 426.67 (1/3) = -213.33
mLandBoundaryX = -static_cast<float>(mW) / 2.f + static_cast<float>(mW) / 3.f;
GameManager::setSharedData("terrainLandBoundaryX", mLandBoundaryX);
GameManager::setSharedData("enemyRevealRadius", 260.f);
mTransform.scaleX *= 10.f;
mTransform.scaleY *= 10.f;
@@ -21,72 +39,147 @@ namespace Game::AGame {
mTransform.y -= mTex->getHeight() * mTransform.adjustedScaleY() / 2.f;
LOG("W: " << mW << " H: " << mH);
spawnLevel(1);
}
mTransform.x = mW / 2.f - (mW / 3.f);
mTransform.y = -mH;
void Background::render(Game::Renderer::Renderer* renderer, Game::Renderer::RendererConfig config) {
if (!renderer || !mTex || !mSeaTex) {
return;
}
const float worldLeft = -static_cast<float>(mW) / 2.f;
const float worldRight = static_cast<float>(mW) / 2.f;
const float worldTop = -static_cast<float>(mH) / 2.f;
const float worldBottom = static_cast<float>(mH) / 2.f;
const float landLeft = worldLeft;
const float landRight = mLandBoundaryX;
const float seaLeft = mLandBoundaryX;
const float seaRight = worldRight;
auto drawSection = [&](const std::shared_ptr<Game::Renderer::Texture>& tex, float startX, float endX) {
if (!tex) {
return;
}
float tileW = 0.f;
float tileH = 0.f;
SDL_GetTextureSize(tex->getSDLTexture(), &tileW, &tileH);
tileW *= mTiledScale;
tileH *= mTiledScale;
if (tileW <= 0.f || tileH <= 0.f) {
return;
}
const float screenStartX = startX - config.camX + config.screenW / 2.f;
const float screenEndX = endX - config.camX + config.screenW / 2.f;
const float screenStartY = worldTop - config.camY + config.screenH / 2.f;
const float screenEndY = worldBottom - config.camY + config.screenH / 2.f;
for (float x = screenStartX; x < screenEndX; x += tileW) {
for (float y = screenStartY; y < screenEndY; y += tileH) {
SDL_FRect dst{ x, y, tileW, tileH };
SDL_RenderTexture(renderer->getSDLRenderer(), tex->getSDLTexture(), nullptr, &dst);
}
}
};
drawSection(mTex, landLeft, landRight);
drawSection(mSeaTex, seaLeft, seaRight);
}
void Background::spawnFriendly(int stage, int count) {
const float viewLeft = -mW / 2.f;
const float viewTop = -mH / 2.f;
const float viewBottom = mH / 2.f;
Object::Transform tS;
tS.rotation = 0.f;
tS.scaleX = 6.f;
tS.scaleY = 6.f;
const float halfFriendlyW = mFriendlyTex->getWidth() * tS.adjustedScaleX() / 2.f;
const float halfFriendlyH = mFriendlyTex->getHeight() * tS.adjustedScaleY() / 2.f;
for (int i = 0; i < count; ++i) {
tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewLeft + halfFriendlyW + 25.f), static_cast<int>(mLandBoundaryX - halfFriendlyW - 25.f)));
tS.y = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewTop + halfFriendlyH + 100.f), static_cast<int>(viewBottom - halfFriendlyH - 100.f)));
GameManager::instantiateEntity(std::make_unique<AGame::Friendly>("Friendly" + std::to_string(stage) + "_" + std::to_string(i + 1), mFriendlyTex, tS));
}
}
void Background::spawnLevel(int stage) {
const float viewLeft = -mW / 2.f;
const float viewRight = mW / 2.f;
const float viewTop = -mH / 2.f;
const float viewBottom = mH / 2.f;
const int enemyCount = 2 + stage * 2;
const int trashCount = 4 + stage * 3;
const int friendlyCount = 1 + (stage - 1);
GameManager::setSharedData("gameStage", stage);
GameManager::setSharedData("enemyActiveCount", enemyCount);
GameManager::setSharedData("trashActiveCount", trashCount);
GameManager::setSharedData("friendlyActiveCount", friendlyCount);
if (stage > 1) {
auto* player = GameManager::getEntityByName<Player>("Player");
if (player) {
player->respawnRandomSea(mLandBoundaryX);
}
}
Object::Transform tS;
tS.rotation = 0.f;
tS.scaleX = 7.f;
tS.scaleY = 7.f;
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) {
tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewLeft + halfEnemyW + 25.f), static_cast<int>(mLandBoundaryX - halfEnemyW - 25.f)));
tS.y = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewTop + halfEnemyH + 100.f), static_cast<int>(viewBottom - halfEnemyH - 100.f)));
GameManager::instantiateEntity(std::make_unique<AGame::Enemy>("Enemy" + std::to_string(stage) + "_" + std::to_string(i + 1), mEnemyTex, tS));
}
tS.scaleX = 5.5f;
tS.scaleY = 5.5f;
const float halfTrashW = mTrashTex->getWidth() * tS.adjustedScaleX() / 2.f;
const float halfTrashH = mTrashTex->getHeight() * tS.adjustedScaleY() / 2.f;
for (int i = 0; i < trashCount; ++i) {
tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(mLandBoundaryX + halfTrashW + 25.f), static_cast<int>(viewRight - halfTrashW - 25.f)));
tS.y = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewTop + halfTrashH + 100.f), static_cast<int>(viewBottom - halfTrashH - 100.f)));
GameManager::instantiateEntity(std::make_unique<AGame::Trash>("Trash" + std::to_string(stage) + "_" + std::to_string(i + 1), mTrashTex, tS));
}
spawnFriendly(stage, friendlyCount);
}
void Background::update(float deltaTime) {
mEnemySpawnTimer += deltaTime;
(void)deltaTime;
int cnt = GameManager::getSharedData<int>("enemyActiveCount");
if (mEnemySpawnTimer >= mTimeToSpawn && cnt < 5) {
mEnemySpawnTimer = 0.f; // RESET
GameManager::setSharedData("enemyActiveCount", cnt + 1);
// Spawn Enemy on grass
Object::Transform tS;
tS.scaleY = 7.f;
tS.scaleX = 7.f;
tS.rotation = 0.f;
const int enemyCount = GameManager::getSharedData<int>("enemyActiveCount");
const int trashCount = GameManager::getSharedData<int>("trashActiveCount");
const int stage = GameManager::getSharedData<int>("gameStage");
float camX, camY;
Object::Camera::getInstance().getPosition(camX, camY);
const float halfEnemyW = mEnemyTex->getWidth() * tS.adjustedScaleX() / 2.f;
const float halfEnemyH = mEnemyTex->getHeight() * tS.adjustedScaleY() / 2.f;
const float viewLeft = camX - (mW / 2.f);
const float viewRight = camX + (mW / 2.f);
const float viewTop = camY - (mH / 2.f);
const float viewBottom = camY + (mH / 2.f);
// Right 1/3 of the currently visible screen, in world coordinates.
int spawnMinX = static_cast<int>(viewLeft + (2.f * mW / 3.f) + halfEnemyW);
int spawnMaxX = static_cast<int>(viewRight - halfEnemyW - 25.f);
int spawnMinY = static_cast<int>(viewTop + halfEnemyH + 100.f);
int spawnMaxY = static_cast<int>(viewBottom - halfEnemyH - 100.f);
// Safety for tiny windows / huge sprites.
if (spawnMinX > spawnMaxX) spawnMinX = spawnMaxX = static_cast<int>(camX);
if (spawnMinY > spawnMaxY) spawnMinY = spawnMaxY = static_cast<int>(camY);
tS.x = static_cast<float>(Utils::getUtils().rirng32(spawnMinX, spawnMaxX));
tS.y = static_cast<float>(Utils::getUtils().rirng32(spawnMinY, spawnMaxY));
GameManager::instantiateEntity(std::make_unique<AGame::Enemy>("Enemy" + std::to_string(cnt + 1), mEnemyTex, tS));
// Spawn Trash at shoreline
tS.scaleX = 5.5f;
tS.scaleY = 5.5f;
tS.rotation = 0.f;
tS.x = mTransform.x - 75.f;
tS.y = static_cast<float>(Utils::getUtils().rirng32(spawnMinY, spawnMaxY));
GameManager::instantiateEntity(std::make_unique<AGame::Trash>("Trash" + std::to_string(cnt + 1), mTrashTex, tS));
if (mPendingLevelSpawn) {
if (enemyCount <= 0 && trashCount <= 0) {
GameManager::processPendingEntityRemovals();
mPendingLevelSpawn = false;
spawnLevel(mPendingLevelStage);
}
return;
}
/*const bool* state = SDL_GetKeyboardState(nullptr);
if (state[SDL_SCANCODE_P]) {
mTransform.scaleX *= 2.f;
mTransform.scaleY *= 2.f;
if (enemyCount <= 0 && trashCount <= 0) {
if (stage < mMaxLevels) {
mPendingLevelSpawn = true;
mPendingLevelStage = stage + 1;
} else if (!GameManager::getSharedData<bool>("gameWon")) {
GameManager::setSharedData("gameWon", true);
LOG("All levels cleared");
}
}
if (state[SDL_SCANCODE_L]) {
mTransform.scaleX *= 0.5f;
mTransform.scaleY *= 0.5f;
}*/
//mTransform.rotation += 1.f; // Rotate clockwise for testing
//mTransform.scaleX = 1.f + 1.f * std::sin(RUNNING_TIME() / 0.5f); // Pulsate scale for testing
//mTransform.scaleY = 1.f + 0.5f * std::cos(RUNNING_TIME() / 0.5f); // Pulsate scale for testing
//Object::Camera::getInstance().move(1.f, 0.f);
}
void Background::onWindowResized(int newWidth, int newHeight) {