mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-22 02:07:43 +07:00
Performance Improvements to Construction Automation (#8508)
* Improvements to construction automation * Remove early return in work boat consideration * Reviews and minor addition * Move cache init to correct file * I swear I didn't delete that line during the merge * Redundancy
This commit is contained in:
parent
e113a3a140
commit
2c0ce05f78
@ -107,7 +107,9 @@ object Automation {
|
||||
|
||||
fun tryTrainMilitaryUnit(city: City) {
|
||||
if (city.isPuppet) return
|
||||
val chosenUnitName = chooseMilitaryUnit(city)
|
||||
if ((city.cityConstructions.getCurrentConstruction() as? BaseUnit)?.isMilitary() == true)
|
||||
return // already training a military unit
|
||||
val chosenUnitName = chooseMilitaryUnit(city, city.civInfo.gameInfo.ruleSet.units.values.asSequence())
|
||||
if (chosenUnitName != null)
|
||||
city.cityConstructions.currentConstructionFromQueue = chosenUnitName
|
||||
}
|
||||
@ -133,57 +135,55 @@ object Automation {
|
||||
return totalCarriableUnits < totalCarryingSlots
|
||||
}
|
||||
|
||||
fun chooseMilitaryUnit(city: City): String? {
|
||||
fun chooseMilitaryUnit(city: City, availableUnits: Sequence<BaseUnit>): String? {
|
||||
val currentChoice = city.cityConstructions.getCurrentConstruction()
|
||||
if (currentChoice is BaseUnit && !currentChoice.isCivilian()) return city.cityConstructions.currentConstructionFromQueue
|
||||
|
||||
var militaryUnits = city.getRuleset().units.values
|
||||
.filter { !it.isCivilian() }
|
||||
.filter { allowSpendingResource(city.civInfo, it) }
|
||||
|
||||
val findWaterConnectedCitiesAndEnemies =
|
||||
BFS(city.getCenterTile()) { it.isWater || it.isCityCenter() }
|
||||
findWaterConnectedCitiesAndEnemies.stepToEnd()
|
||||
if (findWaterConnectedCitiesAndEnemies.getReachedTiles().none {
|
||||
(it.isCityCenter() && it.getOwner() != city.civInfo)
|
||||
|| (it.militaryUnit != null && it.militaryUnit!!.civInfo != city.civInfo)
|
||||
}) // there is absolutely no reason for you to make water units on this body of water.
|
||||
militaryUnits = militaryUnits.filter { !it.isWaterUnit() }
|
||||
|
||||
|
||||
val carryingOnlyUnits = militaryUnits.filter {
|
||||
it.hasUnique(UniqueType.CarryAirUnits)
|
||||
&& it.hasUnique(UniqueType.CannotAttack)
|
||||
// if not coastal, removeShips == true so don't even consider ships
|
||||
var removeShips = true
|
||||
if (city.isCoastal()) {
|
||||
// in the future this could be simplified by assigning every distinct non-lake body of
|
||||
// water their own ID like a continent ID
|
||||
val findWaterConnectedCitiesAndEnemies =
|
||||
BFS(city.getCenterTile()) { it.isWater || it.isCityCenter() }
|
||||
findWaterConnectedCitiesAndEnemies.stepToEnd()
|
||||
removeShips = findWaterConnectedCitiesAndEnemies.getReachedTiles().none {
|
||||
(it.isCityCenter() && it.getOwner() != city.civInfo)
|
||||
|| (it.militaryUnit != null && it.militaryUnit!!.civInfo != city.civInfo)
|
||||
} // there is absolutely no reason for you to make water units on this body of water.
|
||||
}
|
||||
|
||||
for (unit in carryingOnlyUnits)
|
||||
if (providesUnneededCarryingSlots(unit, city.civInfo))
|
||||
militaryUnits = militaryUnits.filterNot { it == unit }
|
||||
|
||||
// Only now do we filter out the constructable units because that's a heavier check
|
||||
militaryUnits = militaryUnits.filter { it.isBuildable(city.cityConstructions) } // gather once because we have a .any afterwards
|
||||
val militaryUnits = availableUnits
|
||||
.filter { it.isMilitary() }
|
||||
.filterNot { removeShips && it.isWaterUnit() }
|
||||
.filter { allowSpendingResource(city.civInfo, it) }
|
||||
.filterNot {
|
||||
// filter out carrier-type units that can't attack if we don't need them
|
||||
(it.hasUnique(UniqueType.CarryAirUnits) && it.hasUnique(UniqueType.CannotAttack))
|
||||
&& providesUnneededCarryingSlots(it, city.civInfo)
|
||||
}
|
||||
// Only now do we filter out the constructable units because that's a heavier check
|
||||
.filter { it.isBuildable(city.cityConstructions) }
|
||||
.toList()
|
||||
|
||||
val chosenUnit: BaseUnit
|
||||
if (!city.civInfo.isAtWar()
|
||||
&& city.civInfo.cities.any { it.getCenterTile().militaryUnit == null }
|
||||
&& militaryUnits.any { it.isRanged() } // this is for city defence so get a ranged unit if we can
|
||||
&& city.civInfo.cities.any { it.getCenterTile().militaryUnit == null }
|
||||
&& militaryUnits.any { it.isRanged() } // this is for city defence so get a ranged unit if we can
|
||||
) {
|
||||
chosenUnit = militaryUnits
|
||||
.filter { it.isRanged() }
|
||||
.maxByOrNull { it.cost }!!
|
||||
} else { // randomize type of unit and take the most expensive of its kind
|
||||
val availableTypes = militaryUnits
|
||||
.map { it.unitType }
|
||||
.distinct()
|
||||
if (availableTypes.none()) return null
|
||||
val bestUnitsForType = availableTypes.map { type ->
|
||||
militaryUnits
|
||||
.filter { unit -> unit.unitType == type }
|
||||
.maxByOrNull { unit -> unit.cost }!!
|
||||
val bestUnitsForType = hashMapOf<String, BaseUnit>()
|
||||
for (unit in militaryUnits) {
|
||||
if (bestUnitsForType[unit.unitType] == null || bestUnitsForType[unit.unitType]!!.cost < unit.cost) {
|
||||
bestUnitsForType[unit.unitType] = unit
|
||||
}
|
||||
}
|
||||
// Check the maximum force evaluation for the shortlist so we can prune useless ones (ie scouts)
|
||||
val bestForce = bestUnitsForType.maxOf { it.getForceEvaluation() }
|
||||
chosenUnit = bestUnitsForType.filter { it.uniqueTo != null || it.getForceEvaluation() > bestForce / 3 }.random()
|
||||
val bestForce = bestUnitsForType.maxOfOrNull { it.value.getForceEvaluation() } ?: return null
|
||||
chosenUnit = bestUnitsForType.filterValues { it.uniqueTo != null || it.getForceEvaluation() > bestForce / 3 }.values.random()
|
||||
}
|
||||
return chosenUnit.name
|
||||
}
|
||||
|
@ -24,11 +24,17 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
private val cityInfo = cityConstructions.city
|
||||
private val civInfo = cityInfo.civInfo
|
||||
|
||||
private val buildings = cityInfo.getRuleset().buildings.values
|
||||
private val buildableBuildings = hashMapOf<String, Boolean>()
|
||||
private val buildableUnits = hashMapOf<String, Boolean>()
|
||||
private val buildings = cityInfo.getRuleset().buildings.values.asSequence()
|
||||
|
||||
private val nonWonders = buildings.filterNot { it.isAnyWonder() }
|
||||
.filterNot { buildableBuildings[it.name] == false } // if we already know that this building can't be built here then don't even consider it
|
||||
private val statBuildings = nonWonders.filter { !it.isEmpty() && Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
private val wonders = buildings.filter { it.isAnyWonder() }
|
||||
|
||||
private val units = cityInfo.getRuleset().units.values
|
||||
private val units = cityInfo.getRuleset().units.values.asSequence()
|
||||
.filterNot { buildableUnits[it.name] == false } // if we already know that this unit can't be built here then don't even consider it
|
||||
|
||||
private val civUnits = civInfo.units.getCivUnits()
|
||||
private val militaryUnits = civUnits.count { it.baseUnit.isMilitary() }
|
||||
@ -57,8 +63,14 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
choices.add(ConstructionChoice(choice, choiceModifier, cityConstructions.getRemainingWork(choice)))
|
||||
}
|
||||
|
||||
private fun Collection<INonPerpetualConstruction>.isBuildable(): Collection<INonPerpetualConstruction> {
|
||||
return this.filter { it.isBuildable(cityConstructions) }
|
||||
private fun Sequence<INonPerpetualConstruction>.filterBuildable(): Sequence<INonPerpetualConstruction> {
|
||||
return this.filter {
|
||||
val cache = if (it is Building) buildableBuildings else buildableUnits
|
||||
if (cache[it.name] == null) {
|
||||
cache[it.name] = it.isBuildable(cityConstructions)
|
||||
}
|
||||
cache[it.name]!!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -115,7 +127,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
if (!isAtWar && (civInfo.stats.statsForNextTurn.gold < 0 || militaryUnits > max(5, cities * 2))) return
|
||||
if (civInfo.gold < -50) return
|
||||
|
||||
val militaryUnit = Automation.chooseMilitaryUnit(cityInfo) ?: return
|
||||
val militaryUnit = Automation.chooseMilitaryUnit(cityInfo, units) ?: return
|
||||
val unitsToCitiesRatio = cities.toFloat() / (militaryUnits + 1)
|
||||
// most buildings and civ units contribute the the civ's growth, military units are anti-growth
|
||||
var modifier = sqrt(unitsToCitiesRatio) / 2
|
||||
@ -137,8 +149,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
val buildableWorkboatUnits = units
|
||||
.filter {
|
||||
it.hasUnique(UniqueType.CreateWaterImprovements)
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}.isBuildable()
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}.filterBuildable()
|
||||
val alreadyHasWorkBoat = buildableWorkboatUnits.any()
|
||||
&& !cityInfo.getTiles().any {
|
||||
it.civilianUnit?.hasUnique(UniqueType.CreateWaterImprovements) == true
|
||||
@ -153,7 +165,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
if (!bfs.getReachedTiles()
|
||||
.any { tile ->
|
||||
tile.hasViewableResource(civInfo) && tile.improvement == null && tile.getOwner() == civInfo
|
||||
&& tile.tileResource.getImprovements().any {
|
||||
&& tile.tileResource.getImprovements().any {
|
||||
tile.improvementFunctions.canBuildImprovement(tile.ruleset.tileImprovements[it]!!, civInfo)
|
||||
}
|
||||
}
|
||||
@ -169,8 +181,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
val workerEquivalents = units
|
||||
.filter {
|
||||
it.hasUnique(UniqueType.BuildImprovements)
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}.isBuildable()
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}.filterBuildable()
|
||||
if (workerEquivalents.none()) return // for mods with no worker units
|
||||
|
||||
// For the first 3 cities, dedicate a worker, from then on only build another worker if you have 12 cities.
|
||||
@ -184,10 +196,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
}
|
||||
|
||||
private fun addCultureBuildingChoice() {
|
||||
val cultureBuilding = nonWonders
|
||||
.filter { it.isStatRelated(Stat.Culture)
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}.isBuildable()
|
||||
val cultureBuilding = statBuildings
|
||||
.filter { it.isStatRelated(Stat.Culture) }
|
||||
.filterBuildable()
|
||||
.minByOrNull { it.cost }
|
||||
if (cultureBuilding != null) {
|
||||
var modifier = 0.5f
|
||||
@ -199,17 +210,19 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
}
|
||||
|
||||
private fun addSpaceshipPartChoice() {
|
||||
val spaceshipPart = (nonWonders + units).filter { it.name in spaceshipParts }.isBuildable()
|
||||
if (spaceshipPart.isNotEmpty()) {
|
||||
if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) return
|
||||
val spaceshipPart = (nonWonders + units).filter { it.name in spaceshipParts }.filterBuildable().firstOrNull()
|
||||
if (spaceshipPart != null) {
|
||||
val modifier = 2f
|
||||
addChoice(relativeCostEffectiveness, spaceshipPart.first().name, modifier)
|
||||
addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addOtherBuildingChoice() {
|
||||
val otherBuilding = nonWonders
|
||||
.filter { Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.isBuildable().minByOrNull { it.cost }
|
||||
.filterBuildable()
|
||||
.minByOrNull { it.cost }
|
||||
if (otherBuilding != null) {
|
||||
val modifier = 0.6f
|
||||
addChoice(relativeCostEffectiveness, otherBuilding.name, modifier)
|
||||
@ -219,16 +232,16 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
private fun getWonderPriority(wonder: Building): Float {
|
||||
// Only start building if we are the city that would complete it the soonest
|
||||
if (wonder.hasUnique(UniqueType.TriggersCulturalVictory)
|
||||
&& cityInfo == civInfo.cities.minByOrNull {
|
||||
it.cityConstructions.turnsToConstruction(wonder.name)
|
||||
}!!
|
||||
&& cityInfo == civInfo.cities.minByOrNull {
|
||||
it.cityConstructions.turnsToConstruction(wonder.name)
|
||||
}!!
|
||||
) {
|
||||
return 10f
|
||||
}
|
||||
if (wonder.name in buildingsForVictory)
|
||||
return 5f
|
||||
if (civInfo.wantsToFocusOn(Victory.Focus.Culture)
|
||||
// TODO: Moddability
|
||||
// TODO: Moddability
|
||||
&& wonder.name in listOf("Sistine Chapel", "Eiffel Tower", "Cristo Redentor", "Neuschwanstein", "Sydney Opera House"))
|
||||
return 3f
|
||||
if (wonder.isStatRelated(Stat.Science)) {
|
||||
@ -236,7 +249,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
return if (civInfo.wantsToFocusOn(Victory.Focus.Science)) 1.5f
|
||||
else 1.3f
|
||||
}
|
||||
if (wonder.name == "Manhattan Project") {
|
||||
if (wonder.hasUnique(UniqueType.EnablesNuclearWeapons)) {
|
||||
return if (civInfo.wantsToFocusOn(Victory.Focus.Military)) 2f
|
||||
else 1.3f
|
||||
}
|
||||
@ -250,7 +263,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
|
||||
val highestPriorityWonder = wonders
|
||||
.filter { Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.isBuildable().maxByOrNull { getWonderPriority(it as Building) }
|
||||
.filterBuildable()
|
||||
.maxByOrNull { getWonderPriority(it as Building) }
|
||||
?: return
|
||||
|
||||
val citiesBuildingWonders = civInfo.cities
|
||||
@ -265,7 +279,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
val unitTrainingBuilding = nonWonders
|
||||
.filter { it.hasUnique(UniqueType.UnitStartingExperience)
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}.isBuildable()
|
||||
}
|
||||
.filterBuildable()
|
||||
.minByOrNull { it.cost }
|
||||
if (unitTrainingBuilding != null && (!civInfo.wantsToFocusOn(Victory.Focus.Culture) || isAtWar)) {
|
||||
var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
|
||||
@ -280,7 +295,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
val defensiveBuilding = nonWonders
|
||||
.filter { it.cityStrength > 0
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}.isBuildable()
|
||||
}
|
||||
.filterBuildable()
|
||||
.minByOrNull { it.cost }
|
||||
if (defensiveBuilding != null && (isAtWar || !civInfo.wantsToFocusOn(Victory.Focus.Culture))) {
|
||||
var modifier = 0.2f
|
||||
@ -301,7 +317,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
.filter { (it.isStatRelated(Stat.Happiness)
|
||||
|| it.hasUnique(UniqueType.RemoveAnnexUnhappiness))
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.isBuildable()
|
||||
.filterBuildable()
|
||||
.minByOrNull { it.cost }
|
||||
if (happinessBuilding != null) {
|
||||
var modifier = 1f
|
||||
@ -315,10 +331,11 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
|
||||
private fun addScienceBuildingChoice() {
|
||||
if (allTechsAreResearched) return
|
||||
val scienceBuilding = nonWonders
|
||||
val scienceBuilding = statBuildings
|
||||
.filter { it.isStatRelated(Stat.Science)
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.isBuildable().minByOrNull { it.cost }
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.filterBuildable()
|
||||
.minByOrNull { it.cost }
|
||||
if (scienceBuilding != null) {
|
||||
var modifier = 1.1f
|
||||
if (civInfo.wantsToFocusOn(Victory.Focus.Science))
|
||||
@ -328,9 +345,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
}
|
||||
|
||||
private fun addGoldBuildingChoice() {
|
||||
val goldBuilding = nonWonders.filter { it.isStatRelated(Stat.Gold)
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.isBuildable().minByOrNull { it.cost }
|
||||
val goldBuilding = statBuildings.filter { it.isStatRelated(Stat.Gold) }
|
||||
.filterBuildable()
|
||||
.minByOrNull { it.cost }
|
||||
if (goldBuilding != null) {
|
||||
val modifier = if (civInfo.stats.statsForNextTurn.gold < 0) 3f else 1.2f
|
||||
addChoice(relativeCostEffectiveness, goldBuilding.name, modifier)
|
||||
@ -338,9 +355,10 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
}
|
||||
|
||||
private fun addProductionBuildingChoice() {
|
||||
val productionBuilding = nonWonders
|
||||
.filter { it.isStatRelated(Stat.Production) && Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.isBuildable().minByOrNull { it.cost }
|
||||
val productionBuilding = statBuildings
|
||||
.filter { it.isStatRelated(Stat.Production) }
|
||||
.filterBuildable()
|
||||
.minByOrNull { it.cost }
|
||||
if (productionBuilding != null) {
|
||||
addChoice(relativeCostEffectiveness, productionBuilding.name, 1.5f)
|
||||
}
|
||||
@ -353,7 +371,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
(it.isStatRelated(Stat.Food)
|
||||
|| it.hasUnique(UniqueType.CarryOverFood, conditionalState)
|
||||
) && Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}.isBuildable().minByOrNull { it.cost }
|
||||
}.filterBuildable().minByOrNull { it.cost }
|
||||
if (foodBuilding != null) {
|
||||
var modifier = 1f
|
||||
if (cityInfo.population.population < 5) modifier = 1.3f
|
||||
|
@ -89,7 +89,7 @@ object NextTurnAutomation {
|
||||
chooseReligiousBeliefs(civInfo)
|
||||
}
|
||||
|
||||
reassignWorkedTiles(civInfo) // second most expensive
|
||||
automateCities(civInfo) // second most expensive
|
||||
trainSettler(civInfo)
|
||||
tryVoteForDiplomaticVictory(civInfo)
|
||||
}
|
||||
@ -892,7 +892,7 @@ object NextTurnAutomation {
|
||||
for (city in civInfo.cities) UnitAutomation.tryBombardEnemy(city)
|
||||
}
|
||||
|
||||
private fun reassignWorkedTiles(civInfo: Civilization) {
|
||||
private fun automateCities(civInfo: Civilization) {
|
||||
for (city in civInfo.cities) {
|
||||
if (city.isPuppet && city.population.population > 9
|
||||
&& !city.isInResistance()) {
|
||||
@ -901,9 +901,13 @@ object NextTurnAutomation {
|
||||
|
||||
city.reassignAllPopulation()
|
||||
|
||||
if (city.health < city.getMaxHealth()) {
|
||||
Automation.tryTrainMilitaryUnit(city) // need defenses if city is under attack
|
||||
if (city.cityConstructions.constructionQueue.isNotEmpty())
|
||||
continue // found a unit to build so move on
|
||||
}
|
||||
|
||||
city.cityConstructions.chooseNextConstruction()
|
||||
if (city.health < city.getMaxHealth())
|
||||
Automation.tryTrainMilitaryUnit(city) // override previous decision if city is under attack
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,8 +476,8 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
if (baseBuilding.replaces != null)
|
||||
return getEquivalentBuilding(baseBuilding.replaces!!)
|
||||
|
||||
for (building in gameInfo.ruleSet.buildings.values)
|
||||
if (building.replaces == baseBuilding.name && building.uniqueTo == civName)
|
||||
for (building in cache.uniqueBuildings)
|
||||
if (building.replaces == baseBuilding.name)
|
||||
return building
|
||||
return baseBuilding
|
||||
}
|
||||
@ -492,8 +492,8 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
if (baseUnit.replaces != null)
|
||||
return getEquivalentUnit(baseUnit.replaces!!) // Equivalent of unique unit is the equivalent of the replaced unit
|
||||
|
||||
for (unit in gameInfo.ruleSet.units.values)
|
||||
if (unit.replaces == baseUnit.name && unit.uniqueTo == civName)
|
||||
for (unit in cache.uniqueUnits)
|
||||
if (unit.replaces == baseUnit.name)
|
||||
return unit
|
||||
return baseUnit
|
||||
}
|
||||
|
@ -9,12 +9,14 @@ import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.civilization.Proximity
|
||||
import com.unciv.logic.map.MapShape
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueTriggerActivation
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
|
||||
/** CivInfo class was getting too crowded */
|
||||
class CivInfoTransientCache(val civInfo: Civilization) {
|
||||
@ -25,6 +27,13 @@ class CivInfoTransientCache(val civInfo: Civilization) {
|
||||
@Transient
|
||||
val lastEraResourceUsedForUnit = java.util.HashMap<String, Int>()
|
||||
|
||||
/** Easy way to look up a Civilization's unique units and buildings */
|
||||
@Transient
|
||||
val uniqueUnits = hashSetOf<BaseUnit>()
|
||||
|
||||
@Transient
|
||||
val uniqueBuildings = hashSetOf<Building>()
|
||||
|
||||
fun setTransients(){
|
||||
val ruleset = civInfo.gameInfo.ruleSet
|
||||
for (resource in ruleset.tileResources.values.asSequence().filter { it.resourceType == ResourceType.Strategic }.map { it.name }) {
|
||||
@ -39,6 +48,18 @@ class CivInfoTransientCache(val civInfo: Civilization) {
|
||||
if (lastEraForUnit != null)
|
||||
lastEraResourceUsedForUnit[resource] = lastEraForUnit
|
||||
}
|
||||
|
||||
for (building in ruleset.buildings.values) {
|
||||
if (building.uniqueTo == civInfo.civName) {
|
||||
uniqueBuildings.add(building)
|
||||
}
|
||||
}
|
||||
|
||||
for (unit in ruleset.units.values) {
|
||||
if (unit.uniqueTo == civInfo.civName) {
|
||||
uniqueUnits.add(unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSightAndResources() {
|
||||
|
@ -577,7 +577,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
||||
if (uniqueTo != null && uniqueTo != civInfo.civName)
|
||||
yield(RejectionReasonType.UniqueToOtherNation.toInstance("Unique to $uniqueTo"))
|
||||
|
||||
if (civInfo.gameInfo.ruleSet.buildings.values.any { it.uniqueTo == civInfo.civName && it.replaces == name })
|
||||
if (civInfo.cache.uniqueBuildings.any { it.replaces == name })
|
||||
yield(RejectionReasonType.ReplacedByOurUnique.toInstance())
|
||||
|
||||
if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!))
|
||||
|
@ -147,7 +147,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
|
||||
if (uniqueTo != null && uniqueTo != civInfo.civName)
|
||||
yield(RejectionReasonType.UniqueToOtherNation.toInstance("Unique to $uniqueTo"))
|
||||
if (ruleSet.units.values.any { it.uniqueTo == civInfo.civName && it.replaces == name })
|
||||
if (civInfo.cache.uniqueUnits.any { it.replaces == name })
|
||||
yield(RejectionReasonType.ReplacedByOurUnique.toInstance("Our unique unit replaces this"))
|
||||
|
||||
if (!civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled && isNuclearWeapon())
|
||||
|
Loading…
Reference in New Issue
Block a user