mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-12 00:39:56 +07:00
AI rationing of strategic resources; Hydro Plant re-enabled (#5401)
* AI evaluation of resources * optimizations * sell or disband when needed for space victory * use for all constructions * use in trade evaluations * .requiresResource()
This commit is contained in:
@ -913,7 +913,6 @@
|
|||||||
"requiredBuilding": "Bank",
|
"requiredBuilding": "Bank",
|
||||||
"requiredTech": "Electricity"
|
"requiredTech": "Electricity"
|
||||||
},
|
},
|
||||||
/* This works and even has icon but AI cannot manage its Aluminum at this moment
|
|
||||||
{
|
{
|
||||||
"name": "Hydro Plant",
|
"name": "Hydro Plant",
|
||||||
"requiredResource": "Aluminum",
|
"requiredResource": "Aluminum",
|
||||||
@ -922,7 +921,6 @@
|
|||||||
"uniques": ["Must be on [River]","[+1 Production] from [River] tiles [in this city]"],
|
"uniques": ["Must be on [River]","[+1 Production] from [River] tiles [in this city]"],
|
||||||
"requiredTech": "Electricity"
|
"requiredTech": "Electricity"
|
||||||
},
|
},
|
||||||
*/
|
|
||||||
|
|
||||||
// Modern Era
|
// Modern Era
|
||||||
|
|
||||||
|
@ -77,6 +77,9 @@ class GameInfo {
|
|||||||
@Transient
|
@Transient
|
||||||
var simulateUntilWin = false
|
var simulateUntilWin = false
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var spaceResources = HashSet<String>()
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
//region Pure functions
|
//region Pure functions
|
||||||
|
|
||||||
@ -312,6 +315,9 @@ class GameInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spaceResources.addAll(ruleSet.buildings.values.filter { it.hasUnique("Spaceship part") }
|
||||||
|
.flatMap { it.getResourceRequirements().keys } )
|
||||||
|
|
||||||
barbarians.setTransients(this)
|
barbarians.setTransients(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package com.unciv.logic.automation
|
package com.unciv.logic.automation
|
||||||
|
|
||||||
import com.unciv.logic.city.CityInfo
|
import com.unciv.logic.city.CityInfo
|
||||||
|
import com.unciv.logic.city.INonPerpetualConstruction
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.logic.map.BFS
|
import com.unciv.logic.map.BFS
|
||||||
import com.unciv.logic.map.TileInfo
|
import com.unciv.logic.map.TileInfo
|
||||||
|
import com.unciv.models.ruleset.Building
|
||||||
import com.unciv.models.ruleset.VictoryType
|
import com.unciv.models.ruleset.VictoryType
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import com.unciv.models.ruleset.unit.BaseUnit
|
import com.unciv.models.ruleset.unit.BaseUnit
|
||||||
@ -60,14 +62,11 @@ object Automation {
|
|||||||
fun chooseMilitaryUnit(city: CityInfo): String? {
|
fun chooseMilitaryUnit(city: CityInfo): String? {
|
||||||
var militaryUnits =
|
var militaryUnits =
|
||||||
city.cityConstructions.getConstructableUnits().filter { !it.isCivilian() }
|
city.cityConstructions.getConstructableUnits().filter { !it.isCivilian() }
|
||||||
|
.filter { allowSpendingResource(city.civInfo, it) }
|
||||||
if (militaryUnits.map { it.name }
|
if (militaryUnits.map { it.name }
|
||||||
.contains(city.cityConstructions.currentConstructionFromQueue))
|
.contains(city.cityConstructions.currentConstructionFromQueue))
|
||||||
return city.cityConstructions.currentConstructionFromQueue
|
return city.cityConstructions.currentConstructionFromQueue
|
||||||
|
|
||||||
// This is so that the AI doesn't use all its aluminum on units and have none left for spaceship parts
|
|
||||||
val aluminum = city.civInfo.getCivResourcesByName()["Aluminum"]
|
|
||||||
if (aluminum != null && aluminum < 2) // mods may have no aluminum
|
|
||||||
militaryUnits.filter { !it.getResourceRequirements().containsKey("Aluminum") }
|
|
||||||
|
|
||||||
val findWaterConnectedCitiesAndEnemies =
|
val findWaterConnectedCitiesAndEnemies =
|
||||||
BFS(city.getCenterTile()) { it.isWater || it.isCityCenter() }
|
BFS(city.getCenterTile()) { it.isWater || it.isCityCenter() }
|
||||||
@ -100,6 +99,80 @@ object Automation {
|
|||||||
return chosenUnit.name
|
return chosenUnit.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Determines whether the AI should be willing to spend strategic resources to build
|
||||||
|
* [construction] in [city], assumes that we are actually able to do so. */
|
||||||
|
fun allowSpendingResource(civInfo: CivilizationInfo, construction: INonPerpetualConstruction): Boolean {
|
||||||
|
// City states do whatever they want
|
||||||
|
if (civInfo.isCityState())
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Spaceships are always allowed
|
||||||
|
if (construction.hasUnique("Spaceship part"))
|
||||||
|
return true
|
||||||
|
|
||||||
|
val requiredResources = construction.getResourceRequirements()
|
||||||
|
// Does it even require any resources?
|
||||||
|
if (requiredResources.isEmpty())
|
||||||
|
return true
|
||||||
|
|
||||||
|
val civResources = civInfo.getCivResourcesByName()
|
||||||
|
|
||||||
|
// Rule of thumb: reserve 2-3 for spaceship, then reserve half each for buildings and units
|
||||||
|
// Assume that no buildings provide any resources
|
||||||
|
for ((resource, amount) in requiredResources) {
|
||||||
|
|
||||||
|
// Also count things under construction
|
||||||
|
var futureForUnits = 0
|
||||||
|
var futureForBuildings = 0
|
||||||
|
|
||||||
|
for (city in civInfo.cities) {
|
||||||
|
val otherConstruction = city.cityConstructions.getCurrentConstruction()
|
||||||
|
if (otherConstruction is Building)
|
||||||
|
futureForBuildings += otherConstruction.getResourceRequirements()[resource] ?: 0
|
||||||
|
else
|
||||||
|
futureForUnits += otherConstruction.getResourceRequirements()[resource] ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we have some for space
|
||||||
|
if (resource in civInfo.gameInfo.spaceResources && civResources[resource]!! - amount - futureForBuildings - futureForUnits
|
||||||
|
< getReservedSpaceResourceAmount(civInfo)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume buildings remain useful
|
||||||
|
val neededForBuilding = civInfo.lastEraResourceUsedForBuilding[resource] != null
|
||||||
|
// Don't care about old units
|
||||||
|
val neededForUnits = civInfo.lastEraResourceUsedForUnit[resource] != null
|
||||||
|
&& civInfo.lastEraResourceUsedForUnit[resource]!! >= civInfo.getEraNumber()
|
||||||
|
|
||||||
|
// No need to save for both
|
||||||
|
if (!neededForBuilding || !neededForUnits) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val usedForUnits = civInfo.detailedCivResources.filter { it.resource.name == resource && it.origin == "Units" }.sumOf { -it.amount }
|
||||||
|
val usedForBuildings = civInfo.detailedCivResources.filter { it.resource.name == resource && it.origin == "Buildings" }.sumOf { -it.amount }
|
||||||
|
|
||||||
|
if (construction is Building) {
|
||||||
|
// Will more than half the total resources be used for buildings after this construction?
|
||||||
|
if (civResources[resource]!! + usedForUnits < usedForBuildings + amount + futureForBuildings) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Will more than half the total resources be used for units after this construction?
|
||||||
|
if (civResources[resource]!! + usedForBuildings < usedForUnits + amount + futureForUnits) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getReservedSpaceResourceAmount(civInfo: CivilizationInfo): Int {
|
||||||
|
return if (civInfo.nation.preferredVictoryType == VictoryType.Scientific) 3 else 2
|
||||||
|
}
|
||||||
|
|
||||||
fun threatAssessment(assessor: CivilizationInfo, assessed: CivilizationInfo): ThreatLevel {
|
fun threatAssessment(assessor: CivilizationInfo, assessed: CivilizationInfo): ThreatLevel {
|
||||||
val powerLevelComparison =
|
val powerLevelComparison =
|
||||||
assessed.getStatForRanking(RankingType.Force) / assessor.getStatForRanking(RankingType.Force).toFloat()
|
assessed.getStatForRanking(RankingType.Force) / assessor.getStatForRanking(RankingType.Force).toFloat()
|
||||||
|
@ -115,7 +115,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
|
|
||||||
private fun addWorkBoatChoice() {
|
private fun addWorkBoatChoice() {
|
||||||
val buildableWorkboatUnits = cityInfo.cityConstructions.getConstructableUnits()
|
val buildableWorkboatUnits = cityInfo.cityConstructions.getConstructableUnits()
|
||||||
.filter { it.uniques.contains(Constants.workBoatsUnique) }
|
.filter { it.uniques.contains(Constants.workBoatsUnique)
|
||||||
|
&& Automation.allowSpendingResource(civInfo, it) }
|
||||||
val canBuildWorkboat = buildableWorkboatUnits.any()
|
val canBuildWorkboat = buildableWorkboatUnits.any()
|
||||||
&& !cityInfo.getTiles().any { it.civilianUnit?.hasUnique(Constants.workBoatsUnique) == true }
|
&& !cityInfo.getTiles().any { it.civilianUnit?.hasUnique(Constants.workBoatsUnique) == true }
|
||||||
if (!canBuildWorkboat) return
|
if (!canBuildWorkboat) return
|
||||||
@ -140,7 +141,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
val workerEquivalents = civInfo.gameInfo.ruleSet.units.values
|
val workerEquivalents = civInfo.gameInfo.ruleSet.units.values
|
||||||
.filter { it.uniques.any {
|
.filter { it.uniques.any {
|
||||||
unique -> unique.equalsPlaceholderText(Constants.canBuildImprovements)
|
unique -> unique.equalsPlaceholderText(Constants.canBuildImprovements)
|
||||||
} && it.isBuildable(cityConstructions) }
|
} && it.isBuildable(cityConstructions)
|
||||||
|
&& Automation.allowSpendingResource(civInfo, it) }
|
||||||
if (workerEquivalents.isEmpty()) return // for mods with no worker units
|
if (workerEquivalents.isEmpty()) return // for mods with no worker units
|
||||||
if (civInfo.getIdleUnits().any { it.isAutomated() && it.hasUniqueToBuildImprovements })
|
if (civInfo.getIdleUnits().any { it.isAutomated() && it.hasUniqueToBuildImprovements })
|
||||||
return // If we have automated workers who have no work to do then it's silly to construct new workers.
|
return // If we have automated workers who have no work to do then it's silly to construct new workers.
|
||||||
@ -155,7 +157,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
|
|
||||||
private fun addCultureBuildingChoice() {
|
private fun addCultureBuildingChoice() {
|
||||||
val cultureBuilding = buildableNotWonders
|
val cultureBuilding = buildableNotWonders
|
||||||
.filter { it.isStatRelated(Stat.Culture) }.minByOrNull { it.cost }
|
.filter { it.isStatRelated(Stat.Culture)
|
||||||
|
&& Automation.allowSpendingResource(civInfo, it) }.minByOrNull { it.cost }
|
||||||
if (cultureBuilding != null) {
|
if (cultureBuilding != null) {
|
||||||
var modifier = 0.5f
|
var modifier = 0.5f
|
||||||
if (cityInfo.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it
|
if (cityInfo.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it
|
||||||
@ -175,7 +178,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addOtherBuildingChoice() {
|
private fun addOtherBuildingChoice() {
|
||||||
val otherBuilding = buildableNotWonders.minByOrNull { it.cost }
|
val otherBuilding = buildableNotWonders
|
||||||
|
.filter { Automation.allowSpendingResource(civInfo, it) }.minByOrNull { it.cost }
|
||||||
if (otherBuilding != null) {
|
if (otherBuilding != null) {
|
||||||
val modifier = 0.6f
|
val modifier = 0.6f
|
||||||
addChoice(relativeCostEffectiveness, otherBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, otherBuilding.name, modifier)
|
||||||
@ -211,6 +215,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
if (!buildableWonders.any()) return
|
if (!buildableWonders.any()) return
|
||||||
|
|
||||||
val highestPriorityWonder = buildableWonders
|
val highestPriorityWonder = buildableWonders
|
||||||
|
.filter { Automation.allowSpendingResource(civInfo, it) }
|
||||||
.maxByOrNull { getWonderPriority(it) }!!
|
.maxByOrNull { getWonderPriority(it) }!!
|
||||||
val citiesBuildingWonders = civInfo.cities
|
val citiesBuildingWonders = civInfo.cities
|
||||||
.count { it.cityConstructions.isBuildingWonder() }
|
.count { it.cityConstructions.isBuildingWonder() }
|
||||||
@ -222,7 +227,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
|
|
||||||
private fun addUnitTrainingBuildingChoice() {
|
private fun addUnitTrainingBuildingChoice() {
|
||||||
val unitTrainingBuilding = buildableNotWonders.asSequence()
|
val unitTrainingBuilding = buildableNotWonders.asSequence()
|
||||||
.filter { it.hasUnique("New [] units start with [] Experience []") }.minByOrNull { it.cost }
|
.filter { it.hasUnique("New [] units start with [] Experience []")
|
||||||
|
&& Automation.allowSpendingResource(civInfo, it)}.minByOrNull { it.cost }
|
||||||
if (unitTrainingBuilding != null && (preferredVictoryType != VictoryType.Cultural || isAtWar)) {
|
if (unitTrainingBuilding != null && (preferredVictoryType != VictoryType.Cultural || isAtWar)) {
|
||||||
var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
|
var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
|
||||||
if (isAtWar) modifier *= 2
|
if (isAtWar) modifier *= 2
|
||||||
@ -234,7 +240,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
|
|
||||||
private fun addDefenceBuildingChoice() {
|
private fun addDefenceBuildingChoice() {
|
||||||
val defensiveBuilding = buildableNotWonders.asSequence()
|
val defensiveBuilding = buildableNotWonders.asSequence()
|
||||||
.filter { it.cityStrength > 0 }.minByOrNull { it.cost }
|
.filter { it.cityStrength > 0
|
||||||
|
&& Automation.allowSpendingResource(civInfo, it)}.minByOrNull { it.cost }
|
||||||
if (defensiveBuilding != null && (isAtWar || preferredVictoryType != VictoryType.Cultural)) {
|
if (defensiveBuilding != null && (isAtWar || preferredVictoryType != VictoryType.Cultural)) {
|
||||||
var modifier = 0.2f
|
var modifier = 0.2f
|
||||||
if (isAtWar) modifier = 0.5f
|
if (isAtWar) modifier = 0.5f
|
||||||
@ -250,8 +257,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
|
|
||||||
private fun addHappinessBuildingChoice() {
|
private fun addHappinessBuildingChoice() {
|
||||||
val happinessBuilding = buildableNotWonders.asSequence()
|
val happinessBuilding = buildableNotWonders.asSequence()
|
||||||
.filter { it.isStatRelated(Stat.Happiness)
|
.filter { (it.isStatRelated(Stat.Happiness)
|
||||||
|| it.uniques.contains("Remove extra unhappiness from annexed cities") }
|
|| it.uniques.contains("Remove extra unhappiness from annexed cities"))
|
||||||
|
&& Automation.allowSpendingResource(civInfo, it)}
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost }
|
||||||
if (happinessBuilding != null) {
|
if (happinessBuilding != null) {
|
||||||
var modifier = 1f
|
var modifier = 1f
|
||||||
@ -265,7 +273,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
private fun addScienceBuildingChoice() {
|
private fun addScienceBuildingChoice() {
|
||||||
if (allTechsAreResearched) return
|
if (allTechsAreResearched) return
|
||||||
val scienceBuilding = buildableNotWonders.asSequence()
|
val scienceBuilding = buildableNotWonders.asSequence()
|
||||||
.filter { it.isStatRelated(Stat.Science) }
|
.filter { it.isStatRelated(Stat.Science)
|
||||||
|
&& Automation.allowSpendingResource(civInfo, it)}
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost }
|
||||||
if (scienceBuilding != null) {
|
if (scienceBuilding != null) {
|
||||||
var modifier = 1.1f
|
var modifier = 1.1f
|
||||||
@ -276,7 +285,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addGoldBuildingChoice() {
|
private fun addGoldBuildingChoice() {
|
||||||
val goldBuilding = buildableNotWonders.asSequence().filter { it.isStatRelated(Stat.Gold) }
|
val goldBuilding = buildableNotWonders.asSequence().filter { it.isStatRelated(Stat.Gold)
|
||||||
|
&& Automation.allowSpendingResource(civInfo, it)}
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost }
|
||||||
if (goldBuilding != null) {
|
if (goldBuilding != null) {
|
||||||
val modifier = if (civInfo.statsForNextTurn.gold < 0) 3f else 1.2f
|
val modifier = if (civInfo.statsForNextTurn.gold < 0) 3f else 1.2f
|
||||||
@ -286,7 +296,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
|
|
||||||
private fun addProductionBuildingChoice() {
|
private fun addProductionBuildingChoice() {
|
||||||
val productionBuilding = buildableNotWonders.asSequence()
|
val productionBuilding = buildableNotWonders.asSequence()
|
||||||
.filter { it.isStatRelated(Stat.Production) }
|
.filter { it.isStatRelated(Stat.Production) && Automation.allowSpendingResource(civInfo, it) }
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost }
|
||||||
if (productionBuilding != null) {
|
if (productionBuilding != null) {
|
||||||
addChoice(relativeCostEffectiveness, productionBuilding.name, 1.5f)
|
addChoice(relativeCostEffectiveness, productionBuilding.name, 1.5f)
|
||||||
@ -294,8 +304,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addFoodBuildingChoice() {
|
private fun addFoodBuildingChoice() {
|
||||||
val foodBuilding = buildableNotWonders.asSequence().filter { it.isStatRelated(Stat.Food)
|
val foodBuilding = buildableNotWonders.asSequence().filter { (it.isStatRelated(Stat.Food)
|
||||||
|| it.uniqueObjects.any { it.placeholderText=="[]% of food is carried over after population increases" }}
|
|| it.uniqueObjects.any { it.placeholderText=="[]% of food is carried over after population increases" })
|
||||||
|
&& Automation.allowSpendingResource(civInfo, it) }
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost }
|
||||||
if (foodBuilding != null) {
|
if (foodBuilding != null) {
|
||||||
var modifier = 1f
|
var modifier = 1f
|
||||||
|
@ -46,6 +46,7 @@ object NextTurnAutomation {
|
|||||||
exchangeLuxuries(civInfo)
|
exchangeLuxuries(civInfo)
|
||||||
issueRequests(civInfo)
|
issueRequests(civInfo)
|
||||||
adoptPolicy(civInfo) // todo can take a second - why?
|
adoptPolicy(civInfo) // todo can take a second - why?
|
||||||
|
freeUpSpaceResources(civInfo)
|
||||||
} else {
|
} else {
|
||||||
civInfo.getFreeTechForCityState()
|
civInfo.getFreeTechForCityState()
|
||||||
civInfo.updateDiplomaticRelationshipForCityState()
|
civInfo.updateDiplomaticRelationshipForCityState()
|
||||||
@ -300,6 +301,39 @@ object NextTurnAutomation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** If we are able to build a spaceship but have already spent our resources, try disbanding
|
||||||
|
* a unit and selling a building to make room. Can happen due to trades etc */
|
||||||
|
private fun freeUpSpaceResources(civInfo: CivilizationInfo) {
|
||||||
|
// Can't build spaceships
|
||||||
|
if (!civInfo.hasUnique("Enables construction of Spaceship parts"))
|
||||||
|
return
|
||||||
|
|
||||||
|
for (resource in civInfo.gameInfo.spaceResources) {
|
||||||
|
// Have enough resources already
|
||||||
|
if (civInfo.getCivResourcesByName()[resource]!! >= Automation.getReservedSpaceResourceAmount(civInfo))
|
||||||
|
continue
|
||||||
|
|
||||||
|
val unitToDisband = civInfo.getCivUnits()
|
||||||
|
.filter { it.baseUnit.requiresResource(resource) }
|
||||||
|
.minByOrNull { it.getForceEvaluation() }
|
||||||
|
if (unitToDisband != null) {
|
||||||
|
unitToDisband.disband()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (city in civInfo.cities) {
|
||||||
|
if (city.hasSoldBuildingThisTurn)
|
||||||
|
continue
|
||||||
|
val buildingToSell = civInfo.gameInfo.ruleSet.buildings.values.filter {
|
||||||
|
it.name in city.cityConstructions.builtBuildings
|
||||||
|
&& it.requiresResource(resource) }.randomOrNull()
|
||||||
|
if (buildingToSell != null) {
|
||||||
|
city.sellBuilding(buildingToSell.name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun chooseReligiousBeliefs(civInfo: CivilizationInfo) {
|
private fun chooseReligiousBeliefs(civInfo: CivilizationInfo) {
|
||||||
choosePantheon(civInfo)
|
choosePantheon(civInfo)
|
||||||
foundReligion(civInfo)
|
foundReligion(civInfo)
|
||||||
|
@ -78,6 +78,11 @@ object UnitAutomation {
|
|||||||
val upgradedUnit = unit.getUnitToUpgradeTo()
|
val upgradedUnit = unit.getUnitToUpgradeTo()
|
||||||
if (!upgradedUnit.isBuildable(unit.civInfo)) return false // for resource reasons, usually
|
if (!upgradedUnit.isBuildable(unit.civInfo)) return false // for resource reasons, usually
|
||||||
|
|
||||||
|
if (upgradedUnit.getResourceRequirements().keys.any { !unit.baseUnit.requiresResource(it) }) {
|
||||||
|
// The upgrade requires new resource types, so check if we are willing to invest them
|
||||||
|
if (!Automation.allowSpendingResource(unit.civInfo, upgradedUnit)) return false
|
||||||
|
}
|
||||||
|
|
||||||
val upgradeAction = UnitActions.getUpgradeAction(unit)
|
val upgradeAction = UnitActions.getUpgradeAction(unit)
|
||||||
?: return false
|
?: return false
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ interface IConstruction : INamed {
|
|||||||
fun isBuildable(cityConstructions: CityConstructions): Boolean
|
fun isBuildable(cityConstructions: CityConstructions): Boolean
|
||||||
fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean
|
fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean
|
||||||
fun getResourceRequirements(): HashMap<String,Int>
|
fun getResourceRequirements(): HashMap<String,Int>
|
||||||
|
fun requiresResource(resource: String): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
||||||
@ -208,4 +209,6 @@ open class PerpetualConstruction(override var name: String, val description: Str
|
|||||||
|
|
||||||
override fun getResourceRequirements(): HashMap<String, Int> = hashMapOf()
|
override fun getResourceRequirements(): HashMap<String, Int> = hashMapOf()
|
||||||
|
|
||||||
|
override fun requiresResource(resource: String) = false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,12 @@ class CivilizationInfo {
|
|||||||
@Transient
|
@Transient
|
||||||
var nonStandardTerrainDamage = false
|
var nonStandardTerrainDamage = false
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var lastEraResourceUsedForBuilding = HashMap<String, Int>()
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
val lastEraResourceUsedForUnit = HashMap<String, Int>()
|
||||||
|
|
||||||
var playerType = PlayerType.AI
|
var playerType = PlayerType.AI
|
||||||
|
|
||||||
/** Used in online multiplayer for human players */
|
/** Used in online multiplayer for human players */
|
||||||
@ -681,6 +687,20 @@ class CivilizationInfo {
|
|||||||
// Cache whether this civ gets nonstandard terrain damage for performance reasons.
|
// Cache whether this civ gets nonstandard terrain damage for performance reasons.
|
||||||
nonStandardTerrainDamage = getMatchingUniques("Units ending their turn on [] tiles take [] damage")
|
nonStandardTerrainDamage = getMatchingUniques("Units ending their turn on [] tiles take [] damage")
|
||||||
.any { gameInfo.ruleSet.terrains[it.params[0]]!!.damagePerTurn != it.params[1].toInt() }
|
.any { gameInfo.ruleSet.terrains[it.params[0]]!!.damagePerTurn != it.params[1].toInt() }
|
||||||
|
|
||||||
|
// Cache the last era each resource is used for buildings or units respectively for AI building evaluation
|
||||||
|
for (resource in gameInfo.ruleSet.tileResources.values.filter { it.resourceType == ResourceType.Strategic }.map { it.name }) {
|
||||||
|
val applicableBuildings = gameInfo.ruleSet.buildings.values.filter { getEquivalentBuilding(it) == it && it.requiresResource(resource) }
|
||||||
|
val applicableUnits = gameInfo.ruleSet.units.values.filter { getEquivalentUnit(it) == it && it.requiresResource(resource) }
|
||||||
|
|
||||||
|
val lastEraForBuilding = applicableBuildings.map { gameInfo.ruleSet.eras[gameInfo.ruleSet.technologies[it.requiredTech]?.era()]?.eraNumber ?: 0 }.maxOrNull()
|
||||||
|
val lastEraForUnit = applicableUnits.map { gameInfo.ruleSet.eras[gameInfo.ruleSet.technologies[it.requiredTech]?.era()]?.eraNumber ?: 0 }.maxOrNull()
|
||||||
|
|
||||||
|
if (lastEraForBuilding != null)
|
||||||
|
lastEraResourceUsedForBuilding[resource] = lastEraForBuilding
|
||||||
|
if (lastEraForUnit != null)
|
||||||
|
lastEraResourceUsedForUnit[resource] = lastEraForUnit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSightAndResources() {
|
fun updateSightAndResources() {
|
||||||
|
@ -191,6 +191,11 @@ class TradeEvaluation {
|
|||||||
else 500 // you want to take away our last lux of this type?!
|
else 500 // you want to take away our last lux of this type?!
|
||||||
}
|
}
|
||||||
TradeType.Strategic_Resource -> {
|
TradeType.Strategic_Resource -> {
|
||||||
|
if (civInfo.gameInfo.spaceResources.contains(offer.name) &&
|
||||||
|
(civInfo.hasUnique("Enables construction of Spaceship parts") ||
|
||||||
|
tradePartner.hasUnique("Enables construction of Spaceship parts")))
|
||||||
|
return 10000 // We'd rather win the game, thanks
|
||||||
|
|
||||||
if (!civInfo.isAtWar()) return 50 * offer.amount
|
if (!civInfo.isAtWar()) return 50 * offer.amount
|
||||||
|
|
||||||
val canUseForUnits = civInfo.gameInfo.ruleSet.units.values
|
val canUseForUnits = civInfo.gameInfo.ruleSet.units.values
|
||||||
|
@ -690,6 +690,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
if (get(stat) > 0) return true
|
if (get(stat) > 0) return true
|
||||||
if (getStatPercentageBonuses(null)[stat] > 0) return true
|
if (getStatPercentageBonuses(null)[stat] > 0) return true
|
||||||
if (uniqueObjects.any { it.placeholderText == "[] per [] population []" && it.stats[stat] > 0 }) return true
|
if (uniqueObjects.any { it.placeholderText == "[] per [] population []" && it.stats[stat] > 0 }) return true
|
||||||
|
if (uniqueObjects.any { it.placeholderText == "[] from [] tiles []" && it.stats[stat] > 0 }) return true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,4 +711,12 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
|
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
|
||||||
return resourceRequirements
|
return resourceRequirements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun requiresResource(resource: String): Boolean {
|
||||||
|
if (requiredResource == resource) return true
|
||||||
|
for (unique in getMatchingUniques(UniqueType.ConsumesResources)) {
|
||||||
|
if (unique.params[1] == resource) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -540,6 +540,14 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
|||||||
return resourceRequirements
|
return resourceRequirements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun requiresResource(resource: String): Boolean {
|
||||||
|
if (requiredResource == resource) return true
|
||||||
|
for (unique in getMatchingUniques(UniqueType.ConsumesResources)) {
|
||||||
|
if (unique.params[1] == resource) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fun isRanged() = rangedStrength > 0
|
fun isRanged() = rangedStrength > 0
|
||||||
fun isMelee() = !isRanged() && strength > 0
|
fun isMelee() = !isRanged() && strength > 0
|
||||||
fun isMilitary() = isRanged() || isMelee()
|
fun isMilitary() = isRanged() || isMelee()
|
||||||
|
Reference in New Issue
Block a user