mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-24 22:59:34 +07:00
City keyboard buy construction and tile (#4958)
* City keyboard buy construction and tile * City keyboard buy - a comment
This commit is contained in:
@ -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))
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user