mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-29 06:51:30 -04:00
Resource stockpiles! (#9147)
* Resource stockpiles! * toString extension including sign (+/-) * Trigger uniques to provide/consume stockpiled resources * Fixed build * Display 'per turn' for stockpiled resources that are consumed per turn * "Costs [amount] [resource]" works! * Stockpile unique costs are displayed in construction button * Added unique to prevert certain resources from being traded
This commit is contained in:
parent
0c60f87b27
commit
adb51d9264
@ -963,6 +963,8 @@ You may choose a free Policy =
|
|||||||
You may choose [amount] free Policies =
|
You may choose [amount] free Policies =
|
||||||
You gain the [policy] Policy =
|
You gain the [policy] Policy =
|
||||||
You enter a Golden Age =
|
You enter a Golden Age =
|
||||||
|
You have gained [amount] [resourceName] =
|
||||||
|
You have lost [amount] [resourceName] =
|
||||||
|
|
||||||
## Trigger causes
|
## Trigger causes
|
||||||
|
|
||||||
|
@ -591,7 +591,7 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
|||||||
|
|
||||||
spaceResources.clear()
|
spaceResources.clear()
|
||||||
spaceResources.addAll(ruleset.buildings.values.filter { it.hasUnique(UniqueType.SpaceshipPart) }
|
spaceResources.addAll(ruleset.buildings.values.filter { it.hasUnique(UniqueType.SpaceshipPart) }
|
||||||
.flatMap { it.getResourceRequirements().keys })
|
.flatMap { it.getResourceRequirementsPerTurn().keys })
|
||||||
spaceResources.addAll(ruleset.victories.values.flatMap { it.requiredSpaceshipParts })
|
spaceResources.addAll(ruleset.victories.values.flatMap { it.requiredSpaceshipParts })
|
||||||
|
|
||||||
barbarians.setTransients(this)
|
barbarians.setTransients(this)
|
||||||
|
@ -263,7 +263,7 @@ object Automation {
|
|||||||
if (construction.name in civInfo.gameInfo.spaceResources)
|
if (construction.name in civInfo.gameInfo.spaceResources)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
val requiredResources = construction.getResourceRequirements()
|
val requiredResources = construction.getResourceRequirementsPerTurn()
|
||||||
// Does it even require any resources?
|
// Does it even require any resources?
|
||||||
if (requiredResources.isEmpty())
|
if (requiredResources.isEmpty())
|
||||||
return true
|
return true
|
||||||
@ -281,9 +281,9 @@ object Automation {
|
|||||||
for (city in civInfo.cities) {
|
for (city in civInfo.cities) {
|
||||||
val otherConstruction = city.cityConstructions.getCurrentConstruction()
|
val otherConstruction = city.cityConstructions.getCurrentConstruction()
|
||||||
if (otherConstruction is Building)
|
if (otherConstruction is Building)
|
||||||
futureForBuildings += otherConstruction.getResourceRequirements()[resource] ?: 0
|
futureForBuildings += otherConstruction.getResourceRequirementsPerTurn()[resource] ?: 0
|
||||||
else
|
else
|
||||||
futureForUnits += otherConstruction.getResourceRequirements()[resource] ?: 0
|
futureForUnits += otherConstruction.getResourceRequirementsPerTurn()[resource] ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we have some for space
|
// Make sure we have some for space
|
||||||
|
@ -121,7 +121,7 @@ object UnitAutomation {
|
|||||||
val upgradedUnit = unit.upgrade.getUnitToUpgradeTo()
|
val upgradedUnit = unit.upgrade.getUnitToUpgradeTo()
|
||||||
if (!upgradedUnit.isBuildable(unit.civ)) return false // for resource reasons, usually
|
if (!upgradedUnit.isBuildable(unit.civ)) return false // for resource reasons, usually
|
||||||
|
|
||||||
if (upgradedUnit.getResourceRequirements().keys.any { !unit.baseUnit.requiresResource(it) }) {
|
if (upgradedUnit.getResourceRequirementsPerTurn().keys.any { !unit.baseUnit.requiresResource(it) }) {
|
||||||
// The upgrade requires new resource types, so check if we are willing to invest them
|
// The upgrade requires new resource types, so check if we are willing to invest them
|
||||||
if (!Automation.allowSpendingResource(unit.civ, upgradedUnit)) return false
|
if (!Automation.allowSpendingResource(unit.civ, upgradedUnit)) return false
|
||||||
}
|
}
|
||||||
|
@ -818,7 +818,7 @@ object Battle {
|
|||||||
|
|
||||||
var damageModifierFromMissingResource = 1f
|
var damageModifierFromMissingResource = 1f
|
||||||
val civResources = attacker.getCivInfo().getCivResourcesByName()
|
val civResources = attacker.getCivInfo().getCivResourcesByName()
|
||||||
for (resource in attacker.unit.baseUnit.getResourceRequirements().keys) {
|
for (resource in attacker.unit.baseUnit.getResourceRequirementsPerTurn().keys) {
|
||||||
if (civResources[resource]!! < 0 && !attacker.getCivInfo().isBarbarian())
|
if (civResources[resource]!! < 0 && !attacker.getCivInfo().isBarbarian())
|
||||||
damageModifierFromMissingResource *= 0.5f // I could not find a source for this number, but this felt about right
|
damageModifierFromMissingResource *= 0.5f // I could not find a source for this number, but this felt about right
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ object BattleDamage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val civResources = civInfo.getCivResourcesByName()
|
val civResources = civInfo.getCivResourcesByName()
|
||||||
for (resource in combatant.unit.baseUnit.getResourceRequirements().keys)
|
for (resource in combatant.unit.baseUnit.getResourceRequirementsPerTurn().keys)
|
||||||
if (civResources[resource]!! < 0 && !civInfo.isBarbarian())
|
if (civResources[resource]!! < 0 && !civInfo.isBarbarian())
|
||||||
modifiers["Missing resource"] = -25 //todo ModConstants
|
modifiers["Missing resource"] = -25 //todo ModConstants
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ class City : IsPartOfGameInfoSerialization {
|
|||||||
for (building in cityConstructions.getBuiltBuildings()) {
|
for (building in cityConstructions.getBuiltBuildings()) {
|
||||||
// Free buildings cost no resources
|
// Free buildings cost no resources
|
||||||
if (building.name in freeBuildings) continue
|
if (building.name in freeBuildings) continue
|
||||||
cityResources.subtractResourceRequirements(building.getResourceRequirements(), getRuleset(), "Buildings")
|
cityResources.subtractResourceRequirements(building.getResourceRequirementsPerTurn(), getRuleset(), "Buildings")
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unique in getLocalMatchingUniques(UniqueType.ProvidesResources, StateForConditionals(civ, this))) { // E.G "Provides [1] [Iron]"
|
for (unique in getLocalMatchingUniques(UniqueType.ProvidesResources, StateForConditionals(civ, this))) { // E.G "Provides [1] [Iron]"
|
||||||
|
@ -349,7 +349,19 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
constructionQueue.clear()
|
constructionQueue.clear()
|
||||||
|
|
||||||
for (constructionName in queueSnapshot) {
|
for (constructionName in queueSnapshot) {
|
||||||
if (getConstruction(constructionName).isBuildable(this))
|
val construction = getConstruction(constructionName)
|
||||||
|
// First construction will be built next turn, we need to make sure it has the correct resources
|
||||||
|
if (constructionQueue.isEmpty() && getWorkDone(constructionName) == 0) {
|
||||||
|
val costUniques = construction.getMatchingUniquesNotConflicting(UniqueType.CostsResources)
|
||||||
|
val civResources = city.civ.getCivResourcesByName()
|
||||||
|
|
||||||
|
if (costUniques.any {
|
||||||
|
val resourceName = it.params[1]
|
||||||
|
civResources[resourceName] == null
|
||||||
|
|| it.params[0].toInt() > civResources[resourceName]!! })
|
||||||
|
continue // Removes this construction from the queue
|
||||||
|
}
|
||||||
|
if (construction.isBuildable(this))
|
||||||
constructionQueue.add(constructionName)
|
constructionQueue.add(constructionName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -392,6 +404,14 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun constructionBegun(construction: IConstruction) {
|
private fun constructionBegun(construction: IConstruction) {
|
||||||
|
val costUniques = construction.getMatchingUniquesNotConflicting(UniqueType.CostsResources)
|
||||||
|
|
||||||
|
for (unique in costUniques){
|
||||||
|
val amount = unique.params[0].toInt()
|
||||||
|
val resourceName = unique.params[1]
|
||||||
|
city.civ.resourceStockpiles.add(resourceName, -amount)
|
||||||
|
}
|
||||||
|
|
||||||
if (construction !is Building) return
|
if (construction !is Building) return
|
||||||
if (!construction.hasUnique(UniqueType.TriggersAlertOnStart)) return
|
if (!construction.hasUnique(UniqueType.TriggersAlertOnStart)) return
|
||||||
val buildingIcon = "BuildingIcons/${construction.name}"
|
val buildingIcon = "BuildingIcons/${construction.name}"
|
||||||
|
@ -31,6 +31,7 @@ import com.unciv.logic.civilization.transients.CivInfoTransientCache
|
|||||||
import com.unciv.logic.map.mapunit.MapUnit
|
import com.unciv.logic.map.mapunit.MapUnit
|
||||||
import com.unciv.logic.map.tile.Tile
|
import com.unciv.logic.map.tile.Tile
|
||||||
import com.unciv.logic.trade.TradeRequest
|
import com.unciv.logic.trade.TradeRequest
|
||||||
|
import com.unciv.models.Counter
|
||||||
import com.unciv.models.ruleset.Building
|
import com.unciv.models.ruleset.Building
|
||||||
import com.unciv.models.ruleset.Policy
|
import com.unciv.models.ruleset.Policy
|
||||||
import com.unciv.models.ruleset.Victory
|
import com.unciv.models.ruleset.Victory
|
||||||
@ -111,7 +112,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
var detailedCivResources = ResourceSupplyList()
|
var detailedCivResources = ResourceSupplyList()
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
var summarizedCivResources = ResourceSupplyList()
|
var summarizedCivResourceSupply = ResourceSupplyList()
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
val cityStateFunctions = CityStateFunctions(this)
|
val cityStateFunctions = CityStateFunctions(this)
|
||||||
@ -172,6 +173,8 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
/** See DiplomacyManager.flagsCountdown for why this does not map Enums to ints */
|
/** See DiplomacyManager.flagsCountdown for why this does not map Enums to ints */
|
||||||
var flagsCountdown = HashMap<String, Int>()
|
var flagsCountdown = HashMap<String, Int>()
|
||||||
|
|
||||||
|
var resourceStockpiles = Counter<String>()
|
||||||
|
|
||||||
/** Arraylist instead of HashMap as the same unique might appear multiple times
|
/** Arraylist instead of HashMap as the same unique might appear multiple times
|
||||||
* We don't use pairs, as these cannot be serialized due to having no no-arg constructor
|
* We don't use pairs, as these cannot be serialized due to having no no-arg constructor
|
||||||
* We ALSO can't use a class inheriting from ArrayList<TemporaryUnique>() because ANNOYINGLY that doesn't pass deserialization
|
* We ALSO can't use a class inheriting from ArrayList<TemporaryUnique>() because ANNOYINGLY that doesn't pass deserialization
|
||||||
@ -288,6 +291,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
toReturn.attacksSinceTurnStart = attacksSinceTurnStart.copy()
|
toReturn.attacksSinceTurnStart = attacksSinceTurnStart.copy()
|
||||||
toReturn.hasMovedAutomatedUnits = hasMovedAutomatedUnits
|
toReturn.hasMovedAutomatedUnits = hasMovedAutomatedUnits
|
||||||
toReturn.statsHistory = statsHistory.clone()
|
toReturn.statsHistory = statsHistory.clone()
|
||||||
|
toReturn.resourceStockpiles = resourceStockpiles.clone()
|
||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,12 +380,18 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
|
|
||||||
fun getHappiness() = stats.happiness
|
fun getHappiness() = stats.happiness
|
||||||
|
|
||||||
fun getCivResources(): ResourceSupplyList = summarizedCivResources
|
/** Note that for stockpiled resources, this gives by how much it grows per turn, not current amount */
|
||||||
|
fun getCivResourceSupply(): ResourceSupplyList = summarizedCivResourceSupply
|
||||||
|
|
||||||
// Preserves some origins for resources so we can separate them for trades
|
/** Preserves some origins for resources so we can separate them for trades
|
||||||
|
* Stockpiled uniques cannot be traded currently
|
||||||
|
*/
|
||||||
fun getCivResourcesWithOriginsForTrade(): ResourceSupplyList {
|
fun getCivResourcesWithOriginsForTrade(): ResourceSupplyList {
|
||||||
val newResourceSupplyList = ResourceSupplyList(keepZeroAmounts = true)
|
val newResourceSupplyList = ResourceSupplyList(keepZeroAmounts = true)
|
||||||
|
|
||||||
for (resourceSupply in detailedCivResources) {
|
for (resourceSupply in detailedCivResources) {
|
||||||
|
if (resourceSupply.resource.isStockpiled()) continue
|
||||||
|
if (resourceSupply.resource.hasUnique(UniqueType.CannotBeTraded)) continue
|
||||||
// If we got it from another trade or from a CS, preserve the origin
|
// If we got it from another trade or from a CS, preserve the origin
|
||||||
if (resourceSupply.isCityStateOrTradeOrigin()) {
|
if (resourceSupply.isCityStateOrTradeOrigin()) {
|
||||||
newResourceSupplyList.add(resourceSupply.copy())
|
newResourceSupplyList.add(resourceSupply.copy())
|
||||||
@ -398,12 +408,16 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a dictionary of ALL resource names, and the amount that the civ has of each
|
* Returns a dictionary of ALL resource names, and the amount that the civ has of each
|
||||||
|
* Stockpiled resources return the stockpiled amount
|
||||||
*/
|
*/
|
||||||
fun getCivResourcesByName(): HashMap<String, Int> {
|
fun getCivResourcesByName(): HashMap<String, Int> {
|
||||||
val hashMap = HashMap<String, Int>(gameInfo.ruleset.tileResources.size)
|
val hashMap = HashMap<String, Int>(gameInfo.ruleset.tileResources.size)
|
||||||
for (resource in gameInfo.ruleset.tileResources.keys) hashMap[resource] = 0
|
for (resource in gameInfo.ruleset.tileResources.keys) hashMap[resource] = 0
|
||||||
for (entry in getCivResources())
|
for (entry in getCivResourceSupply())
|
||||||
hashMap[entry.resource.name] = entry.amount
|
if (!entry.resource.isStockpiled())
|
||||||
|
hashMap[entry.resource.name] = entry.amount
|
||||||
|
for ((key, value) in resourceStockpiles)
|
||||||
|
hashMap[key] = value
|
||||||
return hashMap
|
return hashMap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,7 +456,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
yieldAll(religionManager.religion!!.getFounderUniques()
|
yieldAll(religionManager.religion!!.getFounderUniques()
|
||||||
.filter { it.isOfType(uniqueType) && it.conditionalsApply(stateForConditionals) })
|
.filter { it.isOfType(uniqueType) && it.conditionalsApply(stateForConditionals) })
|
||||||
|
|
||||||
yieldAll(getCivResources().asSequence()
|
yieldAll(getCivResourceSupply().asSequence()
|
||||||
.filter { it.amount > 0 }
|
.filter { it.amount > 0 }
|
||||||
.flatMap { it.resource.getMatchingUniques(uniqueType, stateForConditionals) }
|
.flatMap { it.resource.getMatchingUniques(uniqueType, stateForConditionals) }
|
||||||
)
|
)
|
||||||
|
@ -404,6 +404,7 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization {
|
|||||||
val isResourceFilter: (TradeOffer) -> Boolean = {
|
val isResourceFilter: (TradeOffer) -> Boolean = {
|
||||||
(it.type == TradeType.Strategic_Resource || it.type == TradeType.Luxury_Resource)
|
(it.type == TradeType.Strategic_Resource || it.type == TradeType.Luxury_Resource)
|
||||||
&& resourcesMap.containsKey(it.name)
|
&& resourcesMap.containsKey(it.name)
|
||||||
|
&& !resourcesMap[it.name]!!.isStockpiled()
|
||||||
}
|
}
|
||||||
for (trade in trades) {
|
for (trade in trades) {
|
||||||
for (offer in trade.ourOffers.filter(isResourceFilter))
|
for (offer in trade.ourOffers.filter(isResourceFilter))
|
||||||
@ -436,8 +437,8 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization {
|
|||||||
|
|
||||||
// Every cancelled trade can change this - if 1 resource is missing,
|
// Every cancelled trade can change this - if 1 resource is missing,
|
||||||
// don't cancel all trades of that resource, only cancel one (the first one, as it happens, since they're added chronologically)
|
// don't cancel all trades of that resource, only cancel one (the first one, as it happens, since they're added chronologically)
|
||||||
val negativeCivResources = civInfo.getCivResources()
|
val negativeCivResources = civInfo.getCivResourceSupply()
|
||||||
.filter { it.amount < 0 }.map { it.resource.name }
|
.filter { it.amount < 0 && !it.resource.isStockpiled() }.map { it.resource.name }
|
||||||
|
|
||||||
for (offer in trade.ourOffers) {
|
for (offer in trade.ourOffers) {
|
||||||
if (offer.type in listOf(TradeType.Luxury_Resource, TradeType.Strategic_Resource)
|
if (offer.type in listOf(TradeType.Luxury_Resource, TradeType.Strategic_Resource)
|
||||||
|
@ -36,6 +36,11 @@ class TurnManager(val civInfo: Civilization) {
|
|||||||
if (civInfo.cities.isNotEmpty() && civInfo.gameInfo.ruleset.technologies.isNotEmpty())
|
if (civInfo.cities.isNotEmpty() && civInfo.gameInfo.ruleset.technologies.isNotEmpty())
|
||||||
civInfo.tech.updateResearchProgress()
|
civInfo.tech.updateResearchProgress()
|
||||||
|
|
||||||
|
|
||||||
|
civInfo.cache.updateCivResources() // If you offered a trade last turn, this turn it will have been accepted/declined
|
||||||
|
for (stockpiledResource in civInfo.getCivResourceSupply().filter { it.resource.isStockpiled() })
|
||||||
|
civInfo.resourceStockpiles.add(stockpiledResource.resource.name, stockpiledResource.amount)
|
||||||
|
|
||||||
civInfo.civConstructions.startTurn()
|
civInfo.civConstructions.startTurn()
|
||||||
civInfo.attacksSinceTurnStart.clear()
|
civInfo.attacksSinceTurnStart.clear()
|
||||||
civInfo.updateStatsForNextTurn() // for things that change when turn passes e.g. golden age, city state influence
|
civInfo.updateStatsForNextTurn() // for things that change when turn passes e.g. golden age, city state influence
|
||||||
@ -70,8 +75,6 @@ class TurnManager(val civInfo: Civilization) {
|
|||||||
unit.doAction()
|
unit.doAction()
|
||||||
} else civInfo.hasMovedAutomatedUnits = false
|
} else civInfo.hasMovedAutomatedUnits = false
|
||||||
|
|
||||||
civInfo.cache.updateCivResources() // If you offered a trade last turn, this turn it will have been accepted/declined
|
|
||||||
|
|
||||||
for (tradeRequest in civInfo.tradeRequests.toList()) { // remove trade requests where one of the sides can no longer supply
|
for (tradeRequest in civInfo.tradeRequests.toList()) { // remove trade requests where one of the sides can no longer supply
|
||||||
val offeringCiv = civInfo.gameInfo.getCivilization(tradeRequest.requestingCiv)
|
val offeringCiv = civInfo.gameInfo.getCivilization(tradeRequest.requestingCiv)
|
||||||
if (offeringCiv.isDefeated() || !TradeEvaluation().isTradeValid(tradeRequest.trade, civInfo, offeringCiv)) {
|
if (offeringCiv.isDefeated() || !TradeEvaluation().isTradeValid(tradeRequest.trade, civInfo, offeringCiv)) {
|
||||||
|
@ -98,7 +98,7 @@ class UnitManager(val civInfo:Civilization) {
|
|||||||
// Not relevant when updating Tile transients, since some info of the civ itself isn't yet available,
|
// 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
|
// and in any case it'll be updated once civ info transients are
|
||||||
civInfo.updateStatsForNextTurn() // unit upkeep
|
civInfo.updateStatsForNextTurn() // unit upkeep
|
||||||
if (mapUnit.baseUnit.getResourceRequirements().isNotEmpty())
|
if (mapUnit.baseUnit.getResourceRequirementsPerTurn().isNotEmpty())
|
||||||
civInfo.cache.updateCivResources()
|
civInfo.cache.updateCivResources()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ class UnitManager(val civInfo:Civilization) {
|
|||||||
nextPotentiallyDueAt = 0
|
nextPotentiallyDueAt = 0
|
||||||
|
|
||||||
civInfo.updateStatsForNextTurn() // unit upkeep
|
civInfo.updateStatsForNextTurn() // unit upkeep
|
||||||
if (mapUnit.baseUnit.getResourceRequirements().isNotEmpty())
|
if (mapUnit.baseUnit.getResourceRequirementsPerTurn().isNotEmpty())
|
||||||
civInfo.cache.updateCivResources()
|
civInfo.cache.updateCivResources()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,10 +230,10 @@ class CivInfoStatsForNextTurn(val civInfo: Civilization) {
|
|||||||
for (unique in civInfo.getMatchingUniques(UniqueType.BonusHappinessFromLuxury))
|
for (unique in civInfo.getMatchingUniques(UniqueType.BonusHappinessFromLuxury))
|
||||||
happinessPerUniqueLuxury += unique.params[0].toInt()
|
happinessPerUniqueLuxury += unique.params[0].toInt()
|
||||||
|
|
||||||
val ownedLuxuries = civInfo.getCivResources().map { it.resource }
|
val ownedLuxuries = civInfo.getCivResourceSupply().map { it.resource }
|
||||||
.filter { it.resourceType == ResourceType.Luxury }
|
.filter { it.resourceType == ResourceType.Luxury }
|
||||||
|
|
||||||
val relevantLuxuries = civInfo.getCivResources().asSequence()
|
val relevantLuxuries = civInfo.getCivResourceSupply().asSequence()
|
||||||
.map { it.resource }
|
.map { it.resource }
|
||||||
.count { it.resourceType == ResourceType.Luxury
|
.count { it.resourceType == ResourceType.Luxury
|
||||||
&& it.getMatchingUniques(UniqueType.ObsoleteWith)
|
&& it.getMatchingUniques(UniqueType.ObsoleteWith)
|
||||||
@ -245,7 +245,7 @@ class CivInfoStatsForNextTurn(val civInfo: Civilization) {
|
|||||||
|
|
||||||
val luxuriesProvidedByCityStates = civInfo.getKnownCivs().asSequence()
|
val luxuriesProvidedByCityStates = civInfo.getKnownCivs().asSequence()
|
||||||
.filter { it.isCityState() && it.getAllyCiv() == civInfo.civName }
|
.filter { it.isCityState() && it.getAllyCiv() == civInfo.civName }
|
||||||
.flatMap { it.getCivResources().map { res -> res.resource } }
|
.flatMap { it.getCivResourceSupply().map { res -> res.resource } }
|
||||||
.distinct()
|
.distinct()
|
||||||
.count { it.resourceType === ResourceType.Luxury && ownedLuxuries.contains(it) }
|
.count { it.resourceType === ResourceType.Luxury && ownedLuxuries.contains(it) }
|
||||||
|
|
||||||
|
@ -305,13 +305,13 @@ class CivInfoTransientCache(val civInfo: Civilization) {
|
|||||||
|
|
||||||
for (unit in civInfo.units.getCivUnits())
|
for (unit in civInfo.units.getCivUnits())
|
||||||
newDetailedCivResources.subtractResourceRequirements(
|
newDetailedCivResources.subtractResourceRequirements(
|
||||||
unit.baseUnit.getResourceRequirements(), civInfo.gameInfo.ruleset, "Units")
|
unit.baseUnit.getResourceRequirementsPerTurn(), civInfo.gameInfo.ruleset, "Units")
|
||||||
|
|
||||||
// Check if anything has actually changed so we don't update stats for no reason - this uses List equality which means it checks the elements
|
// Check if anything has actually changed so we don't update stats for no reason - this uses List equality which means it checks the elements
|
||||||
if (civInfo.detailedCivResources == newDetailedCivResources) return
|
if (civInfo.detailedCivResources == newDetailedCivResources) return
|
||||||
|
|
||||||
civInfo.detailedCivResources = newDetailedCivResources
|
civInfo.detailedCivResources = newDetailedCivResources
|
||||||
civInfo.summarizedCivResources = newDetailedCivResources.sumByResource("All")
|
civInfo.summarizedCivResourceSupply = newDetailedCivResources.sumByResource("All")
|
||||||
|
|
||||||
civInfo.updateStatsForNextTurn() // More or less resources = more or less happiness, with potential domino effects
|
civInfo.updateStatsForNextTurn() // More or less resources = more or less happiness, with potential domino effects
|
||||||
}
|
}
|
||||||
|
@ -538,7 +538,7 @@ class TileMap : IsPartOfGameInfoSerialization {
|
|||||||
// And update civ stats, since the new unit changes both unit upkeep and resource consumption
|
// And update civ stats, since the new unit changes both unit upkeep and resource consumption
|
||||||
civInfo.updateStatsForNextTurn()
|
civInfo.updateStatsForNextTurn()
|
||||||
|
|
||||||
if (unit.baseUnit.getResourceRequirements().isNotEmpty())
|
if (unit.baseUnit.getResourceRequirementsPerTurn().isNotEmpty())
|
||||||
civInfo.cache.updateCivResources()
|
civInfo.cache.updateCivResources()
|
||||||
|
|
||||||
return unit
|
return unit
|
||||||
|
@ -36,19 +36,20 @@ class TileInfoImprovementFunctions(val tile: Tile) {
|
|||||||
yield(ImprovementBuildingProblem.NotJustOutsideBorders)
|
yield(ImprovementBuildingProblem.NotJustOutsideBorders)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (improvement.getMatchingUniques(UniqueType.OnlyAvailableWhen, StateForConditionals.IgnoreConditionals).any {
|
if (improvement.getMatchingUniques(UniqueType.OnlyAvailableWhen, StateForConditionals.IgnoreConditionals)
|
||||||
!it.conditionalsApply(stateForConditionals)
|
.any { !it.conditionalsApply(stateForConditionals) })
|
||||||
})
|
|
||||||
yield(ImprovementBuildingProblem.UnmetConditional)
|
yield(ImprovementBuildingProblem.UnmetConditional)
|
||||||
|
|
||||||
if (improvement.getMatchingUniques(UniqueType.ObsoleteWith, stateForConditionals).any {
|
if (improvement.getMatchingUniques(UniqueType.ObsoleteWith, stateForConditionals)
|
||||||
civInfo.tech.isResearched(it.params[0])
|
.any { civInfo.tech.isResearched(it.params[0]) })
|
||||||
})
|
|
||||||
yield(ImprovementBuildingProblem.Obsolete)
|
yield(ImprovementBuildingProblem.Obsolete)
|
||||||
|
|
||||||
if (improvement.getMatchingUniques(UniqueType.ConsumesResources, stateForConditionals).any {
|
if (improvement.getMatchingUniques(UniqueType.ConsumesResources, stateForConditionals)
|
||||||
civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
|
.any { civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt() })
|
||||||
})
|
yield(ImprovementBuildingProblem.MissingResources)
|
||||||
|
|
||||||
|
if (improvement.getMatchingUniques(UniqueType.CostsResources)
|
||||||
|
.any { civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt() })
|
||||||
yield(ImprovementBuildingProblem.MissingResources)
|
yield(ImprovementBuildingProblem.MissingResources)
|
||||||
|
|
||||||
val knownFeatureRemovals = tile.ruleset.tileImprovements.values
|
val knownFeatureRemovals = tile.ruleset.tileImprovements.values
|
||||||
|
@ -118,9 +118,9 @@ class TradeEvaluation {
|
|||||||
val amountToBuyInOffer = min(amountWillingToBuy, offer.amount)
|
val amountToBuyInOffer = min(amountWillingToBuy, offer.amount)
|
||||||
|
|
||||||
val canUseForBuildings = civInfo.cities
|
val canUseForBuildings = civInfo.cities
|
||||||
.any { city -> city.cityConstructions.getBuildableBuildings().any { it.getResourceRequirements().containsKey(offer.name) } }
|
.any { city -> city.cityConstructions.getBuildableBuildings().any { it.getResourceRequirementsPerTurn().containsKey(offer.name) } }
|
||||||
val canUseForUnits = civInfo.cities
|
val canUseForUnits = civInfo.cities
|
||||||
.any { city -> city.cityConstructions.getConstructableUnits().any { it.getResourceRequirements().containsKey(offer.name) } }
|
.any { city -> city.cityConstructions.getConstructableUnits().any { it.getResourceRequirementsPerTurn().containsKey(offer.name) } }
|
||||||
if (!canUseForBuildings && !canUseForUnits) return 0
|
if (!canUseForBuildings && !canUseForUnits) return 0
|
||||||
|
|
||||||
return 50 * amountToBuyInOffer
|
return 50 * amountToBuyInOffer
|
||||||
@ -217,7 +217,7 @@ class TradeEvaluation {
|
|||||||
if (!civInfo.isAtWar()) return 50 * offer.amount
|
if (!civInfo.isAtWar()) return 50 * offer.amount
|
||||||
|
|
||||||
val canUseForUnits = civInfo.gameInfo.ruleset.units.values
|
val canUseForUnits = civInfo.gameInfo.ruleset.units.values
|
||||||
.any { it.getResourceRequirements().containsKey(offer.name)
|
.any { it.getResourceRequirementsPerTurn().containsKey(offer.name)
|
||||||
&& it.isBuildable(civInfo) }
|
&& it.isBuildable(civInfo) }
|
||||||
if (!canUseForUnits) return 50 * offer.amount
|
if (!canUseForUnits) return 50 * offer.amount
|
||||||
|
|
||||||
|
@ -122,12 +122,14 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
if (isNationalWonder) lines += "National Wonder"
|
if (isNationalWonder) lines += "National Wonder"
|
||||||
if (!isFree) {
|
if (!isFree) {
|
||||||
val availableResources = if (!showAdditionalInfo) emptyMap()
|
val availableResources = if (!showAdditionalInfo) emptyMap()
|
||||||
else city.civ.getCivResources().associate { it.resource.name to it.amount }
|
else city.civ.getCivResourcesByName()
|
||||||
for ((resource, amount) in getResourceRequirements()) {
|
for ((resourceName, amount) in getResourceRequirementsPerTurn()) {
|
||||||
val available = availableResources[resource] ?: 0
|
val available = availableResources[resourceName] ?: 0
|
||||||
lines += if (showAdditionalInfo)
|
val resource = city.getRuleset().tileResources[resourceName] ?: continue
|
||||||
"{${resource.getConsumesAmountString(amount)}} ({[$available] available})"
|
val consumesString = resourceName.getConsumesAmountString(amount, resource.isStockpiled())
|
||||||
else resource.getConsumesAmountString(amount)
|
|
||||||
|
lines += if (showAdditionalInfo) "$consumesString ({[$available] available})"
|
||||||
|
else consumesString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,14 +256,14 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
textList += FormattedLine("Requires [$requiredBuilding] to be built in the city",
|
textList += FormattedLine("Requires [$requiredBuilding] to be built in the city",
|
||||||
link="Building/$requiredBuilding")
|
link="Building/$requiredBuilding")
|
||||||
|
|
||||||
val resourceRequirements = getResourceRequirements()
|
val resourceRequirements = getResourceRequirementsPerTurn()
|
||||||
if (resourceRequirements.isNotEmpty()) {
|
if (resourceRequirements.isNotEmpty()) {
|
||||||
textList += FormattedLine()
|
textList += FormattedLine()
|
||||||
for ((resource, amount) in resourceRequirements) {
|
for ((resourceName, amount) in resourceRequirements) {
|
||||||
|
val resource = ruleset.tileResources[resourceName] ?: continue
|
||||||
textList += FormattedLine(
|
textList += FormattedLine(
|
||||||
// the 1 variant should deprecate some time
|
resourceName.getConsumesAmountString(amount, resource.isStockpiled()),
|
||||||
resource.getConsumesAmountString(amount),
|
link="Resources/$resourceName", color="#F42" )
|
||||||
link="Resources/$resource", color="#F42" )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,7 +617,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
yield(RejectionReasonType.RequiresBuildingInThisCity.toInstance("Requires a [${civ.getEquivalentBuilding(requiredBuilding!!)}] in this city"))
|
yield(RejectionReasonType.RequiresBuildingInThisCity.toInstance("Requires a [${civ.getEquivalentBuilding(requiredBuilding!!)}] in this city"))
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((resource, requiredAmount) in getResourceRequirements()) {
|
for ((resource, requiredAmount) in getResourceRequirementsPerTurn()) {
|
||||||
val availableAmount = civ.getCivResourcesByName()[resource]!!
|
val availableAmount = civ.getCivResourcesByName()[resource]!!
|
||||||
if (availableAmount < requiredAmount) {
|
if (availableAmount < requiredAmount) {
|
||||||
yield(RejectionReasonType.ConsumesResources.toInstance(resource.getNeedMoreAmountString(requiredAmount - availableAmount)))
|
yield(RejectionReasonType.ConsumesResources.toInstance(resource.getNeedMoreAmountString(requiredAmount - availableAmount)))
|
||||||
@ -740,7 +742,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
|
|
||||||
fun isSellable() = !isAnyWonder() && !hasUnique(UniqueType.Unsellable)
|
fun isSellable() = !isAnyWonder() && !hasUnique(UniqueType.Unsellable)
|
||||||
|
|
||||||
override fun getResourceRequirements(): HashMap<String, Int> = resourceRequirementsInternal
|
override fun getResourceRequirementsPerTurn(): HashMap<String, Int> = resourceRequirementsInternal
|
||||||
|
|
||||||
private val resourceRequirementsInternal: HashMap<String, Int> by lazy {
|
private val resourceRequirementsInternal: HashMap<String, Int> by lazy {
|
||||||
val resourceRequirements = HashMap<String, Int>()
|
val resourceRequirements = HashMap<String, Int>()
|
||||||
@ -756,6 +758,9 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
for (unique in getMatchingUniques(UniqueType.ConsumesResources)) {
|
for (unique in getMatchingUniques(UniqueType.ConsumesResources)) {
|
||||||
if (unique.params[1] == resource) return true
|
if (unique.params[1] == resource) return true
|
||||||
}
|
}
|
||||||
|
for (unique in getMatchingUniques(UniqueType.CostsResources)) {
|
||||||
|
if (unique.params[1] == resource) return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.unciv.logic.city
|
|||||||
import com.unciv.logic.civilization.Civilization
|
import com.unciv.logic.civilization.Civilization
|
||||||
import com.unciv.models.ruleset.unique.IHasUniques
|
import com.unciv.models.ruleset.unique.IHasUniques
|
||||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||||
|
import com.unciv.models.ruleset.unique.Unique
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.stats.INamed
|
import com.unciv.models.stats.INamed
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
@ -14,8 +15,11 @@ import kotlin.math.roundToInt
|
|||||||
interface IConstruction : INamed {
|
interface IConstruction : INamed {
|
||||||
fun isBuildable(cityConstructions: CityConstructions): Boolean
|
fun isBuildable(cityConstructions: CityConstructions): Boolean
|
||||||
fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean
|
fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean
|
||||||
fun getResourceRequirements(): HashMap<String,Int>
|
/** Gets *per turn* resource requirements - does not include immediate costs for stockpiled resources */
|
||||||
|
fun getResourceRequirementsPerTurn(): HashMap<String,Int>
|
||||||
fun requiresResource(resource: String): Boolean
|
fun requiresResource(resource: String): Boolean
|
||||||
|
/** We can't call this getMatchingUniques because then it would conflict with IHasUniques */
|
||||||
|
fun getMatchingUniquesNotConflicting(uniqueType: UniqueType) = sequenceOf<Unique>()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
||||||
@ -82,6 +86,9 @@ interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
|||||||
fun getCostForConstructionsIncreasingInPrice(baseCost: Int, increaseCost: Int, previouslyBought: Int): Int {
|
fun getCostForConstructionsIncreasingInPrice(baseCost: Int, increaseCost: Int, previouslyBought: Int): Int {
|
||||||
return (baseCost + increaseCost / 2f * ( previouslyBought * previouslyBought + previouslyBought )).toInt()
|
return (baseCost + increaseCost / 2f * ( previouslyBought * previouslyBought + previouslyBought )).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getMatchingUniquesNotConflicting(uniqueType: UniqueType): Sequence<Unique> =
|
||||||
|
getMatchingUniques(uniqueType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -210,7 +217,7 @@ open class PerpetualConstruction(override var name: String, val description: Str
|
|||||||
override fun isBuildable(cityConstructions: CityConstructions): Boolean =
|
override fun isBuildable(cityConstructions: CityConstructions): Boolean =
|
||||||
throw Exception("Impossible!")
|
throw Exception("Impossible!")
|
||||||
|
|
||||||
override fun getResourceRequirements(): HashMap<String, Int> = hashMapOf()
|
override fun getResourceRequirementsPerTurn(): HashMap<String, Int> = hashMapOf()
|
||||||
|
|
||||||
override fun requiresResource(resource: String) = false
|
override fun requiresResource(resource: String) = false
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (resource in unit.getResourceRequirements().keys)
|
for (resource in unit.getResourceRequirementsPerTurn().keys)
|
||||||
if (!ruleset.tileResources.containsKey(resource))
|
if (!ruleset.tileResources.containsKey(resource))
|
||||||
lines += "${unit.name} requires resource $resource which does not exist!"
|
lines += "${unit.name} requires resource $resource which does not exist!"
|
||||||
if (unit.replaces != null && !ruleset.units.containsKey(unit.replaces!!))
|
if (unit.replaces != null && !ruleset.units.containsKey(unit.replaces!!))
|
||||||
@ -189,7 +189,7 @@ class RulesetValidator(val ruleset: Ruleset) {
|
|||||||
for (specialistName in building.specialistSlots.keys)
|
for (specialistName in building.specialistSlots.keys)
|
||||||
if (!ruleset.specialists.containsKey(specialistName))
|
if (!ruleset.specialists.containsKey(specialistName))
|
||||||
lines += "${building.name} provides specialist $specialistName which does not exist!"
|
lines += "${building.name} provides specialist $specialistName which does not exist!"
|
||||||
for (resource in building.getResourceRequirements().keys)
|
for (resource in building.getResourceRequirementsPerTurn().keys)
|
||||||
if (!ruleset.tileResources.containsKey(resource))
|
if (!ruleset.tileResources.containsKey(resource))
|
||||||
lines += "${building.name} requires resource $resource which does not exist!"
|
lines += "${building.name} requires resource $resource which does not exist!"
|
||||||
if (building.replaces != null && !ruleset.buildings.containsKey(building.replaces!!))
|
if (building.replaces != null && !ruleset.buildings.containsKey(building.replaces!!))
|
||||||
|
@ -236,8 +236,8 @@ class Nation : RulesetObject() {
|
|||||||
yield(FormattedLine("${Fonts.range} " + "[${unit.range}] vs [${originalUnit.range}]".tr(), indent=1))
|
yield(FormattedLine("${Fonts.range} " + "[${unit.range}] vs [${originalUnit.range}]".tr(), indent=1))
|
||||||
if (unit.movement != originalUnit.movement)
|
if (unit.movement != originalUnit.movement)
|
||||||
yield(FormattedLine("${Fonts.movement} " + "[${unit.movement}] vs [${originalUnit.movement}]".tr(), indent=1))
|
yield(FormattedLine("${Fonts.movement} " + "[${unit.movement}] vs [${originalUnit.movement}]".tr(), indent=1))
|
||||||
for (resource in originalUnit.getResourceRequirements().keys)
|
for (resource in originalUnit.getResourceRequirementsPerTurn().keys)
|
||||||
if (!unit.getResourceRequirements().containsKey(resource)) {
|
if (!unit.getResourceRequirementsPerTurn().containsKey(resource)) {
|
||||||
yield(FormattedLine("[$resource] not required", link="Resource/$resource", indent=1))
|
yield(FormattedLine("[$resource] not required", link="Resource/$resource", indent=1))
|
||||||
}
|
}
|
||||||
// This does not use the auto-linking FormattedLine(Unique) for two reasons:
|
// This does not use the auto-linking FormattedLine(Unique) for two reasons:
|
||||||
|
@ -62,7 +62,7 @@ class ResourceSupplyList(
|
|||||||
add(resourceSupply)
|
add(resourceSupply)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add entries from a requirements list (as produced by [IConstruction.getResourceRequirements]), expressing requirement as negative supply. */
|
/** Add entries from a requirements list (as produced by [IConstruction.getResourceRequirementsPerTurn]), expressing requirement as negative supply. */
|
||||||
fun subtractResourceRequirements(resourceRequirements: HashMap<String, Int>, ruleset: Ruleset, origin: String) {
|
fun subtractResourceRequirements(resourceRequirements: HashMap<String, Int>, ruleset: Ruleset, origin: String) {
|
||||||
for ((resourceName, amount) in resourceRequirements) {
|
for ((resourceName, amount) in resourceRequirements) {
|
||||||
val resource = ruleset.tileResources[resourceName] ?: continue
|
val resource = ruleset.tileResources[resourceName] ?: continue
|
||||||
|
@ -15,6 +15,7 @@ class TileResource : RulesetStatsObject() {
|
|||||||
var resourceType: ResourceType = ResourceType.Bonus
|
var resourceType: ResourceType = ResourceType.Bonus
|
||||||
var terrainsCanBeFoundOn: List<String> = listOf()
|
var terrainsCanBeFoundOn: List<String> = listOf()
|
||||||
var improvement: String? = null
|
var improvement: String? = null
|
||||||
|
/** stats that this resource adds to a tile */
|
||||||
var improvementStats: Stats? = null
|
var improvementStats: Stats? = null
|
||||||
var revealedBy: String? = null
|
var revealedBy: String? = null
|
||||||
var improvedBy: List<String> = listOf()
|
var improvedBy: List<String> = listOf()
|
||||||
@ -91,7 +92,7 @@ class TileResource : RulesetStatsObject() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val buildingsThatConsumeThis = ruleset.buildings.values.filter { it.getResourceRequirements().containsKey(name) }
|
val buildingsThatConsumeThis = ruleset.buildings.values.filter { it.getResourceRequirementsPerTurn().containsKey(name) }
|
||||||
if (buildingsThatConsumeThis.isNotEmpty()) {
|
if (buildingsThatConsumeThis.isNotEmpty()) {
|
||||||
textList += FormattedLine()
|
textList += FormattedLine()
|
||||||
textList += FormattedLine("{Buildings that consume this resource}:")
|
textList += FormattedLine("{Buildings that consume this resource}:")
|
||||||
@ -100,7 +101,7 @@ class TileResource : RulesetStatsObject() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val unitsThatConsumeThis = ruleset.units.values.filter { it.getResourceRequirements().containsKey(name) }
|
val unitsThatConsumeThis = ruleset.units.values.filter { it.getResourceRequirementsPerTurn().containsKey(name) }
|
||||||
if (unitsThatConsumeThis.isNotEmpty()) {
|
if (unitsThatConsumeThis.isNotEmpty()) {
|
||||||
textList += FormattedLine()
|
textList += FormattedLine()
|
||||||
textList += FormattedLine("{Units that consume this resource}: ")
|
textList += FormattedLine("{Units that consume this resource}: ")
|
||||||
@ -135,6 +136,8 @@ class TileResource : RulesetStatsObject() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isStockpiled() = hasUnique(UniqueType.Stockpiled)
|
||||||
|
|
||||||
class DepositAmount {
|
class DepositAmount {
|
||||||
var sparse: Int = 1
|
var sparse: Int = 1
|
||||||
var default: Int = 2
|
var default: Int = 2
|
||||||
|
@ -330,6 +330,38 @@ object UniqueTriggerActivation {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniqueType.OneTimeProvideResources -> {
|
||||||
|
val amount = unique.params[0].toInt()
|
||||||
|
val resourceName = unique.params[1]
|
||||||
|
val resource = ruleSet.tileResources[resourceName] ?: return false
|
||||||
|
if (!resource.isStockpiled()) return false
|
||||||
|
|
||||||
|
civInfo.resourceStockpiles.add(resourceName, amount)
|
||||||
|
|
||||||
|
val notificationText = getNotificationText(notification, triggerNotificationText,
|
||||||
|
"You have gained [$amount] [$resourceName]")
|
||||||
|
?: return true
|
||||||
|
|
||||||
|
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Science, "ResourceIcons/$resourceName")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueType.OneTimeConsumeResources -> {
|
||||||
|
val amount = unique.params[0].toInt()
|
||||||
|
val resourceName = unique.params[1]
|
||||||
|
val resource = ruleSet.tileResources[resourceName] ?: return false
|
||||||
|
if (!resource.isStockpiled()) return false
|
||||||
|
|
||||||
|
civInfo.resourceStockpiles.add(resourceName, amount)
|
||||||
|
|
||||||
|
val notificationText = getNotificationText(notification, triggerNotificationText,
|
||||||
|
"You have lost [$amount] [$resourceName]")
|
||||||
|
?: return true
|
||||||
|
|
||||||
|
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Science, "ResourceIcons/$resourceName")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
UniqueType.OneTimeRevealEntireMap -> {
|
UniqueType.OneTimeRevealEntireMap -> {
|
||||||
if (notification != null) {
|
if (notification != null) {
|
||||||
civInfo.addNotification(notification, LocationAction(tile?.position), NotificationCategory.General, NotificationIcon.Scout)
|
civInfo.addNotification(notification, LocationAction(tile?.position), NotificationCategory.General, NotificationIcon.Scout)
|
||||||
@ -366,6 +398,21 @@ object UniqueTriggerActivation {
|
|||||||
return promotedUnitLocations.isNotEmpty()
|
return promotedUnitLocations.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mechanics for granting great people are wonky, but basically the following happens:
|
||||||
|
* Based on the game speed, a timer with some amount of turns is set, 40 on regular speed
|
||||||
|
* Every turn, 1 is subtracted from this timer, as long as you have at least 1 city state ally
|
||||||
|
* So no, the number of city-state allies does not matter for this. You have a global timer for all of them combined.
|
||||||
|
* If the timer reaches the amount of city-state allies you have (or 10, whichever is lower), it is reset.
|
||||||
|
* You will then receive a random great person from a random city-state you are allied to
|
||||||
|
* The very first time after acquiring this policy, the timer is set to half of its normal value
|
||||||
|
* This is the basics, and apart from this, there is some randomness in the exact turn count, but I don't know how much
|
||||||
|
* There is surprisingly little information findable online about this policy, and the civ 5 source files are
|
||||||
|
* also quite though to search through, so this might all be incorrect.
|
||||||
|
* For now this mechanic seems decent enough that this is fine.
|
||||||
|
* Note that the way this is implemented now, this unique does NOT stack
|
||||||
|
* I could parametrize the [Allied], but eh.
|
||||||
|
*/
|
||||||
UniqueType.CityStateCanGiftGreatPeople -> {
|
UniqueType.CityStateCanGiftGreatPeople -> {
|
||||||
civInfo.addFlag(
|
civInfo.addFlag(
|
||||||
CivFlags.CityStateGreatPersonGift.name,
|
CivFlags.CityStateGreatPersonGift.name,
|
||||||
@ -376,21 +423,6 @@ object UniqueTriggerActivation {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// The mechanics for granting great people are wonky, but basically the following happens:
|
|
||||||
// Based on the game speed, a timer with some amount of turns is set, 40 on regular speed
|
|
||||||
// Every turn, 1 is subtracted from this timer, as long as you have at least 1 city state ally
|
|
||||||
// So no, the number of city-state allies does not matter for this. You have a global timer for all of them combined.
|
|
||||||
// If the timer reaches the amount of city-state allies you have (or 10, whichever is lower), it is reset.
|
|
||||||
// You will then receive a random great person from a random city-state you are allied to
|
|
||||||
// The very first time after acquiring this policy, the timer is set to half of its normal value
|
|
||||||
// This is the basics, and apart from this, there is some randomness in the exact turn count, but I don't know how much
|
|
||||||
|
|
||||||
// There is surprisingly little information findable online about this policy, and the civ 5 source files are
|
|
||||||
// also quite though to search through, so this might all be incorrect.
|
|
||||||
// For now this mechanic seems decent enough that this is fine.
|
|
||||||
|
|
||||||
// Note that the way this is implemented now, this unique does NOT stack
|
|
||||||
// I could parametrize the [Allied], but eh.
|
|
||||||
|
|
||||||
UniqueType.OneTimeGainStat -> {
|
UniqueType.OneTimeGainStat -> {
|
||||||
val stat = Stat.safeValueOf(unique.params[1]) ?: return false
|
val stat = Stat.safeValueOf(unique.params[1]) ?: return false
|
||||||
@ -414,6 +446,7 @@ object UniqueTriggerActivation {
|
|||||||
civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.General, stat.notificationIcon)
|
civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.General, stat.notificationIcon)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
UniqueType.OneTimeGainStatRange -> {
|
UniqueType.OneTimeGainStatRange -> {
|
||||||
val stat = Stat.safeValueOf(unique.params[2]) ?: return false
|
val stat = Stat.safeValueOf(unique.params[2]) ?: return false
|
||||||
|
|
||||||
|
@ -155,6 +155,9 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
|||||||
ConsumesResources("Consumes [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit),
|
ConsumesResources("Consumes [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit),
|
||||||
ProvidesResources("Provides [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Global),
|
ProvidesResources("Provides [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Global),
|
||||||
|
|
||||||
|
/** For stockpiled resources */
|
||||||
|
CostsResources("Costs [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit),
|
||||||
|
|
||||||
GrowthPercentBonus("[relativeAmount]% growth [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
|
GrowthPercentBonus("[relativeAmount]% growth [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
|
||||||
CarryOverFood("[relativeAmount]% Food is carried over after population increases [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
|
CarryOverFood("[relativeAmount]% Food is carried over after population increases [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
|
||||||
|
|
||||||
@ -579,6 +582,9 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
|||||||
/////// Resource uniques
|
/////// Resource uniques
|
||||||
ResourceAmountOnTiles("Deposits in [tileFilter] tiles always provide [amount] resources", UniqueTarget.Resource),
|
ResourceAmountOnTiles("Deposits in [tileFilter] tiles always provide [amount] resources", UniqueTarget.Resource),
|
||||||
CityStateOnlyResource("Can only be created by Mercantile City-States", UniqueTarget.Resource),
|
CityStateOnlyResource("Can only be created by Mercantile City-States", UniqueTarget.Resource),
|
||||||
|
Stockpiled("Stockpiled", UniqueTarget.Resource),
|
||||||
|
CannotBeTraded("Cannot be traded", UniqueTarget.Resource),
|
||||||
|
NotShownOnWorldScreen("Not shown on world screen", UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers),
|
||||||
|
|
||||||
ResourceWeighting("Generated with weight [amount]", UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers),
|
ResourceWeighting("Generated with weight [amount]", UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers),
|
||||||
MinorDepositWeighting("Minor deposits generated with weight [amount]", UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers),
|
MinorDepositWeighting("Minor deposits generated with weight [amount]", UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers),
|
||||||
@ -724,7 +730,12 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
|||||||
OneTimeFreeBelief("Gain a free [beliefType] belief", UniqueTarget.Triggerable),
|
OneTimeFreeBelief("Gain a free [beliefType] belief", UniqueTarget.Triggerable),
|
||||||
OneTimeTriggerVoting("Triggers voting for the Diplomatic Victory", UniqueTarget.Triggerable), // used in Building
|
OneTimeTriggerVoting("Triggers voting for the Diplomatic Victory", UniqueTarget.Triggerable), // used in Building
|
||||||
|
|
||||||
OneTimeGainStat("Gain [amount] [stat]", UniqueTarget.Triggerable),
|
/** For stockpiled resources */
|
||||||
|
OneTimeConsumeResources("Instantly consumes [amount] [resource]", UniqueTarget.Triggerable),
|
||||||
|
/** For stockpiled resources */
|
||||||
|
OneTimeProvideResources("Instantly provides [amount] [resource]", UniqueTarget.Triggerable),
|
||||||
|
|
||||||
|
OneTimeGainStat("Gain [amount] [stat/resource]", UniqueTarget.Triggerable),
|
||||||
OneTimeGainStatRange("Gain [amount]-[amount] [stat]", UniqueTarget.Triggerable),
|
OneTimeGainStatRange("Gain [amount]-[amount] [stat]", UniqueTarget.Triggerable),
|
||||||
OneTimeGainPantheon("Gain enough Faith for a Pantheon", UniqueTarget.Triggerable),
|
OneTimeGainPantheon("Gain enough Faith for a Pantheon", UniqueTarget.Triggerable),
|
||||||
OneTimeGainProphet("Gain enough Faith for [amount]% of a Great Prophet", UniqueTarget.Triggerable),
|
OneTimeGainProphet("Gain enough Faith for [amount]% of a Great Prophet", UniqueTarget.Triggerable),
|
||||||
|
@ -175,7 +175,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!civ.isBarbarian()) { // Barbarians don't need resources
|
if (!civ.isBarbarian()) { // Barbarians don't need resources
|
||||||
for ((resource, requiredAmount) in getResourceRequirements()) {
|
for ((resource, requiredAmount) in getResourceRequirementsPerTurn()) {
|
||||||
val availableAmount = civ.getCivResourcesByName()[resource]!!
|
val availableAmount = civ.getCivResourcesByName()[resource]!!
|
||||||
if (availableAmount < requiredAmount) {
|
if (availableAmount < requiredAmount) {
|
||||||
result.add(
|
result.add(
|
||||||
@ -317,7 +317,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
|||||||
fun movesLikeAirUnits() = type.getMovementType() == UnitMovementType.Air
|
fun movesLikeAirUnits() = type.getMovementType() == UnitMovementType.Air
|
||||||
|
|
||||||
/** Returns resource requirements from both uniques and requiredResource field */
|
/** Returns resource requirements from both uniques and requiredResource field */
|
||||||
override fun getResourceRequirements(): HashMap<String, Int> = resourceRequirementsInternal
|
override fun getResourceRequirementsPerTurn(): HashMap<String, Int> = resourceRequirementsInternal
|
||||||
|
|
||||||
private val resourceRequirementsInternal: HashMap<String, Int> by lazy {
|
private val resourceRequirementsInternal: HashMap<String, Int> by lazy {
|
||||||
val resourceRequirements = HashMap<String, Int>()
|
val resourceRequirements = HashMap<String, Int>()
|
||||||
@ -327,7 +327,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
|||||||
resourceRequirements
|
resourceRequirements
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun requiresResource(resource: String) = getResourceRequirements().containsKey(resource)
|
override fun requiresResource(resource: String) = getResourceRequirementsPerTurn().containsKey(resource)
|
||||||
|
|
||||||
fun isRanged() = rangedStrength > 0
|
fun isRanged() = rangedStrength > 0
|
||||||
fun isMelee() = !isRanged() && strength > 0
|
fun isMelee() = !isRanged() && strength > 0
|
||||||
|
@ -2,6 +2,7 @@ package com.unciv.ui.components.extensions
|
|||||||
|
|
||||||
import com.badlogic.gdx.math.Vector2
|
import com.badlogic.gdx.math.Vector2
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
|
import com.unciv.ui.components.Fonts
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
@ -17,7 +18,11 @@ fun Int.toPercent() = toFloat().toPercent()
|
|||||||
fun Float.toPercent() = 1 + this/100
|
fun Float.toPercent() = 1 + this/100
|
||||||
|
|
||||||
/** Convert a [resource name][this] into "Consumes [amount] $resource" string (untranslated) */
|
/** Convert a [resource name][this] into "Consumes [amount] $resource" string (untranslated) */
|
||||||
fun String.getConsumesAmountString(amount: Int) = "Consumes [$amount] [$this]"
|
fun String.getConsumesAmountString(amount: Int, isStockpiled:Boolean): String {
|
||||||
|
val uniqueString = "{Consumes [$amount] [$this]}"
|
||||||
|
if (!isStockpiled) return uniqueString
|
||||||
|
else return "$uniqueString /${Fonts.turn}"
|
||||||
|
}
|
||||||
|
|
||||||
/** Convert a [resource name][this] into "Need [amount] more $resource" string (untranslated) */
|
/** Convert a [resource name][this] into "Need [amount] more $resource" string (untranslated) */
|
||||||
fun String.getNeedMoreAmountString(amount: Int) = "Need [$amount] more [$this]"
|
fun String.getNeedMoreAmountString(amount: Int) = "Need [$amount] more [$this]"
|
||||||
@ -34,7 +39,7 @@ fun Duration.format(): String {
|
|||||||
if (firstPartAlreadyAdded) {
|
if (firstPartAlreadyAdded) {
|
||||||
sb.append(", ")
|
sb.append(", ")
|
||||||
}
|
}
|
||||||
sb.append("[${part}] $unit")
|
sb.append("[$part] $unit")
|
||||||
firstPartAlreadyAdded = true
|
firstPartAlreadyAdded = true
|
||||||
}
|
}
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
|
@ -9,10 +9,10 @@ import com.unciv.models.ruleset.unit.UnitMovementType
|
|||||||
import com.unciv.models.ruleset.unit.UnitType
|
import com.unciv.models.ruleset.unit.UnitType
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
|
||||||
import com.unciv.ui.components.Fonts
|
import com.unciv.ui.components.Fonts
|
||||||
import com.unciv.ui.components.extensions.getConsumesAmountString
|
import com.unciv.ui.components.extensions.getConsumesAmountString
|
||||||
import com.unciv.ui.components.extensions.toPercent
|
import com.unciv.ui.components.extensions.toPercent
|
||||||
|
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
object BaseUnitDescriptions {
|
object BaseUnitDescriptions {
|
||||||
@ -35,10 +35,12 @@ object BaseUnitDescriptions {
|
|||||||
* @param city Supplies civInfo to show available resources after resource requirements */
|
* @param city Supplies civInfo to show available resources after resource requirements */
|
||||||
fun getDescription(baseUnit: BaseUnit, city: City): String {
|
fun getDescription(baseUnit: BaseUnit, city: City): String {
|
||||||
val lines = mutableListOf<String>()
|
val lines = mutableListOf<String>()
|
||||||
val availableResources = city.civ.getCivResources().associate { it.resource.name to it.amount }
|
val availableResources = city.civ.getCivResourcesByName()
|
||||||
for ((resource, amount) in baseUnit.getResourceRequirements()) {
|
for ((resourceName, amount) in baseUnit.getResourceRequirementsPerTurn()) {
|
||||||
val available = availableResources[resource] ?: 0
|
val available = availableResources[resourceName] ?: 0
|
||||||
lines += "{${resource.getConsumesAmountString(amount)}} ({[$available] available})".tr()
|
val resource = baseUnit.ruleset.tileResources[resourceName] ?: continue
|
||||||
|
val consumesString = resourceName.getConsumesAmountString(amount, resource.isStockpiled())
|
||||||
|
lines += "$consumesString ({[$available] available})".tr()
|
||||||
}
|
}
|
||||||
var strengthLine = ""
|
var strengthLine = ""
|
||||||
if (baseUnit.strength != 0) {
|
if (baseUnit.strength != 0) {
|
||||||
@ -90,7 +92,7 @@ object BaseUnitDescriptions {
|
|||||||
val buyCost = (30.0 * baseUnit.cost.toFloat().pow(0.75f) * baseUnit.hurryCostModifier.toPercent()).toInt() / 10 * 10
|
val buyCost = (30.0 * baseUnit.cost.toFloat().pow(0.75f) * baseUnit.hurryCostModifier.toPercent()).toInt() / 10 * 10
|
||||||
stats += "$buyCost${Fonts.gold}"
|
stats += "$buyCost${Fonts.gold}"
|
||||||
}
|
}
|
||||||
textList += FormattedLine(stats.joinToString(", ", "{Cost}: "))
|
textList += FormattedLine(stats.joinToString("/", "{Cost}: "))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baseUnit.replacementTextForUniques.isNotEmpty()) {
|
if (baseUnit.replacementTextForUniques.isNotEmpty()) {
|
||||||
@ -105,12 +107,13 @@ object BaseUnitDescriptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val resourceRequirements = baseUnit.getResourceRequirements()
|
val resourceRequirements = baseUnit.getResourceRequirementsPerTurn()
|
||||||
if (resourceRequirements.isNotEmpty()) {
|
if (resourceRequirements.isNotEmpty()) {
|
||||||
textList += FormattedLine()
|
textList += FormattedLine()
|
||||||
for ((resource, amount) in resourceRequirements) {
|
for ((resourceName, amount) in resourceRequirements) {
|
||||||
|
val resource = ruleset.tileResources[resourceName] ?: continue
|
||||||
textList += FormattedLine(
|
textList += FormattedLine(
|
||||||
resource.getConsumesAmountString(amount),
|
resourceName.getConsumesAmountString(amount, resource.isStockpiled()),
|
||||||
link = "Resource/$resource", color = "#F42"
|
link = "Resource/$resource", color = "#F42"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
|
|
||||||
val useStoredProduction = entry is Building || !cityConstructions.isBeingConstructedOrEnqueued(entry.name)
|
val useStoredProduction = entry is Building || !cityConstructions.isBeingConstructedOrEnqueued(entry.name)
|
||||||
val buttonText = cityConstructions.getTurnsToConstructionString(entry.name, useStoredProduction).trim()
|
val buttonText = cityConstructions.getTurnsToConstructionString(entry.name, useStoredProduction).trim()
|
||||||
val resourcesRequired = entry.getResourceRequirements()
|
val resourcesRequired = entry.getResourceRequirementsPerTurn()
|
||||||
val mostImportantRejection =
|
val mostImportantRejection =
|
||||||
entry.getRejectionReasons(cityConstructions)
|
entry.getRejectionReasons(cityConstructions)
|
||||||
.filter { it.isImportantRejection() }
|
.filter { it.isImportantRejection() }
|
||||||
@ -317,9 +317,11 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
if (constructionName in PerpetualConstruction.perpetualConstructionsMap) "\n∞"
|
if (constructionName in PerpetualConstruction.perpetualConstructionsMap) "\n∞"
|
||||||
else cityConstructions.getTurnsToConstructionString(constructionName, isFirstConstructionOfItsKind)
|
else cityConstructions.getTurnsToConstructionString(constructionName, isFirstConstructionOfItsKind)
|
||||||
|
|
||||||
val constructionResource = cityConstructions.getConstruction(constructionName).getResourceRequirements()
|
val constructionResource = cityConstructions.getConstruction(constructionName).getResourceRequirementsPerTurn()
|
||||||
for ((resource, amount) in constructionResource)
|
for ((resourceName, amount) in constructionResource) {
|
||||||
text += "\n" + resource.getConsumesAmountString(amount).tr()
|
val resource = cityConstructions.city.getRuleset().tileResources[resourceName] ?: continue
|
||||||
|
text += "\n" + resourceName.getConsumesAmountString(amount, resource.isStockpiled()).tr()
|
||||||
|
}
|
||||||
|
|
||||||
table.defaults().pad(2f).minWidth(40f)
|
table.defaults().pad(2f).minWidth(40f)
|
||||||
if (isFirstConstructionOfItsKind) table.add(getProgressBar(constructionName)).minWidth(5f)
|
if (isFirstConstructionOfItsKind) table.add(getProgressBar(constructionName)).minWidth(5f)
|
||||||
@ -396,6 +398,12 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
resourceTable.add(ImageGetter.getResourcePortrait(resource, 15f)).padBottom(1f)
|
resourceTable.add(ImageGetter.getResourcePortrait(resource, 15f)).padBottom(1f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (unique in constructionButtonDTO.construction.getMatchingUniquesNotConflicting(UniqueType.CostsResources)){
|
||||||
|
val color = if (constructionButtonDTO.rejectionReason?.type == RejectionReasonType.ConsumesResources)
|
||||||
|
Color.RED else Color.WHITE
|
||||||
|
resourceTable.add(unique.params[0].toLabel(fontColor = color)).expandX().left().padLeft(5f)
|
||||||
|
resourceTable.add(ImageGetter.getResourcePortrait(unique.params[1], 15f)).padBottom(1f)
|
||||||
|
}
|
||||||
constructionTable.add(resourceTable).expandX().left()
|
constructionTable.add(resourceTable).expandX().left()
|
||||||
|
|
||||||
pickConstructionButton.add(constructionTable).expandX().left()
|
pickConstructionButton.add(constructionTable).expandX().left()
|
||||||
|
@ -11,10 +11,6 @@ import com.unciv.models.ruleset.tile.ResourceSupplyList
|
|||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import com.unciv.models.ruleset.tile.TileResource
|
import com.unciv.models.ruleset.tile.TileResource
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.screens.civilopediascreen.CivilopediaCategories
|
|
||||||
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
|
|
||||||
import com.unciv.ui.images.ImageGetter
|
|
||||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
|
||||||
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
|
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
|
||||||
import com.unciv.ui.components.extensions.addSeparator
|
import com.unciv.ui.components.extensions.addSeparator
|
||||||
import com.unciv.ui.components.extensions.addSeparatorVertical
|
import com.unciv.ui.components.extensions.addSeparatorVertical
|
||||||
@ -22,6 +18,10 @@ import com.unciv.ui.components.extensions.onClick
|
|||||||
import com.unciv.ui.components.extensions.pad
|
import com.unciv.ui.components.extensions.pad
|
||||||
import com.unciv.ui.components.extensions.surroundWithCircle
|
import com.unciv.ui.components.extensions.surroundWithCircle
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
|
import com.unciv.ui.images.ImageGetter
|
||||||
|
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||||
|
import com.unciv.ui.screens.civilopediascreen.CivilopediaCategories
|
||||||
|
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
|
||||||
|
|
||||||
|
|
||||||
class ResourcesOverviewTab(
|
class ResourcesOverviewTab(
|
||||||
@ -73,10 +73,16 @@ class ResourcesOverviewTab(
|
|||||||
private val extraOrigins: List<ExtraInfoOrigin> = extraDrilldown.asSequence()
|
private val extraOrigins: List<ExtraInfoOrigin> = extraDrilldown.asSequence()
|
||||||
.mapNotNull { ExtraInfoOrigin.safeValueOf(it.origin) }.distinct().toList()
|
.mapNotNull { ExtraInfoOrigin.safeValueOf(it.origin) }.distinct().toList()
|
||||||
|
|
||||||
private fun ResourceSupplyList.getLabel(resource: TileResource, origin: String): Label? =
|
private fun ResourceSupplyList.getLabel(resource: TileResource, origin: String): Label? {
|
||||||
get(resource, origin)?.amount?.toLabel()
|
val amount = get(resource, origin)?.amount ?: return null
|
||||||
private fun ResourceSupplyList.getTotalLabel(resource: TileResource): Label =
|
return if (resource.isStockpiled() && amount > 0) "+$amount".toLabel()
|
||||||
filter { it.resource == resource }.sumOf { it.amount }.toLabel()
|
else amount.toLabel()
|
||||||
|
}
|
||||||
|
private fun ResourceSupplyList.getTotalLabel(resource: TileResource): Label {
|
||||||
|
val total = filter { it.resource == resource }.sumOf { it.amount }
|
||||||
|
return if (resource.isStockpiled() && total > 0) "+$total".toLabel()
|
||||||
|
else total.toLabel()
|
||||||
|
}
|
||||||
private fun getResourceImage(name: String) =
|
private fun getResourceImage(name: String) =
|
||||||
ImageGetter.getResourcePortrait(name, iconSize).apply {
|
ImageGetter.getResourcePortrait(name, iconSize).apply {
|
||||||
onClick {
|
onClick {
|
||||||
|
@ -12,6 +12,7 @@ import com.badlogic.gdx.utils.Align
|
|||||||
import com.unciv.logic.civilization.Civilization
|
import com.unciv.logic.civilization.Civilization
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import com.unciv.models.ruleset.tile.TileResource
|
import com.unciv.models.ruleset.tile.TileResource
|
||||||
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.stats.Stats
|
import com.unciv.models.stats.Stats
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.components.Fonts
|
import com.unciv.ui.components.Fonts
|
||||||
@ -24,6 +25,7 @@ import com.unciv.ui.components.extensions.onClick
|
|||||||
import com.unciv.ui.components.extensions.setFontColor
|
import com.unciv.ui.components.extensions.setFontColor
|
||||||
import com.unciv.ui.components.extensions.setFontSize
|
import com.unciv.ui.components.extensions.setFontSize
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
|
import com.unciv.ui.components.extensions.toStringSigned
|
||||||
import com.unciv.ui.components.extensions.toTextButton
|
import com.unciv.ui.components.extensions.toTextButton
|
||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
import com.unciv.ui.popups.popups
|
import com.unciv.ui.popups.popups
|
||||||
@ -347,14 +349,23 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
|
|||||||
turnsLabel.setText(Fonts.turn + "" + civInfo.gameInfo.turns + " | " + yearText)
|
turnsLabel.setText(Fonts.turn + "" + civInfo.gameInfo.turns + " | " + yearText)
|
||||||
resourcesWrapper.clearChildren()
|
resourcesWrapper.clearChildren()
|
||||||
var firstPadLeft = 20f // We want a distance from the turns entry to the first resource, but only if any resource is displayed
|
var firstPadLeft = 20f // We want a distance from the turns entry to the first resource, but only if any resource is displayed
|
||||||
val civResources = civInfo.getCivResources()
|
val civResources = civInfo.getCivResourcesByName()
|
||||||
|
val civResourceSupply = civInfo.getCivResourceSupply()
|
||||||
for ((resource, label, icon) in resourceActors) {
|
for ((resource, label, icon) in resourceActors) {
|
||||||
if (resource.revealedBy != null && !civInfo.tech.isResearched(resource.revealedBy!!))
|
if (resource.revealedBy != null && !civInfo.tech.isResearched(resource.revealedBy!!))
|
||||||
continue
|
continue
|
||||||
|
if (resource.hasUnique(UniqueType.NotShownOnWorldScreen)) continue
|
||||||
|
|
||||||
resourcesWrapper.add(icon).padLeft(firstPadLeft).padRight(0f)
|
resourcesWrapper.add(icon).padLeft(firstPadLeft).padRight(0f)
|
||||||
firstPadLeft = 5f
|
firstPadLeft = 5f
|
||||||
val amount = civResources.get(resource, "All")?.amount ?: 0
|
val amount = civResources[resource.name] ?: 0
|
||||||
label.setText(amount)
|
if (!resource.isStockpiled())
|
||||||
|
label.setText(amount)
|
||||||
|
else {
|
||||||
|
val perTurn = civResourceSupply.firstOrNull { it.resource == resource }?.amount ?: 0
|
||||||
|
if (perTurn == 0) label.setText(amount)
|
||||||
|
else label.setText("$amount (${perTurn.toStringSigned()})")
|
||||||
|
}
|
||||||
resourcesWrapper.add(label).padTop(8f) // digits don't have descenders, so push them down a little
|
resourcesWrapper.add(label).padTop(8f) // digits don't have descenders, so push them down a little
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,9 +336,9 @@ object UnitActions {
|
|||||||
// Check _new_ resource requirements
|
// Check _new_ resource requirements
|
||||||
// Using Counter to aggregate is a bit exaggerated, but - respect the mad modder.
|
// Using Counter to aggregate is a bit exaggerated, but - respect the mad modder.
|
||||||
val resourceRequirementsDelta = Counter<String>()
|
val resourceRequirementsDelta = Counter<String>()
|
||||||
for ((resource, amount) in unit.baseUnit().getResourceRequirements())
|
for ((resource, amount) in unit.baseUnit().getResourceRequirementsPerTurn())
|
||||||
resourceRequirementsDelta.add(resource, -amount)
|
resourceRequirementsDelta.add(resource, -amount)
|
||||||
for ((resource, amount) in upgradedUnit.getResourceRequirements())
|
for ((resource, amount) in upgradedUnit.getResourceRequirementsPerTurn())
|
||||||
resourceRequirementsDelta.add(resource, amount)
|
resourceRequirementsDelta.add(resource, amount)
|
||||||
val newResourceRequirementsString = resourceRequirementsDelta.entries
|
val newResourceRequirementsString = resourceRequirementsDelta.entries
|
||||||
.filter { it.value > 0 }
|
.filter { it.value > 0 }
|
||||||
|
@ -42,9 +42,9 @@ object UnitActionsUpgrade{
|
|||||||
// Check _new_ resource requirements (display only - yes even for free or special upgrades)
|
// 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.
|
// Using Counter to aggregate is a bit exaggerated, but - respect the mad modder.
|
||||||
val resourceRequirementsDelta = Counter<String>()
|
val resourceRequirementsDelta = Counter<String>()
|
||||||
for ((resource, amount) in unit.baseUnit().getResourceRequirements())
|
for ((resource, amount) in unit.baseUnit().getResourceRequirementsPerTurn())
|
||||||
resourceRequirementsDelta.add(resource, -amount)
|
resourceRequirementsDelta.add(resource, -amount)
|
||||||
for ((resource, amount) in upgradedUnit.getResourceRequirements())
|
for ((resource, amount) in upgradedUnit.getResourceRequirementsPerTurn())
|
||||||
resourceRequirementsDelta.add(resource, amount)
|
resourceRequirementsDelta.add(resource, amount)
|
||||||
val newResourceRequirementsString = resourceRequirementsDelta.entries
|
val newResourceRequirementsString = resourceRequirementsDelta.entries
|
||||||
.filter { it.value > 0 }
|
.filter { it.value > 0 }
|
||||||
|
@ -74,7 +74,17 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
|||||||
??? example "Triggers voting for the Diplomatic Victory"
|
??? example "Triggers voting for the Diplomatic Victory"
|
||||||
Applicable to: Triggerable
|
Applicable to: Triggerable
|
||||||
|
|
||||||
??? example "Gain [amount] [stat]"
|
??? example "Instantly consumes [amount] [resource]"
|
||||||
|
Example: "Instantly consumes [3] [Iron]"
|
||||||
|
|
||||||
|
Applicable to: Triggerable
|
||||||
|
|
||||||
|
??? example "Instantly provides [amount] [resource]"
|
||||||
|
Example: "Instantly provides [3] [Iron]"
|
||||||
|
|
||||||
|
Applicable to: Triggerable
|
||||||
|
|
||||||
|
??? example "Gain [amount] [stat/resource]"
|
||||||
Example: "Gain [3] [Culture]"
|
Example: "Gain [3] [Culture]"
|
||||||
|
|
||||||
Applicable to: Triggerable
|
Applicable to: Triggerable
|
||||||
@ -914,6 +924,11 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
|||||||
|
|
||||||
Applicable to: Building, Unit, Improvement
|
Applicable to: Building, Unit, Improvement
|
||||||
|
|
||||||
|
??? example "Costs [amount] [resource]"
|
||||||
|
Example: "Costs [3] [Iron]"
|
||||||
|
|
||||||
|
Applicable to: Building, Unit, Improvement
|
||||||
|
|
||||||
??? example "Unbuildable"
|
??? example "Unbuildable"
|
||||||
Applicable to: Building, Unit, Improvement
|
Applicable to: Building, Unit, Improvement
|
||||||
|
|
||||||
@ -1601,6 +1616,12 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
|||||||
??? example "Can only be created by Mercantile City-States"
|
??? example "Can only be created by Mercantile City-States"
|
||||||
Applicable to: Resource
|
Applicable to: Resource
|
||||||
|
|
||||||
|
??? example "Stockpiled"
|
||||||
|
Applicable to: Resource
|
||||||
|
|
||||||
|
??? example "Not shown on world screen"
|
||||||
|
Applicable to: Resource
|
||||||
|
|
||||||
??? example "Generated with weight [amount]"
|
??? example "Generated with weight [amount]"
|
||||||
Example: "Generated with weight [3]"
|
Example: "Generated with weight [3]"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user