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,14 @@
 	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.GetNeighbours(pos.x, pos.y, true);
+	warn(uneval(neighbours));
+	var neighbours = cmpTerritoryManager.GetNeighbours(pos.x, pos.y, false);
+	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)
@@ -224,6 +224,7 @@
 	}
 
 	virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z);
+	virtual std::vector<player_id_t> GetNeighbours(entity_pos_t x, entity_pos_t z, bool filterConnected);
 	virtual bool IsConnected(entity_pos_t x, entity_pos_t z);
 
 	// To support lazy updates of territory render data,
@@ -252,6 +253,8 @@
 
 	void CalculateTerritories();
 
+	u32 GetNeighbourBits(u16 i, u16 j, bool filterConnected);
+
 	/**
 	 * Updates @p grid based on the obstruction shapes of all entities with
 	 * a TerritoryInfluence component. Grid cells are 0 if no influence,
@@ -280,25 +283,6 @@
 
 typedef PriorityQueueHeap<std::pair<u16, u16>, u32, std::greater<u32> > OpenQueue;
 
-static void ProcessNeighbour(u32 falloff, u16 i, u16 j, u32 pg, bool diagonal,
-		Grid<u32>& grid, OpenQueue& queue, const Grid<u8>& costGrid)
-{
-	u32 dg = falloff * costGrid.get(i, j);
-	if (diagonal)
-		dg = (dg * 362) / 256;
-
-	// Stop if new cost g=pg-dg is not better than previous value for that tile
-	// (arranged to avoid underflow if pg < dg)
-	if (pg <= grid.get(i, j) + dg)
-		return;
-
-	u32 g = pg - dg; // cost to this tile = cost to predecessor - falloff from predecessor
-
-	grid.set(i, j, g);
-	OpenQueue::Item tile = { std::make_pair(i, j), g };
-	queue.push(tile);
-}
-
 static void FloodFill(Grid<u32>& grid, Grid<u8>& costGrid, OpenQueue& openTiles, u32 falloff)
 {
 	u16 tilesW = grid.m_W;
@@ -308,25 +292,39 @@
 	{
 		OpenQueue::Item tile = openTiles.pop();
 
-		// Process neighbours (if they're not off the edge of the map)
-		u16 x = tile.id.first;
-		u16 z = tile.id.second;
-		if (x > 0)
-			ProcessNeighbour(falloff, (u16)(x-1), z, tile.rank, false, grid, openTiles, costGrid);
-		if (x < tilesW-1)
-			ProcessNeighbour(falloff, (u16)(x+1), z, tile.rank, false, grid, openTiles, costGrid);
-		if (z > 0)
-			ProcessNeighbour(falloff, x, (u16)(z-1), tile.rank, false, grid, openTiles, costGrid);
-		if (z < tilesH-1)
-			ProcessNeighbour(falloff, x, (u16)(z+1), tile.rank, false, grid, openTiles, costGrid);
-		if (x > 0 && z > 0)
-			ProcessNeighbour(falloff, (u16)(x-1), (u16)(z-1), tile.rank, true, grid, openTiles, costGrid);
-		if (x > 0 && z < tilesH-1)
-			ProcessNeighbour(falloff, (u16)(x-1), (u16)(z+1), tile.rank, true, grid, openTiles, costGrid);
-		if (x < tilesW-1 && z > 0)
-			ProcessNeighbour(falloff, (u16)(x+1), (u16)(z-1), tile.rank, true, grid, openTiles, costGrid);
-		if (x < tilesW-1 && z < tilesH-1)
-			ProcessNeighbour(falloff, (u16)(x+1), (u16)(z+1), tile.rank, true, grid, openTiles, costGrid);
+		// Process neighbours
+		int x = tile.id.first;
+		int z = tile.id.second;
+		u32 pg = tile.rank;
+		for (int i = -1; i < 2; ++i)
+		{
+			int ti = x + i;
+			if (ti < 0 || ti >= tilesW)
+				continue;
+			for (int j = -1; j < 2; ++j)
+			{
+				if (i == 0 && j == 0)
+					continue;
+				int tj = z + j;
+				if (tj < 0 || tj >= tilesH)
+					continue;
+
+				u32 dg = falloff * costGrid.get(ti, tj);
+				if (i * j != 0) // diagonal tile
+					dg = (dg * 362) / 256;
+
+				// Stop if new cost g=pg-dg is not better than previous value for that tile
+				// (arranged to avoid underflow if pg < dg)
+				if (pg <= grid.get(ti, tj) + dg)
+					continue;
+
+				u32 g = pg - dg; // cost to this tile = cost to predecessor - falloff from predecessor
+
+				grid.set(ti, tj, g);
+				OpenQueue::Item tile = { std::make_pair(ti, tj), g };
+				openTiles.push(tile);
+			}
+		}
 	}
 }
 
@@ -382,14 +380,14 @@
 	// 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)
+	for (auto& it : influences)
 	{
 		// Ignore any with no weight or radius (to avoid divide-by-zero later)
-		ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it->second);
+		ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it.second);
 		if (cmpTerritoryInfluence->GetWeight() == 0 || cmpTerritoryInfluence->GetRadius() == 0)
 			continue;
 
-		CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), it->first);
+		CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), it.first);
 		if (!cmpOwnership)
 			continue;
 
@@ -403,14 +401,14 @@
 			continue;
 
 		// Ignore if invalid position
-		CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it->first);
+		CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it.first);
 		if (!cmpPosition || !cmpPosition->IsInWorld())
 			continue;
 
-		influenceEntities[owner].push_back(it->first);
+		influenceEntities[owner].push_back(it.first);
 
 		if (cmpTerritoryInfluence->IsRoot())
-			rootInfluenceEntities.push_back(it->first);
+			rootInfluenceEntities.push_back(it.first);
 	}
 
 	// For each player, store the sum of influences on each tile
@@ -418,29 +416,25 @@
 	// 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)
 	{
 		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)
+		std::vector<entity_id_t>& ents = it.second;
+		// Compute the influence map of the current entity, then add it to the player grid
+		for (auto& eit : ents)
 		{
-			// 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);
+			CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), eit);
 			CFixedVector2D pos = cmpPosition->GetPosition2D();
 			u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);
 			u16 j = (u16)clamp((pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesH-1);
 
-			CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), *eit);
+			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,11 +445,13 @@
 			// 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));
+		playerGrids.emplace_back(it.first, playerGrid);
 	}
 
 	// Set m_Territories to the player ID with the highest influence for each tile
@@ -467,12 +463,10 @@
 			for (size_t k = 0; k < playerGrids.size(); ++k)
 			{
 				u32 w = playerGrids[k].second.get(i, j);
-				if (w > bestWeight)
-				{
-					player_id_t id = playerGrids[k].first;
-					m_Territories->set(i, j, (u8)id);
-					bestWeight = w;
-				}
+				if (w <= bestWeight)
+					continue;
+				m_Territories->set(i, j, (u8)playerGrids[k].first);
+				bestWeight = w;
 			}
 		}
 	}
@@ -479,11 +473,11 @@
 
 	// 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)
 	{
 		// (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);
+		CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), it);
+		CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it);
 
 		CFixedVector2D pos = cmpPosition->GetPosition2D();
 		u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);
@@ -499,41 +493,94 @@
 
 		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;
+			int x = tileStack.back().first;
+			int z = tileStack.back().second;
 			tileStack.pop_back();
+			for (int i = -1; i < 2; ++i)
+			{
+				int ti = x + i;
+				if (ti < 0 || ti >= tilesW) // check grid bounds
+					continue;
+				for (int j = -1; j < 2; ++j)
+				{
+					if (i == 0 && j == 0) // don't process the same tile
+						continue;
+					int tj = z + j;
+					if (tj < 0 || tj >= tilesH) // check grid bounds
+						continue;
+					if (!grid.get(ti, tj) == owner)
+						continue;
+					grid.set(ti, tj, owner | TERRITORY_CONNECTED_MASK);
+					tileStack.emplace_back(ti, tj);
+				}
+			}
+		}
+	}
+}
 
-			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);
+/**
+ * Get a binary number representing the neighbours 
+ * of the territory of tile (i,j)
+ * Every bit represents a player being a neighbour or not.
+ *
+ * Set the filterConnected to true to only return neighbours with connected
+ * territory
+ */
+u32 CCmpTerritoryManager::GetNeighbourBits(u16 i, u16 j, bool filterConnected)
+{
+	if (!m_Territories)
+		CalculateTerritories();
 
-			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);
+	player_id_t thisOwner = m_Territories->get(i, j) & TERRITORY_PLAYER_MASK;
+
+	u16 tilesW = m_Territories->m_W;
+	u16 tilesH = m_Territories->m_H;
+
+	// use a flood-fill algorithm that fills up to the borders and remembers the owners
+	Grid<bool>* markerGrid = new Grid<bool>(tilesW, tilesH);
+	u32 neighbourBits = 0;
+	std::vector<std::pair<u16, u16> > tileStack;
+	tileStack.emplace_back(i, j);
+	markerGrid->set(i, j, true);
+	while (!tileStack.empty())
+	{
+		int x = tileStack.back().first;
+		int z = tileStack.back().second;
+		tileStack.pop_back();
+		for (int i = -1; i < 2; ++i)
+		{
+			int ti = x + i;
+			if (ti < 0 || ti >= tilesW)
+				continue;
+			for (int j = -1; j < 2; ++j)
+			{
+				if (i == 0 && j == 0)
+					continue;
+				int tj = z + j;
+				if (tj < 0 || tj >= tilesH)
+					continue;
+
+				if (markerGrid->get(ti, tj))
+					continue;
+				player_id_t owner = m_Territories->get(ti, tj) & TERRITORY_PLAYER_MASK;
+				if (owner == thisOwner)
+				{
+					// expand the tile
+					tileStack.emplace_back(ti, tj);
+					markerGrid->set(ti, tj, true);
+				}
+				else if (owner == 0 || !filterConnected)
+					neighbourBits |= (1 << owner); // add gaia or unconnected player to the neighbours
+				else if ((m_Territories->get(ti, tj) & TERRITORY_CONNECTED_MASK) != 0)
+					neighbourBits |= (1 << owner); // add connected player to the neighbours
+			}
 		}
-
-#undef MARK_AND_PUSH
 	}
