Code Org - Moved more Unit movement things into UnitMovementAlgorithms

This commit is contained in:
Yair Morgenstern 2019-07-11 21:43:03 +03:00
parent e78f408bff
commit 00e112e668
15 changed files with 198 additions and 201 deletions

View file

@ -19,12 +19,12 @@ class SpecificUnitAutomation{
fun automateWorkBoats(unit: MapUnit) {
val seaResourcesInCities = unit.civInfo.cities.flatMap { it.getTilesInRange() }.asSequence()
.filter { hasWorkableSeaResource(it, unit.civInfo) && (unit.canMoveTo(it) || unit.currentTile == it) }
.filter { hasWorkableSeaResource(it, unit.civInfo) && (unit.movement.canMoveTo(it) || unit.currentTile == it) }
val closestReachableResource = seaResourcesInCities.sortedBy { it.arialDistanceTo(unit.currentTile) }
.firstOrNull { unit.movementAlgs().canReach(it) }
.firstOrNull { unit.movement.canReach(it) }
if (closestReachableResource != null) {
unit.movementAlgs().headTowards(closestReachableResource)
unit.movement.headTowards(closestReachableResource)
if (unit.currentMovement > 0 && unit.currentTile == closestReachableResource) {
val createImprovementAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen)
.firstOrNull { it.name.startsWith("Create") } // could be either fishing boats or oil well
@ -32,12 +32,12 @@ class SpecificUnitAutomation{
return createImprovementAction.action() // unit is already gone, can't "Explore"
}
}
else UnitAutomation().tryExplore(unit, unit.getDistanceToTiles())
else UnitAutomation().tryExplore(unit, unit.movement.getDistanceToTiles())
}
fun automateGreatGeneral(unit: MapUnit){
//try to follow nearby units. Do not garrison in city if possible
val militaryUnitTilesInDistance = unit.getDistanceToTiles().map { it.key }
val militaryUnitTilesInDistance = unit.movement.getDistanceToTiles().map { it.key }
.filter {val militant = it.militaryUnit
militant != null && militant.civInfo == unit.civInfo
&& (it.civilianUnit == null || it.civilianUnit == unit)
@ -49,17 +49,17 @@ class SpecificUnitAutomation{
val militaryUnit = it.militaryUnit
militaryUnit!=null && militaryUnit.civInfo==unit.civInfo
} }
unit.movementAlgs().headTowards(tilesSortedByAffectedTroops.first())
unit.movement.headTowards(tilesSortedByAffectedTroops.first())
return
}
//if no unit to follow, take refuge in city.
val cityToGarrison = unit.civInfo.cities.map {it.getCenterTile()}
.sortedBy { it.arialDistanceTo(unit.currentTile) }
.firstOrNull { it.civilianUnit == null && unit.canMoveTo(it) && unit.movementAlgs().canReach(it)}
.firstOrNull { it.civilianUnit == null && unit.movement.canMoveTo(it) && unit.movement.canReach(it)}
if (cityToGarrison != null) {
unit.movementAlgs().headTowards(cityToGarrison)
unit.movement.headTowards(cityToGarrison)
return
}
}
@ -100,16 +100,16 @@ class SpecificUnitAutomation{
.associateBy ( {it},{ Automation().rankTile(it,unit.civInfo) })
val possibleCityLocations = unit.getTile().getTilesInDistance(5)
.filter { (unit.canMoveTo(it) || unit.currentTile==it) && it !in tilesNearCities && it.isLand }
.filter { (unit.movement.canMoveTo(it) || unit.currentTile==it) && it !in tilesNearCities && it.isLand }
val bestCityLocation: TileInfo? = possibleCityLocations
.asSequence()
.sortedByDescending { rankTileAsCityCenter(it, nearbyTileRankings) }
.firstOrNull { unit.movementAlgs().canReach(it) }
.firstOrNull { unit.movement.canReach(it) }
if(bestCityLocation==null) { // We got a badass over here, all tiles within 5 are taken? Screw it, random walk.
if(UnitAutomation().tryExplore(unit, unit.getDistanceToTiles())) return // try to find new areas
UnitAutomation().wander(unit, unit.getDistanceToTiles()) // go around aimlessly
if(UnitAutomation().tryExplore(unit, unit.movement.getDistanceToTiles())) return // try to find new areas
UnitAutomation().wander(unit, unit.movement.getDistanceToTiles()) // go around aimlessly
return
}
@ -119,7 +119,7 @@ class SpecificUnitAutomation{
if (unit.getTile() == bestCityLocation)
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).first { it.name == "Found city" }.action()
else {
unit.movementAlgs().headTowards(bestCityLocation)
unit.movement.headTowards(bestCityLocation)
if (unit.currentMovement > 0 && unit.getTile() == bestCityLocation)
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).first { it.name == "Found city" }.action()
}
@ -136,24 +136,24 @@ class SpecificUnitAutomation{
stats.toHashMap()[relatedStat]!!
}
for(city in citiesByStatBoost){
val pathToCity =unit.movementAlgs().getShortestPath(city.getCenterTile())
val pathToCity =unit.movement.getShortestPath(city.getCenterTile())
if(pathToCity.isEmpty()) continue
if(pathToCity.size>2){
unit.movementAlgs().headTowards(city.getCenterTile())
unit.movement.headTowards(city.getCenterTile())
return
}
// if we got here, we're pretty close, start looking!
val tiles = city.getTiles().asSequence()
.filter { (unit.canMoveTo(it) || unit.currentTile==it)
.filter { (unit.movement.canMoveTo(it) || unit.currentTile==it)
&& it.isLand
&& !it.isCityCenter()
&& it.resource==null }
.sortedByDescending { Automation().rankTile(it,unit.civInfo) }.toList()
val chosenTile = tiles.firstOrNull { unit.movementAlgs().canReach(it) }
val chosenTile = tiles.firstOrNull { unit.movement.canReach(it) }
if(chosenTile==null) continue // to another city
unit.movementAlgs().headTowards(chosenTile)
unit.movement.headTowards(chosenTile)
if(unit.currentTile==chosenTile && unit.currentMovement>0)
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen)
.first { it.name.startsWith("Create") }.action()
@ -171,12 +171,12 @@ class SpecificUnitAutomation{
if(UnitAutomation().tryAttackNearbyEnemy(unit)) return
val reachableCities = tilesInRange
.filter { it.isCityCenter() && it.getOwner()==unit.civInfo && unit.canMoveTo(it)}
.filter { it.isCityCenter() && it.getOwner()==unit.civInfo && unit.movement.canMoveTo(it)}
for(city in reachableCities){
if(city.getTilesInDistance(unit.getRange())
.any { UnitAutomation().containsAttackableEnemy(it,MapUnitCombatant(unit)) }) {
unit.moveToTile(city)
unit.movement.moveToTile(city)
return
}
}

View file

@ -42,7 +42,7 @@ class UnitAutomation{
}
val unitActions = UnitActions().getUnitActions(unit,UnCivGame.Current.worldScreen)
var unitDistanceToTiles = unit.getDistanceToTiles()
var unitDistanceToTiles = unit.movement.getDistanceToTiles()
if(unit.civInfo.isBarbarianCivilization() &&
unit.currentTile.improvement==Constants.barbarianEncampment && unit.type.isLandUnit()) {
@ -52,7 +52,7 @@ class UnitAutomation{
if(tryGoToRuin(unit,unitDistanceToTiles)){
if(unit.currentMovement==0f) return
unitDistanceToTiles = unit.getDistanceToTiles()
unitDistanceToTiles = unit.movement.getDistanceToTiles()
}
if (tryUpgradeUnit(unit, unitActions)) return
@ -104,15 +104,15 @@ class UnitAutomation{
val encampmentsCloseToCities
= knownEncampments.filter { cities.any { city -> city.getCenterTile().arialDistanceTo(it) < 6 } }
.sortedBy { it.arialDistanceTo(unit.currentTile) }
val encampmentToHeadTowards = encampmentsCloseToCities.firstOrNull { unit.movementAlgs().canReach(it) }
val encampmentToHeadTowards = encampmentsCloseToCities.firstOrNull { unit.movement.canReach(it) }
if(encampmentToHeadTowards==null) return false
unit.movementAlgs().headTowards(encampmentToHeadTowards)
unit.movement.headTowards(encampmentToHeadTowards)
return true
}
fun tryHealUnit(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>):Boolean {
val tilesInDistance = unitDistanceToTiles.keys.filter { unit.canMoveTo(it) }
val tilesInDistance = unitDistanceToTiles.keys.filter { unit.movement.canMoveTo(it) }
if(unitDistanceToTiles.isEmpty()) return true // can't move, so...
val unitTile = unit.getTile()
@ -123,8 +123,8 @@ class UnitAutomation{
if(tilesByHealingRate.keys.none { it!=0 }){// We can't heal here at all! We're probably embarked
val reachableCityTile = unit.civInfo.cities.map { it.getCenterTile() }
.sortedBy { it.arialDistanceTo(unit.currentTile) }
.firstOrNull{unit.movementAlgs().canReach(it)}
if(reachableCityTile!=null) unit.movementAlgs().headTowards(reachableCityTile)
.firstOrNull{unit.movement.canReach(it)}
if(reachableCityTile!=null) unit.movement.headTowards(reachableCityTile)
else wander(unit,unitDistanceToTiles)
return true
}
@ -136,7 +136,7 @@ class UnitAutomation{
if(bestTileForHealingRank == 0) return false // can't actually heal here...
if(unitTile!=bestTileForHealing && bestTileForHealingRank > unit.rankTileForHealing(unitTile))
unit.moveToTile(bestTileForHealing)
unit.movement.moveToTile(bestTileForHealing)
if(unit.currentMovement>0 && unit.canFortify()) unit.fortify()
return true
@ -145,12 +145,12 @@ class UnitAutomation{
fun tryPillageImprovement(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) : Boolean {
if(unit.type.isCivilian()) return false
val tilesInDistance = unitDistanceToTiles.filter {it.value < unit.currentMovement}.keys
.filter { unit.canMoveTo(it) && UnitActions().canPillage(unit,it) }
.filter { unit.movement.canMoveTo(it) && UnitActions().canPillage(unit,it) }
if (tilesInDistance.isEmpty()) return false
val tileToPillage = tilesInDistance.maxBy { it.getDefensiveBonus() }!!
if (unit.getTile()!=tileToPillage)
unit.moveToTile(tileToPillage)
unit.movement.moveToTile(tileToPillage)
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen)
.first { it.name == "Pillage" }.action()
@ -207,7 +207,7 @@ class UnitAutomation{
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(...)
.map { it.key }
.filter { unit.canMoveTo(it) || it==unit.getTile() }
.filter { unit.movement.canMoveTo(it) || it==unit.getTile() }
for(reachableTile in tilesToAttackFrom){ // tiles we'll still have energy after we reach there
val tilesInAttackRange =
@ -227,14 +227,14 @@ class UnitAutomation{
private fun tryAdvanceTowardsCloseEnemy(unit: MapUnit): Boolean {
// this can be sped up if we check each layer separately
var closeEnemies = unit.getTile().getTilesInDistance(5)
.filter{ containsAttackableEnemy(it, MapUnitCombatant(unit)) && unit.movementAlgs().canReach(it)}
.filter{ containsAttackableEnemy(it, MapUnitCombatant(unit)) && unit.movement.canReach(it)}
if(unit.type.isRanged())
closeEnemies = closeEnemies.filterNot { it.isCityCenter() && it.getCity()!!.health==1 }
val closestEnemy = closeEnemies.minBy { it.arialDistanceTo(unit.getTile()) }
if (closestEnemy != null) {
unit.movementAlgs().headTowards(closestEnemy)
unit.movement.headTowards(closestEnemy)
return true
}
return false
@ -244,9 +244,9 @@ class UnitAutomation{
val settlerOrGreatPersonToAccompany = unit.civInfo.getCivUnits()
.firstOrNull { val tile = it.currentTile
(it.name== Constants.settler || it.name in GreatPersonManager().statToGreatPersonMapping.values)
&& tile.militaryUnit==null && unit.canMoveTo(tile) && unit.movementAlgs().canReach(tile) }
&& tile.militaryUnit==null && unit.movement.canMoveTo(tile) && unit.movement.canReach(tile) }
if(settlerOrGreatPersonToAccompany==null) return false
unit.movementAlgs().headTowards(settlerOrGreatPersonToAccompany.currentTile)
unit.movement.headTowards(settlerOrGreatPersonToAccompany.currentTile)
return true
}
@ -280,10 +280,10 @@ class UnitAutomation{
.sortedBy { cityCenterTile -> // sort enemy cities by closeness to our cities, and only then choose the first reachable - checking canReach is comparatively very time-intensive!
unit.civInfo.cities.asSequence().map { cityCenterTile.arialDistanceTo(it.getCenterTile()) }.min()!!
}
.firstOrNull { unit.movementAlgs().canReach(it) }
.firstOrNull { unit.movement.canReach(it) }
if (closestReachableEnemyCity != null) {
val unitDistanceToTiles = unit.getDistanceToTiles()
val unitDistanceToTiles = unit.movement.getDistanceToTiles()
val tilesInBombardRange = closestReachableEnemyCity.getTilesInDistance(2)
val reachableTilesNotInBombardRange = unitDistanceToTiles.keys.filter { it !in tilesInBombardRange }
val canMoveIntoBombardRange = tilesInBombardRange.any { unitDistanceToTiles.containsKey(it)}
@ -293,7 +293,7 @@ class UnitAutomation{
.filter { it.isLand }
val closestReachableLandingGroundTile = suitableGatheringGroundTiles
.sortedBy { it.arialDistanceTo(unit.currentTile) }
.firstOrNull { unit.movementAlgs().canReach(it) }
.firstOrNull { unit.movement.canReach(it) }
// don't head straight to the city, try to head to landing grounds -
// this is against tha AI's brilliant plan of having everyone embarked and attacking via sea when unnecessary.
@ -302,7 +302,7 @@ class UnitAutomation{
if(tileToHeadTo !in tilesInBombardRange) // no need to worry, keep going as the movement alg. says
unit.movementAlgs().headTowards(tileToHeadTo)
unit.movement.headTowards(tileToHeadTo)
else{
if(unit.getRange()>2){ // should never be in a bombardable position
@ -311,7 +311,7 @@ class UnitAutomation{
// move into position far away enough that the bombard doesn't hurt
if(tilesCanAttackFromButNotInBombardRange.any())
unit.movementAlgs().headTowards(tilesCanAttackFromButNotInBombardRange.minBy { unitDistanceToTiles[it]!! }!!)
unit.movement.headTowards(tilesCanAttackFromButNotInBombardRange.minBy { unitDistanceToTiles[it]!! }!!)
}
else {
// calculate total damage of units in surrounding 4-spaces from enemy city (so we can attack a city from 2 directions at once)
@ -324,7 +324,7 @@ class UnitAutomation{
totalAttackOnCityPerTurn += BattleDamage().calculateDamageToDefender(MapUnitCombatant(militaryUnit), enemyCityCombatant)
}
if(totalAttackOnCityPerTurn * 3 > closestReachableEnemyCity.getCity()!!.health) // if we can defeat it in 3 turns with the current units,
unit.movementAlgs().headTowards(closestReachableEnemyCity) // go for it!
unit.movement.headTowards(closestReachableEnemyCity) // go for it!
}
}
@ -346,14 +346,14 @@ class UnitAutomation{
val enemyTileToAttackNextTurn = chooseAttackTarget(unit, attackableEnemiesNextTurn)
if (enemyTileToAttackNextTurn != null) {
unit.moveToTile(enemyTileToAttackNextTurn.tileToAttackFrom)
unit.movement.moveToTile(enemyTileToAttackNextTurn.tileToAttackFrom)
return true
}
return false
}
fun tryAttackNearbyEnemy(unit: MapUnit): Boolean {
val attackableEnemies = getAttackableEnemies(unit, unit.getDistanceToTiles())
val attackableEnemies = getAttackableEnemies(unit, unit.movement.getDistanceToTiles())
// Only take enemies we can fight without dying
.filter {
BattleDamage().calculateDamageToAttacker(MapUnitCombatant(unit),
@ -418,7 +418,7 @@ class UnitAutomation{
val citiesWithoutGarrison = unit.civInfo.cities.filter {
val centerTile = it.getCenterTile()
centerTile.militaryUnit==null
&& unit.canMoveTo(centerTile)
&& unit.movement.canMoveTo(centerTile)
}
fun isCityThatNeedsDefendingInWartime(city: CityInfo): Boolean {
@ -445,17 +445,17 @@ class UnitAutomation{
val closestReachableCityNeedsDefending =citiesToTry
.sortedBy{ it.getCenterTile().arialDistanceTo(unit.currentTile) }
.firstOrNull { unit.movementAlgs().canReach(it.getCenterTile()) }
.firstOrNull { unit.movement.canReach(it.getCenterTile()) }
if(closestReachableCityNeedsDefending==null) return false
unit.movementAlgs().headTowards(closestReachableCityNeedsDefending.getCenterTile())
unit.movement.headTowards(closestReachableCityNeedsDefending.getCenterTile())
return true
}
fun tryGoToRuin(unit:MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
if(!unit.civInfo.isMajorCiv()) return false // barbs don't have anything to do in ruins
val tileWithRuin = unitDistanceToTiles.keys.firstOrNull{unit.canMoveTo(it) && it.improvement == Constants.ancientRuins}
val tileWithRuin = unitDistanceToTiles.keys.firstOrNull{unit.movement.canMoveTo(it) && it.improvement == Constants.ancientRuins}
if(tileWithRuin==null) return false
unit.moveToTile(tileWithRuin)
unit.movement.moveToTile(tileWithRuin)
return true
}
@ -466,16 +466,16 @@ class UnitAutomation{
}
for(tile in unit.currentTile.getTilesInDistance(5))
if(unit.canMoveTo(tile) && tile.position !in unit.civInfo.exploredTiles
&& unit.movementAlgs().canReach(tile)){
unit.movementAlgs().headTowards(tile)
if(unit.movement.canMoveTo(tile) && tile.position !in unit.civInfo.exploredTiles
&& unit.movement.canReach(tile)){
unit.movement.headTowards(tile)
return true
}
return false
}
fun automatedExplore(unit:MapUnit){
val unitDistanceToTiles = unit.getDistanceToTiles()
val unitDistanceToTiles = unit.movement.getDistanceToTiles()
if(tryGoToRuin(unit, unitDistanceToTiles) && unit.currentMovement==0f) return
if (unit.health < 80) {
@ -485,10 +485,10 @@ class UnitAutomation{
for(i in 1..10){
val unexploredTilesAtDistance = unit.getTile().getTilesAtDistance(i)
.filter { unit.canMoveTo(it) && it.position !in unit.civInfo.exploredTiles
&& unit.movementAlgs().canReach(it) }
.filter { unit.movement.canMoveTo(it) && it.position !in unit.civInfo.exploredTiles
&& unit.movement.canReach(it) }
if(unexploredTilesAtDistance.isNotEmpty()){
unit.movementAlgs().headTowards(unexploredTilesAtDistance.random())
unit.movement.headTowards(unexploredTilesAtDistance.random())
return
}
}
@ -498,11 +498,11 @@ class UnitAutomation{
fun wander(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>) {
val reachableTiles= unitDistanceToTiles
.filter { unit.canMoveTo(it.key) && unit.movementAlgs().canReach(it.key) }
.filter { unit.movement.canMoveTo(it.key) && unit.movement.canReach(it.key) }
val reachableTilesMaxWalkingDistance = reachableTiles.filter { it.value == unit.currentMovement }
if (reachableTilesMaxWalkingDistance.any()) unit.moveToTile(reachableTilesMaxWalkingDistance.toList().random().first)
else if (reachableTiles.any()) unit.moveToTile(reachableTiles.toList().random().first)
if (reachableTilesMaxWalkingDistance.any()) unit.movement.moveToTile(reachableTilesMaxWalkingDistance.toList().random().first)
else if (reachableTiles.any()) unit.movement.moveToTile(reachableTiles.toList().random().first)
}

View file

@ -12,7 +12,7 @@ import com.unciv.models.gamebasics.tile.TileImprovement
class WorkerAutomation(val unit: MapUnit) {
fun automateWorkerAction() {
val enemyUnitsInWalkingDistance = unit.getDistanceToTiles().keys
val enemyUnitsInWalkingDistance = unit.movement.getDistanceToTiles().keys
.filter { it.militaryUnit!=null && it.militaryUnit!!.civInfo!=unit.civInfo
&& unit.civInfo.isAtWarWith(it.militaryUnit!!.civInfo) }
@ -26,7 +26,7 @@ class WorkerAutomation(val unit: MapUnit) {
}
if (tileToWork != tile) {
val reachedTile = unit.movementAlgs().headTowards(tileToWork)
val reachedTile = unit.movement.headTowards(tileToWork)
if(reachedTile!=tile) unit.doPreTurnAction() // otherwise, we get a situation where the worker is automated, so it tries to move but doesn't, then tries to automate, then move, etc, forever. Stack overflow exception!
return
}
@ -57,7 +57,7 @@ class WorkerAutomation(val unit: MapUnit) {
if(citiesThatNeedConnecting.isEmpty()) return false // do nothing.
val citiesThatNeedConnectingBfs = citiesThatNeedConnecting
.map { city -> BFS(city.getCenterTile()){it.isLand && unit.canPassThrough(it)} }
.map { city -> BFS(city.getCenterTile()){it.isLand && unit.movement.canPassThrough(it)} }
.toMutableList()
val connectedCities = unit.civInfo.cities.filter { it.isCapital() || it.cityStats.isConnectedToCapital(targetRoad) }
@ -77,10 +77,11 @@ class WorkerAutomation(val unit: MapUnit) {
val tileToConstructRoadOn :TileInfo
if(unit.currentTile in roadableTiles) tileToConstructRoadOn = unit.currentTile
else{
val reachableTiles = roadableTiles.filter { unit.canMoveTo(it)&& unit.movementAlgs().canReach(it)}
val reachableTiles = roadableTiles
.filter { unit.movement.canMoveTo(it)&& unit.movement.canReach(it)}
if(reachableTiles.isEmpty()) continue
tileToConstructRoadOn = reachableTiles.minBy { unit.movementAlgs().getShortestPath(it).size }!!
unit.movementAlgs().headTowards(tileToConstructRoadOn)
tileToConstructRoadOn = reachableTiles.minBy { unit.movement.getShortestPath(it).size }!!
unit.movement.headTowards(tileToConstructRoadOn)
}
if(unit.currentMovement>0 && unit.currentTile==tileToConstructRoadOn
&& unit.currentTile.improvementInProgress!=targetRoad.name)
@ -110,8 +111,7 @@ class WorkerAutomation(val unit: MapUnit) {
// the tile needs to be actually reachable - more difficult than it seems,
// which is why we DON'T calculate this for every possible tile in the radius,
// but only for the tile that's about to be chosen.
val selectedTile = workableTiles.firstOrNull{
unit.movementAlgs().canReach(it) }
val selectedTile = workableTiles.firstOrNull{unit.movement.canReach(it) }
if (selectedTile != null
&& getPriority(selectedTile, unit.civInfo)>1

View file

@ -21,7 +21,7 @@ class Battle(val gameInfo:GameInfo) {
fun moveAndAttack(attacker: ICombatant, attackableTile: UnitAutomation.AttackableTile){
if (attacker is MapUnitCombatant) {
attacker.unit.moveToTile(attackableTile.tileToAttackFrom)
attacker.unit.movement.moveToTile(attackableTile.tileToAttackFrom)
if (attacker.unit.hasUnique("Must set up to ranged attack") && attacker.unit.action != "Set Up") {
attacker.unit.action = "Set Up"
attacker.unit.useMovementPoints(1f)
@ -124,11 +124,11 @@ class Battle(val gameInfo:GameInfo) {
else if (attacker.isMelee()
&& (defender.isDefeated() || defender.getCivInfo()==attacker.getCivInfo())
// This is so that if we attack e.g. a barbarian in enemy territory that we can't enter, we won't enter it
&& (attacker as MapUnitCombatant).unit.canMoveTo(attackedTile)) {
&& (attacker as MapUnitCombatant).unit.movement.canMoveTo(attackedTile)) {
// we destroyed an enemy military unit and there was a civilian unit in the same tile as well
if(attackedTile.civilianUnit!=null && attackedTile.civilianUnit!!.civInfo != attacker.getCivInfo())
captureCivilianUnit(attacker,MapUnitCombatant(attackedTile.civilianUnit!!))
attacker.unit.moveToTile(attackedTile)
attacker.unit.movement.moveToTile(attackedTile)
}
@ -262,7 +262,7 @@ class Battle(val gameInfo:GameInfo) {
}
}
(attacker as MapUnitCombatant).unit.moveToTile(city.getCenterTile())
(attacker as MapUnitCombatant).unit.movement.moveToTile(city.getCenterTile())
}
fun getMapCombatantOfTile(tile:TileInfo): ICombatant? {

View file

@ -113,7 +113,7 @@ class CityExpansionManager {
for(unit in tileInfo.getUnits())
if(!unit.civInfo.canEnterTiles(cityInfo.civInfo))
unit.movementAlgs().teleportToClosestMoveableTile()
unit.movement.teleportToClosestMoveableTile()
cityInfo.civInfo.updateViewableTiles()
}

View file

@ -241,8 +241,8 @@ class CityInfo {
// Edge case! What if a water unit is in a city, and you raze the city?
// Well, the water unit has to return to the water!
for(unit in getCenterTile().getUnits())
if(!unit.canPassThrough(getCenterTile()))
unit.movementAlgs().teleportToClosestMoveableTile()
if(!unit.movement.canPassThrough(getCenterTile()))
unit.movement.teleportToClosestMoveableTile()
civInfo.cities = civInfo.cities.toMutableList().apply { remove(this@CityInfo) }
getTiles().forEach { expansion.relinquishOwnership(it) }

View file

@ -212,7 +212,7 @@ class DiplomacyManager() {
if(hasOpenBorders && !newHasOpenBorders){ // borders were closed, get out!
for(unit in civInfo.getCivUnits().filter { it.currentTile.getOwner()?.civName == otherCivName }){
unit.movementAlgs().teleportToClosestMoveableTile()
unit.movement.teleportToClosestMoveableTile()
}
}
@ -343,11 +343,11 @@ class DiplomacyManager() {
val otherCiv = otherCiv()
// We get out of their territory
for(unit in civInfo.getCivUnits().filter { it.getTile().getOwner()== otherCiv})
unit.movementAlgs().teleportToClosestMoveableTile()
unit.movement.teleportToClosestMoveableTile()
// And we get out of theirs
for(unit in otherCiv.getCivUnits().filter { it.getTile().getOwner()== civInfo})
unit.movementAlgs().teleportToClosestMoveableTile()
unit.movement.teleportToClosestMoveableTile()
}
fun hasFlag(flag:DiplomacyFlags) = flagsCountdown.containsKey(flag.name)

View file

@ -14,8 +14,6 @@ import com.unciv.models.gamebasics.tile.TerrainType
import com.unciv.models.gamebasics.unit.BaseUnit
import com.unciv.models.gamebasics.unit.UnitType
import java.text.DecimalFormat
import java.util.*
import kotlin.collections.ArrayList
class MapUnit {
@ -23,6 +21,8 @@ class MapUnit {
@Transient lateinit var baseUnit: BaseUnit
@Transient internal lateinit var currentTile :TileInfo
@Transient val movement = UnitMovementAlgorithms(this)
// This is saved per each unit because if we need to recalculate viewable tiles every time a unit moves,
// and we need to go over ALL the units, that's a lot of time spent on updating information we should already know!
// About 10% of total NextTurn performance time, at the time of this change!
@ -97,11 +97,6 @@ class MapUnit {
return movement
}
fun getDistanceToTiles(): HashMap<TileInfo, Float> {
val tile = getTile()
return movementAlgs().getDistanceToTilesWithinTurn(tile.position,currentMovement)
}
// This SHOULD NOT be a hashset, because if it is, thenn promotions with the same text (e.g. barrage I, barrage II)
// will not get counted twice!
@Transient var tempUniques= ArrayList<String>()
@ -160,63 +155,10 @@ class MapUnit {
return action!!.split(" ")[1].toInt()
}
fun movementAlgs() = UnitMovementAlgorithms(this)
override fun toString(): String {
return "$name - $owner"
}
// This is the most called function in the entire game,
// so multiple callees of this function have been optimized,
// because optimization on this function results in massive benefits!
fun canPassThrough(tile: TileInfo):Boolean{
if(tile.getBaseTerrain().impassable) return false
if(tile.isLand && type.isWaterUnit() && !tile.isCityCenter())
return false
if(tile.isWater && type.isLandUnit()){
if(!civInfo.tech.unitsCanEmbark) return false
if(tile.isOcean && !civInfo.tech.embarkedUnitsCanEnterOcean)
return false
}
if(tile.isOcean && baseUnit.uniques.contains("Cannot enter ocean tiles")) return false
if(tile.isOcean && baseUnit.uniques.contains("Cannot enter ocean tiles until Astronomy")
&& !civInfo.tech.isResearched("Astronomy"))
return false
val tileOwner = tile.getOwner()
if(tileOwner!=null && tileOwner.civName!=owner) {
if (tile.isCityCenter()) return false
if (!civInfo.canEnterTiles(tileOwner)
&& !(civInfo.isPlayerCivilization() && tileOwner.isCityState())) return false
// AIs won't enter city-state's border.
}
val unitsInTile = tile.getUnits()
if(unitsInTile.isNotEmpty()){
val firstUnit = unitsInTile.first()
if(firstUnit.civInfo != civInfo && civInfo.isAtWarWith(firstUnit.civInfo))
return false
}
return true
}
/**
* Designates whether we can enter the tile - without attacking
* DOES NOT designate whether we can reach that tile in the current turn
*/
fun canMoveTo(tile: TileInfo): Boolean {
if(type.isAirUnit())
return tile.airUnits.size<6 && tile.isCityCenter() && tile.getCity()?.civInfo==civInfo
if(!canPassThrough(tile)) return false
if (type.isCivilian())
return tile.civilianUnit==null && (tile.militaryUnit==null || tile.militaryUnit!!.owner==owner)
else return tile.militaryUnit==null && (tile.civilianUnit==null || tile.civilianUnit!!.owner==owner)
}
fun isIdle(): Boolean {
if (currentMovement == 0f) return false
@ -332,7 +274,7 @@ class MapUnit {
val currentTile = getTile()
if (currentMovement == 0f) return // We've already done stuff this turn, and can't do any more stuff
val enemyUnitsInWalkingDistance = getDistanceToTiles().keys
val enemyUnitsInWalkingDistance = movement.getDistanceToTiles().keys
.filter { it.militaryUnit!=null && civInfo.isAtWarWith(it.militaryUnit!!.civInfo)}
if(enemyUnitsInWalkingDistance.isNotEmpty()) {
if (mapUnitAction?.shouldStopOnEnemyInSight()==true)
@ -346,8 +288,8 @@ class MapUnit {
val destination = action!!.replace("moveTo ", "").split(",").dropLastWhile { it.isEmpty() }.toTypedArray()
val destinationVector = Vector2(Integer.parseInt(destination[0]).toFloat(), Integer.parseInt(destination[1]).toFloat())
val destinationTile = currentTile.tileMap[destinationVector]
if(!movementAlgs().canReach(destinationTile)) return // That tile that we were moving towards is now unreachable
val gotTo = movementAlgs().headTowards(destinationTile)
if(!movement.canReach(destinationTile)) return // That tile that we were moving towards is now unreachable
val gotTo = movement.headTowards(destinationTile)
if(gotTo==currentTile) // We didn't move at all
return
if (gotTo.position == destinationVector) action = null
@ -420,35 +362,6 @@ class MapUnit {
}
}
fun moveToTile(otherTile: TileInfo) {
if(otherTile==getTile()) return // already here!
class CantEnterThisTileException(msg: String) : Exception(msg)
if(!canMoveTo(otherTile))
throw CantEnterThisTileException("$this can't enter $otherTile")
if(type.isAirUnit()){ // they move differently from all other units
action=null
removeFromTile()
putInTile(otherTile)
currentMovement=0f
return
}
val distanceToTiles = getDistanceToTiles()
class YouCantGetThereFromHereException(msg: String) : Exception(msg)
if (!distanceToTiles.containsKey(otherTile))
throw YouCantGetThereFromHereException("$this can't get from ${currentTile.position} to ${otherTile.position}.")
if(otherTile.isCityCenter() && otherTile.getOwner()!=civInfo)
throw Exception("This is an enemy city, you can't go here!")
currentMovement -= distanceToTiles[otherTile]!!
if (currentMovement < 0.1) currentMovement = 0f // silly floats which are "almost zero"
if(isFortified() || action=="Set Up" || action=="Sleep") action=null // unfortify/setup after moving
removeFromTile()
putInTile(otherTile)
}
fun endTurn() {
doPostTurnAction()
@ -464,7 +377,7 @@ class MapUnit {
due = true
val tileOwner = getTile().getOwner()
if(tileOwner!=null && !civInfo.canEnterTiles(tileOwner) && !tileOwner.isCityState()) // if an enemy city expanded onto this tile while I was in it
movementAlgs().teleportToClosestMoveableTile()
movement.teleportToClosestMoveableTile()
doPreTurnAction()
}
@ -483,7 +396,7 @@ class MapUnit {
fun putInTile(tile:TileInfo){
when {
!canMoveTo(tile) -> throw Exception("I can't go there!")
!movement.canMoveTo(tile) -> throw Exception("I can't go there!")
type.isAirUnit() -> tile.airUnits.add(this)
type.isCivilian() -> tile.civilianUnit=this
else -> tile.militaryUnit=this

View file

@ -76,9 +76,9 @@ class TileMap {
val unit = GameBasics.Units[unitName]!!.getMapUnit()
val tilesInDistance = getTilesInDistance(position, 2)
unit.assignOwner(civInfo,false) // both the civ name and actual civ need to be in here in order to calculate the canMoveTo...Darn
var unitToPlaceTile = tilesInDistance.firstOrNull { unit.canMoveTo(it) && (unit.type.isWaterUnit() || it.isLand) }
var unitToPlaceTile = tilesInDistance.firstOrNull { unit.movement.canMoveTo(it) && (unit.type.isWaterUnit() || it.isLand) }
if (unitToPlaceTile==null)
unitToPlaceTile = tilesInDistance.firstOrNull { unit.canMoveTo(it) }
unitToPlaceTile = tilesInDistance.firstOrNull { unit.movement.canMoveTo(it) }
if(unitToPlaceTile!=null) { //see if a land unit can be placed on land. if impossible, put it on water.
// only once we know the unit can be placed do we add it to the civ's unit list

View file

@ -5,8 +5,6 @@ import com.unciv.Constants
import com.unciv.logic.civilization.CivilizationInfo
class UnitMovementAlgorithms(val unit:MapUnit) {
val tileMap = unit.getTile().tileMap
private fun getMovementCostBetweenAdjacentTiles(from: TileInfo, to: TileInfo, civInfo: CivilizationInfo): Float {
var cost = getMovementCostBetweenAdjacentTiles(from,to)
@ -47,7 +45,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): HashMap<TileInfo, Float> {
if(unitMovement==0f) return hashMapOf()
val distanceToTiles = LinkedHashMap<TileInfo, Float>()
val unitTile = tileMap[origin]
val unitTile = unit.getTile().tileMap[origin]
distanceToTiles[unitTile] = 0f
var tilesToCheck = listOf(unitTile)
@ -57,7 +55,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
for (neighbor in tileToCheck.neighbors) {
var totalDistanceToTile:Float
if (!unit.canPassThrough(neighbor))
if (!canPassThrough(neighbor))
totalDistanceToTile = unitMovement // Can't go here.
// The reason that we don't just "return" is so that when calculating how to reach an enemy,
// You need to assume his tile is reachable, otherwise all movement algs on reaching enemy
@ -106,7 +104,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
distanceToDestination[tileToCheck] = distanceToTilesThisTurn[reachableTile]!!
else {
if (movementTreeParents.containsKey(reachableTile)) continue // We cannot be faster than anything existing...
if (!unit.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
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
movementTreeParents[reachableTile] = tileToCheck
newTilesToCheck.add(reachableTile)
}
@ -144,15 +142,15 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
if (currentTile == destination) return currentTile
if(unit.type.isAirUnit()){
unit.moveToTile(destination)
moveToTile(destination)
return destination
}
val distanceToTiles = unit.getDistanceToTiles()
val distanceToTiles = getDistanceToTiles()
val destinationTileThisTurn: TileInfo
if (distanceToTiles.containsKey(destination)) { // we can get there this turn
if (unit.canMoveTo(destination))
if (canMoveTo(destination))
destinationTileThisTurn = destination
else // Someone is blocking to the path to the final tile...
{
@ -161,7 +159,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
return currentTile
val reachableDestinationNeighbors = destinationNeighbors
.filter { distanceToTiles.containsKey(it) && unit.canMoveTo(it) }
.filter { distanceToTiles.containsKey(it) && canMoveTo(it) }
if (reachableDestinationNeighbors.isEmpty()) // We can't get closer...
return currentTile
@ -174,7 +172,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
destinationTileThisTurn = path.first()
}
unit.moveToTile(destinationTileThisTurn)
moveToTile(destinationTileThisTurn)
return destinationTileThisTurn
}
@ -186,7 +184,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
fun getFullPathToCloseTile(destination: TileInfo): List<TileInfo> {
val currentUnitTile = unit.getTile()
val distanceToTiles = unit.getDistanceToTiles()
val distanceToTiles = getDistanceToTiles()
val reversedList = ArrayList<TileInfo>()
var currentTile = destination
while(currentTile != currentUnitTile){
@ -209,7 +207,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
while(allowedTile==null && distance<5){
distance++
allowedTile = unit.getTile().getTilesAtDistance(distance)
.firstOrNull{unit.canMoveTo(it)}
.firstOrNull{canMoveTo(it)}
}
// No tile within 4 spaces? move him to a city.
@ -217,7 +215,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
if(allowedTile==null){
for(city in unit.civInfo.cities){
allowedTile = city.getCenterTile().getTilesInDistance(1)
.firstOrNull { unit.canMoveTo(it) }
.firstOrNull { canMoveTo(it) }
if(allowedTile!=null) break
}
}
@ -226,4 +224,90 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
unit.putInTile(allowedTile)
}
fun moveToTile(otherTile: TileInfo) {
if(otherTile==unit.getTile()) return // already here!
class CantEnterThisTileException(msg: String) : Exception(msg)
if(!canMoveTo(otherTile))
throw CantEnterThisTileException("$this can't enter $otherTile")
if(unit.type.isAirUnit()){ // they move differently from all other units
unit.action=null
unit.removeFromTile()
unit.putInTile(otherTile)
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(otherTile.isCityCenter() && otherTile.getOwner()!=unit.civInfo)
throw Exception("This is an enemy city, you can't go here!")
unit.currentMovement -= distanceToTiles[otherTile]!!
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)
}
/**
* Designates whether we can enter the tile - without attacking
* DOES NOT designate whether we can reach that tile in the current turn
*/
fun canMoveTo(tile: TileInfo): Boolean {
if(unit.type.isAirUnit())
return tile.airUnits.size<6 && tile.isCityCenter() && tile.getCity()?.civInfo==unit.civInfo
if(!canPassThrough(tile)) return false
if (unit.type.isCivilian())
return tile.civilianUnit==null && (tile.militaryUnit==null || tile.militaryUnit!!.owner==unit.owner)
else return tile.militaryUnit==null && (tile.civilianUnit==null || tile.civilianUnit!!.owner==unit.owner)
}
// This is the most called function in the entire game,
// so multiple callees of this function have been optimized,
// because optimization on this function results in massive benefits!
fun canPassThrough(tile: TileInfo):Boolean{
if(tile.getBaseTerrain().impassable) return false
if(tile.isLand && unit.type.isWaterUnit() && !tile.isCityCenter())
return false
if(tile.isWater && unit.type.isLandUnit()){
if(!unit.civInfo.tech.unitsCanEmbark) return false
if(tile.isOcean && !unit.civInfo.tech.embarkedUnitsCanEnterOcean)
return false
}
if(tile.isOcean && unit.baseUnit.uniques.contains("Cannot enter ocean tiles")) return false
if(tile.isOcean && unit.baseUnit.uniques.contains("Cannot enter ocean tiles until Astronomy")
&& !unit.civInfo.tech.isResearched("Astronomy"))
return false
val tileOwner = tile.getOwner()
if(tileOwner!=null && tileOwner.civName!=unit.owner) {
if (tile.isCityCenter()) return false
if (!unit.civInfo.canEnterTiles(tileOwner)
&& !(unit.civInfo.isPlayerCivilization() && tileOwner.isCityState())) return false
// AIs won't enter city-state's border.
}
val unitsInTile = tile.getUnits()
if(unitsInTile.isNotEmpty()){
val firstUnit = unitsInTile.first()
if(firstUnit.civInfo != unit.civInfo && unit.civInfo.isAtWarWith(firstUnit.civInfo))
return false
}
return true
}
fun getDistanceToTiles() = getDistanceToTilesWithinTurn(unit.currentTile.position,unit.currentMovement)
}

View file

@ -52,13 +52,13 @@ class BuildLongRoadAction(
// independent of movement costs, but should respect impassable terrain like water and enemy territory
private fun stepForward(destination: TileInfo): Boolean {
var success = false
val tilesUnitCanCurrentlyReach = unit.getDistanceToTiles().keys
val tilesUnitCanCurrentlyReach = unit.movement.getDistanceToTiles().keys
for (step in getPath(destination).drop(1)) {
if(step !in tilesUnitCanCurrentlyReach) return false // we're out of tiles in reachable distance, no need to check any further
if (unit.currentMovement > 0f) {
if(unit.canMoveTo(step)) {
unit.moveToTile(step)
if(unit.movement.canMoveTo(step)) {
unit.movement.moveToTile(step)
success = true
// if there is a road already, take multiple steps, otherwise this is where we're going to build a road
if (!isRoadFinished(step)) return true
@ -83,7 +83,7 @@ class BuildLongRoadAction(
.getPathTo(destination).reversed()
}
private fun isRoadableTile(it: TileInfo) = it.isLand && unit.canPassThrough(it)
private fun isRoadableTile(it: TileInfo) = it.isLand && unit.movement.canPassThrough(it)
private fun startWorkingOnRoad(): Boolean {
val tile = unit.currentTile

View file

@ -94,7 +94,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
if(offer.type== TradeType.City){
val city = from.cities.first { it.name==offer.name }
city.moveToCiv(to)
city.getCenterTile().getUnits().forEach { it.movementAlgs().teleportToClosestMoveableTile() }
city.getCenterTile().getUnits().forEach { it.movement.teleportToClosestMoveableTile() }
to.updateViewableTiles()
from.updateViewableTiles()
}

View file

@ -102,7 +102,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
val newSelectedUnit = unitTable.selectedUnit
if (previousSelectedUnit != null && previousSelectedUnit.getTile() != tileInfo
&& previousSelectedUnit.canMoveTo(tileInfo) && previousSelectedUnit.movementAlgs().canReach(tileInfo)) {
&& previousSelectedUnit.movement.canMoveTo(tileInfo) && previousSelectedUnit.movement.canReach(tileInfo)) {
// this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread
addTileOverlaysWithUnitMovement(previousSelectedUnit, tileInfo)
}
@ -132,12 +132,12 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
* so that and that alone will be relegated to the concurrent thread.
*/
val turnsToGetThere = if(selectedUnit.type.isAirUnit()) 1
else selectedUnit.movementAlgs().getShortestPath(tileInfo).size // this is what takes the most time, tbh
else selectedUnit.movement.getShortestPath(tileInfo).size // this is what takes the most time, tbh
Gdx.app.postRunnable {
if(UnCivGame.Current.settings.singleTapMove && turnsToGetThere==1) {
// single turn instant move
selectedUnit.movementAlgs().headTowards(tileInfo)
selectedUnit.movement.headTowards(tileInfo)
worldScreen.bottomBar.unitTable.selectedUnit = selectedUnit // keep moved unit selected
} else {
// add "move to" button
@ -287,14 +287,14 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
val isAirUnit = unit.type.isAirUnit()
val tilesInMoveRange = if(isAirUnit) unit.getTile().getTilesInDistance(unit.getRange())
else unit.getDistanceToTiles().keys
else unit.movement.getDistanceToTiles().keys
if(isAirUnit)
for(tile in tilesInMoveRange)
tileGroups[tile]!!.showCircle(Color.BLUE,0.3f)
for (tile: TileInfo in tilesInMoveRange)
if (unit.canMoveTo(tile))
if (unit.movement.canMoveTo(tile))
tileGroups[tile]!!.showCircle(Color.WHITE,
if (UnCivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
@ -302,7 +302,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
val unitType = unit.type
val attackableTiles: List<TileInfo> = if (unitType.isCivilian()) listOf()
else {
val tiles = UnitAutomation().getAttackableEnemies(unit, unit.getDistanceToTiles()).map { it.tileToAttack }
val tiles = UnitAutomation().getAttackableEnemies(unit, unit.movement.getDistanceToTiles()).map { it.tileToAttack }
tiles.filter { (UnCivGame.Current.viewEntireMapForDebug || playerViewableTilePositions.contains(it.position)) }
}

View file

@ -146,7 +146,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
if (attacker.canAttack()) {
if (attacker is MapUnitCombatant) {
attackableEnemy = UnitAutomation()
.getAttackableEnemies(attacker.unit, attacker.unit.getDistanceToTiles())
.getAttackableEnemies(attacker.unit, attacker.unit.movement.getDistanceToTiles())
.firstOrNull{ it.tileToAttack == defender.getTile()}
}
else if (attacker is CityCombatant)

View file

@ -5,13 +5,13 @@ import com.badlogic.gdx.scenes.scene2d.ui.Button
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.action.BuildLongRoadAction
import com.unciv.logic.map.action.MapUnitAction
import com.unciv.ui.utils.CameraStageBaseScreen
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.Sounds
import com.unciv.ui.utils.onClick
import com.unciv.ui.worldscreen.TileMapHolder
import com.unciv.logic.map.action.BuildLongRoadAction
import com.unciv.logic.map.action.MapUnitAction
import kotlin.concurrent.thread
class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUnit, val targetTile: TileInfo) : VerticalGroup() {
@ -57,14 +57,14 @@ class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUni
fun onMoveButtonClick() {
// this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread
thread {
if (selectedUnit.movementAlgs().canReach(targetTile)) {
if (selectedUnit.movement.canReach(targetTile)) {
try {
// Because this is darned concurrent (as it MUST be to avoid ANRs),
// there are edge cases where the canReach is true,
// but until it reaches the headTowards the board has changed and so the headTowards fails.
// I can't think of any way to avoid this,
// but it's so rare and edge-case-y that ignoring its failure is actually acceptable, hence the empty catch
selectedUnit.movementAlgs().headTowards(targetTile)
selectedUnit.movement.headTowards(targetTile)
Sounds.play("whoosh")
if (selectedUnit.currentTile != targetTile)
selectedUnit.action = "moveTo " + targetTile.position.x.toInt() + "," + targetTile.position.y.toInt()