148 lines
5.1 KiB
C++
148 lines
5.1 KiB
C++
#include "EntityHitTest.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
#include "EntityAdmin.h"
|
|
#include "PositionComponent.h"
|
|
#include "DebrisComponent.h"
|
|
#include "ShipIdentityComponent.h"
|
|
#include "StationBodyComponent.h"
|
|
#include "HealthComponent.h"
|
|
|
|
entt::entity entityAtWorldPos(EntityAdmin& admin, QVector2D worldPos)
|
|
{
|
|
const QPoint tile(static_cast<int>(std::floor(worldPos.x())),
|
|
static_cast<int>(std::floor(worldPos.y())));
|
|
|
|
entt::entity stationHit = entt::null;
|
|
admin.forEach<StationBodyComponent, HealthComponent>(
|
|
[&](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<PositionComponent, HealthComponent>(
|
|
[&](entt::entity entity, const PositionComponent& pos, const HealthComponent& h)
|
|
{
|
|
if (h.hp <= 0.0f) { return; }
|
|
if (admin.hasAll<StationBodyComponent>(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 debrisAtWorldPos(EntityAdmin& admin, QVector2D worldPos)
|
|
{
|
|
// Slightly larger than the debris's rendered radius (0.2 tiles) so small pieces
|
|
// remain easy to click; tunable.
|
|
constexpr float kDebrisHitRadiusSquared = 0.35f * 0.35f;
|
|
entt::entity bestDebris = entt::null;
|
|
float bestDistSquared = kDebrisHitRadiusSquared;
|
|
|
|
admin.forEach<DebrisComponent, PositionComponent>(
|
|
[&](entt::entity entity, const DebrisComponent& /*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;
|
|
bestDebris = entity;
|
|
}
|
|
});
|
|
|
|
return bestDebris;
|
|
}
|
|
|
|
std::vector<entt::entity> debrisInBox(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<entt::entity> result;
|
|
admin.forEach<DebrisComponent, PositionComponent>(
|
|
[&](entt::entity entity, const DebrisComponent& /*sd*/, const PositionComponent& pos)
|
|
{
|
|
const int tileX = static_cast<int>(std::floor(pos.value.x()));
|
|
const int tileY = static_cast<int>(std::floor(pos.value.y()));
|
|
if (tileX >= minX && tileX <= maxX && tileY >= minY && tileY <= maxY)
|
|
{
|
|
result.push_back(entity);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
std::vector<entt::entity> 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<entt::entity> result;
|
|
|
|
// Stations: included when any occupied body cell lies in the box.
|
|
admin.forEach<StationBodyComponent, HealthComponent>(
|
|
[&](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<ShipIdentityComponent, PositionComponent, HealthComponent>(
|
|
[&](entt::entity entity, const ShipIdentityComponent& /*id*/,
|
|
const PositionComponent& pos, const HealthComponent& h)
|
|
{
|
|
if (h.hp <= 0.0f) { return; }
|
|
const int tileX = static_cast<int>(std::floor(pos.value.x()));
|
|
const int tileY = static_cast<int>(std::floor(pos.value.y()));
|
|
if (tileX >= minX && tileX <= maxX && tileY >= minY && tileY <= maxY)
|
|
{
|
|
result.push_back(entity);
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|