diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index c5292373af..e4c05c6184 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -64,6 +64,10 @@ object Battle { if (defender.isDefeated() && defender is CityCombatant && attacker is MapUnitCombatant && attacker.isMelee() && !attacker.unit.hasUnique("Unable to capture cities")) conquerCity(defender.city, attacker) + // Exploring units surviving an attack should "wake up" + if (!defender.isDefeated() && defender is MapUnitCombatant && defender.unit.action == Constants.unitActionExplore) + defender.unit.action = null + // we're a melee unit and we destroyed\captured an enemy unit postBattleMoveToAttackedTile(attacker, defender, attackedTile) diff --git a/core/src/com/unciv/logic/city/CityExpansionManager.kt b/core/src/com/unciv/logic/city/CityExpansionManager.kt index ccafd5467e..091c3daa82 100644 --- a/core/src/com/unciv/logic/city/CityExpansionManager.kt +++ b/core/src/com/unciv/logic/city/CityExpansionManager.kt @@ -107,6 +107,11 @@ class CityExpansionManager { return null } + /** + * Removes one tile from this city's owned tiles, unconditionally, and updates dependent + * things like worked tiles, locked tiles, and stats. + * @param tileInfo The tile to relinquish + */ fun relinquishOwnership(tileInfo: TileInfo) { cityInfo.tiles = cityInfo.tiles.withoutItem(tileInfo.position) for (city in cityInfo.civInfo.cities) { @@ -124,6 +129,14 @@ class CityExpansionManager { cityInfo.cityStats.update() } + /** + * Takes one tile into possession of this city, either unowned or owned by any other city. + * + * Also manages consequences like auto population reassign, stats, and displacing units + * that are no longer allowed on that tile. + * + * @param tileInfo The tile to take over + */ fun takeOwnership(tileInfo: TileInfo) { if (tileInfo.isCityCenter()) throw Exception("What?!") if (tileInfo.getCity() != null) diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 1ca32e3a98..b0838e7319 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -198,15 +198,11 @@ class MapUnit { return getUniques().any { it.placeholderText == unique } } - fun updateVisibleTiles() { - if (type.isAirUnit()) { - viewableTiles = if (hasUnique("6 tiles in every direction always visible")) - getTile().getTilesInDistance(6).toList() // it's that simple - else listOf() // bomber units don't do recon - civInfo.updateViewableTiles() // for the civ - return - } - + /** + * Determines this (land or sea) unit's current maximum vision range from unit properties, civ uniques and terrain. + * @return Maximum distance of tiles this unit may possibly see + */ + private fun getVisibilityRange(): Int { var visibilityRange = 2 visibilityRange += getUniques().count { it.text == "+1 Visibility Range" } for (unique in civInfo.getMatchingUniques("+[] Sight for all [] units")) @@ -223,13 +219,25 @@ class MapUnit { visibilityRange += 1 // - val tile = getTile() - for (unique in tile.getAllTerrains().flatMap { it.uniqueObjects }) + for (unique in getTile().getAllTerrains().flatMap { it.uniqueObjects }) if (unique.placeholderText == "[] Sight for [] units" && matchesFilter(unique.params[1])) visibilityRange += unique.params[0].toInt() - viewableTiles = tile.getViewableTilesList(visibilityRange) + return visibilityRange + } + /** + * Update this unit's cache of viewable tiles and its civ's as well. + */ + fun updateVisibleTiles() { + if (type.isAirUnit()) { + viewableTiles = if (hasUnique("6 tiles in every direction always visible")) + getTile().getTilesInDistance(6).toList() // it's that simple + else listOf() // bomber units don't do recon + civInfo.updateViewableTiles() // for the civ + return + } + viewableTiles = getTile().getViewableTilesList(getVisibilityRange()) civInfo.updateViewableTiles() // for the civ } @@ -570,9 +578,10 @@ class MapUnit { attacksThisTurn = 0 due = true - // Wake sleeping units if there's an enemy nearby and civilian is not protected + // Wake sleeping units if there's an enemy in vision range: + // Military units always but civilians only if not protected. if (isSleeping() && (!type.isCivilian() || currentTile.militaryUnit == null) && - currentTile.getTilesInDistance(2).any { + this.viewableTiles.any { it.militaryUnit != null && it.militaryUnit!!.civInfo.isAtWarWith(civInfo) } ) diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index c9586dcbe9..10b8c5bf0a 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -284,7 +284,12 @@ class UnitMovementAlgorithms(val unit:MapUnit) { return true } - + /** + * 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. + */ fun teleportToClosestMoveableTile() { var allowedTile: TileInfo? = null var distance = 0 @@ -304,8 +309,12 @@ class UnitMovementAlgorithms(val unit:MapUnit) { } } unit.removeFromTile() // we "teleport" them away - if (allowedTile != null) // it's possible that there is no close tile, and all the guy's cities are full. Screw him then. + if (allowedTile != null) { // it's possible that there is no close tile, and all the guy's cities are full. Screw him then. unit.putInTile(allowedTile) + // Cancel sleep or fortification if forcibly displaced - for now, leave movement / auto / explore orders + if (unit.isSleeping() || unit.isFortified()) + unit.action = null + } } fun moveToTile(destination: TileInfo) {