dodatna logika

This commit is contained in:
2026-05-02 18:09:17 +02:00
parent 56d567b77d
commit e4389f035d
16 changed files with 239 additions and 37 deletions

View File

@@ -1,10 +1,43 @@
#include <game/agame/background.hpp>
#include <game/agame/player.hpp>
#include <window/window.hpp>
#include <state/gamestate.hpp>
#include <object/camera.hpp>
#include <object/components/boxcollider.hpp>
#include <algorithm>
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <utils.hpp>
namespace {
void writeFinalScoreFile(int score) {
std::ofstream file("score.txt", std::ios::trunc);
if (!file.is_open()) {
WARN("Failed to open score.txt for writing");
return;
}
const auto now = std::chrono::system_clock::now();
const std::time_t nowTime = std::chrono::system_clock::to_time_t(now);
std::tm localTime{};
#if defined(_WIN32)
localtime_s(&localTime, &nowTime);
#else
localtime_r(&nowTime, &localTime);
#endif
std::string playerName = Game::GameManager::getSharedData<std::string>("playerName");
if (playerName.empty()) playerName = "Player";
file << "Končna statistika igre:\n";
file << "Igralec: " << playerName << "\n";
file << "Točke: " << score << "\n";
file << "Datum: " << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S") << "\n";
}
}
namespace Game::AGame {
void Background::start() {
mSeaTex = std::make_shared<Game::Renderer::Texture>("../resources/l3sea.png", SDL_GetRenderer(Window::Window::getSDLWindowBackend()), "seaTex");
@@ -90,6 +123,7 @@ namespace Game::AGame {
void Background::spawnFriendly(int stage, int count) {
const float viewLeft = -mW / 2.f;
const float viewRight = mW / 2.f;
const float viewTop = -mH / 2.f;
const float viewBottom = mH / 2.f;
@@ -100,10 +134,38 @@ namespace Game::AGame {
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) {
// Split friendlies: most on land, a smaller number may appear on sea
// Decide how many friendlies appear on the sea.
// For stage 1 keep them on land; for later stages allow at least one on sea
int seaCount = 0;
if (stage > 1) {
seaCount = std::max(1, count / 3); // roughly one third on sea for later stages
}
int landCount = count - seaCount;
// Spawn land friendlies (left side)
for (int i = 0; i < landCount; ++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));
auto* friendly = State::GameState::getInstance().addEntity(std::make_unique<AGame::Friendly>("Friendly" + std::to_string(stage) + "_L" + std::to_string(i + 1), mFriendlyTex, tS));
if (friendly) {
if (auto* collider = friendly->getComponent<Object::Components::BoxCollider>()) {
collider->setScale(0.75f);
}
}
}
// Spawn a smaller number of friendlies on the sea (right side)
for (int i = 0; i < seaCount; ++i) {
tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(mLandBoundaryX + halfFriendlyW + 25.f), static_cast<int>(viewRight - 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)));
auto* friendly = State::GameState::getInstance().addEntity(std::make_unique<AGame::Friendly>("Friendly" + std::to_string(stage) + "_S" + std::to_string(i + 1), mFriendlyTex, tS));
if (friendly) {
if (auto* collider = friendly->getComponent<Object::Components::BoxCollider>()) {
collider->setScale(0.75f);
}
}
}
}
@@ -155,6 +217,34 @@ namespace Game::AGame {
spawnFriendly(stage, friendlyCount);
}
void Background::spawnTrashAt(const Object::Transform& tS, bool seaOnly) {
// Prepare transform and ensure sea-only pop is placed on the sea side
Object::Transform t = tS;
t.rotation = 0.f;
t.scaleX = 5.5f;
t.scaleY = 5.5f;
const float halfTrashW = mTrashTex->getWidth() * t.adjustedScaleX() / 2.f;
if (seaOnly) {
const float minSeaX = mLandBoundaryX + halfTrashW + 25.f;
if (t.x < minSeaX) t.x = minSeaX;
}
// Create a unique-ish name for the auto-spawned trash
const int id = Utils::getUtils().rirng32(0, 1000000);
const std::string name = "Trash_Auto_" + std::to_string(id);
GameManager::instantiateEntity(std::make_unique<AGame::Trash>(name, mTrashTex, t));
// If requested, mark the spawned trash as sea-only
if (seaOnly) {
auto* tr = GameManager::getEntityByName<AGame::Trash>(name);
if (tr) tr->setSeaOnly(true);
}
GameManager::setSharedData("trashActiveCount", GameManager::getSharedData<int>("trashActiveCount") + 1);
}
void Background::update(float deltaTime) {
(void)deltaTime;
@@ -162,6 +252,52 @@ namespace Game::AGame {
const int trashCount = GameManager::getSharedData<int>("trashActiveCount");
const int stage = GameManager::getSharedData<int>("gameStage");
// Periodically spawn a friendly on land or sea with a small probability
// evaluated each update using deltaTime so the average interval is respected.
const int activeFriendlies = GameManager::getSharedData<int>("friendlyActiveCount");
if (activeFriendlies < mMaxAutoFriendlies) {
// Compute chance = deltaTime / avgInterval
const float chance = deltaTime / std::max(0.0001f, mFriendlySpawnAvgInterval);
const int thresh = static_cast<int>(chance * 10000.f);
if (thresh > 0) {
const int roll = Utils::getUtils().rirng32(0, 9999);
if (roll < thresh) {
// Decide side: sea probability increases with stage
float seaProb = (stage > 1) ? 0.3f : 0.1f;
const int sideRoll = Utils::getUtils().rirng32(0, 99);
const bool spawnSea = sideRoll < static_cast<int>(seaProb * 100.f);
Object::Transform tS;
tS.rotation = 0.f;
tS.scaleX = 6.f;
tS.scaleY = 6.f;
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 float halfFriendlyW = mFriendlyTex->getWidth() * tS.adjustedScaleX() / 2.f;
const float halfFriendlyH = mFriendlyTex->getHeight() * tS.adjustedScaleY() / 2.f;
if (!spawnSea) {
tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(viewLeft + halfFriendlyW + 25.f), static_cast<int>(mLandBoundaryX - halfFriendlyW - 25.f)));
} else {
tS.x = static_cast<float>(Utils::getUtils().rirng32(static_cast<int>(mLandBoundaryX + halfFriendlyW + 25.f), static_cast<int>(viewRight - 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)));
const int id = Utils::getUtils().rirng32(0, 1000000);
const std::string name = std::string("Friendly_Auto_") + std::to_string(id);
auto* friendly = State::GameState::getInstance().addEntity(std::make_unique<AGame::Friendly>(name, mFriendlyTex, tS));
if (friendly) {
if (auto* collider = friendly->getComponent<Object::Components::BoxCollider>()) {
collider->setScale(0.75f);
}
GameManager::setSharedData("friendlyActiveCount", GameManager::getSharedData<int>("friendlyActiveCount") + 1);
}
}
}
}
if (mPendingLevelSpawn) {
if (enemyCount <= 0 && trashCount <= 0) {
GameManager::processPendingEntityRemovals();
@@ -176,6 +312,7 @@ namespace Game::AGame {
mPendingLevelSpawn = true;
mPendingLevelStage = stage + 1;
} else if (!GameManager::getSharedData<bool>("gameWon")) {
writeFinalScoreFile(GameManager::getSharedData<int>("gameScore"));
GameManager::setSharedData("gameWon", true);
LOG("All levels cleared");
}