mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-29 06:51:30 -04:00
City keyboard buy construction and tile (#4958)
* City keyboard buy construction and tile * City keyboard buy - a comment
This commit is contained in:
parent
15c4b67781
commit
df1695f782
@ -15,6 +15,7 @@ import com.unciv.models.ruleset.unit.BaseUnit
|
|||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.utils.*
|
import com.unciv.ui.utils.*
|
||||||
|
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -29,6 +30,7 @@ import com.unciv.ui.utils.AutoScrollPane as ScrollPane
|
|||||||
class CityConstructionsTable(private val cityScreen: CityScreen) {
|
class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||||
/* -1 = Nothing, >= 0 queue entry (0 = current construction) */
|
/* -1 = Nothing, >= 0 queue entry (0 = current construction) */
|
||||||
private var selectedQueueEntry = -1 // None
|
private var selectedQueueEntry = -1 // None
|
||||||
|
private var preferredBuyStat = Stat.Gold // Used for keyboard buy
|
||||||
var improvementBuildingToConstruct: Building? = null
|
var improvementBuildingToConstruct: Building? = null
|
||||||
|
|
||||||
private val upperTable = Table(CameraStageBaseScreen.skin)
|
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) {
|
private fun purchaseConstruction(construction: INonPerpetualConstruction, stat: Stat = Stat.Gold) {
|
||||||
|
Sounds.play(stat.purchaseSound)
|
||||||
val city = cityScreen.city
|
val city = cityScreen.city
|
||||||
if (!city.cityConstructions.purchaseConstruction(construction.name, selectedQueueEntry, false, stat)) {
|
if (!city.cityConstructions.purchaseConstruction(construction.name, selectedQueueEntry, false, stat)) {
|
||||||
Popup(cityScreen).apply {
|
Popup(cityScreen).apply {
|
||||||
@ -418,64 +503,15 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
if (isSelectedQueueEntry() || cityScreen.selectedConstruction?.isBuildable(city.cityConstructions) != true) {
|
if (isSelectedQueueEntry() || cityScreen.selectedConstruction?.isBuildable(city.cityConstructions) != true) {
|
||||||
selectedQueueEntry = -1
|
selectedQueueEntry = -1
|
||||||
cityScreen.selectedConstruction = null
|
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()
|
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 {
|
private fun getRaisePriorityButton(constructionQueueIndex: Int, name: String, city: CityInfo): Table {
|
||||||
val tab = Table()
|
val tab = Table()
|
||||||
tab.add(ImageGetter.getImage("OtherIcons/Up").surroundWithCircle(40f))
|
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.UncivGame
|
||||||
import com.unciv.logic.city.CityInfo
|
import com.unciv.logic.city.CityInfo
|
||||||
import com.unciv.logic.city.IConstruction
|
import com.unciv.logic.city.IConstruction
|
||||||
|
import com.unciv.logic.city.INonPerpetualConstruction
|
||||||
import com.unciv.logic.map.TileInfo
|
import com.unciv.logic.map.TileInfo
|
||||||
import com.unciv.ui.map.TileGroupMap
|
import com.unciv.ui.map.TileGroupMap
|
||||||
import com.unciv.ui.tilegroups.TileSetStrings
|
import com.unciv.ui.tilegroups.TileSetStrings
|
||||||
import com.unciv.ui.utils.*
|
import com.unciv.ui.utils.*
|
||||||
import java.util.*
|
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 {
|
companion object {
|
||||||
/** Distance from stage edges to floating widgets */
|
/** Distance from stage edges to floating widgets */
|
||||||
const val posFromEdge = 5f
|
const val posFromEdge = 5f
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedTile: TileInfo? = null
|
|
||||||
var selectedConstruction: IConstruction? = null
|
|
||||||
|
|
||||||
/** Toggles or adds/removes all state changing buttons */
|
/** Toggles or adds/removes all state changing buttons */
|
||||||
val canChangeState = UncivGame.Current.worldScreen.canChangeState
|
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.LEFT] = { page(-1) }
|
||||||
keyPressDispatcher[Input.Keys.RIGHT] = { 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() {
|
internal fun update() {
|
||||||
|
@ -11,6 +11,7 @@ import com.unciv.models.translations.tr
|
|||||||
import com.unciv.ui.civilopedia.CivilopediaScreen
|
import com.unciv.ui.civilopedia.CivilopediaScreen
|
||||||
import com.unciv.ui.civilopedia.MarkupRenderer
|
import com.unciv.ui.civilopedia.MarkupRenderer
|
||||||
import com.unciv.ui.utils.*
|
import com.unciv.ui.utils.*
|
||||||
|
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class CityScreenTileTable(private val cityScreen: CityScreen): Table() {
|
class CityScreenTileTable(private val cityScreen: CityScreen): Table() {
|
||||||
@ -42,22 +43,15 @@ class CityScreenTileTable(private val cityScreen: CityScreen): Table() {
|
|||||||
innerTable.row()
|
innerTable.row()
|
||||||
innerTable.add(getTileStatsTable(stats)).row()
|
innerTable.add(getTileStatsTable(stats)).row()
|
||||||
|
|
||||||
if (selectedTile.getOwner() == null && selectedTile.neighbors.any { it.getCity() == city }
|
if (isTilePurchaseShown(selectedTile)) {
|
||||||
&& selectedTile in city.tilesInRange) {
|
|
||||||
val goldCostOfTile = city.expansion.getGoldCostOfTile(selectedTile)
|
val goldCostOfTile = city.expansion.getGoldCostOfTile(selectedTile)
|
||||||
|
|
||||||
val buyTileButton = "Buy for [$goldCostOfTile] gold".toTextButton()
|
val buyTileButton = "Buy for [$goldCostOfTile] gold".toTextButton()
|
||||||
buyTileButton.onClick(UncivSound.Coin) {
|
buyTileButton.onClick {
|
||||||
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.disable()
|
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()
|
innerTable.add(buyTileButton).padTop(5f).row()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +86,49 @@ class CityScreenTileTable(private val cityScreen: CityScreen): Table() {
|
|||||||
pack()
|
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 {
|
private fun getTileStatsTable(stats: Stats): Table {
|
||||||
val statsTable = Table()
|
val statsTable = Table()
|
||||||
statsTable.defaults().pad(2f)
|
statsTable.defaults().pad(2f)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user