Files
letnik3koncni-prap/src/game/agame/enemy.cpp
2026-05-13 07:58:59 +02:00

175 lines
8.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <game/agame/enemy.hpp>
#include <game/agame/background.hpp>
#include <object/components/boxcollider.hpp>
#include <game/agame/player.hpp>
#include <state/gamestate.hpp>
#include <algorithm>
#include <cmath>
#include <utils.hpp>
#include <game/gamemanager.hpp>
#include <window/window.hpp>
namespace Game::AGame {
void Enemy::start() {
mZIndex = 20;
addComponent<Object::Components::BoxCollider>();
LOG("Sovražnik 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;
mDirectionChangeTimer = 0.f;
}
void Enemy::update(float deltaTime) {
(void)deltaTime;
auto* player = GameManager::getEntityByName<Player>("Player");
if (!player) {
mIsVisible = false;
return;
}
// Enemies are visible only within a reveal radius around the player
const float revealRadius = GameManager::getSharedData<float>("enemyRevealRadius");
const float px = player->getTransform()->x + (player->getTexture() ? player->getTexture()->getWidth() * player->getTransform()->adjustedScaleX() / 2.f : 0.f);
const float py = player->getTransform()->y + (player->getTexture() ? player->getTexture()->getHeight() * player->getTransform()->adjustedScaleY() / 2.f : 0.f);
const float ew = getTexture() ? getTexture()->getWidth() * mTransform.adjustedScaleX() : 0.f;
const float eh = getTexture() ? getTexture()->getHeight() * mTransform.adjustedScaleY() : 0.f;
const float ex = mTransform.x + ew / 2.f;
const float ey = mTransform.y + eh / 2.f;
const float dxv = px - ex;
const float dyv = py - ey;
mIsVisible = (dxv * dxv + dyv * dyv) <= (revealRadius * revealRadius);
// 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
mTransform.x += mMoveSpeedX * deltaTime;
mTransform.y += mMoveSpeedY * deltaTime;
// Decrease shoreline-spawn cooldown
if (mShoreSpawnCooldown > 0.f) mShoreSpawnCooldown = std::max(0.f, mShoreSpawnCooldown - 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;
// 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) {
mTransform.x = landBoundaryX - 25.f - halfWidth;
mMoveSpeedX = -std::abs(mMoveSpeedX);
// Enemy hit the shoreline on the right side; spawn a trash on the sea side
if (mShoreSpawnCooldown <= 0.f) {
auto* bg = GameManager::getEntityByName<AGame::Background>("BG");
if (bg) {
Object::Transform t;
t.rotation = 0.f;
t.scaleX = 5.5f;
t.scaleY = 5.5f;
// Place trash on sea side at same Y
t.y = mTransform.y;
t.x = landBoundaryX + 10.f; // will be adjusted inside spawnTrashAt
bg->spawnTrashAt(t, true);
}
mShoreSpawnCooldown = 3.0f; // 3 second cooldown per enemy
}
}
if (mTransform.y - halfHeight < -h / 2.f + 25.f) {
mTransform.y = -h / 2.f + 25.f + halfHeight;
mMoveSpeedY = std::abs(mMoveSpeedY);
}
if (mTransform.y + halfHeight > h / 2.f - 25.f) {
mTransform.y = h / 2.f - 25.f - halfHeight;
mMoveSpeedY = -std::abs(mMoveSpeedY);
}
}
bool Enemy::hasAdjacentEnemy() {
if (!getTexture()) return false;
const float detectionRadius = 40.f;
const float enemyWidth = getTexture()->getWidth() * mTransform.adjustedScaleX();
const float enemyHeight = getTexture()->getHeight() * mTransform.adjustedScaleY();
const float centerX = mTransform.x + enemyWidth / 2.f;
const float centerY = mTransform.y + enemyHeight / 2.f;
auto entities = GameManager::getEntityByName<Object::Entity>("Dummy");
if (!entities) {
auto snapshot = State::GameState::getInstance().getEntitiesSnapshot();
for (auto* other : snapshot) {
if (!other || other == this || !dynamic_cast<Enemy*>(other)) continue;
auto* otherEnemy = dynamic_cast<Enemy*>(other);
if (!otherEnemy || !otherEnemy->getTexture()) continue;
const float otherWidth = otherEnemy->getTexture()->getWidth() * otherEnemy->getTransform()->adjustedScaleX();
const float otherHeight = otherEnemy->getTexture()->getHeight() * otherEnemy->getTransform()->adjustedScaleY();
const float otherCenterX = otherEnemy->getTransform()->x + otherWidth / 2.f;
const float otherCenterY = otherEnemy->getTransform()->y + otherHeight / 2.f;
const float dx = centerX - otherCenterX;
const float dy = centerY - otherCenterY;
if (dx * dx + dy * dy <= detectionRadius * detectionRadius) {
return true;
}
}
}
return false;
}
void Enemy::onCollisionEnter(Object::Entity* other) {
auto* player = dynamic_cast<Player*>(other);
if (!player || !mIsVisible) {
return;
}
if (hasAdjacentEnemy()) {
LOG("Igralec je trčil v močno skupino onesnaževalcev; konec igre!");
GameManager::setSharedData("gameLost", true);
GameManager::destroyEntity(player);
return;
}
LOG("Sovražnik '" << getName() << "' je trčil v igralca; odstranjujem onesnaževalca in dodeljujem točke");
GameManager::setSharedData("enemyActiveCount", std::max(0, GameManager::getSharedData<int>("enemyActiveCount") - 1));
GameManager::setSharedData("gameScore", GameManager::getSharedData<int>("gameScore") + 100);
GameManager::destroyEntity(this);
}
}