Added "Consumes [amount] [resource]" unique to units

This commit is contained in:
Yair Morgenstern
2021-02-06 21:27:41 +02:00
parent 3d683c767a
commit 4104f72729
9 changed files with 99 additions and 88 deletions

View File

@ -65,7 +65,7 @@ object Automation {
// 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.requiredResource != "Aluminum" }
militaryUnits.filter { !it.getResourceRequirements().containsKey("Aluminum") }
val findWaterConnectedCitiesAndEnemies = BFS(city.getCenterTile()) { it.isWater || it.isCityCenter() }
findWaterConnectedCitiesAndEnemies.stepToEnd()

View File

@ -20,7 +20,7 @@ object BattleDamage {
const val BONUS_VS_UNIT_TYPE = """(Bonus|Penalty) vs (.*) (\d*)%"""
// This should be deprecated and converted to "+[]% Strength vs []", "-[]% Strength vs []"
private fun getBattleDamageModifiersOfUnit(unit:MapUnit): MutableList<BattleDamageModifier> {
private fun getBattleDamageModifiersOfUnit(unit: MapUnit): MutableList<BattleDamageModifier> {
val modifiers = mutableListOf<BattleDamageModifier>()
for (ability in unit.getUniques()) {
// This beut allows us to have generic unit uniques: "Bonus vs City 75%", "Penatly vs Mounted 25%" etc.
@ -39,7 +39,7 @@ object BattleDamage {
private fun getGeneralModifiers(combatant: ICombatant, enemy: ICombatant): Counter<String> {
val modifiers = Counter<String>()
fun addToModifiers(BDM:BattleDamageModifier) =
fun addToModifiers(BDM: BattleDamageModifier) =
modifiers.add(BDM.getText(), (BDM.modificationAmount).toInt())
val civInfo = combatant.getCivInfo()
@ -80,11 +80,11 @@ object BattleDamage {
.any { it.civInfo == civInfo && !it.type.isCivilian() && !it.type.isAirUnit() })
modifiers["Discipline"] = 15
val requiredResource = combatant.unit.baseUnit.requiredResource
if (requiredResource != null && civInfo.getCivResourcesByName()[requiredResource]!! < 0
&& !civInfo.isBarbarian()) {
modifiers["Missing resource"] = -25
}
val civResources = civInfo.getCivResourcesByName()
for (resource in combatant.unit.baseUnit.getResourceRequirements().keys)
if (civResources[resource]!! < 0 && !civInfo.isBarbarian())
modifiers["Missing resource"] = -25
val nearbyCivUnits = combatant.unit.getTile().getTilesInDistance(2)
.filter { it.civilianUnit?.civInfo == combatant.unit.civInfo }
@ -94,7 +94,7 @@ object BattleDamage {
modifiers["Great General"] = greatGeneralModifier
}
if(civInfo.goldenAges.isGoldenAge() && civInfo.hasUnique("+10% Strength for all units during Golden Age"))
if (civInfo.goldenAges.isGoldenAge() && civInfo.hasUnique("+10% Strength for all units during Golden Age"))
modifiers["Golden Age"] = 10
if (enemy.getCivInfo().isCityState() && civInfo.hasUnique("+30% Strength when fighting City-State units and cities"))
@ -103,21 +103,21 @@ object BattleDamage {
}
if (enemy.getCivInfo().isBarbarian()) {
modifiers["Difficulty"] = (civInfo.gameInfo.getDifficulty().barbarianBonus*100).toInt()
modifiers["Difficulty"] = (civInfo.gameInfo.getDifficulty().barbarianBonus * 100).toInt()
if (civInfo.hasUnique("+25% bonus vs Barbarians"))
modifiers["vs Barbarians"] = 25
}
return modifiers
}
fun getAttackModifiers(attacker: ICombatant, tileToAttackFrom:TileInfo?, defender: ICombatant): Counter<String> {
fun getAttackModifiers(attacker: ICombatant, tileToAttackFrom: TileInfo?, defender: ICombatant): Counter<String> {
val modifiers = getGeneralModifiers(attacker, defender)
if (attacker is MapUnitCombatant) {
modifiers.add(getTileSpecificModifiers(attacker, defender.getTile()))
for(unique in attacker.unit.getMatchingUniques("+[]% Strength when attacking")) {
for (unique in attacker.unit.getMatchingUniques("+[]% Strength when attacking")) {
modifiers.add("Attacker Bonus", unique.params[0].toInt())
}
@ -173,20 +173,20 @@ object BattleDamage {
modifiers.putAll(getTileSpecificModifiers(defender, tile))
val tileDefenceBonus = tile.getDefensiveBonus()
if ( (!defender.unit.hasUnique("No defensive terrain bonus") && tileDefenceBonus > 0)
|| (!defender.unit.hasUnique("No defensive terrain penalty") && tileDefenceBonus < 0) )
modifiers["Tile"] = (tileDefenceBonus*100).toInt()
if ((!defender.unit.hasUnique("No defensive terrain bonus") && tileDefenceBonus > 0)
|| (!defender.unit.hasUnique("No defensive terrain penalty") && tileDefenceBonus < 0))
modifiers["Tile"] = (tileDefenceBonus * 100).toInt()
if (attacker.isRanged()) {
val defenceVsRanged = 25 * defender.unit.getUniques().count { it.text == "+25% Defence against ranged attacks" }
if (defenceVsRanged > 0) modifiers["defence vs ranged"] = defenceVsRanged
}
for(unique in defender.unit.getMatchingUniques("+[]% Strength when defending")) {
for (unique in defender.unit.getMatchingUniques("+[]% Strength when defending")) {
modifiers.add("Defender Bonus", unique.params[0].toInt())
}
for(unique in defender.unit.getMatchingUniques("+[]% defence in [] tiles")) {
for (unique in defender.unit.getMatchingUniques("+[]% defence in [] tiles")) {
if (tile.matchesUniqueFilter(unique.params[1]))
modifiers["[${unique.params[1]}] defence"] = unique.params[0].toInt()
}
@ -235,7 +235,7 @@ object BattleDamage {
private fun modifiersToMultiplicationBonus(modifiers: Counter<String>): Float {
var finalModifier = 1f
for (modifierValue in modifiers.values) finalModifier *= (1 + modifierValue/100f) // so 25 will result in *= 1.25
for (modifierValue in modifiers.values) finalModifier *= (1 + modifierValue / 100f) // so 25 will result in *= 1.25
return finalModifier
}
@ -252,7 +252,7 @@ object BattleDamage {
* Includes attack modifiers
*/
private fun getAttackingStrength(attacker: ICombatant, tileToAttackFrom: TileInfo?, defender: ICombatant): Float {
val attackModifier = modifiersToMultiplicationBonus(getAttackModifiers(attacker,tileToAttackFrom, defender))
val attackModifier = modifiersToMultiplicationBonus(getAttackModifiers(attacker, tileToAttackFrom, defender))
return attacker.getAttackingStrength() * attackModifier
}
@ -267,24 +267,24 @@ object BattleDamage {
}
fun calculateDamageToAttacker(attacker: ICombatant, tileToAttackFrom: TileInfo?, defender: ICombatant): Int {
if(attacker.isRanged()) return 0
if(defender.getUnitType().isCivilian()) return 0
val ratio = getAttackingStrength(attacker, tileToAttackFrom, defender) / getDefendingStrength(attacker,defender)
if (attacker.isRanged()) return 0
if (defender.getUnitType().isCivilian()) return 0
val ratio = getAttackingStrength(attacker, tileToAttackFrom, defender) / getDefendingStrength(attacker, defender)
return (damageModifier(ratio, true) * getHealthDependantDamageRatio(defender)).roundToInt()
}
fun calculateDamageToDefender(attacker: ICombatant, tileToAttackFrom: TileInfo?, defender: ICombatant): Int {
val ratio = getAttackingStrength(attacker,tileToAttackFrom, defender) / getDefendingStrength(attacker,defender)
return (damageModifier(ratio,false) * getHealthDependantDamageRatio(attacker)).roundToInt()
val ratio = getAttackingStrength(attacker, tileToAttackFrom, defender) / getDefendingStrength(attacker, defender)
return (damageModifier(ratio, false) * getHealthDependantDamageRatio(attacker)).roundToInt()
}
private fun damageModifier(attackerToDefenderRatio: Float, damageToAttacker:Boolean): Float {
private fun damageModifier(attackerToDefenderRatio: Float, damageToAttacker: Boolean): Float {
// https://forums.civfanatics.com/threads/getting-the-combat-damage-math.646582/#post-15468029
val strongerToWeakerRatio = attackerToDefenderRatio.pow(if (attackerToDefenderRatio < 1) -1 else 1)
var ratioModifier = ((((strongerToWeakerRatio + 3)/4).pow(4) +1)/2)
if((damageToAttacker && attackerToDefenderRatio>1) || (!damageToAttacker && attackerToDefenderRatio<1)) // damage ratio from the weaker party is inverted
var ratioModifier = ((((strongerToWeakerRatio + 3) / 4).pow(4) + 1) / 2)
if ((damageToAttacker && attackerToDefenderRatio > 1) || (!damageToAttacker && attackerToDefenderRatio < 1)) // damage ratio from the weaker party is inverted
ratioModifier = ratioModifier.pow(-1)
val randomCenteredAround30 = (24 + 12 * Random().nextFloat())
return randomCenteredAround30 * ratioModifier
return randomCenteredAround30 * ratioModifier
}
}
}

View File

@ -157,9 +157,9 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo) {
}
for (dip in civInfo.diplomacy.values) newDetailedCivResources.add(dip.resourcesFromTrade())
for (resource in civInfo.getCivUnits().mapNotNull { it.baseUnit.requiredResource }
.map { civInfo.gameInfo.ruleSet.tileResources[it]!! })
newDetailedCivResources.add(resource, -1, "Units")
for (unit in civInfo.getCivUnits())
for ((resource, amount) in unit.baseUnit.getResourceRequirements())
newDetailedCivResources.add(civInfo.gameInfo.ruleSet.tileResources[resource]!!, -amount, "Units")
civInfo.detailedCivResources = newDetailedCivResources
}
}

View File

@ -9,9 +9,9 @@ import com.unciv.models.ruleset.tile.ResourceType
import kotlin.math.min
import kotlin.math.sqrt
class TradeEvaluation{
class TradeEvaluation {
fun isTradeValid(trade:Trade, offerer:CivilizationInfo, tradePartner: CivilizationInfo): Boolean {
fun isTradeValid(trade: Trade, offerer: CivilizationInfo, tradePartner: CivilizationInfo): Boolean {
// Edge case time! Guess what happens if you offer a peace agreement to the AI for all their cities except for the capital,
// and then capture their capital THAT SAME TURN? It can agree, leading to the civilization getting instantly destroyed!
@ -28,14 +28,14 @@ class TradeEvaluation{
return true
}
private fun isOfferValid(tradeOffer: TradeOffer, offerer:CivilizationInfo): Boolean {
private fun isOfferValid(tradeOffer: TradeOffer, offerer: CivilizationInfo): Boolean {
fun hasResource(tradeOffer: TradeOffer): Boolean {
val resourcesByName = offerer.getCivResourcesByName()
return resourcesByName.containsKey(tradeOffer.name) && resourcesByName[tradeOffer.name]!! >= 0
}
when(tradeOffer.type){
when (tradeOffer.type) {
TradeType.Gold -> return true // even if they go negative it's okay
TradeType.Gold_Per_Turn -> return true // even if they go negative it's okay
TradeType.Treaty -> return true
@ -73,7 +73,7 @@ class TradeEvaluation{
TradeType.Treaty -> {
return when (offer.name) {
// Since it will be evaluated twice, once when they evaluate our offer and once when they evaluate theirs
Constants.peaceTreaty -> evaluatePeaceCostForThem(civInfo,tradePartner)
Constants.peaceTreaty -> evaluatePeaceCostForThem(civInfo, tradePartner)
Constants.researchAgreement -> evaluateResearchAgreementCostForThem(civInfo, tradePartner)
else -> 1000
}
@ -118,9 +118,9 @@ class TradeEvaluation{
val amountToBuyInOffer = min(amountWillingToBuy, offer.amount)
val canUseForBuildings = civInfo.cities
.any { city -> city.cityConstructions.getBuildableBuildings().any { it.requiredResource == offer.name } }
.any { city -> city.cityConstructions.getBuildableBuildings().any { it.getResourceRequirements().containsKey(offer.name) } }
val canUseForUnits = civInfo.cities
.any { city -> city.cityConstructions.getConstructableUnits().any { it.requiredResource == offer.name } }
.any { city -> city.cityConstructions.getConstructableUnits().any { it.getResourceRequirements().containsKey(offer.name) } }
if (!canUseForBuildings && !canUseForUnits) return 0
return 50 * amountToBuyInOffer
@ -128,11 +128,11 @@ class TradeEvaluation{
TradeType.Technology ->
return (sqrt(civInfo.gameInfo.ruleSet.technologies[offer.name]!!.cost.toDouble())
* civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt()*20
* civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt() * 20
TradeType.Introduction -> return 250
TradeType.WarDeclaration -> {
val civToDeclareWarOn = civInfo.gameInfo.getCivilization(offer.name)
val threatToThem = Automation.threatAssessment(civInfo,civToDeclareWarOn)
val threatToThem = Automation.threatAssessment(civInfo, civToDeclareWarOn)
if (!civInfo.isAtWarWith(civToDeclareWarOn)) return 0 // why should we pay you to go fight someone...?
else when (threatToThem) {
@ -144,15 +144,15 @@ class TradeEvaluation{
}
}
TradeType.City -> {
val city = tradePartner.cities.first { it.id==offer.name }
val city = tradePartner.cities.first { it.id == offer.name }
val stats = city.cityStats.currentCityStats
if(civInfo.getHappiness() + city.cityStats.happinessList.values.sum() < 0)
if (civInfo.getHappiness() + city.cityStats.happinessList.values.sum() < 0)
return 0 // we can't really afford to go into negative happiness because of buying a city
val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food
val sumOfStats = stats.culture + stats.gold + stats.science + stats.production + stats.happiness + stats.food
return sumOfStats.toInt() * 100
}
TradeType.Agreement -> {
if(offer.name==Constants.openBorders) return 100
if (offer.name == Constants.openBorders) return 100
throw Exception("Invalid agreement type!")
}
}
@ -171,16 +171,16 @@ class TradeEvaluation{
}
}
TradeType.Luxury_Resource -> {
return if(civInfo.getCivResourcesByName()[offer.name]!!>1)
return if (civInfo.getCivResourcesByName()[offer.name]!! > 1)
250 // fair price
else 500 // you want to take away our last lux of this type?!
}
TradeType.Strategic_Resource -> {
if(!civInfo.isAtWar()) return 50*offer.amount
if (!civInfo.isAtWar()) return 50 * offer.amount
val canUseForUnits = civInfo.gameInfo.ruleSet.units.values
.any { it.requiredResource==offer.name && it.isBuildable(civInfo) }
if(!canUseForUnits) return 50*offer.amount
.any { it.getResourceRequirements().containsKey(offer.name) && it.isBuildable(civInfo) }
if (!canUseForUnits) return 50 * offer.amount
val amountLeft = civInfo.getCivResourcesByName()[offer.name]!!
@ -193,13 +193,13 @@ class TradeEvaluation{
var totalCost = 0
// I know it's confusing, you're welcome to change to a more understandable way of counting if you can think of one...
for(numberOfResource in (amountLeft-offer.amount+1)..amountLeft){
if(numberOfResource>5) totalCost+=100
else totalCost += (6-numberOfResource) * 100
for (numberOfResource in (amountLeft - offer.amount + 1)..amountLeft) {
if (numberOfResource > 5) totalCost += 100
else totalCost += (6 - numberOfResource) * 100
}
return totalCost
}
TradeType.Technology -> return sqrt(civInfo.gameInfo.ruleSet.technologies[offer.name]!!.cost.toDouble()).toInt()*20
TradeType.Technology -> return sqrt(civInfo.gameInfo.ruleSet.technologies[offer.name]!!.cost.toDouble()).toInt() * 20
TradeType.Introduction -> return 250
TradeType.WarDeclaration -> {
val civToDeclareWarOn = civInfo.gameInfo.getCivilization(offer.name)
@ -217,17 +217,17 @@ class TradeEvaluation{
TradeType.City -> {
val city = civInfo.cities.first { it.id == offer.name }
val stats = city.cityStats.currentCityStats
val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food
val sumOfStats = stats.culture + stats.gold + stats.science + stats.production + stats.happiness + stats.food
return sumOfStats.toInt() * 100
}
TradeType.Agreement -> {
if(offer.name == Constants.openBorders){
return when(civInfo.getDiplomacyManager(tradePartner).relationshipLevel()){
if (offer.name == Constants.openBorders) {
return when (civInfo.getDiplomacyManager(tradePartner).relationshipLevel()) {
RelationshipLevel.Unforgivable -> 10000
RelationshipLevel.Enemy -> 2000
RelationshipLevel.Competitor -> 500
RelationshipLevel.Neutral -> 200
RelationshipLevel.Favorable,RelationshipLevel.Friend,RelationshipLevel.Ally -> 100
RelationshipLevel.Favorable, RelationshipLevel.Friend, RelationshipLevel.Ally -> 100
}
}
throw Exception("Invalid agreement type!")
@ -238,22 +238,21 @@ class TradeEvaluation{
fun evaluatePeaceCostForThem(ourCivilization: CivilizationInfo, otherCivilization: CivilizationInfo): Int {
val ourCombatStrength = Automation.evaluteCombatStrength(ourCivilization)
val theirCombatStrength = Automation.evaluteCombatStrength(otherCivilization)
if(ourCombatStrength==theirCombatStrength) return 0
if(ourCombatStrength==0) return -1000
if(theirCombatStrength==0) return 1000 // Chumps got no cities or units
if(ourCombatStrength>theirCombatStrength){
val absoluteAdvantage = ourCombatStrength-theirCombatStrength
if (ourCombatStrength == theirCombatStrength) return 0
if (ourCombatStrength == 0) return -1000
if (theirCombatStrength == 0) return 1000 // Chumps got no cities or units
if (ourCombatStrength > theirCombatStrength) {
val absoluteAdvantage = ourCombatStrength - theirCombatStrength
val percentageAdvantage = absoluteAdvantage / theirCombatStrength.toFloat()
return (absoluteAdvantage*percentageAdvantage).toInt() * 10
}
else{
val absoluteAdvantage = theirCombatStrength-ourCombatStrength
return (absoluteAdvantage * percentageAdvantage).toInt() * 10
} else {
val absoluteAdvantage = theirCombatStrength - ourCombatStrength
val percentageAdvantage = absoluteAdvantage / ourCombatStrength.toFloat()
return -(absoluteAdvantage*percentageAdvantage).toInt() * 10
return -(absoluteAdvantage * percentageAdvantage).toInt() * 10
}
}
fun evaluateResearchAgreementCostForThem(ourCivilization: CivilizationInfo, otherCivilization: CivilizationInfo): Int {
return -100 * (ourCivilization.getEraNumber()-otherCivilization.getEraNumber())
return -100 * (ourCivilization.getEraNumber() - otherCivilization.getEraNumber())
}
}

View File

@ -65,6 +65,7 @@ class Nation : INamed {
// Same for Inca unique
@Transient
var ignoreHillMovementCost = false
@Transient
var embarkDisembarkCosts1 = false
@ -93,8 +94,7 @@ class Nation : INamed {
if (uniqueName != "") textList += uniqueName.tr() + ":"
if (uniqueText != "") {
textList += " " + uniqueText.tr()
}
else {
} else {
textList += " " + uniques.joinToString(", ") { it.tr() }
textList += ""
}
@ -112,7 +112,7 @@ class Nation : INamed {
private fun addUniqueBuildingsText(textList: ArrayList<String>, ruleset: Ruleset) {
for (building in ruleset.buildings.values
.filter { it.uniqueTo == name && "Will not be displayed in Civilopedia" !in it.uniques}) {
.filter { it.uniqueTo == name && "Will not be displayed in Civilopedia" !in it.uniques }) {
if (building.replaces != null && ruleset.buildings.containsKey(building.replaces!!)) {
val originalBuilding = ruleset.buildings[building.replaces!!]!!
@ -141,7 +141,7 @@ class Nation : INamed {
private fun addUniqueUnitsText(textList: ArrayList<String>, ruleset: Ruleset) {
for (unit in ruleset.units.values
.filter { it.uniqueTo == name && "Will not be displayed in Civilopedia" !in it.uniques}) {
.filter { it.uniqueTo == name && "Will not be displayed in Civilopedia" !in it.uniques }) {
if (unit.replaces != null && ruleset.units.containsKey(unit.replaces!!)) {
val originalUnit = ruleset.units[unit.replaces!!]!!
textList += unit.name.tr() + " - " + "Replaces [${originalUnit.name}]".tr()
@ -155,18 +155,18 @@ class Nation : INamed {
textList += " ${Fonts.range} " + "[${unit.range}] vs [${originalUnit.range}]".tr()
if (unit.movement != originalUnit.movement)
textList += " ${Fonts.movement} " + "[${unit.movement}] vs [${originalUnit.movement}]".tr()
if (originalUnit.requiredResource != null && unit.requiredResource == null)
textList += " " + "[${originalUnit.requiredResource}] not required".tr()
for (resource in originalUnit.getResourceRequirements().keys)
if (!unit.getResourceRequirements().containsKey(resource))
textList += " " + "[$resource] not required".tr()
for (unique in unit.uniques.filterNot { it in originalUnit.uniques })
textList += " " + Translations.translateBonusOrPenalty(unique)
for (unique in originalUnit.uniques.filterNot { it in unit.uniques })
textList += " " + "Lost ability".tr() + "(" + "vs [${originalUnit.name}]".tr() + "): " + Translations.translateBonusOrPenalty(unique)
for (promotion in unit.promotions.filter { it !in originalUnit.promotions })
textList += " " + promotion.tr() + " (" + Translations.translateBonusOrPenalty(ruleset.unitPromotions[promotion]!!.effect) + ")"
} else if(unit.replaces != null){
} else if (unit.replaces != null) {
textList += unit.name.tr() + " - " + "Replaces [${unit.replaces}], which is not found in the ruleset!".tr()
}
else {
} else {
textList += unit.name.tr()
textList += " " + unit.getDescription(true).split("\n").joinToString("\n ")
}
@ -185,4 +185,4 @@ class Nation : INamed {
textList += " " + unique.tr()
}
}
}
}

View File

@ -252,8 +252,9 @@ class Ruleset {
lines += "${unit.name} requires tech ${unit.requiredTech} which does not exist!"
if (unit.obsoleteTech != null && !technologies.containsKey(unit.obsoleteTech!!))
lines += "${unit.name} obsoletes at tech ${unit.obsoleteTech} which does not exist!"
if (unit.requiredResource != null && !tileResources.containsKey(unit.requiredResource!!))
lines += "${unit.name} requires resource ${unit.requiredResource} which does not exist!"
for (resource in unit.getResourceRequirements().keys)
if (!tileResources.containsKey(resource))
lines += "${unit.name} requires resource $resource which does not exist!"
if (unit.upgradesTo != null && !units.containsKey(unit.upgradesTo!!))
lines += "${unit.name} upgrades to unit ${unit.upgradesTo} which does not exist!"
if (unit.replaces != null && !units.containsKey(unit.replaces!!))
@ -267,7 +268,7 @@ class Ruleset {
for (building in buildings.values) {
if (building.requiredTech != null && !technologies.containsKey(building.requiredTech!!))
lines += "${building.name} requires tech ${building.requiredTech} which does not exist!"
for(resource in building.getResourceRequirements().keys)
for (resource in building.getResourceRequirements().keys)
if (!tileResources.containsKey(resource))
lines += "${building.name} requires resource $resource which does not exist!"
if (building.replaces != null && !buildings.containsKey(building.replaces!!))

View File

@ -37,7 +37,7 @@ class TileResource : NamedStats() {
stringBuilder.appendln("{Buildings that consume this resource}: ".tr()
+ buildingsThatConsumeThis.joinToString { it.name.tr() })
val unitsThatConsumeThis = ruleset.units.values.filter { it.requiredResource == name }
val unitsThatConsumeThis = ruleset.units.values.filter { it.getResourceRequirements().containsKey(name) }
if (unitsThatConsumeThis.isNotEmpty())
stringBuilder.appendln("{Units that consume this resource}: ".tr()
+ unitsThatConsumeThis.joinToString { it.name.tr() })

View File

@ -55,7 +55,10 @@ class BaseUnit : INamed, IConstruction {
fun getDescription(forPickerScreen: Boolean): String {
val sb = StringBuilder()
if (requiredResource != null) sb.appendln("Consumes 1 [{$requiredResource}]".tr())
for ((resource, amount) in getResourceRequirements()) {
if (amount == 1) sb.appendln("Consumes 1 [$resource]".tr())
else sb.appendln("Consumes [$amount]] [$resource]".tr())
}
if (!forPickerScreen) {
if (uniqueTo != null) sb.appendln("Unique to [$uniqueTo], replaces [$replaces]".tr())
else sb.appendln("{Cost}: $cost".tr())
@ -161,7 +164,13 @@ class BaseUnit : INamed, IConstruction {
if (civInfo.cities.none { it.cityConstructions.containsBuildingOrEquivalent(filter) }) return unique.text // Wonder is not built
} else if (!civInfo.policies.adoptedPolicies.contains(filter)) return "Policy is not adopted"
}
if (requiredResource != null && !civInfo.hasResource(requiredResource!!) && !civInfo.gameInfo.gameParameters.godMode) return "Consumes 1 [$requiredResource]"
for ((resource, amount) in getResourceRequirements())
if (civInfo.getCivResourcesByName()[resource]!! < amount) {
if (amount == 1) return "Consumes 1 [$resource]" // Again, to preserve existing translations
else return "Consumes [$amount] [$resource]"
}
if (uniques.contains(Constants.settlerUnique) && civInfo.isCityState()) return "No settler for city-states"
if (uniques.contains(Constants.settlerUnique) && civInfo.isOneCityChallenger()) return "No settler for players in One City Challenge"
return ""
@ -242,4 +251,4 @@ class BaseUnit : INamed, IConstruction {
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
return resourceRequirements
}
}
}

View File

@ -124,8 +124,10 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre
val useStoredProduction = !cityConstructions.isBeingConstructedOrEnqueued(unit.name)
val turnsToUnit = cityConstructions.turnsToConstruction(unit.name, useStoredProduction)
var buttonText = unit.name.tr() + turnOrTurns(turnsToUnit)
if (unit.requiredResource != null)
buttonText += "\n" + "Consumes 1 [${unit.requiredResource}]".tr()
for ((resource, amount) in unit.getResourceRequirements()) {
if (amount == 1) buttonText += "\n" + "Consumes 1 [$resource]".tr()
else buttonText += "\n" + "Consumes [$amount] [$resource]".tr()
}
constructionButtonDTOList.add(ConstructionButtonDTO(unit,
buttonText,