Compare commits
3 Commits
d9769bdbbb
...
d93e71e716
| Author | SHA1 | Date | |
|---|---|---|---|
| d93e71e716 | |||
| 8ff3e29374 | |||
| 892d8f22eb |
5
TODO.txt
Normal file
5
TODO.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Smeti random premikanje - ok
|
||||
vnos imena kot text box
|
||||
neko sledenje igralcu (nasprotnikov)
|
||||
zavezniki naj nekaj delajo
|
||||
fullscreen
|
||||
@@ -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<Game::Renderer::Texture> mSeaTex;
|
||||
std::shared_ptr<Game::Renderer::Texture> mEnemyTex;
|
||||
std::shared_ptr<Game::Renderer::Texture> mTrashTex;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@ namespace Game {
|
||||
static void destroyEntity(T* entity);
|
||||
static void processPendingEntityRemovals();
|
||||
|
||||
static void pushPlayerPosition(Object::Transform transform) { mPlayerTransformHistory.push_back(transform); }
|
||||
static void getPlayerPositionHistory(std::vector<Object::Transform>& outHistory) { outHistory = mPlayerTransformHistory; }
|
||||
|
||||
private:
|
||||
int mTargetUpdatesPerSecond = TARGET_UPDATE_RATE;
|
||||
clock::time_point mLastUpdate;
|
||||
@@ -65,6 +68,7 @@ namespace Game {
|
||||
static std::unordered_map<std::string, int> mSharedInts;
|
||||
static std::unordered_map<std::string, float> mSharedFloats;
|
||||
static std::unordered_map<std::string, bool> mSharedBools;
|
||||
static std::vector<Object::Transform> mPlayerTransformHistory;
|
||||
static GameStateEnum mCurrentGameState;
|
||||
float mLastDelta = 0.f;
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -35,6 +35,23 @@ namespace {
|
||||
file << "Igralec: " << playerName << "\n";
|
||||
file << "Točke: " << score << "\n";
|
||||
file << "Datum: " << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S") << "\n";
|
||||
|
||||
// Replay system
|
||||
std::vector<Game::Object::Transform> playerHistory;
|
||||
Game::GameManager::getPlayerPositionHistory(playerHistory);
|
||||
std::ofstream replayFile("replay.txt", std::ios::trunc);
|
||||
if (!replayFile.is_open()) {
|
||||
WARN("Neuspešno odpiranje replay.txt za pisanje");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& transform : playerHistory) {
|
||||
replayFile << transform.x << " " << transform.y << " " << transform.rotation << " " << transform.scaleX << " " << transform.scaleY << "\n";
|
||||
}
|
||||
|
||||
LOG("Zapis končne statistike in replaya igre dokončan");
|
||||
replayFile.close();
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +74,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 +148,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 +216,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 +290,11 @@ namespace Game::AGame {
|
||||
const bool spawnSea = sideRoll < static_cast<int>(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 +344,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;
|
||||
}
|
||||
}
|
||||
@@ -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<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f;
|
||||
const float speed = 20.f + static_cast<float>(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<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f;
|
||||
const float speed = 20.f + static_cast<float>(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) {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#include <game/agame/friendly.hpp>
|
||||
#include <game/gamemanager.hpp>
|
||||
#include <game/agame/player.hpp>
|
||||
#include <game/agame/trash.hpp>
|
||||
#include <object/components/boxcollider.hpp>
|
||||
#include <state/gamestate.hpp>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <utils.hpp>
|
||||
#include <window/window.hpp>
|
||||
|
||||
@@ -13,25 +16,104 @@ namespace Game::AGame {
|
||||
addComponent<Object::Components::BoxCollider>();
|
||||
LOG("Zaveznik zagnan: " << getName());
|
||||
|
||||
// Initialize random movement
|
||||
const float angle = static_cast<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f;
|
||||
const float speed = 20.f + static_cast<float>(Utils::getUtils().rirng32(0, 30));
|
||||
mMoveSpeedX = std::cos(angle) * speed;
|
||||
mMoveSpeedY = std::sin(angle) * speed;
|
||||
const float landBoundaryX = GameManager::getSharedData<float>("terrainLandBoundaryX");
|
||||
const float entityWidth = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() : 0.f;
|
||||
mOnSea = (mTransform.x + entityWidth / 2.f) > landBoundaryX;
|
||||
|
||||
if (!mOnSea) {
|
||||
// Initialize random movement for land friendlies
|
||||
const float angle = static_cast<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f;
|
||||
const float speed = LAND_SPEED_MIN + static_cast<float>(Utils::getUtils().rirng32(0, static_cast<int>(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;
|
||||
|
||||
// Semi-random movement with periodic direction changes
|
||||
mDirectionChangeTimer += deltaTime;
|
||||
if (mDirectionChangeTimer > 2.0f) {
|
||||
const float angle = static_cast<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f;
|
||||
const float speed = 20.f + static_cast<float>(Utils::getUtils().rirng32(0, 30));
|
||||
mMoveSpeedX = std::cos(angle) * speed;
|
||||
mMoveSpeedY = std::sin(angle) * speed;
|
||||
mDirectionChangeTimer = 0.f;
|
||||
const float landBoundaryX = GameManager::getSharedData<float>("terrainLandBoundaryX");
|
||||
mOnSea = friendlyCenterX > landBoundaryX;
|
||||
|
||||
if (mOnSea) {
|
||||
auto snapshot = State::GameState::getInstance().getEntitiesSnapshot();
|
||||
Trash* nearestTrash = nullptr;
|
||||
float nearestDistanceSquared = std::numeric_limits<float>::max();
|
||||
|
||||
for (auto* entity : snapshot) {
|
||||
if (!entity) continue;
|
||||
auto* trash = dynamic_cast<Trash*>(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<float>(Utils::getUtils().rirng32(0, 360)) * 3.14159f / 180.f;
|
||||
const float speed = LAND_SPEED_MIN + static_cast<float>(Utils::getUtils().rirng32(0, static_cast<int>(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<Trash*>(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;
|
||||
|
||||
if (distanceSquared <= CLEANUP_RADIUS * CLEANUP_RADIUS) {
|
||||
// Clean up this trash: award points and remove it
|
||||
GameManager::setSharedData("gameScore", GameManager::getSharedData<int>("gameScore") + CLEANUP_SCORE_BONUS);
|
||||
GameManager::setSharedData("trashActiveCount", std::max(0, GameManager::getSharedData<int>("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<float>("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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -161,6 +161,9 @@ namespace Game::AGame {
|
||||
} else if (!mIsShipMode && mGroundTex) {
|
||||
setTexture(mGroundTex);
|
||||
}
|
||||
|
||||
// Push replay
|
||||
GameManager::pushPlayerPosition(mTransform);
|
||||
}
|
||||
|
||||
void Player::onCollisionEnter(Object::Entity* other) {
|
||||
|
||||
@@ -11,15 +11,20 @@ namespace Game::AGame {
|
||||
|
||||
void Trash::update(float deltaTime) {
|
||||
(void)deltaTime;
|
||||
if (mSeaOnly) {
|
||||
/*if (mSeaOnly) {
|
||||
const float landBoundaryX = GameManager::getSharedData<float>("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<float>(Utils::getUtils().rirng32(-50, 50)) * deltaTime;
|
||||
mTransform.y += static_cast<float>(Utils::getUtils().rirng32(-50, 50)) * deltaTime;
|
||||
|
||||
//return;
|
||||
}
|
||||
|
||||
void Trash::onCollisionEnter(Object::Entity* other) {
|
||||
|
||||
@@ -67,6 +67,7 @@ namespace Game {
|
||||
std::unordered_map<std::string, int> GameManager::mSharedInts;
|
||||
std::unordered_map<std::string, float> GameManager::mSharedFloats;
|
||||
std::unordered_map<std::string, bool> GameManager::mSharedBools;
|
||||
std::vector<Object::Transform> GameManager::mPlayerTransformHistory;
|
||||
|
||||
void GameManager::removeSharedData(const std::string& key, SharedDataType type) {
|
||||
if (type == SharedDataType::STRING) {
|
||||
|
||||
15
src/main.cpp
15
src/main.cpp
@@ -30,14 +30,25 @@ int main() {
|
||||
|
||||
State::GameState::getInstance().addEntity(std::make_unique<AGame::Background>("BG", std::make_shared<Game::Renderer::Texture>("../resources/bgtest.png", window.getRenderer()->getSDLRenderer()), Object::DEFAULT_TRANSFORM));
|
||||
|
||||
auto* player = dynamic_cast<AGame::Player*>(State::GameState::getInstance().addEntity(std::make_unique<AGame::Player>("Player", std::make_shared<Game::Renderer::Texture>("../resources/l3ladja.png", window.getRenderer()->getSDLRenderer()), Object::DEFAULT_TRANSFORM)));
|
||||
auto* player = dynamic_cast<AGame::Player*>(
|
||||
State::GameState::getInstance().addEntity(
|
||||
std::make_unique<AGame::Player>(
|
||||
"Player",
|
||||
std::make_shared<Game::Renderer::Texture>(
|
||||
"../resources/l3ladja.png",
|
||||
window.getRenderer()->getSDLRenderer()
|
||||
),
|
||||
Object::DEFAULT_TRANSFORM
|
||||
)
|
||||
)
|
||||
);
|
||||
if (player) {
|
||||
player->addComponent<Object::Components::BoxCollider>();
|
||||
player->setShipTexture(std::make_shared<Game::Renderer::Texture>("../resources/l3ladja.png", window.getRenderer()->getSDLRenderer()));
|
||||
player->setGroundTexture(std::make_shared<Game::Renderer::Texture>("../resources/l3player.png", window.getRenderer()->getSDLRenderer()));
|
||||
}
|
||||
|
||||
State::GameState::getInstance().addEntity(std::make_unique<AGame::HUDText>("HUD", std::make_shared<Game::Renderer::Font>("../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<AGame::HUDText>("HUD", std::make_shared<Game::Renderer::Font>("../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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <object/ui/uitextbox.hpp>
|
||||
#include <renderer/renderer.hpp>
|
||||
#include <object/camera.hpp>
|
||||
|
||||
namespace Game::Object {
|
||||
|
||||
@@ -8,15 +9,17 @@ namespace Game::Object {
|
||||
: Entity(name, font, transform), mX(x), mY(y), mConfig(config) { }
|
||||
|
||||
void UITextBox::start() {
|
||||
mTransform.x = mX - mTex->getWidth() * mTransform.adjustedScaleX() / 2.f;
|
||||
mTransform.y = mY - mTex->getHeight() * mTransform.adjustedScaleY() / 2.f;
|
||||
|
||||
mBoxWidth = static_cast<float>(mTex->getWidth()) * mTransform.adjustedScaleX();
|
||||
mBoxHeight = static_cast<float>(mTex->getHeight()) * mTransform.adjustedScaleY();
|
||||
// Compute visual box size first (respecting min sizes and scale), then center the box
|
||||
mBoxWidth = static_cast<float>(mTex ? mTex->getWidth() : 0.f) * mTransform.adjustedScaleX();
|
||||
mBoxHeight = static_cast<float>(mTex ? mTex->getHeight() : 0.f) * mTransform.adjustedScaleY();
|
||||
|
||||
if (mBoxWidth < mConfig.minWidth) mBoxWidth = mConfig.minWidth;
|
||||
if (mBoxHeight < mConfig.minHeight) mBoxHeight = mConfig.minHeight;
|
||||
|
||||
// Center using the computed box dimensions rather than raw texture size so padding/min sizes are respected
|
||||
mTransform.x = mX - mBoxWidth / 2.f;
|
||||
mTransform.y = mY - mBoxHeight / 2.f;
|
||||
|
||||
refreshVisualText();
|
||||
}
|
||||
|
||||
@@ -112,15 +115,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() {
|
||||
|
||||
@@ -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<float>(screenW) / LOGICAL_WIDTH;
|
||||
const float scaleY = static_cast<float>(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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user