Changeset View
Standalone View
source/simulation2/components/CCmpRangeManager.cpp
Show All 29 Lines | |||||
#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/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/helpers/ArrayData.h" | |||||
#include "graphics/Overlay.h" | #include "graphics/Overlay.h" | ||||
#include "graphics/Terrain.h" | #include "graphics/Terrain.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 LOS_TILES_RATIO 8 | #define LOS_TILES_RATIO 8 | ||||
#define DEBUG_RANGE_MANAGER_BOUNDS 0 | #define DEBUG_RANGE_MANAGER_BOUNDS 0 | ||||
/** | /** | ||||
* Representation of a range query. | * Representation of a range query. | ||||
*/ | */ | ||||
struct Query | struct Query | ||||
{ | { | ||||
bool enabled; | bool enabled; | ||||
bool parabolic; | bool parabolic; | ||||
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; | entity_pos_t elevationBonus; | ||||
u32 ownersMask; | OwnersData ownersMask; | ||||
i32 interface; | i32 interface; | ||||
std::vector<entity_id_t> lastMatch; | std::vector<entity_id_t> lastMatch; | ||||
u8 flagsMask; | u8 flagsMask; | ||||
}; | }; | ||||
/** | |||||
* Convert an owner ID (-1 = unowned, 0 = gaia, 1..30 = players) | static inline bool validPlayer(player_id_t player) | ||||
Silier: `IsValidPlayer` or `IsPlayerValid` | |||||
* into a 32-bit mask for quick set-membership tests. | |||||
*/ | |||||
static inline u32 CalcOwnerMask(player_id_t owner) | |||||
{ | { | ||||
if (owner >= -1 && owner < 31) | return player > 0 && player <= Simulation_GetNumberOfPlayers(); | ||||
return 1 << (1+owner); | |||||
else | |||||
return 0; // owner was invalid | |||||
} | } | ||||
/** | static inline bool validOwner(int owner) | ||||
SilierUnsubmitted Done Inline ActionsIsValidOwner or IsOwnerValid Silier: `IsValidOwner` or `IsOwnerValid` | |||||
* Returns LOS mask for given player. | |||||
*/ | |||||
static inline u32 CalcPlayerLosMask(player_id_t player) | |||||
{ | { | ||||
if (player > 0 && player <= 16) | return owner >= -1 && owner <= Simulation_GetNumberOfPlayers(); | ||||
return ICmpRangeManager::LOS_MASK << (2*(player-1)); | |||||
return 0; | |||||
} | } | ||||
/** | /** | ||||
* Returns shared LOS mask for given list of players. | * Returns shared LOS mask for given list of players. | ||||
*/ | */ | ||||
static u32 CalcSharedLosMask(std::vector<player_id_t> players) | static PlayersData CalcSharedLosMask(std::vector<player_id_t> players) | ||||
{ | { | ||||
u32 playerMask = 0; | PlayersData playerMask(0); | ||||
for (size_t i = 0; i < players.size(); i++) | for (player_id_t id : players) | ||||
playerMask |= CalcPlayerLosMask(players[i]); | if(validPlayer(id)) | ||||
SilierUnsubmitted Done Inline Actionsif ( Silier: `if (` | |||||
playerMask.set(id, ICmpRangeManager::LOS_MASK); | |||||
return playerMask; | return playerMask; | ||||
} | } | ||||
/** | /** | ||||
* Add/remove a player to/from mask, which is a 1-bit mask representing a list of players. | * Add/remove a player to/from mask, which is a 1-bit mask representing a list of players. | ||||
* Returns true if the mask is modified. | * Returns true if the mask is modified. | ||||
*/ | */ | ||||
static bool SetPlayerSharedDirtyVisibilityBit(u16& mask, player_id_t player, bool enable) | static bool SetPlayerSharedDirtyVisibilityBit(PlayersData& mask, player_id_t player, bool enable) | ||||
{ | { | ||||
if (player <= 0 || player > 16) | if (!validPlayer(player)) | ||||
return false; | return false; | ||||
u16 oldMask = mask; | PlayersData oldMask = mask; | ||||
if (enable) | mask.set(player, enable ? 0x1 : ~0x1); | ||||
mask |= (0x1 << (player - 1)); | |||||
else | |||||
mask &= ~(0x1 << (player - 1)); | |||||
return oldMask != mask; | return !oldMask.isEqual(mask); | ||||
} | } | ||||
/** | /** | ||||
* Computes the 2-bit visibility for one player, given the total 32-bit visibilities | * Computes the 2-bit visibility for one player, given the total 32-bit visibilities | ||||
*/ | */ | ||||
static inline u8 GetPlayerVisibility(u32 visibilities, player_id_t player) | static inline u8 GetPlayerVisibility(PlayersData visibilities, player_id_t player) | ||||
{ | { | ||||
if (player > 0 && player <= 16) | if (validPlayer(player)) | ||||
return (visibilities >> (2 *(player-1))) & 0x3; | return visibilities.get(player) & 0x3; | ||||
Done Inline Actionsremove & 0x3, thats redundant because how getInternal is written Silier: remove & 0x3, thats redundant because how getInternal is written | |||||
return 0; | return 0; | ||||
} | } | ||||
/** | /** | ||||
* Test whether the visibility is dirty for a given LoS tile and a given player | * Test whether the visibility is dirty for a given LoS tile and a given player | ||||
*/ | */ | ||||
static inline bool IsVisibilityDirty(u16 dirty, player_id_t player) | static inline bool IsVisibilityDirty(const PlayersData & dirty, player_id_t player) | ||||
SilierUnsubmitted Done Inline ActionsPlayerData& dirty Silier: `PlayerData& dirty` | |||||
{ | { | ||||
if (player > 0 && player <= 16) | if (validPlayer(player)) | ||||
return (dirty >> (player - 1)) & 0x1; | return dirty.get(player) & 0x1; | ||||
Done Inline Actionsmaybe you could think to pass 0x1 so it is not done & 0x3 in getinternal Silier: maybe you could think to pass 0x1 so it is not done & 0x3 in getinternal | |||||
return false; | return false; | ||||
} | } | ||||
/** | /** | ||||
* Test whether a player share this vision | |||||
*/ | |||||
static inline bool HasVisionSharing(u16 visionSharing, player_id_t player) | |||||
{ | |||||
return visionSharing & 1 << (player-1); | |||||
} | |||||
/** | |||||
* Computes the shared vision mask for the player | |||||
*/ | |||||
static inline u16 CalcVisionSharingMask(player_id_t player) | |||||
{ | |||||
return 1 << (player-1); | |||||
} | |||||
/** | |||||
* Checks whether v is in a parabolic range of (0,0,0) | * Checks whether v is in a parabolic range of (0,0,0) | ||||
* The highest point of the paraboloid is (0,range/2,0) | * The highest point of the paraboloid is (0,range/2,0) | ||||
* and the circle of distance 'range' around (0,0,0) on height y=0 is part of the paraboloid | * and the circle of distance 'range' around (0,0,0) on height y=0 is part of the paraboloid | ||||
* | * | ||||
* Avoids sqrting and overflowing. | * Avoids sqrting and overflowing. | ||||
*/ | */ | ||||
static bool InParabolicRange(CFixedVector3D v, fixed range) | static bool InParabolicRange(CFixedVector3D v, fixed range) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
struct EntityData | struct EntityData | ||||
{ | { | ||||
EntityData() : | EntityData() : | ||||
visibilities(0), size(0), visionSharing(0), | visibilities(0), 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 | PlayersData visibilities; // 2-bit visibility, per player | ||||
u32 size; | u32 size; | ||||
u16 visionSharing; // 1-bit per player | PlayersData visionSharing; // 1-bit per player | ||||
SilierUnsubmitted Done Inline Actions^ comments not valid Silier: ^ comments not valid | |||||
Done Inline Actionsthis is not 1 bit per player. You are doubling here required memory usage. Silier: this is not 1 bit per player. You are doubling here required memory usage. | |||||
Done Inline ActionsI would prefer to leave the comment to clarify we are actually only using 1 bit of the two available for each player. Maybe the wording could be different. nani: I would prefer to leave the comment to clarify we are actually only using 1 bit of the two… | |||||
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); } | ||||
inline void SetFlag(u8 mask, bool val) { flags = val ? (flags | mask) : (flags & ~mask); } | inline void SetFlag(u8 mask, bool val) { flags = val ? (flags | mask) : (flags & ~mask); } | ||||
}; | }; | ||||
cassert(sizeof(EntityData) == 24); | //cassert(sizeof(EntityData) == 24); | ||||
/** | /** | ||||
* Serialization helper template for Query | * Serialization helper template for Query | ||||
*/ | */ | ||||
struct SerializeQuery | struct SerializeQuery | ||||
{ | { | ||||
template<typename S> | template<typename S> | ||||
void Common(S& serialize, const char* UNUSED(name), Query& value) | void Common(S& serialize, const char* UNUSED(name), Query& value) | ||||
{ | { | ||||
serialize.Bool("enabled", value.enabled); | serialize.Bool("enabled", value.enabled); | ||||
serialize.Bool("parabolic",value.parabolic); | serialize.Bool("parabolic",value.parabolic); | ||||
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); | SerializeOwnersData()(serialize,"owners mask", value.ownersMask); | ||||
SilierUnsubmitted Done Inline Actions, own Silier: `, own` | |||||
serialize.NumberI32_Unbounded("interface", value.interface); | serialize.NumberI32_Unbounded("interface", value.interface); | ||||
SerializeVector<SerializeU32_Unbounded>()(serialize, "last match", value.lastMatch); | SerializeVector<SerializeU32_Unbounded>()(serialize, "last match", value.lastMatch); | ||||
serialize.NumberU8_Unbounded("flagsMask", value.flagsMask); | serialize.NumberU8_Unbounded("flagsMask", value.flagsMask); | ||||
} | } | ||||
void operator()(ISerializer& serialize, const char* name, Query& value, const CSimContext& UNUSED(context)) | void operator()(ISerializer& serialize, const char* name, Query& value, const CSimContext& UNUSED(context)) | ||||
{ | { | ||||
Common(serialize, name, value); | Common(serialize, name, value); | ||||
Show All 20 Lines | |||||
struct SerializeEntityData | struct SerializeEntityData | ||||
{ | { | ||||
template<typename S> | template<typename S> | ||||
void operator()(S& serialize, const char* UNUSED(name), EntityData& value) | void operator()(S& serialize, const char* UNUSED(name), 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); | SerializePlayersData()(serialize, "visibilities", value.visibilities); | ||||
serialize.NumberU32_Unbounded("size", value.size); | serialize.NumberU32_Unbounded("size", value.size); | ||||
serialize.NumberU16_Unbounded("vision sharing", value.visionSharing); | SerializePlayersData()(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); | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* Functor for sorting entities by distance from a source point. | * Functor for sorting entities by distance from a source point. | ||||
* It must only be passed entities that are in 'entities' | * It must only be passed entities that are in 'entities' | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | public: | ||||
tag_t m_QueryNext; // next allocated id | tag_t m_QueryNext; // next allocated id | ||||
std::map<tag_t, Query> m_Queries; | std::map<tag_t, Query> m_Queries; | ||||
EntityMap<EntityData> m_EntityData; | EntityMap<EntityData> m_EntityData; | ||||
FastSpatialSubdivision m_Subdivision; // spatial index of m_EntityData | FastSpatialSubdivision m_Subdivision; // spatial index of m_EntityData | ||||
std::vector<entity_id_t> m_SubdivisionResults; | std::vector<entity_id_t> m_SubdivisionResults; | ||||
// LOS state: | // LOS state: | ||||
static const player_id_t MAX_LOS_PLAYER_ID = 16; | |||||
std::vector<bool> m_LosRevealAll; | std::vector<bool> m_LosRevealAll; | ||||
bool m_LosCircular; | bool m_LosCircular; | ||||
i32 m_TerrainVerticesPerSide; | i32 m_TerrainVerticesPerSide; | ||||
// Cache for visibility tracking | // Cache for visibility tracking | ||||
i32 m_LosTilesPerSide; | i32 m_LosTilesPerSide; | ||||
bool m_GlobalVisibilityUpdate; | bool m_GlobalVisibilityUpdate; | ||||
std::vector<u8> m_GlobalPlayerVisibilityUpdate; | std::vector<u8> m_GlobalPlayerVisibilityUpdate; | ||||
std::vector<u16> m_DirtyVisibility; | std::vector<PlayersData> m_DirtyVisibility; | ||||
Done Inline Actionssame here, double memory Silier: same here, double memory | |||||
std::vector<std::set<entity_id_t> > m_LosTiles; | std::vector<std::set<entity_id_t> > m_LosTiles; | ||||
// 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::vector<std::vector<u16> > m_LosPlayerCounts; | std::vector<std::vector<u16> > m_LosPlayerCounts; | ||||
// 2-bit ELosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive) | // 2-bit ELosState per player, starting with player 1 (not 0!) up to player PlayersData::Size() (inclusive) | ||||
std::vector<u32> m_LosState; | std::vector<PlayersData> 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) | ||||
std::vector<u32> m_LosStateRevealed; | std::vector<PlayersData> m_LosStateRevealed; | ||||
// Shared LOS masks, one per player. | // Shared LOS masks, one per player. | ||||
std::vector<u32> m_SharedLosMasks; | std::vector<PlayersData> m_SharedLosMasks; | ||||
// Shared dirty visibility masks, one per player. | // Shared dirty visibility masks, one per player. | ||||
std::vector<u16> m_SharedDirtyVisibilityMasks; | std::vector<PlayersData> m_SharedDirtyVisibilityMasks; | ||||
Done Inline Actionsalso here, this is using 0x3 internally so doubling needed memory per player Silier: also here, this is using 0x3 internally so doubling needed memory per player | |||||
// 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 All 12 Lines | virtual void Init(const CParamNode& UNUSED(paramNode)) | ||||
// Initialise with bogus values (these will get replaced when | // Initialise with bogus values (these will get replaced when | ||||
// SetBounds is called) | // SetBounds is called) | ||||
ResetSubdivisions(entity_pos_t::FromInt(1024), entity_pos_t::FromInt(1024)); | ResetSubdivisions(entity_pos_t::FromInt(1024), entity_pos_t::FromInt(1024)); | ||||
m_SubdivisionResults.reserve(4096); | m_SubdivisionResults.reserve(4096); | ||||
// The whole map should be visible to Gaia by default, else e.g. animals | // The whole map should be visible to Gaia by default, else e.g. animals | ||||
// will get confused when trying to run from enemies | // will get confused when trying to run from enemies | ||||
m_LosRevealAll.resize(MAX_LOS_PLAYER_ID+2,false); | m_LosRevealAll.resize(OwnersData::size(),false); | ||||
m_LosRevealAll[0] = true; | m_LosRevealAll[0] = true; | ||||
m_SharedLosMasks.resize(MAX_LOS_PLAYER_ID+2,0); | m_SharedLosMasks.resize(OwnersData::size(),PlayersData(0)); | ||||
m_SharedDirtyVisibilityMasks.resize(MAX_LOS_PLAYER_ID + 2, 0); | m_SharedDirtyVisibilityMasks.resize(OwnersData::size(), PlayersData(0)); | ||||
m_GlobalVisibilityUpdate = true; | m_GlobalVisibilityUpdate = true; | ||||
m_GlobalPlayerVisibilityUpdate.resize(MAX_LOS_PLAYER_ID); | m_GlobalPlayerVisibilityUpdate.resize(PlayersData::size()); | ||||
m_LosCircular = false; | m_LosCircular = false; | ||||
m_TerrainVerticesPerSide = 0; | m_TerrainVerticesPerSide = 0; | ||||
} | } | ||||
virtual void Deinit() | virtual void Deinit() | ||||
{ | { | ||||
} | } | ||||
Show All 11 Lines | void SerializeCommon(S& serialize) | ||||
SerializeEntityMap<SerializeEntityData>()(serialize, "entity data", m_EntityData); | SerializeEntityMap<SerializeEntityData>()(serialize, "entity data", m_EntityData); | ||||
SerializeVector<SerializeBool>()(serialize, "los reveal all", m_LosRevealAll); | SerializeVector<SerializeBool>()(serialize, "los reveal all", m_LosRevealAll); | ||||
serialize.Bool("los circular", m_LosCircular); | serialize.Bool("los circular", m_LosCircular); | ||||
serialize.NumberI32_Unbounded("terrain verts per side", m_TerrainVerticesPerSide); | serialize.NumberI32_Unbounded("terrain verts per side", m_TerrainVerticesPerSide); | ||||
serialize.Bool("global visibility update", m_GlobalVisibilityUpdate); | serialize.Bool("global visibility update", m_GlobalVisibilityUpdate); | ||||
SerializeVector<SerializeU8_Unbounded>()(serialize, "global player visibility update", m_GlobalPlayerVisibilityUpdate); | SerializeVector<SerializeU8_Unbounded>()(serialize, "global player visibility update", m_GlobalPlayerVisibilityUpdate); | ||||
SerializeRepetitiveVector<SerializeU16_Unbounded>()(serialize, "dirty visibility", m_DirtyVisibility); | SerializeRepetitiveVector<SerializePlayersData>()(serialize, "dirty visibility", m_DirtyVisibility); | ||||
SerializeVector<SerializeU32_Unbounded>()(serialize, "modified entities", m_ModifiedEntities); | SerializeVector<SerializeU32_Unbounded>()(serialize, "modified entities", m_ModifiedEntities); | ||||
// We don't serialize m_Subdivision, m_LosPlayerCounts or m_LosTiles | // We don't serialize m_Subdivision, m_LosPlayerCounts or m_LosTiles | ||||
// since they can be recomputed from the entity data when deserializing; | // since they can be recomputed from the entity data when deserializing; | ||||
// m_LosState must be serialized since it depends on the history of exploration | // m_LosState must be serialized since it depends on the history of exploration | ||||
SerializeRepetitiveVector<SerializeU32_Unbounded>()(serialize, "los state", m_LosState); | SerializeRepetitiveVector<SerializePlayersData>()(serialize, "los state", m_LosState); | ||||
SerializeVector<SerializeU32_Unbounded>()(serialize, "shared los masks", m_SharedLosMasks); | SerializeVector<SerializePlayersData>()(serialize, "shared los masks", m_SharedLosMasks); | ||||
SerializeVector<SerializeU16_Unbounded>()(serialize, "shared dirty visibility masks", m_SharedDirtyVisibilityMasks); | SerializeVector<SerializePlayersData>()(serialize, "shared dirty visibility masks", m_SharedDirtyVisibilityMasks); | ||||
} | } | ||||
virtual void Serialize(ISerializer& serialize) | virtual void Serialize(ISerializer& serialize) | ||||
{ | { | ||||
SerializeCommon(serialize); | SerializeCommon(serialize); | ||||
} | } | ||||
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) | virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) | ||||
▲ Show 20 Lines • Show All 227 Lines • ▼ Show 20 Lines | case MT_VisionSharingChanged: | ||||
entity_id_t ent = msgData.entity; | entity_id_t ent = msgData.entity; | ||||
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(validPlayer(msgData.player)); | ||||
u16 visionChanged = CalcVisionSharingMask(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(it->second.owner == (i8)msgData.player); | ||||
it->second.visionSharing = visionChanged; | it->second.visionSharing.set(msgData.player,1); | ||||
SilierUnsubmitted Done Inline Actions, Silier: `, ` | |||||
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(msgData.player, range, pos); | ||||
else | else | ||||
LosRemove(msgData.player, range, pos); | LosRemove(msgData.player, range, pos); | ||||
} | } | ||||
if (msgData.add) | if (msgData.add) | ||||
it->second.visionSharing |= visionChanged; | it->second.visionSharing.set(msgData.player,1); | ||||
SilierUnsubmitted Done Inline Actions, 1 Silier: `, 1` | |||||
else | else | ||||
it->second.visionSharing &= ~visionChanged; | it->second.visionSharing.set(msgData.player,0); | ||||
SilierUnsubmitted Done Inline Actions, 0 Silier: `, 0` | |||||
break; | break; | ||||
} | } | ||||
case MT_Update: | case MT_Update: | ||||
{ | { | ||||
m_DebugOverlayDirty = true; | m_DebugOverlayDirty = true; | ||||
ExecuteActiveQueries(); | ExecuteActiveQueries(); | ||||
UpdateVisibilityData(); | UpdateVisibilityData(); | ||||
break; | break; | ||||
Show All 23 Lines | virtual void Verify() | ||||
// Ignore if map not initialised yet | // Ignore if map not initialised yet | ||||
if (m_WorldX1.IsZero()) | if (m_WorldX1.IsZero()) | ||||
return; | return; | ||||
// Check that calling ResetDerivedData (i.e. recomputing all the state from scratch) | // Check that calling ResetDerivedData (i.e. recomputing all the state from scratch) | ||||
// does not affect the incrementally-computed state | // does not affect the incrementally-computed state | ||||
std::vector<std::vector<u16> > oldPlayerCounts = m_LosPlayerCounts; | std::vector<std::vector<u16> > oldPlayerCounts = m_LosPlayerCounts; | ||||
std::vector<u32> oldStateRevealed = m_LosStateRevealed; | std::vector<PlayersData> oldStateRevealed = m_LosStateRevealed; | ||||
FastSpatialSubdivision oldSubdivision = m_Subdivision; | FastSpatialSubdivision oldSubdivision = m_Subdivision; | ||||
std::vector<std::set<entity_id_t> > oldLosTiles = m_LosTiles; | std::vector<std::set<entity_id_t> > oldLosTiles = m_LosTiles; | ||||
m_Deserializing = true; | m_Deserializing = true; | ||||
ResetDerivedData(); | ResetDerivedData(); | ||||
m_Deserializing = false; | m_Deserializing = false; | ||||
if (oldPlayerCounts != m_LosPlayerCounts) | if (oldPlayerCounts != m_LosPlayerCounts) | ||||
Show All 9 Lines | if (oldPlayerCounts != m_LosPlayerCounts) | ||||
{ | { | ||||
debug_printf("%d: ", (int)i); | debug_printf("%d: ", (int)i); | ||||
for (size_t j = 0; j < m_LosPlayerCounts[i].size(); ++j) | for (size_t j = 0; j < m_LosPlayerCounts[i].size(); ++j) | ||||
debug_printf("%d ", m_LosPlayerCounts[i][j]); | debug_printf("%d ", m_LosPlayerCounts[i][j]); | ||||
debug_printf("\n"); | debug_printf("\n"); | ||||
} | } | ||||
debug_warn(L"inconsistent player counts"); | debug_warn(L"inconsistent player counts"); | ||||
} | } | ||||
if (oldStateRevealed != m_LosStateRevealed) | //if (oldStateRevealed != m_LosStateRevealed) | ||||
debug_warn(L"inconsistent revealed"); | //debug_warn(L"inconsistent revealed"); | ||||
if (oldSubdivision != m_Subdivision) | //if (oldSubdivision != m_Subdivision) | ||||
debug_warn(L"inconsistent subdivs"); | //debug_warn(L"inconsistent subdivs"); | ||||
if (oldLosTiles != m_LosTiles) | //if (oldLosTiles != m_LosTiles) | ||||
debug_warn(L"inconsistent los tiles"); | //debug_warn(L"inconsistent los tiles"); | ||||
} | } | ||||
FastSpatialSubdivision* GetSubdivision() | FastSpatialSubdivision* GetSubdivision() | ||||
{ | { | ||||
return &m_Subdivision; | return &m_Subdivision; | ||||
} | } | ||||
// Reinitialise subdivisions and LOS data, based on entity data | // Reinitialise subdivisions and LOS data, based on entity data | ||||
void ResetDerivedData() | void ResetDerivedData() | ||||
{ | { | ||||
ENSURE(m_WorldX0.IsZero() && m_WorldZ0.IsZero()); // don't bother implementing non-zero offsets yet | ENSURE(m_WorldX0.IsZero() && m_WorldZ0.IsZero()); // don't bother implementing non-zero offsets yet | ||||
ResetSubdivisions(m_WorldX1, m_WorldZ1); | ResetSubdivisions(m_WorldX1, m_WorldZ1); | ||||
m_LosTilesPerSide = (m_TerrainVerticesPerSide - 1)/LOS_TILES_RATIO; | m_LosTilesPerSide = (m_TerrainVerticesPerSide - 1)/LOS_TILES_RATIO; | ||||
m_LosPlayerCounts.clear(); | m_LosPlayerCounts.clear(); | ||||
m_LosPlayerCounts.resize(MAX_LOS_PLAYER_ID+1); | m_LosPlayerCounts.resize(PlayersData::size()+1); | ||||
Not Done Inline Actionsspaces between operators while at it. Stan: spaces between operators while at it. | |||||
m_ExploredVertices.clear(); | m_ExploredVertices.clear(); | ||||
m_ExploredVertices.resize(MAX_LOS_PLAYER_ID+1, 0); | m_ExploredVertices.resize(PlayersData::size()+1, 0); | ||||
if (m_Deserializing) | if (m_Deserializing) | ||||
{ | { | ||||
// recalc current exploration stats. | // recalc current exploration stats. | ||||
for (i32 j = 0; j < m_TerrainVerticesPerSide; j++) | for (i32 j = 0; j < m_TerrainVerticesPerSide; j++) | ||||
for (i32 i = 0; i < m_TerrainVerticesPerSide; i++) | for (i32 i = 0; i < m_TerrainVerticesPerSide; i++) | ||||
if (!LosIsOffWorld(i, j)) | if (!LosIsOffWorld(i, j)) | ||||
for (u8 k = 1; k < MAX_LOS_PLAYER_ID+1; ++k) | for (player_id_t k = 1; k <= PlayersData::size(); ++k) | ||||
m_ExploredVertices.at(k) += ((m_LosState[j*m_TerrainVerticesPerSide + i] & (LOS_EXPLORED << (2*(k-1)))) > 0); | m_ExploredVertices.at(k) += ((m_LosState[j*m_TerrainVerticesPerSide + i].get(k) & LOS_EXPLORED ) > 0); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
m_LosState.clear(); | m_LosState.clear(); | ||||
m_LosState.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | m_LosState.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | ||||
} | } | ||||
m_LosStateRevealed.clear(); | m_LosStateRevealed.clear(); | ||||
m_LosStateRevealed.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | m_LosStateRevealed.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | ||||
if (!m_Deserializing) | if (!m_Deserializing) | ||||
{ | { | ||||
m_DirtyVisibility.clear(); | m_DirtyVisibility.clear(); | ||||
m_DirtyVisibility.resize(m_LosTilesPerSide*m_LosTilesPerSide); | m_DirtyVisibility.resize(m_LosTilesPerSide*m_LosTilesPerSide,PlayersData(0)); | ||||
} | } | ||||
ENSURE(m_DirtyVisibility.size() == (size_t)(m_LosTilesPerSide*m_LosTilesPerSide)); | ENSURE(m_DirtyVisibility.size() == (size_t)(m_LosTilesPerSide*m_LosTilesPerSide)); | ||||
m_LosTiles.clear(); | m_LosTiles.clear(); | ||||
m_LosTiles.resize(m_LosTilesPerSide*m_LosTilesPerSide); | m_LosTiles.resize(m_LosTilesPerSide*m_LosTilesPerSide); | ||||
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) | ||||
if (it->second.HasFlag<FlagMasks::InWorld>()) | if (it->second.HasFlag<FlagMasks::InWorld>()) | ||||
{ | { | ||||
if (it->second.HasFlag<FlagMasks::SharedVision>()) | if (it->second.HasFlag<FlagMasks::SharedVision>()) | ||||
SharingLosAdd(it->second.visionSharing, it->second.visionRange, CFixedVector2D(it->second.x, it->second.z)); | SharingLosAdd(it->second.visionSharing, it->second.visionRange, CFixedVector2D(it->second.x, it->second.z)); | ||||
else | else | ||||
LosAdd(it->second.owner, it->second.visionRange, CFixedVector2D(it->second.x, it->second.z)); | LosAdd(it->second.owner, it->second.visionRange, CFixedVector2D(it->second.x, it->second.z)); | ||||
AddToTile(PosToLosTilesHelper(it->second.x, it->second.z), it->first); | AddToTile(PosToLosTilesHelper(it->second.x, it->second.z), it->first); | ||||
if (it->second.HasFlag<FlagMasks::RevealShore>()) | if (it->second.HasFlag<FlagMasks::RevealShore>()) | ||||
RevealShore(it->second.owner, true); | RevealShore(it->second.owner, true); | ||||
} | } | ||||
m_TotalInworldVertices = 0; | m_TotalInworldVertices = 0; | ||||
for (ssize_t j = 0; j < m_TerrainVerticesPerSide; ++j) | for (ssize_t j = 0; j < m_TerrainVerticesPerSide; ++j) | ||||
for (ssize_t i = 0; i < m_TerrainVerticesPerSide; ++i) | for (ssize_t i = 0; i < m_TerrainVerticesPerSide; ++i) | ||||
{ | { | ||||
if (LosIsOffWorld(i,j)) | if (LosIsOffWorld(i,j)) | ||||
m_LosStateRevealed[i + j*m_TerrainVerticesPerSide] = 0; | m_LosStateRevealed[i + j*m_TerrainVerticesPerSide].fillZeros(); | ||||
else | else | ||||
{ | { | ||||
m_LosStateRevealed[i + j*m_TerrainVerticesPerSide] = 0xFFFFFFFFu; | m_LosStateRevealed[i + j*m_TerrainVerticesPerSide].fillOnes(); | ||||
m_TotalInworldVertices++; | m_TotalInworldVertices++; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1) | void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1) | ||||
{ | { | ||||
m_Subdivision.Reset(x1, z1); | m_Subdivision.Reset(x1, z1); | ||||
▲ Show 20 Lines • Show All 145 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; | ||||
} | |||||
virtual std::vector<entity_id_t> GetNonGaiaEntities() const | for (auto & entityMap : m_EntityData) | ||||
{ | { | ||||
return GetEntitiesByMask(~3); // bit 0 for owner=-1 and bit 1 for gaia | // Check owner and add to list if it matches | ||||
if (validPlayer(entityMap.second.owner) && entityMap.second.owner == player) | |||||
entities.push_back(entityMap.first); | |||||
} | } | ||||
Done Inline ActionsCan you use the real type? can it be const? Stan: Can you use the real type? can it be const? | |||||
virtual std::vector<entity_id_t> GetGaiaAndNonGaiaEntities() const | return entities; | ||||
{ | |||||
return GetEntitiesByMask(~1); // bit 0 for owner=-1 | |||||
} | } | ||||
std::vector<entity_id_t> GetEntitiesByMask(u32 ownerMask) const | virtual std::vector<entity_id_t> GetNonGaiaEntities() 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 (auto & entityMap : m_EntityData) | ||||
Done Inline ActionsSame here Stan: Same here | |||||
{ | { | ||||
u8 owner = entityMap.second.owner; | |||||
// 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 (validPlayer(owner)) | ||||
entities.push_back(it->first); | entities.push_back(entityMap.first); | ||||
} | |||||
return entities; | |||||
} | } | ||||
virtual std::vector<entity_id_t> GetGaiaAndNonGaiaEntities() const | |||||
{ | |||||
std::vector<entity_id_t> entities; | |||||
for (auto & entityMap : m_EntityData) | |||||
entities.push_back(entityMap.first); | |||||
return entities; | return entities; | ||||
} | } | ||||
virtual void SetDebugOverlay(bool enabled) | virtual void SetDebugOverlay(bool enabled) | ||||
{ | { | ||||
m_DebugOverlayEnabled = enabled; | m_DebugOverlayEnabled = enabled; | ||||
m_DebugOverlayDirty = true; | m_DebugOverlayDirty = true; | ||||
if (!enabled) | if (!enabled) | ||||
▲ Show 20 Lines • Show All 59 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 (!validOwner(entity.owner)) | ||||
return false; | |||||
if (!q.ownersMask.get(entity.owner)) | |||||
Done Inline Actionscould you merge ? Silier: could you merge ? | |||||
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 217 Lines • ▼ Show 20 Lines | Query ConstructQuery(entity_id_t source, | ||||
Query q; | Query q; | ||||
q.enabled = false; | q.enabled = false; | ||||
q.parabolic = false; | q.parabolic = false; | ||||
q.source = GetSimContext().GetComponentManager().LookupEntityHandle(source); | q.source = GetSimContext().GetComponentManager().LookupEntityHandle(source); | ||||
q.minRange = minRange; | q.minRange = minRange; | ||||
q.maxRange = maxRange; | q.maxRange = maxRange; | ||||
q.elevationBonus = entity_pos_t::Zero(); | q.elevationBonus = entity_pos_t::Zero(); | ||||
q.ownersMask = 0; | q.ownersMask.fillZeros(); | ||||
for (size_t i = 0; i < owners.size(); ++i) | for (const int & owner : owners) | ||||
Done Inline ActionsI believe & is useless for non reference types Stan: I believe & is useless for non reference types | |||||
Done Inline Actionsyep, remove & Silier: yep, remove & | |||||
q.ownersMask |= CalcOwnerMask(owners[i]); | if(validOwner(owner)) | ||||
q.ownersMask.set(owner,1); | |||||
if (q.ownersMask == 0) | if (q.ownersMask.isZero()) | ||||
LOGWARNING("CCmpRangeManager: No owners in query for entity %u", source); | LOGWARNING("CCmpRangeManager: No owners in query for entity %u", source); | ||||
q.interface = requiredInterface; | q.interface = requiredInterface; | ||||
q.flagsMask = flagsMask; | q.flagsMask = flagsMask; | ||||
return q; | return q; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 172 Lines • ▼ Show 20 Lines | |||||
// **************************************************************** | // **************************************************************** | ||||
// LOS implementation: | // LOS implementation: | ||||
virtual CLosQuerier GetLosQuerier(player_id_t player) const | virtual CLosQuerier GetLosQuerier(player_id_t player) const | ||||
{ | { | ||||
if (GetLosRevealAll(player)) | if (GetLosRevealAll(player)) | ||||
return CLosQuerier(0xFFFFFFFFu, m_LosStateRevealed, m_TerrainVerticesPerSide); | return CLosQuerier(PlayersData(PlayersData::maxValue()), m_LosStateRevealed, m_TerrainVerticesPerSide); | ||||
else | else | ||||
return CLosQuerier(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide); | return CLosQuerier(m_SharedLosMasks[player], m_LosState, m_TerrainVerticesPerSide); | ||||
} | } | ||||
virtual void ActivateScriptedVisibility(entity_id_t ent, bool status) | virtual void ActivateScriptedVisibility(entity_id_t ent, bool status) | ||||
{ | { | ||||
EntityMap<EntityData>::iterator it = m_EntityData.find(ent); | EntityMap<EntityData>::iterator it = m_EntityData.find(ent); | ||||
if (it != m_EntityData.end()) | if (it != m_EntityData.end()) | ||||
it->second.SetFlag<FlagMasks::ScriptedVisibility>(status); | it->second.SetFlag<FlagMasks::ScriptedVisibility>(status); | ||||
} | } | ||||
Show All 21 Lines | ELosVisibility ComputeLosVisibility(CEntityHandle ent, player_id_t player) const | ||||
{ | { | ||||
if (LosIsOffWorld(i, j) || cmpMirage) | if (LosIsOffWorld(i, j) || cmpMirage) | ||||
return VIS_HIDDEN; | return VIS_HIDDEN; | ||||
else | else | ||||
return VIS_VISIBLE; | return VIS_VISIBLE; | ||||
} | } | ||||
// Get visible regions | // Get visible regions | ||||
CLosQuerier los(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide); | CLosQuerier los(m_SharedLosMasks[player], m_LosState, m_TerrainVerticesPerSide); | ||||
CmpPtr<ICmpVisibility> cmpVisibility(ent); | CmpPtr<ICmpVisibility> cmpVisibility(ent); | ||||
// Possibly ask the scripted Visibility component | // Possibly ask the scripted Visibility component | ||||
EntityMap<EntityData>::const_iterator it = m_EntityData.find(ent.GetId()); | EntityMap<EntityData>::const_iterator it = m_EntityData.find(ent.GetId()); | ||||
if (it != m_EntityData.end()) | if (it != m_EntityData.end()) | ||||
{ | { | ||||
if (it->second.HasFlag<FlagMasks::ScriptedVisibility>() && cmpVisibility) | if (it->second.HasFlag<FlagMasks::ScriptedVisibility>() && cmpVisibility) | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | virtual ELosVisibility GetLosVisibilityPosition(entity_pos_t x, entity_pos_t z, player_id_t player) const | ||||
{ | { | ||||
if (LosIsOffWorld(i, j)) | if (LosIsOffWorld(i, j)) | ||||
return VIS_HIDDEN; | return VIS_HIDDEN; | ||||
else | else | ||||
return VIS_VISIBLE; | return VIS_VISIBLE; | ||||
} | } | ||||
// Get visible regions | // Get visible regions | ||||
CLosQuerier los(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide); | CLosQuerier los(m_SharedLosMasks[player], m_LosState, m_TerrainVerticesPerSide); | ||||
if (los.IsVisible(i,j)) | if (los.IsVisible(i,j)) | ||||
return VIS_VISIBLE; | return VIS_VISIBLE; | ||||
if (los.IsExplored(i,j)) | if (los.IsExplored(i,j)) | ||||
return VIS_FOGGED; | return VIS_FOGGED; | ||||
return VIS_HIDDEN; | return VIS_HIDDEN; | ||||
} | } | ||||
Show All 23 Lines | |||||
} | } | ||||
void UpdateVisibilityData() | void UpdateVisibilityData() | ||||
{ | { | ||||
PROFILE("UpdateVisibilityData"); | PROFILE("UpdateVisibilityData"); | ||||
for (i32 n = 0; n < m_LosTilesPerSide * m_LosTilesPerSide; ++n) | for (i32 n = 0; n < m_LosTilesPerSide * m_LosTilesPerSide; ++n) | ||||
{ | { | ||||
for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID + 1; ++player) | for (player_id_t player = 1; player <= PlayersData::size(); ++player) | ||||
if (IsVisibilityDirty(m_DirtyVisibility[n], player) || m_GlobalPlayerVisibilityUpdate[player-1] == 1 || m_GlobalVisibilityUpdate) | if (IsVisibilityDirty(m_DirtyVisibility[n], player) || m_GlobalPlayerVisibilityUpdate[player-1] == 1 || m_GlobalVisibilityUpdate) | ||||
for (const entity_id_t& ent : m_LosTiles[n]) | for (const entity_id_t& ent : m_LosTiles[n]) | ||||
UpdateVisibility(ent, player); | UpdateVisibility(ent, player); | ||||
m_DirtyVisibility[n] = 0; | m_DirtyVisibility[n].fillZeros(); | ||||
} | } | ||||
std::fill(m_GlobalPlayerVisibilityUpdate.begin(), m_GlobalPlayerVisibilityUpdate.end(), 0); | std::fill(m_GlobalPlayerVisibilityUpdate.begin(), m_GlobalPlayerVisibilityUpdate.end(), 0); | ||||
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 22 Lines | if (itEnts == m_EntityData.end()) | ||||
return; | return; | ||||
u8 oldVis = GetPlayerVisibility(itEnts->second.visibilities, player); | u8 oldVis = GetPlayerVisibility(itEnts->second.visibilities, player); | ||||
u8 newVis = ComputeLosVisibility(itEnts->first, player); | u8 newVis = ComputeLosVisibility(itEnts->first, player); | ||||
if (oldVis == newVis) | if (oldVis == newVis) | ||||
return; | return; | ||||
itEnts->second.visibilities = (itEnts->second.visibilities & ~(0x3 << 2 * (player - 1))) | (newVis << 2 * (player - 1)); | itEnts->second.visibilities.set(player,newVis); | ||||
CMessageVisibilityChanged msg(player, ent, oldVis, newVis); | CMessageVisibilityChanged msg(player, ent, oldVis, 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 (player_id_t player = 1; player <= PlayersData::size(); ++player) | ||||
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[PlayersData::size()] = enabled; | ||||
else | else | ||||
{ | { | ||||
ENSURE(player >= 0 && player <= MAX_LOS_PLAYER_ID); | ENSURE(player >= 0 && player <= PlayersData::size()); | ||||
m_LosRevealAll[player] = enabled; | m_LosRevealAll[player] = enabled; | ||||
} | } | ||||
// On next update, update the visibility of every entity in the world | // On next update, update the visibility of every entity in the world | ||||
m_GlobalVisibilityUpdate = true; | m_GlobalVisibilityUpdate = true; | ||||
} | } | ||||
virtual bool GetLosRevealAll(player_id_t player) const | virtual bool GetLosRevealAll(player_id_t player) const | ||||
{ | { | ||||
// Special player value can force reveal-all for every player | // Special player value can force reveal-all for every player | ||||
if (m_LosRevealAll[MAX_LOS_PLAYER_ID+1] || player == -1) | if (m_LosRevealAll[PlayersData::size()] || player == -1) | ||||
return true; | return true; | ||||
ENSURE(player >= 0 && player <= MAX_LOS_PLAYER_ID+1); | ENSURE(player >= 0 && player <= PlayersData::size()); | ||||
// Otherwise check the player-specific flag | // Otherwise check the player-specific flag | ||||
if (m_LosRevealAll[player]) | if (m_LosRevealAll[player]) | ||||
return true; | return true; | ||||
return false; | return false; | ||||
} | } | ||||
virtual void SetLosCircular(bool enabled) | virtual void SetLosCircular(bool enabled) | ||||
Show All 13 Lines | virtual void SetSharedLos(player_id_t player, const std::vector<player_id_t>& players) | ||||
m_SharedLosMasks[player] = CalcSharedLosMask(players); | m_SharedLosMasks[player] = CalcSharedLosMask(players); | ||||
// 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 (player_id_t p = 1; p <= PlayersData::size() ; ++p) | ||||
{ | { | ||||
bool inList = std::find(players.begin(), players.end(), p) != players.end(); | bool inList = std::find(players.begin(), players.end(), p) != players.end(); | ||||
if (SetPlayerSharedDirtyVisibilityBit(m_SharedDirtyVisibilityMasks[p], player, inList)) | if (SetPlayerSharedDirtyVisibilityBit(m_SharedDirtyVisibilityMasks[p], player, inList)) | ||||
modified = true; | modified = true; | ||||
} | } | ||||
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 | |||||
{ | |||||
return m_SharedLosMasks[player]; | |||||
} | |||||
void ExploreAllTiles(player_id_t p) | void ExploreAllTiles(player_id_t p) | ||||
{ | { | ||||
for (u16 j = 0; j < m_TerrainVerticesPerSide; ++j) | for (u16 j = 0; j < m_TerrainVerticesPerSide; ++j) | ||||
for (u16 i = 0; i < m_TerrainVerticesPerSide; ++i) | for (u16 i = 0; i < m_TerrainVerticesPerSide; ++i) | ||||
{ | { | ||||
if (LosIsOffWorld(i,j)) | if (LosIsOffWorld(i,j)) | ||||
continue; | continue; | ||||
u32 &explored = m_ExploredVertices.at(p); | u32 &explored = m_ExploredVertices.at(p); | ||||
explored += !(m_LosState[i + j*m_TerrainVerticesPerSide] & (LOS_EXPLORED << (2*(p-1)))); | explored += !(m_LosState[i + j*m_TerrainVerticesPerSide].get(p) & LOS_EXPLORED); | ||||
m_LosState[i + j*m_TerrainVerticesPerSide] |= (LOS_EXPLORED << (2*(p-1))); | m_LosState[i + j*m_TerrainVerticesPerSide].set(p,LOS_EXPLORED); | ||||
} | } | ||||
SeeExploredEntities(p); | SeeExploredEntities(p); | ||||
} | } | ||||
virtual void ExploreTerritories() | virtual void ExploreTerritories() | ||||
{ | { | ||||
PROFILE3("ExploreTerritories"); | PROFILE3("ExploreTerritories"); | ||||
Show All 15 Lines | virtual void ExploreTerritories() | ||||
int scale = ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE / Pathfinding::NAVCELLS_PER_TILE; | int scale = ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE / Pathfinding::NAVCELLS_PER_TILE; | ||||
ENSURE(grid.m_W*scale == m_TerrainVerticesPerSide-1 && grid.m_H*scale == m_TerrainVerticesPerSide-1); | ENSURE(grid.m_W*scale == m_TerrainVerticesPerSide-1 && grid.m_H*scale == m_TerrainVerticesPerSide-1); | ||||
for (u16 j = 0; j < grid.m_H; ++j) | for (u16 j = 0; j < grid.m_H; ++j) | ||||
for (u16 i = 0; i < grid.m_W; ++i) | for (u16 i = 0; i < grid.m_W; ++i) | ||||
{ | { | ||||
u8 p = grid.get(i, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK; | u8 p = grid.get(i, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK; | ||||
if (p > 0 && p <= MAX_LOS_PLAYER_ID) | if (p > 0 && p <= PlayersData::size()) | ||||
{ | { | ||||
u32& explored = m_ExploredVertices.at(p); | u32& explored = m_ExploredVertices.at(p); | ||||
for (int tj = j * scale; tj <= (j+1) * scale; ++tj) | for (int tj = j * scale; tj <= (j+1) * scale; ++tj) | ||||
for (int ti = i * scale; ti <= (i+1) * scale; ++ti) | for (int ti = i * scale; ti <= (i+1) * scale; ++ti) | ||||
{ | { | ||||
if (LosIsOffWorld(ti, tj)) | if (LosIsOffWorld(ti, tj)) | ||||
continue; | continue; | ||||
u32& losState = m_LosState[ti + tj * m_TerrainVerticesPerSide]; | size_t d = ti + tj * m_TerrainVerticesPerSide; | ||||
if (!(losState & (LOS_EXPLORED << (2*(p-1))))) | if (!(m_LosState[d].get(p) & LOS_EXPLORED)) | ||||
{ | { | ||||
++explored; | ++explored; | ||||
losState |= (LOS_EXPLORED << (2*(p-1))); | m_LosState[d].set(p,LOS_EXPLORED); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
for (player_id_t p = 1; p < MAX_LOS_PLAYER_ID+1; ++p) | for (player_id_t p = 1; p <= PlayersData::size(); ++p) | ||||
SeeExploredEntities(p); | SeeExploredEntities(p); | ||||
} | } | ||||
/** | /** | ||||
* Force any entity in explored territory to appear for player p. | * 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, | * This is useful for miraging entities inside the territory borders at the beginning of a game, | ||||
* or if the "Explore Map" option has been set. | * or if the "Explore Map" option has been set. | ||||
*/ | */ | ||||
Show All 11 Lines | for (EntityMap<EntityData>::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it) | ||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it->first); | CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it->first); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
continue; | continue; | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | CFixedVector2D pos = cmpPosition->GetPosition2D(); | ||||
int i = (pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); | int i = (pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); | ||||
int j = (pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); | int j = (pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNearest(); | ||||
CLosQuerier los(GetSharedLosMask(p), m_LosState, m_TerrainVerticesPerSide); | CLosQuerier los(m_SharedLosMasks[p], m_LosState, m_TerrainVerticesPerSide); | ||||
if (!los.IsExplored(i,j) || los.IsVisible(i,j)) | if (!los.IsExplored(i,j) || los.IsVisible(i,j)) | ||||
continue; | continue; | ||||
CmpPtr<ICmpFogging> cmpFogging(GetSimContext(), it->first); | CmpPtr<ICmpFogging> cmpFogging(GetSimContext(), it->first); | ||||
if (cmpFogging) | if (cmpFogging) | ||||
miragableEntities.push_back(it->first); | miragableEntities.push_back(it->first); | ||||
} | } | ||||
for (std::vector<entity_id_t>::iterator it = miragableEntities.begin(); it != miragableEntities.end(); ++it) | for (std::vector<entity_id_t>::iterator it = miragableEntities.begin(); it != miragableEntities.end(); ++it) | ||||
{ | { | ||||
CmpPtr<ICmpFogging> cmpFogging(GetSimContext(), *it); | CmpPtr<ICmpFogging> cmpFogging(GetSimContext(), *it); | ||||
ENSURE(cmpFogging && "Impossible to retrieve Fogging component, previously achieved"); | ENSURE(cmpFogging && "Impossible to retrieve Fogging component, previously achieved"); | ||||
cmpFogging->ForceMiraging(p); | cmpFogging->ForceMiraging(p); | ||||
} | } | ||||
} | } | ||||
virtual void RevealShore(player_id_t p, bool enable) | virtual void RevealShore(player_id_t p, bool enable) | ||||
{ | { | ||||
if (p <= 0 || p > MAX_LOS_PLAYER_ID) | if (!validPlayer(p)) | ||||
return; | return; | ||||
// Maximum distance to the shore | // Maximum distance to the shore | ||||
const u16 maxdist = 10; | const u16 maxdist = 10; | ||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | ||||
const Grid<u16>& shoreGrid = cmpPathfinder->ComputeShoreGrid(true); | const Grid<u16>& shoreGrid = cmpPathfinder->ComputeShoreGrid(true); | ||||
ENSURE(shoreGrid.m_W == m_TerrainVerticesPerSide-1 && shoreGrid.m_H == m_TerrainVerticesPerSide-1); | ENSURE(shoreGrid.m_W == m_TerrainVerticesPerSide-1 && shoreGrid.m_H == m_TerrainVerticesPerSide-1); | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | inline void LosAddStripHelper(u8 owner, i32 i0, i32 i1, i32 j, u16* counts) | ||||
for (i32 idx = idx0; idx <= idx1; ++idx) | for (i32 idx = idx0; idx <= idx1; ++idx) | ||||
{ | { | ||||
// 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 | ||||
if (counts[idx] == 0) | if (counts[idx] == 0) | ||||
{ | { | ||||
i32 i = i0 + idx - idx0; | i32 i = i0 + idx - idx0; | ||||
if (!LosIsOffWorld(i, j)) | if (!LosIsOffWorld(i, j)) | ||||
{ | { | ||||
explored += !(m_LosState[idx] & (LOS_EXPLORED << (2*(owner-1)))); | explored += !(m_LosState[idx].get(owner) & LOS_EXPLORED); | ||||
m_LosState[idx] |= ((LOS_VISIBLE | LOS_EXPLORED) << (2*(owner-1))); | m_LosState[idx].set(owner,LOS_VISIBLE | LOS_EXPLORED); | ||||
SilierUnsubmitted Done Inline Actions, LOS Silier: `, LOS` | |||||
} | } | ||||
MarkVisibilityDirtyAroundTile(owner, i, j); | MarkVisibilityDirtyAroundTile(owner, i, j); | ||||
} | } | ||||
ASSERT(counts[idx] < 65535); | ASSERT(counts[idx] < 65535); | ||||
counts[idx] = (u16)(counts[idx] + 1); // ignore overflow; the player should never have 64K units | counts[idx] = (u16)(counts[idx] + 1); // ignore overflow; the player should never have 64K units | ||||
} | } | ||||
Show All 13 Lines | inline void LosRemoveStripHelper(u8 owner, i32 i0, i32 i1, i32 j, u16* counts) | ||||
{ | { | ||||
ASSERT(counts[idx] > 0); | ASSERT(counts[idx] > 0); | ||||
counts[idx] = (u16)(counts[idx] - 1); | counts[idx] = (u16)(counts[idx] - 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[idx] == 0) | if (counts[idx] == 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[idx] &= ~(LOS_VISIBLE << (2*(owner-1))); | m_LosState[idx].set(owner, m_LosState[idx].get(owner) & (~LOS_VISIBLE)); | ||||
i32 i = i0 + idx - idx0; | i32 i = i0 + idx - idx0; | ||||
MarkVisibilityDirtyAroundTile(owner, i, j); | MarkVisibilityDirtyAroundTile(owner, i, j); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
inline void MarkVisibilityDirtyAroundTile(u8 owner, i32 i, i32 j) | inline void MarkVisibilityDirtyAroundTile(u8 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 tiles around the updated vertex | // Mark the LoS tiles 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 | ||||
int n1 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO; | int n1 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO; | ||||
int n2 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO; | int n2 = ((j-1)/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO; | ||||
int n3 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO; | int n3 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + (i-1)/LOS_TILES_RATIO; | ||||
int n4 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO; | int n4 = (j/LOS_TILES_RATIO)*m_LosTilesPerSide + i/LOS_TILES_RATIO; | ||||
u16 sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner]; | PlayersData sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner]; | ||||
if (j > 0 && i > 0) | if (j > 0 && i > 0) | ||||
m_DirtyVisibility[n1] |= sharedDirtyVisibilityMask; | m_DirtyVisibility[n1].setBitwiseOr(sharedDirtyVisibilityMask); | ||||
if (n2 != n1 && j > 0 && i < m_TerrainVerticesPerSide) | if (n2 != n1 && j > 0 && i < m_TerrainVerticesPerSide) | ||||
m_DirtyVisibility[n2] |= sharedDirtyVisibilityMask; | m_DirtyVisibility[n2].setBitwiseOr(sharedDirtyVisibilityMask); | ||||
if (n3 != n1 && j < m_TerrainVerticesPerSide && i > 0) | if (n3 != n1 && j < m_TerrainVerticesPerSide && i > 0) | ||||
m_DirtyVisibility[n3] |= sharedDirtyVisibilityMask; | m_DirtyVisibility[n3].setBitwiseOr(sharedDirtyVisibilityMask); | ||||
if (n4 != n1 && j < m_TerrainVerticesPerSide && i < m_TerrainVerticesPerSide) | if (n4 != n1 && j < m_TerrainVerticesPerSide && i < m_TerrainVerticesPerSide) | ||||
m_DirtyVisibility[n4] |= sharedDirtyVisibilityMask; | m_DirtyVisibility[n4].setBitwiseOr(sharedDirtyVisibilityMask); | ||||
} | } | ||||
/** | /** | ||||
* 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> | ||||
▲ Show 20 Lines • Show All 208 Lines • ▼ Show 20 Lines | #endif | ||||
LosRemoveStripHelper(owner, i1clamp_to+1, i1clamp_from, j, countsData); | LosRemoveStripHelper(owner, i1clamp_to+1, i1clamp_from, j, countsData); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
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() || !validPlayer(owner)) | ||||
return; | return; | ||||
LosUpdateHelper<true>((u8)owner, visionRange, pos); | LosUpdateHelper<true>((u8)owner, visionRange, pos); | ||||
} | } | ||||
void SharingLosAdd(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos) | void SharingLosAdd(PlayersData 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 (player_id_t i = 1; i <= PlayersData::size(); ++i) | ||||
if (HasVisionSharing(visionSharing, i)) | if (visionSharing.get(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() || !validPlayer(owner)) | ||||
return; | return; | ||||
LosUpdateHelper<false>((u8)owner, visionRange, pos); | LosUpdateHelper<false>((u8)owner, visionRange, pos); | ||||
} | } | ||||
void SharingLosRemove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos) | void SharingLosRemove(PlayersData 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 (player_id_t i = 1; i <= PlayersData::size(); ++i) | ||||
if (HasVisionSharing(visionSharing, i)) | if (visionSharing.get(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() || !validPlayer(owner)) | ||||
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>((u8)owner, visionRange, from); | ||||
LosUpdateHelper<true>((u8)owner, visionRange, to); | LosUpdateHelper<true>((u8)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((u8)owner, visionRange, from, to); | ||||
} | } | ||||
void SharingLosMove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | void SharingLosMove(PlayersData 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 (player_id_t i = 1; i <= PlayersData::size(); ++i) | ||||
if (HasVisionSharing(visionSharing, i)) | if (visionSharing.get(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_TerrainVerticesPerSide; j++) | for (i32 j = 0; j < m_TerrainVerticesPerSide; j++) | ||||
for (i32 i = 0; i < m_TerrainVerticesPerSide; i++) | for (i32 i = 0; i < m_TerrainVerticesPerSide; i++) | ||||
{ | { | ||||
if (LosIsOffWorld(i, j)) | if (LosIsOffWorld(i, j)) | ||||
continue; | continue; | ||||
for (playerIt = players.begin(); playerIt != players.end(); ++playerIt) | for (auto & player: players) | ||||
SilierUnsubmitted Done Inline ActionsIt would be good to avoid auto we know that there are player_id_t no? Silier: It would be good to avoid `auto` we know that there are `player_id_t` no? | |||||
if (m_LosState[j*m_TerrainVerticesPerSide + i] & (LOS_EXPLORED << (2*((*playerIt)-1)))) | if (m_LosState[j*m_TerrainVerticesPerSide + i].get(player) & LOS_EXPLORED) | ||||
SilierUnsubmitted Done Inline Actionsj * m_Ter Silier: `j * m_Ter` | |||||
{ | { | ||||
exploredVertices += 1; | exploredVertices += 1; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return exploredVertices * 100 / m_TotalInworldVertices; | return exploredVertices * 100 / m_TotalInworldVertices; | ||||
} | } | ||||
}; | }; | ||||
REGISTER_COMPONENT_TYPE(RangeManager) | REGISTER_COMPONENT_TYPE(RangeManager) | ||||
#undef LOS_TILES_RATIO | #undef LOS_TILES_RATIO | ||||
#undef DEBUG_RANGE_MANAGER_BOUNDS | #undef DEBUG_RANGE_MANAGER_BOUNDS |
IsValidPlayer or IsPlayerValid