Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/components/CCmpRangeManager.cpp
Show All 26 Lines | |||||
#include "simulation2/components/ICmpMirage.h" | #include "simulation2/components/ICmpMirage.h" | ||||
#include "simulation2/components/ICmpOwnership.h" | #include "simulation2/components/ICmpOwnership.h" | ||||
#include "simulation2/components/ICmpPosition.h" | #include "simulation2/components/ICmpPosition.h" | ||||
#include "simulation2/components/ICmpObstructionManager.h" | #include "simulation2/components/ICmpObstructionManager.h" | ||||
#include "simulation2/components/ICmpTerritoryManager.h" | #include "simulation2/components/ICmpTerritoryManager.h" | ||||
#include "simulation2/components/ICmpVisibility.h" | #include "simulation2/components/ICmpVisibility.h" | ||||
#include "simulation2/components/ICmpVision.h" | #include "simulation2/components/ICmpVision.h" | ||||
#include "simulation2/components/ICmpWaterManager.h" | #include "simulation2/components/ICmpWaterManager.h" | ||||
#include "simulation2/helpers/PackedArray.h" | |||||
#include "simulation2/helpers/Los.h" | #include "simulation2/helpers/Los.h" | ||||
#include "simulation2/helpers/MapEdgeTiles.h" | #include "simulation2/helpers/MapEdgeTiles.h" | ||||
#include "simulation2/helpers/Render.h" | #include "simulation2/helpers/Render.h" | ||||
#include "simulation2/helpers/Spatial.h" | #include "simulation2/helpers/Spatial.h" | ||||
#include "simulation2/serialization/SerializedTypes.h" | #include "simulation2/serialization/SerializedTypes.h" | ||||
#include "graphics/Overlay.h" | #include "graphics/Overlay.h" | ||||
#include "lib/timer.h" | #include "lib/timer.h" | ||||
#include "ps/CLogger.h" | #include "ps/CLogger.h" | ||||
#include "ps/Profile.h" | #include "ps/Profile.h" | ||||
#include "renderer/Scene.h" | #include "renderer/Scene.h" | ||||
#define DEBUG_RANGE_MANAGER_BOUNDS 0 | #define DEBUG_RANGE_MANAGER_BOUNDS 0 | ||||
namespace | namespace std | ||||
{ | { | ||||
/** | template<> class numeric_limits<LosVisibility> { public: static constexpr int digits = 2; }; | ||||
* How many LOS vertices to have per region. | template<> class numeric_limits<LosState> { public: static constexpr int digits = 2; }; | ||||
* LOS regions are used to keep track of units. | |||||
*/ | |||||
constexpr int LOS_REGION_RATIO = 8; | |||||
/** | |||||
* Tolerance for parabolic range calculations. | |||||
* TODO C++20: change this to constexpr by fixing CFixed with std::is_constant_evaluated | |||||
*/ | |||||
const fixed PARABOLIC_RANGE_TOLERANCE = fixed::FromInt(1)/2; | |||||
/** | |||||
* Convert an owner ID (-1 = unowned, 0 = gaia, 1..30 = players) | |||||
* into a 32-bit mask for quick set-membership tests. | |||||
*/ | |||||
u32 CalcOwnerMask(player_id_t owner) | |||||
{ | |||||
if (owner >= -1 && owner < 31) | |||||
return 1 << (1+owner); | |||||
else | |||||
return 0; // owner was invalid | |||||
} | } | ||||
/** | namespace | ||||
* Returns LOS mask for given player. | |||||
*/ | |||||
u32 CalcPlayerLosMask(player_id_t player) | |||||
{ | { | ||||
if (player > 0 && player <= 16) | |||||
return (u32)LosState::MASK << (2*(player-1)); | |||||
return 0; | |||||
} | |||||
/** | /** | ||||
* Returns shared LOS mask for given list of players. | * Use this type to store something for each player, including GAIA player(s). | ||||
*/ | */ | ||||
u32 CalcSharedLosMask(std::vector<player_id_t> players) | template<typename T> | ||||
{ | using PerPlayer = PackedArray<T, MAX_NB_OF_PLAYERS>; | ||||
u32 playerMask = 0; | |||||
for (size_t i = 0; i < players.size(); i++) | |||||
playerMask |= CalcPlayerLosMask(players[i]); | |||||
return playerMask; | |||||
} | |||||
/** | /** | ||||
* Add/remove a player to/from mask, which is a 1-bit mask representing a list of players. | * Use this type to store something for each player, including GAIA player(s), | ||||
* Returns true if the mask is modified. | * as well as a single bit-flag located at index -1. | ||||
*/ | */ | ||||
bool SetPlayerSharedDirtyVisibilityBit(u16& mask, player_id_t player, bool enable) | template<typename T> | ||||
{ | using PerPlayerAndFlag = PackedArray<T, MAX_NB_OF_PLAYERS + 1, true>; | ||||
if (player <= 0 || player > 16) | |||||
return false; | |||||
u16 oldMask = mask; | |||||
if (enable) | |||||
mask |= (0x1 << (player - 1)); | |||||
else | |||||
mask &= ~(0x1 << (player - 1)); | |||||
return oldMask != mask; | |||||
} | |||||
/** | /** | ||||
* Computes the 2-bit visibility for one player, given the total 32-bit visibilities | * 'Declaration of intent' typedef for a valid, non-GAIA player (range 1..MAX_NB_OF_PLAYERS). | ||||
*/ | */ | ||||
LosVisibility GetPlayerVisibility(u32 visibilities, player_id_t player) | using nongaia_player_id_t = std::make_unsigned_t<player_id_t>; | ||||
{ | |||||
if (player > 0 && player <= 16) | |||||
return static_cast<LosVisibility>( (visibilities >> (2 *(player-1))) & 0x3 ); | |||||
return LosVisibility::HIDDEN; | |||||
} | |||||
/** | constexpr std::array<nongaia_player_id_t, MAX_NB_OF_PLAYERS - 1> GetNonGaiaPlayers() | ||||
* Test whether the visibility is dirty for a given LoS region and a given player | |||||
*/ | |||||
bool IsVisibilityDirty(u16 dirty, player_id_t player) | |||||
{ | { | ||||
if (player > 0 && player <= 16) | std::array<nongaia_player_id_t, MAX_NB_OF_PLAYERS - 1> ret{}; | ||||
return (dirty >> (player - 1)) & 0x1; | for (nongaia_player_id_t i = 0; i < MAX_NB_OF_PLAYERS - 1; ++i) | ||||
phosit: That's an `std::iota`. | |||||
return false; | ret[i] = i + 1; | ||||
return ret; | |||||
} | } | ||||
Not Done Inline Actions#include <array> Stan: #include <array> | |||||
constexpr std::array<nongaia_player_id_t, MAX_NB_OF_PLAYERS - 1> nongaia_players = GetNonGaiaPlayers(); | |||||
/** | /** | ||||
* Test whether a player share this vision | * How many LOS vertices to have per region. | ||||
* LOS regions are used to keep track of units. | |||||
*/ | */ | ||||
bool HasVisionSharing(u16 visionSharing, player_id_t player) | constexpr int LOS_REGION_RATIO = 8; | ||||
{ | |||||
return (visionSharing & (1 << (player - 1))) != 0; | |||||
} | |||||
/** | /** | ||||
* Computes the shared vision mask for the player | * Tolerance for parabolic range calculations. | ||||
* TODO C++20: change this to constexpr by fixing CFixed with std::is_constant_evaluated | |||||
*/ | */ | ||||
u16 CalcVisionSharingMask(player_id_t player) | const fixed PARABOLIC_RANGE_TOLERANCE = fixed::FromInt(1)/2; | ||||
{ | |||||
return 1 << (player-1); | |||||
} | |||||
/** | /** | ||||
* Representation of a range query. | * Representation of a range query. | ||||
*/ | */ | ||||
struct Query | struct Query | ||||
{ | { | ||||
std::vector<entity_id_t> lastMatch; | std::vector<entity_id_t> lastMatch; | ||||
CEntityHandle source; // TODO: this could crash if an entity is destroyed while a Query is still referencing it | CEntityHandle source; // TODO: this could crash if an entity is destroyed while a Query is still referencing it | ||||
entity_pos_t minRange; | entity_pos_t minRange; | ||||
entity_pos_t maxRange; | entity_pos_t maxRange; | ||||
entity_pos_t elevationBonus; // Used for parabolas only. | entity_pos_t elevationBonus; // Used for parabolas only. | ||||
u32 ownersMask; | PerPlayerAndFlag<bool> ownersMask; // Flag used for 'no owner', i.e. owner == -1. | ||||
i32 interface; | i32 interface; | ||||
u8 flagsMask; | u8 flagsMask; | ||||
bool enabled; | bool enabled; | ||||
bool parabolic; | bool parabolic; | ||||
bool accountForSize; // If true, the query accounts for unit sizes, otherwise it treats all entities as points. | bool accountForSize; // If true, the query accounts for unit sizes, otherwise it treats all entities as points. | ||||
}; | }; | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | enum FlagMasks | ||||
RevealShore = 0x20, | RevealShore = 0x20, | ||||
ScriptedVisibility = 0x40, | ScriptedVisibility = 0x40, | ||||
SharedVision = 0x80 | SharedVision = 0x80 | ||||
}; | }; | ||||
struct EntityData | struct EntityData | ||||
{ | { | ||||
EntityData() : | EntityData() : | ||||
visibilities(0), size(0), visionSharing(0), | visibilities(LosVisibility::HIDDEN), size(0), visionSharing(0), | ||||
owner(-1), flags(FlagMasks::Normal) | owner(-1), flags(FlagMasks::Normal) | ||||
{ } | { } | ||||
entity_pos_t x, z; | entity_pos_t x, z; | ||||
entity_pos_t visionRange; | entity_pos_t visionRange; | ||||
u32 visibilities; // 2-bit visibility, per player | PerPlayer<LosVisibility> visibilities; | ||||
u32 size; | u32 size; | ||||
u16 visionSharing; // 1-bit per player | PerPlayer<bool> visionSharing; | ||||
i8 owner; | i8 owner; | ||||
u8 flags; // See the FlagMasks enum | u8 flags; // See the FlagMasks enum | ||||
template<int mask> | template<int mask> | ||||
inline bool HasFlag() const { return (flags & mask) != 0; } | inline bool HasFlag() const { return (flags & mask) != 0; } | ||||
template<int mask> | template<int mask> | ||||
inline void SetFlag(bool val) { flags = val ? (flags | mask) : (flags & ~mask); } | inline void SetFlag(bool val) { flags = val ? (flags | mask) : (flags & ~mask); } | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
struct SerializeHelper<Query> | struct SerializeHelper<Query> | ||||
{ | { | ||||
template<typename S> | template<typename S> | ||||
void Common(S& serialize, const char* UNUSED(name), Serialize::qualify<S, Query> value) | void Common(S& serialize, const char* UNUSED(name), Serialize::qualify<S, Query> value) | ||||
{ | { | ||||
serialize.NumberFixed_Unbounded("min range", value.minRange); | serialize.NumberFixed_Unbounded("min range", value.minRange); | ||||
serialize.NumberFixed_Unbounded("max range", value.maxRange); | serialize.NumberFixed_Unbounded("max range", value.maxRange); | ||||
serialize.NumberFixed_Unbounded("elevation bonus", value.elevationBonus); | serialize.NumberFixed_Unbounded("elevation bonus", value.elevationBonus); | ||||
serialize.NumberU32_Unbounded("owners mask", value.ownersMask); | Serializer(serialize, "owners mask", value.ownersMask); | ||||
serialize.NumberI32_Unbounded("interface", value.interface); | serialize.NumberI32_Unbounded("interface", value.interface); | ||||
Serializer(serialize, "last match", value.lastMatch); | Serializer(serialize, "last match", value.lastMatch); | ||||
serialize.NumberU8_Unbounded("flagsMask", value.flagsMask); | serialize.NumberU8_Unbounded("flagsMask", value.flagsMask); | ||||
serialize.Bool("enabled", value.enabled); | serialize.Bool("enabled", value.enabled); | ||||
serialize.Bool("parabolic",value.parabolic); | serialize.Bool("parabolic",value.parabolic); | ||||
serialize.Bool("account for size",value.accountForSize); | serialize.Bool("account for size",value.accountForSize); | ||||
} | } | ||||
Show All 24 Lines | |||||
struct SerializeHelper<EntityData> | struct SerializeHelper<EntityData> | ||||
{ | { | ||||
template<typename S> | template<typename S> | ||||
void operator()(S& serialize, const char* UNUSED(name), Serialize::qualify<S, EntityData> value) | void operator()(S& serialize, const char* UNUSED(name), Serialize::qualify<S, EntityData> value) | ||||
{ | { | ||||
serialize.NumberFixed_Unbounded("x", value.x); | serialize.NumberFixed_Unbounded("x", value.x); | ||||
serialize.NumberFixed_Unbounded("z", value.z); | serialize.NumberFixed_Unbounded("z", value.z); | ||||
serialize.NumberFixed_Unbounded("vision", value.visionRange); | serialize.NumberFixed_Unbounded("vision", value.visionRange); | ||||
serialize.NumberU32_Unbounded("visibilities", value.visibilities); | Serializer(serialize, "visibilities", value.visibilities); | ||||
serialize.NumberU32_Unbounded("size", value.size); | serialize.NumberU32_Unbounded("size", value.size); | ||||
serialize.NumberU16_Unbounded("vision sharing", value.visionSharing); | Serializer(serialize, "vision sharing", value.visionSharing); | ||||
serialize.NumberI8_Unbounded("owner", value.owner); | serialize.NumberI8_Unbounded("owner", value.owner); | ||||
serialize.NumberU8_Unbounded("flags", value.flags); | serialize.NumberU8_Unbounded("flags", value.flags); | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* Range manager implementation. | * Range manager implementation. | ||||
* Maintains a list of all entities (and their positions and owners), which is used for | * Maintains a list of all entities (and their positions and owners), which is used for | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | public: | ||||
std::array<bool, MAX_LOS_PLAYER_ID+2> m_LosRevealAll; | std::array<bool, MAX_LOS_PLAYER_ID+2> m_LosRevealAll; | ||||
bool m_LosCircular; | bool m_LosCircular; | ||||
i32 m_LosVerticesPerSide; | i32 m_LosVerticesPerSide; | ||||
// Cache for visibility tracking | // Cache for visibility tracking | ||||
i32 m_LosRegionsPerSide; | i32 m_LosRegionsPerSide; | ||||
bool m_GlobalVisibilityUpdate; | bool m_GlobalVisibilityUpdate; | ||||
std::array<bool, MAX_LOS_PLAYER_ID> m_GlobalPlayerVisibilityUpdate; | std::array<bool, MAX_LOS_PLAYER_ID> m_GlobalPlayerVisibilityUpdate; | ||||
Grid<u16> m_DirtyVisibility; | Grid<PerPlayer<bool>> m_DirtyVisibility; | ||||
Grid<std::set<entity_id_t>> m_LosRegions; | Grid<std::set<entity_id_t>> m_LosRegions; | ||||
// List of entities that must be updated, regardless of the status of their tile | // List of entities that must be updated, regardless of the status of their tile | ||||
std::vector<entity_id_t> m_ModifiedEntities; | std::vector<entity_id_t> m_ModifiedEntities; | ||||
// Counts of units seeing vertex, per vertex, per player (starting with player 0). | // Counts of units seeing vertex, per vertex, per player (starting with player 0). | ||||
// Use u16 to avoid overflows when we have very large (but not infeasibly large) numbers | // Use u16 to avoid overflows when we have very large (but not infeasibly large) numbers | ||||
// of units in a very small area. | // of units in a very small area. | ||||
// (Note we use vertexes, not tiles, to better match the renderer.) | // (Note we use vertexes, not tiles, to better match the renderer.) | ||||
// Lazily constructed when it's needed, to save memory in smaller games. | // Lazily constructed when it's needed, to save memory in smaller games. | ||||
std::array<Grid<u16>, MAX_LOS_PLAYER_ID> m_LosPlayerCounts; | std::array<Grid<u16>, MAX_LOS_PLAYER_ID> m_LosPlayerCounts; | ||||
// 2-bit LosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive) | // 2-bit LosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive) | ||||
Grid<u32> m_LosState; | Grid<u32> m_LosState; | ||||
// Special static visibility data for the "reveal whole map" mode | // Special static visibility data for the "reveal whole map" mode | ||||
// (TODO: this is usually a waste of memory) | // (TODO: this is usually a waste of memory) | ||||
Grid<u32> m_LosStateRevealed; | Grid<u32> m_LosStateRevealed; | ||||
// Shared LOS masks, one per player. | // Shared LOS masks, one per player. | ||||
std::array<u32, MAX_LOS_PLAYER_ID+2> m_SharedLosMasks; | std::array<PerPlayer<LosState>, MAX_LOS_PLAYER_ID+2> m_SharedLosMasks; | ||||
// Shared dirty visibility masks, one per player. | // Shared dirty visibility masks, one per player. | ||||
std::array<u16, MAX_LOS_PLAYER_ID+2> m_SharedDirtyVisibilityMasks; | std::array<PerPlayer<bool>, MAX_LOS_PLAYER_ID+2> m_SharedDirtyVisibilityMasks; | ||||
// Cache explored vertices per player (not serialized) | // Cache explored vertices per player (not serialized) | ||||
u32 m_TotalInworldVertices; | u32 m_TotalInworldVertices; | ||||
std::vector<u32> m_ExploredVertices; | std::vector<u32> m_ExploredVertices; | ||||
static std::string GetSchema() | static std::string GetSchema() | ||||
{ | { | ||||
return "<a:component type='system'/><empty/>"; | return "<a:component type='system'/><empty/>"; | ||||
▲ Show 20 Lines • Show All 296 Lines • ▼ Show 20 Lines | case MT_VisionSharingChanged: | ||||
EntityMap<EntityData>::iterator it = m_EntityData.find(ent); | EntityMap<EntityData>::iterator it = m_EntityData.find(ent); | ||||
// Ignore if we're not already tracking this entity | // Ignore if we're not already tracking this entity | ||||
if (it == m_EntityData.end()) | if (it == m_EntityData.end()) | ||||
break; | break; | ||||
ENSURE(msgData.player > 0 && msgData.player < MAX_LOS_PLAYER_ID+1); | ENSURE(msgData.player > 0 && msgData.player < MAX_LOS_PLAYER_ID+1); | ||||
u16 visionChanged = CalcVisionSharingMask(msgData.player); | nongaia_player_id_t player = msgData.player; | ||||
if (!it->second.HasFlag<FlagMasks::SharedVision>()) | if (!it->second.HasFlag<FlagMasks::SharedVision>()) | ||||
{ | { | ||||
// Activation of the Vision Sharing | // Activation of the Vision Sharing | ||||
ENSURE(it->second.owner == (i8)msgData.player); | ENSURE(nongaia_player_id_t(it->second.owner) == player); | ||||
it->second.visionSharing = visionChanged; | it->second.visionSharing[player] = true; | ||||
it->second.SetFlag<FlagMasks::SharedVision>(true); | it->second.SetFlag<FlagMasks::SharedVision>(true); | ||||
break; | break; | ||||
} | } | ||||
if (it->second.HasFlag<FlagMasks::InWorld>()) | if (it->second.HasFlag<FlagMasks::InWorld>()) | ||||
{ | { | ||||
entity_pos_t range = it->second.visionRange; | entity_pos_t range = it->second.visionRange; | ||||
CFixedVector2D pos(it->second.x, it->second.z); | CFixedVector2D pos(it->second.x, it->second.z); | ||||
if (msgData.add) | if (msgData.add) | ||||
LosAdd(msgData.player, range, pos); | LosAdd(player, range, pos); | ||||
else | else | ||||
LosRemove(msgData.player, range, pos); | LosRemove(player, range, pos); | ||||
} | } | ||||
// Add / remove the player from the mask. | |||||
if (msgData.add) | it->second.visionSharing[player] = msgData.add; | ||||
it->second.visionSharing |= visionChanged; | |||||
else | |||||
it->second.visionSharing &= ~visionChanged; | |||||
break; | break; | ||||
} | } | ||||
case MT_Update: | case MT_Update: | ||||
{ | { | ||||
m_DebugOverlayDirty = true; | m_DebugOverlayDirty = true; | ||||
ExecuteActiveQueries(); | ExecuteActiveQueries(); | ||||
UpdateVisibilityData(); | UpdateVisibilityData(); | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | void ResetDerivedData() | ||||
m_ExploredVertices.resize(MAX_LOS_PLAYER_ID+1, 0); | m_ExploredVertices.resize(MAX_LOS_PLAYER_ID+1, 0); | ||||
if (m_Deserializing) | if (m_Deserializing) | ||||
{ | { | ||||
// recalc current exploration stats. | // recalc current exploration stats. | ||||
for (i32 j = 0; j < m_LosVerticesPerSide; j++) | for (i32 j = 0; j < m_LosVerticesPerSide; j++) | ||||
for (i32 i = 0; i < m_LosVerticesPerSide; i++) | for (i32 i = 0; i < m_LosVerticesPerSide; i++) | ||||
if (!LosIsOffWorld(i, j)) | if (!LosIsOffWorld(i, j)) | ||||
for (u8 k = 1; k < MAX_LOS_PLAYER_ID+1; ++k) | for (nongaia_player_id_t k : nongaia_players) | ||||
m_ExploredVertices.at(k) += ((m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*(k-1)))) > 0); | m_ExploredVertices.at(k) += ((m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*(k-1)))) > 0); | ||||
} else | } else | ||||
m_LosState.resize(m_LosVerticesPerSide, m_LosVerticesPerSide); | m_LosState.resize(m_LosVerticesPerSide, m_LosVerticesPerSide); | ||||
m_LosStateRevealed.resize(m_LosVerticesPerSide, m_LosVerticesPerSide); | m_LosStateRevealed.resize(m_LosVerticesPerSide, m_LosVerticesPerSide); | ||||
if (!m_Deserializing) | if (!m_Deserializing) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 182 Lines • ▼ Show 20 Lines | virtual std::vector<entity_id_t> ResetActiveQuery(tag_t tag) | ||||
// Return the list sorted by distance from the entity | // Return the list sorted by distance from the entity | ||||
std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos)); | std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos)); | ||||
return r; | return r; | ||||
} | } | ||||
virtual std::vector<entity_id_t> GetEntitiesByPlayer(player_id_t player) const | virtual std::vector<entity_id_t> GetEntitiesByPlayer(player_id_t player) const | ||||
{ | { | ||||
return GetEntitiesByMask(CalcOwnerMask(player)); | std::vector<entity_id_t> entities; | ||||
for (EntityMap<EntityData>::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it) | |||||
Not Done Inline Actionsrange based for loop phosit: range based for loop | |||||
Done Inline ActionsWould std::copy_if work here? Stan: Would std::copy_if work here? | |||||
Done Inline ActionsThink entity map might not support the range-loop actually. wraitii: Think entity map might not support the range-loop actually. | |||||
Not Done Inline ActionsIt does support "range based for loop" and std::copy_if. phosit: It does support "range based for loop" and `std::copy_if`.
`std::copy_if` can't be used in this… | |||||
if (it->second.owner == player) | |||||
entities.push_back(it->first); | |||||
return entities; | |||||
} | } | ||||
virtual std::vector<entity_id_t> GetNonGaiaEntities() const | virtual std::vector<entity_id_t> GetNonGaiaEntities() const | ||||
{ | { | ||||
return GetEntitiesByMask(~3u); // bit 0 for owner=-1 and bit 1 for gaia | PerPlayerAndFlag<bool> mask(true); | ||||
mask[-1] = false; | |||||
mask[0] = false; | |||||
return GetEntitiesByMask(mask); | |||||
} | } | ||||
virtual std::vector<entity_id_t> GetGaiaAndNonGaiaEntities() const | virtual std::vector<entity_id_t> GetGaiaAndNonGaiaEntities() const | ||||
{ | { | ||||
return GetEntitiesByMask(~1u); // bit 0 for owner=-1 | PerPlayerAndFlag<bool> mask(true); | ||||
mask[-1] = false; | |||||
return GetEntitiesByMask(mask); | |||||
} | } | ||||
std::vector<entity_id_t> GetEntitiesByMask(u32 ownerMask) const | std::vector<entity_id_t> GetEntitiesByMask(PerPlayerAndFlag<bool> mask) const | ||||
{ | { | ||||
std::vector<entity_id_t> entities; | std::vector<entity_id_t> entities; | ||||
for (EntityMap<EntityData>::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it) | for (EntityMap<EntityData>::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it) | ||||
{ | { | ||||
// Check owner and add to list if it matches | // Check owner and add to list if it matches | ||||
if (CalcOwnerMask(it->second.owner) & ownerMask) | if (mask[it->second.owner]) | ||||
entities.push_back(it->first); | entities.push_back(it->first); | ||||
} | } | ||||
return entities; | return entities; | ||||
} | } | ||||
virtual void SetDebugOverlay(bool enabled) | virtual void SetDebugOverlay(bool enabled) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | public: | ||||
} | } | ||||
/** | /** | ||||
* Returns whether the given entity matches the given query (ignoring maxRange) | * Returns whether the given entity matches the given query (ignoring maxRange) | ||||
*/ | */ | ||||
bool TestEntityQuery(const Query& q, entity_id_t id, const EntityData& entity) const | bool TestEntityQuery(const Query& q, entity_id_t id, const EntityData& entity) const | ||||
{ | { | ||||
// Quick filter to ignore entities with the wrong owner | // Quick filter to ignore entities with the wrong owner | ||||
if (!(CalcOwnerMask(entity.owner) & q.ownersMask)) | if (!q.ownersMask[entity.owner]) | ||||
return false; | return false; | ||||
// Ignore entities not present in the world | // Ignore entities not present in the world | ||||
if (!entity.HasFlag<FlagMasks::InWorld>()) | if (!entity.HasFlag<FlagMasks::InWorld>()) | ||||
return false; | return false; | ||||
// Ignore entities that don't match the current flags | // Ignore entities that don't match the current flags | ||||
if (!((entity.flags & FlagMasks::AllQuery) & q.flagsMask)) | if (!((entity.flags & FlagMasks::AllQuery) & q.flagsMask)) | ||||
▲ Show 20 Lines • Show All 237 Lines • ▼ Show 20 Lines | if (q.accountForSize && q.source.GetId() != INVALID_ENTITY && q.maxRange != entity_pos_t::FromInt(-1)) | ||||
size = it->second.size; | size = it->second.size; | ||||
} | } | ||||
// Adjust the range query based on the querier's obstruction radius. | // Adjust the range query based on the querier's obstruction radius. | ||||
// The smallest side of the obstruction isn't known here, so we can't safely adjust the min-range, only the max. | // The smallest side of the obstruction isn't known here, so we can't safely adjust the min-range, only the max. | ||||
// 'size' is the diagonal size rounded up so this will cover all possible rotations of the querier. | // 'size' is the diagonal size rounded up so this will cover all possible rotations of the querier. | ||||
q.maxRange += fixed::FromInt(size); | q.maxRange += fixed::FromInt(size); | ||||
} | } | ||||
q.ownersMask = 0; | if (owners.empty()) | ||||
for (size_t i = 0; i < owners.size(); ++i) | |||||
q.ownersMask |= CalcOwnerMask(owners[i]); | |||||
if (q.ownersMask == 0) | |||||
LOGWARNING("CCmpRangeManager: No owners in query for entity %u", source); | LOGWARNING("CCmpRangeManager: No owners in query for entity %u", source); | ||||
q.ownersMask = PerPlayerAndFlag<bool>(false); | |||||
for (int player : owners) | |||||
q.ownersMask[player] = true; | |||||
q.interface = requiredInterface; | q.interface = requiredInterface; | ||||
q.flagsMask = flagsMask; | q.flagsMask = flagsMask; | ||||
return q; | return q; | ||||
} | } | ||||
Query ConstructParabolicQuery(entity_id_t source, | Query ConstructParabolicQuery(entity_id_t source, | ||||
entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t elevationBonus, | entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t elevationBonus, | ||||
▲ Show 20 Lines • Show All 303 Lines • ▼ Show 20 Lines | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return LosVisibility::HIDDEN; | return LosVisibility::HIDDEN; | ||||
// Gaia and observers do not have a visibility cache | // Gaia and observers do not have a visibility cache | ||||
if (player <= 0) | if (player <= 0) | ||||
return ComputeLosVisibility(ent, player); | return ComputeLosVisibility(ent, player); | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | CFixedVector2D pos = cmpPosition->GetPosition2D(); | ||||
if (IsVisibilityDirty(m_DirtyVisibility[PosToLosRegionsHelper(pos.X, pos.Y)], player)) | nongaia_player_id_t validPlayer = player; | ||||
if (m_DirtyVisibility[PosToLosRegionsHelper(pos.X, pos.Y)][validPlayer]) | |||||
return ComputeLosVisibility(ent, player); | return ComputeLosVisibility(ent, player); | ||||
if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), entId) != m_ModifiedEntities.end()) | if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), entId) != m_ModifiedEntities.end()) | ||||
return ComputeLosVisibility(ent, player); | return ComputeLosVisibility(ent, player); | ||||
EntityMap<EntityData>::const_iterator it = m_EntityData.find(entId); | EntityMap<EntityData>::const_iterator it = m_EntityData.find(entId); | ||||
if (it == m_EntityData.end()) | if (it == m_EntityData.end()) | ||||
return ComputeLosVisibility(ent, player); | return ComputeLosVisibility(ent, player); | ||||
return static_cast<LosVisibility>(GetPlayerVisibility(it->second.visibilities, player)); | return it->second.visibilities[validPlayer]; | ||||
} | } | ||||
virtual LosVisibility GetLosVisibility(entity_id_t ent, player_id_t player) const | virtual LosVisibility GetLosVisibility(entity_id_t ent, player_id_t player) const | ||||
{ | { | ||||
CEntityHandle handle = GetSimContext().GetComponentManager().LookupEntityHandle(ent); | CEntityHandle handle = GetSimContext().GetComponentManager().LookupEntityHandle(ent); | ||||
return GetLosVisibility(handle, player); | return GetLosVisibility(handle, player); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
void UpdateVisibilityData() | void UpdateVisibilityData() | ||||
{ | { | ||||
PROFILE("UpdateVisibilityData"); | PROFILE("UpdateVisibilityData"); | ||||
for (u16 i = 0; i < m_LosRegionsPerSide; ++i) | for (u16 i = 0; i < m_LosRegionsPerSide; ++i) | ||||
for (u16 j = 0; j < m_LosRegionsPerSide; ++j) | for (u16 j = 0; j < m_LosRegionsPerSide; ++j) | ||||
{ | { | ||||
LosRegion pos{i, j}; | LosRegion pos{i, j}; | ||||
for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID + 1; ++player) | for (nongaia_player_id_t player : nongaia_players) | ||||
if (IsVisibilityDirty(m_DirtyVisibility[pos], player) || m_GlobalPlayerVisibilityUpdate[player-1] == 1 || m_GlobalVisibilityUpdate) | if (m_DirtyVisibility[pos][player] || m_GlobalPlayerVisibilityUpdate[player-1] == 1 || m_GlobalVisibilityUpdate) | ||||
for (const entity_id_t& ent : m_LosRegions[pos]) | for (const entity_id_t& ent : m_LosRegions[pos]) | ||||
UpdateVisibility(ent, player); | UpdateVisibility(ent, player); | ||||
m_DirtyVisibility[pos] = 0; | m_DirtyVisibility[pos] = PerPlayer<bool>(false); | ||||
} | } | ||||
std::fill(m_GlobalPlayerVisibilityUpdate.begin(), m_GlobalPlayerVisibilityUpdate.end(), false); | std::fill(m_GlobalPlayerVisibilityUpdate.begin(), m_GlobalPlayerVisibilityUpdate.end(), false); | ||||
m_GlobalVisibilityUpdate = false; | m_GlobalVisibilityUpdate = false; | ||||
// Calling UpdateVisibility can modify m_ModifiedEntities, so be careful: | // Calling UpdateVisibility can modify m_ModifiedEntities, so be careful: | ||||
// infinite loops could be triggered by feedback between entities and their mirages. | // infinite loops could be triggered by feedback between entities and their mirages. | ||||
std::map<entity_id_t, u8> attempts; | std::map<entity_id_t, u8> attempts; | ||||
Show All 10 Lines | |||||
} | } | ||||
virtual void RequestVisibilityUpdate(entity_id_t ent) | virtual void RequestVisibilityUpdate(entity_id_t ent) | ||||
{ | { | ||||
if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), ent) == m_ModifiedEntities.end()) | if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), ent) == m_ModifiedEntities.end()) | ||||
m_ModifiedEntities.push_back(ent); | m_ModifiedEntities.push_back(ent); | ||||
} | } | ||||
void UpdateVisibility(entity_id_t ent, player_id_t player) | void UpdateVisibility(entity_id_t ent, nongaia_player_id_t player) | ||||
{ | { | ||||
EntityMap<EntityData>::iterator itEnts = m_EntityData.find(ent); | EntityMap<EntityData>::iterator itEnts = m_EntityData.find(ent); | ||||
if (itEnts == m_EntityData.end()) | if (itEnts == m_EntityData.end()) | ||||
return; | return; | ||||
LosVisibility oldVis = GetPlayerVisibility(itEnts->second.visibilities, player); | LosVisibility oldVis = itEnts->second.visibilities[player]; | ||||
LosVisibility newVis = ComputeLosVisibility(itEnts->first, player); | LosVisibility newVis = ComputeLosVisibility(itEnts->first, player); | ||||
if (oldVis == newVis) | if (oldVis == newVis) | ||||
return; | return; | ||||
itEnts->second.visibilities = (itEnts->second.visibilities & ~(0x3 << 2 * (player - 1))) | ((u8)newVis << 2 * (player - 1)); | itEnts->second.visibilities[player] = newVis; | ||||
CMessageVisibilityChanged msg(player, ent, static_cast<int>(oldVis), static_cast<int>(newVis)); | CMessageVisibilityChanged msg(player, ent, static_cast<int>(oldVis), static_cast<int>(newVis)); | ||||
GetSimContext().GetComponentManager().PostMessage(ent, msg); | GetSimContext().GetComponentManager().PostMessage(ent, msg); | ||||
} | } | ||||
void UpdateVisibility(entity_id_t ent) | void UpdateVisibility(entity_id_t ent) | ||||
{ | { | ||||
for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID + 1; ++player) | for (nongaia_player_id_t player : nongaia_players) | ||||
UpdateVisibility(ent, player); | UpdateVisibility(ent, player); | ||||
} | } | ||||
virtual void SetLosRevealAll(player_id_t player, bool enabled) | virtual void SetLosRevealAll(player_id_t player, bool enabled) | ||||
{ | { | ||||
if (player == -1) | if (player == -1) | ||||
m_LosRevealAll[MAX_LOS_PLAYER_ID+1] = enabled; | m_LosRevealAll[MAX_LOS_PLAYER_ID+1] = enabled; | ||||
else | else | ||||
Show All 28 Lines | |||||
virtual bool GetLosCircular() const | virtual bool GetLosCircular() const | ||||
{ | { | ||||
return m_LosCircular; | return m_LosCircular; | ||||
} | } | ||||
virtual void SetSharedLos(player_id_t player, const std::vector<player_id_t>& players) | virtual void SetSharedLos(player_id_t player, const std::vector<player_id_t>& players) | ||||
{ | { | ||||
m_SharedLosMasks[player] = CalcSharedLosMask(players); | if (player <= 0) | ||||
{ | |||||
LOGERROR("Cannot set shared LOS for player %i (GAIA or invalid)", player); | |||||
return; | |||||
} | |||||
nongaia_player_id_t validPlayer = player; | |||||
m_SharedLosMasks[validPlayer] = PerPlayer<LosState>(LosState::UNEXPLORED); | |||||
for (player_id_t p : players) | |||||
{ | |||||
if (p <= 0) | |||||
{ | |||||
LOGERROR("Cannot share LOS of player %i (GAIA or invalid)", p); | |||||
return; | |||||
} | |||||
m_SharedLosMasks[validPlayer][nongaia_player_id_t(p)] = LosState::MASK; | |||||
} | |||||
// Units belonging to any of 'players' can now trigger visibility updates for 'player'. | // Units belonging to any of 'players' can now trigger visibility updates for 'player'. | ||||
// If shared LOS partners have been removed, we disable visibility updates from them | // If shared LOS partners have been removed, we disable visibility updates from them | ||||
// in order to improve performance. That also allows us to properly determine whether | // in order to improve performance. That also allows us to properly determine whether | ||||
// 'player' needs a global visibility update for this turn. | // 'player' needs a global visibility update for this turn. | ||||
bool modified = false; | bool modified = false; | ||||
for (player_id_t p = 1; p < MAX_LOS_PLAYER_ID+1; ++p) | for (nongaia_player_id_t p : nongaia_players) | ||||
{ | { | ||||
bool inList = std::find(players.begin(), players.end(), p) != players.end(); | bool inList = std::find(players.begin(), players.end(), p) != players.end(); | ||||
auto&& mask = m_SharedDirtyVisibilityMasks[p][validPlayer]; | |||||
if (SetPlayerSharedDirtyVisibilityBit(m_SharedDirtyVisibilityMasks[p], player, inList)) | if (mask != inList) | ||||
modified = true; | modified = true; | ||||
mask = inList; | |||||
} | } | ||||
if (modified && (size_t)player <= m_GlobalPlayerVisibilityUpdate.size()) | if (modified && (size_t)player <= m_GlobalPlayerVisibilityUpdate.size()) | ||||
m_GlobalPlayerVisibilityUpdate[player-1] = 1; | m_GlobalPlayerVisibilityUpdate[player-1] = 1; | ||||
} | } | ||||
virtual u32 GetSharedLosMask(player_id_t player) const | virtual u32 GetSharedLosMask(player_id_t player) const | ||||
{ | { | ||||
return m_SharedLosMasks[player]; | return *m_SharedLosMasks[player].data(); | ||||
} | } | ||||
void ExploreMap(player_id_t p) | void ExploreMap(player_id_t p) | ||||
{ | { | ||||
for (i32 j = 0; j < m_LosVerticesPerSide; ++j) | for (i32 j = 0; j < m_LosVerticesPerSide; ++j) | ||||
for (i32 i = 0; i < m_LosVerticesPerSide; ++i) | for (i32 i = 0; i < m_LosVerticesPerSide; ++i) | ||||
{ | { | ||||
if (LosIsOffWorld(i,j)) | if (LosIsOffWorld(i,j)) | ||||
▲ Show 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | else | ||||
i >= m_LosVerticesPerSide - MAP_EDGE_TILES || | i >= m_LosVerticesPerSide - MAP_EDGE_TILES || | ||||
j >= m_LosVerticesPerSide - MAP_EDGE_TILES; | j >= m_LosVerticesPerSide - MAP_EDGE_TILES; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). | * Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). | ||||
*/ | */ | ||||
inline void LosAddStripHelper(u8 owner, i32 i0, i32 i1, i32 j, Grid<u16>& counts) | inline void LosAddStripHelper(nongaia_player_id_t owner, i32 i0, i32 i1, i32 j, Grid<u16>& counts) | ||||
{ | { | ||||
if (i1 < i0) | if (i1 < i0) | ||||
return; | return; | ||||
u32 &explored = m_ExploredVertices.at(owner); | u32 &explored = m_ExploredVertices.at(owner); | ||||
for (i32 i = i0; i <= i1; ++i) | for (i32 i = i0; i <= i1; ++i) | ||||
{ | { | ||||
// Increasing from zero to non-zero - move from unexplored/explored to visible+explored | // Increasing from zero to non-zero - move from unexplored/explored to visible+explored | ||||
Show All 11 Lines | for (i32 i = i0; i <= i1; ++i) | ||||
ENSURE(counts.get(i, j) < std::numeric_limits<u16>::max()); | ENSURE(counts.get(i, j) < std::numeric_limits<u16>::max()); | ||||
counts.get(i, j) = (u16)(counts.get(i, j) + 1); // ignore overflow; the player should never have 64K units | counts.get(i, j) = (u16)(counts.get(i, j) + 1); // ignore overflow; the player should never have 64K units | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). | * Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). | ||||
*/ | */ | ||||
inline void LosRemoveStripHelper(u8 owner, i32 i0, i32 i1, i32 j, Grid<u16>& counts) | inline void LosRemoveStripHelper(nongaia_player_id_t owner, i32 i0, i32 i1, i32 j, Grid<u16>& counts) | ||||
{ | { | ||||
if (i1 < i0) | if (i1 < i0) | ||||
return; | return; | ||||
for (i32 i = i0; i <= i1; ++i) | for (i32 i = i0; i <= i1; ++i) | ||||
{ | { | ||||
ASSERT(counts.get(i, j) > 0); | ASSERT(counts.get(i, j) > 0); | ||||
counts.get(i, j) = (u16)(counts.get(i, j) - 1); | counts.get(i, j) = (u16)(counts.get(i, j) - 1); | ||||
// Decreasing from non-zero to zero - move from visible+explored to explored | // Decreasing from non-zero to zero - move from visible+explored to explored | ||||
if (counts.get(i, j) == 0) | if (counts.get(i, j) == 0) | ||||
{ | { | ||||
// (If LosIsOffWorld then this is a no-op, so don't bother doing the check) | // (If LosIsOffWorld then this is a no-op, so don't bother doing the check) | ||||
m_LosState.get(i, j) &= ~((int)LosState::VISIBLE << (2*(owner-1))); | m_LosState.get(i, j) &= ~((int)LosState::VISIBLE << (2*(owner-1))); | ||||
MarkVisibilityDirtyAroundTile(owner, i, j); | MarkVisibilityDirtyAroundTile(owner, i, j); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
inline void MarkVisibilityDirtyAroundTile(u8 owner, i32 i, i32 j) | inline void MarkVisibilityDirtyAroundTile(nongaia_player_id_t owner, i32 i, i32 j) | ||||
{ | { | ||||
// If we're still in the deserializing process, we must not modify m_DirtyVisibility | // If we're still in the deserializing process, we must not modify m_DirtyVisibility | ||||
if (m_Deserializing) | if (m_Deserializing) | ||||
return; | return; | ||||
// Mark the LoS regions around the updated vertex | // Mark the LoS regions around the updated vertex | ||||
// 1: left-up, 2: right-up, 3: left-down, 4: right-down | // 1: left-up, 2: right-up, 3: left-down, 4: right-down | ||||
LosRegion n1 = LosVertexToLosRegionsHelper(i-1, j-1); | LosRegion n1 = LosVertexToLosRegionsHelper(i-1, j-1); | ||||
LosRegion n2 = LosVertexToLosRegionsHelper(i-1, j); | LosRegion n2 = LosVertexToLosRegionsHelper(i-1, j); | ||||
LosRegion n3 = LosVertexToLosRegionsHelper(i, j-1); | LosRegion n3 = LosVertexToLosRegionsHelper(i, j-1); | ||||
LosRegion n4 = LosVertexToLosRegionsHelper(i, j); | LosRegion n4 = LosVertexToLosRegionsHelper(i, j); | ||||
u16 sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner]; | PerPlayer<bool> sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner]; | ||||
if (j > 0 && i > 0) | if (j > 0 && i > 0) | ||||
m_DirtyVisibility[n1] |= sharedDirtyVisibilityMask; | for (nongaia_player_id_t player : nongaia_players) | ||||
m_DirtyVisibility[n1][player] |= sharedDirtyVisibilityMask[player]; | |||||
if (n2 != n1 && j > 0 && i < m_LosVerticesPerSide) | if (n2 != n1 && j > 0 && i < m_LosVerticesPerSide) | ||||
m_DirtyVisibility[n2] |= sharedDirtyVisibilityMask; | for (nongaia_player_id_t player : nongaia_players) | ||||
m_DirtyVisibility[n2][player] |= sharedDirtyVisibilityMask[player]; | |||||
if (n3 != n1 && j < m_LosVerticesPerSide && i > 0) | if (n3 != n1 && j < m_LosVerticesPerSide && i > 0) | ||||
m_DirtyVisibility[n3] |= sharedDirtyVisibilityMask; | for (nongaia_player_id_t player : nongaia_players) | ||||
m_DirtyVisibility[n3][player] |= sharedDirtyVisibilityMask[player]; | |||||
if (n4 != n1 && j < m_LosVerticesPerSide && i < m_LosVerticesPerSide) | if (n4 != n1 && j < m_LosVerticesPerSide && i < m_LosVerticesPerSide) | ||||
m_DirtyVisibility[n4] |= sharedDirtyVisibilityMask; | for (nongaia_player_id_t player : nongaia_players) | ||||
m_DirtyVisibility[n4][player] |= sharedDirtyVisibilityMask[player]; | |||||
} | } | ||||
Done Inline ActionsNeed to genericize these wraitii: Need to genericize these | |||||
/** | /** | ||||
* Update the LOS state of tiles within a given circular range, | * Update the LOS state of tiles within a given circular range, | ||||
* either adding or removing visibility depending on the template parameter. | * either adding or removing visibility depending on the template parameter. | ||||
* Assumes owner is in the valid range. | * Assumes owner is in the valid range. | ||||
*/ | */ | ||||
template<bool adding> | template<bool adding> | ||||
void LosUpdateHelper(u8 owner, entity_pos_t visionRange, CFixedVector2D pos) | void LosUpdateHelper(nongaia_player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos) | ||||
{ | { | ||||
if (m_LosVerticesPerSide == 0) // do nothing if not initialised yet | if (m_LosVerticesPerSide == 0) // do nothing if not initialised yet | ||||
return; | return; | ||||
PROFILE("LosUpdateHelper"); | PROFILE("LosUpdateHelper"); | ||||
Grid<u16>& counts = m_LosPlayerCounts.at(owner); | Grid<u16>& counts = m_LosPlayerCounts.at(owner); | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | #endif | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Update the LOS state of tiles within a given circular range, | * Update the LOS state of tiles within a given circular range, | ||||
* by removing visibility around the 'from' position | * by removing visibility around the 'from' position | ||||
* and then adding visibility around the 'to' position. | * and then adding visibility around the 'to' position. | ||||
*/ | */ | ||||
void LosUpdateHelperIncremental(u8 owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | void LosUpdateHelperIncremental(nongaia_player_id_t owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | ||||
{ | { | ||||
if (m_LosVerticesPerSide == 0) // do nothing if not initialised yet | if (m_LosVerticesPerSide == 0) // do nothing if not initialised yet | ||||
return; | return; | ||||
PROFILE("LosUpdateHelperIncremental"); | PROFILE("LosUpdateHelperIncremental"); | ||||
Grid<u16>& counts = m_LosPlayerCounts.at(owner); | Grid<u16>& counts = m_LosPlayerCounts.at(owner); | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | #endif | ||||
} | } | ||||
} | } | ||||
void LosAdd(player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos) | void LosAdd(player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos) | ||||
{ | { | ||||
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | ||||
return; | return; | ||||
LosUpdateHelper<true>((u8)owner, visionRange, pos); | LosUpdateHelper<true>(nongaia_player_id_t(owner), visionRange, pos); | ||||
} | } | ||||
void SharingLosAdd(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos) | void SharingLosAdd(PerPlayer<bool> visionSharing, entity_pos_t visionRange, CFixedVector2D pos) | ||||
{ | { | ||||
if (visionRange.IsZero()) | if (visionRange.IsZero()) | ||||
return; | return; | ||||
for (player_id_t i = 1; i < MAX_LOS_PLAYER_ID+1; ++i) | for (nongaia_player_id_t i : nongaia_players) | ||||
if (HasVisionSharing(visionSharing, i)) | if (visionSharing[i]) | ||||
LosAdd(i, visionRange, pos); | LosAdd(i, visionRange, pos); | ||||
} | } | ||||
void LosRemove(player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos) | void LosRemove(player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos) | ||||
{ | { | ||||
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | ||||
return; | return; | ||||
LosUpdateHelper<false>((u8)owner, visionRange, pos); | LosUpdateHelper<false>(nongaia_player_id_t(owner), visionRange, pos); | ||||
} | } | ||||
void SharingLosRemove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos) | void SharingLosRemove(PerPlayer<bool> visionSharing, entity_pos_t visionRange, CFixedVector2D pos) | ||||
{ | { | ||||
if (visionRange.IsZero()) | if (visionRange.IsZero()) | ||||
return; | return; | ||||
for (player_id_t i = 1; i < MAX_LOS_PLAYER_ID+1; ++i) | for (nongaia_player_id_t i : nongaia_players) | ||||
if (HasVisionSharing(visionSharing, i)) | if (visionSharing[i]) | ||||
LosRemove(i, visionRange, pos); | LosRemove(i, visionRange, pos); | ||||
} | } | ||||
void LosMove(player_id_t owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | void LosMove(player_id_t owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | ||||
{ | { | ||||
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | ||||
return; | return; | ||||
if ((from - to).CompareLength(visionRange) > 0) | if ((from - to).CompareLength(visionRange) > 0) | ||||
{ | { | ||||
// If it's a very large move, then simply remove and add to the new position | // If it's a very large move, then simply remove and add to the new position | ||||
LosUpdateHelper<false>((u8)owner, visionRange, from); | LosUpdateHelper<false>(nongaia_player_id_t(owner), visionRange, from); | ||||
LosUpdateHelper<true>((u8)owner, visionRange, to); | LosUpdateHelper<true>(nongaia_player_id_t(owner), visionRange, to); | ||||
} | } | ||||
else | else | ||||
// Otherwise use the version optimised for mostly-overlapping circles | // Otherwise use the version optimised for mostly-overlapping circles | ||||
LosUpdateHelperIncremental((u8)owner, visionRange, from, to); | LosUpdateHelperIncremental(nongaia_player_id_t(owner), visionRange, from, to); | ||||
} | } | ||||
void SharingLosMove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | void SharingLosMove(PerPlayer<bool> visionSharing, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | ||||
{ | { | ||||
if (visionRange.IsZero()) | if (visionRange.IsZero()) | ||||
return; | return; | ||||
for (player_id_t i = 1; i < MAX_LOS_PLAYER_ID+1; ++i) | for (nongaia_player_id_t i : nongaia_players) | ||||
if (HasVisionSharing(visionSharing, i)) | if (visionSharing[i]) | ||||
LosMove(i, visionRange, from, to); | LosMove(i, visionRange, from, to); | ||||
} | } | ||||
virtual u8 GetPercentMapExplored(player_id_t player) const | virtual u8 GetPercentMapExplored(player_id_t player) const | ||||
{ | { | ||||
return m_ExploredVertices.at((u8)player) * 100 / m_TotalInworldVertices; | return m_ExploredVertices.at((u8)player) * 100 / m_TotalInworldVertices; | ||||
} | } | ||||
virtual u8 GetUnionPercentMapExplored(const std::vector<player_id_t>& players) const | virtual u8 GetUnionPercentMapExplored(const std::vector<player_id_t>& players) const | ||||
{ | { | ||||
u32 exploredVertices = 0; | u32 exploredVertices = 0; | ||||
std::vector<player_id_t>::const_iterator playerIt; | std::vector<player_id_t>::const_iterator playerIt; | ||||
for (i32 j = 0; j < m_LosVerticesPerSide; j++) | for (i32 j = 0; j < m_LosVerticesPerSide; j++) | ||||
for (i32 i = 0; i < m_LosVerticesPerSide; i++) | for (i32 i = 0; i < m_LosVerticesPerSide; i++) | ||||
{ | { | ||||
if (LosIsOffWorld(i, j)) | if (LosIsOffWorld(i, j)) | ||||
continue; | continue; | ||||
for (playerIt = players.begin(); playerIt != players.end(); ++playerIt) | for (playerIt = players.begin(); playerIt != players.end(); ++playerIt) | ||||
if (m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*((*playerIt)-1)))) | if (m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*((*playerIt)-1)))) | ||||
{ | { | ||||
exploredVertices += 1; | exploredVertices += 1; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
Not Done Inline ActionsThat's an std::find phosit: That's an `std::find` | |||||
Not Done Inline Actionsstd::any no? Stan: std::any no? | |||||
Done Inline ActionsThink it's a std::find_if, not sure it's more readable but worth checking wraitii: Think it's a `std::find_if`, not sure it's more readable but worth checking | |||||
Not Done Inline ActionsIt's definitly not a std::find ^^ phosit: It's definitly not a `std::find` ^^
It's also not a `std::any` (that woud be a type;-) )
`std… | |||||
return exploredVertices * 100 / m_TotalInworldVertices; | return exploredVertices * 100 / m_TotalInworldVertices; | ||||
} | } | ||||
}; | }; | ||||
REGISTER_COMPONENT_TYPE(RangeManager) | REGISTER_COMPONENT_TYPE(RangeManager) |
Wildfire Games · Phabricator
That's an std::iota.