+	return neighbourBits;
 }
 
 /**
@@ -559,15 +606,15 @@
 
 void CCmpTerritoryManager::RasteriseInfluences(CComponentManager::InterfaceList& infls, Grid<u8>& grid)
 {
-	for (CComponentManager::InterfaceList::iterator it = infls.begin(); it != infls.end(); ++it)
+	for (auto& it : infls)
 	{
-		ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it->second);
+		ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it.second);
 
 		i32 cost = cmpTerritoryInfluence->GetCost();
 		if (cost == -1)
 			continue;
 
-		CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), it->first);
+		CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), it.first);
 		if (!cmpObstruction)
 			continue;
 
@@ -722,6 +769,25 @@
 	return m_Territories->get(i, j) & TERRITORY_PLAYER_MASK;
 }
 
+std::vector<player_id_t> CCmpTerritoryManager::GetNeighbours(entity_pos_t x, entity_pos_t z, bool filterConnected)
+{
+	if (!m_Territories)
+		CalculateTerritories();
+	u16 i, j;
+	NearestTile(x, z, i, j, m_Territories->m_W, m_Territories->m_H);
+	u32 neighbourBits = GetNeighbourBits(i, j, filterConnected);
+	std::vector<player_id_t> neighbours;
+	player_id_t p = 0;
+	while (neighbourBits)
+	{
+		if (neighbourBits & 1)
+			neighbours.push_back(p);
+		++p;
+		neighbourBits >>= 1;
+	}
+	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_3("GetNeighbours", std::vector<player_id_t>, ICmpTerritoryManager, GetNeighbours, entity_pos_t, entity_pos_t, bool)
 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)
@@ -47,6 +47,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> GetNeighbours(entity_pos_t x, entity_pos_t z, bool filterConnected) = 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
