allow multi-select for ships/stations, mixable with scrap

This commit is contained in:
2026-07-20 20:39:48 +02:00
parent 9622fa4345
commit be475e2836
15 changed files with 509 additions and 206 deletions

View File

@@ -4,7 +4,7 @@ SET(HDRS
${CMAKE_CURRENT_SOURCE_DIR}/TickAdvancedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/BuildingBlocksChangedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/ExpansionCostChangedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/EntitySelectedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/EntitySelectionChangedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/GameSpeedChangedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/BossWaveUpdatedEvent.h
${CMAKE_CURRENT_SOURCE_DIR}/SchematicChoicesAvailableEvent.h

View File

@@ -1,21 +0,0 @@
#ifndef ENTITY_SELECTED_EVENT_H
#define ENTITY_SELECTED_EVENT_H
#include <optional>
#include "entt/entity/entity.hpp"
#include "Event.h"
class EntitySelectedEvent : public Event
{
public:
explicit EntitySelectedEvent(std::optional<entt::entity> entity)
: entity(entity)
{
}
const std::optional<entt::entity> entity;
};
#endif // ENTITY_SELECTED_EVENT_H

View File

@@ -0,0 +1,25 @@
#ifndef ENTITY_SELECTION_CHANGED_EVENT_H
#define ENTITY_SELECTION_CHANGED_EVENT_H
#include <vector>
#include "entt/entity/entity.hpp"
#include "Event.h"
// The set of currently selected ships and/or defence stations. An empty list means
// no actor is selected. Actors share the "field" selection category with scrap piles
// (REQ-UI-SELECTION-CATEGORIES): they can be selected together, but never together with
// buildings.
class EntitySelectionChangedEvent : public Event
{
public:
explicit EntitySelectionChangedEvent(std::vector<entt::entity> entities)
: entities(std::move(entities))
{
}
const std::vector<entt::entity> entities;
};
#endif // ENTITY_SELECTION_CHANGED_EVENT_H

View File

@@ -6,6 +6,7 @@
#include "EntityAdmin.h"
#include "PositionComponent.h"
#include "ScrapDataComponent.h"
#include "ShipIdentityComponent.h"
#include "StationBodyComponent.h"
#include "HealthComponent.h"
@@ -101,3 +102,46 @@ std::vector<entt::entity> scrapInBox(EntityAdmin& admin, QPoint tileA, QPoint ti
});
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;
}

View File

@@ -19,3 +19,10 @@ entt::entity scrapAtWorldPos(EntityAdmin& admin, QVector2D worldPos);
// Returns every scrap pile whose position falls within the inclusive tile rectangle
// spanned by tileA and tileB, in any corner order (REQ-UI-SCRAP-MULTI-SELECT).
std::vector<entt::entity> scrapInBox(EntityAdmin& admin, QPoint tileA, QPoint tileB);
// Returns every living actor (ship or defence station, player or enemy) that falls
// within the inclusive tile rectangle spanned by tileA and tileB, in any corner order
// (REQ-UI-MULTI-SELECT, REQ-UI-ENTITY-CLICK-SELECT). A ship is included when its floored
// position tile lies in the box; a station is included when any of its body cells does.
// Dead actors (hp <= 0) and the HQ proxy are excluded.
std::vector<entt::entity> actorsInBox(EntityAdmin& admin, QPoint tileA, QPoint tileB);