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