diff --git a/binaries/data/mods/public/shaders/overlayline.fp b/binaries/data/mods/public/shaders/overlayline.fp
index 3e6ac390d1..368ce42a3b 100644
--- a/binaries/data/mods/public/shaders/overlayline.fp
+++ b/binaries/data/mods/public/shaders/overlayline.fp
@@ -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
diff --git a/binaries/data/mods/public/simulation/components/TerritoryDecay.js b/binaries/data/mods/public/simulation/components/TerritoryDecay.js
new file mode 100644
index 0000000000..8b22a8780a
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/TerritoryDecay.js
@@ -0,0 +1,74 @@
+function TerritoryDecay() {}
+
+TerritoryDecay.prototype.Schema =
+ "" +
+ "" +
+ "";
+
+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);
diff --git a/binaries/data/mods/public/simulation/components/Timer.js b/binaries/data/mods/public/simulation/components/Timer.js
index d1e61872fb..20cf763e28 100644
--- a/binaries/data/mods/public/simulation/components/Timer.js
+++ b/binaries/data/mods/public/simulation/components/Timer.js
@@ -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];
diff --git a/binaries/data/mods/public/simulation/components/interfaces/TerritoryDecay.js b/binaries/data/mods/public/simulation/components/interfaces/TerritoryDecay.js
new file mode 100644
index 0000000000..555d9651a6
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/interfaces/TerritoryDecay.js
@@ -0,0 +1 @@
+Engine.RegisterInterface("TerritoryDecay");
diff --git a/binaries/data/mods/public/simulation/templates/other/hellenic_propylaea.xml b/binaries/data/mods/public/simulation/templates/other/hellenic_propylaea.xml
index 19009720e6..f99b0fc8ed 100644
--- a/binaries/data/mods/public/simulation/templates/other/hellenic_propylaea.xml
+++ b/binaries/data/mods/public/simulation/templates/other/hellenic_propylaea.xml
@@ -44,6 +44,7 @@
+ false
40
65536
diff --git a/binaries/data/mods/public/simulation/templates/other/hellenic_royal_stoa.xml b/binaries/data/mods/public/simulation/templates/other/hellenic_royal_stoa.xml
index 8a98eb10eb..1fb706112c 100644
--- a/binaries/data/mods/public/simulation/templates/other/hellenic_royal_stoa.xml
+++ b/binaries/data/mods/public/simulation/templates/other/hellenic_royal_stoa.xml
@@ -44,6 +44,7 @@
+ false
40
65536
diff --git a/binaries/data/mods/public/simulation/templates/other/hellenic_stoa.xml b/binaries/data/mods/public/simulation/templates/other/hellenic_stoa.xml
index 4b187df7e7..20e9b12100 100644
--- a/binaries/data/mods/public/simulation/templates/other/hellenic_stoa.xml
+++ b/binaries/data/mods/public/simulation/templates/other/hellenic_stoa.xml
@@ -44,6 +44,7 @@
+ false
36
65536
diff --git a/binaries/data/mods/public/simulation/templates/special/territory_block.xml b/binaries/data/mods/public/simulation/templates/special/territory_block.xml
index b5c17fb23d..d63567770d 100644
--- a/binaries/data/mods/public/simulation/templates/special/territory_block.xml
+++ b/binaries/data/mods/public/simulation/templates/special/territory_block.xml
@@ -24,6 +24,7 @@
64
+ false
0
0
diff --git a/binaries/data/mods/public/simulation/templates/special/territory_pull.xml b/binaries/data/mods/public/simulation/templates/special/territory_pull.xml
index 5118d37077..ebc95e2971 100644
--- a/binaries/data/mods/public/simulation/templates/special/territory_pull.xml
+++ b/binaries/data/mods/public/simulation/templates/special/territory_pull.xml
@@ -24,6 +24,7 @@
0
+ false
0
0
diff --git a/binaries/data/mods/public/simulation/templates/template_structure.xml b/binaries/data/mods/public/simulation/templates/template_structure.xml
index 15c031864e..1c1aac9ef3 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure.xml
@@ -66,6 +66,9 @@
0.6
12.0
+
+ 20
+
40
true
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml b/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
index 31e561c9ca..f8a7016d21 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
@@ -76,6 +76,7 @@
+ true
180
65536
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml b/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
index 628dc50ebf..90d04f7cb9 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
@@ -45,6 +45,7 @@
8.0
+ false
20
65536
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml b/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml
index 3231843897..621db29397 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml
@@ -51,6 +51,7 @@
+ false
40
65536
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_defense_scout_tower.xml b/binaries/data/mods/public/simulation/templates/template_structure_defense_scout_tower.xml
index d21a924991..987cab4862 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_defense_scout_tower.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_defense_scout_tower.xml
@@ -66,6 +66,7 @@
21.0
+ false
32
65536
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml b/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml
index 2802114f08..9418cdf4c8 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml
@@ -42,6 +42,7 @@
+ false
20
65536
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml b/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
index bf34821629..5037edf887 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
@@ -38,6 +38,7 @@
+ false
40
65536
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_economic_mill.xml b/binaries/data/mods/public/simulation/templates/template_structure_economic_mill.xml
index aac86ca391..418c97560f 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_economic_mill.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_economic_mill.xml
@@ -42,6 +42,7 @@
+ false
20
65536
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml b/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml
index 260585b865..23692c8499 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml
@@ -46,6 +46,7 @@
+ false
60
65536
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml b/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
index 227c3e97c9..ee5e2331c8 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
@@ -62,6 +62,7 @@
+ false
100
65536
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_special.xml b/binaries/data/mods/public/simulation/templates/template_structure_special.xml
index 978c05759c..b5d515ebba 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_special.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_special.xml
@@ -20,6 +20,7 @@
+ false
40
65536
diff --git a/source/graphics/TerritoryTexture.cpp b/source/graphics/TerritoryTexture.cpp
index e13b2cda75..44f2f06f74 100644
--- a/source/graphics/TerritoryTexture.cpp
+++ b/source/graphics/TerritoryTexture.cpp
@@ -192,7 +192,7 @@ void CTerritoryTexture::GenerateBitmap(const Grid& 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& 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;
diff --git a/source/simulation2/MessageTypes.h b/source/simulation2/MessageTypes.h
index c548cc1c14..7d4eefe55a 100644
--- a/source/simulation2/MessageTypes.h
+++ b/source/simulation2/MessageTypes.h
@@ -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.
diff --git a/source/simulation2/TypeList.h b/source/simulation2/TypeList.h
index 60c8514e24..a7ac94b6ce 100644
--- a/source/simulation2/TypeList.h
+++ b/source/simulation2/TypeList.h
@@ -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,
diff --git a/source/simulation2/components/CCmpRangeManager.cpp b/source/simulation2/components/CCmpRangeManager.cpp
index b07379a0d5..cef54520a9 100644
--- a/source/simulation2/components/CCmpRangeManager.cpp
+++ b/source/simulation2/components/CCmpRangeManager.cpp
@@ -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)));
diff --git a/source/simulation2/components/CCmpTerrain.cpp b/source/simulation2/components/CCmpTerrain.cpp
index b209629cd9..65ec29bb67 100644
--- a/source/simulation2/components/CCmpTerrain.cpp
+++ b/source/simulation2/components/CCmpTerrain.cpp
@@ -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;
diff --git a/source/simulation2/components/CCmpTerritoryInfluence.cpp b/source/simulation2/components/CCmpTerritoryInfluence.cpp
index 029bedb2c5..71895f6aab 100644
--- a/source/simulation2/components/CCmpTerritoryInfluence.cpp
+++ b/source/simulation2/components/CCmpTerritoryInfluence.cpp
@@ -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:
""
""
""
+ ""
+ ""
+ ""
""
""
""
@@ -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;
diff --git a/source/simulation2/components/CCmpTerritoryManager.cpp b/source/simulation2/components/CCmpTerritoryManager.cpp
index b6dce86d42..d396bf04e2 100644
--- a/source/simulation2/components/CCmpTerritoryManager.cpp
+++ b/source/simulation2/components/CCmpTerritoryManager.cpp
@@ -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* m_Territories;
- TerritoryOverlay* m_DebugOverlay;
- std::vector 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 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 (msg);
+ Interpolate(msgData.frameTime, msgData.offset);
+ break;
+ }
case MT_RenderSubmit:
{
const CMessageRenderSubmit& msgData = static_cast (msg);
@@ -166,10 +203,12 @@ public:
virtual const Grid& 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 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& grid, Grid& 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& grid, Grid& costGrid, OpenQueue& openTiles,
void CCmpTerritoryManager::CalculateTerritories()
{
- PROFILE("CalculateTerritories");
-
if (m_Territories)
return;
+ PROFILE("CalculateTerritories");
+
CmpPtr 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(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 > influenceEntities;
+ std::vector 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 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::iterator it = rootInfluenceEntities.begin(); it != rootInfluenceEntities.end(); ++it)
+ {
+ // (These components must be valid else the entities wouldn't be added to this list)
+ CmpPtr cmpOwnership(GetSimContext(), *it);
+ CmpPtr 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& grid = *m_Territories;
+
+ u16 maxi = (u16)(grid.m_W-1);
+ u16 maxj = (u16)(grid.m_H-1);
+
+ std::vector > 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::Compu
std::vector boundaries;
CalculateTerritories();
+ ENSURE(m_Territories);
// Copy the territories grid so we can mess with it
Grid grid (*m_Territories);
@@ -506,7 +621,8 @@ std::vector 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& 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::Compu
// process it a second time
std::vector > 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::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& points = m_BoundaryLines.back().m_Coords;
+ std::vector& 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()
diff --git a/source/simulation2/components/ICmpTerrain.h b/source/simulation2/components/ICmpTerrain.h
index e5573b050c..fdff54f32c 100644
--- a/source/simulation2/components/ICmpTerrain.h
+++ b/source/simulation2/components/ICmpTerrain.h
@@ -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;
diff --git a/source/simulation2/components/ICmpTerritoryInfluence.h b/source/simulation2/components/ICmpTerritoryInfluence.h
index f4f666d6e2..ef10607c5a 100644
--- a/source/simulation2/components/ICmpTerritoryInfluence.h
+++ b/source/simulation2/components/ICmpTerritoryInfluence.h
@@ -30,6 +30,8 @@ public:
*/
virtual i32 GetCost() = 0;
+ virtual bool IsRoot() = 0;
+
virtual u32 GetWeight() = 0;
virtual u32 GetRadius() = 0;
diff --git a/source/simulation2/components/ICmpTerritoryManager.cpp b/source/simulation2/components/ICmpTerritoryManager.cpp
index 5a204285ac..a0f04ca680 100644
--- a/source/simulation2/components/ICmpTerritoryManager.cpp
+++ b/source/simulation2/components/ICmpTerritoryManager.cpp
@@ -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)
diff --git a/source/simulation2/components/ICmpTerritoryManager.h b/source/simulation2/components/ICmpTerritoryManager.h
index 9234bb5ee1..57982f26ea 100644
--- a/source/simulation2/components/ICmpTerritoryManager.h
+++ b/source/simulation2/components/ICmpTerritoryManager.h
@@ -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& 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)
};
diff --git a/source/simulation2/scripting/MessageTypeConversions.cpp b/source/simulation2/scripting/MessageTypeConversions.cpp
index 9aec0ee6da..d976a0939f 100644
--- a/source/simulation2/scripting/MessageTypeConversions.cpp
+++ b/source/simulation2/scripting/MessageTypeConversions.cpp
@@ -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();