mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-08 14:57:58 +07:00
Resolved #937 - reveal all hidden tiles visible from every tile on unit movement path
This also solves the problem of units "skipping over" barbarian encampments and ancient ruins when on the path
This commit is contained in:
@ -21,8 +21,8 @@ android {
|
||||
applicationId "com.unciv.app"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 28
|
||||
versionCode 274
|
||||
versionName "2.18.3"
|
||||
versionCode 275
|
||||
versionName "2.18.4"
|
||||
}
|
||||
|
||||
// Had to add this crap for Travis to build, it wanted to sign the app
|
||||
|
@ -8,6 +8,7 @@ import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.GreatPersonManager
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.PathsToTilesWithinTurn
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.gamebasics.unit.UnitType
|
||||
@ -114,7 +115,7 @@ class UnitAutomation{
|
||||
}
|
||||
|
||||
|
||||
fun tryHealUnit(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>):Boolean {
|
||||
fun tryHealUnit(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn):Boolean {
|
||||
val tilesInDistance = unitDistanceToTiles.keys.filter { unit.movement.canMoveTo(it) }
|
||||
if(unitDistanceToTiles.isEmpty()) return true // can't move, so...
|
||||
val unitTile = unit.getTile()
|
||||
@ -145,13 +146,14 @@ class UnitAutomation{
|
||||
return true
|
||||
}
|
||||
|
||||
fun tryPillageImprovement(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) : Boolean {
|
||||
fun tryPillageImprovement(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn) : Boolean {
|
||||
if(unit.type.isCivilian()) return false
|
||||
val tilesInDistance = unitDistanceToTiles.filter {it.value < unit.currentMovement}.keys
|
||||
val tilesThatCanWalkToAndThenPillage = unitDistanceToTiles
|
||||
.filter {it.value.totalDistance < unit.currentMovement}.keys
|
||||
.filter { unit.movement.canMoveTo(it) && UnitActions().canPillage(unit,it) }
|
||||
|
||||
if (tilesInDistance.isEmpty()) return false
|
||||
val tileToPillage = tilesInDistance.maxBy { it.getDefensiveBonus() }!!
|
||||
if (tilesThatCanWalkToAndThenPillage.isEmpty()) return false
|
||||
val tileToPillage = tilesThatCanWalkToAndThenPillage.maxBy { it.getDefensiveBonus() }!!
|
||||
if (unit.getTile()!=tileToPillage)
|
||||
unit.movement.moveToTile(tileToPillage)
|
||||
|
||||
@ -189,7 +191,7 @@ class UnitAutomation{
|
||||
|
||||
class AttackableTile(val tileToAttackFrom:TileInfo, val tileToAttack:TileInfo)
|
||||
|
||||
fun getAttackableEnemies(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): ArrayList<AttackableTile> {
|
||||
fun getAttackableEnemies(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn): ArrayList<AttackableTile> {
|
||||
val tilesWithEnemies = unit.civInfo.viewableTiles
|
||||
.filter { containsAttackableEnemy(it, MapUnitCombatant(unit)) }
|
||||
|
||||
@ -208,7 +210,7 @@ class UnitAutomation{
|
||||
val movementPointsToExpendAfterMovement = if(unitMustBeSetUp) 1 else 0
|
||||
val movementPointsToExpendHere = if(unitMustBeSetUp && unit.action != "Set Up") 1 else 0
|
||||
val movementPointsToExpendBeforeAttack = if(it.key==unit.currentTile) movementPointsToExpendHere else movementPointsToExpendAfterMovement
|
||||
unit.currentMovement - it.value - movementPointsToExpendBeforeAttack > 0.1 } // still got leftover movement points after all that, to attack (0.1 is because of Float nensense, see MapUnit.moveToTile(...)
|
||||
unit.currentMovement - it.value.totalDistance - movementPointsToExpendBeforeAttack > 0.1 } // still got leftover movement points after all that, to attack (0.1 is because of Float nensense, see MapUnit.moveToTile(...)
|
||||
.map { it.key }
|
||||
.filter { unit.movement.canMoveTo(it) || it==unit.getTile() }
|
||||
|
||||
@ -314,7 +316,7 @@ class UnitAutomation{
|
||||
|
||||
// move into position far away enough that the bombard doesn't hurt
|
||||
if(tilesCanAttackFromButNotInBombardRange.any())
|
||||
unit.movement.headTowards(tilesCanAttackFromButNotInBombardRange.minBy { unitDistanceToTiles[it]!! }!!)
|
||||
unit.movement.headTowards(tilesCanAttackFromButNotInBombardRange.minBy { unitDistanceToTiles[it]!!.totalDistance }!!)
|
||||
}
|
||||
else {
|
||||
// calculate total damage of units in surrounding 4-spaces from enemy city (so we can attack a city from 2 directions at once)
|
||||
@ -336,7 +338,7 @@ class UnitAutomation{
|
||||
return false
|
||||
}
|
||||
|
||||
private fun tryDisembarkUnitToAttackPosition(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
|
||||
private fun tryDisembarkUnitToAttackPosition(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn): Boolean {
|
||||
if (!unit.type.isMelee() || !unit.type.isLandUnit() || !unit.isEmbarked()) return false
|
||||
val attackableEnemiesNextTurn = getAttackableEnemies(unit, unitDistanceToTiles)
|
||||
// Only take enemies we can fight without dying
|
||||
@ -457,7 +459,7 @@ class UnitAutomation{
|
||||
return true
|
||||
}
|
||||
|
||||
fun tryGoToRuin(unit:MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
|
||||
fun tryGoToRuin(unit:MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn): Boolean {
|
||||
if(!unit.civInfo.isMajorCiv()) return false // barbs don't have anything to do in ruins
|
||||
val tileWithRuin = unitDistanceToTiles.keys.firstOrNull{unit.movement.canMoveTo(it) && it.improvement == Constants.ancientRuins}
|
||||
if(tileWithRuin==null) return false
|
||||
@ -465,7 +467,7 @@ class UnitAutomation{
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun tryExplore(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
|
||||
internal fun tryExplore(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn): Boolean {
|
||||
if(tryGoToRuin(unit,unitDistanceToTiles))
|
||||
{
|
||||
if(unit.currentMovement==0f) return true
|
||||
@ -502,11 +504,11 @@ class UnitAutomation{
|
||||
}
|
||||
|
||||
|
||||
fun wander(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) {
|
||||
fun wander(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn) {
|
||||
val reachableTiles= unitDistanceToTiles
|
||||
.filter { unit.movement.canMoveTo(it.key) && unit.movement.canReach(it.key) }
|
||||
|
||||
val reachableTilesMaxWalkingDistance = reachableTiles.filter { it.value == unit.currentMovement }
|
||||
val reachableTilesMaxWalkingDistance = reachableTiles.filter { it.value.totalDistance == unit.currentMovement }
|
||||
if (reachableTilesMaxWalkingDistance.any()) unit.movement.moveToTile(reachableTilesMaxWalkingDistance.toList().random().first)
|
||||
else if (reachableTiles.any()) unit.movement.moveToTile(reachableTiles.toList().random().first)
|
||||
|
||||
|
@ -41,12 +41,13 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
return to.getLastTerrain().movementCost.toFloat() // no road
|
||||
}
|
||||
|
||||
class ParentTileAndTotalDistance(val parentTile:TileInfo, val totalDistance: Float)
|
||||
|
||||
fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): HashMap<TileInfo, Float> {
|
||||
if(unitMovement==0f) return hashMapOf()
|
||||
val distanceToTiles = LinkedHashMap<TileInfo, Float>()
|
||||
fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): PathsToTilesWithinTurn {
|
||||
if(unitMovement==0f) return PathsToTilesWithinTurn()
|
||||
val distanceToTiles = PathsToTilesWithinTurn()
|
||||
val unitTile = unit.getTile().tileMap[origin]
|
||||
distanceToTiles[unitTile] = 0f
|
||||
distanceToTiles[unitTile] = ParentTileAndTotalDistance(unitTile,0f)
|
||||
var tilesToCheck = listOf(unitTile)
|
||||
|
||||
while (!tilesToCheck.isEmpty()) {
|
||||
@ -63,10 +64,10 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
|
||||
else {
|
||||
val distanceBetweenTiles = getMovementCostBetweenAdjacentTiles(tileToCheck, neighbor, unit.civInfo)
|
||||
totalDistanceToTile = distanceToTiles[tileToCheck]!! + distanceBetweenTiles
|
||||
totalDistanceToTile = distanceToTiles[tileToCheck]!!.totalDistance + distanceBetweenTiles
|
||||
}
|
||||
|
||||
if (!distanceToTiles.containsKey(neighbor) || distanceToTiles[neighbor]!! > totalDistanceToTile) { // this is the new best path
|
||||
if (!distanceToTiles.containsKey(neighbor) || distanceToTiles[neighbor]!!.totalDistance > totalDistanceToTile) { // this is the new best path
|
||||
if (totalDistanceToTile < unitMovement) // We can still keep moving from here!
|
||||
updatedTiles += neighbor
|
||||
else
|
||||
@ -74,7 +75,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
// In Civ V, you can always travel between adjacent tiles, even if you don't technically
|
||||
// have enough movement points - it simple depletes what you have
|
||||
|
||||
distanceToTiles[neighbor] = totalDistanceToTile
|
||||
distanceToTiles[neighbor] = ParentTileAndTotalDistance(tileToCheck,totalDistanceToTile)
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +102,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn)
|
||||
for (reachableTile in distanceToTilesThisTurn.keys) {
|
||||
if (reachableTile == destination)
|
||||
distanceToDestination[tileToCheck] = distanceToTilesThisTurn[reachableTile]!!
|
||||
distanceToDestination[tileToCheck] = distanceToTilesThisTurn[reachableTile]!!.totalDistance
|
||||
else {
|
||||
if (movementTreeParents.containsKey(reachableTile)) continue // We cannot be faster than anything existing...
|
||||
if (!canMoveTo(reachableTile)) continue // This is a tile that we can''t actually enter - either an intermediary tile containing our unit, or an enemy unit/city
|
||||
@ -163,7 +164,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
if (reachableDestinationNeighbors.isEmpty()) // We can't get closer...
|
||||
return currentTile
|
||||
|
||||
destinationTileThisTurn = reachableDestinationNeighbors.minBy { distanceToTiles[it]!! }!!
|
||||
destinationTileThisTurn = reachableDestinationNeighbors.minBy { distanceToTiles[it]!!.totalDistance }!!
|
||||
}
|
||||
} else { // If the tile is far away, we need to build a path how to get there, and then take the first step
|
||||
val path = getShortestPath(destination)
|
||||
@ -182,24 +183,6 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
return getShortestPath(destination).isNotEmpty()
|
||||
}
|
||||
|
||||
fun getFullPathToCloseTile(destination: TileInfo): List<TileInfo> {
|
||||
val currentUnitTile = unit.getTile()
|
||||
val distanceToTiles = getDistanceToTiles()
|
||||
val reversedList = ArrayList<TileInfo>()
|
||||
var currentTile = destination
|
||||
while(currentTile != currentUnitTile){
|
||||
reversedList.add(currentTile)
|
||||
val distanceToCurrentTile = distanceToTiles[currentTile]!!
|
||||
if(currentUnitTile in currentTile.neighbors
|
||||
&& getMovementCostBetweenAdjacentTiles(currentUnitTile,currentTile,unit.civInfo) == distanceToCurrentTile)
|
||||
return reversedList.reversed()
|
||||
|
||||
for(tile in currentTile.neighbors)
|
||||
currentTile = currentTile.neighbors.first{it in distanceToTiles
|
||||
&& getMovementCostBetweenAdjacentTiles(it,currentTile,unit.civInfo) == distanceToCurrentTile - distanceToTiles[it]!!}
|
||||
}
|
||||
throw Exception("We couldn't get the path between the two tiles")
|
||||
}
|
||||
|
||||
fun teleportToClosestMoveableTile(){
|
||||
var allowedTile:TileInfo? = null
|
||||
@ -225,35 +208,39 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
}
|
||||
|
||||
|
||||
fun moveToTile(otherTile: TileInfo) {
|
||||
if(otherTile==unit.getTile()) return // already here!
|
||||
fun moveToTile(destination: TileInfo) {
|
||||
if(destination==unit.getTile()) return // already here!
|
||||
|
||||
class CantEnterThisTileException(msg: String) : Exception(msg)
|
||||
if(!canMoveTo(otherTile))
|
||||
throw CantEnterThisTileException("$this can't enter $otherTile")
|
||||
if(!canMoveTo(destination))
|
||||
throw CantEnterThisTileException("$this can't enter $destination")
|
||||
|
||||
if(unit.type.isAirUnit()){ // they move differently from all other units
|
||||
unit.action=null
|
||||
unit.removeFromTile()
|
||||
unit.putInTile(otherTile)
|
||||
unit.putInTile(destination)
|
||||
unit.currentMovement=0f
|
||||
return
|
||||
}
|
||||
|
||||
val distanceToTiles = getDistanceToTiles()
|
||||
class YouCantGetThereFromHereException(msg: String) : Exception(msg)
|
||||
if (!distanceToTiles.containsKey(otherTile))
|
||||
throw YouCantGetThereFromHereException("$unit can't get from ${unit.currentTile.position} to ${otherTile.position}.")
|
||||
if (!distanceToTiles.containsKey(destination))
|
||||
throw YouCantGetThereFromHereException("$unit can't get from ${unit.currentTile.position} to ${destination.position}.")
|
||||
|
||||
if(otherTile.isCityCenter() && otherTile.getOwner()!=unit.civInfo)
|
||||
if(destination.isCityCenter() && destination.getOwner()!=unit.civInfo)
|
||||
throw Exception("This is an enemy city, you can't go here!")
|
||||
|
||||
unit.currentMovement -= distanceToTiles[otherTile]!!
|
||||
unit.currentMovement -= distanceToTiles[destination]!!.totalDistance
|
||||
if (unit.currentMovement < 0.1) unit.currentMovement = 0f // silly floats which are "almost zero"
|
||||
if(unit.isFortified() || unit.action=="Set Up" || unit.action=="Sleep")
|
||||
unit.action=null // unfortify/setup after moving
|
||||
unit.removeFromTile()
|
||||
unit.putInTile(otherTile)
|
||||
|
||||
val pathToFinalTile = distanceToTiles.getPathToTile(destination)
|
||||
for(tile in pathToFinalTile){
|
||||
unit.removeFromTile()
|
||||
unit.putInTile(tile)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -349,4 +336,17 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
}
|
||||
return pathsToCities
|
||||
}
|
||||
}
|
||||
|
||||
class PathsToTilesWithinTurn : LinkedHashMap<TileInfo, UnitMovementAlgorithms.ParentTileAndTotalDistance>(){
|
||||
fun getPathToTile(tile: TileInfo): List<TileInfo> {
|
||||
if(!containsKey(tile)) throw Exception("Can't reach this tile!")
|
||||
val reversePathList = ArrayList<TileInfo>()
|
||||
var currentTile = tile
|
||||
while(get(currentTile)!!.parentTile!=currentTile){
|
||||
reversePathList.add(currentTile)
|
||||
currentTile = get(currentTile)!!.parentTile
|
||||
}
|
||||
return reversePathList.reversed()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user