1
0
forked from 0ad/0ad

# Health decay for buildings not in a civ center's territory.

This was SVN commit r10034.
This commit is contained in:
Ykkrosh 2011-08-18 20:28:53 +00:00
parent 6b26820090
commit 308cb26dd4
32 changed files with 359 additions and 56 deletions

View File

@ -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

View File

@ -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);

View File

@ -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];

View File

@ -0,0 +1 @@
Engine.RegisterInterface("TerritoryDecay");

View File

@ -44,6 +44,7 @@
<Static width="26.0" depth="30.0"/>
</Obstruction>
<TerritoryInfluence>
<Root>false</Root>
<Radius>40</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -44,6 +44,7 @@
<Static width="28.0" depth="20.0"/>
</Obstruction>
<TerritoryInfluence>
<Root>false</Root>
<Radius>40</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -44,6 +44,7 @@
<Static width="26.0" depth="10.5"/>
</Obstruction>
<TerritoryInfluence>
<Root>false</Root>
<Radius>36</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -24,6 +24,7 @@
<Selectable/>
<TerritoryInfluence>
<OverrideCost>64</OverrideCost>
<Root>false</Root>
<Weight>0</Weight>
<Radius>0</Radius>
</TerritoryInfluence>

View File

@ -24,6 +24,7 @@
<Selectable/>
<TerritoryInfluence>
<OverrideCost>0</OverrideCost>
<Root>false</Root>
<Weight>0</Weight>
<Radius>0</Radius>
</TerritoryInfluence>

View File

@ -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>

View File

@ -76,6 +76,7 @@
</SoundGroups>
</Sound>
<TerritoryInfluence>
<Root>true</Root>
<Radius>180</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -45,6 +45,7 @@
<HeightOffset>8.0</HeightOffset>
</StatusBars>
<TerritoryInfluence>
<Root>false</Root>
<Radius>20</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -51,6 +51,7 @@
</SoundGroups>
</Sound>
<TerritoryInfluence>
<Root>false</Root>
<Radius>40</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -66,6 +66,7 @@
<HeightOffset>21.0</HeightOffset>
</StatusBars>
<TerritoryInfluence>
<Root>false</Root>
<Radius>32</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -42,6 +42,7 @@
</SoundGroups>
</Sound>
<TerritoryInfluence>
<Root>false</Root>
<Radius>20</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -38,6 +38,7 @@
</SoundGroups>
</Sound>
<TerritoryInfluence>
<Root>false</Root>
<Radius>40</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -42,6 +42,7 @@
</SoundGroups>
</Sound>
<TerritoryInfluence>
<Root>false</Root>
<Radius>20</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -46,6 +46,7 @@
</SoundGroups>
</Sound>
<TerritoryInfluence>
<Root>false</Root>
<Radius>60</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -62,6 +62,7 @@
</SoundGroups>
</Sound>
<TerritoryInfluence>
<Root>false</Root>
<Radius>100</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -20,6 +20,7 @@
<Static width="24.0" depth="24.0"/>
</Obstruction>
<TerritoryInfluence>
<Root>false</Root>
<Radius>40</Radius>
<Weight>65536</Weight>
</TerritoryInfluence>

View File

@ -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;

View File

@ -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.

View File

@ -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,

View File

@ -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)));

View File

@ -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;

View File

@ -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;

View File

@ -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()

View File

@ -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;

View File

@ -30,6 +30,8 @@ public:
*/
virtual i32 GetCost() = 0;
virtual bool IsRoot() = 0;
virtual u32 GetWeight() = 0;
virtual u32 GetRadius() = 0;

View File

@ -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)

View File

@ -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)
};

View File

@ -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();