From 1a050bdaa6854971cbb496d7c9aa7c4195580e26 Mon Sep 17 00:00:00 2001 From: Xander Lenstra <71121390+xlenstra@users.noreply.github.com> Date: Sat, 30 Apr 2022 22:08:48 +0200 Subject: [PATCH] Fixed a crash when a plane tried to enter a full city (#6652) This would only occur if the plane was damaged and looking for a spot to heal, and had no tile to go to with >0 healing. It would then try to head towards the nearest city, regardless of whether it could enter that. As it's an air unit, 'head towards' means 'fly there directly this turn', which obviously caused crashes if the unit couldn't enter that tile. --- .../unciv/logic/automation/UnitAutomation.kt | 31 +++++++++++++++---- core/src/com/unciv/logic/map/MapUnit.kt | 2 +- .../unciv/logic/map/UnitMovementAlgorithms.kt | 2 +- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 96760932ef..879d7023ce 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -280,17 +280,36 @@ object UnitAutomation { .filter { it !in dangerousTiles && unit.movement.canMoveTo(it) } val tilesByHealingRate = viableTilesForHealing.groupBy { unit.rankTileForHealing(it) } - if (tilesByHealingRate.keys.none { it != 0 }) { // We can't heal here at all! We're probably embarked - val reachableCityTile = unit.civInfo.cities.asSequence().map { it.getCenterTile() } + if (tilesByHealingRate.keys.all { it == 0 }) { // We can't heal here at all! We're probably embarked + if (!unit.baseUnit.movesLikeAirUnits()) { + val reachableCityTile = unit.civInfo.cities.asSequence() + .map { it.getCenterTile() } .sortedBy { it.aerialDistanceTo(unit.currentTile) } .firstOrNull { unit.movement.canReach(it) } - if (reachableCityTile != null) unit.movement.headTowards(reachableCityTile) - else wander(unit) + if (reachableCityTile != null) unit.movement.headTowards(reachableCityTile) + else wander(unit) + return true + } + // Try to get closer to an empty city + val emptyCities = unit.civInfo.cities.asSequence() + .map { it.getCenterTile() } + .filter { unit.movement.canMoveTo(it) } + if (emptyCities.none()) return false // Nowhere to move to heal + + val nextTileToMove = unitDistanceToTiles.keys + .filter { unit.movement.canMoveTo(it) } + .minByOrNull { tile -> + emptyCities.minOf { city -> + city.aerialDistanceTo(tile) + } + } ?: return false + + unit.movement.moveToTile(nextTileToMove) return true } - val bestTilesForHealing = tilesByHealingRate.maxByOrNull { it.key }!!.value - val bestTileForHealing = bestTilesForHealing.maxByOrNull { it.getDefensiveBonus() }!! + val bestTilesForHealing = tilesByHealingRate.maxByOrNull { it.key }!!.value + val bestTileForHealing = bestTilesForHealing.maxByOrNull { it.getDefensiveBonus() }!! val bestTileForHealingRank = unit.rankTileForHealing(bestTileForHealing) if (currentUnitTile != bestTileForHealing diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 19057e4491..e3a830fd24 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -889,7 +889,7 @@ class MapUnit { fun putInTile(tile: TileInfo) { when { !movement.canMoveTo(tile) -> - throw Exception("I can't go there!") + throw Exception("Unit $name at $currentTile can't be put in tile ${tile.position}!") baseUnit.movesLikeAirUnits() -> tile.airUnits.add(this) isCivilian() -> tile.civilianUnit = this else -> tile.militaryUnit = this diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 8be969b6f6..863453d411 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -261,7 +261,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) { if (currentTile == finalDestination) return currentTile // If we can fly, head there directly - if (unit.baseUnit.movesLikeAirUnits() || unit.isPreparingParadrop()) return finalDestination + if ((unit.baseUnit.movesLikeAirUnits() || unit.isPreparingParadrop()) && canMoveTo(finalDestination)) return finalDestination val distanceToTiles = getDistanceToTiles()