Improvement picker fixes (#11801)

* 🎵 A little linting 🎶

* Prevent queueing unresearched removals by adding their problem reports on top

* More linting and bugfixes

* Hide "too advanced" Improvements in the PickerScreen

* ImprovementPickerScreen fully Civilopedia-linked

* Fix dumb mistake

* Slightly improve PickerPane descriptionLabel layout

* Slightly improve ImprovementPickerScreen top padding
This commit is contained in:
SomeTroglodyte
2024-06-22 21:20:16 +02:00
committed by GitHub
parent 05020a7d7e
commit de04c00dd0
11 changed files with 205 additions and 126 deletions

View File

@ -94,7 +94,7 @@ class TileImprovementFunctions(val tile: Tile) {
yield(ImprovementBuildingProblem.Other)
}
/** Without regards to what CivInfo it is, a lot of the checks are just for the improvement on the tile.
/** Without regards to what CivInfo it is (so no tech requirement check), a lot of the checks are just for the improvement on the tile.
* Doubles as a check for the map editor.
*/
internal fun canImprovementBeBuiltHere(
@ -105,7 +105,7 @@ class TileImprovementFunctions(val tile: Tile) {
isNormalizeCheck: Boolean = false
): Boolean {
fun TileImprovement.canBeBuildOnThisUnbuildableTerrain(
fun TileImprovement.canBeBuiltOnThisUnbuildableTerrain(
knownFeatureRemovals: List<TileImprovement>? = null,
): Boolean {
val topTerrain = tile.lastTerrain
@ -121,7 +121,7 @@ class TileImprovementFunctions(val tile: Tile) {
if (featureRemovals.any { it !in knownFeatureRemovals }) return false
val clonedTile = tile.clone()
clonedTile.setTerrainFeatures(tile.terrainFeatures.filterNot {
feature -> featureRemovals.any{ it.name.removePrefix(Constants.remove) == feature } })
feature -> featureRemovals.any { it.name.removePrefix(Constants.remove) == feature } })
return clonedTile.improvementFunctions.canImprovementBeBuiltHere(improvement, resourceIsVisible, knownFeatureRemovals, stateForConditionals)
}
@ -142,13 +142,13 @@ class TileImprovementFunctions(val tile: Tile) {
RoadStatus.values().any { it.name == improvement.name } -> !tile.isWater
&& RoadStatus.valueOf(improvement.name) > tile.roadStatus
// Then we check if there is any reason to not allow this improvement to be build
// Then we check if there is any reason to not allow this improvement to be built
// Can't build if there is already an irremovable improvement here
tile.improvement != null && tile.getTileImprovement()!!.hasUnique(UniqueType.Irremovable, stateForConditionals) -> false
// Can't build if this terrain is unbuildable, except when we are specifically allowed to
tile.lastTerrain.unbuildable && !improvement.canBeBuildOnThisUnbuildableTerrain(knownFeatureRemovals) -> false
tile.lastTerrain.unbuildable && !improvement.canBeBuiltOnThisUnbuildableTerrain(knownFeatureRemovals) -> false
// Can't build if any terrain specifically prevents building this improvement
tile.getTerrainMatchingUniques(UniqueType.RestrictedBuildableImprovements, stateForConditionals).any {

View File

@ -98,11 +98,13 @@ class ModConstants {
// Espionage
var maxSpyRank = 3
// How much of a skill bonus each rank gives.
// Rank 0 is 100%, rank 1 is 130%, and so on for stealing technology.
// How much of a skill bonus each rank gives.
// Rank 0 is 100%, rank 1 is 130%, and so on for stealing technology.
// Half as much for a coup.
var spyRankSkillPercentBonus = 30
// UI: If set >= 0, ImprovementPicker will silently skip improvements whose tech requirement is more advanced than your current Era + this
var maxImprovementTechErasForward = -1
fun merge(other: ModConstants) {
for (field in this::class.java.declaredFields) {

View File

@ -33,6 +33,8 @@ class Technology: RulesetObject() {
override fun getCivilopediaTextLines(ruleset: Ruleset) =
TechnologyDescriptions.getCivilopediaTextLines(this, ruleset)
override fun era(ruleset: Ruleset) = ruleset.eras[era()]
fun matchesFilter(filter: String): Boolean {
return when (filter) {
in Constants.all -> true

View File

@ -27,6 +27,7 @@ class TileImprovement : RulesetStatsObject() {
// This is the base cost. A cost of 0 means created instead of buildable.
var turnsToBuild: Int = -1
override fun legacyRequiredTechs() = if (techRequired == null) emptySequence() else sequenceOf(techRequired!!)
fun getTurnsToBuild(civInfo: Civilization, unit: MapUnit): Int {
val state = StateForConditionals(civInfo, unit = unit)

View File

@ -68,14 +68,14 @@ interface IHasUniques : INamed {
fun requiredTechs(): Sequence<String> = legacyRequiredTechs() + techsRequiredByUniques()
fun requiredTechnologies(ruleset: Ruleset): Sequence<Technology?> =
requiredTechs().map{ ruleset.technologies[it] }
requiredTechs().map { ruleset.technologies[it] }
fun era(ruleset: Ruleset): Era? =
requiredTechnologies(ruleset).map{ it?.era() }.map{ ruleset.eras[it] }.maxByOrNull{ it?.eraNumber ?: 0 }
requiredTechnologies(ruleset).map { it?.era() }.map { ruleset.eras[it] }.maxByOrNull { it?.eraNumber ?: 0 }
// This will return null only if requiredTechnologies() is empty or all required techs have no eraNumber
fun techColumn(ruleset: Ruleset): TechColumn? =
requiredTechnologies(ruleset).map{ it?.column }.filterNotNull().maxByOrNull{ it.columnNumber }
requiredTechnologies(ruleset).map { it?.column }.filterNotNull().maxByOrNull { it.columnNumber }
// This will return null only if *all* required techs have null TechColumn.
fun availableInEra(ruleset: Ruleset, requestedEra: String): Boolean {

View File

@ -150,11 +150,10 @@ class ModManagementScreen private constructor(
// Replace the PickerScreen's descriptionLabel
val labelWrapper = Table()
labelWrapper.defaults().top().left().growX()
val labelScroll = descriptionLabel.parent as ScrollPane
descriptionLabel.remove()
labelWrapper.row()
labelWrapper.add(modDescriptionLabel).row()
labelScroll.actor = labelWrapper
descriptionScroll.actor = labelWrapper
isPortrait = isNarrowerThan4to3()
if (isPortrait) initPortrait()

View File

@ -2,7 +2,10 @@ package com.unciv.ui.screens.pickerscreens
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.InputEvent
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.logic.map.mapunit.MapUnit
@ -46,6 +49,7 @@ class ImprovementPickerScreen(
// Support for UniqueType.CreatesOneImprovement
private val tileMarkedForCreatesOneImprovement = tile.isMarkedForCreatesOneImprovement()
private val tileWithoutLastTerrain: Tile
private val maxErasForward = ruleset.modOptions.constants.maxImprovementTechErasForward.takeUnless { it < 0 } ?: Int.MAX_VALUE
private fun getRequiredTechColumn(improvement: TileImprovement) =
ruleset.technologies[improvement.techRequired]?.column?.columnNumber ?: -1
@ -75,6 +79,11 @@ class ImprovementPickerScreen(
accept(selectedImprovement)
}
descriptionLabel.onClick {
val link = selectedImprovement?.makeLink()
if (!link.isNullOrEmpty()) openCivilopedia(link)
}
val regularImprovements = Table()
regularImprovements.defaults().pad(5f)
@ -95,106 +104,26 @@ class ImprovementPickerScreen(
if (!unit.canBuildImprovement(improvement)) continue
val problemReport = getProblemReport(improvement) ?: continue
val image = ImageGetter.getImprovementPortrait(improvement.name, 30f)
// allow multiple key mappings to technologically supersede each other
var shortcutKey = improvement.shortcutKey
if (shortcutKey != null) {
val techLevel = getRequiredTechColumn(improvement)
val isSuperseded = ruleset.tileImprovements.values.asSequence()
// *other* improvements with same shortcutKey
.filter { it.shortcutKey == improvement.shortcutKey && it != improvement }
// civ can build it (checks tech researched)
.filter { tile.improvementFunctions.canBuildImprovement(it, currentPlayerCiv) }
// is technologically more advanced
.filter { getRequiredTechColumn(it) > techLevel }
.any()
// another supersedes this - ignore key binding
if (isSuperseded) shortcutKey = null
}
var labelText = improvement.name.tr(true)
val turnsToBuild = if (tile.improvementInProgress == improvement.name) tile.turnsToImprovement
else improvement.getTurnsToBuild(currentPlayerCiv, unit)
if (turnsToBuild > 0) labelText += " - $turnsToBuild${Fonts.turn}"
val provideResource = tile.hasViewableResource(currentPlayerCiv) && tile.tileResource.isImprovedBy(improvement.name)
if (provideResource) labelText += "\n" + "Provides [${tile.resource}]".tr()
val removeImprovement = (!improvement.isRoad()
&& !improvement.name.startsWith(Constants.remove)
&& improvement.name != Constants.cancelImprovementOrder)
if (tile.improvement != null && removeImprovement) labelText += "\n" + "Replaces [${tile.improvement}]".tr()
val statIcons = getStatIconsTable(provideResource, removeImprovement)
// get benefits of the new improvement
val stats = tile.stats.getStatDiffForImprovement(
improvement,
currentPlayerCiv,
tile.getCity(),
cityUniqueCache
)
//Warn when the current improvement will increase a stat for the tile,
// but the tile is outside of the range (> 3 tiles from any city center) that can be
// worked by a city's population
if (tile.owningCity != null
&& !improvement.isRoad()
&& stats.values.any { it > 0f }
&& !improvement.name.startsWith(Constants.remove)
&& !tile.getTilesInDistance(currentPlayerCiv.modConstants.cityWorkRange)
.any { it.isCityCenter() && it.getCity()!!.civ == currentPlayerCiv }
)
labelText += "\n" + "Not in city work range".tr()
val statsTable = getStatsTable(stats)
statIcons.add(statsTable).padLeft(13f)
regularImprovements.add(statIcons).align(Align.right)
val improvementButton = PickerPane.getPickerOptionButton(image, labelText)
// This is onClick without ActivationTypes.Keystroke equivalence - keys should select *and* close:
improvementButton.onActivation(type = ActivationTypes.Tap, noEquivalence = true) {
selectedImprovement = improvement
pick(improvement.name.tr())
descriptionLabel.setText(improvement.getDescription(ruleset))
}
improvementButton.onDoubleClick { accept(improvement) }
when {
improvement.name == tile.improvementInProgress ->
improvementButton.color = Color.GREEN
problemReport.isQueueable() ->
// TODO should be a skin ButtonStyle, this mixes with the style override from disable() below - which is a very wrong approach anyway
improvementButton.setColor(0.625f, 1f, 0.625f, 1f) // #a0ffa0 - brightened GREEN
}
if (!problemReport.isEmpty() || tileMarkedForCreatesOneImprovement) {
improvementButton.disable()
} else if (shortcutKey != null) {
// Shortcut keys trigger what onDoubleClick does, not equivalent to single Click:
improvementButton.keyShortcuts.add(shortcutKey) { accept(improvement) }
improvementButton.addTooltip(shortcutKey)
}
regularImprovements.add(improvementButton)
regularImprovements.add(getExplanationActor(improvement, problemReport)).padLeft(10f)
regularImprovements.row()
regularImprovements.addImprovementRow(improvement, problemReport, cityUniqueCache)
}
val ownerTable = Table()
if (tile.getOwner() == null) {
ownerTable.add("Unowned tile".toLabel())
ownerTable.add("Unowned tile".toLabel()).pad(10f)
} else if (tile.getOwner()!!.isCurrentPlayer()) {
val button = tile.getCity()!!.name.toTextButton(hideIcons = true)
button.onClick {
this.game.pushScreen(CityScreen(tile.getCity()!!, null, tile))
}
ownerTable.add("Tile owned by [${tile.getOwner()!!.civName}] (You)".toLabel()).padLeft(10f)
val label = "Tile owned by [${tile.getOwner()!!.civName}] (You)".toLabel()
label.onClick { openCivilopedia(tile.getOwner()!!.nation.makeLink()) }
ownerTable.add(label)
ownerTable.add(button).padLeft(20f)
ownerTable.padTop(2.5f) // The button causes the label to have ample padding, just unglue the button from the window border a little
} else {
ownerTable.add("Tile owned by [${tile.getOwner()!!.civName}] - [${tile.getCity()!!.name}]".toLabel()).padLeft(10f)
val label = "Tile owned by [${tile.getOwner()!!.civName}] - [${tile.getCity()!!.name}]".toLabel()
label.onClick { openCivilopedia(tile.getOwner()!!.nation.makeLink()) }
ownerTable.add(label).pad(10f)
}
topTable.add(ownerTable)
@ -202,12 +131,120 @@ class ImprovementPickerScreen(
topTable.add(regularImprovements)
}
private fun Table.addImprovementRow(improvement: TileImprovement, problemReport: ProblemReport, cityUniqueCache: LocalUniqueCache) {
val image = ImageGetter.getImprovementPortrait(improvement.name, 30f)
// allow multiple key mappings to technologically supersede each other
var shortcutKey = improvement.shortcutKey
if (shortcutKey != null) {
val techLevel = getRequiredTechColumn(improvement)
val isSuperseded = ruleset.tileImprovements.values.asSequence()
// *other* improvements with same shortcutKey
.filter { it.shortcutKey == improvement.shortcutKey && it != improvement }
// civ can build it (checks tech researched)
.filter { tile.improvementFunctions.canBuildImprovement(it, currentPlayerCiv) }
// is technologically more advanced
.filter { getRequiredTechColumn(it) > techLevel }
.any()
// another supersedes this - ignore key binding
if (isSuperseded) shortcutKey = null
}
var labelText = improvement.name.tr(true)
val turnsToBuild = if (tile.improvementInProgress == improvement.name) tile.turnsToImprovement
else improvement.getTurnsToBuild(currentPlayerCiv, unit)
if (turnsToBuild > 0) labelText += " - $turnsToBuild${Fonts.turn}"
val provideResource = tile.hasViewableResource(currentPlayerCiv) && tile.tileResource.isImprovedBy(improvement.name)
if (provideResource) labelText += "\n" + "Provides [${tile.resource}]".tr()
val removeImprovement = (!improvement.isRoad()
&& !improvement.name.startsWith(Constants.remove)
&& improvement.name != Constants.cancelImprovementOrder)
if (tile.improvement != null && removeImprovement) labelText += "\n" + "Replaces [${tile.improvement}]".tr()
val statIcons = getStatIconsTable(provideResource, removeImprovement)
// get benefits of the new improvement
val stats = tile.stats.getStatDiffForImprovement(
improvement,
currentPlayerCiv,
tile.getCity(),
cityUniqueCache
)
//Warn when the current improvement will increase a stat for the tile,
// but the tile is outside of the range (> 3 tiles from any city center) that can be
// worked by a city's population
if (tile.owningCity != null
&& !improvement.isRoad()
&& stats.values.any { it > 0f }
&& !improvement.name.startsWith(Constants.remove)
&& !tile.getTilesInDistance(currentPlayerCiv.modConstants.cityWorkRange)
.any { it.isCityCenter() && it.getCity()!!.civ == currentPlayerCiv }
)
labelText += "\n" + "Not in city work range".tr()
val statsTable = getStatsTable(stats)
statIcons.add(statsTable).padLeft(13f)
add(statIcons).align(Align.right)
val improvementButton = PickerPane.getPickerOptionButton(image, labelText)
// This is onClick without ActivationTypes.Keystroke equivalence - keys should select *and* close:
improvementButton.onActivation(type = ActivationTypes.Tap, noEquivalence = true) {
setDescription(improvement, Color.WHITE)
pick(improvement.name.tr())
}
when {
improvement.name == tile.improvementInProgress ->
improvementButton.color = Color.GREEN
problemReport.isQueueable() ->
// TODO should be a skin ButtonStyle, this mixes with the style override from disable() below - which is a very wrong approach anyway
improvementButton.setColor(0.625f, 1f, 0.625f, 1f) // #a0ffa0 - brightened GREEN
}
if (!problemReport.isEmpty() || tileMarkedForCreatesOneImprovement) {
improvementButton.disable()
// Now a little backhanded trick: We want to allow access to information on a disabled improvement
// isDisabled still prevents the Button class from firing its event, but our own ClickListener bypasses that and is fired from Actor code
improvementButton.touchable = Touchable.enabled
improvementButton.addListener(object : ClickListener() {
override fun clicked(event: InputEvent?, x: Float, y: Float) {
setDescription(improvement, Color.LIGHT_GRAY)
}
})
} else {
improvementButton.onDoubleClick { accept(improvement) }
if (shortcutKey != null) {
// Shortcut keys trigger what onDoubleClick does, not equivalent to single Click:
improvementButton.keyShortcuts.add(shortcutKey) { accept(improvement) }
improvementButton.addTooltip(shortcutKey)
}
}
add(improvementButton)
add(getExplanationActor(improvement, problemReport)).padLeft(10f)
row()
}
/** Sets the PickerPane's description and where in Civilopedia a click on it should go - but not the right side button */
private fun setDescription(improvement: TileImprovement, color: Color) {
selectedImprovement = improvement
descriptionLabel.setText(improvement.getDescription(ruleset))
descriptionLabel.color = color
}
private fun getStatIconsTable(provideResource: Boolean, removeImprovement: Boolean): Table {
val statIcons = Table()
// icon for adding the resource by improvement
if (provideResource)
statIcons.add(ImageGetter.getResourcePortrait(tile.resource.toString(), 30f)).pad(3f)
if (provideResource) {
val resourceIcon = ImageGetter.getResourcePortrait(tile.resource!!, 30f) // `!!` is covered by provideResource
val link = ruleset.tileResources[tile.resource]?.makeLink()
if (!link.isNullOrEmpty()) resourceIcon.onClick { openCivilopedia(link) }
statIcons.add(resourceIcon).pad(3f)
}
// icon for removing the resource by replacing improvement
if (removeImprovement && tile.hasViewableResource(currentPlayerCiv) && tile.improvement != null && tile.tileResource.isImprovedBy(tile.improvement!!)) {
@ -237,16 +274,17 @@ class ImprovementPickerScreen(
private class ProblemReport {
var suggestRemoval = false
var removalImprovement: TileImprovement? = null
val proposedSolutions = mutableListOf<String>()
/** `first` is the text, `second` the Civilopedia link */
val proposedSolutions = mutableSetOf<Pair<String, String?>>()
fun isEmpty() = proposedSolutions.isEmpty()
fun isQueueable() = removalImprovement != null && proposedSolutions.size == 1
fun toLabel() = proposedSolutions.joinToString("}\n{", "{", "}").toLabel()
}
private fun getProblemReport(improvement: TileImprovement): ProblemReport? {
private fun getProblemReport(improvement: TileImprovement) = getProblemReport(tile, tileWithoutLastTerrain, improvement)
private fun getProblemReport(tile: Tile, tileWithoutLastTerrain: Tile?, improvement: TileImprovement): ProblemReport? {
val report = ProblemReport()
var unbuildableBecause = tile.improvementFunctions.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
if (!canReport(unbuildableBecause)) {
if (!canReport(unbuildableBecause) && tileWithoutLastTerrain != null) {
// Try after pretending to have removed the top terrain layer.
unbuildableBecause = tileWithoutLastTerrain.improvementFunctions.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
if (!canReport(unbuildableBecause)) return null
@ -257,20 +295,31 @@ class ImprovementPickerScreen(
if (suggestRemoval) {
val removalName = Constants.remove + tile.lastTerrain.name
removalImprovement = ruleset.tileImprovements[removalName]
if (removalImprovement != null)
proposedSolutions.add("${Constants.remove}[${tile.lastTerrain.name}] first")
if (removalImprovement != null) {
val cannotRemoveReport = getProblemReport(tileWithoutLastTerrain!!, null, removalImprovement!!)
?: return null
proposedSolutions.addAll(cannotRemoveReport.proposedSolutions)
proposedSolutions.add("${Constants.remove}[${tile.lastTerrain.name}] first" to removalImprovement!!.makeLink())
}
}
if (ImprovementBuildingProblem.MissingTech in unbuildableBecause)
proposedSolutions.add("Research [${improvement.techRequired}] first")
if (ImprovementBuildingProblem.MissingTech in unbuildableBecause) {
val maxEraNumber = currentPlayerCiv.getEraNumber() + maxErasForward
for (tech in improvement.requiredTechnologies(ruleset)) {
val techEra = tech?.era(ruleset) ?: continue
if (techEra.eraNumber > maxEraNumber) return null
proposedSolutions.add("Research [${tech.name}] first" to tech.makeLink())
}
}
if (ImprovementBuildingProblem.NotJustOutsideBorders in unbuildableBecause)
proposedSolutions.add("Have this tile close to your borders")
proposedSolutions.add("Have this tile close to your borders" to null)
if (ImprovementBuildingProblem.OutsideBorders in unbuildableBecause)
proposedSolutions.add("Have this tile inside your empire")
proposedSolutions.add("Have this tile inside your empire" to null)
if (ImprovementBuildingProblem.MissingResources in unbuildableBecause) {
proposedSolutions.addAll(improvement.getMatchingUniques(UniqueType.ConsumesResources).filter {
currentPlayerCiv.getResourceAmount(it.params[1]) < it.params[0].toInt()
}.map { "Acquire more [$it]" })
val resources = improvement.getMatchingUniques(UniqueType.ConsumesResources)
.filter { currentPlayerCiv.getResourceAmount(it.params[1]) < it.params[0].toInt() }
.map { "Acquire more [${it.params[1]}]" to ruleset.tileResources[it.params[1]]?.makeLink() }
proposedSolutions.addAll(resources)
}
}
return report
@ -286,13 +335,15 @@ class ImprovementPickerScreen(
else -> getPickNowButton { accept(improvement) }
}
val label = report.toLabel()
if (!report.isQueueable()) return label
return Table().apply {
defaults().center()
add(label).padBottom(5f).row()
add(getPickNowButton { accept(report.removalImprovement, improvement) })
for ((text, link) in report.proposedSolutions) {
val label = text.toLabel()
if (!link.isNullOrEmpty()) label.onClick { openCivilopedia(link) }
add(label).padBottom(5f).row()
}
if (report.isQueueable())
add(getPickNowButton { accept(report.removalImprovement, improvement) }).padTop(5f)
}
}
}

View File

@ -2,6 +2,7 @@ package com.unciv.ui.screens.pickerscreens
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.ui.Button
import com.badlogic.gdx.scenes.scene2d.ui.Label
import com.badlogic.gdx.scenes.scene2d.ui.SplitPane
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup
@ -23,7 +24,9 @@ class PickerPane(
* Note if you don't use that helper, you'll need to do both click and keyboard support yourself. */
val closeButton = Constants.close.toTextButton()
/** A scrollable wrapped Label you can use to show descriptions in the [bottomTable], starts empty */
val descriptionLabel = "".toLabel()
val descriptionLabel = DescriptionLabel()
/** Wraps descriptionLabel - a subclass may replace the content, but beware: zero padding around this */
internal val descriptionScroll: AutoScrollPane
/** A wrapper containing [rightSideButton]. You can add buttons, they will be arranged vertically */
val rightSideGroup = VerticalGroup()
/** A button on the lower right of [bottomTable] you can use for a "OK"-type action, starts disabled */
@ -48,8 +51,10 @@ class PickerPane(
bottomTable.add(closeButton).pad(10f)
descriptionLabel.wrap = true
val labelScroll = AutoScrollPane(descriptionLabel, BaseScreen.skin)
bottomTable.add(labelScroll).pad(5f).fill().expand()
val descriptionWithPad = Table()
descriptionWithPad.add(descriptionLabel).pad(10f).grow()
descriptionScroll = AutoScrollPane(descriptionWithPad, BaseScreen.skin)
bottomTable.add(descriptionScroll).grow()
rightSideButton.disable()
rightSideGroup.addActor(rightSideButton)
@ -72,12 +77,28 @@ class PickerPane(
rightSideButton.isEnabled = enabled
}
/** Sets the text of the [rightSideButton] and enables it if it's the player's turn */
/** Sets the text of the [rightSideButton] (does not autotranslate) and enables it if it's the player's turn */
fun pick(rightButtonText: String) {
if (GUI.isMyTurn()) rightSideButton.enable()
rightSideButton.setText(rightButtonText)
}
/** This Label adjusts the Y scroll of its ascendant ScrollPane when its text is set:
* - short texts stay top-aligned with [closeButton]
* - taller texts scroll some of the wrapper top padding out
* - still taller texts scroll most but not all of the top padding out
*
* This also ensures vertical scroll is reset when changing description
*/
inner class DescriptionLabel : Label("", BaseScreen.skin) {
override fun setText(newText: CharSequence?) {
super.setText(newText)
descriptionScroll.validate()
descriptionScroll.scrollY = (prefHeight + 10f - descriptionScroll.height).coerceIn(0f, 8f)
descriptionScroll.updateVisualScroll()
}
}
companion object {
/** Icon size used in [getPickerOptionButton]. */
const val pickerOptionIconSize = 30f

View File

@ -13,6 +13,8 @@ open class PickerScreen(disableScroll: Boolean = false) : BaseScreen() {
val closeButton by pickerPane::closeButton
/** @see PickerPane.descriptionLabel */
val descriptionLabel by pickerPane::descriptionLabel
/** @see PickerPane.descriptionScroll */
protected val descriptionScroll by pickerPane::descriptionScroll
/** @see PickerPane.rightSideGroup */
val rightSideGroup by pickerPane::rightSideGroup
/** @see PickerPane.rightSideButton */

View File

@ -32,7 +32,6 @@ abstract class ReligionPickerScreenCommon(
protected val ruleset = gameInfo.ruleset
private val descriptionTable = Table(skin)
private val descriptionScroll = descriptionLabel.parent as ScrollPane
protected class Selection {
var button: Button? = null

View File

@ -217,6 +217,7 @@ and city distance in another. In case of conflicts, there is no guarantee which
| minimumWarDuration | Int | 10 | [^P] |
| baseTurnsUntilRevolt | Int | 4 | [^Q] |
| cityStateElectionTurns | Int | 15 | [^R] |
| maxImprovementTechErasForward | Int | None | [^S] |
Legend:
@ -255,6 +256,7 @@ Legend:
- [^P]: The number of turns a civ has to wait before negotiating for peace
- [^Q]: The number of turns before a revolt is spawned
- [^R]: The number of turns between city-state elections
- [^S]: If set, the Improvement picker will silently skip improvements whose tech requirement is more advanced than your current Era + this value. Example: With a 0, Trade posts will not show until the Medieval Era, with a 1 they will already show in the CLassical Era.
#### UnitUpgradeCost