diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index 440164996a..d1ac8fa804 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -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() diff --git a/core/src/com/unciv/logic/battle/BattleDamage.kt b/core/src/com/unciv/logic/battle/BattleDamage.kt index 8a96533968..62e2924b80 100644 --- a/core/src/com/unciv/logic/battle/BattleDamage.kt +++ b/core/src/com/unciv/logic/battle/BattleDamage.kt @@ -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 { + private fun getBattleDamageModifiersOfUnit(unit: MapUnit): MutableList { val modifiers = mutableListOf() 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 { val modifiers = Counter() - 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 { + fun getAttackModifiers(attacker: ICombatant, tileToAttackFrom: TileInfo?, defender: ICombatant): Counter { 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): 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 } -} +} \ No newline at end of file diff --git a/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt b/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt index 5c1f0f20f3..4696af98dc 100644 --- a/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt +++ b/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt @@ -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 } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/trade/TradeEvaluation.kt b/core/src/com/unciv/logic/trade/TradeEvaluation.kt index f63be276aa..f3ca0c79ac 100644 --- a/core/src/com/unciv/logic/trade/TradeEvaluation.kt +++ b/core/src/com/unciv/logic/trade/TradeEvaluation.kt @@ -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()) } } \ No newline at end of file diff --git a/core/src/com/unciv/models/ruleset/Nation.kt b/core/src/com/unciv/models/ruleset/Nation.kt index 12189af611..0f147e8f3a 100644 --- a/core/src/com/unciv/models/ruleset/Nation.kt +++ b/core/src/com/unciv/models/ruleset/Nation.kt @@ -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, 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, 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() } } -} +} \ No newline at end of file diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index 2502863982..f89a7d20bb 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -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!!)) diff --git a/core/src/com/unciv/models/ruleset/tile/TileResource.kt b/core/src/com/unciv/models/ruleset/tile/TileResource.kt index b69830f94e..15b71be75d 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileResource.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileResource.kt @@ -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() }) diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index 724b31824d..b762f0482e 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -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 } -} +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt b/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt index 4b58036b26..43f53fe166 100644 --- a/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt +++ b/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt @@ -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,