#include "EntityHitTest.h" #include #include #include "EntityAdmin.h" #include "PositionComponent.h" #include "ScrapDataComponent.h" #include "ShipIdentityComponent.h" #include "StationBodyComponent.h" #include "HealthComponent.h" entt::entity entityAtWorldPos(EntityAdmin& admin, QVector2D worldPos) { const QPoint tile(static_cast(std::floor(worldPos.x())), static_cast(std::floor(worldPos.y()))); entt::entity stationHit = entt::null; admin.forEach( [&](entt::entity entity, const StationBodyComponent& sb, const HealthComponent& h) { if (stationHit != entt::null) { return; } if (h.hp <= 0.0f) { return; } for (const QPoint& cell : sb.bodyCells) { if (cell == tile) { stationHit = entity; return; } } }); if (stationHit != entt::null) { return stationHit; } constexpr float kShipHitRadiusSquared = 0.5f * 0.5f; entt::entity bestShip = entt::null; float bestDistSquared = kShipHitRadiusSquared; admin.forEach( [&](entt::entity entity, const PositionComponent& pos, const HealthComponent& h) { if (h.hp <= 0.0f) { return; } if (admin.hasAll(entity)) { return; } const float dx = pos.value.x() - worldPos.x(); const float dy = pos.value.y() - worldPos.y(); const float distSquared = dx * dx + dy * dy; if (distSquared < bestDistSquared) { bestDistSquared = distSquared; bestShip = entity; } }); return bestShip; } entt::entity scrapAtWorldPos(EntityAdmin& admin, QVector2D worldPos) { // Slightly larger than the scrap's rendered radius (0.2 tiles) so small piles // remain easy to click; tunable. constexpr float kScrapHitRadiusSquared = 0.35f * 0.35f; entt::entity bestScrap = entt::null; float bestDistSquared = kScrapHitRadiusSquared; admin.forEach( [&](entt::entity entity, const ScrapDataComponent& /*sd*/, const PositionComponent& pos) { const float dx = pos.value.x() - worldPos.x(); const float dy = pos.value.y() - worldPos.y(); const float distSquared = dx * dx + dy * dy; if (distSquared < bestDistSquared) { bestDistSquared = distSquared; bestScrap = entity; } }); return bestScrap; } std::vector scrapInBox(EntityAdmin& admin, QPoint tileA, QPoint tileB) { const int minX = std::min(tileA.x(), tileB.x()); const int maxX = std::max(tileA.x(), tileB.x()); const int minY = std::min(tileA.y(), tileB.y()); const int maxY = std::max(tileA.y(), tileB.y()); std::vector result; admin.forEach( [&](entt::entity entity, const ScrapDataComponent& /*sd*/, const PositionComponent& pos) { const int tileX = static_cast(std::floor(pos.value.x())); const int tileY = static_cast(std::floor(pos.value.y())); if (tileX >= minX && tileX <= maxX && tileY >= minY && tileY <= maxY) { result.push_back(entity); } }); return result; } std::vector actorsInBox(EntityAdmin& admin, QPoint tileA, QPoint tileB) { const int minX = std::min(tileA.x(), tileB.x()); const int maxX = std::max(tileA.x(), tileB.x()); const int minY = std::min(tileA.y(), tileB.y()); const int maxY = std::max(tileA.y(), tileB.y()); std::vector result; // Stations: included when any occupied body cell lies in the box. admin.forEach( [&](entt::entity entity, const StationBodyComponent& sb, const HealthComponent& h) { if (h.hp <= 0.0f) { return; } for (const QPoint& cell : sb.bodyCells) { if (cell.x() >= minX && cell.x() <= maxX && cell.y() >= minY && cell.y() <= maxY) { result.push_back(entity); return; } } }); // Ships: included when the floored position tile lies in the box. Requiring // ShipIdentityComponent excludes the HQ proxy and any station bodies. admin.forEach( [&](entt::entity entity, const ShipIdentityComponent& /*id*/, const PositionComponent& pos, const HealthComponent& h) { if (h.hp <= 0.0f) { return; } const int tileX = static_cast(std::floor(pos.value.x())); const int tileY = static_cast(std::floor(pos.value.y())); if (tileX >= minX && tileX <= maxX && tileY >= minY && tileY <= maxY) { result.push_back(entity); } }); return result; }