#pragma once #include #include "BuildingId.h" #include "entt/entity/entity.hpp" // How a new hit combines with what is already selected. enum class SelectionMode { Replace, // plain click or drag: the hit becomes the whole selection Toggle, // Ctrl + click: the hit joins the selection, or leaves it if present Add // Ctrl + box drag: the hits join the selection, never leave it }; // The player's current selection across the three categories, and the rules that // govern moving between them (REQ-UI-SELECTION-CATEGORIES). // // The rules were previously written out once for point-clicks and once for box // drags, which is why they live here now: buildings win over field objects, so // selecting a building clears actors and debris, and selecting either of those // clears buildings — but actors and debris coexist with each other // (REQ-UI-ENTITY-CLICK-SELECT, REQ-UI-DEBRIS-CLICK-SELECT, REQ-UI-MULTI-SELECT, // REQ-UI-DEBRIS-MULTI-SELECT). A point-click and a box drag then differ only in // the SelectionMode they pass and in how many hits they pass. // // Every mutator publishes the change events, so callers never emit them by hand. // Hit-testing is not done here: callers resolve what was hit and pass the result // in, which keeps this free of any simulation dependency. class SelectionController { public: const std::vector& getSelectedBuildings() const; const std::vector& getSelectedActors() const; const std::vector& getSelectedDebris() const; bool isActorSelected(entt::entity actor) const; bool isDebrisSelected(entt::entity debris) const; // Selects buildings and/or construction sites, clearing any field selection. // Always publishes SelectionChangedEvent, even when the result is unchanged, // so a click on an already-selected building still refreshes its panel. void selectBuildings(const std::vector& ids, SelectionMode mode); // Selects field objects, clearing any building selection. Actors and debris are // set together so a Replace can express "these actors and no debris" — which is // what a plain click on an actor means — while a Toggle or Add leaves the // category whose vector is empty untouched. Always publishes both field events. void selectFieldObjects(const std::vector& actors, const std::vector& debris, SelectionMode mode); // Empties all three categories, publishing only for those that were non-empty. void clearAll(); // Replace one category outright, publishing only if it actually changed. For // the per-frame prune of entities that despawned or died: the liveness query // belongs to the caller, which is the one that can see the simulation. void setSelectedActors(std::vector actors); void setSelectedDebris(std::vector debris); private: // Each returns whether anything changed, without publishing. bool clearBuildingsQuietly(); bool clearActorsQuietly(); bool clearDebrisQuietly(); void publishBuildings() const; void publishActors() const; void publishDebris() const; std::vector m_buildings; std::vector m_actors; std::vector m_debris; };