mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-25 21:03:15 -04:00
Earlier version with changes mostly to use Sequences (#1993)
* Fixes Issue #1697 by adding information to the special production constructions. * Get rid of extra $ sign in the SpecialConstruction tooltips * Major refactor to use Sequences instead of List to try to improve logic whenever getting a list of tiles at a distance. * Get rid of extraneous parameter * get rid of extra exception. slight refactor placeUnitNearTile for readability * Fix bug of doing intersection instead of union * Add an extra method to get tiles in distance range * Update based on comments
This commit is contained in:
parent
718473fb31
commit
3c2cb01169
@ -164,11 +164,12 @@ class GameInfo {
|
|||||||
// Barbarians will only spawn in places that no one can see
|
// Barbarians will only spawn in places that no one can see
|
||||||
val allViewableTiles = civilizations.filterNot { it.isBarbarian() }
|
val allViewableTiles = civilizations.filterNot { it.isBarbarian() }
|
||||||
.flatMap { it.viewableTiles }.toHashSet()
|
.flatMap { it.viewableTiles }.toHashSet()
|
||||||
val tilesWithin3ofExistingEncampment = existingEncampments.flatMap { it.getTilesInDistance(3) }
|
val tilesWithin3ofExistingEncampment = existingEncampments.asSequence()
|
||||||
|
.flatMap { it.getTilesInDistance(3) }.toSet()
|
||||||
val viableTiles = tileMap.values.filter {
|
val viableTiles = tileMap.values.filter {
|
||||||
!it.getBaseTerrain().impassable && it.isLand
|
!it.getBaseTerrain().impassable && it.isLand
|
||||||
&& it.terrainFeature==null
|
&& it.terrainFeature == null
|
||||||
&& it.naturalWonder==null
|
&& it.naturalWonder == null
|
||||||
&& it !in tilesWithin3ofExistingEncampment
|
&& it !in tilesWithin3ofExistingEncampment
|
||||||
&& it !in allViewableTiles
|
&& it !in allViewableTiles
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,6 @@ import kotlin.math.sqrt
|
|||||||
|
|
||||||
class Automation {
|
class Automation {
|
||||||
|
|
||||||
internal fun rankTile(tile: TileInfo?, civInfo: CivilizationInfo): Float {
|
|
||||||
if (tile == null) return 0f
|
|
||||||
val stats = tile.getTileStats(null, civInfo)
|
|
||||||
var rank = rankStatsValue(stats, civInfo)
|
|
||||||
if (tile.improvement == null) rank += 0.5f // improvement potential!
|
|
||||||
if (tile.hasViewableResource(civInfo)) rank += 1.0f
|
|
||||||
return rank
|
|
||||||
}
|
|
||||||
|
|
||||||
fun rankTileForCityWork(tile:TileInfo, city: CityInfo, foodWeight: Float = 1f): Float {
|
fun rankTileForCityWork(tile:TileInfo, city: CityInfo, foodWeight: Float = 1f): Float {
|
||||||
val stats = tile.getTileStats(city, city.civInfo)
|
val stats = tile.getTileStats(city, city.civInfo)
|
||||||
@ -63,20 +55,6 @@ class Automation {
|
|||||||
return rank
|
return rank
|
||||||
}
|
}
|
||||||
|
|
||||||
fun rankStatsValue(stats: Stats, civInfo: CivilizationInfo): Float {
|
|
||||||
var rank = 0.0f
|
|
||||||
if (stats.food <= 2) rank += (stats.food * 1.2f) //food get more value to keep city growing
|
|
||||||
else rank += (2.4f + (stats.food - 2) / 2) // 1.2 point for each food up to 2, from there on half a point
|
|
||||||
|
|
||||||
if (civInfo.gold < 0 && civInfo.statsForNextTurn.gold <= 0) rank += stats.gold
|
|
||||||
else rank += stats.gold / 3 // 3 gold is much worse than 2 production
|
|
||||||
|
|
||||||
rank += stats.production
|
|
||||||
rank += stats.science
|
|
||||||
rank += stats.culture
|
|
||||||
return rank
|
|
||||||
}
|
|
||||||
|
|
||||||
fun trainMilitaryUnit(city: CityInfo) {
|
fun trainMilitaryUnit(city: CityInfo) {
|
||||||
val name = chooseMilitaryUnit(city)
|
val name = chooseMilitaryUnit(city)
|
||||||
city.cityConstructions.currentConstruction = name
|
city.cityConstructions.currentConstruction = name
|
||||||
@ -128,6 +106,32 @@ class Automation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
internal fun rankTile(tile: TileInfo?, civInfo: CivilizationInfo): Float {
|
||||||
|
if (tile == null) return 0f
|
||||||
|
val stats = tile.getTileStats(null, civInfo)
|
||||||
|
var rank = rankStatsValue(stats, civInfo)
|
||||||
|
if (tile.improvement == null) rank += 0.5f // improvement potential!
|
||||||
|
if (tile.hasViewableResource(civInfo)) rank += 1.0f
|
||||||
|
return rank
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun rankStatsValue(stats: Stats, civInfo: CivilizationInfo): Float {
|
||||||
|
var rank = 0.0f
|
||||||
|
if (stats.food <= 2) rank += (stats.food * 1.2f) //food get more value to keep city growing
|
||||||
|
else rank += (2.4f + (stats.food - 2) / 2) // 1.2 point for each food up to 2, from there on half a point
|
||||||
|
|
||||||
|
if (civInfo.gold < 0 && civInfo.statsForNextTurn.gold <= 0) rank += stats.gold
|
||||||
|
else rank += stats.gold / 3 // 3 gold is much worse than 2 production
|
||||||
|
|
||||||
|
rank += stats.production
|
||||||
|
rank += stats.science
|
||||||
|
rank += stats.culture
|
||||||
|
return rank
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ThreatLevel{
|
enum class ThreatLevel{
|
||||||
|
@ -86,7 +86,7 @@ class BarbarianAutomation(val civInfo: CivilizationInfo) {
|
|||||||
if (unit.health < 100 && UnitAutomation().tryHealUnit(unit, unitDistanceToTiles)) return
|
if (unit.health < 100 && UnitAutomation().tryHealUnit(unit, unitDistanceToTiles)) return
|
||||||
|
|
||||||
// 6 - wander
|
// 6 - wander
|
||||||
UnitAutomation().wander(unit, unitDistanceToTiles)
|
UnitAutomation.wander(unit, unitDistanceToTiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun automateScout(unit: MapUnit) {
|
private fun automateScout(unit: MapUnit) {
|
||||||
@ -114,7 +114,7 @@ class BarbarianAutomation(val civInfo: CivilizationInfo) {
|
|||||||
if (unit.health < 100 && UnitAutomation().tryHealUnit(unit, unitDistanceToTiles)) return
|
if (unit.health < 100 && UnitAutomation().tryHealUnit(unit, unitDistanceToTiles)) return
|
||||||
|
|
||||||
// 5 - wander
|
// 5 - wander
|
||||||
UnitAutomation().wander(unit, unitDistanceToTiles)
|
UnitAutomation.wander(unit, unitDistanceToTiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findFurthestTileCanMoveTo(
|
private fun findFurthestTileCanMoveTo(
|
||||||
|
@ -63,9 +63,10 @@ class BattleHelper {
|
|||||||
val tilesInAttackRange =
|
val tilesInAttackRange =
|
||||||
if (unit.hasUnique("Ranged attacks may be performed over obstacles") || unit.type.isAirUnit())
|
if (unit.hasUnique("Ranged attacks may be performed over obstacles") || unit.type.isAirUnit())
|
||||||
reachableTile.getTilesInDistance(rangeOfAttack)
|
reachableTile.getTilesInDistance(rangeOfAttack)
|
||||||
else reachableTile.getViewableTiles(rangeOfAttack, unit.type.isWaterUnit())
|
else reachableTile.getViewableTilesList(rangeOfAttack, unit.type.isWaterUnit())
|
||||||
|
.asSequence()
|
||||||
|
|
||||||
attackableTiles += tilesInAttackRange.asSequence().filter { it in tilesWithEnemies }
|
attackableTiles += tilesInAttackRange.filter { it in tilesWithEnemies }
|
||||||
.map { AttackableTile(reachableTile, it) }
|
.map { AttackableTile(reachableTile, it) }
|
||||||
}
|
}
|
||||||
return attackableTiles
|
return attackableTiles
|
||||||
|
@ -37,7 +37,7 @@ class SpecificUnitAutomation{
|
|||||||
return createImprovementAction()
|
return createImprovementAction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else UnitAutomation().tryExplore(unit, unit.movement.getDistanceToTiles())
|
else UnitAutomation.tryExplore(unit, unit.movement.getDistanceToTiles())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun automateGreatGeneral(unit: MapUnit){
|
fun automateGreatGeneral(unit: MapUnit){
|
||||||
@ -72,14 +72,13 @@ class SpecificUnitAutomation{
|
|||||||
fun rankTileAsCityCenter(tileInfo: TileInfo, nearbyTileRankings: Map<TileInfo, Float>,
|
fun rankTileAsCityCenter(tileInfo: TileInfo, nearbyTileRankings: Map<TileInfo, Float>,
|
||||||
luxuryResourcesInCivArea: Sequence<TileResource>): Float {
|
luxuryResourcesInCivArea: Sequence<TileResource>): Float {
|
||||||
val bestTilesFromOuterLayer = tileInfo.getTilesAtDistance(2)
|
val bestTilesFromOuterLayer = tileInfo.getTilesAtDistance(2)
|
||||||
.asSequence()
|
|
||||||
.sortedByDescending { nearbyTileRankings[it] }.take(2)
|
.sortedByDescending { nearbyTileRankings[it] }.take(2)
|
||||||
.toList()
|
.toList()
|
||||||
val top5Tiles = tileInfo.neighbors.union(bestTilesFromOuterLayer)
|
val top5Tiles = tileInfo.neighbors.union(bestTilesFromOuterLayer)
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.sortedByDescending { nearbyTileRankings[it] }
|
.sortedByDescending { nearbyTileRankings[it] }
|
||||||
.take(5)
|
.take(5)
|
||||||
var rank = top5Tiles.map { nearbyTileRankings[it]!! }.sum()
|
var rank = top5Tiles.map { nearbyTileRankings.getValue(it) }.sum()
|
||||||
if (tileInfo.isCoastalTile()) rank += 5
|
if (tileInfo.isCoastalTile()) rank += 5
|
||||||
|
|
||||||
val luxuryResourcesInCityArea = tileInfo.getTilesAtDistance(2).filter { it.resource!=null }
|
val luxuryResourcesInCityArea = tileInfo.getTilesAtDistance(2).filter { it.resource!=null }
|
||||||
@ -94,9 +93,9 @@ class SpecificUnitAutomation{
|
|||||||
|
|
||||||
|
|
||||||
fun automateSettlerActions(unit: MapUnit) {
|
fun automateSettlerActions(unit: MapUnit) {
|
||||||
if(unit.getTile().militaryUnit==null) return // Don't move until you're accompanied by a military unit
|
if (unit.getTile().militaryUnit == null) return // Don't move until you're accompanied by a military unit
|
||||||
|
|
||||||
val tilesNearCities = unit.civInfo.gameInfo.getCities()
|
val tilesNearCities = unit.civInfo.gameInfo.getCities().asSequence()
|
||||||
.flatMap {
|
.flatMap {
|
||||||
val distanceAwayFromCity =
|
val distanceAwayFromCity =
|
||||||
if (unit.civInfo.knows(it.civInfo)
|
if (unit.civInfo.knows(it.civInfo)
|
||||||
@ -106,34 +105,36 @@ class SpecificUnitAutomation{
|
|||||||
else 3
|
else 3
|
||||||
it.getCenterTile().getTilesInDistance(distanceAwayFromCity)
|
it.getCenterTile().getTilesInDistance(distanceAwayFromCity)
|
||||||
}
|
}
|
||||||
.toHashSet()
|
.toSet()
|
||||||
|
|
||||||
// This is to improve performance - instead of ranking each tile in the area up to 19 times, do it once.
|
// This is to improve performance - instead of ranking each tile in the area up to 19 times, do it once.
|
||||||
val nearbyTileRankings = unit.getTile().getTilesInDistance(7)
|
val nearbyTileRankings = unit.getTile().getTilesInDistance(7)
|
||||||
.associateBy ( {it},{ Automation().rankTile(it,unit.civInfo) })
|
.associateBy({ it }, { Automation.rankTile(it, unit.civInfo) })
|
||||||
|
|
||||||
val possibleCityLocations = unit.getTile().getTilesInDistance(5)
|
val possibleCityLocations = unit.getTile().getTilesInDistance(5)
|
||||||
.filter { val tileOwner=it.getOwner()
|
.filter {
|
||||||
it.isLand && (tileOwner==null || tileOwner==unit.civInfo) && // don't allow settler to settle inside other civ's territory
|
val tileOwner = it.getOwner()
|
||||||
(unit.movement.canMoveTo(it) || unit.currentTile==it)
|
it.isLand && (tileOwner == null || tileOwner == unit.civInfo) && // don't allow settler to settle inside other civ's territory
|
||||||
&& it !in tilesNearCities }
|
(unit.movement.canMoveTo(it) || unit.currentTile == it)
|
||||||
|
&& it !in tilesNearCities
|
||||||
|
}
|
||||||
|
|
||||||
val luxuryResourcesInCivArea = unit.civInfo.cities.asSequence()
|
val luxuryResourcesInCivArea = unit.civInfo.cities.asSequence()
|
||||||
.flatMap { it.getTiles().asSequence() }.filter { it.resource!=null }
|
.flatMap { it.getTiles().asSequence() }.filter { it.resource != null }
|
||||||
.map { it.getTileResource() }.filter { it.resourceType==ResourceType.Luxury }
|
.map { it.getTileResource() }.filter { it.resourceType == ResourceType.Luxury }
|
||||||
.distinct()
|
.distinct()
|
||||||
val bestCityLocation: TileInfo? = possibleCityLocations
|
val bestCityLocation: TileInfo? = possibleCityLocations
|
||||||
.asSequence()
|
|
||||||
.sortedByDescending { rankTileAsCityCenter(it, nearbyTileRankings, luxuryResourcesInCivArea) }
|
.sortedByDescending { rankTileAsCityCenter(it, nearbyTileRankings, luxuryResourcesInCivArea) }
|
||||||
.firstOrNull { unit.movement.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 (bestCityLocation == null) { // We got a badass over here, all tiles within 5 are taken? Screw it, random walk.
|
||||||
if (UnitAutomation().tryExplore(unit, unit.movement.getDistanceToTiles())) return // try to find new areas
|
val unitDistanceToTiles = unit.movement.getDistanceToTiles()
|
||||||
UnitAutomation().wander(unit, unit.movement.getDistanceToTiles()) // go around aimlessly
|
if (UnitAutomation.tryExplore(unit, unitDistanceToTiles)) return // try to find new areas
|
||||||
|
UnitAutomation.wander(unit, unitDistanceToTiles) // go around aimlessly
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bestCityLocation.getTilesInDistance(3).any { it.isCityCenter() })
|
if (bestCityLocation.getTilesInDistance(3).any { it.isCityCenter() })
|
||||||
throw Exception("City within distance")
|
throw Exception("City within distance")
|
||||||
|
|
||||||
if (unit.getTile() == bestCityLocation)
|
if (unit.getTile() == bestCityLocation)
|
||||||
@ -169,9 +170,9 @@ class SpecificUnitAutomation{
|
|||||||
&& it.isLand
|
&& it.isLand
|
||||||
&& !it.isCityCenter()
|
&& !it.isCityCenter()
|
||||||
&& it.resource==null }
|
&& it.resource==null }
|
||||||
.sortedByDescending { Automation().rankTile(it,unit.civInfo) }.toList()
|
.sortedByDescending { Automation.rankTile(it,unit.civInfo) }.toList()
|
||||||
val chosenTile = tiles.firstOrNull { unit.movement.canReach(it) }
|
val chosenTile = tiles.firstOrNull { unit.movement.canReach(it) } ?: continue // to another city
|
||||||
if(chosenTile==null) continue // to another city
|
|
||||||
|
|
||||||
unit.movement.headTowards(chosenTile)
|
unit.movement.headTowards(chosenTile)
|
||||||
if(unit.currentTile==chosenTile && unit.currentMovement > 0)
|
if(unit.currentTile==chosenTile && unit.currentMovement > 0)
|
||||||
@ -185,35 +186,41 @@ class SpecificUnitAutomation{
|
|||||||
fun automateFighter(unit: MapUnit) {
|
fun automateFighter(unit: MapUnit) {
|
||||||
val tilesInRange = unit.currentTile.getTilesInDistance(unit.getRange())
|
val tilesInRange = unit.currentTile.getTilesInDistance(unit.getRange())
|
||||||
val enemyAirUnitsInRange = tilesInRange
|
val enemyAirUnitsInRange = tilesInRange
|
||||||
.flatMap { it.airUnits }.filter { it.civInfo.isAtWarWith(unit.civInfo) }
|
.flatMap { it.airUnits.asSequence() }.filter { it.civInfo.isAtWarWith(unit.civInfo) }
|
||||||
|
|
||||||
if(enemyAirUnitsInRange.isNotEmpty()) return // we need to be on standby in case they attack
|
if (enemyAirUnitsInRange.any()) return // we need to be on standby in case they attack
|
||||||
if(battleHelper.tryAttackNearbyEnemy(unit)) return
|
if (battleHelper.tryAttackNearbyEnemy(unit)) return
|
||||||
|
|
||||||
// TODO Implement consideration for landing on aircraft carrier
|
// TODO Implement consideration for landing on aircraft carrier
|
||||||
|
|
||||||
val immediatelyReachableCities = tilesInRange
|
val immediatelyReachableCities = tilesInRange
|
||||||
.filter { it.isCityCenter() && it.getOwner()==unit.civInfo && unit.movement.canMoveTo(it)}
|
.filter { it.isCityCenter() && it.getOwner() == unit.civInfo && unit.movement.canMoveTo(it) }
|
||||||
|
|
||||||
for(city in immediatelyReachableCities){
|
for (city in immediatelyReachableCities) {
|
||||||
if(city.getTilesInDistance(unit.getRange())
|
if (city.getTilesInDistance(unit.getRange())
|
||||||
.any { battleHelper.containsAttackableEnemy(it,MapUnitCombatant(unit)) }) {
|
.any { battleHelper.containsAttackableEnemy(it, MapUnitCombatant(unit)) }) {
|
||||||
unit.movement.moveToTile(city)
|
unit.movement.moveToTile(city)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val pathsToCities = unit.movement.getArialPathsToCities()
|
val pathsToCities = unit.movement.getArialPathsToCities()
|
||||||
if(pathsToCities.isEmpty()) return // can't actually move anywhere else
|
if (pathsToCities.isEmpty()) return // can't actually move anywhere else
|
||||||
|
|
||||||
val citiesByNearbyAirUnits = pathsToCities.keys
|
val citiesByNearbyAirUnits = pathsToCities.keys
|
||||||
.groupBy { it.getTilesInDistance(unit.getRange())
|
.groupBy { key ->
|
||||||
.count{it.airUnits.size>0 && it.airUnits.first().civInfo.isAtWarWith(unit.civInfo)} }
|
key.getTilesInDistance(unit.getRange())
|
||||||
|
.count {
|
||||||
|
val firstAirUnit = it.airUnits.firstOrNull()
|
||||||
|
firstAirUnit != null && firstAirUnit.civInfo.isAtWarWith(unit.civInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(citiesByNearbyAirUnits.keys.any { it!=0 }){
|
if (citiesByNearbyAirUnits.keys.any { it != 0 }) {
|
||||||
val citiesWithMostNeedOfAirUnits = citiesByNearbyAirUnits.maxBy { it.key }!!.value
|
val citiesWithMostNeedOfAirUnits = citiesByNearbyAirUnits.maxBy { it.key }!!.value
|
||||||
val chosenCity = citiesWithMostNeedOfAirUnits.minBy { pathsToCities[it]!!.size }!! // city with min path = least turns to get there
|
//todo: maybe groupby size and choose highest priority within the same size turns
|
||||||
val firstStepInPath = pathsToCities[chosenCity]!!.first()
|
val chosenCity = citiesWithMostNeedOfAirUnits.minBy { pathsToCities.getValue(it).size }!! // city with min path = least turns to get there
|
||||||
|
val firstStepInPath = pathsToCities.getValue(chosenCity).first()
|
||||||
unit.movement.moveToTile(firstStepInPath)
|
unit.movement.moveToTile(firstStepInPath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -255,6 +262,8 @@ class SpecificUnitAutomation{
|
|||||||
}
|
}
|
||||||
if (citiesThatCanAttackFrom.isEmpty()) return
|
if (citiesThatCanAttackFrom.isEmpty()) return
|
||||||
|
|
||||||
|
//todo: this logic looks similar to some parts of automateFighter, maybe pull out common code
|
||||||
|
//todo: maybe groupby size and choose highest priority within the same size turns
|
||||||
val closestCityThatCanAttackFrom = citiesThatCanAttackFrom.minBy { pathsToCities[it]!!.size }!!
|
val closestCityThatCanAttackFrom = citiesThatCanAttackFrom.minBy { pathsToCities[it]!!.size }!!
|
||||||
val firstStepInPath = pathsToCities[closestCityThatCanAttackFrom]!!.first()
|
val firstStepInPath = pathsToCities[closestCityThatCanAttackFrom]!!.first()
|
||||||
airUnit.movement.moveToTile(firstStepInPath)
|
airUnit.movement.moveToTile(firstStepInPath)
|
||||||
|
@ -3,10 +3,7 @@ package com.unciv.logic.automation
|
|||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.logic.battle.Battle
|
import com.unciv.logic.battle.*
|
||||||
import com.unciv.logic.battle.BattleDamage
|
|
||||||
import com.unciv.logic.battle.CityCombatant
|
|
||||||
import com.unciv.logic.battle.MapUnitCombatant
|
|
||||||
import com.unciv.logic.city.CityInfo
|
import com.unciv.logic.city.CityInfo
|
||||||
import com.unciv.logic.civilization.GreatPersonManager
|
import com.unciv.logic.civilization.GreatPersonManager
|
||||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||||
@ -23,6 +20,41 @@ class UnitAutomation {
|
|||||||
companion object {
|
companion object {
|
||||||
const val CLOSE_ENEMY_TILES_AWAY_LIMIT = 5
|
const val CLOSE_ENEMY_TILES_AWAY_LIMIT = 5
|
||||||
const val CLOSE_ENEMY_TURNS_AWAY_LIMIT = 3f
|
const val CLOSE_ENEMY_TURNS_AWAY_LIMIT = 3f
|
||||||
|
|
||||||
|
internal fun tryExplore(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn): Boolean {
|
||||||
|
if (tryGoToRuin(unit, unitDistanceToTiles) && unit.currentMovement == 0f) return true
|
||||||
|
|
||||||
|
for (tile in unit.currentTile.getTilesInDistance(10))
|
||||||
|
if (unit.movement.canMoveTo(tile) && tile.position !in unit.civInfo.exploredTiles
|
||||||
|
&& unit.movement.canReach(tile)
|
||||||
|
&& (tile.getOwner() == null || !tile.getOwner()!!.isCityState())) {
|
||||||
|
unit.movement.headTowards(tile)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryGoToRuin(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn): Boolean {
|
||||||
|
if (!unit.civInfo.isMajorCiv()) return false // barbs don't have anything to do in ruins
|
||||||
|
val tileWithRuin = unitDistanceToTiles.keys
|
||||||
|
.firstOrNull {
|
||||||
|
it.improvement == Constants.ancientRuins && unit.movement.canMoveTo(it)
|
||||||
|
}
|
||||||
|
if (tileWithRuin == null)
|
||||||
|
return false
|
||||||
|
unit.movement.moveToTile(tileWithRuin)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun wander(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn) {
|
||||||
|
val reachableTiles = unitDistanceToTiles
|
||||||
|
.filter { unit.movement.canMoveTo(it.key) && unit.movement.canReach(it.key) }
|
||||||
|
|
||||||
|
val reachableTilesMaxWalkingDistance = reachableTiles.filter { it.value.totalDistance == unit.currentMovement }
|
||||||
|
if (reachableTilesMaxWalkingDistance.any()) unit.movement.moveToTile(reachableTilesMaxWalkingDistance.toList().random().first)
|
||||||
|
else if (reachableTiles.any()) unit.movement.moveToTile(reachableTiles.keys.random())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val battleHelper = BattleHelper()
|
private val battleHelper = BattleHelper()
|
||||||
@ -162,10 +194,9 @@ class UnitAutomation {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBombardTargets(city: CityInfo): List<TileInfo> {
|
fun getBombardTargets(city: CityInfo): Sequence<TileInfo> =
|
||||||
return city.getCenterTile().getViewableTiles(city.range, true)
|
city.getCenterTile().getTilesInDistance(city.range)
|
||||||
.filter { battleHelper.containsAttackableEnemy(it, CityCombatant(city)) }
|
.filter { battleHelper.containsAttackableEnemy(it, CityCombatant(city)) }
|
||||||
}
|
|
||||||
|
|
||||||
/** Move towards the closest attackable enemy of the [unit].
|
/** Move towards the closest attackable enemy of the [unit].
|
||||||
*
|
*
|
||||||
@ -180,7 +211,7 @@ class UnitAutomation {
|
|||||||
var closeEnemies = battleHelper.getAttackableEnemies(
|
var closeEnemies = battleHelper.getAttackableEnemies(
|
||||||
unit,
|
unit,
|
||||||
unitDistanceToTiles,
|
unitDistanceToTiles,
|
||||||
tilesToCheck = unit.getTile().getTilesInDistance(CLOSE_ENEMY_TILES_AWAY_LIMIT)
|
tilesToCheck = unit.getTile().getTilesInDistance(CLOSE_ENEMY_TILES_AWAY_LIMIT).toList()
|
||||||
).filter {
|
).filter {
|
||||||
// Ignore units that would 1-shot you if you attacked
|
// Ignore units that would 1-shot you if you attacked
|
||||||
BattleDamage().calculateDamageToAttacker(MapUnitCombatant(unit),
|
BattleDamage().calculateDamageToAttacker(MapUnitCombatant(unit),
|
||||||
@ -246,34 +277,35 @@ class UnitAutomation {
|
|||||||
|
|
||||||
if (closestReachableEnemyCity != null) {
|
if (closestReachableEnemyCity != null) {
|
||||||
val unitDistanceToTiles = unit.movement.getDistanceToTiles()
|
val unitDistanceToTiles = unit.movement.getDistanceToTiles()
|
||||||
val tilesInBombardRange = closestReachableEnemyCity.getTilesInDistance(2)
|
val tilesInBombardRange = closestReachableEnemyCity.getTilesInDistance(2).toSet()
|
||||||
val reachableTilesNotInBombardRange = unitDistanceToTiles.keys.filter { it !in tilesInBombardRange }
|
|
||||||
|
|
||||||
val suitableGatheringGroundTiles = closestReachableEnemyCity.getTilesAtDistance(4)
|
|
||||||
.union(closestReachableEnemyCity.getTilesAtDistance(3))
|
|
||||||
.filter { it.isLand }
|
|
||||||
|
|
||||||
// don't head straight to the city, try to head to landing grounds -
|
// 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.
|
// this is against tha AI's brilliant plan of having everyone embarked and attacking via sea when unnecessary.
|
||||||
val tileToHeadTo = suitableGatheringGroundTiles
|
val tileToHeadTo = closestReachableEnemyCity.getTilesInDistanceRange(3..4)
|
||||||
.sortedBy { it.arialDistanceTo(unit.currentTile) }
|
.filter { it.isLand }
|
||||||
.firstOrNull { unit.movement.canReach(it) } ?: closestReachableEnemyCity
|
.sortedBy { it.arialDistanceTo(unit.currentTile) }
|
||||||
|
.firstOrNull { unit.movement.canReach(it) } ?: closestReachableEnemyCity
|
||||||
|
|
||||||
if (tileToHeadTo !in tilesInBombardRange) // no need to worry, keep going as the movement alg. says
|
if (tileToHeadTo !in tilesInBombardRange) // no need to worry, keep going as the movement alg. says
|
||||||
unit.movement.headTowards(tileToHeadTo)
|
unit.movement.headTowards(tileToHeadTo)
|
||||||
else {
|
else {
|
||||||
if (unit.getRange() > 2) { // should never be in a bombardable position
|
if (unit.getRange() > 2) { // should never be in a bombardable position
|
||||||
val tilesCanAttackFromButNotInBombardRange =
|
val tileToMoveTo =
|
||||||
reachableTilesNotInBombardRange.filter { it.arialDistanceTo(closestReachableEnemyCity) <= unit.getRange() }
|
unitDistanceToTiles.asSequence()
|
||||||
|
.filter { it.key !in tilesInBombardRange
|
||||||
|
&& it.key.arialDistanceTo(closestReachableEnemyCity) <=
|
||||||
|
unit.getRange() }
|
||||||
|
.minBy { it.value.totalDistance }?.key
|
||||||
|
|
||||||
// move into position far away enough that the bombard doesn't hurt
|
// move into position far away enough that the bombard doesn't hurt
|
||||||
if (tilesCanAttackFromButNotInBombardRange.any())
|
if (tileToMoveTo != null)
|
||||||
unit.movement.headTowards(tilesCanAttackFromButNotInBombardRange.minBy { unitDistanceToTiles[it]!!.totalDistance }!!)
|
unit.movement.headTowards(tileToMoveTo)
|
||||||
} else {
|
} else {
|
||||||
// calculate total damage of units in surrounding 4-spaces from enemy city (so we can attack a city from 2 directions at once)
|
// calculate total damage of units in surrounding 4-spaces from enemy city (so we can attack a city from 2 directions at once)
|
||||||
val militaryUnitsAroundEnemyCity = closestReachableEnemyCity.getTilesInDistance(3)
|
val militaryUnitsAroundEnemyCity = closestReachableEnemyCity.getTilesInDistance(3)
|
||||||
.filter { it.militaryUnit != null && it.militaryUnit!!.civInfo == unit.civInfo }
|
.filter { it.militaryUnit != null && it.militaryUnit!!.civInfo == unit.civInfo }
|
||||||
.map { it.militaryUnit!! }
|
.map { it.militaryUnit }.filterNotNull()
|
||||||
var totalAttackOnCityPerTurn = -20 // cities heal 20 per turn, so anything below that its useless
|
var totalAttackOnCityPerTurn = -20 // cities heal 20 per turn, so anything below that its useless
|
||||||
val enemyCityCombatant = CityCombatant(closestReachableEnemyCity.getCity()!!)
|
val enemyCityCombatant = CityCombatant(closestReachableEnemyCity.getCity()!!)
|
||||||
for (militaryUnit in militaryUnitsAroundEnemyCity) {
|
for (militaryUnit in militaryUnitsAroundEnemyCity) {
|
||||||
@ -290,28 +322,29 @@ class UnitAutomation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun tryBombardEnemy(city: CityInfo): Boolean {
|
fun tryBombardEnemy(city: CityInfo): Boolean {
|
||||||
if (!city.attackedThisTurn) {
|
return when {
|
||||||
val target = chooseBombardTarget(city)
|
city.attackedThisTurn -> false
|
||||||
if (target == null) return false
|
else -> {
|
||||||
val enemy = Battle.getMapCombatantOfTile(target)!!
|
val enemy = chooseBombardTarget(city) ?: return false
|
||||||
Battle.attack(CityCombatant(city), enemy)
|
Battle.attack(CityCombatant(city), enemy)
|
||||||
return true
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun chooseBombardTarget(city: CityInfo): TileInfo? {
|
private fun chooseBombardTarget(city: CityInfo): ICombatant? {
|
||||||
var targets = getBombardTargets(city)
|
val mappedTargets = getBombardTargets(city).map { Battle.getMapCombatantOfTile(it)!! }
|
||||||
if (targets.isEmpty()) return null
|
.filter {
|
||||||
val siegeUnits = targets
|
val unitType = it.getUnitType()
|
||||||
.filter { Battle.getMapCombatantOfTile(it)!!.getUnitType() == UnitType.Siege }
|
unitType == UnitType.Siege || unitType.isRanged()
|
||||||
if (siegeUnits.any()) targets = siegeUnits
|
}
|
||||||
else {
|
.groupByTo(LinkedHashMap()) { it.getUnitType() }
|
||||||
val rangedUnits = targets
|
|
||||||
.filter { Battle.getMapCombatantOfTile(it)!!.getUnitType().isRanged() }
|
var targets = mappedTargets[UnitType.Siege]?.asSequence()
|
||||||
if (rangedUnits.any()) targets = rangedUnits
|
if (targets == null)
|
||||||
}
|
targets = mappedTargets.values.asSequence().flatMap { it.asSequence() }
|
||||||
return targets.minBy { Battle.getMapCombatantOfTile(it)!!.getHealth() }
|
|
||||||
|
return targets.minBy { it.getHealth() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryGarrisoningUnit(unit: MapUnit): Boolean {
|
private fun tryGarrisoningUnit(unit: MapUnit): Boolean {
|
||||||
@ -352,28 +385,6 @@ class UnitAutomation {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryGoToRuin(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn): Boolean {
|
|
||||||
if (!unit.civInfo.isMajorCiv()) return false // barbs don't have anything to do in ruins
|
|
||||||
val tileWithRuin = unitDistanceToTiles.keys
|
|
||||||
.firstOrNull { it.improvement == Constants.ancientRuins && unit.movement.canMoveTo(it) }
|
|
||||||
if (tileWithRuin == null) return false
|
|
||||||
unit.movement.moveToTile(tileWithRuin)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun tryExplore(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn): Boolean {
|
|
||||||
if (tryGoToRuin(unit, unitDistanceToTiles) && unit.currentMovement == 0f) return true
|
|
||||||
|
|
||||||
for (tile in unit.currentTile.getTilesInDistance(10))
|
|
||||||
if (unit.movement.canMoveTo(tile) && tile.position !in unit.civInfo.exploredTiles
|
|
||||||
&& unit.movement.canReach(tile)
|
|
||||||
&& (tile.getOwner()==null || !tile.getOwner()!!.isCityState())) {
|
|
||||||
unit.movement.headTowards(tile)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This is what a unit with the 'explore' action does.
|
/** This is what a unit with the 'explore' action does.
|
||||||
It also explores, but also has other functions, like healing if necessary. */
|
It also explores, but also has other functions, like healing if necessary. */
|
||||||
fun automatedExplore(unit: MapUnit) {
|
fun automatedExplore(unit: MapUnit) {
|
||||||
@ -383,13 +394,4 @@ class UnitAutomation {
|
|||||||
if (tryExplore(unit, unit.movement.getDistanceToTiles())) return
|
if (tryExplore(unit, unit.movement.getDistanceToTiles())) return
|
||||||
unit.civInfo.addNotification("[${unit.name}] finished exploring.", unit.currentTile.position, Color.GRAY)
|
unit.civInfo.addNotification("[${unit.name}] finished exploring.", unit.currentTile.position, Color.GRAY)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wander(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn) {
|
|
||||||
val reachableTiles = unitDistanceToTiles
|
|
||||||
.filter { unit.movement.canMoveTo(it.key) && unit.movement.canReach(it.key) }
|
|
||||||
|
|
||||||
val reachableTilesMaxWalkingDistance = reachableTiles.filter { it.value.totalDistance == unit.currentMovement }
|
|
||||||
if (reachableTilesMaxWalkingDistance.any()) unit.movement.moveToTile(reachableTilesMaxWalkingDistance.toList().random().first)
|
|
||||||
else if (reachableTiles.any()) unit.movement.moveToTile(reachableTiles.toList().random().first)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -14,8 +14,7 @@ class WorkerAutomation(val unit: MapUnit) {
|
|||||||
fun automateWorkerAction() {
|
fun automateWorkerAction() {
|
||||||
val enemyUnitsInWalkingDistance = unit.movement.getDistanceToTiles().keys
|
val enemyUnitsInWalkingDistance = unit.movement.getDistanceToTiles().keys
|
||||||
.filter {
|
.filter {
|
||||||
it.militaryUnit != null && it.militaryUnit!!.civInfo != unit.civInfo
|
it.militaryUnit != null && it.militaryUnit!!.civInfo.isAtWarWith(unit.civInfo)
|
||||||
&& unit.civInfo.isAtWarWith(it.militaryUnit!!.civInfo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enemyUnitsInWalkingDistance.isNotEmpty()) return // Don't you dare move.
|
if (enemyUnitsInWalkingDistance.isNotEmpty()) return // Don't you dare move.
|
||||||
@ -118,24 +117,25 @@ class WorkerAutomation(val unit: MapUnit) {
|
|||||||
* Returns the current tile if no tile to work was found
|
* Returns the current tile if no tile to work was found
|
||||||
*/
|
*/
|
||||||
private fun findTileToWork(): TileInfo {
|
private fun findTileToWork(): TileInfo {
|
||||||
val currentTile=unit.getTile()
|
val currentTile = unit.getTile()
|
||||||
val workableTiles = currentTile.getTilesInDistance(4)
|
val workableTiles = currentTile.getTilesInDistance(4)
|
||||||
.filter {
|
.filter {
|
||||||
(it.civilianUnit== null || it == currentTile)
|
(it.civilianUnit == null || it == currentTile)
|
||||||
&& tileCanBeImproved(it, unit.civInfo) }
|
&& tileCanBeImproved(it, unit.civInfo)
|
||||||
.sortedByDescending { getPriority(it, unit.civInfo) }.toMutableList()
|
}
|
||||||
|
.sortedByDescending { getPriority(it, unit.civInfo) }
|
||||||
|
|
||||||
// the tile needs to be actually reachable - more difficult than it seems,
|
// 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,
|
// 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.
|
// but only for the tile that's about to be chosen.
|
||||||
val selectedTile = workableTiles.firstOrNull{unit.movement.canReach(it) }
|
val selectedTile = workableTiles.firstOrNull { unit.movement.canReach(it) }
|
||||||
|
|
||||||
if (selectedTile != null
|
return if (selectedTile != null
|
||||||
&& getPriority(selectedTile, unit.civInfo)>1
|
&& getPriority(selectedTile, unit.civInfo) > 1
|
||||||
&& (!workableTiles.contains(currentTile)
|
&& (!workableTiles.contains(currentTile)
|
||||||
|| getPriority(selectedTile, unit.civInfo) > getPriority(currentTile, unit.civInfo)))
|
|| getPriority(selectedTile, unit.civInfo) > getPriority(currentTile, unit.civInfo)))
|
||||||
return selectedTile
|
selectedTile
|
||||||
else return currentTile
|
else currentTile
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tileCanBeImproved(tile: TileInfo, civInfo: CivilizationInfo): Boolean {
|
private fun tileCanBeImproved(tile: TileInfo, civInfo: CivilizationInfo): Boolean {
|
||||||
|
@ -62,17 +62,18 @@ class CityExpansionManager {
|
|||||||
fun chooseNewTileToOwn(): TileInfo? {
|
fun chooseNewTileToOwn(): TileInfo? {
|
||||||
for (i in 2..5) {
|
for (i in 2..5) {
|
||||||
val tiles = cityInfo.getCenterTile().getTilesInDistance(i)
|
val tiles = cityInfo.getCenterTile().getTilesInDistance(i)
|
||||||
.filter {it.getOwner() == null && it.neighbors.any { tile->tile.getOwner()==cityInfo.civInfo }}
|
.filter { it.getOwner() == null
|
||||||
if (tiles.isEmpty()) continue
|
&& it.neighbors.any { tile -> tile.getOwner() == cityInfo.civInfo } }
|
||||||
val chosenTile = tiles.maxBy { Automation().rankTile(it,cityInfo.civInfo) }
|
val chosenTile = tiles.maxBy { Automation.rankTile(it, cityInfo.civInfo) }
|
||||||
return chosenTile
|
if (chosenTile != null)
|
||||||
|
return chosenTile
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
//region state-changing functions
|
//region state-changing functions
|
||||||
fun reset() {
|
fun reset() {
|
||||||
for(tile in cityInfo.getTiles())
|
for (tile in cityInfo.getTiles())
|
||||||
relinquishOwnership(tile)
|
relinquishOwnership(tile)
|
||||||
|
|
||||||
// The only way to create a city inside an owned tile is if it's in your territory
|
// The only way to create a city inside an owned tile is if it's in your territory
|
||||||
@ -80,10 +81,9 @@ class CityExpansionManager {
|
|||||||
// It becomes an invisible city and weird shit starts happening
|
// It becomes an invisible city and weird shit starts happening
|
||||||
takeOwnership(cityInfo.getCenterTile())
|
takeOwnership(cityInfo.getCenterTile())
|
||||||
|
|
||||||
cityInfo.getCenterTile().getTilesInDistance(1)
|
for (tile in cityInfo.getCenterTile().getTilesInDistance(1)
|
||||||
.filter { it.getCity()==null } // can't take ownership of owned tiles (by other cities)
|
.filter { it.getCity() == null }) // can't take ownership of owned tiles (by other cities)
|
||||||
.forEach { takeOwnership(it) }
|
takeOwnership(tile)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addNewTileWithCulture() {
|
private fun addNewTileWithCulture() {
|
||||||
|
@ -277,7 +277,7 @@ class CityInfo {
|
|||||||
fun setTransients() {
|
fun setTransients() {
|
||||||
tileMap = civInfo.gameInfo.tileMap
|
tileMap = civInfo.gameInfo.tileMap
|
||||||
centerTileInfo = tileMap[location]
|
centerTileInfo = tileMap[location]
|
||||||
tilesInRange = getCenterTile().getTilesInDistance( 3).toHashSet()
|
tilesInRange = getCenterTile().getTilesInDistance(3).toHashSet()
|
||||||
population.cityInfo = this
|
population.cityInfo = this
|
||||||
expansion.cityInfo = this
|
expansion.cityInfo = this
|
||||||
expansion.setTransients()
|
expansion.setTransients()
|
||||||
|
@ -292,8 +292,9 @@ class CivilizationInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isAtWarWith(otherCiv:CivilizationInfo): Boolean {
|
fun isAtWarWith(otherCiv:CivilizationInfo): Boolean {
|
||||||
if(otherCiv.isBarbarian() || isBarbarian()) return true
|
if (otherCiv.civName == civName) return false // never at war with itself
|
||||||
if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet
|
if (otherCiv.isBarbarian() || isBarbarian()) return true
|
||||||
|
if (!diplomacy.containsKey(otherCiv.civName)) // not encountered yet
|
||||||
return false
|
return false
|
||||||
return getDiplomacyManager(otherCiv).diplomaticStatus == DiplomaticStatus.War
|
return getDiplomacyManager(otherCiv).diplomaticStatus == DiplomaticStatus.War
|
||||||
}
|
}
|
||||||
|
@ -141,10 +141,10 @@ class MapUnit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateVisibleTiles() {
|
fun updateVisibleTiles() {
|
||||||
if(type.isAirUnit()){
|
if(type.isAirUnit()) {
|
||||||
if(hasUnique("6 tiles in every direction always visible"))
|
viewableTiles = if (hasUnique("6 tiles in every direction always visible"))
|
||||||
viewableTiles = getTile().getTilesInDistance(6) // it's that simple
|
getTile().getTilesInDistance(6).toList() // it's that simple
|
||||||
else viewableTiles = listOf() // bomber units don't do recon
|
else listOf() // bomber units don't do recon
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var visibilityRange = 2
|
var visibilityRange = 2
|
||||||
@ -161,7 +161,7 @@ class MapUnit {
|
|||||||
val tile = getTile()
|
val tile = getTile()
|
||||||
if (tile.baseTerrain == Constants.hill && type.isLandUnit()) visibilityRange += 1
|
if (tile.baseTerrain == Constants.hill && type.isLandUnit()) visibilityRange += 1
|
||||||
|
|
||||||
viewableTiles = tile.getViewableTiles(visibilityRange, type.isWaterUnit())
|
viewableTiles = tile.getViewableTilesList(visibilityRange, type.isWaterUnit())
|
||||||
}
|
}
|
||||||
civInfo.updateViewableTiles() // for the civ
|
civInfo.updateViewableTiles() // for the civ
|
||||||
}
|
}
|
||||||
@ -388,9 +388,10 @@ class MapUnit {
|
|||||||
if (amountToHealBy == 0) return
|
if (amountToHealBy == 0) return
|
||||||
|
|
||||||
if (hasUnique("+10 HP when healing")) amountToHealBy += 10
|
if (hasUnique("+10 HP when healing")) amountToHealBy += 10
|
||||||
val adjacentUnits = currentTile.getTilesInDistance(1).flatMap { it.getUnits() }
|
val maxAdjacentHealingBonus = currentTile.getTilesInDistance(1)
|
||||||
if (adjacentUnits.isNotEmpty())
|
.flatMap { it.getUnits().asSequence() }.map { it.adjacentHealingBonus() }.max()
|
||||||
amountToHealBy += adjacentUnits.map { it.adjacentHealingBonus() }.max()!!
|
if (maxAdjacentHealingBonus != null)
|
||||||
|
amountToHealBy += maxAdjacentHealingBonus
|
||||||
if (hasUnique("All healing effects doubled"))
|
if (hasUnique("All healing effects doubled"))
|
||||||
amountToHealBy *= 2
|
amountToHealBy *= 2
|
||||||
healBy(amountToHealBy)
|
healBy(amountToHealBy)
|
||||||
@ -441,19 +442,19 @@ class MapUnit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startTurn(){
|
fun startTurn() {
|
||||||
currentMovement = getMaxMovement().toFloat()
|
currentMovement = getMaxMovement().toFloat()
|
||||||
attacksThisTurn=0
|
attacksThisTurn = 0
|
||||||
due = true
|
due = true
|
||||||
|
|
||||||
// Wake sleeping units if there's an enemy nearby
|
// Wake sleeping units if there's an enemy nearby
|
||||||
if(isSleeping() && currentTile.getTilesInDistance(2).any {
|
if (isSleeping() && currentTile.getTilesInDistance(2).any {
|
||||||
it.militaryUnit!=null && it.militaryUnit!!.civInfo.isAtWarWith(civInfo)
|
it.militaryUnit != null && it.militaryUnit!!.civInfo.isAtWarWith(civInfo)
|
||||||
})
|
})
|
||||||
action=null
|
action = null
|
||||||
|
|
||||||
val tileOwner = getTile().getOwner()
|
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
|
if (tileOwner != null && !civInfo.canEnterTiles(tileOwner) && !tileOwner.isCityState()) // if an enemy city expanded onto this tile while I was in it
|
||||||
movement.teleportToClosestMoveableTile()
|
movement.teleportToClosestMoveableTile()
|
||||||
doPreTurnAction()
|
doPreTurnAction()
|
||||||
}
|
}
|
||||||
@ -520,7 +521,7 @@ class MapUnit {
|
|||||||
civInfo.addNotification("We have captured a barbarian encampment and recovered [${goldGained.toInt()}] gold!", tile.position, Color.RED)
|
civInfo.addNotification("We have captured a barbarian encampment and recovered [${goldGained.toInt()}] gold!", tile.position, Color.RED)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disband(){
|
fun disband() {
|
||||||
// evacuation of transported units before disbanding, if possible
|
// evacuation of transported units before disbanding, if possible
|
||||||
if (type.isAircraftCarrierUnit() || type.isMissileCarrierUnit()) {
|
if (type.isAircraftCarrierUnit() || type.isMissileCarrierUnit()) {
|
||||||
for(unit in currentTile.getUnits().filter { it.type.isAirUnit() && it.isTransported }) {
|
for(unit in currentTile.getUnits().filter { it.type.isAirUnit() && it.isTransported }) {
|
||||||
@ -539,11 +540,17 @@ class MapUnit {
|
|||||||
unit.disband()
|
unit.disband()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy()
|
destroy()
|
||||||
if(currentTile.getOwner()==civInfo)
|
if (currentTile.getOwner() == civInfo)
|
||||||
civInfo.gold += baseUnit.getDisbandGold()
|
civInfo.gold += baseUnit.getDisbandGold()
|
||||||
if (civInfo.isDefeated()) civInfo.destroy()
|
if (civInfo.isDefeated()) civInfo.destroy()
|
||||||
|
for (unit in currentTile.getUnits().filter { it.type.isAirUnit() && it.isTransported }) {
|
||||||
|
if (unit.movement.canMoveTo(currentTile)) continue // we disbanded a carrier in a city, it can still stay in the city
|
||||||
|
val tileCanMoveTo = unit.currentTile.getTilesInDistance(unit.getRange())
|
||||||
|
.firstOrNull { unit.movement.canMoveTo(it) }
|
||||||
|
if (tileCanMoveTo != null) unit.movement.moveToTile(tileCanMoveTo)
|
||||||
|
else unit.disband()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAncientRuinBonus(tile: TileInfo) {
|
private fun getAncientRuinBonus(tile: TileInfo) {
|
||||||
@ -591,7 +598,7 @@ class MapUnit {
|
|||||||
|
|
||||||
// Map of the surrounding area
|
// Map of the surrounding area
|
||||||
actions.add {
|
actions.add {
|
||||||
val revealCenter = tile.getTilesAtDistance(ANCIENT_RUIN_MAP_REVEAL_OFFSET).random(tileBasedRandom)
|
val revealCenter = tile.getTilesAtDistance(ANCIENT_RUIN_MAP_REVEAL_OFFSET).toList().random(tileBasedRandom)
|
||||||
val tilesToReveal = revealCenter
|
val tilesToReveal = revealCenter
|
||||||
.getTilesInDistance(ANCIENT_RUIN_MAP_REVEAL_RANGE)
|
.getTilesInDistance(ANCIENT_RUIN_MAP_REVEAL_RANGE)
|
||||||
.filter { Random.nextFloat() < ANCIENT_RUIN_MAP_REVEAL_CHANCE }
|
.filter { Random.nextFloat() < ANCIENT_RUIN_MAP_REVEAL_CHANCE }
|
||||||
|
@ -68,9 +68,8 @@ open class TileInfo {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun containsUnique(unique: String): Boolean {
|
fun containsUnique(unique: String): Boolean =
|
||||||
return isNaturalWonder() && getNaturalWonder().uniques.contains(unique)
|
isNaturalWonder() && getNaturalWonder().uniques.contains(unique)
|
||||||
}
|
|
||||||
//region pure functions
|
//region pure functions
|
||||||
|
|
||||||
/** Returns military, civilian and air units in tile */
|
/** Returns military, civilian and air units in tile */
|
||||||
@ -104,13 +103,8 @@ open class TileInfo {
|
|||||||
|
|
||||||
// This is for performance - since we access the neighbors of a tile ALL THE TIME,
|
// This is for performance - since we access the neighbors of a tile ALL THE TIME,
|
||||||
// and the neighbors of a tile never change, it's much more efficient to save the list once and for all!
|
// and the neighbors of a tile never change, it's much more efficient to save the list once and for all!
|
||||||
@Transient private var internalNeighbors : List<TileInfo>?=null
|
@delegate:Transient
|
||||||
val neighbors: List<TileInfo>
|
val neighbors: List<TileInfo> by lazy { getTilesAtDistance(1).toList() }
|
||||||
get(){
|
|
||||||
if(internalNeighbors==null)
|
|
||||||
internalNeighbors = getTilesAtDistance(1)
|
|
||||||
return internalNeighbors!!
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getHeight(): Int {
|
fun getHeight(): Int {
|
||||||
if (baseTerrain == Constants.mountain) return 4
|
if (baseTerrain == Constants.mountain) return 4
|
||||||
@ -127,18 +121,15 @@ open class TileInfo {
|
|||||||
return containingCity.civInfo
|
return containingCity.civInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTerrainFeature(): Terrain? {
|
fun getTerrainFeature(): Terrain? =
|
||||||
return if (terrainFeature == null) null else ruleset.terrains[terrainFeature!!]
|
if (terrainFeature == null) null else ruleset.terrains[terrainFeature!!]
|
||||||
}
|
|
||||||
|
|
||||||
fun isWorked(): Boolean {
|
fun isWorked(): Boolean {
|
||||||
val city = getCity()
|
val city = getCity()
|
||||||
return city!=null && city.workedTiles.contains(position)
|
return city!=null && city.workedTiles.contains(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTileStats(observingCiv: CivilizationInfo): Stats {
|
fun getTileStats(observingCiv: CivilizationInfo): Stats = getTileStats(getCity(), observingCiv)
|
||||||
return getTileStats(getCity(), observingCiv)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getTileStats(city: CityInfo?, observingCiv: CivilizationInfo): Stats {
|
fun getTileStats(city: CityInfo?, observingCiv: CivilizationInfo): Stats {
|
||||||
var stats = getBaseTerrain().clone()
|
var stats = getBaseTerrain().clone()
|
||||||
@ -264,21 +255,20 @@ open class TileInfo {
|
|||||||
|
|
||||||
fun isCoastalTile() = neighbors.any { it.baseTerrain==Constants.coast }
|
fun isCoastalTile() = neighbors.any { it.baseTerrain==Constants.coast }
|
||||||
|
|
||||||
fun hasViewableResource(civInfo: CivilizationInfo): Boolean {
|
fun hasViewableResource(civInfo: CivilizationInfo): Boolean =
|
||||||
return resource != null && (getTileResource().revealedBy == null || civInfo.tech.isResearched(getTileResource().revealedBy!!))
|
resource != null && (getTileResource().revealedBy == null || civInfo.tech.isResearched(getTileResource().revealedBy!!))
|
||||||
}
|
|
||||||
|
|
||||||
fun getViewableTiles(distance:Int, ignoreCurrentTileHeight:Boolean = false): List<TileInfo> {
|
fun getViewableTilesList(distance:Int, ignoreCurrentTileHeight: Boolean): List<TileInfo> =
|
||||||
return tileMap.getViewableTiles(this.position,distance,ignoreCurrentTileHeight)
|
tileMap.getViewableTiles(position, distance, ignoreCurrentTileHeight)
|
||||||
}
|
|
||||||
|
|
||||||
fun getTilesInDistance(distance:Int): List<TileInfo> {
|
fun getTilesInDistance(distance: Int): Sequence<TileInfo> =
|
||||||
return tileMap.getTilesInDistance(position,distance)
|
tileMap.getTilesInDistance(position,distance)
|
||||||
}
|
|
||||||
|
|
||||||
fun getTilesAtDistance(distance:Int): List<TileInfo> {
|
fun getTilesInDistanceRange(range: IntRange): Sequence<TileInfo> =
|
||||||
return tileMap.getTilesAtDistance(position,distance)
|
tileMap.getTilesInDistanceRange(position, range)
|
||||||
}
|
|
||||||
|
fun getTilesAtDistance(distance:Int): Sequence<TileInfo> =
|
||||||
|
tileMap.getTilesAtDistance(position, distance)
|
||||||
|
|
||||||
fun getDefensiveBonus(): Float {
|
fun getDefensiveBonus(): Float {
|
||||||
var bonus = getBaseTerrain().defenceBonus
|
var bonus = getBaseTerrain().defenceBonus
|
||||||
|
@ -73,51 +73,49 @@ class TileMap {
|
|||||||
return get(vector.x.toInt(), vector.y.toInt())
|
return get(vector.x.toInt(), vector.y.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTilesInDistance(origin: Vector2, distance: Int): List<TileInfo> {
|
fun getTilesInDistance(origin: Vector2, distance: Int): Sequence<TileInfo> =
|
||||||
val tilesToReturn = mutableListOf<TileInfo>()
|
getTilesInDistanceRange(origin, 0..distance)
|
||||||
for (i in 0 .. distance) {
|
|
||||||
tilesToReturn += getTilesAtDistance(origin, i)
|
|
||||||
}
|
|
||||||
return tilesToReturn
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getTilesAtDistance(origin: Vector2, distance: Int): List<TileInfo> {
|
fun getTilesInDistanceRange(origin: Vector2, range: IntRange): Sequence<TileInfo> =
|
||||||
if(distance==0) return listOf(get(origin))
|
sequence {
|
||||||
val tilesToReturn = ArrayList<TileInfo>()
|
for (i in range)
|
||||||
|
yield(getTilesAtDistance(origin, i))
|
||||||
|
}.flatMap { it }
|
||||||
|
|
||||||
fun addIfTileExists(x:Int,y:Int){
|
fun getTilesAtDistance(origin: Vector2, distance: Int): Sequence<TileInfo> =
|
||||||
if(contains(x,y))
|
if (distance <= 0) // silently take negatives.
|
||||||
tilesToReturn += get(x,y)
|
sequenceOf(get(origin))
|
||||||
}
|
else
|
||||||
|
sequence {
|
||||||
|
fun getIfTileExistsOrNull(x: Int, y: Int) = if (contains(x, y)) get(x, y) else null
|
||||||
|
|
||||||
val centerX = origin.x.toInt()
|
val centerX = origin.x.toInt()
|
||||||
val centerY = origin.y.toInt()
|
val centerY = origin.y.toInt()
|
||||||
|
|
||||||
// Start from 6 O'clock point which means (-distance, -distance) away from the center point
|
// Start from 6 O'clock point which means (-distance, -distance) away from the center point
|
||||||
var currentX = centerX - distance
|
var currentX = centerX - distance
|
||||||
var currentY = centerY - distance
|
var currentY = centerY - distance
|
||||||
|
|
||||||
for (i in 0 until distance) { // From 6 to 8
|
for (i in 0 until distance) { // From 6 to 8
|
||||||
addIfTileExists(currentX,currentY)
|
yield(getIfTileExistsOrNull(currentX, currentY))
|
||||||
// We want to get the tile on the other side of the clock,
|
// We want to get the tile on the other side of the clock,
|
||||||
// so if we're at current = origin-delta we want to get to origin+delta.
|
// so if we're at current = origin-delta we want to get to origin+delta.
|
||||||
// The simplest way to do this is 2*origin - current = 2*origin- (origin - delta) = origin+delta
|
// The simplest way to do this is 2*origin - current = 2*origin- (origin - delta) = origin+delta
|
||||||
addIfTileExists(2*centerX - currentX, 2*centerY - currentY)
|
yield(getIfTileExistsOrNull(2 * centerX - currentX, 2 * centerY - currentY))
|
||||||
currentX += 1 // we're going upwards to the left, towards 8 o'clock
|
currentX += 1 // we're going upwards to the left, towards 8 o'clock
|
||||||
}
|
}
|
||||||
for (i in 0 until distance) { // 8 to 10
|
for (i in 0 until distance) { // 8 to 10
|
||||||
addIfTileExists(currentX,currentY)
|
yield(getIfTileExistsOrNull(currentX, currentY))
|
||||||
addIfTileExists(2*centerX - currentX, 2*centerY - currentY)
|
yield(getIfTileExistsOrNull(2 * centerX - currentX, 2 * centerY - currentY))
|
||||||
currentX += 1
|
currentX += 1
|
||||||
currentY += 1 // we're going up the left side of the hexagon so we're going "up" - +1,+1
|
currentY += 1 // we're going up the left side of the hexagon so we're going "up" - +1,+1
|
||||||
}
|
}
|
||||||
for (i in 0 until distance) { // 10 to 12
|
for (i in 0 until distance) { // 10 to 12
|
||||||
addIfTileExists(currentX,currentY)
|
yield(getIfTileExistsOrNull(currentX, currentY))
|
||||||
addIfTileExists(2*centerX - currentX, 2*centerY - currentY)
|
yield(getIfTileExistsOrNull(2 * centerX - currentX, 2 * centerY - currentY))
|
||||||
currentY += 1 // we're going up the top left side of the hexagon so we're heading "up and to the right"
|
currentY += 1 // we're going up the top left side of the hexagon so we're heading "up and to the right"
|
||||||
}
|
}
|
||||||
return tilesToReturn
|
}.filterNotNull()
|
||||||
}
|
|
||||||
|
|
||||||
/** Tries to place the [unitName] into the [TileInfo] closest to the given the [position]
|
/** Tries to place the [unitName] into the [TileInfo] closest to the given the [position]
|
||||||
*
|
*
|
||||||
@ -134,49 +132,53 @@ class TileMap {
|
|||||||
): MapUnit? {
|
): MapUnit? {
|
||||||
val unit = gameInfo.ruleSet.units[unitName]!!.getMapUnit(gameInfo.ruleSet)
|
val unit = gameInfo.ruleSet.units[unitName]!!.getMapUnit(gameInfo.ruleSet)
|
||||||
|
|
||||||
fun isTileMovePotential(tileInfo:TileInfo): Boolean {
|
fun isTileMovePotential(tileInfo: TileInfo): Boolean =
|
||||||
if(unit.type.isAirUnit()) return true
|
when {
|
||||||
if(unit.type.isWaterUnit()) return tileInfo.isWater || tileInfo.isCityCenter()
|
unit.type.isAirUnit() -> true
|
||||||
else return tileInfo.isLand
|
unit.type.isWaterUnit() -> tileInfo.isWater || tileInfo.isCityCenter()
|
||||||
}
|
else -> tileInfo.isLand
|
||||||
|
}
|
||||||
|
|
||||||
val viableTilesToPlaceUnitInAtDistance1 = getTilesInDistance(position, 1).filter { isTileMovePotential(it) }
|
val viableTilesToPlaceUnitInAtDistance1 = getTilesAtDistance(position, 1)
|
||||||
|
.filter { isTileMovePotential(it) }.toSet()
|
||||||
// This is so that units don't skip over non-potential tiles to go elsewhere -
|
// This is so that units don't skip over non-potential tiles to go elsewhere -
|
||||||
// e.g. a city 2 tiles away from a lake could spawn water units in the lake...Or spawn beyond a mountain range...
|
// e.g. a city 2 tiles away from a lake could spawn water units in the lake...Or spawn beyond a mountain range...
|
||||||
val viableTilesToPlaceUnitInAtDistance2 = getTilesAtDistance(position, 2)
|
val viableTilesToPlaceUnitIn = getTilesAtDistance(position, 2)
|
||||||
.filter { isTileMovePotential(it) && it.neighbors.any { n->n in viableTilesToPlaceUnitInAtDistance1 } }
|
.filter {
|
||||||
|
isTileMovePotential(it)
|
||||||
|
&& it.neighbors.any { n -> n in viableTilesToPlaceUnitInAtDistance1 }
|
||||||
|
} + viableTilesToPlaceUnitInAtDistance1
|
||||||
|
|
||||||
val viableTilesToPlaceUnitIn = viableTilesToPlaceUnitInAtDistance1.union(viableTilesToPlaceUnitInAtDistance2)
|
|
||||||
|
|
||||||
unit.assignOwner(civInfo,false) // both the civ name and actual civ need to be in here in order to calculate the canMoveTo...Darn
|
unit.assignOwner(civInfo, false) // both the civ name and actual civ need to be in here in order to calculate the canMoveTo...Darn
|
||||||
val unitToPlaceTile = viableTilesToPlaceUnitIn.firstOrNull { unit.movement.canMoveTo(it) }
|
val unitToPlaceTile = viableTilesToPlaceUnitIn.firstOrNull { unit.movement.canMoveTo(it) }
|
||||||
|
|
||||||
if(unitToPlaceTile!=null) {
|
if (unitToPlaceTile == null) {
|
||||||
// Remove the tile improvement, e.g. when placing the starter units (so they don't spawn on ruins/encampments)
|
|
||||||
if (removeImprovement) unitToPlaceTile.improvement = null
|
|
||||||
// only once we know the unit can be placed do we add it to the civ's unit list
|
|
||||||
unit.putInTile(unitToPlaceTile)
|
|
||||||
unit.currentMovement = unit.getMaxMovement().toFloat()
|
|
||||||
|
|
||||||
// Only once we add the unit to the civ we can activate addPromotion, because it will try to update civ viewable tiles
|
|
||||||
for(promotion in unit.baseUnit.promotions)
|
|
||||||
unit.promotions.addPromotion(promotion, true)
|
|
||||||
|
|
||||||
// And update civ stats, since the new unit changes both unit upkeep and resource consumption
|
|
||||||
civInfo.updateStatsForNextTurn()
|
|
||||||
civInfo.updateDetailedCivResources()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
civInfo.removeUnit(unit) // since we added it to the civ units in the previous assignOwner
|
civInfo.removeUnit(unit) // since we added it to the civ units in the previous assignOwner
|
||||||
return null // we didn't actually create a unit...
|
return null // we didn't actually create a unit...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the tile improvement, e.g. when placing the starter units (so they don't spawn on ruins/encampments)
|
||||||
|
if (removeImprovement) unitToPlaceTile.improvement = null
|
||||||
|
// only once we know the unit can be placed do we add it to the civ's unit list
|
||||||
|
unit.putInTile(unitToPlaceTile)
|
||||||
|
unit.currentMovement = unit.getMaxMovement().toFloat()
|
||||||
|
|
||||||
|
// Only once we add the unit to the civ we can activate addPromotion, because it will try to update civ viewable tiles
|
||||||
|
for (promotion in unit.baseUnit.promotions)
|
||||||
|
unit.promotions.addPromotion(promotion, true)
|
||||||
|
|
||||||
|
// And update civ stats, since the new unit changes both unit upkeep and resource consumption
|
||||||
|
civInfo.updateStatsForNextTurn()
|
||||||
|
civInfo.updateDetailedCivResources()
|
||||||
|
|
||||||
return unit
|
return unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getViewableTiles(position: Vector2, sightDistance: Int, ignoreCurrentTileHeight: Boolean = false): List<TileInfo> {
|
fun getViewableTiles(position: Vector2, sightDistance: Int, ignoreCurrentTileHeight: Boolean)
|
||||||
if (ignoreCurrentTileHeight) return getTilesInDistance(position, sightDistance)
|
: List<TileInfo> {
|
||||||
|
if (ignoreCurrentTileHeight) return getTilesInDistance(position, sightDistance).toList()
|
||||||
|
|
||||||
val viewableTiles = getTilesInDistance(position, 1).toMutableList()
|
val viewableTiles = getTilesInDistance(position, 1).toMutableList()
|
||||||
val currentTileHeight = get(position).getHeight()
|
val currentTileHeight = get(position).getHeight()
|
||||||
|
@ -43,9 +43,9 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
|||||||
class ParentTileAndTotalDistance(val parentTile:TileInfo, val totalDistance: Float)
|
class ParentTileAndTotalDistance(val parentTile:TileInfo, val totalDistance: Float)
|
||||||
|
|
||||||
fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): PathsToTilesWithinTurn {
|
fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): PathsToTilesWithinTurn {
|
||||||
if(unitMovement==0f) return PathsToTilesWithinTurn()
|
|
||||||
|
|
||||||
val distanceToTiles = PathsToTilesWithinTurn()
|
val distanceToTiles = PathsToTilesWithinTurn()
|
||||||
|
if(unitMovement==0f) return distanceToTiles
|
||||||
|
|
||||||
val unitTile = unit.getTile().tileMap[origin]
|
val unitTile = unit.getTile().tileMap[origin]
|
||||||
distanceToTiles[unitTile] = ParentTileAndTotalDistance(unitTile,0f)
|
distanceToTiles[unitTile] = ParentTileAndTotalDistance(unitTile,0f)
|
||||||
var tilesToCheck = listOf(unitTile)
|
var tilesToCheck = listOf(unitTile)
|
||||||
@ -344,7 +344,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDistanceToTiles() = getDistanceToTilesWithinTurn(unit.currentTile.position,unit.currentMovement)
|
fun getDistanceToTiles(): PathsToTilesWithinTurn
|
||||||
|
= getDistanceToTilesWithinTurn(unit.currentTile.position,unit.currentMovement)
|
||||||
|
|
||||||
fun getArialPathsToCities(): HashMap<TileInfo, ArrayList<TileInfo>> {
|
fun getArialPathsToCities(): HashMap<TileInfo, ArrayList<TileInfo>> {
|
||||||
var tilesToCheck = ArrayList<TileInfo>()
|
var tilesToCheck = ArrayList<TileInfo>()
|
||||||
|
@ -98,7 +98,7 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() {
|
|||||||
updateAnnexAndRazeCityButton()
|
updateAnnexAndRazeCityButton()
|
||||||
updateTileGroups()
|
updateTileGroups()
|
||||||
|
|
||||||
if (city.getCenterTile().getTilesAtDistance(4).isNotEmpty())
|
if (city.getCenterTile().getTilesAtDistance(4).any())
|
||||||
displayTutorial(Tutorial.CityRange)
|
displayTutorial(Tutorial.CityRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,18 +251,20 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
|||||||
tileGroups[unit.getTile()]!!.selectUnit(unit)
|
tileGroups[unit.getTile()]!!.selectUnit(unit)
|
||||||
|
|
||||||
val isAirUnit = unit.type.isAirUnit()
|
val isAirUnit = unit.type.isAirUnit()
|
||||||
val tilesInMoveRange = if(isAirUnit) unit.getTile().getTilesInDistance(unit.getRange())
|
val tilesInMoveRange =
|
||||||
else unit.movement.getDistanceToTiles().keys
|
if (isAirUnit)
|
||||||
|
unit.getTile().getTilesInDistance(unit.getRange())
|
||||||
|
else
|
||||||
|
unit.movement.getDistanceToTiles().keys.asSequence()
|
||||||
|
|
||||||
if(isAirUnit)
|
for (tile in tilesInMoveRange) {
|
||||||
for(tile in tilesInMoveRange)
|
val tileToColor = tileGroups.getValue(tile)
|
||||||
tileGroups[tile]!!.showCircle(Color.BLUE,0.3f)
|
if (isAirUnit)
|
||||||
|
tileToColor.showCircle(Color.BLUE, 0.3f)
|
||||||
for (tile: TileInfo in tilesInMoveRange)
|
|
||||||
if (unit.movement.canMoveTo(tile))
|
if (unit.movement.canMoveTo(tile))
|
||||||
tileGroups[tile]!!.showCircle(Color.WHITE,
|
tileToColor.showCircle(Color.WHITE,
|
||||||
if (UncivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
|
if (UncivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
|
||||||
|
}
|
||||||
|
|
||||||
val unitType = unit.type
|
val unitType = unit.type
|
||||||
val attackableTiles: List<TileInfo> = if (unitType.isCivilian()) listOf()
|
val attackableTiles: List<TileInfo> = if (unitType.isCivilian()) listOf()
|
||||||
@ -281,16 +283,16 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
|||||||
else 0.5f
|
else 0.5f
|
||||||
for (tile in tileGroups.values) {
|
for (tile in tileGroups.values) {
|
||||||
if (tile.icons.populationIcon != null) tile.icons.populationIcon!!.color.a = fadeout
|
if (tile.icons.populationIcon != null) tile.icons.populationIcon!!.color.a = fadeout
|
||||||
if (tile.icons.improvementIcon != null && tile.tileInfo.improvement!=Constants.barbarianEncampment
|
if (tile.icons.improvementIcon != null && tile.tileInfo.improvement != Constants.barbarianEncampment
|
||||||
&& tile.tileInfo.improvement!=Constants.ancientRuins)
|
&& tile.tileInfo.improvement != Constants.ancientRuins)
|
||||||
tile.icons.improvementIcon!!.color.a = fadeout
|
tile.icons.improvementIcon!!.color.a = fadeout
|
||||||
if (tile.resourceImage != null) tile.resourceImage!!.color.a = fadeout
|
if (tile.resourceImage != null) tile.resourceImage!!.color.a = fadeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTilegroupsForSelectedCity(city: CityInfo, playerViewableTilePositions: HashSet<Vector2>) {
|
private fun updateTilegroupsForSelectedCity(city: CityInfo, playerViewableTilePositions: HashSet<Vector2>) {
|
||||||
val attackableTiles: List<TileInfo> = UnitAutomation().getBombardTargets(city)
|
val attackableTiles = UnitAutomation().getBombardTargets(city)
|
||||||
.filter { (UncivGame.Current.viewEntireMapForDebug || playerViewableTilePositions.contains(it.position)) }
|
.filter { (UncivGame.Current.viewEntireMapForDebug || playerViewableTilePositions.contains(it.position)) }
|
||||||
for (attackableTile in attackableTiles) {
|
for (attackableTile in attackableTiles) {
|
||||||
tileGroups[attackableTile]!!.showCircle(colorFromRGB(237, 41, 57))
|
tileGroups[attackableTile]!!.showCircle(colorFromRGB(237, 41, 57))
|
||||||
tileGroups[attackableTile]!!.showCrosshair()
|
tileGroups[attackableTile]!!.showCrosshair()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user