Index: binaries/data/mods/public/simulation/components/TerritoryDecay.js
===================================================================
--- binaries/data/mods/public/simulation/components/TerritoryDecay.js	(revision 16623)
+++ binaries/data/mods/public/simulation/components/TerritoryDecay.js	(working copy)
@@ -33,7 +33,12 @@
 	if (tileOwner != cmpOwnership.GetOwner())
 		return false;
 
-	return cmpTerritoryManager.IsConnected(pos.x, pos.y);
+	if (cmpTerritoryManager.IsConnected(pos.x, pos.y))
+		return true;
+
+	var neighbours = cmpTerritoryManager.GetConnectedNeighbours(pos.x, pos.y);
+	warn(uneval(neighbours));
+	return false;
 };
 
 TerritoryDecay.prototype.IsDecaying = function()
Index: source/simulation2/components/CCmpTerritoryManager.cpp
===================================================================
--- source/simulation2/components/CCmpTerritoryManager.cpp	(revision 16623)
+++ source/simulation2/components/CCmpTerritoryManager.cpp	(working copy)
@@ -45,6 +45,8 @@
 #include "simulation2/helpers/PriorityQueue.h"
 #include "simulation2/helpers/Render.h"
 
+#include "ps/CLogger.h"
+
 class CCmpTerritoryManager;
 
 class TerritoryOverlay : public TerrainOverlay
@@ -88,6 +90,9 @@
 	// connected flag in bit 6 (TERRITORY_CONNECTED_MASK);
 	// processed flag in bit 7 (TERRITORY_PROCESSED_MASK)
 	Grid<u8>* m_Territories;
+	// Bits 0 - 14 are used to mark players (set to 1 when that player is a neighbour)
+	// Bit 15 is used as a clean bit. The state is considered dirty when the bit is 0
+	Grid<u16>* m_NeighbourTerritories;
 
 	// Set to true when territories change; will send a TerritoriesChanged message
 	// during the Update phase
@@ -113,6 +118,7 @@
 	virtual void Init(const CParamNode& UNUSED(paramNode))
 	{
 		m_Territories = NULL;
+		m_NeighbourTerritories = NULL;
 		m_DebugOverlay = NULL;
 //		m_DebugOverlay = new TerritoryOverlay(*this);
 		m_BoundaryLinesDirty = true;
@@ -135,6 +141,7 @@
 	virtual void Deinit()
 	{
 		SAFE_DELETE(m_Territories);
+		SAFE_DELETE(m_NeighbourTerritories);
 		SAFE_DELETE(m_DebugOverlay);
 	}
 
@@ -224,6 +231,7 @@
 	}
 
 	virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z);
