Changeset View
Standalone View
source/simulation2/components/CCmpRangeManager.cpp
Show All 18 Lines | |||||
#include "simulation2/system/Component.h" | #include "simulation2/system/Component.h" | ||||
#include "ICmpRangeManager.h" | #include "ICmpRangeManager.h" | ||||
#include "ICmpTerrain.h" | #include "ICmpTerrain.h" | ||||
#include "simulation2/system/EntityMap.h" | #include "simulation2/system/EntityMap.h" | ||||
#include "simulation2/MessageTypes.h" | #include "simulation2/MessageTypes.h" | ||||
#include "simulation2/components/ICmpFogging.h" | #include "simulation2/components/ICmpFogging.h" | ||||
#include "simulation2/components/ICmpFootprint.h" | |||||
#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/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 | // Enables runtime tests for vision blocking computation | ||||
Stan: Was it supposed to be removed :P | |||||
Done Inline Actionsnot used anymore Silier: not used anymore | |||||
Done Inline ActionsFair. Stan: Fair. | |||||
// Program will trigger exception when finds problem | |||||
#define DEBUG_VIS_COMP 0 | |||||
/** | /** | ||||
* Representation of a range query. | * Representation of a range query. | ||||
*/ | */ | ||||
struct Query | struct Query | ||||
{ | { | ||||
bool enabled; | bool enabled; | ||||
bool parabolic; | bool parabolic; | ||||
▲ Show 20 Lines • Show All 153 Lines • ▼ Show 20 Lines | enum FlagMasks | ||||
ScriptedVisibility = 0x40, | ScriptedVisibility = 0x40, | ||||
SharedVision = 0x80 | SharedVision = 0x80 | ||||
}; | }; | ||||
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), h(0), flags(FlagMasks::Normal) | ||||
{ } | { } | ||||
entity_pos_t x, z; | entity_pos_t x, z; | ||||
entity_pos_t visionRange; | entity_pos_t visionRange; | ||||
u16 h; // height of entity | |||||
Not Done Inline Actionswhitespace Stan: whitespace | |||||
Not Done Inline ActionsComment should be above but I'll settle for it starting with a Capital. Stan: Comment should be above but I'll settle for it starting with a Capital. | |||||
u32 visibilities; // 2-bit visibility, per player | u32 visibilities; // 2-bit visibility, per player | ||||
u32 size; | u32 size; | ||||
Not Done Inline Actionsmaybe name it height, and remove the comment ? Stan: maybe name it height, and remove the comment ? | |||||
u16 visionSharing; // 1-bit per player | u16 visionSharing; // 1-bit per player | ||||
Not Done Inline Actionswhitespace Stan: whitespace | |||||
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) == 28); | ||||
/** | /** | ||||
* 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) | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | |||||
* Range manager implementation. | * Range manager implementation. | ||||
* Maintains a list of all entities (and their positions and owners), which is used for | * Maintains a list of all entities (and their positions and owners), which is used for | ||||
* queries. | * queries. | ||||
* | * | ||||
* LOS implementation is based on the model described in GPG2. | * LOS implementation is based on the model described in GPG2. | ||||
* (TODO: would be nice to make it cleverer, so e.g. mountains and walls | * (TODO: would be nice to make it cleverer, so e.g. mountains and walls | ||||
* can block vision) | * can block vision) | ||||
*/ | */ | ||||
class CCmpRangeManager : public ICmpRangeManager | class CCmpRangeManager : public ICmpRangeManager | ||||
Not Done Inline ActionsNuke ? Stan: Nuke ? | |||||
{ | { | ||||
public: | public: | ||||
static void ClassInit(CComponentManager& componentManager) | static void ClassInit(CComponentManager& componentManager) | ||||
{ | { | ||||
componentManager.SubscribeGloballyToMessageType(MT_Create); | componentManager.SubscribeGloballyToMessageType(MT_Create); | ||||
componentManager.SubscribeGloballyToMessageType(MT_PositionChanged); | componentManager.SubscribeGloballyToMessageType(MT_PositionChanged); | ||||
componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged); | componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged); | ||||
componentManager.SubscribeGloballyToMessageType(MT_Destroy); | componentManager.SubscribeGloballyToMessageType(MT_Destroy); | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | public: | ||||
// 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; | ||||
Not Done Inline Actionscalls Stan: calls | |||||
CTerrain* cTerrain; // large amount of calles so chache it | |||||
Not Done Inline ActionsI guess you meant cache :P Put it above. Stan: I guess you meant cache :P Put it above. | |||||
Not Done Inline Actions@vladislavbelov I wonder in which cases we should use smart pointers instead of raw ones. Stan: @vladislavbelov I wonder in which cases we should use smart pointers instead of raw ones. | |||||
// Map used by all players and vertexes with the same structure as m_LosState | |||||
// Used in los update to determine if given tile was visible | |||||
// No need to serialise | |||||
std::vector<bool> m_Vismap; | |||||
Not Done Inline ActionsOne should probably include vector then :) Stan: One should probably include vector then :) | |||||
Done Inline Actionswhat do you mean ? Silier: what do you mean ? | |||||
Not Done Inline Actions#include <vector> Stan: #include <vector> | |||||
Done Inline Actionsnot needed include, used at lines 402, 384, 392 Silier: not needed include, used at lines 402, 384, 392 | |||||
Not Done Inline ActionsI'm not sure I understand. Forward declaration for STD types is undefined behavior so that wouldn't work + It doesn't work for members. Stan: I'm not sure I understand.
Forward declaration for STD types is undefined behavior so that… | |||||
// Map used by all players and vertexes with the same structure as m_LosState | |||||
// Used in los update to determine if given tile blocked vision | |||||
// No need to serialise | |||||
std::vector<bool> m_RelatHighMap; | |||||
Not Done Inline Actionsstd::vector<bool>. That’s interesting. lyv: `std::vector<bool>`. That’s interesting. | |||||
Done Inline Actionswhat is interesting about it? Silier: what is interesting about it? | |||||
Not Done Inline ActionsAccording to Wiktionary, interesting = interest + -ing, from Latin interesse = present active infinitive of intersum = inter- + sum, where inter:
sum:
In other words he has a patch for it somewhere, see discussion in D1637. elexis: According to Wiktionary, `interesting` = `interest + -ing`, from Latin `interesse` = `present… | |||||
Not Done Inline ActionsNo I do not have a patch anywhere related to this. If I am being more specific at what I am getting at, just see the differences of vector<notBoolT> vs vector<boolT>. lyv: No I do not have a patch anywhere related to this. If I am being more specific at what I am… | |||||
Not Done Inline ActionsJust to clarify, I am not being vague here. I am just trying to avoid making a direct statement since I don't consider myself an expert in C++. lyv: Just to clarify, I am not being vague here. I am just trying to avoid making a direct statement… | |||||
Not Done Inline ActionsI guess you mean that https://en.cppreference.com/w/cpp/container/vector_bool might or might not be worth reading elexis: I guess you mean that https://en.cppreference.com/w/cpp/container/vector_bool might or might… | |||||
Not Done Inline Actions(Also to clarify, I didn't want to imply that you were intentionally vague in that statement, I actually wanted to derive the statement from the etymology but failed the last steps of the deduction and it's useless indirection anyhow) elexis: (Also to clarify, I didn't want to imply that you were intentionally vague in that statement, I… | |||||
Not Done Inline ActionsOr tldr; It's not an STL container and therefore does not work like one (code which would compile for other T vector would not compile for this specific thing). And it's placement is widely regarded as a mistake in the standard. But despite the various attempts to remove or depreciate the thing, it still remains as part of the standard. For backwards compatibility I suppose. I guess this is the part where I propose solutions. boost bitsets. lyv: Or tldr; It's not an STL container and therefore does not work like one (code which would… | |||||
Not Done Inline Actionsrefs https://stackoverflow.com/questions/17794569/why-is-vectorbool-not-a-stl-container So the problem with it is it pretends to be an STL container but isn't one, which is arguably contradictory and thus a mistake, and thus can be assumed to be deprecated in the future and thus not using it now would mean that our code doesn't have to be changed in the future, even if one only uses those functions that compile fine now. The question is whether there is also a plan for a successor, since the use case for bit arrays won't go away. I thought it would be preferable to avoid making the code depend on boost libraries. elexis: refs https://stackoverflow.com/questions/17794569/why-is-vectorbool-not-a-stl-container
So the… | |||||
Not Done Inline ActionsArguments for not using this and using boost. A bitset that is reasonably performant is not that difficult to implement. So that is an alternative too. Or just keep this as is. As long as whoever will commit this is fine with it, this whole discussion can be safely ignored. lyv: Arguments for not using this and using boost.
https://www.boost. | |||||
Not Done Inline Actions@Angen can you try the boost bitesets and do some profiling ? :) You can usually do so in the tests, so that you have an easy sandbox. Stan: @Angen can you try the boost bitesets and do some profiling ? :) You can usually do so in the… | |||||
// Used in los update to know if in current computation was | |||||
// vision blocked | |||||
bool m_VisionBlocked; | |||||
#if DEBUG_VIS_COMP | |||||
// If set to 1 m_VisComputedBefore will reset values at every start of los update | |||||
// To check behaviour of multiple changes of los at the same position | |||||
#define DEBUG_CLEAR_VIS_CHECK 1 | |||||
Not Done Inline ActionsCan't you just use the DEFINE Above instead of adding a new one ? Stan: Can't you just use the DEFINE Above instead of adding a new one ? | |||||
// Holds if tile was checked by los update | |||||
std::vector<bool> m_VisComputedBefore; | |||||
#endif | |||||
// 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 MAX_LOS_PLAYER_ID (inclusive) | ||||
std::vector<u32> m_LosState; | std::vector<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) | ||||
std::vector<u32> m_LosStateRevealed; | std::vector<u32> m_LosStateRevealed; | ||||
// Shared LOS masks, one per player. | // Shared LOS masks, one per player. | ||||
Show All 33 Lines | virtual void Init(const CParamNode& UNUSED(paramNode)) | ||||
m_SharedLosMasks.resize(MAX_LOS_PLAYER_ID+2,0); | m_SharedLosMasks.resize(MAX_LOS_PLAYER_ID+2,0); | ||||
m_SharedDirtyVisibilityMasks.resize(MAX_LOS_PLAYER_ID + 2, 0); | m_SharedDirtyVisibilityMasks.resize(MAX_LOS_PLAYER_ID + 2, 0); | ||||
m_GlobalVisibilityUpdate = true; | m_GlobalVisibilityUpdate = true; | ||||
m_GlobalPlayerVisibilityUpdate.resize(MAX_LOS_PLAYER_ID); | m_GlobalPlayerVisibilityUpdate.resize(MAX_LOS_PLAYER_ID); | ||||
m_LosCircular = false; | m_LosCircular = false; | ||||
m_TerrainVerticesPerSide = 0; | m_TerrainVerticesPerSide = 0; | ||||
cTerrain = &GetSimContext().GetTerrain(); | |||||
Not Done Inline Actionsnullptr. Stan: nullptr. | |||||
} | } | ||||
virtual void Deinit() | virtual void Deinit() | ||||
{ | { | ||||
} | } | ||||
template<typename S> | template<typename S> | ||||
void SerializeCommon(S& serialize) | void SerializeCommon(S& serialize) | ||||
{ | { | ||||
serialize.NumberFixed_Unbounded("world x0", m_WorldX0); | serialize.NumberFixed_Unbounded("world x0", m_WorldX0); | ||||
serialize.NumberFixed_Unbounded("world z0", m_WorldZ0); | serialize.NumberFixed_Unbounded("world z0", m_WorldZ0); | ||||
Done Inline Actionsremove Silier: remove | |||||
serialize.NumberFixed_Unbounded("world x1", m_WorldX1); | serialize.NumberFixed_Unbounded("world x1", m_WorldX1); | ||||
serialize.NumberFixed_Unbounded("world z1", m_WorldZ1); | serialize.NumberFixed_Unbounded("world z1", m_WorldZ1); | ||||
serialize.NumberU32_Unbounded("query next", m_QueryNext); | serialize.NumberU32_Unbounded("query next", m_QueryNext); | ||||
SerializeMap<SerializeU32_Unbounded, SerializeQuery>()(serialize, "queries", m_Queries, GetSimContext()); | SerializeMap<SerializeU32_Unbounded, SerializeQuery>()(serialize, "queries", m_Queries, GetSimContext()); | ||||
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); | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | case MT_Create: | ||||
if (cmpVisibility) | if (cmpVisibility) | ||||
entdata.SetFlag<FlagMasks::RetainInFog>(cmpVisibility->GetRetainInFog()); | entdata.SetFlag<FlagMasks::RetainInFog>(cmpVisibility->GetRetainInFog()); | ||||
// Store the size | // Store the size | ||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); | CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); | ||||
if (cmpObstruction) | if (cmpObstruction) | ||||
entdata.size = cmpObstruction->GetSize().ToInt_RoundToInfinity(); | entdata.size = cmpObstruction->GetSize().ToInt_RoundToInfinity(); | ||||
// Store height | |||||
CmpPtr<ICmpFootprint> cmpFootprint(GetSimContext(), ent); | |||||
if (cmpFootprint) | |||||
entdata.h = cmpFootprint->GetHeight().ToInt_RoundToNearest(); | |||||
// Remember this entity | // Remember this entity | ||||
m_EntityData.insert(ent, entdata); | m_EntityData.insert(ent, entdata); | ||||
break; | break; | ||||
} | } | ||||
case MT_PositionChanged: | case MT_PositionChanged: | ||||
{ | { | ||||
const CMessagePositionChanged& msgData = static_cast<const CMessagePositionChanged&> (msg); | const CMessagePositionChanged& msgData = static_cast<const CMessagePositionChanged&> (msg); | ||||
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; | ||||
if (msgData.inWorld) | if (msgData.inWorld) | ||||
{ | { | ||||
if (it->second.HasFlag<FlagMasks::InWorld>()) | if (it->second.HasFlag<FlagMasks::InWorld>()) | ||||
{ | { | ||||
CFixedVector2D from(it->second.x, it->second.z); | CFixedVector2D from(it->second.x, it->second.z); | ||||
CFixedVector2D to(msgData.x, msgData.z); | CFixedVector2D to(msgData.x, msgData.z); | ||||
m_Subdivision.Move(ent, from, to, it->second.size); | m_Subdivision.Move(ent, from, to, it->second.size); | ||||
if (it->second.HasFlag<FlagMasks::SharedVision>()) | if (it->second.HasFlag<FlagMasks::SharedVision>()) | ||||
SharingLosMove(it->second.visionSharing, it->second.visionRange, from, to); | SharingLosMove(it->second.visionSharing, it->second.visionRange, from, to, it->second.h); | ||||
else | else | ||||
LosMove(it->second.owner, it->second.visionRange, from, to); | LosMove(it->second.owner, it->second.visionRange, from, to, it->second.h); | ||||
i32 oldLosTile = PosToLosTilesHelper(it->second.x, it->second.z); | i32 oldLosTile = PosToLosTilesHelper(it->second.x, it->second.z); | ||||
i32 newLosTile = PosToLosTilesHelper(msgData.x, msgData.z); | i32 newLosTile = PosToLosTilesHelper(msgData.x, msgData.z); | ||||
if (oldLosTile != newLosTile) | if (oldLosTile != newLosTile) | ||||
{ | { | ||||
RemoveFromTile(oldLosTile, ent); | RemoveFromTile(oldLosTile, ent); | ||||
AddToTile(newLosTile, ent); | AddToTile(newLosTile, ent); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
CFixedVector2D to(msgData.x, msgData.z); | CFixedVector2D to(msgData.x, msgData.z); | ||||
m_Subdivision.Add(ent, to, it->second.size); | m_Subdivision.Add(ent, to, it->second.size); | ||||
if (it->second.HasFlag<FlagMasks::SharedVision>()) | if (it->second.HasFlag<FlagMasks::SharedVision>()) | ||||
SharingLosAdd(it->second.visionSharing, it->second.visionRange, to); | SharingLosAdd(it->second.visionSharing, it->second.visionRange, to, it->second.h); | ||||
else | else | ||||
LosAdd(it->second.owner, it->second.visionRange, to); | LosAdd(it->second.owner, it->second.visionRange, to, it->second.h); | ||||
AddToTile(PosToLosTilesHelper(msgData.x, msgData.z), ent); | AddToTile(PosToLosTilesHelper(msgData.x, msgData.z), ent); | ||||
} | } | ||||
it->second.SetFlag<FlagMasks::InWorld>(true); | it->second.SetFlag<FlagMasks::InWorld>(true); | ||||
it->second.x = msgData.x; | it->second.x = msgData.x; | ||||
it->second.z = msgData.z; | it->second.z = msgData.z; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
if (it->second.HasFlag<FlagMasks::InWorld>()) | if (it->second.HasFlag<FlagMasks::InWorld>()) | ||||
{ | { | ||||
CFixedVector2D from(it->second.x, it->second.z); | CFixedVector2D from(it->second.x, it->second.z); | ||||
m_Subdivision.Remove(ent, from, it->second.size); | m_Subdivision.Remove(ent, from, it->second.size); | ||||
if (it->second.HasFlag<FlagMasks::SharedVision>()) | if (it->second.HasFlag<FlagMasks::SharedVision>()) | ||||
SharingLosRemove(it->second.visionSharing, it->second.visionRange, from); | SharingLosRemove(it->second.visionSharing, it->second.visionRange, from, it->second.h); | ||||
else | else | ||||
LosRemove(it->second.owner, it->second.visionRange, from); | LosRemove(it->second.owner, it->second.visionRange, from, it->second.h); | ||||
RemoveFromTile(PosToLosTilesHelper(it->second.x, it->second.z), ent); | RemoveFromTile(PosToLosTilesHelper(it->second.x, it->second.z), ent); | ||||
} | } | ||||
it->second.SetFlag<FlagMasks::InWorld>(false); | it->second.SetFlag<FlagMasks::InWorld>(false); | ||||
it->second.x = entity_pos_t::Zero(); | it->second.x = entity_pos_t::Zero(); | ||||
it->second.z = entity_pos_t::Zero(); | it->second.z = entity_pos_t::Zero(); | ||||
} | } | ||||
Show All 14 Lines | case MT_OwnershipChanged: | ||||
if (it->second.HasFlag<FlagMasks::InWorld>()) | if (it->second.HasFlag<FlagMasks::InWorld>()) | ||||
{ | { | ||||
// Entity vision is taken into account in VisionSharingChanged | // Entity vision is taken into account in VisionSharingChanged | ||||
// when sharing component activated | // when sharing component activated | ||||
if (!it->second.HasFlag<FlagMasks::SharedVision>()) | if (!it->second.HasFlag<FlagMasks::SharedVision>()) | ||||
{ | { | ||||
CFixedVector2D pos(it->second.x, it->second.z); | CFixedVector2D pos(it->second.x, it->second.z); | ||||
LosRemove(it->second.owner, it->second.visionRange, pos); | LosRemove(it->second.owner, it->second.visionRange, pos, it->second.h); | ||||
LosAdd(msgData.to, it->second.visionRange, pos); | LosAdd(msgData.to, it->second.visionRange, pos, it->second.h); | ||||
} | } | ||||
if (it->second.HasFlag<FlagMasks::RevealShore>()) | if (it->second.HasFlag<FlagMasks::RevealShore>()) | ||||
{ | { | ||||
RevealShore(it->second.owner, false); | RevealShore(it->second.owner, false, it->second.h); | ||||
RevealShore(msgData.to, true); | RevealShore(msgData.to, true, it->second.h); | ||||
} | } | ||||
} | } | ||||
ENSURE(-128 <= msgData.to && msgData.to <= 127); | ENSURE(-128 <= msgData.to && msgData.to <= 127); | ||||
it->second.owner = (i8)msgData.to; | it->second.owner = (i8)msgData.to; | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | case MT_VisionRangeChanged: | ||||
it->second.visionRange = newRange; | it->second.visionRange = newRange; | ||||
if (it->second.HasFlag<FlagMasks::InWorld>()) | if (it->second.HasFlag<FlagMasks::InWorld>()) | ||||
{ | { | ||||
CFixedVector2D pos(it->second.x, it->second.z); | CFixedVector2D pos(it->second.x, it->second.z); | ||||
if (it->second.HasFlag<FlagMasks::SharedVision>()) | if (it->second.HasFlag<FlagMasks::SharedVision>()) | ||||
{ | { | ||||
SharingLosRemove(it->second.visionSharing, oldRange, pos); | SharingLosRemove(it->second.visionSharing, oldRange, pos, it->second.h); | ||||
SharingLosAdd(it->second.visionSharing, newRange, pos); | SharingLosAdd(it->second.visionSharing, newRange, pos, it->second.h); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
LosRemove(it->second.owner, oldRange, pos); | LosRemove(it->second.owner, oldRange, pos, it->second.h); | ||||
LosAdd(it->second.owner, newRange, pos); | LosAdd(it->second.owner, newRange, pos, it->second.h); | ||||
} | } | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
case MT_VisionSharingChanged: | case MT_VisionSharingChanged: | ||||
{ | { | ||||
const CMessageVisionSharingChanged& msgData = static_cast<const CMessageVisionSharingChanged&> (msg); | const CMessageVisionSharingChanged& msgData = static_cast<const CMessageVisionSharingChanged&> (msg); | ||||
Show All 17 Lines | case MT_VisionSharingChanged: | ||||
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, it->second.h); | ||||
else | else | ||||
LosRemove(msgData.player, range, pos); | LosRemove(msgData.player, range, pos, it->second.h); | ||||
} | } | ||||
if (msgData.add) | if (msgData.add) | ||||
it->second.visionSharing |= visionChanged; | it->second.visionSharing |= visionChanged; | ||||
else | else | ||||
it->second.visionSharing &= ~visionChanged; | it->second.visionSharing &= ~visionChanged; | ||||
break; | break; | ||||
} | } | ||||
Show All 16 Lines | #endif | ||||
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, ssize_t vertices) | virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, ssize_t vertices) | ||||
{ | { | ||||
m_WorldX0 = x0; | m_WorldX0 = x0; | ||||
m_WorldZ0 = z0; | m_WorldZ0 = z0; | ||||
m_WorldX1 = x1; | m_WorldX1 = x1; | ||||
m_WorldZ1 = z1; | m_WorldZ1 = z1; | ||||
m_TerrainVerticesPerSide = (i32)vertices; | m_TerrainVerticesPerSide = (i32)vertices; | ||||
ResetDerivedData(); | ResetDerivedData(); | ||||
Not Done Inline Actionsnewline. Stan: newline. | |||||
} | } | ||||
Not Done Inline ActionsAnyway to check that they aren't the same to avoid useless assignation ? Stan: Anyway to check that they aren't the same to avoid useless assignation ? | |||||
virtual void Verify() | 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) | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | void ResetDerivedData() | ||||
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(MAX_LOS_PLAYER_ID+1); | ||||
m_ExploredVertices.clear(); | m_ExploredVertices.clear(); | ||||
m_ExploredVertices.resize(MAX_LOS_PLAYER_ID+1, 0); | m_ExploredVertices.resize(MAX_LOS_PLAYER_ID+1, 0); | ||||
if (m_Deserializing) | if (m_Deserializing) | ||||
{ | { | ||||
Not Done Inline ActionsCaps. Stan: Caps. | |||||
// 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 (u8 k = 1; k < MAX_LOS_PLAYER_ID+1; ++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] & (LOS_EXPLORED << (2*(k-1)))) > 0); | ||||
} | } | ||||
else | else | ||||
Show All 13 Lines | void ResetDerivedData() | ||||
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), it->second.h); | ||||
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), it->second.h); | ||||
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, it->second.h); | ||||
} | } | ||||
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] = 0; | ||||
▲ Show 20 Lines • Show All 698 Lines • ▼ Show 20 Lines | virtual void ActivateScriptedVisibility(entity_id_t ent, bool status) | ||||
if (it != m_EntityData.end()) | if (it != m_EntityData.end()) | ||||
it->second.SetFlag<FlagMasks::ScriptedVisibility>(status); | it->second.SetFlag<FlagMasks::ScriptedVisibility>(status); | ||||
} | } | ||||
ELosVisibility ComputeLosVisibility(CEntityHandle ent, player_id_t player) const | ELosVisibility ComputeLosVisibility(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 (ent.GetId() == INVALID_ENTITY) | if (ent.GetId() == INVALID_ENTITY) | ||||
return VIS_HIDDEN; | return VIS_HIDDEN; | ||||
Not Done Inline Actionscould be merged ? Stan: could be merged ? | |||||
Not Done Inline ActionsWould be something like return (!(LosIsOffWorld(i, j) || !(i >= 0 && j >= 0 && i < m_TerrainVerticesPerSide && j < m_TerrainVerticesPerSide)) && m_LosState[j*m_TerrainVerticesPerSide + i] & (state << (2 * (player - 1))) Stan: Would be something like
```lang=cpp
return (!(LosIsOffWorld(i, j) ||
!(i >= 0 && j >= 0… | |||||
Done Inline ActionsI dont know it would be too long Silier: I dont know it would be too long | |||||
Not Done Inline ActionsCould be make clearer with functions maybe :) Stan: Could be make clearer with functions maybe :) | |||||
CmpPtr<ICmpPosition> cmpPosition(ent); | CmpPtr<ICmpPosition> cmpPosition(ent); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
Not Done Inline ActionsMissing spaces between operators, make this a helper function, and use it in both methods, with LOS_VISIBLE and explored as parameter ? Stan: Missing spaces between operators, make this a helper function, and use it in both methods, with… | |||||
return VIS_HIDDEN; | return VIS_HIDDEN; | ||||
// Mirage entities, whatever the situation, are visible for one specific player | // Mirage entities, whatever the situation, are visible for one specific player | ||||
CmpPtr<ICmpMirage> cmpMirage(ent); | CmpPtr<ICmpMirage> cmpMirage(ent); | ||||
if (cmpMirage && cmpMirage->GetPlayer() != player) | if (cmpMirage && cmpMirage->GetPlayer() != player) | ||||
return VIS_HIDDEN; | return VIS_HIDDEN; | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | CFixedVector2D pos = cmpPosition->GetPosition2D(); | ||||
▲ Show 20 Lines • Show All 208 Lines • ▼ Show 20 Lines | |||||
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 < MAX_LOS_PLAYER_ID + 1; ++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) | ||||
Done Inline Actionsremove Silier: remove | |||||
m_LosRevealAll[MAX_LOS_PLAYER_ID+1] = enabled; | m_LosRevealAll[MAX_LOS_PLAYER_ID+1] = enabled; | ||||
else | else | ||||
{ | { | ||||
ENSURE(player >= 0 && player <= MAX_LOS_PLAYER_ID); | ENSURE(player >= 0 && player <= MAX_LOS_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 | ||||
▲ Show 20 Lines • Show All 152 Lines • ▼ Show 20 Lines | void SeeExploredEntities(player_id_t p) const | ||||
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, u16 h) | ||||
{ | { | ||||
if (p <= 0 || p > MAX_LOS_PLAYER_ID) | if (p <= 0 || p > MAX_LOS_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); | ||||
std::vector<u16>& counts = m_LosPlayerCounts.at(p); | std::vector<u16>& counts = m_LosPlayerCounts.at(p); | ||||
ENSURE(!counts.empty()); | ENSURE(!counts.empty()); | ||||
u16* countsData = &counts[0]; | u16* countsData = &counts[0]; | ||||
for (u16 j = 0; j < shoreGrid.m_H; ++j) | for (u16 j = 0; j < shoreGrid.m_H; ++j) | ||||
for (u16 i = 0; i < shoreGrid.m_W; ++i) | for (u16 i = 0; i < shoreGrid.m_W; ++i) | ||||
{ | { | ||||
u16 shoredist = shoreGrid.get(i, j); | u16 shoredist = shoreGrid.get(i, j); | ||||
if (shoredist > maxdist) | if (shoredist > maxdist) | ||||
continue; | continue; | ||||
// Maybe we could be more clever and don't add dummy strips of one tile | // Maybe we could be more clever and don't add dummy strips of one tile | ||||
if (enable) | if (enable) | ||||
LosAddStripHelper(p, i, i, j, countsData); | LosManageStripHelper(true, p, i, i, j, countsData, h, i, j); | ||||
Not Done Inline ActionsReplace true and false by enable, and you should be able to remove the if. Stan: Replace true and false by enable, and you should be able to remove the if. | |||||
Done Inline Actionsoh nice, did not realise it Silier: oh nice, did not realise it | |||||
else | else | ||||
LosRemoveStripHelper(p, i, i, j, countsData); | LosManageStripHelper(false, p, i, i, j, countsData, h, i, j); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Returns whether the given vertex is outside the normal bounds of the world | * Returns whether the given vertex is outside the normal bounds of the world | ||||
* (i.e. outside the range of a circular map) | * (i.e. outside the range of a circular map) | ||||
*/ | */ | ||||
inline bool LosIsOffWorld(ssize_t i, ssize_t j) const | inline bool LosIsOffWorld(ssize_t i, ssize_t j) const | ||||
{ | { | ||||
// WARNING: CCmpPathfinder::UpdateGrid needs to be kept in sync with this | // WARNING: CCmpPathfinder::UpdateGrid needs to be kept in sync with this | ||||
const ssize_t edgeSize = 3; // number of vertexes around the edge that will be off-world | const ssize_t edgeSize = 3; // number of vertexes around the edge that will be off-world | ||||
if (m_LosCircular) | if (m_LosCircular) | ||||
{ | { | ||||
// With a circular map, vertex is off-world if hypot(i - size/2, j - size/2) >= size/2: | // With a circular map, vertex is off-world if hypot(i - size/2, j - size/2) >= size/2: | ||||
ssize_t dist2 = (i - m_TerrainVerticesPerSide/2)*(i - m_TerrainVerticesPerSide/2) | ssize_t dist2 = (i - m_TerrainVerticesPerSide/2)*(i - m_TerrainVerticesPerSide/2) | ||||
+ (j - m_TerrainVerticesPerSide/2)*(j - m_TerrainVerticesPerSide/2); | + (j - m_TerrainVerticesPerSide/2)*(j - m_TerrainVerticesPerSide/2); | ||||
ssize_t r = m_TerrainVerticesPerSide/2 - edgeSize + 1; | ssize_t r = m_TerrainVerticesPerSide/2 - edgeSize + 1; | ||||
// subtract a bit from the radius to ensure nice | // subtract a bit from the radius to ensure nice | ||||
// SoD blurring around the edges of the map | // SoD blurring around the edges of the map | ||||
return (dist2 >= r*r); | return (dist2 >= r*r); | ||||
} | } | ||||
else | |||||
{ | |||||
// With a square map, the outermost edge of the map should be off-world, | // With a square map, the outermost edge of the map should be off-world, | ||||
// so the SoD texture blends out nicely | // so the SoD texture blends out nicely | ||||
return (i < edgeSize || j < edgeSize || i >= m_TerrainVerticesPerSide-edgeSize || j >= m_TerrainVerticesPerSide-edgeSize); | return (i < edgeSize || j < edgeSize || i >= m_TerrainVerticesPerSide - edgeSize || j >= m_TerrainVerticesPerSide - edgeSize); | ||||
} | } | ||||
} | |||||
/** | /** | ||||
* Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). | * Returns true iff both tiles (a1, b1), (a2, b2) are visible from source | ||||
* Using visibility map | |||||
*/ | */ | ||||
inline void LosAddStripHelper(u8 owner, i32 i0, i32 i1, i32 j, u16* counts) | inline bool CheckTilesForVisibility(i32 a1, i32 b1, i32 a2, i32 b2) | ||||
{ | { | ||||
if (i1 < i0) | #if DEBUG_VIS_COMP | ||||
Not Done Inline ActionsI think you should try to merge those blocks as much as possible for readability, vs might make them darker, but not all IDEs will. Stan: I think you should try to merge those blocks as much as possible for readability, vs might make… | |||||
Done Inline ActionsI plan it :) Silier: I plan it :) | |||||
return; | ENSURE(b1 > -1); | ||||
ENSURE(a1 > -1); | |||||
ENSURE(b2 > -1); | |||||
ENSURE(a2 > -1); | |||||
#endif | |||||
i32 idx = (b1)*m_TerrainVerticesPerSide + (a1); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
if (!m_Vismap.at(idx)) | |||||
return false; | |||||
i32 idx2 = (b2)*m_TerrainVerticesPerSide + (a2); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx2)); | |||||
#endif | |||||
return m_Vismap.at(idx2); | |||||
} | |||||
i32 idx0 = j*m_TerrainVerticesPerSide + i0; | /** | ||||
i32 idx1 = j*m_TerrainVerticesPerSide + i1; | * Check if at least one of tiles (a1, b1), (a2, b2) | ||||
u32 &explored = m_ExploredVertices.at(owner); | * is was higher then source | ||||
for (i32 idx = idx0; idx <= idx1; ++idx) | * Using blocking vision map | ||||
{ | */ | ||||
// Increasing from zero to non-zero - move from unexplored/explored to visible+explored | inline bool AreTilesHigher(i32 a1, i32 b1, i32 a2, i32 b2) | ||||
if (counts[idx] == 0) | |||||
{ | { | ||||
i32 i = i0 + idx - idx0; | i32 idx = (b1)*m_TerrainVerticesPerSide + (a1); | ||||
if (!LosIsOffWorld(i, j)) | i32 idx2 = (b2)*m_TerrainVerticesPerSide + (a2); | ||||
return m_RelatHighMap.at(idx) || m_RelatHighMap.at(idx2); | |||||
} | |||||
/** | |||||
* Returns true if tile (i, j) is visible from given starting tile (iFrom, jFrom) | |||||
* Every tile above given height (hFrom) will block vision | |||||
* This is using global visibility map and blocking vision map | |||||
* so correct computation requires to check all tiles closer to source first | |||||
* Source tile is allways marked as visible and has to be checked | |||||
* This is using m_VisionBlocked to know if there was | |||||
* blocking of vision in previous computations | |||||
* Has to be called from single thread | |||||
* @param {idxx} position of (i, j) in visibility map | |||||
*/ | |||||
inline bool IsVisibleTileShowHillSide(i32 iFrom, i32 jFrom, float hFrom, i32 i, i32 j, i32 idxx) | |||||
{ | { | ||||
explored += !(m_LosState[idx] & (LOS_EXPLORED << (2*(owner-1)))); | // Zero: The same tile is allways visible | ||||
m_LosState[idx] |= ((LOS_VISIBLE | LOS_EXPLORED) << (2*(owner-1))); | if (iFrom == i && jFrom == j) { | ||||
m_RelatHighMap.at(idxx) = false; | |||||
return true; | |||||
} | } | ||||
MarkVisibilityDirtyAroundTile(owner, i, j); | // First: Check if we see it by high difference | ||||
float h2 = cTerrain->GetVertexGroundLevel(i, j); | |||||
Not Done Inline Actionsfloat safe ? Stan: float safe ? | |||||
Done Inline Actionsit returns float so yes :) Silier: it returns float so yes :) | |||||
bool blocked = hFrom < h2; | |||||
m_RelatHighMap.at(idxx) = blocked; | |||||
if (blocked) | |||||
m_VisionBlocked = true; | |||||
// Vision was not blocked yet so abort | |||||
if (!m_VisionBlocked) | |||||
return true; | |||||
float h3 = 0; | |||||
// Check distances | |||||
i32 iDistA = std::abs(iFrom - i); | |||||
i32 jDistA = std::abs(jFrom - j); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(i > 0); | |||||
ENSURE(j > 0); | |||||
ENSURE(iFrom > 0); | |||||
ENSURE(jFrom > 0); | |||||
ENSURE(i < m_TerrainVerticesPerSide); | |||||
ENSURE(j < m_TerrainVerticesPerSide); | |||||
ENSURE(iFrom < m_TerrainVerticesPerSide); | |||||
ENSURE(jFrom < m_TerrainVerticesPerSide); | |||||
#endif | |||||
i32 idx; | |||||
// Second: Check if tile before is visible | |||||
// 1) straight vision - check tiles at line (and return right away) | |||||
// 1.1) check on i line | |||||
if (i == iFrom) { | |||||
// 1.1.a) we are down so check to up | |||||
if (j < jFrom) { | |||||
idx = (j + 1)*m_TerrainVerticesPerSide + i; | |||||
if (!m_Vismap.at(idx)) | |||||
return false; | |||||
// tile before is visible | |||||
if (!m_RelatHighMap.at(idx)) | |||||
return true; | |||||
// tile before is blocking vision | |||||
h3 = cTerrain->GetVertexGroundLevel(i, j + 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 1.1.b) we are up so check to down | |||||
if (j > jFrom) { | |||||
idx = (j - 1)*m_TerrainVerticesPerSide + i; | |||||
if (!m_Vismap.at(idx)) | |||||
return false; | |||||
if (!m_RelatHighMap.at(idx)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i, j - 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
} | |||||
// 1.2) check on j line | |||||
else if (j == jFrom) { | |||||
// 1.2.a) we are left so check to right | |||||
if (i < iFrom) { | |||||
idx = j*m_TerrainVerticesPerSide + (i + 1); | |||||
if (!m_Vismap.at(idx)) | |||||
return false; | |||||
if (!m_RelatHighMap.at(idx)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i + 1, j); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 1.2.b) we are right so check to left | |||||
if (i > iFrom) { | |||||
idx = j*m_TerrainVerticesPerSide + (i - 1); | |||||
if (!m_Vismap.at(idx)) | |||||
return false; | |||||
if (!m_RelatHighMap.at(idx)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i - 1, j); | |||||
return h2 - h3 > 1; | |||||
} | |||||
} | |||||
// 2) check diagonals to bottom | |||||
else if (j < jFrom) { | |||||
// 2.1) check diagonals to bottom right | |||||
if (i > iFrom) { | |||||
// 2.1.a) true diagonal | |||||
// - check only next diagonal tile | |||||
if (iDistA == jDistA) { | |||||
idx = (j + 1)*m_TerrainVerticesPerSide + (i - 1); | |||||
if (!m_Vismap.at(idx)) | |||||
return false; | |||||
if (!m_RelatHighMap.at(idx)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i - 1, j + 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 2.1.b) we are under so check to top and top left | |||||
else if (jDistA > iDistA) { | |||||
if (!CheckTilesForVisibility(i, j + 1, i - 1, j + 1)) | |||||
return false; | |||||
// Tiles before are visible | |||||
if (!AreTilesHigher(i, j + 1, i - 1, j + 1)) | |||||
return true; | |||||
// Tiles before are blocking vision | |||||
h3 = cTerrain->GetVertexGroundLevel(i, j + 1); | |||||
if (h2 - h3 <= 1) | |||||
return false; | |||||
h3 = cTerrain->GetVertexGroundLevel(i - 1, j + 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 2.1.c) we are up so check to left and left top | |||||
else if (jDistA < iDistA) { | |||||
if (!CheckTilesForVisibility(i - 1, j, i - 1, j + 1)) | |||||
return false; | |||||
if (!AreTilesHigher(i - 1, j, i - 1, j + 1)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i - 1, j); | |||||
if (h2 - h3 <= 1) | |||||
return false; | |||||
h3 = cTerrain->GetVertexGroundLevel(i - 1, j + 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
} | |||||
// 2.2) check diagonals to bottom left | |||||
else if (i < iFrom) { | |||||
// 2.2.a) true diagonal | |||||
// - check only next diagonal tile | |||||
if (iDistA == jDistA) { | |||||
idx = (j + 1)*m_TerrainVerticesPerSide + (i + 1); | |||||
if (!m_Vismap.at(idx)) | |||||
return false; | |||||
if (!m_RelatHighMap.at(idx)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i + 1, j + 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 2.2.b) we are under so check to top and top right | |||||
else if (jDistA > iDistA) { | |||||
if (!CheckTilesForVisibility(i, j + 1, i + 1, j + 1)) | |||||
return false; | |||||
if (!AreTilesHigher(i, j + 1, i + 1, j + 1)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i, j + 1); | |||||
if (h2 - h3 <= 1) | |||||
return false; | |||||
h3 = cTerrain->GetVertexGroundLevel(i + 1, j + 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 2.2.c) we are up so check to right and right top | |||||
else if (jDistA < iDistA) { | |||||
if (!CheckTilesForVisibility(i + 1, j, i + 1, j + 1)) | |||||
return false; | |||||
if (!AreTilesHigher(i + 1, j, i + 1, j + 1)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i + 1, j); | |||||
if (h2 - h3 <= 1) | |||||
return false; | |||||
h3 = cTerrain->GetVertexGroundLevel(i + 1, j + 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
} | |||||
} | |||||
// 3) check diagonals to top | |||||
else if (j > jFrom) { | |||||
// 3.1) check diagonals to top right | |||||
if (i > iFrom) { | |||||
// 3.1.a) true diagonal | |||||
// - check only next diagonal tile | |||||
if (iDistA == jDistA) { | |||||
idx = (j - 1)*m_TerrainVerticesPerSide + (i - 1); | |||||
if (!m_Vismap.at(idx)) | |||||
return false; | |||||
if (!m_RelatHighMap.at(idx)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i - 1, j - 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 3.1.b) we are up so check down and down left | |||||
else if (jDistA < iDistA) { | |||||
if (!CheckTilesForVisibility(i - 1, j, i - 1, j - 1)) | |||||
return false; | |||||
if (!AreTilesHigher(i - 1, j, i - 1, j - 1)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i - 1, j); | |||||
if (h2 - h3 <= 1) | |||||
return false; | |||||
h3 = cTerrain->GetVertexGroundLevel(i - 1, j - 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 3.1.c) we are under so check to left and left down | |||||
else if (jDistA > iDistA) { | |||||
if (!CheckTilesForVisibility(i, j - 1, i - 1, j - 1)) | |||||
return false; | |||||
if (!AreTilesHigher(i, j - 1, i - 1, j - 1)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i, j - 1); | |||||
if (h2 - h3 <= 1) | |||||
return false; | |||||
h3 = cTerrain->GetVertexGroundLevel(i - 1, j - 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
} | |||||
// 3.2) check diagonals to top left | |||||
else if (i < iFrom) {// | |||||
// 3.2.a) true diagonal | |||||
// - check only next diagonal tile | |||||
if (iDistA == jDistA) { | |||||
idx = (j - 1)*m_TerrainVerticesPerSide + (i + 1); | |||||
if (!m_Vismap.at(idx)) | |||||
return false; | |||||
if (!m_RelatHighMap.at(idx)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i + 1, j - 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 3.2.b) we are down so check to right and down right | |||||
else if (jDistA < iDistA) { | |||||
if (!CheckTilesForVisibility(i + 1, j, i + 1, j - 1)) | |||||
return false; | |||||
if (!AreTilesHigher(i + 1, j, i + 1, j - 1)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i + 1, j); | |||||
if (h2 - h3 <= 1) | |||||
return false; | |||||
h3 = cTerrain->GetVertexGroundLevel(i + 1, j - 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
// 3.2.c) we are up so check to right and right down | |||||
else if (jDistA > iDistA) { | |||||
if (!CheckTilesForVisibility(i, j - 1, i + 1, j - 1)) | |||||
return false; | |||||
if (!AreTilesHigher(i, j - 1, i + 1, j - 1)) | |||||
return true; | |||||
h3 = cTerrain->GetVertexGroundLevel(i, j - 1); | |||||
if (h2 - h3 <= 1) | |||||
return false; | |||||
h3 = cTerrain->GetVertexGroundLevel(i + 1, j - 1); | |||||
return h2 - h3 > 1; | |||||
} | |||||
} | |||||
} | |||||
return true; | |||||
} | } | ||||
ASSERT(counts[idx] < 65535); | /** | ||||
counts[idx] = (u16)(counts[idx] + 1); // ignore overflow; the player should never have 64K units | * Leave as backup if expended visibility checking will be too much for performance | ||||
* @see IsVisibleTileShowHillSide | |||||
*/ | |||||
inline bool IsVisibleTile(i32 iFrom, i32 jFrom, float hFrom, i32 i, i32 j) | |||||
{ | |||||
// Zero: The same tile is allways visible | |||||
if (iFrom == i && jFrom == j) | |||||
return true; | |||||
// First: Check if we see it by height difference | |||||
float h2 = cTerrain->GetVertexGroundLevel(i, j); | |||||
if (hFrom < h2) | |||||
return false; | |||||
// Vision was not blocked yet so abort | |||||
if (!m_VisionBlocked) | |||||
return true; | |||||
// Check distances | |||||
i32 iDistA = std::abs(iFrom - i); | |||||
i32 jDistA = std::abs(jFrom - j); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(i > 0); | |||||
ENSURE(j > 0); | |||||
ENSURE(iFrom > 0); | |||||
ENSURE(jFrom > 0); | |||||
ENSURE(i < m_TerrainVerticesPerSide); | |||||
ENSURE(j < m_TerrainVerticesPerSide); | |||||
ENSURE(iFrom < m_TerrainVerticesPerSide); | |||||
ENSURE(jFrom < m_TerrainVerticesPerSide); | |||||
#endif | |||||
i32 idx; | |||||
// Second: Check if tile before is visible | |||||
// 1) straight vision - check tiles at line (and return right away) | |||||
// 1.1) check on i line | |||||
if (i == iFrom) { | |||||
// 1.1.a) we are down so check to up | |||||
if (j < jFrom) { | |||||
idx = (j + 1)*m_TerrainVerticesPerSide + i; | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
return m_Vismap.at(idx); | |||||
} | } | ||||
// 1.1.b) we are up so check to down | |||||
if (j > jFrom) { | |||||
idx = (j - 1)*m_TerrainVerticesPerSide + i; | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
return m_Vismap.at(idx); | |||||
} | |||||
} | |||||
// 1.2) check on j line | |||||
else if (j == jFrom) { | |||||
// 1.2.a) we are left so check to right | |||||
if (i < iFrom) { | |||||
idx = j*m_TerrainVerticesPerSide + (i + 1); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
return m_Vismap.at(idx); | |||||
} | |||||
// 1.2.b) we are right so check to left | |||||
if (i > iFrom) { | |||||
idx = j*m_TerrainVerticesPerSide + (i - 1); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
return m_Vismap.at(idx); | |||||
} | |||||
} | |||||
// 2) check diagonals to bottom | |||||
else if (j < jFrom) { | |||||
// 2.1) check diagonals to bottom right | |||||
if (i > iFrom) { | |||||
// 2.1.a) true diagonal | |||||
// - check only next diagonal tile | |||||
if (iDistA == jDistA) { | |||||
idx = (j + 1)*m_TerrainVerticesPerSide + (i - 1); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
return m_Vismap.at(idx); | |||||
} | |||||
// 2.1.b) we are under so check to top and top left | |||||
else if (jDistA > iDistA) { | |||||
return CheckTilesForVisibility(i, j + 1, i - 1, j + 1); | |||||
} | |||||
// 2.1.c) we are up so check to left and left top | |||||
else if (jDistA < iDistA) { | |||||
return CheckTilesForVisibility(i - 1, j, i - 1, j + 1); | |||||
} | |||||
} | |||||
// 2.2) check diagonals to bottom left | |||||
else if (i < iFrom) { | |||||
// 2.2.a) true diagonal | |||||
// - check only next diagonal tile | |||||
if (iDistA == jDistA) { | |||||
idx = (j + 1)*m_TerrainVerticesPerSide + (i + 1); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
return m_Vismap.at(idx); | |||||
} | |||||
// 2.2.b) we are under so check to top and top right | |||||
else if (jDistA > iDistA) { | |||||
return CheckTilesForVisibility(i, j + 1, i + 1, j + 1); | |||||
} | |||||
// 2.2.c) we are up so check to right and right top | |||||
else if (jDistA < iDistA) { | |||||
return CheckTilesForVisibility(i + 1, j, i + 1, j + 1); | |||||
} | |||||
} | |||||
} | |||||
// 3) check diagonals to top | |||||
else if (j > jFrom) { | |||||
// 3.1) check diagonals to top right | |||||
if (i > iFrom) { | |||||
// 3.1.a) true diagonal | |||||
// - check only next diagonal tile | |||||
if (iDistA == jDistA) { | |||||
idx = (j - 1)*m_TerrainVerticesPerSide + (i - 1); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
return m_Vismap.at(idx); | |||||
} | |||||
// 3.1.b) we are up so check down and down left | |||||
else if (jDistA < iDistA) { | |||||
return CheckTilesForVisibility(i - 1, j, i - 1, j - 1); | |||||
} | |||||
// 3.1.c) we are under so check to left and left down | |||||
else if (jDistA > iDistA) { | |||||
return CheckTilesForVisibility(i, j - 1, i - 1, j - 1); | |||||
} | |||||
} | |||||
// 3.2) check diagonals to top left | |||||
else if (i < iFrom) {// | |||||
// 3.2.a) true diagonal | |||||
// - check only next diagonal tile | |||||
if (iDistA == jDistA) { | |||||
idx = (j - 1)*m_TerrainVerticesPerSide + (i + 1); | |||||
#if DEBUG_VIS_COMP | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
return m_Vismap.at(idx); | |||||
} | |||||
// 3.2.b) we are down so check to right and down right | |||||
else if (jDistA < iDistA) { | |||||
return CheckTilesForVisibility(i + 1, j, i + 1, j - 1); | |||||
} | |||||
// 3.2.c) we are up so check to right and right down | |||||
else if (jDistA > iDistA) { | |||||
return CheckTilesForVisibility(i, j - 1, i + 1, j - 1); | |||||
} | |||||
} | |||||
} | |||||
return true; | |||||
} | } | ||||
/** | /** | ||||
* Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive). | * Check if tile is visible from given tile taking direction into account | ||||
* This is updating visibility map shared among all tiles so make sure | |||||
* this is called only from one thread | |||||
* @param iFrom position from starting tile | |||||
* @param jFrom position from starting tile | |||||
* @param hFrom height tiles higher than this will block visibility | |||||
* @param i position of tile we are checking | |||||
* @param j position of tile we are checking | |||||
* @param idx index of checking position in visibility map | |||||
* @return true iff given tile is visible | |||||
*/ | |||||
inline bool IsVisibleTileMark(i32 iFrom, i32 jFrom, float hFrom, i32 i, i32 j, i32 idx) | |||||
{ | |||||
#if DEBUG_VIS_COMP | |||||
if (m_VisComputedBefore.empty()) { | |||||
m_VisComputedBefore.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | |||||
ENSURE(!m_VisComputedBefore.at(1)); | |||||
} | |||||
#endif | |||||
// not visible if off world | |||||
// if is of world do not bother updating visibility map | |||||
// there will be allways false | |||||
if (LosIsOffWorld(i, j)) { | |||||
#if DEBUG_VIS_COMP | |||||
if (i > -1 && j > -1 && idx < m_VisComputedBefore.size()) { | |||||
m_VisComputedBefore.at(idx) = true; | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
} | |||||
#endif | |||||
return false; | |||||
} | |||||
// m_Vismap.at(idx) = IsVisibleTile(iFrom, jFrom, hFrom, i, j); | |||||
m_Vismap.at(idx) = IsVisibleTileShowHillSide(iFrom, jFrom, hFrom, i, j, idx); | |||||
#if DEBUG_VIS_COMP | |||||
m_VisComputedBefore.at(idx) = true; | |||||
ENSURE(m_VisComputedBefore.at(idx)); | |||||
#endif | |||||
return m_Vismap.at(idx); | |||||
} | |||||
/** | |||||
* Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive) | |||||
* Looking from tile (x, y) from height h | |||||
* @see IsVisibleTileMark | |||||
*/ | */ | ||||
inline void LosRemoveStripHelper(u8 owner, i32 i0, i32 i1, i32 j, u16* counts) | inline void LosManageStripHelper(bool add, u8 owner, i32 i0, i32 i1, i32 j, u16* counts, float h, i32 x, i32 y) | ||||
{ | { | ||||
if (i1 < i0) | if (i1 < i0) | ||||
return; | return; | ||||
i32 idx0 = j*m_TerrainVerticesPerSide + i0; | i32 idx0 = j*m_TerrainVerticesPerSide + i0; | ||||
i32 idx1 = j*m_TerrainVerticesPerSide + i1; | i32 idx1 = j*m_TerrainVerticesPerSide + i1; | ||||
u32 &explored = m_ExploredVertices.at(owner); | |||||
for (i32 idx = idx0; idx <= idx1; ++idx) | for (i32 idx = idx0; idx <= idx1; ++idx) | ||||
{ | { | ||||
ASSERT(counts[idx] > 0); | i32 i = i0 + idx - idx0; | ||||
counts[idx] = (u16)(counts[idx] - 1); | if (!IsVisibleTileMark(x, y, h, i, j, idx)) | ||||
continue; | |||||
if (add) { | |||||
// Increasing from zero to non-zero - move from unexplored/explored to visible+explored | |||||
if (counts[idx] == 0) | |||||
{ | |||||
explored += !(m_LosState[idx] & (LOS_EXPLORED << (2 * (owner - 1)))); | |||||
m_LosState[idx] |= ((LOS_VISIBLE | LOS_EXPLORED) << (2 * (owner - 1))); | |||||
MarkVisibilityDirtyAroundTile(owner, i, j); | |||||
} | |||||
ENSURE(counts[idx] < 65535); | |||||
counts[idx] = (u16)(counts[idx] + 1); // ignore overflow; the player should never have 64K units | |||||
} | |||||
else { | |||||
if (counts[idx]) { | |||||
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) | |||||
m_LosState[idx] &= ~(LOS_VISIBLE << (2*(owner-1))); | m_LosState[idx] &= ~(LOS_VISIBLE << (2 * (owner - 1))); | ||||
i32 i = i0 + idx - idx0; | |||||
MarkVisibilityDirtyAroundTile(owner, i, j); | MarkVisibilityDirtyAroundTile(owner, i, j); | ||||
} | } | ||||
ENSURE(counts[idx] > -1); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
/** | |||||
* Inverted function compared to LosManagerStripHelper iterating from (i, j0) to (i, j1) | |||||
* @see IsVisibleTileMark | |||||
*/ | |||||
inline void LosManageStripHelperJ(bool add, u8 owner, i32 j0, i32 j1, i32 i, u16* counts, float h, i32 x, i32 y) | |||||
{ | |||||
for (i32 j = j0; j <= j1; ++j) | |||||
LosManageStripHelper(add, owner, i, i, j, counts, h, x, y); | |||||
} | } | ||||
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; | ||||
Show All 15 Lines | inline void MarkVisibilityDirtyAroundTile(u8 owner, i32 i, i32 j) | ||||
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. | ||||
* Provide vision blocking by terrain. | |||||
* Height (eH) is added to ground/water level. | |||||
*/ | */ | ||||
template<bool adding> | template<bool adding> void LosUpdateHelper(u8 owner, entity_pos_t visionRange, CFixedVector2D pos, u16 eH) | ||||
void LosUpdateHelper(u8 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"); | ||||
std::vector<u16>& counts = m_LosPlayerCounts.at(owner); | std::vector<u16>& counts = m_LosPlayerCounts.at(owner); | ||||
// Lazy initialisation of counts: | // Lazy initialisation of counts: | ||||
if (counts.empty()) | if (counts.empty()) | ||||
counts.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | counts.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | ||||
u16* countsData = &counts[0]; | u16* countsData = &counts[0]; | ||||
// Compute the circular region as a series of strips. | // Create empty vision map if needed | ||||
// Rather than quantise pos to vertexes, we do more precise sub-tile computations | if (m_Vismap.empty()) | ||||
// to get smoother behaviour as a unit moves rather than jumping a whole tile | m_Vismap.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | ||||
// at once. | // Create empty blocking map if needed | ||||
// To avoid the cost of sqrt when computing the outline of the circle, | if (m_RelatHighMap.empty()) | ||||
// we loop from the bottom to the top and estimate the width of the current | m_RelatHighMap.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | ||||
// strip based on the previous strip, then adjust each end of the strip | |||||
// inwards or outwards until it's the widest that still falls within the circle. | |||||
// Compute top/bottom coordinates, and clamp to exclude the 1-tile border around the map | |||||
// (so that we never render the sharp edge of the map) | |||||
i32 j0 = ((pos.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity(); | |||||
i32 j1 = ((pos.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(); | |||||
i32 j0clamp = std::max(j0, 1); | |||||
i32 j1clamp = std::min(j1, m_TerrainVerticesPerSide-2); | |||||
// Reset this as we are starting new detection | |||||
m_VisionBlocked = false; | |||||
// Translate world coordinates into fractional tile-space coordinates | // Translate world coordinates into fractional tile-space coordinates | ||||
entity_pos_t x = pos.X / (int)TERRAIN_TILE_SIZE; | entity_pos_t x = pos.X / (int)TERRAIN_TILE_SIZE; | ||||
entity_pos_t y = pos.Y / (int)TERRAIN_TILE_SIZE; | entity_pos_t y = pos.Y / (int)TERRAIN_TILE_SIZE; | ||||
entity_pos_t r = visionRange / (int)TERRAIN_TILE_SIZE; | entity_pos_t r = visionRange / (int)TERRAIN_TILE_SIZE; | ||||
entity_pos_t r2 = r.Square(); | entity_pos_t r2 = r.Square(); | ||||
// Compute the integers on either side of x | |||||
i32 xfloor = (x - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); | i32 xfloor = (x - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); | ||||
i32 xceil = (x + entity_pos_t::Epsilon()).ToInt_RoundToInfinity(); | i32 yfloor = (y - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); | ||||
i32 rfloor = (r - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); | |||||
// Initialise the strip (i0, i1) to a rough guess | #if DEBUG_VIS_COMP | ||||
i32 i0 = xfloor; | if (m_VisComputedBefore.empty()) { | ||||
i32 i1 = xceil; | m_VisComputedBefore.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | ||||
ENSURE(!m_VisComputedBefore.at(1)); | |||||
} | |||||
#if DEBUG_CLEAR_VIS_CHECK | |||||
std::fill(m_VisComputedBefore.begin(), m_VisComputedBefore.end(), false); | |||||
ENSURE(m_VisComputedBefore.size() == m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | |||||
#endif | |||||
#endif | |||||
// Do nothing if of world | |||||
if (LosIsOffWorld(xfloor, yfloor)) { | |||||
#if DEBUG_VIS_COMP | |||||
i32 idxd = yfloor*m_TerrainVerticesPerSide + xfloor; | |||||
m_VisComputedBefore.at(idxd) = true; | |||||
ENSURE(m_VisComputedBefore.at(idxd)); | |||||
#endif | |||||
return; | |||||
} | |||||
for (i32 j = j0clamp; j <= j1clamp; ++j) | // Get height of terrain/water at entity position | ||||
{ | float h = cTerrain->GetVertexGroundLevel(xfloor, yfloor); | ||||
// Adjust i0 and i1 to be the outermost values that don't exceed | CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity()); | ||||
// the circle's radius (i.e. require dy^2 + dx^2 <= r^2). | if (cmpWaterManager) { | ||||
// When moving the points inwards, clamp them to xceil+1 or xfloor-1 | float wl = cmpWaterManager->GetWaterLevel(pos.X, pos.Y).ToFloat(); | ||||
// so they don't accidentally shoot off in the wrong direction forever. | if (wl > h) | ||||
h = wl; | |||||
} | |||||
// Add entity heigth | |||||
h = h + eH; | |||||
i32 i0; | |||||
i32 i1; | |||||
i32 ifrom; | |||||
i32 ito; | |||||
i32 jfrom; | |||||
i32 jto; | |||||
// Direction of incrementation of square | |||||
i32 ipp = 1; | |||||
// Guess how many tiles we should check at squre side | |||||
// Incremented by ipp at each step | |||||
i32 ip = 0; | |||||
// incrementally make squares larger | |||||
for (i32 range = 0; range < rfloor*2 + 1; ++range) { | |||||
// This defines square with side 2*range | |||||
// and centre at entity position | |||||
i0 = xfloor - range; | |||||
i1 = xfloor + range; | |||||
i32 j = yfloor + range; | |||||
i32 j3 = yfloor - range; | |||||
// This defines which tiles at given | |||||
// square we have to check | |||||
i32 ifrom = xfloor - ip; | |||||
i32 ito = xfloor + ip; | |||||
// we cut corners so we do not check twice | |||||
i32 jfrom = yfloor - ip + 1; | |||||
i32 jto = yfloor + ip - 1; | |||||
entity_pos_t dy = entity_pos_t::FromInt(j) - y; | if (ipp > 0) { | ||||
entity_pos_t dy = entity_pos_t::FromInt(range + 1); | |||||
entity_pos_t dy2 = dy.Square(); | entity_pos_t dy2 = dy.Square(); | ||||
while (dy2 + (entity_pos_t::FromInt(i0-1) - x).Square() <= r2) | // We have filled square and half length of diagonal is larger | ||||
--i0; | // than vision range so invert direction | ||||
while (i0 < xceil && dy2 + (entity_pos_t::FromInt(i0) - x).Square() > r2) | if (dy2 + dy2 > r2) | ||||
++i0; | ipp = -1; | ||||
while (dy2 + (entity_pos_t::FromInt(i1+1) - x).Square() <= r2) | // Discard some tiles if they are out of vision range allready | ||||
++i1; | while (dy2 + (entity_pos_t::FromInt(ifrom) - x).Square() > r2) | ||||
while (i1 > xfloor && dy2 + (entity_pos_t::FromInt(i1) - x).Square() > r2) | ifrom++; | ||||
--i1; | while (dy2 + (entity_pos_t::FromInt(ito) - x).Square() > r2) | ||||
ito--; | |||||
#if DEBUG_RANGE_MANAGER_BOUNDS | while (dy2 + (entity_pos_t::FromInt(jfrom) - y).Square() > r2) | ||||
if (i0 <= i1) | jfrom++; | ||||
{ | while (dy2 + (entity_pos_t::FromInt(jto) - y).Square() > r2) | ||||
ENSURE(dy2 + (entity_pos_t::FromInt(i0) - x).Square() <= r2); | jto--; | ||||
ENSURE(dy2 + (entity_pos_t::FromInt(i1) - x).Square() <= r2); | |||||
} | } | ||||
ENSURE(dy2 + (entity_pos_t::FromInt(i0 - 1) - x).Square() > r2); | else if (ipp < 0) { | ||||
ENSURE(dy2 + (entity_pos_t::FromInt(i1 + 1) - x).Square() > r2); | entity_pos_t dy = entity_pos_t::FromInt(range); | ||||
#endif | entity_pos_t dy2 = dy.Square(); | ||||
// We went out of vision range so abort | |||||
if (dy2 > r2) | |||||
break; | |||||
// We might overshoot vision range so make it more looks like circle | |||||
while (dy2 + (entity_pos_t::FromInt(ifrom) - x).Square() > r2) | |||||
ifrom++; | |||||
while (dy2 + (entity_pos_t::FromInt(ito) - x).Square() > r2) | |||||
ito--; | |||||
while (dy2 + (entity_pos_t::FromInt(jfrom) - y).Square() > r2) | |||||
jfrom++; | |||||
while (dy2 + (entity_pos_t::FromInt(jto) - y).Square() > r2) | |||||
jto--; | |||||
} | |||||
ip = ip + ipp; | |||||
// Clamp the strip to exclude the 1-tile border, | // Keep square envelope cordinates in map | ||||
// then add or remove the strip as requested | |||||
i32 i0clamp = std::max(i0, 1); | i32 i0clamp = std::max(i0, 1); | ||||
i32 i1clamp = std::min(i1, m_TerrainVerticesPerSide-2); | i32 i1clamp = std::min(i1, m_TerrainVerticesPerSide - 2); | ||||
if (adding) | i32 jclamp = std::max(j, 1); | ||||
LosAddStripHelper(owner, i0clamp, i1clamp, j, countsData); | i32 j3clamp = std::min(j3, m_TerrainVerticesPerSide - 2); | ||||
else | // The same for side cuts | ||||
LosRemoveStripHelper(owner, i0clamp, i1clamp, j, countsData); | ifrom = std::max(ifrom, 1); | ||||
ito = std::min(ito, m_TerrainVerticesPerSide - 2); | |||||
jfrom = std::max(jfrom, 1); | |||||
jto = std::min(jto, m_TerrainVerticesPerSide - 2); | |||||
// First check top line | |||||
LosManageStripHelper(adding, owner, ifrom, ito, jclamp, countsData, h, xfloor, yfloor); | |||||
// Check bottom line, special case created by clamp | |||||
if (jclamp != j3clamp) | |||||
LosManageStripHelper(adding, owner, ifrom, ito, j3clamp, countsData, h, xfloor, yfloor); | |||||
// Make sure jfrom is not greather than jto because of cutting corners | |||||
if (jfrom <= jto) { | |||||
// Now check remaining tiles at left side of square | |||||
LosManageStripHelperJ(adding, owner, jfrom, jto, i0clamp, countsData, h, xfloor, yfloor); | |||||
// At last do the check for tiles at right side of square, special case by clamp | |||||
if (i0clamp != i1clamp) | |||||
LosManageStripHelperJ(adding, owner, jfrom, jto, i1clamp, countsData, h, xfloor, yfloor); | |||||
} | } | ||||
} | |||||
/** | |||||
* Update the LOS state of tiles within a given circular range, | |||||
* by removing visibility around the 'from' position | |||||
* and then adding visibility around the 'to' position. | |||||
*/ | |||||
void LosUpdateHelperIncremental(u8 owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | |||||
{ | |||||
if (m_TerrainVerticesPerSide == 0) // do nothing if not initialised yet | |||||
return; | |||||
PROFILE("LosUpdateHelperIncremental"); | |||||
std::vector<u16>& counts = m_LosPlayerCounts.at(owner); | // We have no more tiles to check | ||||
if (ip < 1) | |||||
// Lazy initialisation of counts: | break; | ||||
if (counts.empty()) | |||||
counts.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide); | |||||
u16* countsData = &counts[0]; | |||||
// See comments in LosUpdateHelper. | |||||
// This does exactly the same, except computing the strips for | |||||
// both circles simultaneously. | |||||
// (The idea is that the circles will be heavily overlapping, | |||||
// so we can compute the difference between the removed/added strips | |||||
// and only have to touch tiles that have a net change.) | |||||
i32 j0_from = ((from.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity(); | |||||
i32 j1_from = ((from.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(); | |||||
i32 j0_to = ((to.Y - visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToInfinity(); | |||||
i32 j1_to = ((to.Y + visionRange)/(int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(); | |||||
i32 j0clamp = std::max(std::min(j0_from, j0_to), 1); | |||||
i32 j1clamp = std::min(std::max(j1_from, j1_to), m_TerrainVerticesPerSide-2); | |||||
entity_pos_t x_from = from.X / (int)TERRAIN_TILE_SIZE; | |||||
entity_pos_t y_from = from.Y / (int)TERRAIN_TILE_SIZE; | |||||
entity_pos_t x_to = to.X / (int)TERRAIN_TILE_SIZE; | |||||
entity_pos_t y_to = to.Y / (int)TERRAIN_TILE_SIZE; | |||||
entity_pos_t r = visionRange / (int)TERRAIN_TILE_SIZE; | |||||
entity_pos_t r2 = r.Square(); | |||||
i32 xfloor_from = (x_from - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); | |||||
i32 xceil_from = (x_from + entity_pos_t::Epsilon()).ToInt_RoundToInfinity(); | |||||
i32 xfloor_to = (x_to - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity(); | |||||
i32 xceil_to = (x_to + entity_pos_t::Epsilon()).ToInt_RoundToInfinity(); | |||||
i32 i0_from = xfloor_from; | |||||
i32 i1_from = xceil_from; | |||||
i32 i0_to = xfloor_to; | |||||
i32 i1_to = xceil_to; | |||||
for (i32 j = j0clamp; j <= j1clamp; ++j) | |||||
{ | |||||
entity_pos_t dy_from = entity_pos_t::FromInt(j) - y_from; | |||||
entity_pos_t dy2_from = dy_from.Square(); | |||||
while (dy2_from + (entity_pos_t::FromInt(i0_from-1) - x_from).Square() <= r2) | |||||
--i0_from; | |||||
while (i0_from < xceil_from && dy2_from + (entity_pos_t::FromInt(i0_from) - x_from).Square() > r2) | |||||
++i0_from; | |||||
while (dy2_from + (entity_pos_t::FromInt(i1_from+1) - x_from).Square() <= r2) | |||||
++i1_from; | |||||
while (i1_from > xfloor_from && dy2_from + (entity_pos_t::FromInt(i1_from) - x_from).Square() > r2) | |||||
--i1_from; | |||||
entity_pos_t dy_to = entity_pos_t::FromInt(j) - y_to; | |||||
entity_pos_t dy2_to = dy_to.Square(); | |||||
while (dy2_to + (entity_pos_t::FromInt(i0_to-1) - x_to).Square() <= r2) | |||||
--i0_to; | |||||
while (i0_to < xceil_to && dy2_to + (entity_pos_t::FromInt(i0_to) - x_to).Square() > r2) | |||||
++i0_to; | |||||
while (dy2_to + (entity_pos_t::FromInt(i1_to+1) - x_to).Square() <= r2) | |||||
++i1_to; | |||||
while (i1_to > xfloor_to && dy2_to + (entity_pos_t::FromInt(i1_to) - x_to).Square() > r2) | |||||
--i1_to; | |||||
#if DEBUG_RANGE_MANAGER_BOUNDS | |||||
if (i0_from <= i1_from) | |||||
{ | |||||
ENSURE(dy2_from + (entity_pos_t::FromInt(i0_from) - x_from).Square() <= r2); | |||||
ENSURE(dy2_from + (entity_pos_t::FromInt(i1_from) - x_from).Square() <= r2); | |||||
} | |||||
ENSURE(dy2_from + (entity_pos_t::FromInt(i0_from - 1) - x_from).Square() > r2); | |||||
ENSURE(dy2_from + (entity_pos_t::FromInt(i1_from + 1) - x_from).Square() > r2); | |||||
if (i0_to <= i1_to) | |||||
{ | |||||
ENSURE(dy2_to + (entity_pos_t::FromInt(i0_to) - x_to).Square() <= r2); | |||||
ENSURE(dy2_to + (entity_pos_t::FromInt(i1_to) - x_to).Square() <= r2); | |||||
} | |||||
ENSURE(dy2_to + (entity_pos_t::FromInt(i0_to - 1) - x_to).Square() > r2); | |||||
ENSURE(dy2_to + (entity_pos_t::FromInt(i1_to + 1) - x_to).Square() > r2); | |||||
#endif | |||||
// Check whether this strip moved at all | |||||
if (!(i0_to == i0_from && i1_to == i1_from)) | |||||
{ | |||||
i32 i0clamp_from = std::max(i0_from, 1); | |||||
i32 i1clamp_from = std::min(i1_from, m_TerrainVerticesPerSide-2); | |||||
i32 i0clamp_to = std::max(i0_to, 1); | |||||
i32 i1clamp_to = std::min(i1_to, m_TerrainVerticesPerSide-2); | |||||
// Check whether one strip is negative width, | |||||
// and we can just add/remove the entire other strip | |||||
if (i1clamp_from < i0clamp_from) | |||||
{ | |||||
LosAddStripHelper(owner, i0clamp_to, i1clamp_to, j, countsData); | |||||
} | |||||
else if (i1clamp_to < i0clamp_to) | |||||
{ | |||||
LosRemoveStripHelper(owner, i0clamp_from, i1clamp_from, j, countsData); | |||||
} | |||||
else | |||||
{ | |||||
// There are four possible regions of overlap between the two strips | |||||
// (remove before add, remove after add, add before remove, add after remove). | |||||
// Process each of the regions as its own strip. | |||||
// (If this produces negative-width strips then they'll just get ignored | |||||
// which is fine.) | |||||
// (If the strips don't actually overlap (which is very rare with normal unit | |||||
// movement speeds), the region between them will be both added and removed, | |||||
// so we have to do the add first to avoid overflowing to -1 and triggering | |||||
// assertion failures.) | |||||
LosAddStripHelper(owner, i0clamp_to, i0clamp_from-1, j, countsData); | |||||
LosAddStripHelper(owner, i1clamp_from+1, i1clamp_to, j, countsData); | |||||
LosRemoveStripHelper(owner, i0clamp_from, i0clamp_to-1, 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, u16 eH) | ||||
{ | { | ||||
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | ||||
return; | return; | ||||
LosUpdateHelper<true>((u8)owner, visionRange, pos); | LosUpdateHelper<true>((u8)owner, visionRange, pos, eH); | ||||
} | } | ||||
void SharingLosAdd(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos) | void SharingLosAdd(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos, u16 eH) | ||||
{ | { | ||||
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 < MAX_LOS_PLAYER_ID+1; ++i) | ||||
if (HasVisionSharing(visionSharing, i)) | if (HasVisionSharing(visionSharing, i)) | ||||
LosAdd(i, visionRange, pos); | LosAdd(i, visionRange, pos, eH); | ||||
} | } | ||||
void LosRemove(player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos) | void LosRemove(player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos, u16 eH) | ||||
{ | { | ||||
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | ||||
return; | return; | ||||
LosUpdateHelper<false>((u8)owner, visionRange, pos); | LosUpdateHelper<false>((u8)owner, visionRange, pos, eH); | ||||
} | } | ||||
void SharingLosRemove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos) | void SharingLosRemove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos, u16 eH) | ||||
{ | { | ||||
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 < MAX_LOS_PLAYER_ID+1; ++i) | ||||
if (HasVisionSharing(visionSharing, i)) | if (HasVisionSharing(visionSharing, i)) | ||||
LosRemove(i, visionRange, pos); | LosRemove(i, visionRange, pos, eH); | ||||
} | } | ||||
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, u16 eH) | ||||
{ | { | ||||
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID) | ||||
return; | return; | ||||
if ((from - to).CompareLength(visionRange) > 0) | LosUpdateHelper<false>((u8)owner, visionRange, from, eH); | ||||
{ | LosUpdateHelper<true>((u8)owner, visionRange, to, eH); | ||||
// If it's a very large move, then simply remove and add to the new position | |||||
LosUpdateHelper<false>((u8)owner, visionRange, from); | |||||
LosUpdateHelper<true>((u8)owner, visionRange, to); | |||||
} | |||||
else | |||||
// Otherwise use the version optimised for mostly-overlapping circles | |||||
LosUpdateHelperIncremental((u8)owner, visionRange, from, to); | |||||
} | } | ||||
void SharingLosMove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to) | void SharingLosMove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to, u16 eH) | ||||
{ | { | ||||
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 < MAX_LOS_PLAYER_ID+1; ++i) | ||||
if (HasVisionSharing(visionSharing, i)) | if (HasVisionSharing(visionSharing, i)) | ||||
LosMove(i, visionRange, from, to); | LosMove(i, visionRange, from, to, eH); | ||||
} | } | ||||
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) | ||||
Not Done Inline ActionsRange loop maybe. Stan: Range loop maybe. | |||||
Done Inline Actionshere i am not sure Silier: here i am not sure | |||||
if (m_LosState[j*m_TerrainVerticesPerSide + i] & (LOS_EXPLORED << (2*((*playerIt)-1)))) | if (m_LosState[j*m_TerrainVerticesPerSide + i] & (LOS_EXPLORED << (2*((*playerIt)-1)))) | ||||
{ | { | ||||
exploredVertices += 1; | exploredVertices += 1; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return exploredVertices * 100 / m_TotalInworldVertices; | return exploredVertices * 100 / m_TotalInworldVertices; | ||||
} | } | ||||
}; | }; | ||||
REGISTER_COMPONENT_TYPE(RangeManager) | REGISTER_COMPONENT_TYPE(RangeManager) |
Was it supposed to be removed :P