City keyboard buy construction and tile (#4958)

* City keyboard buy construction and tile

* City keyboard buy - a comment
This commit is contained in:
SomeTroglodyte
2021-08-23 19:22:14 +02:00
committed by GitHub
parent 15c4b67781
commit df1695f782
3 changed files with 154 additions and 71 deletions

View File

@ -15,6 +15,7 @@ import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat
import com.unciv.models.translations.tr
import com.unciv.ui.utils.*
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
import kotlin.concurrent.thread
import kotlin.math.max
import kotlin.math.min
@ -29,6 +30,7 @@ import com.unciv.ui.utils.AutoScrollPane as ScrollPane
class CityConstructionsTable(private val cityScreen: CityScreen) {
/* -1 = Nothing, >= 0 queue entry (0 = current construction) */
private var selectedQueueEntry = -1 // None
private var preferredBuyStat = Stat.Gold // Used for keyboard buy
var improvementBuildingToConstruct: Building? = null
private val upperTable = Table(CameraStageBaseScreen.skin)
@ -405,7 +407,90 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
}
}
private fun getBuyButtons(construction: INonPerpetualConstruction?): List<TextButton> {
return Stat.statsUsableToBuy.mapNotNull { getBuyButton(construction, it) }
}
private fun getBuyButton(construction: INonPerpetualConstruction?, stat: Stat = Stat.Gold): TextButton? {
if (stat !in Stat.statsUsableToBuy || construction == null)
return null
val city = cityScreen.city
val button = "".toTextButton()
if (!isConstructionPurchaseShown(construction, stat)) {
// This can't ever be bought with the given currency.
// We want one disabled "buy" button without a price for "priceless" buildings such as wonders
// We don't want such a button when the construction can be bought using a different currency
if (stat != Stat.Gold || construction.canBePurchasedWithAnyStat(city))
return null
button.setText("Buy".tr())
button.disable()
} else {
val constructionBuyCost = construction.getStatBuyCost(city, stat)!!
button.setText("Buy".tr() + " " + constructionBuyCost + stat.character)
button.onClick {
button.disable()
askToBuyConstruction(construction, stat)
}
button.isEnabled = isConstructionPurchaseAllowed(construction, stat, constructionBuyCost)
button.addTooltip('B') // The key binding is done in CityScreen constructor
preferredBuyStat = stat // Not very intelligent, but the least common currency "wins"
}
button.labelCell.pad(5f)
return button
}
/** Ask whether user wants to buy [construction] for [stat].
*
* Used from onClick and keyboard dispatch, thus only minimal parameters are passed,
* and it needs to do all checks and the sound as appropriate.
*/
fun askToBuyConstruction(construction: INonPerpetualConstruction, stat: Stat = preferredBuyStat) {
if (!isConstructionPurchaseShown(construction, stat)) return
val city = cityScreen.city
val constructionBuyCost = construction.getStatBuyCost(city, stat)!!
if (!isConstructionPurchaseAllowed(construction, stat, constructionBuyCost)) return
cityScreen.closeAllPopups()
val purchasePrompt = "Currently you have [${city.getStatReserve(stat)}] [${stat.name}].".tr() + "\n\n" +
"Would you like to purchase [${construction.name}] for [$constructionBuyCost] [${stat.character}]?".tr()
YesNoPopup(
purchasePrompt,
action = { purchaseConstruction(construction, stat) },
screen = cityScreen,
restoreDefault = { cityScreen.update() }
).open()
}
/** This tests whether the buy button should be _shown_ */
private fun isConstructionPurchaseShown(construction: INonPerpetualConstruction, stat: Stat): Boolean {
val city = cityScreen.city
return construction.canBePurchasedWithStat(city, stat) || city.civInfo.gameInfo.gameParameters.godMode
}
/** This tests whether the buy button should be _enabled_ */
private fun isConstructionPurchaseAllowed(construction: INonPerpetualConstruction, stat: Stat, constructionBuyCost: Int): Boolean {
val city = cityScreen.city
return when {
city.isPuppet -> false
!cityScreen.canChangeState -> false
city.isInResistance() -> false
!construction.isPurchasable(city.cityConstructions) -> false // checks via 'rejection reason'
!city.canPurchase(construction) -> false // checks room on map for units
city.civInfo.gameInfo.gameParameters.godMode -> true
constructionBuyCost == 0 -> true
else -> city.getStatReserve(stat) >= constructionBuyCost
}
}
// called only by askToBuyConstruction's Yes answer
private fun purchaseConstruction(construction: INonPerpetualConstruction, stat: Stat = Stat.Gold) {
Sounds.play(stat.purchaseSound)
val city = cityScreen.city
if (!city.cityConstructions.purchaseConstruction(construction.name, selectedQueueEntry, false, stat)) {
Popup(cityScreen).apply {
@ -418,64 +503,15 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
if (isSelectedQueueEntry() || cityScreen.selectedConstruction?.isBuildable(city.cityConstructions) != true) {
selectedQueueEntry = -1
cityScreen.selectedConstruction = null
// Allow buying next queued or auto-assigned construction right away
city.cityConstructions.chooseNextConstruction()
if (city.cityConstructions.currentConstructionFromQueue.isNotEmpty())
cityScreen.selectedConstruction = city.cityConstructions.getCurrentConstruction().takeIf { it is INonPerpetualConstruction }
}
cityScreen.update()
}
private fun getBuyButtons(construction: INonPerpetualConstruction?): List<TextButton> {
return Stat.statsUsableToBuy.mapNotNull { getBuyButton(construction, it) }
}
private fun getBuyButton(construction: INonPerpetualConstruction?, stat: Stat = Stat.Gold): TextButton? {
if (stat !in Stat.statsUsableToBuy || construction == null)
return null
val city = cityScreen.city
val button = "".toTextButton()
if (!construction.canBePurchasedWithStat(city, stat) && !city.civInfo.gameInfo.gameParameters.godMode) {
// This can't ever be bought with the given currency.
// We want one disabled "buy" button without a price for "priceless" buildings such as wonders
// We don't want such a button when the construction can be bought using a different currency
if (stat != Stat.Gold || construction.canBePurchasedWithAnyStat(city))
return null
button.setText("Buy".tr())
button.disable()
} else {
val constructionBuyCost = construction.getStatBuyCost(city, stat)!!
button.setText("Buy".tr() + " " + constructionBuyCost + stat.character)
button.onClick(stat.purchaseSound) {
button.disable()
cityScreen.closeAllPopups()
val purchasePrompt = "Currently you have [${city.getStatReserve(stat)}] [${stat.name}].".tr() + "\n\n" +
"Would you like to purchase [${construction.name}] for [$constructionBuyCost] [${stat.character}]?".tr()
YesNoPopup(
purchasePrompt,
action = { purchaseConstruction(construction, stat) },
screen = cityScreen,
restoreDefault = { cityScreen.update() }
).apply {
promptLabel.setAlignment(Align.center)
open()
}
}
if (!cityScreen.canChangeState
|| !construction.isPurchasable(city.cityConstructions)
|| city.isPuppet
|| city.isInResistance()
|| !city.canPurchase(construction)
|| (constructionBuyCost > city.getStatReserve(stat) && !city.civInfo.gameInfo.gameParameters.godMode)
) button.disable()
}
button.labelCell.pad(5f)
return button
}
private fun getRaisePriorityButton(constructionQueueIndex: Int, name: String, city: CityInfo): Table {
val tab = Table()
tab.add(ImageGetter.getImage("OtherIcons/Up").surroundWithCircle(40f))

View File

@ -7,21 +7,23 @@ import com.badlogic.gdx.utils.Align
import com.unciv.UncivGame
import com.unciv.logic.city.CityInfo
import com.unciv.logic.city.IConstruction
import com.unciv.logic.city.INonPerpetualConstruction
import com.unciv.logic.map.TileInfo
import com.unciv.ui.map.TileGroupMap
import com.unciv.ui.tilegroups.TileSetStrings
import com.unciv.ui.utils.*
import java.util.*
class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() {
class CityScreen(
internal val city: CityInfo,
var selectedConstruction: IConstruction? = null,
var selectedTile: TileInfo? = null
): CameraStageBaseScreen() {
companion object {
/** Distance from stage edges to floating widgets */
const val posFromEdge = 5f
}
var selectedTile: TileInfo? = null
var selectedConstruction: IConstruction? = null
/** Toggles or adds/removes all state changing buttons */
val canChangeState = UncivGame.Current.worldScreen.canChangeState
@ -84,6 +86,14 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() {
keyPressDispatcher[Input.Keys.LEFT] = { page(-1) }
keyPressDispatcher[Input.Keys.RIGHT] = { page(1) }
keyPressDispatcher['T'] = {
if (selectedTile != null)
tileTable.askToBuyTile(selectedTile!!)
}
keyPressDispatcher['B'] = {
if (selectedConstruction is INonPerpetualConstruction)
constructionsTable.askToBuyConstruction(selectedConstruction as INonPerpetualConstruction)
}
}
internal fun update() {

View File

@ -11,6 +11,7 @@ import com.unciv.models.translations.tr
import com.unciv.ui.civilopedia.CivilopediaScreen
import com.unciv.ui.civilopedia.MarkupRenderer
import com.unciv.ui.utils.*
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
import kotlin.math.roundToInt
class CityScreenTileTable(private val cityScreen: CityScreen): Table() {
@ -42,22 +43,15 @@ class CityScreenTileTable(private val cityScreen: CityScreen): Table() {
innerTable.row()
innerTable.add(getTileStatsTable(stats)).row()
if (selectedTile.getOwner() == null && selectedTile.neighbors.any { it.getCity() == city }
&& selectedTile in city.tilesInRange) {
if (isTilePurchaseShown(selectedTile)) {
val goldCostOfTile = city.expansion.getGoldCostOfTile(selectedTile)
val buyTileButton = "Buy for [$goldCostOfTile] gold".toTextButton()
buyTileButton.onClick(UncivSound.Coin) {
val purchasePrompt = "Currently you have [${city.civInfo.gold}] [Gold].".tr() + "\n\n" +
"Would you like to purchase [Tile] for [$goldCostOfTile] [${Stat.Gold.character}]?".tr()
YesNoPopup(purchasePrompt, { city.expansion.buyTile(selectedTile);UncivGame.Current.setScreen(CityScreen(city)) }, cityScreen).open()
}
val canPurchase = goldCostOfTile == 0 || city.civInfo.gold >= goldCostOfTile
if (!canPurchase && !city.civInfo.gameInfo.gameParameters.godMode
|| city.isPuppet
|| !cityScreen.canChangeState)
buyTileButton.onClick {
buyTileButton.disable()
askToBuyTile(selectedTile)
}
buyTileButton.isEnabled = isTilePurchaseAllowed(goldCostOfTile)
buyTileButton.addTooltip('T') // The key binding is done in CityScreen constructor
innerTable.add(buyTileButton).padTop(5f).row()
}
@ -92,6 +86,49 @@ class CityScreenTileTable(private val cityScreen: CityScreen): Table() {
pack()
}
/** Ask whether user wants to buy [selectedTile] for gold.
*
* Used from onClick and keyboard dispatch, thus only minimal parameters are passed,
* and it needs to do all checks and the sound as appropriate.
*/
fun askToBuyTile(selectedTile: TileInfo) {
// These checks are redundant for the onClick action, but not for the keyboard binding
if (!isTilePurchaseShown(selectedTile)) return
val goldCostOfTile = city.expansion.getGoldCostOfTile(selectedTile)
if (!isTilePurchaseAllowed(goldCostOfTile)) return
cityScreen.closeAllPopups()
val purchasePrompt = "Currently you have [${city.civInfo.gold}] [Gold].".tr() + "\n\n" +
"Would you like to purchase [Tile] for [$goldCostOfTile] [${Stat.Gold.character}]?".tr()
YesNoPopup(
purchasePrompt,
action = {
Sounds.play(UncivSound.Coin)
city.expansion.buyTile(selectedTile)
// preselect the next tile on city screen rebuild so bulk buying can go faster
UncivGame.Current.setScreen(CityScreen(city, selectedTile = city.expansion.chooseNewTileToOwn()))
},
screen = cityScreen,
restoreDefault = { cityScreen.update() }
).open()
}
/** This tests whether the buy button should be _shown_ */
private fun isTilePurchaseShown(selectedTile: TileInfo) = when {
selectedTile.getOwner() != null -> false
selectedTile !in city.tilesInRange -> false
else -> selectedTile.neighbors.any { it.getCity() == city }
}
/** This tests whether the buy button should be _enabled_ */
private fun isTilePurchaseAllowed(goldCostOfTile: Int) = when {
city.isPuppet -> false
!cityScreen.canChangeState -> false
city.civInfo.gameInfo.gameParameters.godMode -> true
goldCostOfTile == 0 -> true
else -> city.civInfo.gold >= goldCostOfTile
}
private fun getTileStatsTable(stats: Stats): Table {
val statsTable = Table()
statsTable.defaults().pad(2f)