Fix aircraft disappearing when their carrier is teleported (#7035)

* Fix carrier teleporting without moving airplanes

* Unit test to ensure aircraft and carriers are teleported togother

* Fix other cases where teleportation may occur

* Simplify unit test condition

* Update linting and move return condition

* Allow override for teleporting transported units

* Make sure test catches if transported units get deleted

* Reviews
This commit is contained in:
OptimizedForDensity
2022-06-01 15:28:11 -04:00
committed by GitHub
parent 02226018fa
commit 12674ed69e
2 changed files with 54 additions and 5 deletions

View File

@ -370,10 +370,12 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
* Displace a unit - choose a viable tile close by if possible and 'teleport' the unit there.
* This will not use movement points or check for a possible route.
* It is used e.g. if an enemy city expands its borders, or trades or diplomacy change a unit's
* allowed position.
* allowed position. Does not teleport transported units on their own, these are teleported when
* the transporting unit is moved.
* CAN DESTROY THE UNIT.
*/
fun teleportToClosestMoveableTile() {
if (unit.isTransported) return // handled when carrying unit is teleported
var allowedTile: TileInfo? = null
var distance = 0
// When we didn't limit the allowed distance the game would sometimes spend a whole minute looking for a suitable tile.
@ -394,6 +396,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
if (allowedTile != null) break
}
}
val origin = unit.getTile()
if (allowedTile != null) {
unit.removeFromTile() // we "teleport" them away
unit.putInTile(allowedTile)
@ -401,6 +404,15 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
if (unit.isSleeping() || unit.isFortified())
unit.action = null
unit.mostRecentMoveType = UnitMovementMemoryType.UnitTeleported
// bring along the payloads
val payloadUnits = origin.getUnits().filter { it.isTransported && unit.canTransport(it) }.toList()
for (payload in payloadUnits) {
payload.removeFromTile()
payload.putInTile(allowedTile)
payload.isTransported = true // restore the flag to not leave the payload in the city
payload.mostRecentMoveType = UnitMovementMemoryType.UnitTeleported
}
}
// it's possible that there is no close tile, and all the guy's cities are full.
// Nothing we can do.

View File

@ -100,7 +100,7 @@ class UnitMovementAlgorithmsTests {
if (terrain.impassable) continue
tile.baseTerrain = terrain.name
tile.setTransients()
for (type in ruleSet.unitTypes) {
unit.baseUnit = BaseUnit().apply { unitType = type.key; ruleset = ruleSet }
Assert.assertTrue("%s cannot be at %s".format(type.key, terrain.name),
@ -197,7 +197,7 @@ class UnitMovementAlgorithmsTests {
civInfo.tech.techsResearched.remove("Astronomy")
Assert.assertTrue("$type cannot be in Ocean until Astronomy",
(unit.baseUnit.isMelee() || unit.baseUnit.isRanged())
(unit.baseUnit.isMelee() || unit.baseUnit.isRanged())
!= unit.movement.canPassThrough(tile))
civInfo.tech.techsResearched.add("Astronomy")
@ -208,7 +208,7 @@ class UnitMovementAlgorithmsTests {
fun canNOTPassThroughTileWithEnemyUnits() {
tile.baseTerrain = Constants.grassland
tile.setTransients()
unit.currentTile = tile
val otherCiv = CivilizationInfo()
@ -451,4 +451,41 @@ class UnitMovementAlgorithmsTests {
unit.currentTile == newTiles.last() && otherUnit.civInfo == unit.civInfo)
}
}
@Test
fun `can teleport transport and its transported units to the same tile`() {
civInfo.nation.name = Constants.spectator
tile.baseTerrain = Constants.ocean
tile.position.set(0f, 0f)
tile.setTransients()
createOpponentCivAndCity()
val newTiles = generateTileCopies(3)
setupMilitaryUnitInTheCurrentTile("Aircraft Carrier")
unit.owner = civInfo.civName
unit.civInfo = civInfo
unit.baseUnit.uniques.add("Can carry [2] [Aircraft] units")
unit.updateUniques(ruleSet)
civInfo.addUnit(unit, false)
val fighters = ArrayList<MapUnit>()
for (i in 0..1) {
val newFighter = MapUnit()
newFighter.baseUnit = BaseUnit().apply { unitType = "Fighter"; ruleset = ruleSet }
newFighter.owner = civInfo.civName
newFighter.civInfo = civInfo
newFighter.currentTile = unit.getTile()
tile.airUnits += newFighter
newFighter.name = "Fighter"
newFighter.isTransported = true
civInfo.addUnit(newFighter, false)
fighters += newFighter
}
// simulate ejecting all units within foreign territory
for (unit in civInfo.getCivUnits()) unit.movement.teleportToClosestMoveableTile()
Assert.assertTrue("Transport and transported units must be teleported to the same tile",
civInfo.getCivUnits().toList().size == 3 && civInfo.getCivUnits().all { it.getTile() == newTiles.last() })
}
}