mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-30 06:39:17 +07:00
Resolve To-do in Unit upgrade resource requirements (#9420)
* Update Counter get() nullability * Counter upgrade * Use Counter for unit resource requirements * Linting
This commit is contained in:
@ -279,9 +279,9 @@ object Automation {
|
||||
for (city in civInfo.cities) {
|
||||
val otherConstruction = city.cityConstructions.getCurrentConstruction()
|
||||
if (otherConstruction is Building)
|
||||
futureForBuildings += otherConstruction.getResourceRequirementsPerTurn()[resource] ?: 0
|
||||
futureForBuildings += otherConstruction.getResourceRequirementsPerTurn()[resource]
|
||||
else
|
||||
futureForUnits += otherConstruction.getResourceRequirementsPerTurn()[resource] ?: 0
|
||||
futureForUnits += otherConstruction.getResourceRequirementsPerTurn()[resource]
|
||||
}
|
||||
|
||||
// Make sure we have some for space
|
||||
|
@ -688,7 +688,7 @@ object NextTurnAutomation {
|
||||
// not have used a great prophet to found/enhance our religion.
|
||||
for (belief in BeliefType.values()) {
|
||||
if (belief == BeliefType.None) continue
|
||||
repeat(beliefsToChoose[belief] ?: 0) {
|
||||
repeat(beliefsToChoose[belief]) {
|
||||
chosenBeliefs.add(
|
||||
chooseBeliefOfType(civInfo, belief, chosenBeliefs) ?: return@repeat
|
||||
)
|
||||
|
@ -244,7 +244,7 @@ class City : IsPartOfGameInfoSerialization {
|
||||
return cityResources
|
||||
}
|
||||
|
||||
fun getTileResourceAmount(tile: Tile): Int {
|
||||
private fun getTileResourceAmount(tile: Tile): Int {
|
||||
if (tile.resource == null) return 0
|
||||
if (!tile.providesResources(civ)) return 0
|
||||
|
||||
@ -308,13 +308,13 @@ class City : IsPartOfGameInfoSerialization {
|
||||
for (unique in civ.getMatchingUniques(UniqueType.GreatPersonEarnedFaster, stateForConditionals)) {
|
||||
val unitName = unique.params[0]
|
||||
if (!gppCounter.containsKey(unitName)) continue
|
||||
gppCounter.add(unitName, gppCounter[unitName]!! * unique.params[1].toInt() / 100)
|
||||
gppCounter.add(unitName, gppCounter[unitName] * unique.params[1].toInt() / 100)
|
||||
}
|
||||
|
||||
val allGppPercentageBonus = getGreatPersonPercentageBonus()
|
||||
|
||||
for (unitName in gppCounter.keys)
|
||||
gppCounter.add(unitName, gppCounter[unitName]!! * allGppPercentageBonus / 100)
|
||||
gppCounter.add(unitName, gppCounter[unitName] * allGppPercentageBonus / 100)
|
||||
}
|
||||
|
||||
return sourceToGPP
|
||||
|
@ -206,8 +206,8 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
|
||||
val maxSpecialists = getMaxSpecialists()
|
||||
val specialistsHashmap = specialistAllocations
|
||||
for ((specialistName, amount) in specialistsHashmap)
|
||||
if (amount > maxSpecialists[specialistName]!!)
|
||||
specialistAllocations[specialistName] = maxSpecialists[specialistName]!!
|
||||
if (amount > maxSpecialists[specialistName])
|
||||
specialistAllocations[specialistName] = maxSpecialists[specialistName]
|
||||
|
||||
val localUniqueCache = LocalUniqueCache()
|
||||
|
||||
|
@ -17,7 +17,7 @@ class CityReligionManager : IsPartOfGameInfoSerialization {
|
||||
|
||||
// This needs to be kept track of for the
|
||||
// "[Stats] when a city adopts this religion for the first time" unique
|
||||
val religionsAtSomePointAdopted: HashSet<String> = hashSetOf()
|
||||
private val religionsAtSomePointAdopted: HashSet<String> = hashSetOf()
|
||||
|
||||
private val pressures: Counter<String> = Counter()
|
||||
// Cached because using `updateNumberOfFollowers` to get this value resulted in many calls
|
||||
@ -79,18 +79,16 @@ class CityReligionManager : IsPartOfGameInfoSerialization {
|
||||
if (!city.civ.gameInfo.isReligionEnabled()) return // No religion, no pressures
|
||||
pressures.add(religionName, amount)
|
||||
|
||||
if (shouldUpdateFollowers) {
|
||||
updateNumberOfFollowers(shouldUpdateFollowers)
|
||||
}
|
||||
if (shouldUpdateFollowers) updateNumberOfFollowers()
|
||||
}
|
||||
|
||||
fun removeAllPressuresExceptFor(religion: String) {
|
||||
val pressureFromThisReligion = pressures[religion]!!
|
||||
val pressureFromThisReligion = pressures[religion]
|
||||
// Atheism is never removed
|
||||
val pressureFromAtheism = pressures[Constants.noReligionName]
|
||||
clearAllPressures()
|
||||
pressures.add(religion, pressureFromThisReligion)
|
||||
if (pressureFromAtheism != null) pressures[Constants.noReligionName] = pressureFromAtheism
|
||||
if (pressureFromAtheism != 0) pressures[Constants.noReligionName] = pressureFromAtheism
|
||||
updateNumberOfFollowers()
|
||||
}
|
||||
|
||||
@ -192,13 +190,13 @@ class CityReligionManager : IsPartOfGameInfoSerialization {
|
||||
return followers.clone()
|
||||
}
|
||||
|
||||
fun getFollowersOf(religion: String): Int? {
|
||||
fun getFollowersOf(religion: String): Int {
|
||||
return followers[religion]
|
||||
}
|
||||
|
||||
fun getFollowersOfMajorityReligion(): Int {
|
||||
val majorityReligion = getMajorityReligionName() ?: return 0
|
||||
return followers[majorityReligion]!!
|
||||
return followers[majorityReligion]
|
||||
}
|
||||
|
||||
fun getFollowersOfOtherReligionsThan(religion: String): Int {
|
||||
@ -226,7 +224,7 @@ class CityReligionManager : IsPartOfGameInfoSerialization {
|
||||
val religionWithMaxPressure = pressures.maxByOrNull { it.value }!!.key
|
||||
return when {
|
||||
religionWithMaxPressure == Constants.noReligionName -> null
|
||||
followers[religionWithMaxPressure]!! >= city.population.population / 2 -> religionWithMaxPressure
|
||||
followers[religionWithMaxPressure] >= city.population.population / 2 -> religionWithMaxPressure
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ class CivConstructions : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
|
||||
fun countConstructedObjects(objectToCount: INonPerpetualConstruction): Int {
|
||||
val amountInSpaceShip = civInfo.victoryManager.currentsSpaceshipParts[objectToCount.name] ?: 0
|
||||
val amountInSpaceShip = civInfo.victoryManager.currentsSpaceshipParts[objectToCount.name]
|
||||
|
||||
return amountInSpaceShip + when (objectToCount) {
|
||||
is Building -> civInfo.cities.count {
|
||||
|
@ -441,7 +441,11 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
|
||||
// Does not return local uniques, only global ones.
|
||||
/** Destined to replace getMatchingUniques, gradually, as we fill the enum */
|
||||
fun getMatchingUniques(uniqueType: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(this), cityToIgnore: City? = null) = sequence {
|
||||
fun getMatchingUniques(
|
||||
uniqueType: UniqueType,
|
||||
stateForConditionals: StateForConditionals = StateForConditionals(this),
|
||||
cityToIgnore: City? = null
|
||||
): Sequence<Unique> = sequence {
|
||||
yieldAll(nation.getMatchingUniques(uniqueType, stateForConditionals))
|
||||
yieldAll(cities.asSequence()
|
||||
.filter { it != cityToIgnore }
|
||||
@ -464,7 +468,10 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
yieldAll(gameInfo.ruleset.globalUniques.getMatchingUniques(uniqueType, stateForConditionals))
|
||||
}
|
||||
|
||||
fun getTriggeredUniques(trigger: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(this)) : Sequence<Unique> = sequence{
|
||||
fun getTriggeredUniques(
|
||||
trigger: UniqueType,
|
||||
stateForConditionals: StateForConditionals = StateForConditionals(this)
|
||||
) : Sequence<Unique> = sequence {
|
||||
yieldAll(nation.uniqueMap.getTriggeredUniques(trigger, stateForConditionals))
|
||||
yieldAll(cities.asSequence()
|
||||
.flatMap { city -> city.cityConstructions.builtBuildingUniqueMap.getTriggeredUniques(trigger, stateForConditionals) }
|
||||
|
@ -122,7 +122,7 @@ class ReligionManager : IsPartOfGameInfoSerialization {
|
||||
return false
|
||||
}
|
||||
return (religionState == ReligionState.None && storedFaith >= faithForPantheon()) // earned pantheon
|
||||
|| (freeBeliefs[BeliefType.Pantheon.name] != null && freeBeliefs[BeliefType.Pantheon.name]!! > 0) // free pantheon belief
|
||||
|| freeBeliefs[BeliefType.Pantheon.name] > 0 // free pantheon belief
|
||||
}
|
||||
|
||||
private fun foundPantheon(beliefName: String, useFreeBelief: Boolean) {
|
||||
@ -143,7 +143,7 @@ class ReligionManager : IsPartOfGameInfoSerialization {
|
||||
// https://www.reddit.com/r/civ/comments/2m82wu/can_anyone_detail_the_finer_points_of_great/
|
||||
// Game files (globaldefines.xml)
|
||||
fun faithForNextGreatProphet(): Int {
|
||||
val greatProphetsEarned = civInfo.civConstructions.boughtItemsWithIncreasingPrice[getGreatProphetEquivalent()!!] ?: 0
|
||||
val greatProphetsEarned = civInfo.civConstructions.boughtItemsWithIncreasingPrice[getGreatProphetEquivalent()!!]
|
||||
|
||||
var faithCost =
|
||||
(200 + 100 * greatProphetsEarned * (greatProphetsEarned + 1) / 2f) *
|
||||
@ -306,12 +306,12 @@ class ReligionManager : IsPartOfGameInfoSerialization {
|
||||
|
||||
// function to help with bookkeeping
|
||||
fun chooseBeliefToAdd(type: BeliefType, number: Int) {
|
||||
val numberToAdd = min(number, availableBeliefs[type]!!)
|
||||
val numberToAdd = min(number, availableBeliefs[type])
|
||||
beliefsToChoose.add(type, numberToAdd)
|
||||
availableBeliefs[type] = availableBeliefs[type]!! - numberToAdd
|
||||
availableBeliefs[type] = availableBeliefs[type] - numberToAdd
|
||||
if (type != BeliefType.Any) {
|
||||
// deduct from BeliefType.Any as well
|
||||
availableBeliefs[BeliefType.Any] = availableBeliefs[BeliefType.Any]!! - numberToAdd
|
||||
availableBeliefs[BeliefType.Any] = availableBeliefs[BeliefType.Any] - numberToAdd
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,14 +377,13 @@ class ReligionManager : IsPartOfGameInfoSerialization {
|
||||
// decrement free beliefs if used
|
||||
if (useFreeBeliefs && hasFreeBeliefs()) {
|
||||
for (belief in beliefs) {
|
||||
if (freeBeliefs[belief.type.name] == null) continue
|
||||
freeBeliefs[belief.type.name] = max(freeBeliefs[belief.type.name]!! - 1, 0)
|
||||
freeBeliefs[belief.type.name] = max(freeBeliefs[belief.type.name] - 1, 0)
|
||||
}
|
||||
}
|
||||
// limit the number of free beliefs available to number of remaining beliefs even if player
|
||||
// didn't use free beliefs (e.g., used a prophet or pantheon)
|
||||
for (type in freeBeliefs.keys) {
|
||||
freeBeliefs[type] = min(freeBeliefs[type]!!, numberOfBeliefsAvailable(BeliefType.valueOf(type)))
|
||||
freeBeliefs[type] = min(freeBeliefs[type], numberOfBeliefsAvailable(BeliefType.valueOf(type)))
|
||||
}
|
||||
civInfo.updateStatsForNextTurn() // a belief can have an immediate effect on stats
|
||||
}
|
||||
@ -469,7 +468,7 @@ class ReligionManager : IsPartOfGameInfoSerialization {
|
||||
if (religion == null) return 0
|
||||
return civInfo.gameInfo.getCities()
|
||||
.filter { it.matchesFilter(cityFilter, civInfo) }
|
||||
.sumOf { it.religion.getFollowersOf(religion!!.name)!! }
|
||||
.sumOf { it.religion.getFollowersOf(religion!!.name) }
|
||||
}
|
||||
|
||||
fun getHolyCity(): City? {
|
||||
|
@ -346,7 +346,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
|
||||
val possibleResources = resourcesOfType
|
||||
.filter { it.terrainsCanBeFoundOn.contains(tile.lastTerrain.name) }
|
||||
if (possibleResources.isEmpty()) continue
|
||||
val resourceWithLeastAssignments = possibleResources.minByOrNull { resourceToNumber[it.name]!! }!!
|
||||
val resourceWithLeastAssignments = possibleResources.minByOrNull { resourceToNumber[it.name] }!!
|
||||
resourceToNumber.add(resourceWithLeastAssignments.name, 1)
|
||||
tile.setTileResource(resourceWithLeastAssignments, rng = randomness.RNG)
|
||||
}
|
||||
|
@ -59,14 +59,7 @@ class UnitUpgradeManager(val unit:MapUnit) {
|
||||
): Boolean {
|
||||
if (unit.name == unitToUpgradeTo.name) return false
|
||||
|
||||
// We need to remove the unit from the civ for this check,
|
||||
// because if the unit requires, say, horses, and so does its upgrade,
|
||||
// and the civ currently has 0 horses, we need to see if the upgrade will be buildable
|
||||
// WHEN THE CURRENT UNIT IS NOT HERE
|
||||
// TODO redesign without kludge: Inform getRejectionReasons about 'virtually available' resources somehow
|
||||
unit.civ.units.removeUnit(unit)
|
||||
val rejectionReasons = unitToUpgradeTo.getRejectionReasons(unit.civ)
|
||||
unit.civ.units.addUnit(unit)
|
||||
val rejectionReasons = unitToUpgradeTo.getRejectionReasons(unit.civ, additionalResources = unit.baseUnit.getResourceRequirementsPerTurn())
|
||||
|
||||
var relevantRejectionReasons = rejectionReasons.filterNot { it.type == RejectionReasonType.Unbuildable }
|
||||
if (ignoreRequirements)
|
||||
|
@ -2,44 +2,62 @@ package com.unciv.models
|
||||
|
||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||
|
||||
open class Counter<K> : LinkedHashMap<K, Int>(), IsPartOfGameInfoSerialization {
|
||||
open class Counter<K>(
|
||||
fromMap: Map<K, Int>? = null
|
||||
) : LinkedHashMap<K, Int>(fromMap?.size ?: 10), IsPartOfGameInfoSerialization {
|
||||
init {
|
||||
if (fromMap != null)
|
||||
for ((key, value) in fromMap)
|
||||
put(key, value)
|
||||
}
|
||||
|
||||
override operator fun get(key: K): Int? { // don't return null if empty
|
||||
override operator fun get(key: K): Int { // don't return null if empty
|
||||
return if (containsKey(key))
|
||||
// .toInt(), because GDX deserializes Counter values as *floats* for some reason
|
||||
super.get(key)!!.toInt()
|
||||
else 0
|
||||
}
|
||||
|
||||
override fun put(key: K, value: Int): Int? {
|
||||
if (value == 0) return remove(key) // No objects of this sort left, no need to count
|
||||
return super.put(key, value)
|
||||
}
|
||||
|
||||
fun add(key: K, value: Int) {
|
||||
if (!containsKey(key))
|
||||
put(key, value)
|
||||
else
|
||||
put(key, get(key)!! + value)
|
||||
if (get(key) == 0) remove(key) // No objects of this sort left, no need to count
|
||||
put(key, get(key) + value)
|
||||
}
|
||||
|
||||
fun add(other: Counter<K>) {
|
||||
for ((key, value) in other) add(key, value)
|
||||
}
|
||||
operator fun plusAssign(other: Counter<K>) = add(other)
|
||||
|
||||
fun remove(other: Counter<K>) {
|
||||
for ((key, value) in other) add(key, -value)
|
||||
}
|
||||
operator fun minusAssign(other: Counter<K>) = remove(other)
|
||||
|
||||
fun times(amount:Int): Counter<K> {
|
||||
operator fun times(amount: Int): Counter<K> {
|
||||
val newCounter = Counter<K>()
|
||||
for (key in keys) newCounter[key] = this[key]!! * amount
|
||||
for (key in keys) newCounter[key] = this[key] * amount
|
||||
return newCounter
|
||||
}
|
||||
|
||||
fun sumValues(): Int {
|
||||
return this.map { it.value }.sum()
|
||||
}
|
||||
operator fun plus(other: Counter<K>) = clone().apply { add(other) }
|
||||
|
||||
fun sumValues() = values.sum()
|
||||
|
||||
override fun clone(): Counter<K> {
|
||||
val newCounter = Counter<K>()
|
||||
newCounter.add(this)
|
||||
return newCounter
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ZERO: Counter<String> = object : Counter<String>() {
|
||||
override fun put(key: String, value: Int): Int? {
|
||||
throw UnsupportedOperationException("Do not modify Counter.ZERO")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -357,7 +357,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
||||
|
||||
override fun canBePurchasedWithStat(city: City?, stat: Stat): Boolean {
|
||||
if (stat == Stat.Gold && isAnyWonder()) return false
|
||||
if (city == null) return super.canBePurchasedWithStat(city, stat)
|
||||
if (city == null) return super.canBePurchasedWithStat(null, stat)
|
||||
|
||||
val conditionalState = StateForConditionals(civInfo = city.civ, city = city)
|
||||
return (
|
||||
@ -402,7 +402,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
||||
getCostForConstructionsIncreasingInPrice(
|
||||
it.params[1].toInt(),
|
||||
it.params[4].toInt(),
|
||||
city.civ.civConstructions.boughtItemsWithIncreasingPrice[name] ?: 0
|
||||
city.civ.civConstructions.boughtItemsWithIncreasingPrice[name]
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -750,10 +750,10 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
||||
|
||||
fun isSellable() = !isAnyWonder() && !hasUnique(UniqueType.Unsellable)
|
||||
|
||||
override fun getResourceRequirementsPerTurn(): HashMap<String, Int> = resourceRequirementsInternal
|
||||
override fun getResourceRequirementsPerTurn(): Counter<String> = resourceRequirementsInternal
|
||||
|
||||
private val resourceRequirementsInternal: HashMap<String, Int> by lazy {
|
||||
val resourceRequirements = HashMap<String, Int>()
|
||||
private val resourceRequirementsInternal: Counter<String> by lazy {
|
||||
val resourceRequirements = Counter<String>()
|
||||
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
|
||||
for (unique in uniqueObjects)
|
||||
if (unique.isOfType(UniqueType.ConsumesResources))
|
||||
|
@ -3,6 +3,7 @@ package com.unciv.models.ruleset
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.models.Counter
|
||||
import com.unciv.models.ruleset.unique.IHasUniques
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
@ -18,7 +19,7 @@ interface IConstruction : INamed {
|
||||
fun isBuildable(cityConstructions: CityConstructions): Boolean
|
||||
fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean
|
||||
/** Gets *per turn* resource requirements - does not include immediate costs for stockpiled resources */
|
||||
fun getResourceRequirementsPerTurn(): HashMap<String,Int>
|
||||
fun getResourceRequirementsPerTurn(): Counter<String>
|
||||
fun requiresResource(resource: String): Boolean
|
||||
/** We can't call this getMatchingUniques because then it would conflict with IHasUniques */
|
||||
fun getMatchingUniquesNotConflicting(uniqueType: UniqueType) = sequenceOf<Unique>()
|
||||
@ -223,7 +224,7 @@ open class PerpetualConstruction(override var name: String, val description: Str
|
||||
override fun isBuildable(cityConstructions: CityConstructions): Boolean =
|
||||
throw Exception("Impossible!")
|
||||
|
||||
override fun getResourceRequirementsPerTurn(): HashMap<String, Int> = hashMapOf()
|
||||
override fun getResourceRequirementsPerTurn() = Counter.ZERO
|
||||
|
||||
override fun requiresResource(resource: String) = false
|
||||
|
||||
|
@ -7,6 +7,7 @@ import com.unciv.models.ruleset.RejectionReason
|
||||
import com.unciv.models.ruleset.RejectionReasonType
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.models.Counter
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetObject
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
@ -86,7 +87,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
override fun getProductionCost(civInfo: Civilization): Int = costFunctions.getProductionCost(civInfo)
|
||||
|
||||
override fun canBePurchasedWithStat(city: City?, stat: Stat): Boolean {
|
||||
if (city == null) return super.canBePurchasedWithStat(city, stat)
|
||||
if (city == null) return super.canBePurchasedWithStat(null, stat)
|
||||
if (getRejectionReasons(city.civ, city).any { it.type != RejectionReasonType.Unbuildable })
|
||||
return false
|
||||
if (costFunctions.canBePurchasedWithStat(city, stat)) return true
|
||||
@ -142,44 +143,48 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
yieldAll(getRejectionReasons(civInfo, cityConstructions.city))
|
||||
}
|
||||
|
||||
fun getRejectionReasons(civ: Civilization, city: City? = null): Sequence<RejectionReason> {
|
||||
val result = mutableListOf<RejectionReason>()
|
||||
fun getRejectionReasons(
|
||||
civ: Civilization,
|
||||
city: City? = null,
|
||||
additionalResources: Counter<String> = Counter.ZERO
|
||||
): Sequence<RejectionReason> = sequence {
|
||||
if (requiredTech != null && !civ.tech.isResearched(requiredTech!!))
|
||||
result.add(RejectionReasonType.RequiresTech.toInstance("$requiredTech not researched"))
|
||||
yield(RejectionReasonType.RequiresTech.toInstance("$requiredTech not researched"))
|
||||
if (obsoleteTech != null && civ.tech.isResearched(obsoleteTech!!))
|
||||
result.add(RejectionReasonType.Obsoleted.toInstance("Obsolete by $obsoleteTech"))
|
||||
yield(RejectionReasonType.Obsoleted.toInstance("Obsolete by $obsoleteTech"))
|
||||
|
||||
if (uniqueTo != null && uniqueTo != civ.civName)
|
||||
result.add(RejectionReasonType.UniqueToOtherNation.toInstance("Unique to $uniqueTo"))
|
||||
yield(RejectionReasonType.UniqueToOtherNation.toInstance("Unique to $uniqueTo"))
|
||||
if (civ.cache.uniqueUnits.any { it.replaces == name })
|
||||
result.add(RejectionReasonType.ReplacedByOurUnique.toInstance("Our unique unit replaces this"))
|
||||
yield(RejectionReasonType.ReplacedByOurUnique.toInstance("Our unique unit replaces this"))
|
||||
|
||||
if (!civ.gameInfo.gameParameters.nuclearWeaponsEnabled && isNuclearWeapon())
|
||||
result.add(RejectionReasonType.DisabledBySetting.toInstance())
|
||||
yield(RejectionReasonType.DisabledBySetting.toInstance())
|
||||
|
||||
for (unique in uniqueObjects.filter { it.conditionalsApply(civ, city) }) {
|
||||
when (unique.type) {
|
||||
UniqueType.Unbuildable ->
|
||||
result.add(RejectionReasonType.Unbuildable.toInstance())
|
||||
yield(RejectionReasonType.Unbuildable.toInstance())
|
||||
|
||||
UniqueType.FoundCity -> if (civ.isCityState() || civ.isOneCityChallenger())
|
||||
result.add(RejectionReasonType.NoSettlerForOneCityPlayers.toInstance())
|
||||
yield(RejectionReasonType.NoSettlerForOneCityPlayers.toInstance())
|
||||
|
||||
UniqueType.MaxNumberBuildable -> if (civ.civConstructions.countConstructedObjects(
|
||||
this@BaseUnit
|
||||
) >= unique.params[0].toInt()
|
||||
)
|
||||
result.add(RejectionReasonType.MaxNumberBuildable.toInstance())
|
||||
yield(RejectionReasonType.MaxNumberBuildable.toInstance())
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
if (!civ.isBarbarian()) { // Barbarians don't need resources
|
||||
val civResources = Counter(civ.getCivResourcesByName()) + additionalResources
|
||||
for ((resource, requiredAmount) in getResourceRequirementsPerTurn()) {
|
||||
val availableAmount = civ.getCivResourcesByName()[resource]!!
|
||||
val availableAmount = civResources[resource]
|
||||
if (availableAmount < requiredAmount) {
|
||||
result.add(
|
||||
yield(
|
||||
RejectionReasonType.ConsumesResources.toInstance(
|
||||
resource.getNeedMoreAmountString(
|
||||
requiredAmount - availableAmount
|
||||
@ -192,15 +197,14 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
|
||||
val stateForConditionals = StateForConditionals(civ, city)
|
||||
for (unique in civ.getMatchingUniques(UniqueType.CannotBuildUnits, stateForConditionals))
|
||||
if (this.matchesFilter(unique.params[0])) {
|
||||
if (this@BaseUnit.matchesFilter(unique.params[0])) {
|
||||
val hasHappinessCondition = unique.conditionals.any {
|
||||
it.type == UniqueType.ConditionalBelowHappiness || it.type == UniqueType.ConditionalBetweenHappiness
|
||||
}
|
||||
if (hasHappinessCondition)
|
||||
result.add(RejectionReasonType.CannotBeBuiltUnhappiness.toInstance(unique.text))
|
||||
else result.add(RejectionReasonType.CannotBeBuilt.toInstance())
|
||||
yield(RejectionReasonType.CannotBeBuiltUnhappiness.toInstance(unique.text))
|
||||
else yield(RejectionReasonType.CannotBeBuilt.toInstance())
|
||||
}
|
||||
return result.asSequence()
|
||||
}
|
||||
|
||||
fun isBuildable(civInfo: Civilization) = getRejectionReasons(civInfo).none()
|
||||
@ -317,10 +321,10 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
fun movesLikeAirUnits() = type.getMovementType() == UnitMovementType.Air
|
||||
|
||||
/** Returns resource requirements from both uniques and requiredResource field */
|
||||
override fun getResourceRequirementsPerTurn(): HashMap<String, Int> = resourceRequirementsInternal
|
||||
override fun getResourceRequirementsPerTurn(): Counter<String> = resourceRequirementsInternal
|
||||
|
||||
private val resourceRequirementsInternal: HashMap<String, Int> by lazy {
|
||||
val resourceRequirements = HashMap<String, Int>()
|
||||
private val resourceRequirementsInternal: Counter<String> by lazy {
|
||||
val resourceRequirements = Counter<String>()
|
||||
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1
|
||||
for (unique in getMatchingUniques(UniqueType.ConsumesResources))
|
||||
resourceRequirements[unique.params[1]] = unique.params[0].toInt()
|
||||
@ -334,7 +338,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
fun isMilitary() = isRanged() || isMelee()
|
||||
fun isCivilian() = !isMilitary()
|
||||
|
||||
val isLandUnitInternal by lazy { type.isLandUnit() }
|
||||
private val isLandUnitInternal by lazy { type.isLandUnit() }
|
||||
fun isLandUnit() = isLandUnitInternal
|
||||
fun isWaterUnit() = type.isWaterUnit()
|
||||
fun isAirUnit() = type.isAirUnit()
|
||||
|
@ -88,7 +88,7 @@ class BaseUnitCost(val baseUnit: BaseUnit) {
|
||||
baseUnit.getCostForConstructionsIncreasingInPrice(
|
||||
it.params[1].toInt(),
|
||||
it.params[4].toInt(),
|
||||
city.civ.civConstructions.boughtItemsWithIncreasingPrice[baseUnit.name] ?: 0
|
||||
city.civ.civConstructions.boughtItemsWithIncreasingPrice[baseUnit.name]
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -66,7 +66,7 @@ class CityReligionInfoTable(
|
||||
add(followerCount.toLabel()).pad(5f)
|
||||
addSeparatorVertical(gridColor)
|
||||
if (futurePressures.containsKey(religion))
|
||||
add(("+ [${futurePressures[religion]!!}] pressure").toLabel()).pad(5f)
|
||||
add(("+ [${futurePressures[religion]}] pressure").toLabel()).pad(5f)
|
||||
else
|
||||
add()
|
||||
row()
|
||||
|
@ -33,7 +33,7 @@ import kotlin.math.ceil
|
||||
import kotlin.math.round
|
||||
import com.unciv.ui.components.AutoScrollPane as ScrollPane
|
||||
|
||||
class CityStatsTable(val cityScreen: CityScreen): Table() {
|
||||
class CityStatsTable(private val cityScreen: CityScreen): Table() {
|
||||
private val innerTable = Table() // table within this Table. Slightly smaller creates border
|
||||
private val upperTable = Table() // fixed position table
|
||||
private val lowerTable = Table() // table that will be in the ScrollPane
|
||||
@ -288,7 +288,7 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
|
||||
val specialist = cityInfo.getRuleset().specialists[specialistName]
|
||||
?: continue // probably a mod that doesn't have the specialist defined yet
|
||||
repeat(amount) {
|
||||
if (assignedSpec[specialistName]!! > 0) {
|
||||
if (assignedSpec[specialistName] > 0) {
|
||||
specialistIcons.add(ImageGetter.getSpecialistIcon(specialist.colorObject))
|
||||
.size(20f)
|
||||
assignedSpec.add(specialistName, -1)
|
||||
@ -342,7 +342,7 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
|
||||
var gppPerTurn = 0
|
||||
|
||||
for ((_, gppCounter) in greatPersonPoints) {
|
||||
val gppPointsFromSource = gppCounter[greatPersonName]!!
|
||||
val gppPointsFromSource = gppCounter[greatPersonName]
|
||||
if (gppPointsFromSource == 0) continue
|
||||
gppPerTurn += gppPointsFromSource
|
||||
}
|
||||
@ -356,7 +356,7 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
|
||||
val gppCurrent = city.civ.greatPeople.greatPersonPointsCounter[greatPersonName]
|
||||
val gppNeeded = city.civ.greatPeople.getPointsRequiredForGreatPerson()
|
||||
|
||||
val percent = gppCurrent!! / gppNeeded.toFloat()
|
||||
val percent = gppCurrent / gppNeeded.toFloat()
|
||||
|
||||
val progressBar = ImageGetter.ProgressBar(300f, 25f, false)
|
||||
progressBar.setBackground(Color.BLACK.cpy().apply { a = 0.8f })
|
||||
|
@ -16,7 +16,7 @@ import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
|
||||
class SpecialistAllocationTable(val cityScreen: CityScreen) : Table(BaseScreen.skin) {
|
||||
class SpecialistAllocationTable(private val cityScreen: CityScreen) : Table(BaseScreen.skin) {
|
||||
val cityInfo = cityScreen.city
|
||||
|
||||
fun update() {
|
||||
@ -44,7 +44,7 @@ class SpecialistAllocationTable(val cityScreen: CityScreen) : Table(BaseScreen.s
|
||||
if (!cityInfo.getRuleset().specialists.containsKey(specialistName)) // specialist doesn't exist in this ruleset, probably a mod
|
||||
continue
|
||||
val newSpecialists = cityInfo.population.getNewSpecialists()
|
||||
val assignedSpecialists = newSpecialists[specialistName]!!
|
||||
val assignedSpecialists = newSpecialists[specialistName]
|
||||
|
||||
if (cityScreen.canChangeState) add(getUnassignButton(assignedSpecialists, specialistName))
|
||||
add(getAllocationTable(assignedSpecialists, maxSpecialists, specialistName)).pad(10f)
|
||||
@ -56,7 +56,7 @@ class SpecialistAllocationTable(val cityScreen: CityScreen) : Table(BaseScreen.s
|
||||
}
|
||||
|
||||
|
||||
fun getAllocationTable(assignedSpecialists: Int, maxSpecialists: Int, specialistName: String): Table {
|
||||
private fun getAllocationTable(assignedSpecialists: Int, maxSpecialists: Int, specialistName: String): Table {
|
||||
|
||||
val specialistIconTable = Table()
|
||||
val specialistObject = cityInfo.getRuleset().specialists[specialistName]!!
|
||||
|
@ -208,7 +208,7 @@ class StatsOverviewTab(
|
||||
for ((greatPerson, points) in greatPersonPoints) {
|
||||
add(greatPerson.toLabel()).left()
|
||||
add("$points/$pointsToGreatPerson".toLabel())
|
||||
add(greatPersonPointsPerTurn[greatPerson]!!.toLabel()).right().row()
|
||||
add(greatPersonPointsPerTurn[greatPerson].toLabel()).right().row()
|
||||
}
|
||||
|
||||
val pointsForGreatGeneral = viewingPlayer.greatPeople.greatGeneralPoints
|
||||
|
Reference in New Issue
Block a user