UnitActionType now knows keys, sounds and most icons (#4607)

* Removed Char-only restriction in keyboard support for unit actions, remap a few keys, key tooltips for non-ascii keys

* UnitActionType now knows keys, sounds and most icons

* UnitActionType empower - fix Prophets spreading Pantheons
This commit is contained in:
SomeTroglodyte
2021-07-29 17:16:49 +02:00
committed by GitHub
parent cf367ad313
commit 4e249a0a1c
4 changed files with 163 additions and 141 deletions

View File

@ -1,42 +1,144 @@
package com.unciv.models package com.unciv.models
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.unciv.ui.utils.KeyCharAndCode
import com.unciv.ui.utils.ImageGetter
import com.unciv.Constants
import com.unciv.models.translations.equalsPlaceholderText
import com.unciv.models.translations.getPlaceholderParameters
/** Unit Actions - class - carries dynamic data and actual execution.
* Static properties are in [UnitActionType].
* Note this is for the buttons offering actions, not the ongoing action stored with a [MapUnit][com.unciv.logic.map.MapUnit]
*/
data class UnitAction( data class UnitAction(
val type: UnitActionType, val type: UnitActionType,
val title: String = type.value, val title: String = type.value,
val isCurrentAction: Boolean = false, val isCurrentAction: Boolean = false,
val uncivSound: UncivSound = UncivSound.Click, val uncivSound: UncivSound = type.uncivSound,
val action: (() -> Unit)? = null val action: (() -> Unit)? = null
) ) {
fun getIcon(): Actor {
if (type.imageGetter != null) return type.imageGetter.invoke()
return when {
type == UnitActionType.Upgrade
&& title.equalsPlaceholderText("Upgrade to [] ([] gold)") -> {
ImageGetter.getUnitIcon(title.getPlaceholderParameters()[0])
}
type == UnitActionType.Create
&& title.equalsPlaceholderText("Create []") -> {
ImageGetter.getImprovementIcon(title.getPlaceholderParameters()[0])
}
type == UnitActionType.SpreadReligion
&& title.equalsPlaceholderText("Spread []") -> {
val religionName = title.getPlaceholderParameters()[0]
ImageGetter.getReligionIcon(
if (ImageGetter.religionIconExists(religionName)) religionName else "Pantheon"
).apply { color = Color.BLACK }
}
type == UnitActionType.Fortify || type == UnitActionType.FortifyUntilHealed -> {
val match = fortificationRegex.matchEntire(title)
val percentFortified = match?.groups?.get(1)?.value?.toInt() ?: 0
ImageGetter.getImage("OtherIcons/Shield").apply {
color = Color.BLACK.cpy().lerp(Color.GREEN, percentFortified / 80f)
}
}
else -> ImageGetter.getImage("OtherIcons/Star")
}
}
companion object {
private val fortificationRegex = Regex(""".* (\d+)%""")
}
}
enum class UnitActionType(val value: String) { /** Unit Actions - generic enum with static properties
SwapUnits("Swap units"), *
Automate("Automate"), * @param value _default_ label to display, can be overridden in UnitAction instantiation
StopAutomation("Stop automation"), * @param imageGetter optional lambda to get an Icon - `null` if icon is dependent on outside factors and needs special handling
StopMovement("Stop movement"), * @param key keyboard binding - can be a [KeyCharAndCode], a [Char], or omitted.
Sleep("Sleep"), * @param uncivSound _default_ sound, can be overridden in UnitAction instantiation
SleepUntilHealed("Sleep until healed"), */
Fortify("Fortify"), enum class UnitActionType(
FortifyUntilHealed("Fortify until healed"), val value: String,
Explore("Explore"), val imageGetter: (()-> Actor)?,
StopExploration("Stop exploration"), val key: KeyCharAndCode,
Promote("Promote"), val uncivSound: UncivSound = UncivSound.Click
Upgrade("Upgrade"), ) {
Pillage("Pillage"), SwapUnits("Swap units",
Paradrop("Paradrop"), { ImageGetter.getImage("OtherIcons/Swap") }, 'y'),
SetUp("Set up"), Automate("Automate",
FoundCity("Found city"), { ImageGetter.getUnitIcon("Great Engineer") }, 'm'),
ConstructImprovement("Construct improvement"), StopAutomation("Stop automation",
{ ImageGetter.getImage("OtherIcons/Stop") }, 'm'),
StopMovement("Stop movement",
{ imageGetStopMove() }, '.'),
Sleep("Sleep",
{ ImageGetter.getImage("OtherIcons/Sleep") }, 'f'),
SleepUntilHealed("Sleep until healed",
{ ImageGetter.getImage("OtherIcons/Sleep") }, 'h'),
Fortify("Fortify",
null, 'f', UncivSound.Fortify),
FortifyUntilHealed("Fortify until healed",
null, 'h', UncivSound.Fortify),
Explore("Explore",
{ ImageGetter.getUnitIcon("Scout") }, 'x'),
StopExploration("Stop exploration",
{ ImageGetter.getImage("OtherIcons/Stop") }, 'x'),
Promote("Promote",
{ imageGetPromote() }, 'o', UncivSound.Promote),
Upgrade("Upgrade",
null, 'u', UncivSound.Upgrade),
Pillage("Pillage",
{ ImageGetter.getImage("OtherIcons/Pillage") }, 'p'),
Paradrop("Paradrop",
{ ImageGetter.getUnitIcon("Paratrooper") }, 'p'),
SetUp("Set up",
{ ImageGetter.getUnitIcon("Catapult") }, 't', UncivSound.Setup),
FoundCity("Found city",
{ ImageGetter.getUnitIcon(Constants.settler) }, 'c', UncivSound.Silent),
ConstructImprovement("Construct improvement",
{ ImageGetter.getUnitIcon(Constants.worker) }, 'i'),
// Deprecated since 3.15.4 // Deprecated since 3.15.4
ConstructRoad("Construct road"), ConstructRoad("Construct road", {ImageGetter.getImprovementIcon("Road")}, 'r'),
// //
Create("Create"), Create("Create",
SpreadReligion("Spread Religion"), null, 'i', UncivSound.Chimes),
HurryResearch("Hurry Research"), SpreadReligion("Spread Religion",
StartGoldenAge("Start Golden Age"), null, 'g', UncivSound.Choir),
HurryWonder("Hurry Wonder"), HurryResearch("Hurry Research",
ConductTradeMission("Conduct Trade Mission"), { ImageGetter.getUnitIcon("Great Scientist") }, 'g', UncivSound.Chimes),
FoundReligion("Found a Religion"), StartGoldenAge("Start Golden Age",
DisbandUnit("Disband unit"), { ImageGetter.getUnitIcon("Great Artist") }, 'g', UncivSound.Chimes),
GiftUnit("Gift unit"), HurryWonder("Hurry Wonder",
ShowAdditionalActions("Show more"), { ImageGetter.getUnitIcon("Great Engineer") }, 'g', UncivSound.Chimes),
} ConductTradeMission("Conduct Trade Mission",
{ ImageGetter.getUnitIcon("Great Merchant") }, 'g', UncivSound.Chimes),
FoundReligion("Found a Religion",
{ ImageGetter.getUnitIcon("Great Prophet") }, 'g', UncivSound.Choir),
DisbandUnit("Disband unit",
{ ImageGetter.getImage("OtherIcons/DisbandUnit") }, KeyCharAndCode.DEL),
GiftUnit("Gift unit",
{ ImageGetter.getImage("OtherIcons/Present") }, UncivSound.Silent),
ShowAdditionalActions("Show more",
{ imageGetShowMore() }, KeyCharAndCode(Input.Keys.PAGE_DOWN)),
HideAdditionalActions("Back",
{ imageGetHideMore() }, KeyCharAndCode(Input.Keys.PAGE_UP)),
;
// Allow shorter initializations
constructor(value: String, imageGetter: (() -> Actor)?, key: Char, uncivSound: UncivSound = UncivSound.Click)
: this(value, imageGetter, KeyCharAndCode(key), uncivSound)
constructor(value: String, imageGetter: (() -> Actor)?, uncivSound: UncivSound = UncivSound.Click)
: this(value, imageGetter, KeyCharAndCode.UNKNOWN, uncivSound)
companion object {
// readability factories
private fun imageGetStopMove() = ImageGetter.getStatIcon("Movement").apply { color = Color.RED }
private fun imageGetPromote() = ImageGetter.getImage("OtherIcons/Star").apply { color = Color.GOLD }
private fun imageGetShowMore() = ImageGetter.getImage("OtherIcons/ArrowRight").apply { color = Color.BLACK }
private fun imageGetHideMore() = ImageGetter.getImage("OtherIcons/ArrowLeft").apply { color = Color.BLACK }
}
}

View File

@ -291,6 +291,7 @@ object ImageGetter {
return circle return circle
} }
fun religionIconExists(iconName: String) = imageExists("ReligionIcons/$iconName")
fun getReligionIcon(iconName: String): Image { fun getReligionIcon(iconName: String): Image {
return getImage("ReligionIcons/$iconName") return getImage("ReligionIcons/$iconName")
} }

