diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index 9e7b9f08f5..7c2df3a059 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -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 diff --git a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt index 6c885de59e..a94ebfef2c 100644 --- a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt +++ b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt @@ -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 ) diff --git a/core/src/com/unciv/logic/city/City.kt b/core/src/com/unciv/logic/city/City.kt index f7d99168a5..eccbca52d5 100644 --- a/core/src/com/unciv/logic/city/City.kt +++ b/core/src/com/unciv/logic/city/City.kt @@ -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 diff --git a/core/src/com/unciv/logic/city/managers/CityPopulationManager.kt b/core/src/com/unciv/logic/city/managers/CityPopulationManager.kt index f8e15fd412..d34461d585 100644 --- a/core/src/com/unciv/logic/city/managers/CityPopulationManager.kt +++ b/core/src/com/unciv/logic/city/managers/CityPopulationManager.kt @@ -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() diff --git a/core/src/com/unciv/logic/city/managers/CityReligionManager.kt b/core/src/com/unciv/logic/city/managers/CityReligionManager.kt index aa1cc7012b..40ca3706d9 100644 --- a/core/src/com/unciv/logic/city/managers/CityReligionManager.kt +++ b/core/src/com/unciv/logic/city/managers/CityReligionManager.kt @@ -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 = hashSetOf() + private val religionsAtSomePointAdopted: HashSet = hashSetOf() private val pressures: Counter = 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 } } diff --git a/core/src/com/unciv/logic/civilization/CivConstructions.kt b/core/src/com/unciv/logic/civilization/CivConstructions.kt index 81c3be6d3c..cafeb6685e 100644 --- a/core/src/com/unciv/logic/civilization/CivConstructions.kt +++ b/core/src/com/unciv/logic/civilization/CivConstructions.kt @@ -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 { diff --git a/core/src/com/unciv/logic/civilization/Civilization.kt b/core/src/com/unciv/logic/civilization/Civilization.kt index 37b067fb2a..54b7c48339 100644 --- a/core/src/com/unciv/logic/civilization/Civilization.kt +++ b/core/src/com/unciv/logic/civilization/Civilization.kt @@ -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 = 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 = sequence{ + fun getTriggeredUniques( + trigger: UniqueType, + stateForConditionals: StateForConditionals = StateForConditionals(this) + ) : Sequence = sequence { yieldAll(nation.uniqueMap.getTriggeredUniques(trigger, stateForConditionals)) yieldAll(cities.asSequence() .flatMap { city -> city.cityConstructions.builtBuildingUniqueMap.getTriggeredUniques(trigger, stateForConditionals) } diff --git a/core/src/com/unciv/logic/civilization/managers/ReligionManager.kt b/core/src/com/unciv/logic/civilization/managers/ReligionManager.kt index e6ba58cafb..4ba93d91b2 100644 --- a/core/src/com/unciv/logic/civilization/managers/ReligionManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/ReligionManager.kt @@ -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? { diff --git a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt index abc4c8d13f..64d6351581 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt @@ -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) } diff --git a/core/src/com/unciv/logic/map/mapunit/UnitUpgradeManager.kt b/core/src/com/unciv/logic/map/mapunit/UnitUpgradeManager.kt index cc4631a868..7e3ded9046 100644 --- a/core/src/com/unciv/logic/map/mapunit/UnitUpgradeManager.kt +++ b/core/src/com/unciv/logic/map/mapunit/UnitUpgradeManager.kt @@ -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) diff --git a/core/src/com/unciv/models/Counter.kt b/core/src/com/unciv/models/Counter.kt index c4ecb3c10c..1b8d6f2876 100644 --- a/core/src/com/unciv/models/Counter.kt +++ b/core/src/com/unciv/models/Counter.kt @@ -2,44 +2,62 @@ package com.unciv.models import com.unciv.logic.IsPartOfGameInfoSerialization -open class Counter : LinkedHashMap(), IsPartOfGameInfoSerialization { +open class Counter( + fromMap: Map? = null +) : LinkedHashMap(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) { for ((key, value) in other) add(key, value) } + operator fun plusAssign(other: Counter) = add(other) fun remove(other: Counter) { for ((key, value) in other) add(key, -value) } + operator fun minusAssign(other: Counter) = remove(other) - fun times(amount:Int): Counter { + operator fun times(amount: Int): Counter { val newCounter = Counter() - 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) = clone().apply { add(other) } + + fun sumValues() = values.sum() override fun clone(): Counter { val newCounter = Counter() newCounter.add(this) return newCounter } + + companion object { + val ZERO: Counter = object : Counter() { + override fun put(key: String, value: Int): Int? { + throw UnsupportedOperationException("Do not modify Counter.ZERO") + } + } + } } diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index 17c856218e..43f8e1c798 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -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 = resourceRequirementsInternal + override fun getResourceRequirementsPerTurn(): Counter = resourceRequirementsInternal - private val resourceRequirementsInternal: HashMap by lazy { - val resourceRequirements = HashMap() + private val resourceRequirementsInternal: Counter by lazy { + val resourceRequirements = Counter() if (requiredResource != null) resourceRequirements[requiredResource!!] = 1 for (unique in uniqueObjects) if (unique.isOfType(UniqueType.ConsumesResources)) diff --git a/core/src/com/unciv/models/ruleset/IConstruction.kt b/core/src/com/unciv/models/ruleset/IConstruction.kt index 27fafd25bd..85f45d9395 100644 --- a/core/src/com/unciv/models/ruleset/IConstruction.kt +++ b/core/src/com/unciv/models/ruleset/IConstruction.kt @@ -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 + fun getResourceRequirementsPerTurn(): Counter fun requiresResource(resource: String): Boolean /** We can't call this getMatchingUniques because then it would conflict with IHasUniques */ fun getMatchingUniquesNotConflicting(uniqueType: UniqueType) = sequenceOf() @@ -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 = hashMapOf() + override fun getResourceRequirementsPerTurn() = Counter.ZERO override fun requiresResource(resource: String) = false diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index 41c9531c7c..d5393cf0ec 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -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 { - val result = mutableListOf() + fun getRejectionReasons( + civ: Civilization, + city: City? = null, + additionalResources: Counter = Counter.ZERO + ): Sequence = 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 = resourceRequirementsInternal + override fun getResourceRequirementsPerTurn(): Counter = resourceRequirementsInternal - private val resourceRequirementsInternal: HashMap by lazy { - val resourceRequirements = HashMap() + private val resourceRequirementsInternal: Counter by lazy { + val resourceRequirements = Counter() 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() diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnitCost.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnitCost.kt index 2b43c687f4..845fb68b2b 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnitCost.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnitCost.kt @@ -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] ) } ) diff --git a/core/src/com/unciv/ui/screens/cityscreen/CityReligionInfoTable.kt b/core/src/com/unciv/ui/screens/cityscreen/CityReligionInfoTable.kt index 04e9261d98..5f465d8b4d 100644 --- a/core/src/com/unciv/ui/screens/cityscreen/CityReligionInfoTable.kt +++ b/core/src/com/unciv/ui/screens/cityscreen/CityReligionInfoTable.kt @@ -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() diff --git a/core/src/com/unciv/ui/screens/cityscreen/CityStatsTable.kt b/core/src/com/unciv/ui/screens/cityscreen/CityStatsTable.kt index e5febfb940..81bfdb84ff 100644 --- a/core/src/com/unciv/ui/screens/cityscreen/CityStatsTable.kt +++ b/core/src/com/unciv/ui/screens/cityscreen/CityStatsTable.kt @@ -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 }) diff --git a/core/src/com/unciv/ui/screens/cityscreen/SpecialistAllocationTable.kt b/core/src/com/unciv/ui/screens/cityscreen/SpecialistAllocationTable.kt index 33eb520cd8..9f7456a49f 100644 --- a/core/src/com/unciv/ui/screens/cityscreen/SpecialistAllocationTable.kt +++ b/core/src/com/unciv/ui/screens/cityscreen/SpecialistAllocationTable.kt @@ -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]!! diff --git a/core/src/com/unciv/ui/screens/overviewscreen/StatsOverviewTab.kt b/core/src/com/unciv/ui/screens/overviewscreen/StatsOverviewTab.kt index 7eb37ea2c9..f62101d51f 100644 --- a/core/src/com/unciv/ui/screens/overviewscreen/StatsOverviewTab.kt +++ b/core/src/com/unciv/ui/screens/overviewscreen/StatsOverviewTab.kt @@ -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