mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 07:17:50 +07:00
chore: 'use gold' automation from NextTurnAutomation
Cleaned up Tile.kt a bit by moving single-use functions to where they are used
This commit is contained in:
@ -32,10 +32,8 @@ import com.unciv.models.Counter
|
||||
import com.unciv.models.ruleset.Belief
|
||||
import com.unciv.models.ruleset.BeliefType
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.INonPerpetualConstruction
|
||||
import com.unciv.models.ruleset.MilestoneType
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.PerpetualConstruction
|
||||
import com.unciv.models.ruleset.Policy
|
||||
import com.unciv.models.ruleset.PolicyBranch
|
||||
import com.unciv.models.ruleset.Victory
|
||||
@ -47,8 +45,6 @@ import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.screens.victoryscreen.RankingType
|
||||
import java.util.SortedMap
|
||||
import java.util.TreeMap
|
||||
import kotlin.math.min
|
||||
import kotlin.random.Random
|
||||
|
||||
@ -84,7 +80,7 @@ object NextTurnAutomation {
|
||||
|
||||
chooseTechToResearch(civInfo)
|
||||
automateCityBombardment(civInfo)
|
||||
useGold(civInfo)
|
||||
UseGoldAutomation.useGold(civInfo)
|
||||
if (!civInfo.isCityState()) {
|
||||
protectCityStates(civInfo)
|
||||
bullyCityStates(civInfo)
|
||||
@ -291,149 +287,6 @@ object NextTurnAutomation {
|
||||
civInfo.popupAlerts.clear() // AIs don't care about popups.
|
||||
}
|
||||
|
||||
private fun tryGainInfluence(civInfo: Civilization, cityState: Civilization) {
|
||||
if (civInfo.gold < 250) return // save up
|
||||
if (cityState.getDiplomacyManager(civInfo).getInfluence() < 20) {
|
||||
cityState.cityStateFunctions.receiveGoldGift(civInfo, 250)
|
||||
return
|
||||
}
|
||||
if (civInfo.gold < 500) return // it's not worth it to invest now, wait until you have enough for 2
|
||||
cityState.cityStateFunctions.receiveGoldGift(civInfo, 500)
|
||||
return
|
||||
}
|
||||
|
||||
private fun useGoldForCityStates(civ: Civilization) {
|
||||
// RARE EDGE CASE: If you ally with a city-state, you may reveal more map that includes ANOTHER civ!
|
||||
// So if we don't lock this list, we may later discover that there are more known civs, concurrent modification exception!
|
||||
val knownCityStates = civ.getKnownCivs().filter { it.isCityState() }.toList()
|
||||
|
||||
// canBeMarriedBy checks actual cost, but it can't be below 500*speedmodifier, and the later check is expensive
|
||||
if (civ.gold >= 330 && civ.getHappiness() > 0 && civ.hasUnique(UniqueType.CityStateCanBeBoughtForGold)) {
|
||||
for (cityState in knownCityStates.toList() ) { // Materialize sequence as diplomaticMarriage may kill a CS
|
||||
if (cityState.cityStateFunctions.canBeMarriedBy(civ))
|
||||
cityState.cityStateFunctions.diplomaticMarriage(civ)
|
||||
if (civ.getHappiness() <= 0) break // Stop marrying if happiness is getting too low
|
||||
}
|
||||
}
|
||||
|
||||
if (civ.gold < 250) return // skip checks if tryGainInfluence will bail anyway
|
||||
if (civ.wantsToFocusOn(Victory.Focus.Culture)) {
|
||||
for (cityState in knownCityStates.filter { it.cityStateFunctions.canProvideStat(Stat.Culture) }) {
|
||||
val diploManager = cityState.getDiplomacyManager(civ)
|
||||
if (diploManager.getInfluence() < 40) { // we want to gain influence with them
|
||||
tryGainInfluence(civ, cityState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (civ.gold < 250 || knownCityStates.none()) return
|
||||
val cityState = knownCityStates
|
||||
.filter { it.getAllyCiv() != civ.civName }
|
||||
.associateWith { valueCityStateAlliance(civ, it) }
|
||||
.maxByOrNull { it.value }?.takeIf { it.value > 0 }?.key
|
||||
if (cityState != null) {
|
||||
tryGainInfluence(civ, cityState)
|
||||
}
|
||||
}
|
||||
|
||||
/** allow AI to spend money to purchase city-state friendship, buildings & unit */
|
||||
private fun useGold(civ: Civilization) {
|
||||
if (civ.isMajorCiv())
|
||||
useGoldForCityStates(civ)
|
||||
|
||||
for (city in civ.cities.sortedByDescending { it.population.population }) {
|
||||
val construction = city.cityConstructions.getCurrentConstruction()
|
||||
if (construction is PerpetualConstruction) continue
|
||||
if ((construction as INonPerpetualConstruction).canBePurchasedWithStat(city, Stat.Gold)
|
||||
&& city.civ.gold / 3 >= construction.getStatBuyCost(city, Stat.Gold)!!) {
|
||||
city.cityConstructions.purchaseConstruction(construction, 0, true)
|
||||
}
|
||||
}
|
||||
|
||||
maybeBuyCityTiles(civ)
|
||||
}
|
||||
|
||||
private fun maybeBuyCityTiles(civInfo: Civilization) {
|
||||
if (civInfo.gold <= 0)
|
||||
return
|
||||
// Don't buy tiles in the very early game. It is unlikely that we already have the required
|
||||
// tech, the necessary worker and that there is a reasonable threat from another player to
|
||||
// grab the tile. We could also check all that, but it would require a lot of cycles each
|
||||
// turn and this is probably a good approximation.
|
||||
if (civInfo.gameInfo.turns < (civInfo.gameInfo.speed.scienceCostModifier * 20).toInt())
|
||||
return
|
||||
|
||||
val highlyDesirableTiles: SortedMap<Tile, MutableSet<City>> = TreeMap(
|
||||
compareByDescending<Tile?> { it?.naturalWonder != null }
|
||||
.thenByDescending { it?.resource != null && it.tileResource.resourceType == ResourceType.Luxury }
|
||||
.thenByDescending { it?.resource != null && it.tileResource.resourceType == ResourceType.Strategic }
|
||||
// This is necessary, so that the map keeps Tiles with the same resource as two
|
||||
// separate entries.
|
||||
.thenBy { it.hashCode() }
|
||||
)
|
||||
for (city in civInfo.cities.filter { !it.isPuppet && !it.isBeingRazed }) {
|
||||
val highlyDesirableTilesInCity = city.tilesInRange.filter {
|
||||
val hasNaturalWonder = it.naturalWonder != null
|
||||
val hasLuxuryCivDoesntOwn =
|
||||
it.hasViewableResource(civInfo)
|
||||
&& it.tileResource.resourceType == ResourceType.Luxury
|
||||
&& !civInfo.hasResource(it.resource!!)
|
||||
val hasResourceCivHasNoneOrLittle =
|
||||
it.hasViewableResource(civInfo)
|
||||
&& it.tileResource.resourceType == ResourceType.Strategic
|
||||
&& civInfo.getResourceAmount(it.resource!!) <= 3
|
||||
|
||||
it.isVisible(civInfo) && it.getOwner() == null
|
||||
&& it.neighbors.any { neighbor -> neighbor.getCity() == city }
|
||||
(hasNaturalWonder || hasLuxuryCivDoesntOwn || hasResourceCivHasNoneOrLittle)
|
||||
}
|
||||
for (highlyDesirableTileInCity in highlyDesirableTilesInCity) {
|
||||
highlyDesirableTiles.getOrPut(highlyDesirableTileInCity) { mutableSetOf() }
|
||||
.add(city)
|
||||
}
|
||||
}
|
||||
|
||||
// Always try to buy highly desirable tiles if it can be afforded.
|
||||
for (highlyDesirableTile in highlyDesirableTiles) {
|
||||
val cityWithLeastCostToBuy = highlyDesirableTile.value.minBy {
|
||||
it.getCenterTile().aerialDistanceTo(highlyDesirableTile.key)
|
||||
}
|
||||
val bfs = BFS(cityWithLeastCostToBuy.getCenterTile())
|
||||
{
|
||||
it.getOwner() == null || it.owningCity == cityWithLeastCostToBuy
|
||||
}
|
||||
bfs.stepUntilDestination(highlyDesirableTile.key)
|
||||
val tilesThatNeedBuying =
|
||||
bfs.getPathTo(highlyDesirableTile.key).filter { it.getOwner() == null }
|
||||
.toList().reversed() // getPathTo is from destination to source
|
||||
|
||||
// We're trying to acquire everything and revert if it fails, because of the difficult
|
||||
// way how tile acquisition cost is calculated. Everytime you buy a tile, the next one
|
||||
// gets more expensive and by how much depends on other things such as game speed. To
|
||||
// not introduce hidden dependencies on that and duplicate that logic here to calculate
|
||||
// the price of the whole path, this is probably simpler.
|
||||
var ranOutOfMoney = false
|
||||
var goldSpent = 0
|
||||
for (tileThatNeedsBuying in tilesThatNeedBuying) {
|
||||
val goldCostOfTile =
|
||||
cityWithLeastCostToBuy.expansion.getGoldCostOfTile(tileThatNeedsBuying)
|
||||
if (civInfo.gold >= goldCostOfTile) {
|
||||
cityWithLeastCostToBuy.expansion.buyTile(tileThatNeedsBuying)
|
||||
goldSpent += goldCostOfTile
|
||||
} else {
|
||||
ranOutOfMoney = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (ranOutOfMoney) {
|
||||
for (tileThatNeedsBuying in tilesThatNeedBuying) {
|
||||
cityWithLeastCostToBuy.expansion.relinquishOwnership(tileThatNeedsBuying)
|
||||
}
|
||||
civInfo.addGold(goldSpent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun valueCityStateAlliance(civInfo: Civilization, cityState: Civilization): Int {
|
||||
var value = 0
|
||||
|
||||
@ -532,7 +385,7 @@ object NextTurnAutomation {
|
||||
if (civInfo.tech.techsToResearch.isEmpty()) {
|
||||
val costs = getGroupedResearchableTechs()
|
||||
if (costs.isEmpty()) return
|
||||
|
||||
|
||||
val cheapestTechs = costs[0]
|
||||
//Do not consider advanced techs if only one tech left in cheapest group
|
||||
val techToResearch: Technology =
|
||||
|
@ -0,0 +1,177 @@
|
||||
package com.unciv.logic.automation.civilization
|
||||
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.map.BFS
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.INonPerpetualConstruction
|
||||
import com.unciv.models.ruleset.PerpetualConstruction
|
||||
import com.unciv.models.ruleset.Victory
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import java.util.SortedMap
|
||||
import java.util.TreeMap
|
||||
|
||||
object UseGoldAutomation {
|
||||
|
||||
|
||||
/** allow AI to spend money to purchase city-state friendship, buildings & unit */
|
||||
fun useGold(civ: Civilization) {
|
||||
if (civ.isMajorCiv())
|
||||
useGoldForCityStates(civ)
|
||||
|
||||
for (city in civ.cities.sortedByDescending { it.population.population }) {
|
||||
val construction = city.cityConstructions.getCurrentConstruction()
|
||||
if (construction is PerpetualConstruction) continue
|
||||
if ((construction as INonPerpetualConstruction).canBePurchasedWithStat(city, Stat.Gold)
|
||||
&& city.civ.gold / 3 >= construction.getStatBuyCost(city, Stat.Gold)!!) {
|
||||
city.cityConstructions.purchaseConstruction(construction, 0, true)
|
||||
}
|
||||
}
|
||||
|
||||
maybeBuyCityTiles(civ)
|
||||
}
|
||||
|
||||
|
||||
private fun useGoldForCityStates(civ: Civilization) {
|
||||
// RARE EDGE CASE: If you ally with a city-state, you may reveal more map that includes ANOTHER civ!
|
||||
// So if we don't lock this list, we may later discover that there are more known civs, concurrent modification exception!
|
||||
val knownCityStates = civ.getKnownCivs().filter { it.isCityState() }.toList()
|
||||
|
||||
// canBeMarriedBy checks actual cost, but it can't be below 500*speedmodifier, and the later check is expensive
|
||||
if (civ.gold >= 330 && civ.getHappiness() > 0 && civ.hasUnique(UniqueType.CityStateCanBeBoughtForGold)) {
|
||||
for (cityState in knownCityStates.toList() ) { // Materialize sequence as diplomaticMarriage may kill a CS
|
||||
if (cityState.cityStateFunctions.canBeMarriedBy(civ))
|
||||
cityState.cityStateFunctions.diplomaticMarriage(civ)
|
||||
if (civ.getHappiness() <= 0) break // Stop marrying if happiness is getting too low
|
||||
}
|
||||
}
|
||||
|
||||
if (civ.gold < 250) return // skip checks if tryGainInfluence will bail anyway
|
||||
if (civ.wantsToFocusOn(Victory.Focus.Culture)) {
|
||||
for (cityState in knownCityStates.filter { it.cityStateFunctions.canProvideStat(Stat.Culture) }) {
|
||||
val diploManager = cityState.getDiplomacyManager(civ)
|
||||
if (diploManager.getInfluence() < 40) { // we want to gain influence with them
|
||||
tryGainInfluence(civ, cityState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (civ.gold < 250 || knownCityStates.none()) return
|
||||
val cityState = knownCityStates
|
||||
.filter { it.getAllyCiv() != civ.civName }
|
||||
.associateWith { NextTurnAutomation.valueCityStateAlliance(civ, it) }
|
||||
.maxByOrNull { it.value }?.takeIf { it.value > 0 }?.key
|
||||
if (cityState != null) {
|
||||
tryGainInfluence(civ, cityState)
|
||||
}
|
||||
}
|
||||
|
||||
private fun maybeBuyCityTiles(civInfo: Civilization) {
|
||||
if (civInfo.gold <= 0)
|
||||
return
|
||||
// Don't buy tiles in the very early game. It is unlikely that we already have the required
|
||||
// tech, the necessary worker and that there is a reasonable threat from another player to
|
||||
// grab the tile. We could also check all that, but it would require a lot of cycles each
|
||||
// turn and this is probably a good approximation.
|
||||
if (civInfo.gameInfo.turns < (civInfo.gameInfo.speed.scienceCostModifier * 20).toInt())
|
||||
return
|
||||
|
||||
val highlyDesirableTiles: SortedMap<Tile, MutableSet<City>> = getHighlyDesirableTilesToCityMap(civInfo)
|
||||
|
||||
// Always try to buy highly desirable tiles if it can be afforded.
|
||||
for (highlyDesirableTile in highlyDesirableTiles) {
|
||||
val cityWithLeastCostToBuy = highlyDesirableTile.value.minBy {
|
||||
it.getCenterTile().aerialDistanceTo(highlyDesirableTile.key)
|
||||
}
|
||||
val bfs = BFS(cityWithLeastCostToBuy.getCenterTile())
|
||||
{
|
||||
it.getOwner() == null || it.owningCity == cityWithLeastCostToBuy
|
||||
}
|
||||
bfs.stepUntilDestination(highlyDesirableTile.key)
|
||||
val tilesThatNeedBuying =
|
||||
bfs.getPathTo(highlyDesirableTile.key).filter { it.getOwner() == null }
|
||||
.toList().reversed() // getPathTo is from destination to source
|
||||
|
||||
// We're trying to acquire everything and revert if it fails, because of the difficult
|
||||
// way how tile acquisition cost is calculated. Everytime you buy a tile, the next one
|
||||
// gets more expensive and by how much depends on other things such as game speed. To
|
||||
// not introduce hidden dependencies on that and duplicate that logic here to calculate
|
||||
// the price of the whole path, this is probably simpler.
|
||||
var ranOutOfMoney = false
|
||||
var goldSpent = 0
|
||||
for (tileThatNeedsBuying in tilesThatNeedBuying) {
|
||||
val goldCostOfTile =
|
||||
cityWithLeastCostToBuy.expansion.getGoldCostOfTile(tileThatNeedsBuying)
|
||||
if (civInfo.gold >= goldCostOfTile) {
|
||||
cityWithLeastCostToBuy.expansion.buyTile(tileThatNeedsBuying)
|
||||
goldSpent += goldCostOfTile
|
||||
} else {
|
||||
ranOutOfMoney = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (ranOutOfMoney) {
|
||||
for (tileThatNeedsBuying in tilesThatNeedBuying) {
|
||||
cityWithLeastCostToBuy.expansion.relinquishOwnership(tileThatNeedsBuying)
|
||||
}
|
||||
civInfo.addGold(goldSpent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getHighlyDesirableTilesToCityMap(civInfo: Civilization): SortedMap<Tile, MutableSet<City>> {
|
||||
val highlyDesirableTiles: SortedMap<Tile, MutableSet<City>> = TreeMap(
|
||||
compareByDescending<Tile?> { it?.naturalWonder != null }
|
||||
.thenByDescending { it?.resource != null && it.tileResource.resourceType == ResourceType.Luxury }
|
||||
.thenByDescending { it?.resource != null && it.tileResource.resourceType == ResourceType.Strategic }
|
||||
// This is necessary, so that the map keeps Tiles with the same resource as two
|
||||
// separate entries.
|
||||
.thenBy { it.hashCode() }
|
||||
)
|
||||
|
||||
for (city in civInfo.cities.filter { !it.isPuppet && !it.isBeingRazed }) {
|
||||
val highlyDesirableTilesInCity = city.tilesInRange.filter {
|
||||
isHighlyDesirableTile(it, civInfo, city)
|
||||
}
|
||||
for (highlyDesirableTileInCity in highlyDesirableTilesInCity) {
|
||||
highlyDesirableTiles.getOrPut(highlyDesirableTileInCity) { mutableSetOf() }
|
||||
.add(city)
|
||||
}
|
||||
}
|
||||
return highlyDesirableTiles
|
||||
}
|
||||
|
||||
private fun isHighlyDesirableTile(it: Tile, civInfo: Civilization, city: City): Boolean {
|
||||
if (!it.isVisible(civInfo)) return false
|
||||
if (it.getOwner() != null) return false
|
||||
if (it.neighbors.none { neighbor -> neighbor.getCity() == city }) return false
|
||||
|
||||
fun hasNaturalWonder() = it.naturalWonder != null
|
||||
|
||||
fun hasLuxuryCivDoesntOwn() =
|
||||
it.hasViewableResource(civInfo)
|
||||
&& it.tileResource.resourceType == ResourceType.Luxury
|
||||
&& !civInfo.hasResource(it.resource!!)
|
||||
|
||||
fun hasResourceCivHasNoneOrLittle() =
|
||||
it.hasViewableResource(civInfo)
|
||||
&& it.tileResource.resourceType == ResourceType.Strategic
|
||||
&& civInfo.getResourceAmount(it.resource!!) <= 3
|
||||
|
||||
return (hasNaturalWonder() || hasLuxuryCivDoesntOwn() || hasResourceCivHasNoneOrLittle())
|
||||
}
|
||||
|
||||
private fun tryGainInfluence(civInfo: Civilization, cityState: Civilization) {
|
||||
if (civInfo.gold < 250) return // save up
|
||||
if (cityState.getDiplomacyManager(civInfo).getInfluence() < 20) {
|
||||
cityState.cityStateFunctions.receiveGoldGift(civInfo, 250)
|
||||
return
|
||||
}
|
||||
if (civInfo.gold < 500) return // it's not worth it to invest now, wait until you have enough for 2
|
||||
cityState.cityStateFunctions.receiveGoldGift(civInfo, 500)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
@ -890,6 +890,22 @@ class MapRegions (val ruleset: Ruleset){
|
||||
}
|
||||
|
||||
|
||||
// For dividing the map into Regions to determine start locations
|
||||
fun Tile.getTileFertility(checkCoasts: Boolean): Int {
|
||||
var fertility = 0
|
||||
for (terrain in allTerrains) {
|
||||
if (terrain.hasUnique(UniqueType.OverrideFertility))
|
||||
return terrain.getMatchingUniques(UniqueType.OverrideFertility).first().params[0].toInt()
|
||||
else
|
||||
fertility += terrain.getMatchingUniques(UniqueType.AddFertility)
|
||||
.sumOf { it.params[0].toInt() }
|
||||
}
|
||||
if (isAdjacentToRiver()) fertility += 1
|
||||
if (isAdjacentTo(Constants.freshWater)) fertility += 1 // meaning total +2 for river
|
||||
if (checkCoasts && isCoastalTile()) fertility += 2
|
||||
return fertility
|
||||
}
|
||||
|
||||
fun getRegionPriority(terrain: Terrain?): Int? {
|
||||
if (terrain == null) // ie "hybrid"
|
||||
return 99999 // a big number
|
||||
|
@ -620,6 +620,16 @@ class MapUnit : IsPartOfGameInfoSerialization {
|
||||
|
||||
fun removeFromTile() = currentTile.removeUnit(this)
|
||||
|
||||
|
||||
/** Return null if military on tile, or no civilian */
|
||||
private fun Tile.getUnguardedCivilian(attacker: MapUnit): MapUnit? {
|
||||
return when {
|
||||
militaryUnit != null && militaryUnit != attacker -> null
|
||||
civilianUnit != null -> civilianUnit!!
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun moveThroughTile(tile: Tile) {
|
||||
// addPromotion requires currentTile to be valid because it accesses ruleset through it.
|
||||
// getAncientRuinBonus, if it places a new unit, does too
|
||||
|
@ -205,13 +205,6 @@ open class Tile : IsPartOfGameInfoSerialization {
|
||||
return null
|
||||
}
|
||||
|
||||
/** Return null if military on tile, or no civilian */
|
||||
fun getUnguardedCivilian(attacker: MapUnit): MapUnit? {
|
||||
if (militaryUnit != null && militaryUnit != attacker) return null
|
||||
if (civilianUnit != null) return civilianUnit!!
|
||||
return null
|
||||
}
|
||||
|
||||
fun getCity(): City? = owningCity
|
||||
|
||||
@Transient
|
||||
@ -353,16 +346,6 @@ open class Tile : IsPartOfGameInfoSerialization {
|
||||
// We have to .toList() so that the values are stored together once for caching,
|
||||
// and the toSequence so that aggregations (like neighbors.flatMap{it.units} don't take up their own space
|
||||
|
||||
/** Returns the left shared neighbor of `this` and [neighbor] (relative to the view direction `this`->[neighbor]), or null if there is no such tile. */
|
||||
fun getLeftSharedNeighbor(neighbor: Tile): Tile? {
|
||||
return tileMap.getClockPositionNeighborTile(this,(tileMap.getNeighborTileClockPosition(this, neighbor) - 2) % 12)
|
||||
}
|
||||
|
||||
/** Returns the right shared neighbor of `this` and [neighbor] (relative to the view direction `this`->[neighbor]), or null if there is no such tile. */
|
||||
fun getRightSharedNeighbor(neighbor: Tile): Tile? {
|
||||
return tileMap.getClockPositionNeighborTile(this,(tileMap.getNeighborTileClockPosition(this, neighbor) + 2) % 12)
|
||||
}
|
||||
|
||||
fun getRow() = HexMath.getRow(position)
|
||||
fun getColumn() = HexMath.getColumn(position)
|
||||
|
||||
@ -444,16 +427,12 @@ open class Tile : IsPartOfGameInfoSerialization {
|
||||
else -> false // Neutral - unblocks tile;
|
||||
}
|
||||
}
|
||||
|
||||
if (isLand) // Only water tiles are blocked if empty
|
||||
return false
|
||||
|
||||
// For water tiles need also to check neighbors:
|
||||
// enemy military naval units blockade all adjacent water tiles.
|
||||
for (neighbor in neighbors) {
|
||||
if (!neighbor.isWater)
|
||||
continue
|
||||
|
||||
for (neighbor in neighbors.filter { it.isWater }) {
|
||||
val neighborUnit = neighbor.militaryUnit ?: continue
|
||||
|
||||
// Embarked units do not blockade adjacent tiles
|
||||
@ -473,22 +452,6 @@ open class Tile : IsPartOfGameInfoSerialization {
|
||||
return workingCity != null && workingCity.lockedTiles.contains(position)
|
||||
}
|
||||
|
||||
// For dividing the map into Regions to determine start locations
|
||||
fun getTileFertility(checkCoasts: Boolean): Int {
|
||||
var fertility = 0
|
||||
for (terrain in allTerrains) {
|
||||
if (terrain.hasUnique(UniqueType.OverrideFertility))
|
||||
return terrain.getMatchingUniques(UniqueType.OverrideFertility).first().params[0].toInt()
|
||||
else
|
||||
fertility += terrain.getMatchingUniques(UniqueType.AddFertility)
|
||||
.sumOf { it.params[0].toInt() }
|
||||
}
|
||||
if (isAdjacentToRiver()) fertility += 1
|
||||
if (isAdjacentTo(Constants.freshWater)) fertility += 1 // meaning total +2 for river
|
||||
if (checkCoasts && isCoastalTile()) fertility += 2
|
||||
return fertility
|
||||
}
|
||||
|
||||
fun providesResources(civInfo: Civilization): Boolean {
|
||||
if (!hasViewableResource(civInfo)) return false
|
||||
if (isCityCenter()) return true
|
||||
@ -576,17 +539,10 @@ open class Tile : IsPartOfGameInfoSerialization {
|
||||
resource != null && (tileResource.revealedBy == null || civInfo.tech.isResearched(
|
||||
tileResource.revealedBy!!))
|
||||
|
||||
fun getViewableTilesList(distance: Int): List<Tile> =
|
||||
tileMap.getViewableTiles(position, distance)
|
||||
|
||||
fun getTilesInDistance(distance: Int): Sequence<Tile> =
|
||||
tileMap.getTilesInDistance(position, distance)
|
||||
|
||||
fun getTilesInDistanceRange(range: IntRange): Sequence<Tile> =
|
||||
tileMap.getTilesInDistanceRange(position, range)
|
||||
|
||||
fun getTilesAtDistance(distance: Int): Sequence<Tile> =
|
||||
tileMap.getTilesAtDistance(position, distance)
|
||||
fun getViewableTilesList(distance: Int): List<Tile> = tileMap.getViewableTiles(position, distance)
|
||||
fun getTilesInDistance(distance: Int): Sequence<Tile> = tileMap.getTilesInDistance(position, distance)
|
||||
fun getTilesInDistanceRange(range: IntRange): Sequence<Tile> = tileMap.getTilesInDistanceRange(position, range)
|
||||
fun getTilesAtDistance(distance: Int): Sequence<Tile> =tileMap.getTilesAtDistance(position, distance)
|
||||
|
||||
fun getDefensiveBonus(): Float {
|
||||
var bonus = baseTerrainObject.defenceBonus
|
||||
@ -937,9 +893,7 @@ open class Tile : IsPartOfGameInfoSerialization {
|
||||
owningCity!!.civ.cache.updateCivResources()
|
||||
}
|
||||
|
||||
fun isPillaged(): Boolean {
|
||||
return improvementIsPillaged || roadIsPillaged
|
||||
}
|
||||
fun isPillaged(): Boolean = improvementIsPillaged || roadIsPillaged
|
||||
|
||||
fun setRepaired() {
|
||||
improvementInProgress = null
|
||||
|
@ -5,8 +5,8 @@ import com.badlogic.gdx.scenes.scene2d.ui.Image
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.components.tilegroups.TileGroup
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.atan
|
||||
|
||||
@ -33,6 +33,17 @@ class TileLayerBorders(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Returns the left shared neighbor of `this` and [neighbor] (relative to the view direction `this`->[neighbor]), or null if there is no such tile. */
|
||||
private fun Tile.getLeftSharedNeighbor(neighbor: Tile): Tile? {
|
||||
return tileMap.getClockPositionNeighborTile(this,(tileMap.getNeighborTileClockPosition(this, neighbor) - 2) % 12)
|
||||
}
|
||||
|
||||
/** Returns the right shared neighbor of `this` and [neighbor] (relative to the view direction `this`->[neighbor]), or null if there is no such tile. */
|
||||
private fun Tile.getRightSharedNeighbor(neighbor: Tile): Tile? {
|
||||
return tileMap.getClockPositionNeighborTile(this,(tileMap.getNeighborTileClockPosition(this, neighbor) + 2) % 12)
|
||||
}
|
||||
|
||||
private fun updateBorders() {
|
||||
|
||||
// This is longer than it could be, because of performance -
|
||||
|
Reference in New Issue
Block a user