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:
SomeTroglodyte
2023-05-22 13:28:36 +02:00
committed by GitHub
parent e2b3432d84
commit ae8d69b5b3
19 changed files with 108 additions and 88 deletions

View File

@ -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

View File

@ -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
)

View File

@ -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

View File

@ -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()

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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) }

View File

@ -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? {

View File

@ -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)
}

View File

@ -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)

View File

@ -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")
}
}
}
}

View File

@ -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))

View File

@ -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

View File

@ -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()

View File

@ -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]
)
}
)

View File

@ -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()

View File

@ -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 })

View File

@ -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]!!

View File

@ -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