#include #include #include #include #include #include #include #include #include #include #include namespace Game::AGame { void Friendly::start() { 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; 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) { // 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; 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 mTransform.x += mMoveSpeedX * deltaTime; mTransform.y += mMoveSpeedY * deltaTime; // Clamp to land section const float halfWidth = fw / 2.f; const float halfHeight = fh / 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 (!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); } if (mTransform.y + halfHeight > h / 2.f - 25.f) { mTransform.y = h / 2.f - 25.f - halfHeight; mMoveSpeedY = -std::abs(mMoveSpeedY); } } void Friendly::onCollisionEnter(Object::Entity* other) { auto* player = dynamic_cast(other); if (!player) { return; } GameManager::setSharedData("friendlyActiveCount", std::max(0, GameManager::getSharedData("friendlyActiveCount") - 1)); GameManager::setSharedData("gameScore", GameManager::getSharedData("gameScore") - 50); GameManager::destroyEntity(this); } }