Fix consuming resources not being affected by conditionals (#10270)

* Fix consuming resources not being affected by condtionals

* Add requirements from buildings back in

* one too many parenthesis

* Whoops, forgot a bracket

* Fix Iconstruction

* Accidental font regression

* Address suggested changes
This commit is contained in:
SeventhM 2023-10-13 01:37:57 -07:00 committed by GitHub
parent 249241247b
commit 3eff519264
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 120 additions and 83 deletions

View File

@ -13,6 +13,7 @@ import com.unciv.models.ruleset.Victory
import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.tile.TileImprovement
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat
@ -247,7 +248,7 @@ object Automation {
construction: INonPerpetualConstruction
): Boolean {
return allowCreateImprovementBuildings(civInfo, city, construction)
&& allowSpendingResource(civInfo, construction)
&& allowSpendingResource(civInfo, construction, city)
}
@Suppress("MemberVisibilityCanBePrivate")
@ -268,7 +269,7 @@ object Automation {
/** Determines whether the AI should be willing to spend strategic resources to build
* [construction] for [civInfo], assumes that we are actually able to do so. */
fun allowSpendingResource(civInfo: Civilization, construction: INonPerpetualConstruction): Boolean {
fun allowSpendingResource(civInfo: Civilization, construction: INonPerpetualConstruction, cityInfo: City? = null): Boolean {
// City states do whatever they want
if (civInfo.isCityState())
return true
@ -277,7 +278,9 @@ object Automation {
if (construction.name in civInfo.gameInfo.spaceResources)
return true
val requiredResources = construction.getResourceRequirementsPerTurn()
val requiredResources = if (construction is BaseUnit)
construction.getResourceRequirementsPerTurn(StateForConditionals(civInfo))
else construction.getResourceRequirementsPerTurn(StateForConditionals(civInfo, cityInfo))
// Does it even require any resources?
if (requiredResources.isEmpty())
return true
@ -295,9 +298,11 @@ object Automation {
for (city in civInfo.cities) {
val otherConstruction = city.cityConstructions.getCurrentConstruction()
if (otherConstruction is Building)
futureForBuildings += otherConstruction.getResourceRequirementsPerTurn()[resource]
futureForBuildings += otherConstruction.getResourceRequirementsPerTurn(
StateForConditionals(civInfo, city))[resource]
else
futureForUnits += otherConstruction.getResourceRequirementsPerTurn()[resource]
futureForUnits += otherConstruction.getResourceRequirementsPerTurn(
StateForConditionals(civInfo))[resource]
}
// Make sure we have some for space

View File

@ -480,7 +480,7 @@ object NextTurnAutomation {
continue
val unitToDisband = civInfo.units.getCivUnits()
.filter { it.baseUnit.requiresResource(resource) }
.filter { it.requiresResource(resource) }
.minByOrNull { it.getForceEvaluation() }
unitToDisband?.disband()
@ -489,7 +489,7 @@ object NextTurnAutomation {
continue
val buildingToSell = civInfo.gameInfo.ruleset.buildings.values.filter {
city.cityConstructions.isBuilt(it.name)
&& it.requiresResource(resource)
&& it.requiresResource(resource, StateForConditionals(civInfo, city))
&& it.isSellable()
&& !civInfo.civConstructions.hasFreeBuilding(city, it) }
.randomOrNull()

View File

@ -14,6 +14,7 @@ import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsPillage
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade
@ -130,7 +131,7 @@ object UnitAutomation {
val upgradedUnit = unit.upgrade.getUnitToUpgradeTo()
if (!upgradedUnit.isBuildable(unit.civ)) return false // for resource reasons, usually
if (upgradedUnit.getResourceRequirementsPerTurn().keys.any { !unit.baseUnit.requiresResource(it) }) {
if (upgradedUnit.getResourceRequirementsPerTurn(StateForConditionals(unit.civ, unit = unit)).keys.any { !unit.requiresResource(it) }) {
// The upgrade requires new resource types, so check if we are willing to invest them
if (!Automation.allowSpendingResource(unit.civ, upgradedUnit)) return false
}

View File

@ -120,7 +120,7 @@ object BattleDamage {
private fun addResourceLackingMalus(combatant: MapUnitCombatant, modifiers: Counter<String>) {
val civInfo = combatant.getCivInfo()
val civResources = civInfo.getCivResourcesByName()
for (resource in combatant.unit.baseUnit.getResourceRequirementsPerTurn().keys)
for (resource in combatant.unit.getResourceRequirementsPerTurn().keys)
if (civResources[resource]!! < 0 && !civInfo.isBarbarian())
modifiers["Missing resource"] = BattleConstants.MISSING_RESOURCES_MALUS
}

View File

@ -204,7 +204,7 @@ object Nuke {
var damageModifierFromMissingResource = 1f
val civResources = attacker.getCivInfo().getCivResourcesByName()
for (resource in attacker.unit.baseUnit.getResourceRequirementsPerTurn().keys) {
for (resource in attacker.unit.getResourceRequirementsPerTurn().keys) {
if (civResources[resource]!! < 0 && !attacker.getCivInfo().isBarbarian())
damageModifierFromMissingResource *= 0.5f // I could not find a source for this number, but this felt about right
// - Original Civ5 does *not* reduce damage from missing resource, from source inspection

View File

@ -78,7 +78,7 @@ object CityResources {
for (building in city.cityConstructions.getBuiltBuildings()) {
// Free buildings cost no resources
if (building.name in freeBuildings) continue
cityResources.subtractResourceRequirements(building.getResourceRequirementsPerTurn(), city.getRuleset(), "Buildings")
cityResources.subtractResourceRequirements(building.getResourceRequirementsPerTurn(StateForConditionals(city.civ, city)), city.getRuleset(), "Buildings")
}
}

View File

@ -87,7 +87,7 @@ class UnitManager(val civInfo:Civilization) {
if (unique.conditionals.any { it.isOfType(UniqueType.TriggerUponGainingUnit) &&
unit.matchesFilter(unique.params[0]) })
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo, triggerNotificationText = triggerNotificationText)
if (unit.baseUnit.getResourceRequirementsPerTurn().isNotEmpty())
if (unit.getResourceRequirementsPerTurn().isNotEmpty())
civInfo.cache.updateCivResources()
for (unique in civInfo.getMatchingUniques(UniqueType.LandUnitsCrossTerrainAfterUnitGained)) {
@ -126,7 +126,7 @@ class UnitManager(val civInfo:Civilization) {
// Not relevant when updating Tile transients, since some info of the civ itself isn't yet available,
// and in any case it'll be updated once civ info transients are
civInfo.updateStatsForNextTurn() // unit upkeep
if (mapUnit.baseUnit.getResourceRequirementsPerTurn().isNotEmpty())
if (mapUnit.getResourceRequirementsPerTurn().isNotEmpty())
civInfo.cache.updateCivResources()
}
}
@ -139,7 +139,7 @@ class UnitManager(val civInfo:Civilization) {
nextPotentiallyDueAt = 0
civInfo.updateStatsForNextTurn() // unit upkeep
if (mapUnit.baseUnit.getResourceRequirementsPerTurn().isNotEmpty())
if (mapUnit.getResourceRequirementsPerTurn().isNotEmpty())
civInfo.cache.updateCivResources()
}

View File

@ -44,8 +44,8 @@ class CivInfoTransientCache(val civInfo: Civilization) {
val ruleset = civInfo.gameInfo.ruleset
for (resource in ruleset.tileResources.values.asSequence().filter { it.resourceType == ResourceType.Strategic }.map { it.name }) {
val applicableBuildings = ruleset.buildings.values.filter { it.requiresResource(resource) && civInfo.getEquivalentBuilding(it) == it }
val applicableUnits = ruleset.units.values.filter { it.requiresResource(resource) && civInfo.getEquivalentUnit(it) == it }
val applicableBuildings = ruleset.buildings.values.filter { it.requiresResource(resource, StateForConditionals.IgnoreConditionals) && civInfo.getEquivalentBuilding(it) == it }
val applicableUnits = ruleset.units.values.filter { it.requiresResource(resource, StateForConditionals.IgnoreConditionals) && civInfo.getEquivalentUnit(it) == it }
val lastEraForBuilding = applicableBuildings.maxOfOrNull { ruleset.eras[ruleset.technologies[it.requiredTech]?.era()]?.eraNumber ?: 0 }
val lastEraForUnit = applicableUnits.maxOfOrNull { ruleset.eras[ruleset.technologies[it.requiredTech]?.era()]?.eraNumber ?: 0 }
@ -316,7 +316,7 @@ class CivInfoTransientCache(val civInfo: Civilization) {
for (unit in civInfo.units.getCivUnits())
newDetailedCivResources.subtractResourceRequirements(
unit.baseUnit.getResourceRequirementsPerTurn(), civInfo.gameInfo.ruleset, "Units")
unit.getResourceRequirementsPerTurn(), civInfo.gameInfo.ruleset, "Units")
newDetailedCivResources.removeAll { it.resource.hasUnique(UniqueType.CityResource) }

View File

@ -587,7 +587,7 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization {
// And update civ stats, since the new unit changes both unit upkeep and resource consumption
civInfo.updateStatsForNextTurn()
if (unit.baseUnit.getResourceRequirementsPerTurn().isNotEmpty())
if (unit.getResourceRequirementsPerTurn().isNotEmpty())
civInfo.cache.updateCivResources()
return unit

View File

@ -12,6 +12,7 @@ import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.map.mapunit.movement.UnitMovement
import com.unciv.logic.map.tile.Tile
import com.unciv.models.Counter
import com.unciv.models.UnitActionType
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.tile.TileImprovement
@ -259,6 +260,23 @@ class MapUnit : IsPartOfGameInfoSerialization {
newUnit.updateVisibleTiles()
}
/** Gets *per turn* resource requirements - does not include immediate costs for stockpiled resources.
* StateForConditionals is assumed to regarding this mapUnit*/
fun getResourceRequirementsPerTurn(): Counter<String> {
val resourceRequirements = Counter<String>()
if (baseUnit.requiredResource != null) resourceRequirements[baseUnit.requiredResource!!] = 1
for (unique in getMatchingUniques(UniqueType.ConsumesResources, StateForConditionals(civ, unit = this)))
resourceRequirements[unique.params[1]] += unique.params[0].toInt()
return resourceRequirements
}
fun requiresResource(resource: String): Boolean {
if (getResourceRequirementsPerTurn().contains(resource)) return true
for (unique in getMatchingUniques(UniqueType.CostsResources, StateForConditionals(civ, unit = this))) {
if (unique.params[1] == resource) return true
}
return false
}
fun getMaxMovement(): Int {
var movement =

View File

@ -62,7 +62,7 @@ class UnitUpgradeManager(val unit:MapUnit) {
): Boolean {
if (unit.name == unitToUpgradeTo.name) return false
val rejectionReasons = unitToUpgradeTo.getRejectionReasons(unit.civ, additionalResources = unit.baseUnit.getResourceRequirementsPerTurn())
val rejectionReasons = unitToUpgradeTo.getRejectionReasons(unit.civ, additionalResources = unit.getResourceRequirementsPerTurn())
var relevantRejectionReasons = rejectionReasons.filterNot { it.type == RejectionReasonType.Unbuildable }
if (ignoreRequirements)

View File

@ -11,6 +11,7 @@ import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.ModOptionsConstants
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.ui.components.extensions.toPercent
import com.unciv.ui.screens.victoryscreen.RankingType
@ -127,9 +128,11 @@ class TradeEvaluation {
val amountToBuyInOffer = min(amountWillingToBuy, offer.amount)
val canUseForBuildings = civInfo.cities
.any { city -> city.cityConstructions.getBuildableBuildings().any { it.getResourceRequirementsPerTurn().containsKey(offer.name) } }
.any { city -> city.cityConstructions.getBuildableBuildings().any {
it.getResourceRequirementsPerTurn(StateForConditionals(civInfo, city)).containsKey(offer.name) } }
val canUseForUnits = civInfo.cities
.any { city -> city.cityConstructions.getConstructableUnits().any { it.getResourceRequirementsPerTurn().containsKey(offer.name) } }
.any { city -> city.cityConstructions.getConstructableUnits().any {
it.getResourceRequirementsPerTurn(StateForConditionals(civInfo)).containsKey(offer.name) } }
if (!canUseForBuildings && !canUseForUnits) return 0
return 50 * amountToBuyInOffer
@ -229,7 +232,7 @@ class TradeEvaluation {
if (!civInfo.isAtWar()) return 50 * offer.amount
val canUseForUnits = civInfo.gameInfo.ruleset.units.values
.any { it.getResourceRequirementsPerTurn().containsKey(offer.name)
.any { it.getResourceRequirementsPerTurn(StateForConditionals(civInfo)).containsKey(offer.name)
&& it.isBuildable(civInfo) }
if (!canUseForUnits) return 50 * offer.amount

View File

@ -86,7 +86,6 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
if (!tileBonusHashmap.containsKey(stats)) tileBonusHashmap[stats] = ArrayList()
tileBonusHashmap[stats]!!.add(unique.params[1])
}
unique.isOfType(UniqueType.ConsumesResources) -> Unit // skip these,
else -> yield(unique.text)
}
for ((key, value) in tileBonusHashmap)
@ -116,7 +115,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
if (isWonder) translatedLines += "Wonder".tr()
if (isNationalWonder) translatedLines += "National Wonder".tr()
if (!isFree) {
for ((resourceName, amount) in getResourceRequirementsPerTurn()) {
for ((resourceName, amount) in getResourceRequirementsPerTurn(StateForConditionals(city.civ, city))) {
val available = city.getResourceAmount(resourceName)
val resource = city.getRuleset().tileResources[resourceName] ?: continue
val consumesString = resourceName.getConsumesAmountString(amount, resource.isStockpiled())
@ -247,15 +246,12 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
textList += FormattedLine("Requires [$requiredBuilding] to be built in the city",
link="Building/$requiredBuilding")
val resourceRequirements = getResourceRequirementsPerTurn()
if (resourceRequirements.isNotEmpty()) {
if (requiredResource != null) {
textList += FormattedLine()
for ((resourceName, amount) in resourceRequirements) {
val resource = ruleset.tileResources[resourceName] ?: continue
textList += FormattedLine(
resourceName.getConsumesAmountString(amount, resource.isStockpiled()),
link="Resources/$resourceName", color="#F42" )
}
val resource = ruleset.tileResources[requiredResource]
textList += FormattedLine(
requiredResource!!.getConsumesAmountString(1, resource!!.isStockpiled()),
link="Resources/$requiredResource", color="#F42" )
}
val stats = cloneStats()
@ -269,7 +265,10 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
} else if (uniques.isNotEmpty()) {
for (unique in uniqueObjects) {
if (unique.hasFlag(UniqueFlag.HiddenToUsers)) continue
if (unique.type == UniqueType.ConsumesResources) continue // already shown from getResourceRequirements
if (unique.type == UniqueType.ConsumesResources) {
textList += FormattedLine(unique.text, link = "Resources/${unique.params[1]}", color = "#F42")
continue
}
textList += FormattedLine(unique)
}
}
@ -620,7 +619,9 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
yield(RejectionReasonType.RequiresBuildingInThisCity.toInstance("Requires a [${civ.getEquivalentBuilding(requiredBuilding!!)}] in this city"))
}
for ((resourceName, requiredAmount) in getResourceRequirementsPerTurn()) {
for ((resourceName, requiredAmount) in getResourceRequirementsPerTurn(
StateForConditionals(cityConstructions.city.civ, cityConstructions.city))
) {
val availableAmount = cityConstructions.city.getResourceAmount(resourceName)
if (availableAmount < requiredAmount) {
yield(RejectionReasonType.ConsumesResources.toInstance(resourceName.getNeedMoreAmountString(requiredAmount - availableAmount)))
@ -704,20 +705,17 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
fun isSellable() = !isAnyWonder() && !hasUnique(UniqueType.Unsellable)
override fun getResourceRequirementsPerTurn(): Counter<String> = resourceRequirementsInternal
private val resourceRequirementsInternal: Counter<String> by lazy {
override fun getResourceRequirementsPerTurn(stateForConditionals: StateForConditionals?): Counter<String> {
val resourceRequirements = Counter<String>()
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
for (unique in uniqueObjects)
if (unique.isOfType(UniqueType.ConsumesResources))
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
resourceRequirements
for (unique in getMatchingUniques(UniqueType.ConsumesResources, stateForConditionals))
resourceRequirements[unique.params[1]] += unique.params[0].toInt()
return resourceRequirements
}
override fun requiresResource(resource: String): Boolean {
if (resourceRequirementsInternal.contains(resource)) return true
for (unique in getMatchingUniques(UniqueType.CostsResources)) {
override fun requiresResource(resource: String, stateForConditionals: StateForConditionals?): Boolean {
if (getResourceRequirementsPerTurn(stateForConditionals).contains(resource)) return true
for (unique in getMatchingUniques(UniqueType.CostsResources, stateForConditionals)) {
if (unique.params[1] == resource) return true
}
return false

View File

@ -18,9 +18,10 @@ import kotlin.math.roundToInt
interface IConstruction : INamed {
fun isBuildable(cityConstructions: CityConstructions): Boolean
fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean
/** Gets *per turn* resource requirements - does not include immediate costs for stockpiled resources */
fun getResourceRequirementsPerTurn(): Counter<String>
fun requiresResource(resource: String): Boolean
/** Gets *per turn* resource requirements - does not include immediate costs for stockpiled resources.
* Uses [stateForConditionals] to determine which civ or city this is built for*/
fun getResourceRequirementsPerTurn(stateForConditionals: StateForConditionals? = null): Counter<String>
fun requiresResource(resource: String, stateForConditionals: StateForConditionals? = null): Boolean
/** We can't call this getMatchingUniques because then it would conflict with IHasUniques */
fun getMatchingUniquesNotConflicting(uniqueType: UniqueType) = sequenceOf<Unique>()
}
@ -206,7 +207,6 @@ enum class RejectionReasonType(val shouldShow: Boolean, val errorMessage: String
}
}
open class PerpetualConstruction(override var name: String, val description: String) :
IConstruction {
@ -232,9 +232,9 @@ open class PerpetualConstruction(override var name: String, val description: Str
override fun isBuildable(cityConstructions: CityConstructions): Boolean =
throw Exception("Impossible!")
override fun getResourceRequirementsPerTurn() = Counter.ZERO
override fun getResourceRequirementsPerTurn(stateForConditionals: StateForConditionals?) = Counter.ZERO
override fun requiresResource(resource: String) = false
override fun requiresResource(resource: String, stateForConditionals: StateForConditionals?) = false
}

View File

@ -5,6 +5,7 @@ import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.Belief
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetStatsObject
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueFlag
import com.unciv.models.ruleset.unique.UniqueTarget
import com.unciv.models.ruleset.unique.UniqueType
@ -101,7 +102,8 @@ class TileResource : RulesetStatsObject() {
}
}
val buildingsThatConsumeThis = ruleset.buildings.values.filter { it.getResourceRequirementsPerTurn().containsKey(name) }
val buildingsThatConsumeThis = ruleset.buildings.values.filter { it.getResourceRequirementsPerTurn(
StateForConditionals.IgnoreConditionals).containsKey(name) }
if (buildingsThatConsumeThis.isNotEmpty()) {
textList += FormattedLine()
textList += FormattedLine("{Buildings that consume this resource}:")
@ -110,7 +112,8 @@ class TileResource : RulesetStatsObject() {
}
}
val unitsThatConsumeThis = ruleset.units.values.filter { it.getResourceRequirementsPerTurn().containsKey(name) }
val unitsThatConsumeThis = ruleset.units.values.filter { it.getResourceRequirementsPerTurn(
StateForConditionals.IgnoreConditionals).containsKey(name) }
if (unitsThatConsumeThis.isNotEmpty()) {
textList += FormattedLine()
textList += FormattedLine("{Units that consume this resource}: ")

View File

@ -40,7 +40,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
val type by lazy { ruleset.unitTypes[unitType]!! }
override var requiredTech: String? = null
private var requiredResource: String? = null
var requiredResource: String? = null
override fun getUniqueTarget() = UniqueTarget.Unit
@ -182,7 +182,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
if (!civ.isBarbarian()) { // Barbarians don't need resources
val civResources = Counter(civ.getCivResourcesByName()) + additionalResources
for ((resource, requiredAmount) in getResourceRequirementsPerTurn()) {
for ((resource, requiredAmount) in getResourceRequirementsPerTurn(StateForConditionals(civ))) {
val availableAmount = civResources[resource]
if (availableAmount < requiredAmount) {
val message = resource.getNeedMoreAmountString(requiredAmount - availableAmount)
@ -310,17 +310,21 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
fun movesLikeAirUnits() = type.getMovementType() == UnitMovementType.Air
/** Returns resource requirements from both uniques and requiredResource field */
override fun getResourceRequirementsPerTurn(): Counter<String> = resourceRequirementsInternal
private val resourceRequirementsInternal: Counter<String> by lazy {
override fun getResourceRequirementsPerTurn(stateForConditionals: StateForConditionals?): Counter<String> {
val resourceRequirements = Counter<String>()
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
for (unique in getMatchingUniques(UniqueType.ConsumesResources))
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
resourceRequirements
for (unique in getMatchingUniques(UniqueType.ConsumesResources, stateForConditionals))
resourceRequirements[unique.params[1]] += unique.params[0].toInt()
return resourceRequirements
}
override fun requiresResource(resource: String) = getResourceRequirementsPerTurn().containsKey(resource)
override fun requiresResource(resource: String, stateForConditionals: StateForConditionals?): Boolean {
if (getResourceRequirementsPerTurn(stateForConditionals).contains(resource)) return true
for (unique in getMatchingUniques(UniqueType.CostsResources, stateForConditionals)) {
if (unique.params[1] == resource) return true
}
return false
}
fun isRanged() = rangedStrength > 0
fun isMelee() = !isRanged() && strength > 0

View File

@ -433,7 +433,7 @@ class RulesetValidator(val ruleset: Ruleset) {
for (specialistName in building.specialistSlots.keys)
if (!ruleset.specialists.containsKey(specialistName))
lines += "${building.name} provides specialist $specialistName which does not exist!"
for (resource in building.getResourceRequirementsPerTurn().keys)
for (resource in building.getResourceRequirementsPerTurn(StateForConditionals.IgnoreConditionals).keys)
if (!ruleset.tileResources.containsKey(resource))
lines += "${building.name} requires resource $resource which does not exist!"
if (building.replaces != null && !ruleset.buildings.containsKey(building.replaces!!))
@ -649,7 +649,7 @@ class RulesetValidator(val ruleset: Ruleset) {
)
}
for (resource in unit.getResourceRequirementsPerTurn().keys)
for (resource in unit.getResourceRequirementsPerTurn(StateForConditionals.IgnoreConditionals).keys)
if (!ruleset.tileResources.containsKey(resource))
lines += "${unit.name} requires resource $resource which does not exist!"
if (unit.replaces != null && !ruleset.units.containsKey(unit.replaces!!))

View File

@ -7,6 +7,7 @@ import com.unciv.logic.city.City
import com.unciv.models.metadata.GameSettings
import com.unciv.models.ruleset.IRulesetObject
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.ruleset.unique.UniqueFlag
import com.unciv.models.ruleset.unique.UniqueType
@ -43,7 +44,7 @@ object BaseUnitDescriptions {
fun getDescription(baseUnit: BaseUnit, city: City): String {
val lines = mutableListOf<String>()
val availableResources = city.civ.getCivResourcesByName()
for ((resourceName, amount) in baseUnit.getResourceRequirementsPerTurn()) {
for ((resourceName, amount) in baseUnit.getResourceRequirementsPerTurn(StateForConditionals(city.civ))) {
val available = availableResources[resourceName] ?: 0
val resource = baseUnit.ruleset.tileResources[resourceName] ?: continue
val consumesString = resourceName.getConsumesAmountString(amount, resource.isStockpiled())
@ -60,7 +61,6 @@ object BaseUnitDescriptions {
if (baseUnit.replacementTextForUniques != "") lines += baseUnit.replacementTextForUniques
else for (unique in baseUnit.uniqueObjects.filterNot {
it.type == UniqueType.Unbuildable
|| it.type == UniqueType.ConsumesResources // already shown from getResourceRequirements
|| it.type?.flags?.contains(UniqueFlag.HiddenToUsers) == true
})
lines += unique.text.tr()
@ -110,21 +110,20 @@ object BaseUnitDescriptions {
textList += FormattedLine()
for (unique in baseUnit.uniqueObjects.sortedBy { it.text }) {
if (unique.hasFlag(UniqueFlag.HiddenToUsers)) continue
if (unique.type == UniqueType.ConsumesResources) continue // already shown from getResourceRequirements
if (unique.type == UniqueType.ConsumesResources) {
textList += FormattedLine(unique.text, link = "Resources/${unique.params[1]}", color = "#F42")
continue
}
textList += FormattedLine(unique)
}
}
val resourceRequirements = baseUnit.getResourceRequirementsPerTurn()
if (resourceRequirements.isNotEmpty()) {
if (baseUnit.requiredResource != null) {
textList += FormattedLine()
for ((resourceName, amount) in resourceRequirements) {
val resource = ruleset.tileResources[resourceName] ?: continue
textList += FormattedLine(
resourceName.getConsumesAmountString(amount, resource.isStockpiled()),
link = "Resource/$resource", color = "#F42"
)
}
val resource = ruleset.tileResources[baseUnit.requiredResource]
textList += FormattedLine(
baseUnit.requiredResource!!.getConsumesAmountString(1, resource!!.isStockpiled()),
link="Resources/${baseUnit.requiredResource}", color="#F42")
}
if (baseUnit.uniqueTo != null) {
@ -293,8 +292,8 @@ object BaseUnitDescriptions {
if (betterUnit.movement != originalUnit.movement)
yield("${Fonts.movement} {[${betterUnit.movement}] vs [${originalUnit.movement}]}" to null)
for (resource in originalUnit.getResourceRequirementsPerTurn().keys)
if (!betterUnit.getResourceRequirementsPerTurn().containsKey(resource)) {
for (resource in originalUnit.getResourceRequirementsPerTurn(StateForConditionals.IgnoreConditionals).keys)
if (!betterUnit.getResourceRequirementsPerTurn(StateForConditionals.IgnoreConditionals).containsKey(resource)) {
yield("[$resource] not required" to "Resource/$resource")
}
// We return the unique text directly, so Nation.getUniqueUnitsText will not use the

View File

@ -19,6 +19,7 @@ import com.unciv.models.ruleset.INonPerpetualConstruction
import com.unciv.models.ruleset.PerpetualConstruction
import com.unciv.models.ruleset.RejectionReason
import com.unciv.models.ruleset.RejectionReasonType
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat
@ -210,7 +211,9 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
val useStoredProduction = entry is Building || !cityConstructions.isBeingConstructedOrEnqueued(entry.name)
val buttonText = cityConstructions.getTurnsToConstructionString(entry, useStoredProduction).trim()
val resourcesRequired = entry.getResourceRequirementsPerTurn()
val resourcesRequired = if (entry is BaseUnit)
entry.getResourceRequirementsPerTurn(StateForConditionals(city.civ))
else entry.getResourceRequirementsPerTurn(StateForConditionals(city.civ, city))
val mostImportantRejection =
entry.getRejectionReasons(cityConstructions)
.filter { it.isImportantRejection() }
@ -325,7 +328,9 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
if (constructionName in PerpetualConstruction.perpetualConstructionsMap) "\n"
else cityConstructions.getTurnsToConstructionString(construction, isFirstConstructionOfItsKind)
val constructionResource = construction.getResourceRequirementsPerTurn()
val constructionResource = if (construction is BaseUnit)
construction.getResourceRequirementsPerTurn(StateForConditionals(city.civ, city))
else construction.getResourceRequirementsPerTurn(StateForConditionals(city.civ))
for ((resourceName, amount) in constructionResource) {
val resource = cityConstructions.city.getRuleset().tileResources[resourceName] ?: continue
text += "\n" + resourceName.getConsumesAmountString(amount, resource.isStockpiled()).tr()

View File

@ -298,9 +298,9 @@ object UnitActionsFromUniques {
// Check _new_ resource requirements
// Using Counter to aggregate is a bit exaggerated, but - respect the mad modder.
val resourceRequirementsDelta = Counter<String>()
for ((resource, amount) in unit.baseUnit().getResourceRequirementsPerTurn())
for ((resource, amount) in unit.getResourceRequirementsPerTurn())
resourceRequirementsDelta.add(resource, -amount)
for ((resource, amount) in unitToTransformTo.getResourceRequirementsPerTurn())
for ((resource, amount) in unitToTransformTo.getResourceRequirementsPerTurn(StateForConditionals(unit.civ, unit = unit)))
resourceRequirementsDelta.add(resource, amount)
val newResourceRequirementsString = resourceRequirementsDelta.entries
.filter { it.value > 0 }

View File

@ -4,6 +4,7 @@ import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.models.Counter
import com.unciv.models.UnitAction
import com.unciv.models.UpgradeUnitAction
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.translations.tr
@ -43,9 +44,9 @@ object UnitActionsUpgrade {
// Check _new_ resource requirements (display only - yes even for free or special upgrades)
// Using Counter to aggregate is a bit exaggerated, but - respect the mad modder.
val resourceRequirementsDelta = Counter<String>()
for ((resource, amount) in unit.baseUnit().getResourceRequirementsPerTurn())
for ((resource, amount) in unit.getResourceRequirementsPerTurn())
resourceRequirementsDelta.add(resource, -amount)
for ((resource, amount) in upgradedUnit.getResourceRequirementsPerTurn())
for ((resource, amount) in upgradedUnit.getResourceRequirementsPerTurn(StateForConditionals(unit.civ, unit = unit)))
resourceRequirementsDelta.add(resource, amount)
for ((resource, _) in resourceRequirementsDelta.filter { it.value < 0 }) // filter copies, so no CCM
resourceRequirementsDelta[resource] = 0