chore(purity): UnitOverview and UnitPromotions

This commit is contained in:
yairm210 2025-08-22 12:37:26 +03:00
parent a27c1ae88d
commit da85856fe3
7 changed files with 26 additions and 10 deletions

View File

@ -68,7 +68,9 @@ allprojects {
"kotlin.collections.shuffled", "kotlin.collections.shuffled",
) )
wellKnownPureClasses = setOf( wellKnownPureClasses = setOf(
"java.lang.StackTraceElement" // moved "java.lang.StackTraceElement", // moved
"java.text.DecimalFormat",
"java.lang.Math",
) )
wellKnownInternalStateClasses = setOf( wellKnownInternalStateClasses = setOf(
"com.badlogic.gdx.math.Vector2", "com.badlogic.gdx.math.Vector2",

View File

@ -183,6 +183,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
* Note this is translated after being returned from this function, so let's pay * Note this is translated after being returned from this function, so let's pay
* attention to combined names (renamed units, religion). * attention to combined names (renamed units, religion).
*/ */
@Readonly
fun displayName(): String { fun displayName(): String {
val baseName = val baseName =
if (instanceName == null) "[$name]" if (instanceName == null) "[$name]"
@ -231,7 +232,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
val type: UnitType val type: UnitType
get() = baseUnit.type get() = baseUnit.type
fun getMovementString(): String = @Readonly fun getMovementString(): String =
(DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + getMaxMovement()).tr() (DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + getMaxMovement()).tr()

View File

@ -34,6 +34,7 @@ class UnitPromotions : IsPartOfGameInfoSerialization {
* @param sorted if `true` return the promotions in json order (`false` gives hashset order) for display. * @param sorted if `true` return the promotions in json order (`false` gives hashset order) for display.
* @return a Sequence of this unit's promotions * @return a Sequence of this unit's promotions
*/ */
@Readonly
fun getPromotions(sorted: Boolean = false): Sequence<Promotion> = sequence { fun getPromotions(sorted: Boolean = false): Sequence<Promotion> = sequence {
if (promotions.isEmpty()) return@sequence if (promotions.isEmpty()) return@sequence
val unitPromotions = unit.civ.gameInfo.ruleset.unitPromotions val unitPromotions = unit.civ.gameInfo.ruleset.unitPromotions
@ -51,14 +52,17 @@ class UnitPromotions : IsPartOfGameInfoSerialization {
} }
/** @return the XP points needed to "buy" the next promotion. 10, 30, 60, 100, 150,... */ /** @return the XP points needed to "buy" the next promotion. 10, 30, 60, 100, 150,... */
fun xpForNextPromotion(): Int = Math.round(baseXpForPromotionNumber(numberOfPromotions + 1) * promotionCostModifier()) @Readonly fun xpForNextPromotion(): Int = Math.round(baseXpForPromotionNumber(numberOfPromotions + 1) * promotionCostModifier())
/** @return the XP points needed to "buy" the next [count] promotions. */ /** @return the XP points needed to "buy" the next [count] promotions. */
@Readonly
fun xpForNextNPromotions(count: Int) = (1..count).sumOf { fun xpForNextNPromotions(count: Int) = (1..count).sumOf {
baseXpForPromotionNumber(numberOfPromotions+it)} * promotionCostModifier() baseXpForPromotionNumber(numberOfPromotions+it)} * promotionCostModifier()
@Readonly
private fun baseXpForPromotionNumber(numberOfPromotions: Int) = (numberOfPromotions) * 10 private fun baseXpForPromotionNumber(numberOfPromotions: Int) = (numberOfPromotions) * 10
@Readonly
private fun promotionCostModifier(): Float { private fun promotionCostModifier(): Float {
var totalPromotionCostModifier = 1f var totalPromotionCostModifier = 1f
for (unique in unit.civ.getMatchingUniques(UniqueType.XPForPromotionModifier)) { for (unique in unit.civ.getMatchingUniques(UniqueType.XPForPromotionModifier)) {
@ -69,8 +73,9 @@ class UnitPromotions : IsPartOfGameInfoSerialization {
} }
/** @return Total XP including that already "spent" on promotions */ /** @return Total XP including that already "spent" on promotions */
fun totalXpProduced() = XP + (numberOfPromotions * (numberOfPromotions + 1)) * 5 @Readonly fun totalXpProduced() = XP + (numberOfPromotions * (numberOfPromotions + 1)) * 5
@Readonly
fun canBePromoted(): Boolean { fun canBePromoted(): Boolean {
if (XP < xpForNextPromotion()) return false if (XP < xpForNextPromotion()) return false
if (getAvailablePromotions().none()) return false if (getAvailablePromotions().none()) return false
@ -134,10 +139,12 @@ class UnitPromotions : IsPartOfGameInfoSerialization {
/** Gets all promotions this unit could currently "buy" with enough [XP] /** Gets all promotions this unit could currently "buy" with enough [XP]
* Checks unit type, already acquired promotions, prerequisites and incompatibility uniques. * Checks unit type, already acquired promotions, prerequisites and incompatibility uniques.
*/ */
@Readonly
fun getAvailablePromotions(): Sequence<Promotion> { fun getAvailablePromotions(): Sequence<Promotion> {
return unit.civ.gameInfo.ruleset.unitPromotions.values.asSequence().filter { isAvailable(it) } return unit.civ.gameInfo.ruleset.unitPromotions.values.asSequence().filter { isAvailable(it) }
} }
@Readonly
private fun isAvailable(promotion: Promotion): Boolean { private fun isAvailable(promotion: Promotion): Boolean {
if (promotion.name in promotions) return false if (promotion.name in promotions) return false
if (unit.type.name !in promotion.unitTypes) return false if (unit.type.name !in promotion.unitTypes) return false

View File

@ -102,7 +102,7 @@ class Religion() : INamed, IsPartOfGameInfoSerialization {
.filter { it.type == beliefType } .filter { it.type == beliefType }
} }
fun getAllBeliefsOrdered(): Sequence<Belief> { @Readonly fun getAllBeliefsOrdered(): Sequence<Belief> {
return mapToExistingBeliefs(followerBeliefs).filter { it.type == BeliefType.Pantheon } + return mapToExistingBeliefs(followerBeliefs).filter { it.type == BeliefType.Pantheon } +
mapToExistingBeliefs(founderBeliefs).filter { it.type == BeliefType.Founder } + mapToExistingBeliefs(founderBeliefs).filter { it.type == BeliefType.Founder } +
mapToExistingBeliefs(followerBeliefs).filter { it.type == BeliefType.Follower } + mapToExistingBeliefs(followerBeliefs).filter { it.type == BeliefType.Follower } +

View File

@ -59,7 +59,7 @@ class UnitOverviewTab(
//todo the comments and todo below are copied verbatim from CityOverviewTab - synergies? //todo the comments and todo below are copied verbatim from CityOverviewTab - synergies?
private val grid = SortableGrid( private val grid = SortableGrid(
columns = UnitOverviewTabColumn.values().asIterable(), columns = UnitOverviewTabColumn.entries.asIterable(),
data = viewingPlayer.units.getCivUnits().asIterable(), data = viewingPlayer.units.getCivUnits().asIterable(),
actionContext = this, actionContext = this,
sortState = persistableData, sortState = persistableData,

View File

@ -17,6 +17,7 @@ import com.unciv.ui.components.widgets.UnitIconGroup
import com.unciv.ui.images.IconTextButton import com.unciv.ui.images.IconTextButton
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.screens.pickerscreens.UnitRenamePopup import com.unciv.ui.screens.pickerscreens.UnitRenamePopup
import yairm210.purity.annotations.Readonly
//todo Extending getEntryValue here to have a second String-based "channel" - could go into SortableGrid, possibly by defining a DataType per column??? //todo Extending getEntryValue here to have a second String-based "channel" - could go into SortableGrid, possibly by defining a DataType per column???
@ -91,6 +92,7 @@ enum class UnitOverviewTabColumn(
label.onClick { showWorldScreenAt(closestCityTile) } label.onClick { showWorldScreenAt(closestCityTile) }
return label return label
} }
@Readonly
private fun getClosestCityTile(item: MapUnit) = item.getTile() private fun getClosestCityTile(item: MapUnit) = item.getTile()
.getTilesInDistance(3).firstOrNull { it.isCityCenter() } .getTilesInDistance(3).firstOrNull { it.isCityCenter() }
}, },
@ -134,11 +136,11 @@ enum class UnitOverviewTabColumn(
override val defaultSort get() = SortableGrid.SortDirection.Ascending override val defaultSort get() = SortableGrid.SortDirection.Ascending
//endregion //endregion
open fun getEntryString(item: MapUnit): String? = getEntryValue(item).takeIf { it > 0 }?.tr() @Readonly open fun getEntryString(item: MapUnit): String? = getEntryValue(item).takeIf { it > 0 }?.tr()
//region Overridden superclass methods //region Overridden superclass methods
override fun getHeaderActor(iconSize: Float) = (headerLabel ?: name).toLabel() override fun getHeaderActor(iconSize: Float) = (headerLabel ?: name).toLabel()
override fun getEntryValue(item: MapUnit) = 0 @Readonly override fun getEntryValue(item: MapUnit) = 0
override fun getEntryActor(item: MapUnit, iconSize: Float, actionContext: UnitOverviewTab): Actor? = override fun getEntryActor(item: MapUnit, iconSize: Float, actionContext: UnitOverviewTab): Actor? =
getEntryString(item)?.toLabel(alignment = Align.center) getEntryString(item)?.toLabel(alignment = Align.center)
override fun getComparator() = if (isNumeric) super.getComparator() override fun getComparator() = if (isNumeric) super.getComparator()

View File

@ -20,6 +20,7 @@ import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.UnitUpgradeMenu import com.unciv.ui.popups.UnitUpgradeMenu
import com.unciv.ui.screens.pickerscreens.PromotionPickerScreen import com.unciv.ui.screens.pickerscreens.PromotionPickerScreen
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade
import yairm210.purity.annotations.Readonly
/** /**
* Helper library for [UnitOverviewTabColumn] * Helper library for [UnitOverviewTabColumn]
@ -43,6 +44,7 @@ open class UnitOverviewTabHelpers {
protected fun showWorldScreenAt(unit: MapUnit) = showWorldScreenAt(unit.currentTile.position, unit) protected fun showWorldScreenAt(unit: MapUnit) = showWorldScreenAt(unit.currentTile.position, unit)
protected fun showWorldScreenAt(tile: Tile) = showWorldScreenAt(tile.position, null) protected fun showWorldScreenAt(tile: Tile) = showWorldScreenAt(tile.position, null)
@Readonly
private fun getWorkerActionText(unit: MapUnit): String? = when { private fun getWorkerActionText(unit: MapUnit): String? = when {
// See UnitTurnManager.endTurn, if..workOnImprovement or UnitGroup.getActionImage: similar logic // See UnitTurnManager.endTurn, if..workOnImprovement or UnitGroup.getActionImage: similar logic
!unit.cache.hasUniqueToBuildImprovements -> null !unit.cache.hasUniqueToBuildImprovements -> null
@ -52,6 +54,7 @@ open class UnitOverviewTabHelpers {
else -> unit.currentTile.improvementInProgress else -> unit.currentTile.improvementInProgress
} }
@Readonly
protected fun getActionText(unit: MapUnit): String? { protected fun getActionText(unit: MapUnit): String? {
val workerText by lazy { getWorkerActionText(unit) } val workerText by lazy { getWorkerActionText(unit) }
return when { return when {
@ -88,6 +91,7 @@ open class UnitOverviewTabHelpers {
return table return table
} }
@Readonly @Suppress("purity") // Calls action
protected fun getUpgradeSortString(unit: MapUnit): String? { protected fun getUpgradeSortString(unit: MapUnit): String? {
val upgrade = UnitActionsUpgrade.getUpgradeActionAnywhere(unit).firstOrNull() val upgrade = UnitActionsUpgrade.getUpgradeActionAnywhere(unit).firstOrNull()
?: return null ?: return null