# Health decay for buildings not in a civ center's territory.
This was SVN commit r10034.
This commit is contained in:
parent
6b26820090
commit
308cb26dd4
@ -15,6 +15,6 @@ TEX los, fragment.texcoord[1], texture[2], 2D;
|
||||
MUL result.color.rgb, color, los.a;
|
||||
|
||||
// Use alpha from base texture
|
||||
MOV result.color.a, base.a;
|
||||
MUL result.color.a, objectColor.a, base.a;
|
||||
|
||||
END
|
||||
|
@ -0,0 +1,74 @@
|
||||
function TerritoryDecay() {}
|
||||
|
||||
TerritoryDecay.prototype.Schema =
|
||||
"<element name='HealthDecayRate' a:help='Decay rate in hitpoints per second'>" +
|
||||
"<data type='positiveInteger'/>" +
|
||||
"</element>";
|
||||
|
||||
TerritoryDecay.prototype.Init = function()
|
||||
{
|
||||
this.timer = undefined;
|
||||
};
|
||||
|
||||
TerritoryDecay.prototype.IsConnected = function()
|
||||
{
|
||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
if (!cmpPosition || !cmpPosition.IsInWorld())
|
||||
return false;
|
||||
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
if (!cmpOwnership)
|
||||
return false;
|
||||
|
||||
var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
|
||||
if (!cmpTerritoryManager)
|
||||
return false;
|
||||
|
||||
var pos = cmpPosition.GetPosition2D();
|
||||
var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
|
||||
if (tileOwner != cmpOwnership.GetOwner())
|
||||
return false;
|
||||
// TODO: this should probably use the same territory restriction
|
||||
// logic as BuildRestrictions, to handle allies etc
|
||||
|
||||
return cmpTerritoryManager.IsConnected(pos.x, pos.y);
|
||||
};
|
||||
|
||||
TerritoryDecay.prototype.UpdateDecayState = function()
|
||||
{
|
||||
var connected = this.IsConnected();
|
||||
if (!connected && !this.timer)
|
||||
{
|
||||
// Start decaying
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
this.timer = cmpTimer.SetInterval(this.entity, IID_TerritoryDecay, "Decay", 1000, 1000, {});
|
||||
}
|
||||
else if (connected && this.timer)
|
||||
{
|
||||
// Stop decaying
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
cmpTimer.CancelTimer(this.timer);
|
||||
this.timer = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
TerritoryDecay.prototype.OnTerritoriesChanged = function(msg)
|
||||
{
|
||||
this.UpdateDecayState();
|
||||
};
|
||||
|
||||
TerritoryDecay.prototype.OnOwnershipChanged = function(msg)
|
||||
{
|
||||
this.UpdateDecayState();
|
||||
};
|
||||
|
||||
TerritoryDecay.prototype.Decay = function()
|
||||
{
|
||||
var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
|
||||
if (!cmpHealth)
|
||||
return; // error
|
||||
|
||||
cmpHealth.Reduce(+this.template.HealthDecayRate);
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_TerritoryDecay, "TerritoryDecay", TerritoryDecay);
|
@ -82,7 +82,11 @@ Timer.prototype.OnUpdate = function(msg)
|
||||
|
||||
var cmp = Engine.QueryInterface(t[0], t[1]);
|
||||
if (!cmp)
|
||||
continue; // the entity was probably destroyed
|
||||
{
|
||||
// The entity was probably destroyed; clean up the timer
|
||||
delete this.timers[id];
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
var lateness = this.time - t[3];
|
||||
|
@ -0,0 +1 @@
|
||||
Engine.RegisterInterface("TerritoryDecay");
|
@ -44,6 +44,7 @@
|
||||
<Static width="26.0" depth="30.0"/>
|
||||
</Obstruction>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>40</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -44,6 +44,7 @@
|
||||
<Static width="28.0" depth="20.0"/>
|
||||
</Obstruction>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>40</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -44,6 +44,7 @@
|
||||
<Static width="26.0" depth="10.5"/>
|
||||
</Obstruction>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>36</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -24,6 +24,7 @@
|
||||
<Selectable/>
|
||||
<TerritoryInfluence>
|
||||
<OverrideCost>64</OverrideCost>
|
||||
<Root>false</Root>
|
||||
<Weight>0</Weight>
|
||||
<Radius>0</Radius>
|
||||
</TerritoryInfluence>
|
||||
|
@ -24,6 +24,7 @@
|
||||
<Selectable/>
|
||||
<TerritoryInfluence>
|
||||
<OverrideCost>0</OverrideCost>
|
||||
<Root>false</Root>
|
||||
<Weight>0</Weight>
|
||||
<Radius>0</Radius>
|
||||
</TerritoryInfluence>
|
||||
|
@ -66,6 +66,9 @@
|
||||
<BarHeight>0.6</BarHeight>
|
||||
<HeightOffset>12.0</HeightOffset>
|
||||
</StatusBars>
|
||||
<TerritoryDecay>
|
||||
<HealthDecayRate>20</HealthDecayRate>
|
||||
</TerritoryDecay>
|
||||
<Vision>
|
||||
<Range>40</Range>
|
||||
<RetainInFog>true</RetainInFog>
|
||||
|
@ -76,6 +76,7 @@
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Root>true</Root>
|
||||
<Radius>180</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -45,6 +45,7 @@
|
||||
<HeightOffset>8.0</HeightOffset>
|
||||
</StatusBars>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>20</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -51,6 +51,7 @@
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>40</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -66,6 +66,7 @@
|
||||
<HeightOffset>21.0</HeightOffset>
|
||||
</StatusBars>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>32</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -42,6 +42,7 @@
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>20</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -38,6 +38,7 @@
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>40</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -42,6 +42,7 @@
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>20</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -46,6 +46,7 @@
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>60</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -62,6 +62,7 @@
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>100</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -20,6 +20,7 @@
|
||||
<Static width="24.0" depth="24.0"/>
|
||||
</Obstruction>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>40</Radius>
|
||||
<Weight>65536</Weight>
|
||||
</TerritoryInfluence>
|
||||
|
@ -192,7 +192,7 @@ void CTerritoryTexture::GenerateBitmap(const Grid<u8>& territories, u8* bitmap,
|
||||
{
|
||||
for (ssize_t i = 0; i < w; ++i)
|
||||
{
|
||||
u8 val = territories.get(i, j);
|
||||
u8 val = territories.get(i, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK;
|
||||
|
||||
CColor color(1, 0, 1, 1);
|
||||
if (val < colors.size())
|
||||
@ -202,14 +202,10 @@ void CTerritoryTexture::GenerateBitmap(const Grid<u8>& territories, u8* bitmap,
|
||||
*p++ = (int)(color.g*255.f);
|
||||
*p++ = (int)(color.r*255.f);
|
||||
|
||||
if ((i > 0 && territories.get(i-1, j) != val)
|
||||
|| (i < w-1 && territories.get(i+1, j) != val)
|
||||
|| (j > 0 && territories.get(i, j-1) != val)
|
||||
|| (j < h-1 && territories.get(i, j+1) != val)
|
||||
// || (i > 0 && j > 0 && territories.get(i-1, j-1) != val)
|
||||
// || (i < w-1 && j > 0 && territories.get(i+1, j-1) != val)
|
||||
// || (i > 0 && j > h-1 && territories.get(i-1, j+1) != val)
|
||||
// || (i < w-1 && j < h-1 && territories.get(i+1, j+1) != val)
|
||||
if ((i > 0 && (territories.get(i-1, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val)
|
||||
|| (i < w-1 && (territories.get(i+1, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val)
|
||||
|| (j > 0 && (territories.get(i, j-1) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val)
|
||||
|| (j < h-1 && (territories.get(i, j+1) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK) != val)
|
||||
)
|
||||
{
|
||||
*p++ = alphaMax;
|
||||
|
@ -286,6 +286,19 @@ public:
|
||||
int32_t i0, j0, i1, j1; // inclusive lower bound, exclusive upper bound, in tiles
|
||||
};
|
||||
|
||||
/**
|
||||
* Sent when territory assignments have changed.
|
||||
*/
|
||||
class CMessageTerritoriesChanged : public CMessage
|
||||
{
|
||||
public:
|
||||
DEFAULT_MESSAGE_IMPL(TerritoriesChanged)
|
||||
|
||||
CMessageTerritoriesChanged()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sent by CCmpRangeManager at most once per turn, when an active range query
|
||||
* has had matching units enter/leave the range since the last RangeUpdate.
|
||||
|
@ -45,6 +45,7 @@ MESSAGE(PositionChanged)
|
||||
MESSAGE(MotionChanged)
|
||||
MESSAGE(RangeUpdate)
|
||||
MESSAGE(TerrainChanged)
|
||||
MESSAGE(TerritoriesChanged)
|
||||
MESSAGE(PathResult)
|
||||
|
||||
// TemplateManager must come before all other (non-test) components,
|
||||
|
@ -916,7 +916,7 @@ public:
|
||||
{
|
||||
for (u16 i = 0; i < grid.m_W; ++i)
|
||||
{
|
||||
u8 p = grid.get(i, j);
|
||||
u8 p = grid.get(i, j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK;
|
||||
if (p > 0 && p <= MAX_LOS_PLAYER_ID)
|
||||
{
|
||||
m_LosState[i + j*m_TerrainVerticesPerSide] |= (LOS_EXPLORED << (2*(p-1)));
|
||||
|
@ -60,6 +60,11 @@ public:
|
||||
Init(paramNode);
|
||||
}
|
||||
|
||||
virtual bool IsLoaded()
|
||||
{
|
||||
return m_Terrain->GetVerticesPerSide() != 0;
|
||||
}
|
||||
|
||||
virtual CFixedVector3D CalcNormal(entity_pos_t x, entity_pos_t z)
|
||||
{
|
||||
CFixedVector3D normal;
|
||||
|
@ -30,6 +30,7 @@ public:
|
||||
DEFAULT_COMPONENT_ALLOCATOR(TerritoryInfluence)
|
||||
|
||||
i32 m_Cost;
|
||||
bool m_Root;
|
||||
u32 m_Weight;
|
||||
u32 m_Radius;
|
||||
|
||||
@ -43,6 +44,9 @@ public:
|
||||
"</data>"
|
||||
"</element>"
|
||||
"</optional>"
|
||||
"<element name='Root'>"
|
||||
"<data type='boolean'/>"
|
||||
"</element>"
|
||||
"<element name='Weight'>"
|
||||
"<data type='nonNegativeInteger'/>"
|
||||
"</element>"
|
||||
@ -58,6 +62,7 @@ public:
|
||||
else
|
||||
m_Cost = -1;
|
||||
|
||||
m_Root = paramNode.GetChild("Root").ToBool();
|
||||
m_Weight = paramNode.GetChild("Weight").ToInt();
|
||||
m_Radius = paramNode.GetChild("Radius").ToInt();
|
||||
}
|
||||
@ -80,6 +85,11 @@ public:
|
||||
return m_Cost;
|
||||
}
|
||||
|
||||
virtual bool IsRoot()
|
||||
{
|
||||
return m_Root;
|
||||
}
|
||||
|
||||
virtual u32 GetWeight()
|
||||
{
|
||||
return m_Weight;
|
||||
|
@ -66,6 +66,8 @@ public:
|
||||
componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged);
|
||||
componentManager.SubscribeGloballyToMessageType(MT_PositionChanged);
|
||||
componentManager.SubscribeToMessageType(MT_TerrainChanged);
|
||||
componentManager.SubscribeToMessageType(MT_Update);
|
||||
componentManager.SubscribeToMessageType(MT_Interpolate);
|
||||
componentManager.SubscribeToMessageType(MT_RenderSubmit);
|
||||
}
|
||||
|
||||
@ -80,20 +82,39 @@ public:
|
||||
float m_BorderThickness;
|
||||
float m_BorderSeparation;
|
||||
|
||||
// Player ID in lower 7 bits; connected flag in high bit
|
||||
Grid<u8>* m_Territories;
|
||||
TerritoryOverlay* m_DebugOverlay;
|
||||
std::vector<SOverlayTexturedLine> m_BoundaryLines;
|
||||
|
||||
// Set to true when territories change; will send a TerritoriesChanged message
|
||||
// during the Update phase
|
||||
bool m_TriggerEvent;
|
||||
|
||||
struct SBoundaryLine
|
||||
{
|
||||
bool connected;
|
||||
CColor color;
|
||||
SOverlayTexturedLine overlay;
|
||||
};
|
||||
|
||||
std::vector<SBoundaryLine> m_BoundaryLines;
|
||||
bool m_BoundaryLinesDirty;
|
||||
|
||||
double m_AnimTime; // time since start of rendering, in seconds
|
||||
|
||||
TerritoryOverlay* m_DebugOverlay;
|
||||
|
||||
virtual void Init(const CParamNode& UNUSED(paramNode))
|
||||
{
|
||||
m_Territories = NULL;
|
||||
m_DebugOverlay = NULL;
|
||||
// m_DebugOverlay = new TerritoryOverlay(*this);
|
||||
m_BoundaryLinesDirty = true;
|
||||
m_TriggerEvent = true;
|
||||
|
||||
m_DirtyID = 1;
|
||||
|
||||
m_AnimTime = 0.0;
|
||||
|
||||
CParamNode externalParamNode;
|
||||
CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml");
|
||||
|
||||
@ -141,6 +162,22 @@ public:
|
||||
MakeDirty();
|
||||
break;
|
||||
}
|
||||
case MT_Update:
|
||||
{
|
||||
if (m_TriggerEvent)
|
||||
{
|
||||
m_TriggerEvent = false;
|
||||
CMessageTerritoriesChanged msg;
|
||||
GetSimContext().GetComponentManager().BroadcastMessage(msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MT_Interpolate:
|
||||
{
|
||||
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
|
||||
Interpolate(msgData.frameTime, msgData.offset);
|
||||
break;
|
||||
}
|
||||
case MT_RenderSubmit:
|
||||
{
|
||||
const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
|
||||
@ -166,10 +203,12 @@ public:
|
||||
virtual const Grid<u8>& GetTerritoryGrid()
|
||||
{
|
||||
CalculateTerritories();
|
||||
ENSURE(m_Territories);
|
||||
return *m_Territories;
|
||||
}
|
||||
|
||||
virtual int32_t GetOwner(entity_pos_t x, entity_pos_t z);
|
||||
virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z);
|
||||
virtual bool IsConnected(entity_pos_t x, entity_pos_t z);
|
||||
|
||||
// To support lazy updates of territory render data,
|
||||
// we maintain a DirtyID here and increment it whenever territories change;
|
||||
@ -182,6 +221,7 @@ public:
|
||||
SAFE_DELETE(m_Territories);
|
||||
++m_DirtyID;
|
||||
m_BoundaryLinesDirty = true;
|
||||
m_TriggerEvent = true;
|
||||
}
|
||||
|
||||
virtual bool NeedUpdate(size_t* dirtyID)
|
||||
@ -205,6 +245,7 @@ public:
|
||||
|
||||
struct TerritoryBoundary
|
||||
{
|
||||
bool connected;
|
||||
player_id_t owner;
|
||||
std::vector<CVector2D> points;
|
||||
};
|
||||
@ -213,6 +254,8 @@ public:
|
||||
|
||||
void UpdateBoundaryLines();
|
||||
|
||||
void Interpolate(float frameTime, float frameOffset);
|
||||
|
||||
void RenderSubmit(SceneCollector& collector);
|
||||
};
|
||||
|
||||
@ -250,8 +293,8 @@ static void ProcessNeighbour(u32 falloff, u16 i, u16 j, u32 pg, bool diagonal,
|
||||
|
||||
static void FloodFill(Grid<u32>& grid, Grid<u8>& costGrid, OpenQueue& openTiles, u32 falloff)
|
||||
{
|
||||
u32 tilesW = grid.m_W;
|
||||
u32 tilesH = grid.m_H;
|
||||
u16 tilesW = grid.m_W;
|
||||
u16 tilesH = grid.m_H;
|
||||
|
||||
while (!openTiles.empty())
|
||||
{
|
||||
@ -281,16 +324,21 @@ static void FloodFill(Grid<u32>& grid, Grid<u8>& costGrid, OpenQueue& openTiles,
|
||||
|
||||
void CCmpTerritoryManager::CalculateTerritories()
|
||||
{
|
||||
PROFILE("CalculateTerritories");
|
||||
|
||||
if (m_Territories)
|
||||
return;
|
||||
|
||||
PROFILE("CalculateTerritories");
|
||||
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
|
||||
|
||||
// If the terrain hasn't been loaded (e.g. this is called during map initialisation),
|
||||
// abort the computation (and assume callers can cope with m_Territories == NULL)
|
||||
if (!cmpTerrain->IsLoaded())
|
||||
return;
|
||||
|
||||
u16 tilesW = cmpTerrain->GetTilesPerSide();
|
||||
u16 tilesH = cmpTerrain->GetTilesPerSide();
|
||||
|
||||
SAFE_DELETE(m_Territories);
|
||||
m_Territories = new Grid<u8>(tilesW, tilesH);
|
||||
|
||||
// Compute terrain-passability-dependent costs per tile
|
||||
@ -324,6 +372,7 @@ void CCmpTerritoryManager::CalculateTerritories()
|
||||
|
||||
// Split influence entities into per-player lists, ignoring any with invalid properties
|
||||
std::map<player_id_t, std::vector<entity_id_t> > influenceEntities;
|
||||
std::vector<entity_id_t> rootInfluenceEntities;
|
||||
for (CComponentManager::InterfaceList::iterator it = influences.begin(); it != influences.end(); ++it)
|
||||
{
|
||||
// Ignore any with no weight or radius (to avoid divide-by-zero later)
|
||||
@ -340,12 +389,19 @@ void CCmpTerritoryManager::CalculateTerritories()
|
||||
if (owner <= 0)
|
||||
continue;
|
||||
|
||||
// We only have 7 bits to store tile ownership, so ignore unrepresentable players
|
||||
if (owner > TERRITORY_PLAYER_MASK)
|
||||
continue;
|
||||
|
||||
// Ignore if invalid position
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it->first);
|
||||
if (cmpPosition.null() || !cmpPosition->IsInWorld())
|
||||
continue;
|
||||
|
||||
influenceEntities[owner].push_back(it->first);
|
||||
|
||||
if (cmpTerritoryInfluence->IsRoot())
|
||||
rootInfluenceEntities.push_back(it->first);
|
||||
}
|
||||
|
||||
// For each player, store the sum of influences on each tile
|
||||
@ -413,6 +469,64 @@ void CCmpTerritoryManager::CalculateTerritories()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Detect territories connected to a 'root' influence (typically a civ center)
|
||||
// belonging to their player, and mark them with the connected flag
|
||||
for (std::vector<entity_id_t>::iterator it = rootInfluenceEntities.begin(); it != rootInfluenceEntities.end(); ++it)
|
||||
{
|
||||
// (These components must be valid else the entities wouldn't be added to this list)
|
||||
CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), *it);
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *it);
|
||||
|
||||
CFixedVector2D pos = cmpPosition->GetPosition2D();
|
||||
u16 i = (u16)clamp((pos.X / (int)CELL_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);
|
||||
u16 j = (u16)clamp((pos.Y / (int)CELL_SIZE).ToInt_RoundToNegInfinity(), 0, tilesH-1);
|
||||
|
||||
u8 owner = (u8)cmpOwnership->GetOwner();
|
||||
|
||||
if (m_Territories->get(i, j) != owner)
|
||||
continue;
|
||||
|
||||
// TODO: would be nice to refactor some of the many flood fill
|
||||
// algorithms in this component
|
||||
|
||||
Grid<u8>& grid = *m_Territories;
|
||||
|
||||
u16 maxi = (u16)(grid.m_W-1);
|
||||
u16 maxj = (u16)(grid.m_H-1);
|
||||
|
||||
std::vector<std::pair<u16, u16> > tileStack;
|
||||
|
||||
#define MARK_AND_PUSH(i, j) STMT(grid.set(i, j, owner | TERRITORY_CONNECTED_MASK); tileStack.push_back(std::make_pair(i, j)); )
|
||||
|
||||
MARK_AND_PUSH(i, j);
|
||||
while (!tileStack.empty())
|
||||
{
|
||||
int ti = tileStack.back().first;
|
||||
int tj = tileStack.back().second;
|
||||
tileStack.pop_back();
|
||||
|
||||
if (ti > 0 && grid.get(ti-1, tj) == owner)
|
||||
MARK_AND_PUSH(ti-1, tj);
|
||||
if (ti < maxi && grid.get(ti+1, tj) == owner)
|
||||
MARK_AND_PUSH(ti+1, tj);
|
||||
if (tj > 0 && grid.get(ti, tj-1) == owner)
|
||||
MARK_AND_PUSH(ti, tj-1);
|
||||
if (tj < maxj && grid.get(ti, tj+1) == owner)
|
||||
MARK_AND_PUSH(ti, tj+1);
|
||||
|
||||
if (ti > 0 && tj > 0 && grid.get(ti-1, tj-1) == owner)
|
||||
MARK_AND_PUSH(ti-1, tj-1);
|
||||
if (ti > 0 && tj < maxj && grid.get(ti-1, tj+1) == owner)
|
||||
MARK_AND_PUSH(ti-1, tj+1);
|
||||
if (ti < maxi && tj > 0 && grid.get(ti+1, tj-1) == owner)
|
||||
MARK_AND_PUSH(ti+1, tj-1);
|
||||
if (ti < maxi && tj < maxj && grid.get(ti+1, tj+1) == owner)
|
||||
MARK_AND_PUSH(ti+1, tj+1);
|
||||
}
|
||||
|
||||
#undef MARK_AND_PUSH
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -481,6 +595,7 @@ std::vector<CCmpTerritoryManager::TerritoryBoundary> CCmpTerritoryManager::Compu
|
||||
std::vector<CCmpTerritoryManager::TerritoryBoundary> boundaries;
|
||||
|
||||
CalculateTerritories();
|
||||
ENSURE(m_Territories);
|
||||
|
||||
// Copy the territories grid so we can mess with it
|
||||
Grid<u8> grid (*m_Territories);
|
||||
@ -506,7 +621,8 @@ std::vector<CCmpTerritoryManager::TerritoryBoundary> CCmpTerritoryManager::Compu
|
||||
// we reach the starting point again
|
||||
|
||||
boundaries.push_back(TerritoryBoundary());
|
||||
boundaries.back().owner = owner;
|
||||
boundaries.back().connected = (owner & TERRITORY_CONNECTED_MASK);
|
||||
boundaries.back().owner = (owner & TERRITORY_PLAYER_MASK);
|
||||
std::vector<CVector2D>& points = boundaries.back().points;
|
||||
|
||||
u8 dir = 0; // 0 == bottom edge of tile, 1 == right, 2 == top, 3 == left
|
||||
@ -585,9 +701,9 @@ std::vector<CCmpTerritoryManager::TerritoryBoundary> CCmpTerritoryManager::Compu
|
||||
// process it a second time
|
||||
std::vector<std::pair<u16, u16> > tileStack;
|
||||
|
||||
#define ZERO_AND_PUSH(i, j) STMT(grid.set(i, j, 0); tileStack.push_back(std::make_pair(i, j)); )
|
||||
#define MARK_AND_PUSH(i, j) STMT(grid.set(i, j, 0); tileStack.push_back(std::make_pair(i, j)); )
|
||||
|
||||
ZERO_AND_PUSH(i, j);
|
||||
MARK_AND_PUSH(i, j);
|
||||
while (!tileStack.empty())
|
||||
{
|
||||
int ti = tileStack.back().first;
|
||||
@ -595,25 +711,25 @@ std::vector<CCmpTerritoryManager::TerritoryBoundary> CCmpTerritoryManager::Compu
|
||||
tileStack.pop_back();
|
||||
|
||||
if (ti > 0 && grid.get(ti-1, tj) == owner)
|
||||
ZERO_AND_PUSH(ti-1, tj);
|
||||
MARK_AND_PUSH(ti-1, tj);
|
||||
if (ti < maxi && grid.get(ti+1, tj) == owner)
|
||||
ZERO_AND_PUSH(ti+1, tj);
|
||||
MARK_AND_PUSH(ti+1, tj);
|
||||
if (tj > 0 && grid.get(ti, tj-1) == owner)
|
||||
ZERO_AND_PUSH(ti, tj-1);
|
||||
MARK_AND_PUSH(ti, tj-1);
|
||||
if (tj < maxj && grid.get(ti, tj+1) == owner)
|
||||
ZERO_AND_PUSH(ti, tj+1);
|
||||
MARK_AND_PUSH(ti, tj+1);
|
||||
|
||||
if (ti > 0 && tj > 0 && grid.get(ti-1, tj-1) == owner)
|
||||
ZERO_AND_PUSH(ti-1, tj-1);
|
||||
MARK_AND_PUSH(ti-1, tj-1);
|
||||
if (ti > 0 && tj < maxj && grid.get(ti-1, tj+1) == owner)
|
||||
ZERO_AND_PUSH(ti-1, tj+1);
|
||||
MARK_AND_PUSH(ti-1, tj+1);
|
||||
if (ti < maxi && tj > 0 && grid.get(ti+1, tj-1) == owner)
|
||||
ZERO_AND_PUSH(ti+1, tj-1);
|
||||
MARK_AND_PUSH(ti+1, tj-1);
|
||||
if (ti < maxi && tj < maxj && grid.get(ti+1, tj+1) == owner)
|
||||
ZERO_AND_PUSH(ti+1, tj+1);
|
||||
MARK_AND_PUSH(ti+1, tj+1);
|
||||
}
|
||||
|
||||
#undef ZERO_AND_PUSH
|
||||
#undef MARK_AND_PUSH
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -658,18 +774,21 @@ void CCmpTerritoryManager::UpdateBoundaryLines()
|
||||
if (!cmpPlayer.null())
|
||||
color = cmpPlayer->GetColour();
|
||||
|
||||
m_BoundaryLines.push_back(SOverlayTexturedLine());
|
||||
m_BoundaryLines.back().m_Terrain = terrain;
|
||||
m_BoundaryLines.back().m_TextureBase = textureBase;
|
||||
m_BoundaryLines.back().m_TextureMask = textureMask;
|
||||
m_BoundaryLines.back().m_Color = color;
|
||||
m_BoundaryLines.back().m_Thickness = m_BorderThickness;
|
||||
m_BoundaryLines.push_back(SBoundaryLine());
|
||||
m_BoundaryLines.back().connected = boundaries[i].connected;
|
||||
m_BoundaryLines.back().color = color;
|
||||
|
||||
m_BoundaryLines.back().overlay.m_Terrain = terrain;
|
||||
m_BoundaryLines.back().overlay.m_TextureBase = textureBase;
|
||||
m_BoundaryLines.back().overlay.m_TextureMask = textureMask;
|
||||
m_BoundaryLines.back().overlay.m_Color = color;
|
||||
m_BoundaryLines.back().overlay.m_Thickness = m_BorderThickness;
|
||||
|
||||
SimRender::SmoothPointsAverage(boundaries[i].points, true);
|
||||
|
||||
SimRender::InterpolatePointsRNS(boundaries[i].points, true, m_BorderSeparation);
|
||||
|
||||
std::vector<float>& points = m_BoundaryLines.back().m_Coords;
|
||||
std::vector<float>& points = m_BoundaryLines.back().overlay.m_Coords;
|
||||
for (size_t j = 0; j < boundaries[i].points.size(); ++j)
|
||||
{
|
||||
points.push_back(boundaries[i].points[j].X);
|
||||
@ -678,8 +797,10 @@ void CCmpTerritoryManager::UpdateBoundaryLines()
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpTerritoryManager::RenderSubmit(SceneCollector& collector)
|
||||
void CCmpTerritoryManager::Interpolate(float frameTime, float UNUSED(frameOffset))
|
||||
{
|
||||
m_AnimTime += frameTime;
|
||||
|
||||
if (m_BoundaryLinesDirty)
|
||||
{
|
||||
UpdateBoundaryLines();
|
||||
@ -687,15 +808,42 @@ void CCmpTerritoryManager::RenderSubmit(SceneCollector& collector)
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_BoundaryLines.size(); ++i)
|
||||
collector.Submit(&m_BoundaryLines[i]);
|
||||
{
|
||||
if (!m_BoundaryLines[i].connected)
|
||||
{
|
||||
CColor c = m_BoundaryLines[i].color;
|
||||
c.a *= 0.2f + 0.8f * fabsf((float)cos(m_AnimTime * M_PI)); // TODO: should let artists tweak this
|
||||
m_BoundaryLines[i].overlay.m_Color = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t CCmpTerritoryManager::GetOwner(entity_pos_t x, entity_pos_t z)
|
||||
void CCmpTerritoryManager::RenderSubmit(SceneCollector& collector)
|
||||
{
|
||||
for (size_t i = 0; i < m_BoundaryLines.size(); ++i)
|
||||
collector.Submit(&m_BoundaryLines[i].overlay);
|
||||
}
|
||||
|
||||
player_id_t CCmpTerritoryManager::GetOwner(entity_pos_t x, entity_pos_t z)
|
||||
{
|
||||
u16 i, j;
|
||||
CalculateTerritories();
|
||||
if (!m_Territories)
|
||||
return 0;
|
||||
|
||||
NearestTile(x, z, i, j, m_Territories->m_W, m_Territories->m_H);
|
||||
return m_Territories->get(i, j);
|
||||
return m_Territories->get(i, j) & TERRITORY_PLAYER_MASK;
|
||||
}
|
||||
|
||||
bool CCmpTerritoryManager::IsConnected(entity_pos_t x, entity_pos_t z)
|
||||
{
|
||||
u16 i, j;
|
||||
CalculateTerritories();
|
||||
if (!m_Territories)
|
||||
return false;
|
||||
|
||||
NearestTile(x, z, i, j, m_Territories->m_W, m_Territories->m_H);
|
||||
return m_Territories->get(i, j) & TERRITORY_CONNECTED_MASK;
|
||||
}
|
||||
|
||||
void TerritoryOverlay::StartRender()
|
||||
|
@ -29,6 +29,8 @@ class CTerrain;
|
||||
class ICmpTerrain : public IComponent
|
||||
{
|
||||
public:
|
||||
virtual bool IsLoaded() = 0;
|
||||
|
||||
virtual CFixedVector3D CalcNormal(entity_pos_t x, entity_pos_t z) = 0;
|
||||
|
||||
virtual entity_pos_t GetGroundLevel(entity_pos_t x, entity_pos_t z) = 0;
|
||||
|
@ -30,6 +30,8 @@ public:
|
||||
*/
|
||||
virtual i32 GetCost() = 0;
|
||||
|
||||
virtual bool IsRoot() = 0;
|
||||
|
||||
virtual u32 GetWeight() = 0;
|
||||
|
||||
virtual u32 GetRadius() = 0;
|
||||
|
@ -22,5 +22,6 @@
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(TerritoryManager)
|
||||
DEFINE_INTERFACE_METHOD_2("GetOwner", int32_t, ICmpTerritoryManager, GetOwner, entity_pos_t, entity_pos_t)
|
||||
DEFINE_INTERFACE_METHOD_2("GetOwner", player_id_t, ICmpTerritoryManager, GetOwner, entity_pos_t, entity_pos_t)
|
||||
DEFINE_INTERFACE_METHOD_2("IsConnected", bool, ICmpTerritoryManager, IsConnected, entity_pos_t, entity_pos_t)
|
||||
END_INTERFACE_WRAPPER(TerritoryManager)
|
||||
|
@ -18,8 +18,10 @@
|
||||
#ifndef INCLUDED_ICMPTERRITORYMANAGER
|
||||
#define INCLUDED_ICMPTERRITORYMANAGER
|
||||
|
||||
#include "simulation2/helpers/Grid.h"
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
#include "simulation2/helpers/Grid.h"
|
||||
#include "simulation2/helpers/Player.h"
|
||||
#include "simulation2/components/ICmpPosition.h"
|
||||
|
||||
class ICmpTerritoryManager : public IComponent
|
||||
@ -27,14 +29,27 @@ class ICmpTerritoryManager : public IComponent
|
||||
public:
|
||||
virtual bool NeedUpdate(size_t* dirtyID) = 0;
|
||||
|
||||
static const int TERRITORY_PLAYER_MASK = 0x7F;
|
||||
static const int TERRITORY_CONNECTED_MASK = 0x80;
|
||||
|
||||
/**
|
||||
* For each tile, the TERRITORY_PLAYER_MASK bits are player ID;
|
||||
* TERRITORY_CONNECTED_MASK is set if the tile is connected to a root object
|
||||
* (civ center etc).
|
||||
*/
|
||||
virtual const Grid<u8>& GetTerritoryGrid() = 0;
|
||||
|
||||
/**
|
||||
* Get owner of territory at given position
|
||||
*
|
||||
* Get owner of territory at given position.
|
||||
* @return player ID of owner; 0 if neutral territory
|
||||
*/
|
||||
virtual int32_t GetOwner(entity_pos_t x, entity_pos_t z) = 0;
|
||||
virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z) = 0;
|
||||
|
||||
/**
|
||||
* Get whether territory at given position is connected to a root object
|
||||
* (civ center etc) owned by that territory's player.
|
||||
*/
|
||||
virtual bool IsConnected(entity_pos_t x, entity_pos_t z) = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(TerritoryManager)
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -26,20 +26,20 @@
|
||||
#define TOJSVAL_SETUP() \
|
||||
JSObject* obj = JS_NewObject(scriptInterface.GetContext(), NULL, NULL, NULL); \
|
||||
if (! obj) \
|
||||
return JSVAL_VOID
|
||||
return JSVAL_VOID;
|
||||
|
||||
#define SET_MSG_PROPERTY(name) \
|
||||
do { \
|
||||
jsval prop = ScriptInterface::ToJSVal(scriptInterface.GetContext(), this->name); \
|
||||
if (! JS_SetProperty(scriptInterface.GetContext(), obj, #name, &prop)) \
|
||||
return JSVAL_VOID; \
|
||||
} while (0)
|
||||
} while (0);
|
||||
|
||||
#define FROMJSVAL_SETUP() \
|
||||
if (! JSVAL_IS_OBJECT(val)) \
|
||||
return NULL; \
|
||||
JSObject* obj = JSVAL_TO_OBJECT(val)
|
||||
jsval prop;
|
||||
JSObject* obj = JSVAL_TO_OBJECT(val); \
|
||||
jsval prop;
|
||||
|
||||
#define GET_MSG_PROPERTY(type, name) \
|
||||
if (! JS_GetProperty(scriptInterface.GetContext(), obj, #name, &prop)) \
|
||||
@ -58,10 +58,10 @@ jsval CMessage::ToJSValCached(ScriptInterface& scriptInterface) const
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
jsval CMessageTurnStart::ToJSVal(ScriptInterface& UNUSED(scriptInterface)) const
|
||||
jsval CMessageTurnStart::ToJSVal(ScriptInterface& scriptInterface) const
|
||||
{
|
||||
LOGWARNING(L"CMessageTurnStart::ToJSVal not implemented");
|
||||
return JSVAL_VOID;
|
||||
TOJSVAL_SETUP();
|
||||
return OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
|
||||
CMessage* CMessageTurnStart::FromJSVal(ScriptInterface& UNUSED(scriptInterface), jsval UNUSED(val))
|
||||
@ -254,6 +254,19 @@ CMessage* CMessageTerrainChanged::FromJSVal(ScriptInterface& scriptInterface, js
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
jsval CMessageTerritoriesChanged::ToJSVal(ScriptInterface& scriptInterface) const
|
||||
{
|
||||
TOJSVAL_SETUP();
|
||||
return OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
|
||||
CMessage* CMessageTerritoriesChanged::FromJSVal(ScriptInterface& UNUSED(scriptInterface), jsval UNUSED(val))
|
||||
{
|
||||
return new CMessageTerritoriesChanged();
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
jsval CMessageRangeUpdate::ToJSVal(ScriptInterface& scriptInterface) const
|
||||
{
|
||||
TOJSVAL_SETUP();
|
||||
|
Loading…
Reference in New Issue
Block a user