diff --git a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt index 266e2be8..2c575910 100644 --- a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt @@ -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 } } diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index e2c4e01d..58fced62 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -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):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) : 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): 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) { 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) } diff --git a/core/src/com/unciv/logic/automation/WorkerAutomation.kt b/core/src/com/unciv/logic/automation/WorkerAutomation.kt index 4aad76bf..96324b92 100644 --- a/core/src/com/unciv/logic/automation/WorkerAutomation.kt +++ b/core/src/com/unciv/logic/automation/WorkerAutomation.kt @@ -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 diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index 5677a9b6..19831d16 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -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? { diff --git a/core/src/com/unciv/logic/city/CityExpansionManager.kt b/core/src/com/unciv/logic/city/CityExpansionManager.kt index e5152e15..e2a3470b 100644 --- a/core/src/com/unciv/logic/city/CityExpansionManager.kt +++ b/core/src/com/unciv/logic/city/CityExpansionManager.kt @@ -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() } diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 8a8563ec..b40ffa83 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -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) } diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt index 6c7b67a6..dd2593b3 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt @@ -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) diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index bbffbadb..6b825a4e 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -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 { - 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() @@ -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 diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index d7d56f18..c899655a 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -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 diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 2d28f0f4..d1aad17f 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -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 { if(unitMovement==0f) return hashMapOf() val distanceToTiles = LinkedHashMap() - 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 { val currentUnitTile = unit.getTile() - val distanceToTiles = unit.getDistanceToTiles() + val distanceToTiles = getDistanceToTiles() val reversedList = ArrayList() 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) } \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/action/BuildLongRoadAction.kt b/core/src/com/unciv/logic/map/action/BuildLongRoadAction.kt index 9a9cf451..4f1af396 100644 --- a/core/src/com/unciv/logic/map/action/BuildLongRoadAction.kt +++ b/core/src/com/unciv/logic/map/action/BuildLongRoadAction.kt @@ -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 diff --git a/core/src/com/unciv/logic/trade/TradeLogic.kt b/core/src/com/unciv/logic/trade/TradeLogic.kt index d2f968a8..5be376d1 100644 --- a/core/src/com/unciv/logic/trade/TradeLogic.kt +++ b/core/src/com/unciv/logic/trade/TradeLogic.kt @@ -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() } diff --git a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt index e3e640bc..403267b6 100644 --- a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt @@ -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 = 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)) } } diff --git a/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt b/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt index ad4a933f..bd4a1d36 100644 --- a/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt +++ b/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt @@ -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) diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt index 973145cf..3b1b5ed3 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt @@ -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()