+	virtual std::vector<player_id_t> GetConnectedNeighbours(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,
@@ -235,6 +243,7 @@
 	void MakeDirty()
 	{
 		SAFE_DELETE(m_Territories);
+		SAFE_DELETE(m_NeighbourTerritories);
 		++m_DirtyID;
 		m_BoundaryLinesDirty = true;
 		m_TriggerEvent = true;
@@ -252,6 +261,8 @@
 
 	void CalculateTerritories();
 
+	u16 GetConnectedNeighbourBits(u16 i, u16 j);
+
 	/**
 	 * Updates @p grid based on the obstruction shapes of all entities with
 	 * a TerritoryInfluence component. Grid cells are 0 if no influence,
@@ -418,17 +429,15 @@
 	// TODO: this is a large waste of memory; we don't really need to store
 	// all the intermediate grids
 
-	for (std::map<player_id_t, std::vector<entity_id_t> >::iterator it = influenceEntities.begin(); it != influenceEntities.end(); ++it)
+	for (auto it = influenceEntities.begin(); it != influenceEntities.end(); ++it)
 	{
 		Grid<u32> playerGrid(tilesW, tilesH);
+		Grid<u32> entityGrid(tilesW, tilesH);
 
 		std::vector<entity_id_t>& ents = it->second;
-		for (std::vector<entity_id_t>::iterator eit = ents.begin(); eit != ents.end(); ++eit)
+		// Compute the influence map of the current entity, then add it to the player grid
+		for (auto eit = ents.begin(); eit != ents.end(); ++eit)
 		{
-			// Compute the influence map of the current entity, then add it to the player grid
-
-			Grid<u32> entityGrid(tilesW, tilesH);
-
 			CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *eit);
 			CFixedVector2D pos = cmpPosition->GetPosition2D();
 			u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);
@@ -437,10 +446,8 @@
 			CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), *eit);
 			u32 weight = cmpTerritoryInfluence->GetWeight();
 			u32 radius = cmpTerritoryInfluence->GetRadius() / TERRAIN_TILE_SIZE;
-			u32 falloff = weight / radius; // earlier check for GetRadius() == 0 prevents divide-by-zero
+			u32 falloff = weight / radius + 1; // earlier check for GetRadius() == 0 prevents divide-by-zero
 
-			// TODO: we should have some maximum value on weight, to avoid overflow
-			// when doing all the sums
 
 			// Initialise the tile under the entity
 			entityGrid.set(i, j, weight);
@@ -451,8 +458,10 @@
 			// Expand influences outwards
 			FloodFill(entityGrid, influenceGrid, openTiles, falloff);
 
-			// TODO: we should do a sparse grid and only add the non-zero regions, for performance
-			playerGrid.add(entityGrid);
+			// TODO: we should have overflow checks when adding, as the number of added
+			// entities can be arbitrary large
+			playerGrid.addRegion(entityGrid, i-radius, j-radius, i+radius, j+radius);
+			entityGrid.reset();
 		}
 
 		playerGrids.push_back(std::make_pair(it->first, playerGrid));
@@ -479,7 +488,7 @@
 
 	// 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)
+	for (auto 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);
@@ -536,6 +545,72 @@
 	}
 }
 
+u16 CCmpTerritoryManager::GetConnectedNeighbourBits(u16 i, u16 j)
+{
+	if (!m_NeighbourTerritories)
+		m_NeighbourTerritories = new Grid<u16>(m_Territories->m_W, m_Territories->m_H);
+
+	if ((m_NeighbourTerritories->get(i, j) & NEIGHBOURS_CLEAN) != 0)
+		return m_NeighbourTerritories->get(i,j) & NEIGHBOURS_PLAYER_MASK;
+
+	player_id_t thisOwner = m_Territories->get(i, j) & TERRITORY_PLAYER_MASK;
+	// we rely on having a gaia bord around the map, so we can't test gaia territory
+	if (thisOwner == 0)
+		return 0;
+	// use a flood-fill algorithm that fills up to the borders and remembers the owners
+	Grid<bool>* markerGrid = new Grid<bool>(m_Territories->m_W, m_Territories->m_H);
+	u16 neighbourBits = 0;
+	std::vector<std::pair<u16, u16> > tileStack;
+	tileStack.push_back(std::make_pair(i, j));
+	markerGrid->set(i, j, true);
+	while (!tileStack.empty())
+	{
+		u16 ti = tileStack.back().first;
+		u16 tj = tileStack.back().second;
+		tileStack.pop_back();
+		for (u16 di = ti - 1; di <= ti + 1; ++di)
+		{
+			for (u16 dj = tj - 1; dj <= tj + 1; ++dj)
+			{
+				if (markerGrid->get(di, dj))
+					continue;
+				player_id_t owner = m_Territories->get(di, dj) & TERRITORY_PLAYER_MASK;
+				if (owner == thisOwner)
+				{
+					tileStack.push_back(std::make_pair(di, dj));
+					markerGrid->set(di, dj, true);
+				}
+				else if ((m_Territories->get(di, dj) & TERRITORY_CONNECTED_MASK) != 0)
+					neighbourBits |= (1 << owner);
+			}
+		}
+	}
+	// now cache the calculated owners over the territory with another flood fill
+	tileStack.push_back(std::make_pair(i, j));
+	m_NeighbourTerritories->set(i, j, neighbourBits | NEIGHBOURS_CLEAN);
+	while (!tileStack.empty())
+	{
+		u16 ti = tileStack.back().first;
+		u16 tj = tileStack.back().second;
+		tileStack.pop_back();
+		for (u16 di = ti - 1; di <= ti + 1; ++di)
+		{
+			for (u16 dj = tj - 1; dj <= tj + 1; ++dj)
+			{
+				if ((m_NeighbourTerritories->get(di, dj) & NEIGHBOURS_CLEAN) != 0)
+					continue;
+				player_id_t owner = m_Territories->get(di, dj) & TERRITORY_PLAYER_MASK;
+				if (owner == thisOwner) // only flood fill this area
+				{
+					m_NeighbourTerritories->set(di, dj, neighbourBits | NEIGHBOURS_CLEAN);
+					tileStack.push_back(std::make_pair(di, dj));
+				}
+			}
+		}
+	}
+	return neighbourBits & NEIGHBOURS_PLAYER_MASK;
+}
+
 /**
  * Compute the tile indexes on the grid nearest to a given point
  */
@@ -722,6 +797,21 @@
 	return m_Territories->get(i, j) & TERRITORY_PLAYER_MASK;
 }
 
+std::vector<player_id_t> CCmpTerritoryManager::GetConnectedNeighbours(entity_pos_t x, entity_pos_t z)
+{
+	if (!m_Territories)
+		CalculateTerritories();
+	u16 i, j;
+	NearestTile(x, z, i, j, m_Territories->m_W, m_Territories->m_H);
+	u16 neighbourBits = GetConnectedNeighbourBits(i, j);
+	std::vector<player_id_t> neighbours;
+	for (entity_id_t player = 0; player < 15; ++player)
+		if ((neighbourBits & (1 << player)) != 0)
+			neighbours.push_back(player);
+
+	return neighbours;
+}
+
 bool CCmpTerritoryManager::IsConnected(entity_pos_t x, entity_pos_t z)
 {
 	u16 i, j;
Index: source/simulation2/components/ICmpTerritoryManager.cpp
===================================================================
--- source/simulation2/components/ICmpTerritoryManager.cpp	(revision 16623)
+++ source/simulation2/components/ICmpTerritoryManager.cpp	(working copy)
@@ -23,5 +23,6 @@
 
 BEGIN_INTERFACE_WRAPPER(TerritoryManager)
 DEFINE_INTERFACE_METHOD_2("GetOwner", player_id_t, ICmpTerritoryManager, GetOwner, entity_pos_t, entity_pos_t)
+DEFINE_INTERFACE_METHOD_2("GetConnectedNeighbours", std::vector<player_id_t>, ICmpTerritoryManager, GetConnectedNeighbours, entity_pos_t, entity_pos_t)
 DEFINE_INTERFACE_METHOD_2("IsConnected", bool, ICmpTerritoryManager, IsConnected, entity_pos_t, entity_pos_t)
 END_INTERFACE_WRAPPER(TerritoryManager)
Index: source/simulation2/components/ICmpTerritoryManager.h
===================================================================
--- source/simulation2/components/ICmpTerritoryManager.h	(revision 16623)
+++ source/simulation2/components/ICmpTerritoryManager.h	(working copy)
@@ -32,6 +32,8 @@
 	static const int TERRITORY_PLAYER_MASK = 0x3F;
 	static const int TERRITORY_CONNECTED_MASK = 0x40;
 	static const int TERRITORY_PROCESSED_MASK = 0x80; //< For internal use; marks a tile as processed.
+	static const int NEIGHBOURS_PLAYER_MASK = 0x7FFF;
+	static const int NEIGHBOURS_CLEAN = 0x8000;
 
 	/**
 	 * For each tile, the TERRITORY_PLAYER_MASK bits are player ID;
@@ -47,6 +49,12 @@
 	virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z) = 0;
 
 	/**
+	 * Get a list of neighbours of this area. Only return the neighbours that are also
+	 * connected on their own (see the IsConnected method).
+	 */
+	virtual std::vector<player_id_t> GetConnectedNeighbours(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.
 	 */
Index: source/simulation2/helpers/Grid.h
===================================================================
--- source/simulation2/helpers/Grid.h	(revision 16623)
+++ source/simulation2/helpers/Grid.h	(working copy)
@@ -92,6 +92,26 @@
 			m_Data[i] += g.m_Data[i];
 	}
 
+	// Add a region of a grid to this grid
+	void addRegion(const Grid& g, int minI, int minJ, int maxI, int maxJ)
+	{
+#if GRID_BOUNDS_DEBUG
+		ENSURE(g.m_W == m_W && g.m_H == m_H);
+		ENSURE(minI <= maxI && minJ <= maxJ);
+#endif
+		minI = std::min(std::max(minI, 0), (int)g.m_W);
+		maxI = std::min(std::max(maxI, 0), (int)g.m_W);
+		minJ = std::min(std::max(minJ, 0), (int)g.m_H);
+		maxJ = std::min(std::max(maxJ, 0), (int)g.m_H);
+		for (int j = minJ; j <= maxJ; ++j)
+		{
+			for (int i = minI; i <= maxI; ++i)
+			{
+				m_Data[j*m_W + i] += g.m_Data[j*m_W + i];
+			}
+		}
+	}
+
 	void set(int i, int j, const T& value)
 	{
 #if GRID_BOUNDS_DEBUG
