diff --git a/source/simulation2/components/CCmpRangeManager.cpp b/source/simulation2/components/CCmpRangeManager.cpp index 6cbcb9d9ad..9799f529e8 100644 --- a/source/simulation2/components/CCmpRangeManager.cpp +++ b/source/simulation2/components/CCmpRangeManager.cpp @@ -277,6 +277,35 @@ struct EntityData static_assert(sizeof(EntityData) == 24); +// Convert world-space circle to tile-space bounds +struct TileCircle +{ + std::int32_t centerX, centerZ; + std::int32_t radius; + std::int32_t squaredRadius; + std::int32_t minX, maxX, minZ, maxZ; +}; + +// Apply an action to every tile within the circle +template +void ForEachTileInCircle(const TileCircle& circle, Func&& func) +{ + for (std::int32_t tileZ = circle.minZ; tileZ <= circle.maxZ; ++tileZ) + { + const std::int32_t deltaZ{tileZ - circle.centerZ}; + const std::int32_t squaredDeltaZ{deltaZ * deltaZ}; + + for (std::int32_t tileX = circle.minX; tileX <= circle.maxX; ++tileX) + { + const std::int32_t deltaX{tileX - circle.centerX}; + if (deltaX * deltaX + squaredDeltaZ > circle.squaredRadius) + continue; + + func(tileX, tileZ); + } + } +} + /** * Functor for sorting entities by distance from a source point. * It must only be passed entities that are in 'entities' @@ -2130,6 +2159,50 @@ public: SeeExploredEntities(p); } + TileCircle WorldToTileCircle(entity_pos_t worldX, entity_pos_t worldZ, fixed radius) + { + const std::int32_t centerTileX{(worldX / LOS_TILE_SIZE).ToInt_RoundToNearest()}; + const std::int32_t centerTileZ{(worldZ / LOS_TILE_SIZE).ToInt_RoundToNearest()}; + const std::int32_t tileRadius{(radius / LOS_TILE_SIZE).ToInt_RoundToInfinity()}; + + return TileCircle{ + .centerX = centerTileX, + .centerZ = centerTileZ, + .radius = tileRadius, + .squaredRadius = tileRadius * tileRadius, + .minX = std::max(centerTileX - tileRadius, 0), + .maxX = std::min(centerTileX + tileRadius, m_LosVerticesPerSide - 1), + .minZ = std::max(centerTileZ - tileRadius, 0), + .maxZ = std::min(centerTileZ + tileRadius, m_LosVerticesPerSide - 1) + }; + } + + void ExploreCircle(player_id_t playerId, entity_pos_t worldX, entity_pos_t worldZ, fixed radius) override + { + if (playerId <= 0 || playerId > MAX_LOS_PLAYER_ID) + return; + + const TileCircle circle{WorldToTileCircle(worldX, worldZ, radius)}; + const u32 exploredBitMask{static_cast(LosState::EXPLORED) << (2 * (playerId - 1))}; + u32& exploredCount = m_ExploredVertices.at(playerId); + + ForEachTileInCircle(circle, + [&](std::int32_t tileX, std::int32_t tileZ) + { + if (LosIsOffWorld(tileX, tileZ)) + return; + + u32& losState = m_LosState.get(tileX, tileZ); + if (losState & exploredBitMask) + return; + + losState |= exploredBitMask; + ++exploredCount; + MarkVisibilityDirtyAroundTile(playerId, tileX, tileZ); + } + ); + } + /** * Force any entity in explored territory to appear for player p. * This is useful for miraging entities inside the territory borders at the beginning of a game, diff --git a/source/simulation2/components/ICmpRangeManager.cpp b/source/simulation2/components/ICmpRangeManager.cpp index a39aa2783c..61a927aefd 100644 --- a/source/simulation2/components/ICmpRangeManager.cpp +++ b/source/simulation2/components/ICmpRangeManager.cpp @@ -62,6 +62,7 @@ DEFINE_INTERFACE_METHOD("GetGaiaAndNonGaiaEntities", ICmpRangeManager, GetGaiaAn DEFINE_INTERFACE_METHOD("SetDebugOverlay", ICmpRangeManager, SetDebugOverlay) DEFINE_INTERFACE_METHOD("ExploreMap", ICmpRangeManager, ExploreMap) DEFINE_INTERFACE_METHOD("ExploreTerritories", ICmpRangeManager, ExploreTerritories) +DEFINE_INTERFACE_METHOD("ExploreCircle", ICmpRangeManager, ExploreCircle) DEFINE_INTERFACE_METHOD("SetLosRevealWholeMap", ICmpRangeManager, SetLosRevealWholeMap) DEFINE_INTERFACE_METHOD("GetLosRevealWholeMap", ICmpRangeManager, GetLosRevealWholeMap) DEFINE_INTERFACE_METHOD("SetLosRevealWholeMapForAll", ICmpRangeManager, SetLosRevealWholeMapForAll) diff --git a/source/simulation2/components/ICmpRangeManager.h b/source/simulation2/components/ICmpRangeManager.h index 4ed58b91e5..2f1bb3802c 100644 --- a/source/simulation2/components/ICmpRangeManager.h +++ b/source/simulation2/components/ICmpRangeManager.h @@ -333,6 +333,12 @@ public: */ virtual void ExploreTerritories() = 0; + /** + * Explore a circular area of the map for the given player. + * This marks the tiles as explored (fogged) but not necessarily visible. + */ + virtual void ExploreCircle(player_id_t p, entity_pos_t x, entity_pos_t z, fixed radius) = 0; + /** * Reveal the shore for specified player p. * This works like for entities: if RevealShore is called multiple times with enabled, it