From a7826b12b806cceea442074a5175f5e9e24c50ca Mon Sep 17 00:00:00 2001 From: Stavros Date: Sun, 30 Nov 2025 18:57:24 +0200 Subject: [PATCH 1/6] 1. Bug fix where an ocean tile cannot be assigned if the city is at expansion level greater than 2. 2. Added more conditions to how a tile ownership is determined, and how/if unassigned tiles are correctly assigned to a city. --- C7/MapView.cs | 6 ++-- C7Engine/C7GameData/City.cs | 3 +- C7Engine/C7GameData/GameData.cs | 64 ++++++++++++++++++++++++++------- C7Engine/C7GameData/Tile.cs | 41 +++++++++++++++++++-- 4 files changed, 95 insertions(+), 19 deletions(-) diff --git a/C7/MapView.cs b/C7/MapView.cs index badb5f41..2e1de2f3 100644 --- a/C7/MapView.cs +++ b/C7/MapView.cs @@ -271,7 +271,7 @@ public override void drawObject(LooseView looseView, GameData gameData, Tile til int randomJungleRow = tile.YCoordinate % 2; int randomJungleColumn; ImageTexture jungleTexture; - if (tile.getEdgeNeighbors().Any(t => t.IsWater())) { + if (tile.GetEdgeNeighbors().Any(t => t.IsWater())) { randomJungleColumn = tile.XCoordinate % 6; jungleTexture = smallJungleTexture; } else { @@ -298,7 +298,7 @@ public override void drawObject(LooseView looseView, GameData gameData, Tile til } } else { forestRow = tile.YCoordinate % 2; - if (tile.getEdgeNeighbors().Any(t => t.IsWater())) { + if (tile.GetEdgeNeighbors().Any(t => t.IsWater())) { forestColumn = tile.XCoordinate % 5; if (tile.baseTerrainType.Key == "grassland") { forestTexture = smallForestTexture; @@ -343,7 +343,7 @@ public override void drawObject(LooseView looseView, GameData gameData, Tile til int randomJungleRow = tile.YCoordinate % 2; int randomMarshColumn; ImageTexture marshTexture; - if (tile.getEdgeNeighbors().Any(t => t.IsWater())) { + if (tile.GetEdgeNeighbors().Any(t => t.IsWater())) { randomMarshColumn = tile.XCoordinate % 5; marshTexture = smallMarshTexture; } else { diff --git a/C7Engine/C7GameData/City.cs b/C7Engine/C7GameData/City.cs index 2941a95f..d05c2bb2 100644 --- a/C7Engine/C7GameData/City.cs +++ b/C7Engine/C7GameData/City.cs @@ -670,8 +670,9 @@ public List GetTilesWithinBorders() { private List GetTilesOfRank(int rank) { List result = new(); foreach (Tile t in location.GetTilesWithinRankDistance(rank)) { + // Law II // Ocean tiles may only hold claims of rank 2. - if (t.baseTerrainType.Key == "ocean" && rank > 2) { + if (t.baseTerrainType.Key == "ocean" && t.rankDistanceTo(location) > 2) { continue; } result.Add(t); diff --git a/C7Engine/C7GameData/GameData.cs b/C7Engine/C7GameData/GameData.cs index b114bb47..d2d4eb7c 100644 --- a/C7Engine/C7GameData/GameData.cs +++ b/C7Engine/C7GameData/GameData.cs @@ -147,6 +147,26 @@ public void UpdateTileOwners() { foreach (Player player in players) { player.tileKnowledge.RecomputeActiveTiles(); player.UpdateResourcesInBorders(map.tiles.Where(t => t.owningCity?.owner == player)); + + foreach (Tile t in player.tileKnowledge.knownTiles.Where(t => t.owningCity == null && t.GetEdgeNeighbors().Any(e => e.owningCity != null)).ToList()) { + // Law VII + if (t.neighbors.TryGetValue(TileDirection.NORTHWEST, out Tile tnw) + && t.neighbors.TryGetValue(TileDirection.SOUTHEAST, out Tile tse) + && tnw.owningCity != null && tse.owningCity != null + && tnw.owningCity.owner == tse.owningCity.owner) { + t.owningCity = ResolveTileOwnershipConflict(tnw.owningCity, tse.owningCity, t); + t.owningCity.owner.tileKnowledge.AddTilesToKnown(t); + continue; + } + // Law VIII + if (t.neighbors.TryGetValue(TileDirection.NORTHEAST, out Tile tne) + && t.neighbors.TryGetValue(TileDirection.SOUTHWEST, out Tile tsw) + && tne.owningCity != null && tsw.owningCity != null + && tne.owningCity.owner == tsw.owningCity.owner) { + t.owningCity = ResolveTileOwnershipConflict(tne.owningCity, tsw.owningCity, t); + t.owningCity.owner.tileKnowledge.AddTilesToKnown(t); + } + } } } @@ -256,26 +276,46 @@ public void InvalidateCachedTradeNetwork() { // Rules taken from https://forums.civfanatics.com/threads/the-eight-laws-of-border-dynamics.106882/ private City ResolveTileOwnershipConflict(City a, City b, Tile t) { + if (a.Equals(b)) return a; + int aRank = a.location.rankDistanceTo(t); int bRank = b.location.rankDistanceTo(t); + // Law I + // Cities can claim tiles of rank n+1, where n is the city's expansion level + if (a.GetBorderExpansionLevel() + 1 < aRank && b.GetBorderExpansionLevel() + 1 >= bRank) return b; + if (b.GetBorderExpansionLevel() + 1 < bRank && a.GetBorderExpansionLevel() + 1 >= aRank) return a; + + // Law III // The city with the lowest rank claim gets the tile. - if (aRank > bRank) { - return b; - } else if (aRank < bRank) { - return a; - } + if (aRank > bRank) return b; + if (aRank < bRank) return a; + // Law IV // If the ranks are equal, the city with more culture gets the tile. - if (a.GetCulture() < b.GetCulture()) { - return b; - } else if (a.GetCulture() > b.GetCulture()) { - return a; - } + if (a.GetCulture() + a.GetCulturePerTurn() < b.GetCulture() + b.GetCulturePerTurn()) return b; + if (a.GetCulture() + a.GetCulturePerTurn() > b.GetCulture() + b.GetCulturePerTurn()) return a; + // Law V // If the cultures are equal the oldest city gets the tile. - // TODO: track city age - for now we just return the first. - return a; + // TODO: track city age - for now we are going to skip this. + // return a; + + // Law VI + // Starting North of the disputed tile, we go counter-clockwise + // trying to find the first tile that has one of the competing cities. + // We start at (rank - 1) because the rank distance does not necessarily reflect the actual "ring" + // the city tile is in, so a tile at rank 3, could well mean it's in the 2nd ring. + for (int r = aRank - 1; r <= aRank; r++) { + if (r <= 0) continue; + Tile winner = t.FindInRing(r, tile => tile.HasCity && (tile.cityAtTile == a || tile.cityAtTile == b), false); + if (winner != null) { + return winner.owningCity; + } + } + + // should never happen, if it does some part of the algorithm has gone wrong + throw new Exception($"Failed to resolve ownership of {t} between {a.name} and {b.name}, something went wrong"); } } } diff --git a/C7Engine/C7GameData/Tile.cs b/C7Engine/C7GameData/Tile.cs index a7faa166..befcdc2c 100644 --- a/C7Engine/C7GameData/Tile.cs +++ b/C7Engine/C7GameData/Tile.cs @@ -186,9 +186,13 @@ public bool NeighborsOcean() { /// This is used by some graphics algorithms. /// /// - public Tile[] getEdgeNeighbors() { - Tile[] edgeNeighbors = { neighbors[TileDirection.NORTHEAST], neighbors[TileDirection.NORTHWEST], neighbors[TileDirection.SOUTHEAST], neighbors[TileDirection.SOUTHWEST]}; - return edgeNeighbors; + public Tile[] GetEdgeNeighbors() { + List edgeNeighbors = new(); + if (neighbors.TryGetValue(TileDirection.NORTHEAST, out Tile ne)) edgeNeighbors.Add(ne); + if (neighbors.TryGetValue(TileDirection.NORTHWEST, out Tile nw)) edgeNeighbors.Add(nw); + if (neighbors.TryGetValue(TileDirection.SOUTHEAST, out Tile se)) edgeNeighbors.Add(se); + if (neighbors.TryGetValue(TileDirection.SOUTHWEST, out Tile sw)) edgeNeighbors.Add(sw); + return edgeNeighbors.ToArray(); } public override string ToString() { @@ -612,6 +616,37 @@ public Tile GetTileAtNeighborIndex(int neighborIndex) { return map.tileAt(XCoordinate + xDelta, YCoordinate + yDelta); } + public Tile FindInRing(int rank, Func predicate, bool clockwise = true) { + Tile startingTile = map.tileAt(this.XCoordinate, this.YCoordinate - (2 * rank)); + + if (predicate(startingTile)) return startingTile; + + int dx = startingTile.XCoordinate; + int dy = startingTile.YCoordinate; + + // Going SW(counter-clockwise) or SE(clockwise) + for (int _ = 1; _ < (2 * rank) + 1; _++) { + if (clockwise) { dx++; dy++; } else { dx--; dy++; } + if (predicate(map.tileAt(dx, dy))) return map.tileAt(dx, dy); + } + // Going SE(counter-clockwise) or SW(clockwise) + for (int _ = 1; _ < (2 * rank) + 1; _++) { + if (clockwise) { dx--; dy++; } else { dx++; dy++; } + if (predicate(map.tileAt(dx, dy))) return map.tileAt(dx, dy); + } + // Going NE(counter-clockwise) or NW(clockwise) + for (int _ = 1; _ < (2 * rank) + 1; _++) { + if (clockwise) { dx--; dy--; } else { dx++; dy--; } + if (predicate(map.tileAt(dx, dy))) return map.tileAt(dx, dy); + } + // Going NW(counter-clockwise) or NE(clockwise) + for (int _ = 1; _ < (2 * rank); _++) { + if (clockwise) { dx++; dy--; } else { dx--; dy--; } + if (predicate(map.tileAt(dx, dy))) return map.tileAt(dx, dy); + } + return null; + } + // Returns the tiles in the spiral ordering defined by // GetTileAtNeighborIndex(i). public List GetTilesWithinRankDistance(int rank) { From 60b1fa481e4ba75ed5bef43bc132f3540343d8e9 Mon Sep 17 00:00:00 2001 From: Stavros Date: Wed, 3 Dec 2025 19:57:06 +0200 Subject: [PATCH 2/6] Documenting how FindInRing differs from GetTilesWithinRankDistance --- C7Engine/C7GameData/Tile.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/C7Engine/C7GameData/Tile.cs b/C7Engine/C7GameData/Tile.cs index befcdc2c..9729a429 100644 --- a/C7Engine/C7GameData/Tile.cs +++ b/C7Engine/C7GameData/Tile.cs @@ -616,6 +616,28 @@ public Tile GetTileAtNeighborIndex(int neighborIndex) { return map.tileAt(XCoordinate + xDelta, YCoordinate + yDelta); } + + /// + /// + /// Walks clockwise/counter-clockwise the nth ring around + /// the specified tile starting on the northmost tile + /// and tries to find the first tile that matches our boolean criterion. + /// + /// + /// This differs from , + /// because it includes all the tiles regardless of the distance. + /// An example would be that GetTilesWithinRankDistance() with a rank of 2 + /// will not return a NN, SS, WW, or EE tile, whereas this method will. + /// + /// + /// It is mostly used to calculate to whom we should assign tiles + /// that are being claimed by more than 1 city or civilization. + /// + /// + /// + /// + /// + /// public Tile FindInRing(int rank, Func predicate, bool clockwise = true) { Tile startingTile = map.tileAt(this.XCoordinate, this.YCoordinate - (2 * rank)); From 9cd4157a68905d3b35243ecd1284dc27179d1afa Mon Sep 17 00:00:00 2001 From: Stavros Date: Thu, 4 Dec 2025 04:17:32 +0200 Subject: [PATCH 3/6] Fixed case where tiles are getting off grid, throwing an exception --- C7Engine/C7GameData/Tile.cs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/C7Engine/C7GameData/Tile.cs b/C7Engine/C7GameData/Tile.cs index 9729a429..27e5d02d 100644 --- a/C7Engine/C7GameData/Tile.cs +++ b/C7Engine/C7GameData/Tile.cs @@ -640,35 +640,46 @@ public Tile GetTileAtNeighborIndex(int neighborIndex) { /// public Tile FindInRing(int rank, Func predicate, bool clockwise = true) { Tile startingTile = map.tileAt(this.XCoordinate, this.YCoordinate - (2 * rank)); - - if (predicate(startingTile)) return startingTile; - int dx = startingTile.XCoordinate; int dy = startingTile.YCoordinate; + if (startingTile != Tile.NONE && predicate(startingTile)) return startingTile; + // Going SW(counter-clockwise) or SE(clockwise) for (int _ = 1; _ < (2 * rank) + 1; _++) { if (clockwise) { dx++; dy++; } else { dx--; dy++; } - if (predicate(map.tileAt(dx, dy))) return map.tileAt(dx, dy); + if (!IsInValidCoordinates(dx, dy, out Tile currentTile) || !predicate(currentTile)) continue; + return currentTile; } // Going SE(counter-clockwise) or SW(clockwise) for (int _ = 1; _ < (2 * rank) + 1; _++) { if (clockwise) { dx--; dy++; } else { dx++; dy++; } - if (predicate(map.tileAt(dx, dy))) return map.tileAt(dx, dy); + if (!IsInValidCoordinates(dx, dy, out Tile currentTile) || !predicate(currentTile)) continue; + return currentTile; } // Going NE(counter-clockwise) or NW(clockwise) for (int _ = 1; _ < (2 * rank) + 1; _++) { if (clockwise) { dx--; dy--; } else { dx++; dy--; } - if (predicate(map.tileAt(dx, dy))) return map.tileAt(dx, dy); + if (!IsInValidCoordinates(dx, dy, out Tile currentTile) || !predicate(currentTile)) continue; + return currentTile; } // Going NW(counter-clockwise) or NE(clockwise) for (int _ = 1; _ < (2 * rank); _++) { if (clockwise) { dx++; dy--; } else { dx--; dy--; } - if (predicate(map.tileAt(dx, dy))) return map.tileAt(dx, dy); + if (!IsInValidCoordinates(dx, dy, out Tile currentTile) || !predicate(currentTile)) continue; + return currentTile; } return null; } + private bool IsInValidCoordinates(int dx, int dy, out Tile currentTile) + { + if (map.wrapHorizontally) dx %= map.numTilesWide; + if (map.wrapVertically) dy %= map.numTilesTall; + currentTile = map.tileAt(dx, dy); + return currentTile != null && currentTile != Tile.NONE; + } + // Returns the tiles in the spiral ordering defined by // GetTileAtNeighborIndex(i). public List GetTilesWithinRankDistance(int rank) { From 1a4c0d27c2ecf61be53b7162a3c76cba0a0bd500 Mon Sep 17 00:00:00 2001 From: Stavros Date: Thu, 4 Dec 2025 04:22:22 +0200 Subject: [PATCH 4/6] Whitespace Formatting --- C7Engine/C7GameData/Tile.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/C7Engine/C7GameData/Tile.cs b/C7Engine/C7GameData/Tile.cs index 27e5d02d..8573213a 100644 --- a/C7Engine/C7GameData/Tile.cs +++ b/C7Engine/C7GameData/Tile.cs @@ -672,8 +672,7 @@ public Tile FindInRing(int rank, Func predicate, bool clockwise = tr return null; } - private bool IsInValidCoordinates(int dx, int dy, out Tile currentTile) - { + private bool IsInValidCoordinates(int dx, int dy, out Tile currentTile) { if (map.wrapHorizontally) dx %= map.numTilesWide; if (map.wrapVertically) dy %= map.numTilesTall; currentTile = map.tileAt(dx, dy); From fd1381533e6e26fc45f8804625b4c0b667835092 Mon Sep 17 00:00:00 2001 From: Stavros Date: Thu, 4 Dec 2025 20:42:09 +0200 Subject: [PATCH 5/6] Removed redundant method to check if tile is in valid coordinates --- C7Engine/C7GameData/Tile.cs | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/C7Engine/C7GameData/Tile.cs b/C7Engine/C7GameData/Tile.cs index 8573213a..f6c9bca7 100644 --- a/C7Engine/C7GameData/Tile.cs +++ b/C7Engine/C7GameData/Tile.cs @@ -639,46 +639,43 @@ public Tile GetTileAtNeighborIndex(int neighborIndex) { /// /// public Tile FindInRing(int rank, Func predicate, bool clockwise = true) { - Tile startingTile = map.tileAt(this.XCoordinate, this.YCoordinate - (2 * rank)); - int dx = startingTile.XCoordinate; - int dy = startingTile.YCoordinate; + int x = this.XCoordinate; + int y = this.YCoordinate - (2 * rank); - if (startingTile != Tile.NONE && predicate(startingTile)) return startingTile; + Tile currentTile = map.tileAt(x, y); + if (currentTile != Tile.NONE && predicate(currentTile)) return currentTile; // Going SW(counter-clockwise) or SE(clockwise) for (int _ = 1; _ < (2 * rank) + 1; _++) { - if (clockwise) { dx++; dy++; } else { dx--; dy++; } - if (!IsInValidCoordinates(dx, dy, out Tile currentTile) || !predicate(currentTile)) continue; + if (clockwise) { x++; y++; } else { x--; y++; } + currentTile = map.tileAt(x, y); + if (currentTile == Tile.NONE || !predicate(currentTile)) continue; return currentTile; } // Going SE(counter-clockwise) or SW(clockwise) for (int _ = 1; _ < (2 * rank) + 1; _++) { - if (clockwise) { dx--; dy++; } else { dx++; dy++; } - if (!IsInValidCoordinates(dx, dy, out Tile currentTile) || !predicate(currentTile)) continue; + if (clockwise) { x--; y++; } else { x++; y++; } + currentTile = map.tileAt(x, y); + if (currentTile == Tile.NONE || !predicate(currentTile)) continue; return currentTile; } // Going NE(counter-clockwise) or NW(clockwise) for (int _ = 1; _ < (2 * rank) + 1; _++) { - if (clockwise) { dx--; dy--; } else { dx++; dy--; } - if (!IsInValidCoordinates(dx, dy, out Tile currentTile) || !predicate(currentTile)) continue; + if (clockwise) { x--; y--; } else { x++; y--; } + currentTile = map.tileAt(x, y); + if (currentTile == Tile.NONE || !predicate(currentTile)) continue; return currentTile; } // Going NW(counter-clockwise) or NE(clockwise) for (int _ = 1; _ < (2 * rank); _++) { - if (clockwise) { dx++; dy--; } else { dx--; dy--; } - if (!IsInValidCoordinates(dx, dy, out Tile currentTile) || !predicate(currentTile)) continue; + if (clockwise) { x++; y--; } else { x--; y--; } + currentTile = map.tileAt(x, y); + if (currentTile == Tile.NONE || !predicate(currentTile)) continue; return currentTile; } return null; } - private bool IsInValidCoordinates(int dx, int dy, out Tile currentTile) { - if (map.wrapHorizontally) dx %= map.numTilesWide; - if (map.wrapVertically) dy %= map.numTilesTall; - currentTile = map.tileAt(dx, dy); - return currentTile != null && currentTile != Tile.NONE; - } - // Returns the tiles in the spiral ordering defined by // GetTileAtNeighborIndex(i). public List GetTilesWithinRankDistance(int rank) { From b6c6b793475c8dda635649118cb3b74c6db465b6 Mon Sep 17 00:00:00 2001 From: Stavros Date: Mon, 8 Dec 2025 01:09:30 +0200 Subject: [PATCH 6/6] Check after resolving an ownership conflict that the tile is not an ocean tile with a rank larger than 2 --- C7Engine/C7GameData/GameData.cs | 60 ++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/C7Engine/C7GameData/GameData.cs b/C7Engine/C7GameData/GameData.cs index d2d4eb7c..a1aa392b 100644 --- a/C7Engine/C7GameData/GameData.cs +++ b/C7Engine/C7GameData/GameData.cs @@ -133,8 +133,8 @@ public void UpdateTileOwners() { foreach (Tile t in city.GetTilesWithinBorders()) { // If another city has claim to this tile, we need to resolve // that conflict. - if (t.owningCity != null) { - t.owningCity = ResolveTileOwnershipConflict(t.owningCity, city, t); + if (t.owningCity != null && ResolveTileOwnershipConflict(t.owningCity, city, t, out City winnerCity)) { + t.owningCity = winnerCity; t.owningCity.owner.tileKnowledge.AddTilesToKnown(t, recomputeActiveTiles); continue; } @@ -150,26 +150,29 @@ public void UpdateTileOwners() { foreach (Tile t in player.tileKnowledge.knownTiles.Where(t => t.owningCity == null && t.GetEdgeNeighbors().Any(e => e.owningCity != null)).ToList()) { // Law VII - if (t.neighbors.TryGetValue(TileDirection.NORTHWEST, out Tile tnw) - && t.neighbors.TryGetValue(TileDirection.SOUTHEAST, out Tile tse) - && tnw.owningCity != null && tse.owningCity != null - && tnw.owningCity.owner == tse.owningCity.owner) { - t.owningCity = ResolveTileOwnershipConflict(tnw.owningCity, tse.owningCity, t); - t.owningCity.owner.tileKnowledge.AddTilesToKnown(t); - continue; - } + TryResolveOpposingNeighbors(t, TileDirection.NORTHWEST, TileDirection.SOUTHEAST); + if (t.owningCity != null) continue; // Law VIII - if (t.neighbors.TryGetValue(TileDirection.NORTHEAST, out Tile tne) - && t.neighbors.TryGetValue(TileDirection.SOUTHWEST, out Tile tsw) - && tne.owningCity != null && tsw.owningCity != null - && tne.owningCity.owner == tsw.owningCity.owner) { - t.owningCity = ResolveTileOwnershipConflict(tne.owningCity, tsw.owningCity, t); - t.owningCity.owner.tileKnowledge.AddTilesToKnown(t); - } + TryResolveOpposingNeighbors(t, TileDirection.NORTHEAST, TileDirection.SOUTHWEST); } } } + private void TryResolveOpposingNeighbors(Tile t, TileDirection dirA, TileDirection dirB) { + if (!t.neighbors.TryGetValue(dirA, out Tile a) || !t.neighbors.TryGetValue(dirB, out Tile b)) return; + if (a.owningCity == null || b.owningCity == null) return; + if (a.owningCity.owner != b.owningCity.owner) return; + if (!ResolveTileOwnershipConflict(a.owningCity, b.owningCity, t, out City winnerCity)) return; + + // Law II + if (t.baseTerrainType.Key == "ocean" && t.rankDistanceTo(winnerCity.location) > 2) { + t.owningCity = null; + return; + } + t.owningCity = winnerCity; + winnerCity.owner.tileKnowledge.AddTilesToKnown(t); + } + public void UpdateTileOwnersOnCityDestruction(City city) { city.location.owningCity = null; @@ -275,26 +278,27 @@ public void InvalidateCachedTradeNetwork() { } // Rules taken from https://forums.civfanatics.com/threads/the-eight-laws-of-border-dynamics.106882/ - private City ResolveTileOwnershipConflict(City a, City b, Tile t) { - if (a.Equals(b)) return a; + private bool ResolveTileOwnershipConflict(City a, City b, Tile t, out City owner) { + owner = null; + if (a.Equals(b)) { owner = a; return true; } int aRank = a.location.rankDistanceTo(t); int bRank = b.location.rankDistanceTo(t); // Law I // Cities can claim tiles of rank n+1, where n is the city's expansion level - if (a.GetBorderExpansionLevel() + 1 < aRank && b.GetBorderExpansionLevel() + 1 >= bRank) return b; - if (b.GetBorderExpansionLevel() + 1 < bRank && a.GetBorderExpansionLevel() + 1 >= aRank) return a; + if (a.GetBorderExpansionLevel() + 1 < aRank && b.GetBorderExpansionLevel() + 1 >= bRank) { owner = b; return true; } + if (b.GetBorderExpansionLevel() + 1 < bRank && a.GetBorderExpansionLevel() + 1 >= aRank) { owner = a; return true; } // Law III // The city with the lowest rank claim gets the tile. - if (aRank > bRank) return b; - if (aRank < bRank) return a; + if (aRank > bRank) { owner = b; return true; } + if (aRank < bRank) { owner = a; return true; } // Law IV // If the ranks are equal, the city with more culture gets the tile. - if (a.GetCulture() + a.GetCulturePerTurn() < b.GetCulture() + b.GetCulturePerTurn()) return b; - if (a.GetCulture() + a.GetCulturePerTurn() > b.GetCulture() + b.GetCulturePerTurn()) return a; + if (a.GetCulture() + a.GetCulturePerTurn() < b.GetCulture() + b.GetCulturePerTurn()) { owner = b; return true; } + if (a.GetCulture() + a.GetCulturePerTurn() > b.GetCulture() + b.GetCulturePerTurn()) { owner = a; return true; } // Law V // If the cultures are equal the oldest city gets the tile. @@ -309,9 +313,9 @@ private City ResolveTileOwnershipConflict(City a, City b, Tile t) { for (int r = aRank - 1; r <= aRank; r++) { if (r <= 0) continue; Tile winner = t.FindInRing(r, tile => tile.HasCity && (tile.cityAtTile == a || tile.cityAtTile == b), false); - if (winner != null) { - return winner.owningCity; - } + if (winner == null) continue; + owner = winner.owningCity; + return true; } // should never happen, if it does some part of the algorithm has gone wrong