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/EnumArray.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 "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 | ||||
using LosTile = std::pair<u16, u16>; | |||||
/** | /** | ||||
* Convert an owner ID (-1 = unowned, 0 = gaia, 1..30 = players) | * Convert an owner ID (-1 = unowned, 0 = gaia, 1..30 = players) | ||||
* into a 32-bit mask for quick set-membership tests. | * into a bit-mask for quick set-membership tests. | ||||
*/ | */ | ||||
static inline u32 CalcOwnerMask(player_id_t owner) | static EnumArray<bool, MAX_PLAYERS+1> CalcOwnerMask(player_id_t owner) | ||||
Stan: Shouldn't that use Enum<LosMask, MAX_PLAYERS + 1> instead ?
Also why did you remove the… | |||||
{ | { | ||||
if (owner >= -1 && owner < 31) | if (owner == INVALID_PLAYER) | ||||
return 1 << (1+owner); | return EnumArray<bool, MAX_PLAYERS+1>(true, MAX_PLAYER_ID+1); | ||||
else if (owner >= 0 && owner <= MAX_PLAYER_ID) | |||||
return EnumArray<bool, MAX_PLAYERS+1>(true, owner); | |||||
else | else | ||||
return 0; // owner was invalid | return EnumArray<bool, MAX_PLAYERS+1>(); // Invalid owner, default to 0 everywhere. | ||||
Not Done Inline ActionsNo else branch after return, although I guess it all could be a ternar. Stan: No else branch after return, although I guess it all could be a ternar. | |||||
} | |||||
/** | |||||
* Returns LOS mask for given player. | |||||
*/ | |||||
static inline 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. | * Returns shared LOS mask for given list of players. | ||||
*/ | */ | ||||
static u32 CalcSharedLosMask(std::vector<player_id_t> players) | static LosMask CalcSharedLosMask(std::vector<player_id_t> players) | ||||
{ | { | ||||
u32 playerMask = 0; | LosMask playerMask; | ||||
for (size_t i = 0; i < players.size(); i++) | for (size_t i = 0; i < players.size(); i++) | ||||
playerMask |= CalcPlayerLosMask(players[i]); | playerMask |= LosMask::mask(players[i]); | ||||
return playerMask; | return playerMask; | ||||
} | } | ||||
/** | /** | ||||
* 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. | |||||
*/ | |||||
static bool SetPlayerSharedDirtyVisibilityBit(u16& mask, player_id_t player, bool enable) | |||||
{ | |||||
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 | |||||
*/ | |||||
static inline LosVisibility GetPlayerVisibility(u32 visibilities, player_id_t player) | |||||
{ | |||||
if (player > 0 && player <= 16) | |||||
return static_cast<LosVisibility>( (visibilities >> (2 *(player-1))) & 0x3 ); | |||||
return LosVisibility::HIDDEN; | |||||
} | |||||
/** | |||||
* 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) | |||||
{ | |||||
if (player > 0 && player <= 16) | |||||
return (dirty >> (player - 1)) & 0x1; | |||||
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); | |||||
} | |||||
/** | |||||
* 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; | EnumArray<bool, MAX_PLAYERS+1> ownersMask; // Range queries can check against unowned entities. | ||||
i32 interface; | i32 interface; | ||||
std::vector<entity_id_t> lastMatch; | std::vector<entity_id_t> lastMatch; | ||||
u8 flagsMask; | u8 flagsMask; | ||||
}; | }; | ||||
/** | /** | ||||
* 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) | ||||
▲ Show 20 Lines • Show All 48 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(), size(0), visionSharing(), | ||||
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 | EnumArray<LosVisibility, MAX_PLAYERS> visibilities; | ||||
u32 size; | u32 size; | ||||
u16 visionSharing; // 1-bit per player | EnumArray<bool, MAX_PLAYERS> 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 All 11 Lines | 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); | SerializeEnumArray()(serialize, "owners mask", value.ownersMask); | ||||
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); | SerializeEnumArray()(serialize, "visibilities", value.visibilities); | ||||
serialize.NumberU32_Unbounded("size", value.size); | serialize.NumberU32_Unbounded("size", value.size); | ||||
serialize.NumberU16_Unbounded("vision sharing", value.visionSharing); | SerializeEnumArray()(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; | // The last value is a special value that means "for all". | ||||
std::array<bool, MAX_PLAYERS+1> m_LosRevealAll; | |||||
using LosTile = std::pair<u16, u16>; | |||||
std::array<bool, MAX_LOS_PLAYER_ID+2> 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::array<bool, MAX_LOS_PLAYER_ID> m_GlobalPlayerVisibilityUpdate; | std::array<bool, MAX_PLAYERS> m_GlobalPlayerVisibilityUpdate; | ||||
Grid<u16> m_DirtyVisibility; | Grid<EnumArray<bool, MAX_PLAYERS>> m_DirtyVisibility; | ||||
Grid<std::set<entity_id_t>> m_LosTiles; | Grid<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::array<Grid<u16>, MAX_LOS_PLAYER_ID> m_LosPlayerCounts; | std::array<Grid<u16>, MAX_PLAYERS> m_LosPlayerCounts; | ||||
// 2-bit LosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive) | Grid<LosMask> 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) | ||||
StanUnsubmitted Not Done Inline ActionsStill accurate? Stan: Still accurate? | |||||
wraitiiAuthorUnsubmitted Done Inline ActionsYup'. We're creating a custom version with ones everywhere so that other code can transparently use this. wraitii: Yup'. We're creating a custom version with ones everywhere so that other code can transparently… | |||||
Grid<u32> m_LosStateRevealed; | Grid<LosMask> m_LosStateRevealed; | ||||
// Shared LOS masks, one per player. | // Shared LOS masks, one per player (including GAIA and invalid) | ||||
std::array<u32, MAX_LOS_PLAYER_ID+2> m_SharedLosMasks; | std::array<LosMask, MAX_PLAYERS> m_SharedLosMasks; | ||||
// Shared dirty visibility masks, one per player. | // Shared dirty visibility masks, one per player (including GAIA and invalid) | ||||
std::array<u16, MAX_LOS_PLAYER_ID+2> m_SharedDirtyVisibilityMasks; | std::array<EnumArray<bool, MAX_PLAYERS>, MAX_PLAYERS> 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 All 11 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[0] = true; | // TODO: this particular setting should be made easily moddable. | ||||
// If you do make it moddable, remember to change all loops starting at FIRST_NONGAIA_PLAYER_ID. | |||||
for (player_id_t gaia_player = 0; gaia_player < FIRST_NONGAIA_PLAYER_ID; ++gaia_player) | |||||
m_LosRevealAll[gaia_player] = true; | |||||
for (player_id_t player = FIRST_NONGAIA_PLAYER_ID; player <= MAX_PLAYER_ID; ++player) | |||||
m_LosRevealAll[player] = false; | |||||
m_GlobalVisibilityUpdate = true; | m_GlobalVisibilityUpdate = true; | ||||
m_LosCircular = false; | m_LosCircular = false; | ||||
m_TerrainVerticesPerSide = 0; | m_TerrainVerticesPerSide = 0; | ||||
} | } | ||||
virtual void Deinit() | virtual void Deinit() | ||||
Show All 13 Lines | void SerializeCommon(S& serialize) | ||||
SerializeEntityMap<SerializeEntityData>()(serialize, "entity data", m_EntityData); | SerializeEntityMap<SerializeEntityData>()(serialize, "entity data", m_EntityData); | ||||
SerializeArray<SerializeBool>()(serialize, "los reveal all", m_LosRevealAll); | SerializeArray<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); | ||||
SerializeArray<SerializeBool>()(serialize, "global player visibility update", m_GlobalPlayerVisibilityUpdate); | SerializeArray<SerializeBool>()(serialize, "global player visibility update", m_GlobalPlayerVisibilityUpdate); | ||||
SerializedGridCompressed<SerializeU16_Unbounded>()(serialize, "dirty visibility", m_DirtyVisibility); | SerializedGridCompressed<SerializeEnumArray>()(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 | ||||
SerializedGridCompressed<SerializeU32_Unbounded>()(serialize, "los state", m_LosState); | SerializedGridCompressed<SerializeEnumArray>()(serialize, "los state", m_LosState); | ||||
SerializeArray<SerializeU32_Unbounded>()(serialize, "shared los masks", m_SharedLosMasks); | SerializeArray<SerializeEnumArray>()(serialize, "shared los masks", m_SharedLosMasks); | ||||
SerializeArray<SerializeU16_Unbounded>()(serialize, "shared dirty visibility masks", m_SharedDirtyVisibilityMasks); | SerializeArray<SerializeEnumArray>()(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(msgData.player > FIRST_NONGAIA_PLAYER_ID && msgData.player <= MAX_PLAYER_ID); | ||||
u16 visionChanged = CalcVisionSharingMask(msgData.player); | EnumArray<bool, MAX_PLAYERS> visionChanged(true, 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 = visionChanged; | ||||
it->second.SetFlag<FlagMasks::SharedVision>(true); | it->second.SetFlag<FlagMasks::SharedVision>(true); | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | public: | ||||
{ | { | ||||
// 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::array<Grid<u16>, MAX_LOS_PLAYER_ID> oldPlayerCounts = m_LosPlayerCounts; | std::array<Grid<u16>, MAX_PLAYERS> oldPlayerCounts = m_LosPlayerCounts; | ||||
Grid<u32> oldStateRevealed = m_LosStateRevealed; | Grid<LosMask> oldStateRevealed = m_LosStateRevealed; | ||||
FastSpatialSubdivision oldSubdivision = m_Subdivision; | FastSpatialSubdivision oldSubdivision = m_Subdivision; | ||||
Grid<std::set<entity_id_t> > oldLosTiles = m_LosTiles; | Grid<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 40 Lines | void ResetDerivedData() | ||||
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; | ||||
for (size_t player_id = 0; player_id < m_LosPlayerCounts.size(); ++player_id) | for (size_t player_id = 0; player_id < m_LosPlayerCounts.size(); ++player_id) | ||||
m_LosPlayerCounts[player_id].reset(); | m_LosPlayerCounts[player_id].reset(); | ||||
m_ExploredVertices.clear(); | m_ExploredVertices.clear(); | ||||
m_ExploredVertices.resize(MAX_LOS_PLAYER_ID+1, 0); | m_ExploredVertices.resize(MAX_PLAYERS, 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 player = FIRST_NONGAIA_PLAYER_ID; player <= MAX_PLAYER_ID; ++player) | ||||
m_ExploredVertices.at(k) += ((m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*(k-1)))) > 0); | m_ExploredVertices.at(player) += static_cast<bool>(m_LosState.get(i, j).at(player) & LosStateFlags::EXPLORED); | ||||
} else | } else | ||||
m_LosState.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); | m_LosState.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); | ||||
m_LosStateRevealed.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); | m_LosStateRevealed.resize(m_TerrainVerticesPerSide, m_TerrainVerticesPerSide); | ||||
if (!m_Deserializing) | if (!m_Deserializing) | ||||
{ | { | ||||
m_DirtyVisibility.resize(m_LosTilesPerSide, m_LosTilesPerSide); | m_DirtyVisibility.resize(m_LosTilesPerSide, m_LosTilesPerSide); | ||||
Show All 16 Lines | for (EntityMap<EntityData>::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it) | ||||
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.get(i, j) = 0; | m_LosStateRevealed.get(i, j) = LosMask::all(LosStateFlags::UNEXPLORED); | ||||
else | else | ||||
{ | { | ||||
m_LosStateRevealed.get(i, j) = 0xFFFFFFFFu; | m_LosStateRevealed.get(i, j) = LosMask::all(LosStateFlags::EXPLORED | LosStateFlags::VISIBLE); | ||||
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 150 Lines • ▼ Show 20 Lines | public: | ||||
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)); | return GetEntitiesByMask(CalcOwnerMask(player)); | ||||
} | } | ||||
virtual std::vector<entity_id_t> GetNonGaiaEntities() const | virtual std::vector<entity_id_t> GetNonGaiaEntities() const | ||||
{ | { | ||||
return GetEntitiesByMask(~3); // bit 0 for owner=-1 and bit 1 for gaia | EnumArray<bool, MAX_PLAYERS+1> mask(true, MAX_PLAYERS+1); | ||||
for (player_id_t gaia_players = 0; gaia_players < FIRST_NONGAIA_PLAYER_ID; ++gaia_players) | |||||
mask |= EnumArray<bool, MAX_PLAYERS+1>(true, gaia_players); | |||||
return GetEntitiesByMask(~mask); | |||||
} | } | ||||
virtual std::vector<entity_id_t> GetGaiaAndNonGaiaEntities() const | virtual std::vector<entity_id_t> GetGaiaAndNonGaiaEntities() const | ||||
{ | { | ||||
return GetEntitiesByMask(~1); // bit 0 for owner=-1 | return GetEntitiesByMask(~EnumArray<bool, MAX_PLAYERS+1>(true, MAX_PLAYERS+1)); | ||||
} | } | ||||
std::vector<entity_id_t> GetEntitiesByMask(u32 ownerMask) const | std::vector<entity_id_t> GetEntitiesByMask(EnumArray<bool, MAX_PLAYERS+1> ownerMask) 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 (CalcOwnerMask(it->second.owner) & ownerMask) | ||||
entities.push_back(it->first); | entities.push_back(it->first); | ||||
▲ Show 20 Lines • Show All 303 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 = EnumArray<bool, MAX_PLAYERS+1>(); | ||||
for (size_t i = 0; i < owners.size(); ++i) | for (size_t i = 0; i < owners.size(); ++i) | ||||
q.ownersMask |= CalcOwnerMask(owners[i]); | q.ownersMask |= CalcOwnerMask(owners[i]); | ||||
if (q.ownersMask == 0) | if (!q.ownersMask) | ||||
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(LosMask::all(LosStateFlags::EXPLORED | LosStateFlags::VISIBLE), m_LosStateRevealed, m_TerrainVerticesPerSide); | ||||
else | else | ||||
return CLosQuerier(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide); | return CLosQuerier(GetSharedLosMask(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()) | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | virtual LosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player) const | ||||
// Entities not with positions in the world are never visible | // Entities not with positions in the world are never visible | ||||
if (entId == INVALID_ENTITY) | if (entId == INVALID_ENTITY) | ||||
return LosVisibility::HIDDEN; | return LosVisibility::HIDDEN; | ||||
CmpPtr<ICmpPosition> cmpPosition(ent); | CmpPtr<ICmpPosition> cmpPosition(ent); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return LosVisibility::HIDDEN; | return LosVisibility::HIDDEN; | ||||
// Gaia and observers do not have a visibility cache | // 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 (m_DirtyVisibility[PosToLosTilesHelper(pos.X, pos.Y)].at(player)) | |||||
if (IsVisibilityDirty(m_DirtyVisibility[PosToLosTilesHelper(pos.X, pos.Y)], player)) | |||||
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 static_cast<LosVisibility>(it->second.visibilities.at(player)); | ||||
} | } | ||||
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 54 Lines • ▼ Show 20 Lines | |||||
void UpdateVisibilityData() | void UpdateVisibilityData() | ||||
{ | { | ||||
PROFILE("UpdateVisibilityData"); | PROFILE("UpdateVisibilityData"); | ||||
for (u16 i = 0; i < m_LosTilesPerSide; ++i) | for (u16 i = 0; i < m_LosTilesPerSide; ++i) | ||||
for (u16 j = 0; j < m_LosTilesPerSide; ++j) | for (u16 j = 0; j < m_LosTilesPerSide; ++j) | ||||
{ | { | ||||
LosTile pos{i, j}; | LosTile pos{i, j}; | ||||
for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID + 1; ++player) | for (player_id_t player = FIRST_NONGAIA_PLAYER_ID; player <= MAX_PLAYER_ID; ++player) | ||||
if (IsVisibilityDirty(m_DirtyVisibility[pos], player) || m_GlobalPlayerVisibilityUpdate[player-1] == 1 || m_GlobalVisibilityUpdate) | |||||
for (const entity_id_t& ent : m_LosTiles[pos]) | for (const entity_id_t& ent : m_LosTiles[pos]) | ||||
if (m_DirtyVisibility[pos].at(player) || m_GlobalPlayerVisibilityUpdate[player] == 1 || m_GlobalVisibilityUpdate) | |||||
UpdateVisibility(ent, player); | UpdateVisibility(ent, player); | ||||
m_DirtyVisibility[pos] = 0; | m_DirtyVisibility[pos] = m_DirtyVisibility[pos].all(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 16 Lines | |||||
} | } | ||||
void UpdateVisibility(entity_id_t ent, player_id_t player) | void UpdateVisibility(entity_id_t ent, 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.at(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 (player_id_t player = FIRST_NONGAIA_PLAYER_ID; player <= MAX_PLAYER_ID; ++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 == INVALID_PLAYER) | ||||
m_LosRevealAll[MAX_LOS_PLAYER_ID+1] = enabled; | m_LosRevealAll[MAX_PLAYER_ID+1] = enabled; | ||||
else | else | ||||
{ | { | ||||
ENSURE(player >= 0 && player <= MAX_LOS_PLAYER_ID); | ENSURE(player >= 0 && player <= MAX_PLAYER_ID); | ||||
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[MAX_PLAYER_ID+1] || player == INVALID_PLAYER) | ||||
return true; | return true; | ||||
ENSURE(player >= 0 && player <= MAX_LOS_PLAYER_ID+1); | ENSURE(player >= 0 && player <= MAX_PLAYER_ID); | ||||
// Otherwise check the player-specific flag | // Otherwise check the player-specific flag | ||||
if (m_LosRevealAll[player]) | if (m_LosRevealAll[player]) | ||||
Not Done Inline ActionsCould be inlined Stan: Could be inlined | |||||
return true; | return true; | ||||
return false; | return false; | ||||
} | } | ||||
virtual void SetLosCircular(bool enabled) | virtual void SetLosCircular(bool enabled) | ||||
{ | { | ||||
m_LosCircular = enabled; | m_LosCircular = enabled; | ||||
Show All 11 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 = FIRST_NONGAIA_PLAYER_ID; p <= MAX_PLAYER_ID; ++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 ((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 | virtual LosMask GetSharedLosMask(player_id_t player) const | ||||
{ | { | ||||
return m_SharedLosMasks[player]; | 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.get(i, j) & ((u32)LosState::EXPLORED << (2*(p-1)))); | explored += !(m_LosState.get(i, j).at(p) & LosStateFlags::EXPLORED); | ||||
m_LosState.get(i, j) |= ((u32)LosState::EXPLORED << (2*(p-1))); | m_LosState.get(i, j)[p] |= LosStateFlags::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 >= FIRST_NONGAIA_PLAYER_ID && p <= MAX_PLAYER_ID) | ||||
{ | { | ||||
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.get(ti, tj); | LosMask& losState = m_LosState.get(ti, tj); | ||||
if (!(losState & ((u32)LosState::EXPLORED << (2*(p-1))))) | if (!(losState.at(p) & LosStateFlags::EXPLORED)) | ||||
{ | { | ||||
++explored; | ++explored; | ||||
losState |= ((u32)LosState::EXPLORED << (2*(p-1))); | losState[p] |= LosStateFlags::EXPLORED; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
for (player_id_t p = 1; p < MAX_LOS_PLAYER_ID+1; ++p) | for (player_id_t p = FIRST_NONGAIA_PLAYER_ID; p <= MAX_PLAYER_ID; ++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 30 Lines | 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 (p == INVALID_PLAYER || p < FIRST_NONGAIA_PLAYER_ID || p > MAX_PLAYER_ID) | ||||
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 43 Lines • ▼ Show 20 Lines | else | ||||
i >= m_TerrainVerticesPerSide - MAP_EDGE_TILES || | i >= m_TerrainVerticesPerSide - MAP_EDGE_TILES || | ||||
j >= m_TerrainVerticesPerSide - MAP_EDGE_TILES; | j >= m_TerrainVerticesPerSide - 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(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 | ||||
if (counts.get(i, j) == 0) | if (counts.get(i, j) == 0) | ||||
{ | { | ||||
if (!LosIsOffWorld(i, j)) | if (!LosIsOffWorld(i, j)) | ||||
{ | { | ||||
explored += !(m_LosState.get(i, j) & ((u32)LosState::EXPLORED << (2*(owner-1)))); | explored += !(m_LosState.get(i, j).at(owner) & LosStateFlags::EXPLORED); | ||||
m_LosState.get(i, j) |= (((int)LosState::VISIBLE | (u32)LosState::EXPLORED) << (2*(owner-1))); | m_LosState.get(i, j)[owner] |= (LosStateFlags::VISIBLE | LosStateFlags::EXPLORED); | ||||
} | } | ||||
MarkVisibilityDirtyAroundTile(owner, i, j); | MarkVisibilityDirtyAroundTile(owner, i, j); | ||||
} | } | ||||
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(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)[owner].unset_bit(LosStateFlags::VISIBLE); | ||||
MarkVisibilityDirtyAroundTile(owner, i, j); | MarkVisibilityDirtyAroundTile(owner, i, j); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
inline void MarkVisibilityDirtyAroundTile(u8 owner, i32 i, i32 j) | inline void MarkVisibilityDirtyAroundTile(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 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 | ||||
LosTile n1 = PosToLosTilesHelper(i-1, j-1); | LosTile n1 = PosToLosTilesHelper(i-1, j-1); | ||||
LosTile n2 = PosToLosTilesHelper(i-1, j); | LosTile n2 = PosToLosTilesHelper(i-1, j); | ||||
LosTile n3 = PosToLosTilesHelper(i, j-1); | LosTile n3 = PosToLosTilesHelper(i, j-1); | ||||
LosTile n4 = PosToLosTilesHelper(i, j); | LosTile n4 = PosToLosTilesHelper(i, j); | ||||
u16 sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner]; | EnumArray<bool, MAX_PLAYERS> sharedDirtyVisibilityMask = m_SharedDirtyVisibilityMasks[owner]; | ||||
if (j > 0 && i > 0) | if (j > 0 && i > 0) | ||||
m_DirtyVisibility[n1] |= sharedDirtyVisibilityMask; | m_DirtyVisibility[n1] |= sharedDirtyVisibilityMask; | ||||
if (n2 != n1 && j > 0 && i < m_TerrainVerticesPerSide) | if (n2 != n1 && j > 0 && i < m_TerrainVerticesPerSide) | ||||
m_DirtyVisibility[n2] |= sharedDirtyVisibilityMask; | m_DirtyVisibility[n2] |= sharedDirtyVisibilityMask; | ||||
if (n3 != n1 && j < m_TerrainVerticesPerSide && i > 0) | if (n3 != n1 && j < m_TerrainVerticesPerSide && i > 0) | ||||
m_DirtyVisibility[n3] |= sharedDirtyVisibilityMask; | m_DirtyVisibility[n3] |= 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] |= 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> | ||||
void LosUpdateHelper(u8 owner, entity_pos_t visionRange, CFixedVector2D pos) | void LosUpdateHelper(player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos) | ||||
{ | { | ||||
if (m_TerrainVerticesPerSide == 0) // do nothing if not initialised yet | if (m_TerrainVerticesPerSide == 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(player_id_t owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | ||||
{ | { | ||||
if (m_TerrainVerticesPerSide == 0) // do nothing if not initialised yet | if (m_TerrainVerticesPerSide == 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 108 Lines • ▼ Show 20 Lines | #endif | ||||
LosRemoveStripHelper(owner, i1clamp_to+1, i1clamp_from, j, counts); | LosRemoveStripHelper(owner, i1clamp_to+1, i1clamp_from, j, counts); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
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 == INVALID_PLAYER || owner < FIRST_NONGAIA_PLAYER_ID || owner > MAX_PLAYER_ID) | ||||
return; | return; | ||||
LosUpdateHelper<true>((u8)owner, visionRange, pos); | LosUpdateHelper<true>(owner, visionRange, pos); | ||||
} | } | ||||
void SharingLosAdd(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos) | void SharingLosAdd(EnumArray<bool, MAX_PLAYERS> 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 player = FIRST_NONGAIA_PLAYER_ID; player <= MAX_PLAYER_ID; ++player) | ||||
if (HasVisionSharing(visionSharing, i)) | if (visionSharing.at(player)) | ||||
LosAdd(i, visionRange, pos); | LosAdd(player, 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 == INVALID_PLAYER || owner < FIRST_NONGAIA_PLAYER_ID || owner > MAX_PLAYER_ID) | ||||
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(EnumArray<bool, MAX_PLAYERS> 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 player = FIRST_NONGAIA_PLAYER_ID; player <= MAX_PLAYER_ID; ++player) | ||||
if (HasVisionSharing(visionSharing, i)) | if (visionSharing.at(player)) | ||||
LosRemove(i, visionRange, pos); | LosRemove(player, 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 == INVALID_PLAYER || owner < FIRST_NONGAIA_PLAYER_ID || owner > MAX_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>(owner, visionRange, from); | ||||
LosUpdateHelper<true>((u8)owner, visionRange, to); | LosUpdateHelper<true>(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(owner, visionRange, from, to); | ||||
} | } | ||||
void SharingLosMove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | void SharingLosMove(EnumArray<bool, MAX_PLAYERS> 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 player = FIRST_NONGAIA_PLAYER_ID; player <= MAX_PLAYER_ID; ++player) | ||||
if (HasVisionSharing(visionSharing, i)) | if (visionSharing.at(player)) | ||||
LosMove(i, visionRange, from, to); | LosMove(player, 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 (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).at(*playerIt) & LosStateFlags::EXPLORED) | ||||
{ | { | ||||
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 |
Wildfire Games · Phabricator
Shouldn't that use Enum<LosMask, MAX_PLAYERS + 1> instead ?
Also why did you remove the inlining?