diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 866f046999..b490cf26b7 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -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. diff --git a/tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt b/tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt index 2894baf43f..4078acaa9f 100644 --- a/tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt +++ b/tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt @@ -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) } -} \ No newline at end of file + @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() + 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() }) + } + +}