#ifndef STAR_ENTITY_MAP_HPP #define STAR_ENTITY_MAP_HPP #include "StarSpatialHash2D.hpp" #include "StarEntity.hpp" namespace Star { STAR_CLASS(EntityMap); STAR_CLASS(TileEntity); STAR_CLASS(InteractiveEntity); STAR_EXCEPTION(EntityMapException, StarException); // Class used by WorldServer and WorldClient to store entites organized in a // spatial hash. Provides convenient ways of querying entities based on // different selection criteria. // // Several of the methods in EntityMap take callbacks or filters that will be // called while iterating over internal structures. They are all designed so // that adding new entities is safe to do from the callback, but removing // entities is never safe to do from any callback function. class EntityMap { public: static float const SpatialHashSectorSize; static int const MaximumEntityBoundBox; // beginIdSpace and endIdSpace is the *inclusive* range for new enittyIds. EntityMap(Vec2U const& worldSize, EntityId beginIdSpace, EntityId endIdSpace); // Get the next free id in the entity id space. EntityId reserveEntityId(); // Or a specific one, can fail. Maybe maybeReserveEntityId(EntityId entityId); // If it doesn't matter that we don't get the one want EntityId reserveEntityId(EntityId entityId); // Add an entity to this EntityMap. The entity must already be initialized // and have a unique EntityId returned by reserveEntityId. void addEntity(EntityPtr entity); EntityPtr removeEntity(EntityId entityId); size_t size() const; List entityIds() const; // Iterates through the entity map optionally in the given order, updating // the spatial information for each entity along the way. void updateAllEntities(EntityCallback const& callback = {}, function sortOrder = {}); // If the given unique entity is in this map, then return its entity id EntityId uniqueEntityId(String const& uniqueId) const; EntityPtr entity(EntityId entityId) const; EntityPtr uniqueEntity(String const& uniqueId) const; // Queries entities based on metaBoundBox List entityQuery(RectF const& boundBox, EntityFilter const& filter = {}) const; // A fuzzy query of the entities at this position, sorted by closeness. List entitiesAt(Vec2F const& pos, EntityFilter const& filter = {}) const; List entitiesAtTile(Vec2I const& pos, EntityFilterOf const& filter = {}) const; // Sort of a fuzzy line intersection test. Tests if a given line intersects // the bounding box of any entities, and returns them. List entityLineQuery(Vec2F const& begin, Vec2F const& end, EntityFilter const& filter = {}) const; // Callback versions of query functions. void forEachEntity(RectF const& boundBox, EntityCallback const& callback) const; void forEachEntityLine(Vec2F const& begin, Vec2F const& end, EntityCallback const& callback) const; // Returns tile-based entities that occupy the given tile position. void forEachEntityAtTile(Vec2I const& pos, EntityCallbackOf const& callback) const; // Iterate through all the entities, optionally in the given sort order. void forAllEntities(EntityCallback const& callback, function sortOrder = {}) const; // Stops searching when filter returns true, and returns the entity which // caused it. EntityPtr findEntity(RectF const& boundBox, EntityFilter const& filter) const; EntityPtr findEntityLine(Vec2F const& begin, Vec2F const& end, EntityFilter const& filter) const; EntityPtr findEntityAtTile(Vec2I const& pos, EntityFilterOf const& filter) const; // Closest entity that satisfies the given selector, if given. EntityPtr closestEntity(Vec2F const& center, float radius, EntityFilter const& filter = {}) const; // Returns interactive entity that is near the given world position InteractiveEntityPtr interactiveEntityNear(Vec2F const& pos, float maxRadius = 1.5f) const; // Whether or not any tile entity occupies this tile bool tileIsOccupied(Vec2I const& pos, bool includeEphemeral = false) const; // Intersects any entity's collision area bool spaceIsOccupied(RectF const& rect, bool includeEphemeral = false) const; // Convenience template methods that filter based on dynamic cast and deal // with pointers to a derived entity type. template shared_ptr get(EntityId entityId) const; template shared_ptr getUnique(String const& uniqueId) const; template List> query(RectF const& boundBox, EntityFilterOf const& filter = {}) const; template List> all(EntityFilterOf const& filter = {}) const; template List> lineQuery(Vec2F const& begin, Vec2F const& end, EntityFilterOf const& filter = {}) const; template shared_ptr closest(Vec2F const& center, float radius, EntityFilterOf const& filter = {}) const; template List> atTile(Vec2I const& pos) const; private: typedef SpatialHash2D SpatialMap; WorldGeometry m_geometry; SpatialMap m_spatialMap; BiHashMap m_uniqueMap; EntityId m_nextId; EntityId m_beginIdSpace; EntityId m_endIdSpace; List m_entrySortBuffer; }; template shared_ptr EntityMap::get(EntityId entityId) const { return as(entity(entityId)); } template shared_ptr EntityMap::getUnique(String const& uniqueId) const { return as(uniqueEntity(uniqueId)); } template List> EntityMap::query(RectF const& boundBox, EntityFilterOf const& filter) const { List> entities; for (auto const& entity : entityQuery(boundBox, entityTypeFilter(filter))) entities.append(as(entity)); return entities; } template List> EntityMap::all(EntityFilterOf const& filter) const { List> entities; forAllEntities([&](EntityPtr const& entity) { if (auto e = as(entity)) { if (!filter || filter(e)) entities.append(e); } }); return entities; } template List> EntityMap::lineQuery(Vec2F const& begin, Vec2F const& end, EntityFilterOf const& filter) const { List> entities; for (auto const& entity : entityLineQuery(begin, end, entityTypeFilter(filter))) entities.append(as(entity)); return entities; } template shared_ptr EntityMap::closest(Vec2F const& center, float radius, EntityFilterOf const& filter) const { return as(closestEntity(center, radius, entityTypeFilter(filter))); } template List> EntityMap::atTile(Vec2I const& pos) const { List> list; forEachEntityAtTile(pos, [&](TileEntityPtr const& entity) { if (auto e = as(entity)) list.append(std::move(e)); return false; }); return list; } } #endif