View File

@ -156,7 +156,7 @@ object UnitActions {
if (!unit.hasUnique("Founds a new city") || tile.isWater) return null if (!unit.hasUnique("Founds a new city") || tile.isWater) return null
if (unit.currentMovement <= 0 || tile.getTilesInDistance(3).any { it.isCityCenter() }) if (unit.currentMovement <= 0 || tile.getTilesInDistance(3).any { it.isCityCenter() })
return UnitAction(UnitActionType.FoundCity, uncivSound = UncivSound.Silent, action = null) return UnitAction(UnitActionType.FoundCity, action = null)
val foundAction = { val foundAction = {
UncivGame.Current.settings.addCompletedTutorialTask("Found city") UncivGame.Current.settings.addCompletedTutorialTask("Found city")
@ -168,14 +168,14 @@ object UnitActions {
} }
if (unit.civInfo.playerType == PlayerType.AI) if (unit.civInfo.playerType == PlayerType.AI)
return UnitAction(UnitActionType.FoundCity, uncivSound = UncivSound.Silent, action = foundAction) return UnitAction(UnitActionType.FoundCity, action = foundAction)
return UnitAction( return UnitAction(
type = UnitActionType.FoundCity, type = UnitActionType.FoundCity,
uncivSound = UncivSound.Chimes, uncivSound = UncivSound.Chimes,
action = { action = {
// check if we would be breaking a promise // check if we would be breaking a promise
val leaders = TestPromiseNotToSettle(unit.civInfo, tile) val leaders = testPromiseNotToSettle(unit.civInfo, tile)
if (leaders == null) if (leaders == null)
foundAction() foundAction()
else { else {
@ -193,7 +193,7 @@ object UnitActions {
* @param tile The tile where the new city would go * @param tile The tile where the new city would go
* @return null if no promises broken, else a String listing the leader(s) we would p* off. * @return null if no promises broken, else a String listing the leader(s) we would p* off.
*/ */
private fun TestPromiseNotToSettle(civInfo: CivilizationInfo, tile: TileInfo): String? { private fun testPromiseNotToSettle(civInfo: CivilizationInfo, tile: TileInfo): String? {
val brokenPromises = HashSet<String>() val brokenPromises = HashSet<String>()
for (otherCiv in civInfo.getKnownCivs().filter { it.isMajorCiv() && !civInfo.isAtWarWith(it) }) { for (otherCiv in civInfo.getKnownCivs().filter { it.isMajorCiv() && !civInfo.isAtWarWith(it) }) {
val diplomacyManager = otherCiv.getDiplomacyManager(civInfo) val diplomacyManager = otherCiv.getDiplomacyManager(civInfo)
@ -211,7 +211,6 @@ object UnitActions {
if (unit.isCivilian() || !unit.promotions.canBePromoted()) return if (unit.isCivilian() || !unit.promotions.canBePromoted()) return
// promotion does not consume movement points, so we can do it always // promotion does not consume movement points, so we can do it always
actionList += UnitAction(UnitActionType.Promote, actionList += UnitAction(UnitActionType.Promote,
uncivSound = UncivSound.Promote,
action = { action = {
UncivGame.Current.setScreen(PromotionPickerScreen(unit)) UncivGame.Current.setScreen(PromotionPickerScreen(unit))
}) })
@ -222,7 +221,6 @@ object UnitActions {
val isSetUp = unit.action == "Set Up" val isSetUp = unit.action == "Set Up"
actionList += UnitAction(UnitActionType.SetUp, actionList += UnitAction(UnitActionType.SetUp,
isCurrentAction = isSetUp, isCurrentAction = isSetUp,
uncivSound = UncivSound.Setup,
action = { action = {
unit.action = Constants.unitActionSetUp unit.action = Constants.unitActionSetUp
unit.useMovementPoints(1f) unit.useMovementPoints(1f)
@ -251,7 +249,7 @@ object UnitActions {
private fun addPillageAction(unit: MapUnit, actionList: ArrayList<UnitAction>, worldScreen: WorldScreen) { private fun addPillageAction(unit: MapUnit, actionList: ArrayList<UnitAction>, worldScreen: WorldScreen) {
val pillageAction = getPillageAction(unit) val pillageAction = getPillageAction(unit)
if (pillageAction == null) return ?: return
if (pillageAction.action == null) if (pillageAction.action == null)
actionList += UnitAction(UnitActionType.Pillage, action = null) actionList += UnitAction(UnitActionType.Pillage, action = null)
else actionList += UnitAction(type = UnitActionType.Pillage) { else actionList += UnitAction(type = UnitActionType.Pillage) {
@ -268,7 +266,7 @@ object UnitActions {
return UnitAction(UnitActionType.Pillage, return UnitAction(UnitActionType.Pillage,
action = { action = {
// http://well-of-souls.com/civ/civ5_improvements.html says that naval improvements are destroyed upon pilllage // http://well-of-souls.com/civ/civ5_improvements.html says that naval improvements are destroyed upon pillage
// and I can't find any other sources so I'll go with that // and I can't find any other sources so I'll go with that
if (tile.isLand) { if (tile.isLand) {
tile.improvementInProgress = tile.improvement tile.improvementInProgress = tile.improvement
@ -309,23 +307,22 @@ object UnitActions {
return UnitAction(UnitActionType.Upgrade, return UnitAction(UnitActionType.Upgrade,
title = "Upgrade to [${upgradedUnit.name}] ([$goldCostOfUpgrade] gold)", title = "Upgrade to [${upgradedUnit.name}] ([$goldCostOfUpgrade] gold)",
uncivSound = UncivSound.Upgrade,
action = { action = {
unit.civInfo.addGold(-goldCostOfUpgrade) unit.civInfo.addGold(-goldCostOfUpgrade)
val unitTile = unit.getTile() val unitTile = unit.getTile()
unit.destroy() unit.destroy()
val newunit = unit.civInfo.placeUnitNearTile(unitTile.position, upgradedUnit.name)!! val newUnit = unit.civInfo.placeUnitNearTile(unitTile.position, upgradedUnit.name)!!
newunit.health = unit.health newUnit.health = unit.health
newunit.promotions = unit.promotions newUnit.promotions = unit.promotions
newunit.instanceName = unit.instanceName newUnit.instanceName = unit.instanceName
for (promotion in newunit.baseUnit.promotions) for (promotion in newUnit.baseUnit.promotions)
if (promotion !in newunit.promotions.promotions) if (promotion !in newUnit.promotions.promotions)
newunit.promotions.addPromotion(promotion, true) newUnit.promotions.addPromotion(promotion, true)
newunit.updateUniques() newUnit.updateUniques()
newunit.updateVisibleTiles() newUnit.updateVisibleTiles()
newunit.currentMovement = 0f newUnit.currentMovement = 0f
}.takeIf { }.takeIf {
unit.civInfo.gold >= goldCostOfUpgrade && !unit.isEmbarked() unit.civInfo.gold >= goldCostOfUpgrade && !unit.isEmbarked()
&& unit.currentMovement == unit.getMaxMovement().toFloat() && unit.currentMovement == unit.getMaxMovement().toFloat()
@ -367,7 +364,6 @@ object UnitActions {
if (unit.currentMovement > 0) for (unique in unit.getUniques()) when (unique.placeholderText) { if (unit.currentMovement > 0) for (unique in unit.getUniques()) when (unique.placeholderText) {
"Can hurry technology research" -> { "Can hurry technology research" -> {
actionList += UnitAction(UnitActionType.HurryResearch, actionList += UnitAction(UnitActionType.HurryResearch,
uncivSound = UncivSound.Chimes,
action = { action = {
unit.civInfo.tech.addScience(unit.civInfo.tech.getScienceFromGreatScientist()) unit.civInfo.tech.addScience(unit.civInfo.tech.getScienceFromGreatScientist())
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
@ -378,7 +374,6 @@ object UnitActions {
"Can start an []-turn golden age" -> { "Can start an []-turn golden age" -> {
val turnsToGoldenAge = unique.params[0].toInt() val turnsToGoldenAge = unique.params[0].toInt()
actionList += UnitAction(UnitActionType.StartGoldenAge, actionList += UnitAction(UnitActionType.StartGoldenAge,
uncivSound = UncivSound.Chimes,
action = { action = {
unit.civInfo.goldenAges.enterGoldenAge(turnsToGoldenAge) unit.civInfo.goldenAges.enterGoldenAge(turnsToGoldenAge)
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
@ -394,7 +389,6 @@ object UnitActions {
else currentConstruction.isAnyWonder() else currentConstruction.isAnyWonder()
} }
actionList += UnitAction(UnitActionType.HurryWonder, actionList += UnitAction(UnitActionType.HurryWonder,
uncivSound = UncivSound.Chimes,
action = { action = {
tile.getCity()!!.cityConstructions.apply { tile.getCity()!!.cityConstructions.apply {
addProductionPoints(300 + 30 * tile.getCity()!!.population.population) //http://civilization.wikia.com/wiki/Great_engineer_(Civ5) addProductionPoints(300 + 30 * tile.getCity()!!.population.population) //http://civilization.wikia.com/wiki/Great_engineer_(Civ5)
@ -410,7 +404,6 @@ object UnitActions {
&& tile.owningCity?.civInfo?.isAtWarWith(unit.civInfo) == false && tile.owningCity?.civInfo?.isAtWarWith(unit.civInfo) == false
val influenceEarned = unique.params[0].toInt() val influenceEarned = unique.params[0].toInt()
actionList += UnitAction(UnitActionType.ConductTradeMission, actionList += UnitAction(UnitActionType.ConductTradeMission,
uncivSound = UncivSound.Chimes,
action = { action = {
// http://civilization.wikia.com/wiki/Great_Merchant_(Civ5) // http://civilization.wikia.com/wiki/Great_Merchant_(Civ5)
var goldEarned = ((350 + 50 * unit.civInfo.getEraNumber()) * unit.civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt() var goldEarned = ((350 + 50 * unit.civInfo.getEraNumber()) * unit.civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt()
@ -432,7 +425,6 @@ object UnitActions {
if (!unit.hasUnique("May found a religion")) return // should later also include enhance religion if (!unit.hasUnique("May found a religion")) return // should later also include enhance religion
if (!unit.civInfo.religionManager.mayUseGreatProphetAtAll(unit)) return if (!unit.civInfo.religionManager.mayUseGreatProphetAtAll(unit)) return
actionList += UnitAction(UnitActionType.FoundReligion, actionList += UnitAction(UnitActionType.FoundReligion,
uncivSound = UncivSound.Choir,
action = { action = {
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
unit.civInfo.religionManager.useGreatProphet(unit) unit.civInfo.religionManager.useGreatProphet(unit)
@ -450,7 +442,6 @@ object UnitActions {
val city = tile.getCity() val city = tile.getCity()
actionList += UnitAction(UnitActionType.SpreadReligion, actionList += UnitAction(UnitActionType.SpreadReligion,
title = "Spread [${unit.religion!!}]", title = "Spread [${unit.religion!!}]",
uncivSound = UncivSound.Choir,
action = { action = {
unit.abilityUsedCount["Religion Spread"] = unit.abilityUsedCount["Religion Spread"]!! + 1 unit.abilityUsedCount["Religion Spread"] = unit.abilityUsedCount["Religion Spread"]!! + 1
city!!.religion[unit.religion!!] = 100 city!!.religion[unit.religion!!] = 100
@ -471,10 +462,9 @@ object UnitActions {
for (unique in uniquesToCheck) { for (unique in uniquesToCheck) {
val improvementName = unique.params[0] val improvementName = unique.params[0]
val improvement = tile.ruleset.tileImprovements[improvementName] val improvement = tile.ruleset.tileImprovements[improvementName]
if (improvement == null) continue ?: continue
finalActions += UnitAction(UnitActionType.Create, finalActions += UnitAction(UnitActionType.Create,
title = "Create [$improvementName]", title = "Create [$improvementName]",
uncivSound = UncivSound.Chimes,
action = { action = {
val unitTile = unit.getTile() val unitTile = unit.getTile()
for (terrainFeature in tile.terrainFeatures.filter { unitTile.ruleset.tileImprovements.containsKey("Remove $it") }) for (terrainFeature in tile.terrainFeatures.filter { unitTile.ruleset.tileImprovements.containsKey("Remove $it") })
@ -575,14 +565,12 @@ object UnitActions {
if (isDamaged && !showingAdditionalActions) if (isDamaged && !showingAdditionalActions)
actionList += UnitAction(UnitActionType.FortifyUntilHealed, actionList += UnitAction(UnitActionType.FortifyUntilHealed,
title = UnitActionType.FortifyUntilHealed.value,
action = { action = {
unit.fortifyUntilHealed() unit.fortifyUntilHealed()
}.takeIf { !unit.isFortifyingUntilHealed() } }.takeIf { !unit.isFortifyingUntilHealed() }
) )
else if (isDamaged || !showingAdditionalActions) else if (isDamaged || !showingAdditionalActions)
actionList += UnitAction(UnitActionType.Fortify, actionList += UnitAction(UnitActionType.Fortify,
uncivSound = UncivSound.Fortify,
action = { action = {
unit.fortify() unit.fortify()
}.takeIf { !isFortified } }.takeIf { !isFortified }
@ -630,7 +618,7 @@ object UnitActions {
// We need to be in another civs territory. // We need to be in another civs territory.
if (recipient == null || recipient.isCurrentPlayer()) return null if (recipient == null || recipient.isCurrentPlayer()) return null
// City States only take miliary units (and GPs for certain civs) // City States only take military units (and GPs for certain civs)
if (recipient.isCityState()) { if (recipient.isCityState()) {
if (unit.isGreatPerson()) { if (unit.isGreatPerson()) {
// Do we have a unique ability to gift GPs? // Do we have a unique ability to gift GPs?
@ -643,7 +631,7 @@ object UnitActions {
else if (!tile.isFriendlyTerritory(unit.civInfo)) return null else if (!tile.isFriendlyTerritory(unit.civInfo)) return null
if (unit.currentMovement <= 0) if (unit.currentMovement <= 0)
return UnitAction(UnitActionType.GiftUnit, uncivSound = UncivSound.Silent, action = null) return UnitAction(UnitActionType.GiftUnit, action = null)
val giftAction = { val giftAction = {
if (recipient.isCityState()) { if (recipient.isCityState()) {
@ -667,12 +655,12 @@ object UnitActions {
UncivGame.Current.worldScreen.shouldUpdate = true UncivGame.Current.worldScreen.shouldUpdate = true
} }
return UnitAction(UnitActionType.GiftUnit, uncivSound = UncivSound.Silent, action = giftAction) return UnitAction(UnitActionType.GiftUnit, action = giftAction)
} }
private fun addToggleActionsAction(unit: MapUnit, actionList: ArrayList<UnitAction>, unitTable: UnitTable) { private fun addToggleActionsAction(unit: MapUnit, actionList: ArrayList<UnitAction>, unitTable: UnitTable) {
actionList += UnitAction(UnitActionType.ShowAdditionalActions, actionList += UnitAction(
title = if (unit.showAdditionalActions) "Back" else "Show more", type = if (unit.showAdditionalActions) UnitActionType.HideAdditionalActions else UnitActionType.ShowAdditionalActions,
action = { action = {
unit.showAdditionalActions = !unit.showAdditionalActions unit.showAdditionalActions = !unit.showAdditionalActions
unitTable.update() unitTable.update()

View File

@ -1,87 +1,19 @@
package com.unciv.ui.worldscreen.unit package com.unciv.ui.worldscreen.unit
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.ui.Button import com.badlogic.gdx.scenes.scene2d.ui.Button
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.map.MapUnit import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.RoadStatus
import com.unciv.models.UnitAction import com.unciv.models.UnitAction
import com.unciv.models.translations.equalsPlaceholderText
import com.unciv.models.translations.getPlaceholderParameters
import com.unciv.ui.utils.* import com.unciv.ui.utils.*
import com.unciv.ui.utils.KeyPressDispatcher.Companion.keyboardAvailable import com.unciv.ui.utils.KeyPressDispatcher.Companion.keyboardAvailable
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
import com.unciv.ui.worldscreen.WorldScreen import com.unciv.ui.worldscreen.WorldScreen
import kotlin.concurrent.thread import kotlin.concurrent.thread
private data class UnitIconAndKey(val icon: Actor, var key: KeyCharAndCode = KeyCharAndCode.UNKNOWN) {
constructor(icon: Actor, key: Char) : this(icon, KeyCharAndCode(key))
}
class UnitActionsTable(val worldScreen: WorldScreen) : Table() { class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
private fun getIconAndKeyForUnitAction(unitAction: String): UnitIconAndKey {
when {
unitAction.equalsPlaceholderText("Upgrade to [] ([] gold)") -> {
// Regexplaination: start with a [, take as many non-] chars as you can, until you reach a ].
// What you find between the first [ and the first ] that comes after it, will be group no. 0
val unitToUpgradeTo = unitAction.getPlaceholderParameters()[0]
return UnitIconAndKey(ImageGetter.getUnitIcon(unitToUpgradeTo), 'u')
}
unitAction.equalsPlaceholderText("Create []") -> {
// Regexplaination: start with a [, take as many non-] chars as you can, until you reach a ].
// What you find between the first [ and the first ] that comes after it, will be group no. 0
val improvementName = unitAction.getPlaceholderParameters()[0]
return UnitIconAndKey(ImageGetter.getImprovementIcon(improvementName), 'i')
}
unitAction.equalsPlaceholderText("Spread []") -> {
// This should later include icons for the different religions. For now, just use the great prophet icon
return UnitIconAndKey(ImageGetter.getUnitIcon("Great Prophet"), 'g')
}
else -> when (unitAction) {
"Sleep" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Sleep"), 'f')
"Sleep until healed" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Sleep"), 'h')
"Fortify" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Shield").apply { color = Color.BLACK }, 'f')
"Fortify until healed" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Shield").apply { color = Color.BLACK }, 'h')
// Move unit is not actually used anywhere
"Move unit" -> return UnitIconAndKey(ImageGetter.getStatIcon("Movement"))
"Stop movement" -> return UnitIconAndKey(ImageGetter.getStatIcon("Movement").apply { color = Color.RED }, KeyCharAndCode(Input.Keys.END))
"Swap units" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Swap"), 'y')
"Promote" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Star").apply { color = Color.GOLD }, 'o')
"Construct improvement" -> return UnitIconAndKey(ImageGetter.getUnitIcon(Constants.worker), 'i')
"Automate" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'm')
"Stop automation" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Stop"), KeyCharAndCode(Input.Keys.END))
"Found city" -> return UnitIconAndKey(ImageGetter.getUnitIcon(Constants.settler), 'c')
"Hurry Research" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Scientist"), 'g')
"Start Golden Age" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Artist"), 'g')
"Hurry Wonder" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'g')
"Conduct Trade Mission" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Merchant"), 'g')
// Deprecated since 3.15.4
"Construct road" -> return UnitIconAndKey(ImageGetter.getImprovementIcon(RoadStatus.Road.name), 'r')
//
"Paradrop" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Paratrooper"), 'p')
"Set up" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Catapult"), 't')
"Explore" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Scout"), 'x')
"Stop exploration" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Stop"), 'x')
"Pillage" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Pillage"), 'p')
"Disband unit" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/DisbandUnit"), KeyCharAndCode.DEL)
"Gift unit" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Present"))
"Show more" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/ArrowRight"), KeyCharAndCode(Input.Keys.PAGE_DOWN))
"Back" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/ArrowLeft"), KeyCharAndCode(Input.Keys.PAGE_UP))
else -> {
// If the unit has been fortifying for some turns
if (unitAction.startsWith("Fortification")) return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Shield"))
return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Star"))
}
}
}
}
fun update(unit: MapUnit?) { fun update(unit: MapUnit?) {
clear() clear()
if (unit == null) return if (unit == null) return
@ -93,16 +25,15 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
private fun getUnitActionButton(unitAction: UnitAction): Button { private fun getUnitActionButton(unitAction: UnitAction): Button {
val iconAndKey = getIconAndKeyForUnitAction(unitAction.title) val icon = unitAction.getIcon()
// If peripheral keyboard not detected, hotkeys will not be displayed // If peripheral keyboard not detected, hotkeys will not be displayed
if (!keyboardAvailable) { iconAndKey.key = KeyCharAndCode.UNKNOWN } val key = if (keyboardAvailable) unitAction.type.key else KeyCharAndCode.UNKNOWN
val actionButton = Button(CameraStageBaseScreen.skin) val actionButton = Button(CameraStageBaseScreen.skin)
actionButton.add(iconAndKey.icon).size(20f).pad(5f) actionButton.add(icon).size(20f).pad(5f)
val fontColor = if (unitAction.isCurrentAction) Color.YELLOW else Color.WHITE val fontColor = if (unitAction.isCurrentAction) Color.YELLOW else Color.WHITE
actionButton.add(unitAction.title.toLabel(fontColor)).pad(5f) actionButton.add(unitAction.title.toLabel(fontColor)).pad(5f)
actionButton.addTooltip(iconAndKey.key) actionButton.addTooltip(key)
actionButton.pack() actionButton.pack()
val action = { val action = {
unitAction.action?.invoke() unitAction.action?.invoke()
@ -111,8 +42,8 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
if (unitAction.action == null) actionButton.disable() if (unitAction.action == null) actionButton.disable()
else { else {
actionButton.onClick(unitAction.uncivSound, action) actionButton.onClick(unitAction.uncivSound, action)
if (iconAndKey.key != KeyCharAndCode.UNKNOWN) if (key != KeyCharAndCode.UNKNOWN)
worldScreen.keyPressDispatcher[iconAndKey.key] = { worldScreen.keyPressDispatcher[key] = {
thread(name = "Sound") { Sounds.play(unitAction.uncivSound) } thread(name = "Sound") { Sounds.play(unitAction.uncivSound) }
action() action()
} }