mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-10 07:16:54 +07:00
Dev Console: Lint, crash fix, add Stat (#10656)
* Console: linting * Console: Commands can control message Color * Console: `civ add gold -1` * Console: Typo
This commit is contained in:
parent
5061b29197
commit
96dcd584cf
@ -2,26 +2,29 @@ package com.unciv.ui.screens.devconsole
|
||||
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
import com.unciv.models.stats.Stat
|
||||
|
||||
fun String.toCliInput() = this.lowercase().replace(" ","-")
|
||||
internal fun String.toCliInput() = this.lowercase().replace(" ","-")
|
||||
|
||||
interface ConsoleCommand {
|
||||
fun handle(console: DevConsolePopup, params: List<String>): String?
|
||||
fun handle(console: DevConsolePopup, params: List<String>): DevConsoleResponse
|
||||
fun autocomplete(params: List<String>): String? = ""
|
||||
}
|
||||
|
||||
class ConsoleAction(val action: (console: DevConsolePopup, params: List<String>)->String?):ConsoleCommand{
|
||||
override fun handle(console: DevConsolePopup, params: List<String>): String? {
|
||||
class ConsoleAction(val action: (console: DevConsolePopup, params: List<String>) -> DevConsoleResponse) : ConsoleCommand {
|
||||
override fun handle(console: DevConsolePopup, params: List<String>): DevConsoleResponse {
|
||||
return action(console, params)
|
||||
}
|
||||
}
|
||||
|
||||
interface ConsoleCommandNode:ConsoleCommand{
|
||||
interface ConsoleCommandNode : ConsoleCommand {
|
||||
val subcommands: HashMap<String, ConsoleCommand>
|
||||
|
||||
override fun handle(console: DevConsolePopup, params: List<String>): String? {
|
||||
if (params.isEmpty()) return "Available commands: " + subcommands.keys.joinToString()
|
||||
val handler = subcommands[params[0]] ?: return "Invalid command.\nAvailable commands: " + subcommands.keys.joinToString("") { "\n- $it" }
|
||||
override fun handle(console: DevConsolePopup, params: List<String>): DevConsoleResponse {
|
||||
if (params.isEmpty())
|
||||
return DevConsoleResponse.hint("Available commands: " + subcommands.keys.joinToString())
|
||||
val handler = subcommands[params[0]]
|
||||
?: return DevConsoleResponse.error("Invalid command.\nAvailable commands:" + subcommands.keys.joinToString("") { "\n- $it" })
|
||||
return handler.handle(console, params.drop(1))
|
||||
}
|
||||
|
||||
@ -43,112 +46,126 @@ interface ConsoleCommandNode:ConsoleCommand{
|
||||
}
|
||||
}
|
||||
|
||||
class ConsoleCommandRoot:ConsoleCommandNode {
|
||||
class ConsoleCommandRoot : ConsoleCommandNode {
|
||||
override val subcommands = hashMapOf<String, ConsoleCommand>(
|
||||
"unit" to ConsoleUnitCommands(),
|
||||
"city" to ConsoleCityCommands(),
|
||||
"tile" to ConsoleTileCommands()
|
||||
"tile" to ConsoleTileCommands(),
|
||||
"civ" to ConsoleCivCommands()
|
||||
)
|
||||
}
|
||||
|
||||
class ConsoleUnitCommands:ConsoleCommandNode {
|
||||
class ConsoleUnitCommands : ConsoleCommandNode {
|
||||
override val subcommands = hashMapOf<String, ConsoleCommand>(
|
||||
|
||||
"add" to ConsoleAction { console, params ->
|
||||
if (params.size != 2)
|
||||
return@ConsoleAction "Format: unit add <civName> <unitName>"
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: unit add <civName> <unitName>")
|
||||
val selectedTile = console.screen.mapHolder.selectedTile
|
||||
?: return@ConsoleAction "No tile selected"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No tile selected")
|
||||
val civ = console.getCivByName(params[0])
|
||||
?: return@ConsoleAction "Unknown civ"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown civ")
|
||||
val baseUnit = console.gameInfo.ruleset.units.values.firstOrNull { it.name.toCliInput() == params[1] }
|
||||
?: return@ConsoleAction "Unknown unit"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown unit")
|
||||
civ.units.placeUnitNearTile(selectedTile.position, baseUnit)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"remove" to ConsoleAction { console, params ->
|
||||
if (params.isNotEmpty())
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: unit remove")
|
||||
val unit = console.getSelectedUnit()
|
||||
?: return@ConsoleAction "Select tile with unit"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Select tile with unit")
|
||||
unit.destroy()
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"addpromotion" to ConsoleAction { console, params ->
|
||||
if (params.size != 1)
|
||||
return@ConsoleAction "Format: unit addpromotion <promotionName>"
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: unit addpromotion <promotionName>")
|
||||
val unit = console.getSelectedUnit()
|
||||
?: return@ConsoleAction "Select tile with unit"
|
||||
val promotion = console.gameInfo.ruleset.unitPromotions.values.firstOrNull { it.name.toCliInput() == params[2] }
|
||||
?: return@ConsoleAction "Unknown promotion"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Select tile with unit")
|
||||
val promotion = console.gameInfo.ruleset.unitPromotions.values.firstOrNull { it.name.toCliInput() == params[0] }
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown promotion")
|
||||
unit.promotions.addPromotion(promotion.name, true)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"removepromotion" to ConsoleAction { console, params ->
|
||||
if (params.size != 1)
|
||||
return@ConsoleAction "Format: unit removepromotion <promotionName>"
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: unit removepromotion <promotionName>")
|
||||
val unit = console.getSelectedUnit()
|
||||
?: return@ConsoleAction "Select tile with unit"
|
||||
val promotion = unit.promotions.getPromotions().firstOrNull { it.name.toCliInput() == params[2] }
|
||||
?: return@ConsoleAction "Promotion not found on unit"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Select tile with unit")
|
||||
val promotion = unit.promotions.getPromotions().firstOrNull { it.name.toCliInput() == params[0] }
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Promotion not found on unit")
|
||||
// No such action in-game so we need to manually update
|
||||
unit.promotions.promotions.remove(promotion.name)
|
||||
unit.updateUniques()
|
||||
unit.updateVisibleTiles()
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
class ConsoleCityCommands:ConsoleCommandNode {
|
||||
class ConsoleCityCommands : ConsoleCommandNode {
|
||||
override val subcommands = hashMapOf<String, ConsoleCommand>(
|
||||
|
||||
"add" to ConsoleAction { console, params ->
|
||||
if (params.size != 1) return@ConsoleAction "Format: city add <civName>"
|
||||
val civ = console.getCivByName(params[0]) ?: return@ConsoleAction "Unknown civ"
|
||||
if (params.size != 1)
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: city add <civName>")
|
||||
val civ = console.getCivByName(params[0])
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown civ")
|
||||
val selectedTile = console.screen.mapHolder.selectedTile
|
||||
?: return@ConsoleAction "No tile selected"
|
||||
if (selectedTile.isCityCenter()) return@ConsoleAction "Tile already contains a city center"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No tile selected")
|
||||
if (selectedTile.isCityCenter())
|
||||
return@ConsoleAction DevConsoleResponse.error("Tile already contains a city center")
|
||||
civ.addCity(selectedTile.position)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"remove" to ConsoleAction { console, params ->
|
||||
if (params.isNotEmpty())
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: city remove")
|
||||
val selectedTile = console.screen.mapHolder.selectedTile
|
||||
?: return@ConsoleAction "No tile selected"
|
||||
val city = selectedTile.getCity() ?: return@ConsoleAction "No city in selected tile"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No tile selected")
|
||||
val city = selectedTile.getCity()
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No city in selected tile")
|
||||
city.destroyCity(overrideSafeties = true)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"setpop" to ConsoleAction { console, params ->
|
||||
if (params.size != 2) return@ConsoleAction "Format: city setpop <cityName> <amount>"
|
||||
val newPop = params[1].toIntOrNull() ?: return@ConsoleAction "Invalid amount " + params[1]
|
||||
if (newPop < 1) return@ConsoleAction "Invalid amount $newPop"
|
||||
if (params.size != 2)
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: city setpop <cityName> <amount>")
|
||||
val newPop = params[1].toIntOrNull() ?: return@ConsoleAction DevConsoleResponse.error("Invalid amount " + params[1])
|
||||
if (newPop < 1) return@ConsoleAction DevConsoleResponse.error("Invalid amount $newPop")
|
||||
val city = console.gameInfo.getCities().firstOrNull { it.name.toCliInput() == params[0] }
|
||||
?: return@ConsoleAction "Unknown city"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown city")
|
||||
city.population.setPopulation(newPop)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"addtile" to ConsoleAction { console, params ->
|
||||
if (params.size != 1)
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: city addtile <cityName>")
|
||||
val selectedTile = console.screen.mapHolder.selectedTile
|
||||
?: return@ConsoleAction "No tile selected"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No tile selected")
|
||||
val city = console.gameInfo.getCities().firstOrNull { it.name.toCliInput() == params[0] }
|
||||
?: return@ConsoleAction "Unknown city"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown city")
|
||||
if (selectedTile.neighbors.none { it.getCity() == city })
|
||||
return@ConsoleAction "Tile is not adjacent to city"
|
||||
return@ConsoleAction DevConsoleResponse.error("Tile is not adjacent to any tile already owned by the city")
|
||||
city.expansion.takeOwnership(selectedTile)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"removetile" to ConsoleAction { console, params ->
|
||||
if (params.isNotEmpty())
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: city removetile")
|
||||
val selectedTile = console.screen.mapHolder.selectedTile
|
||||
?: return@ConsoleAction "No tile selected"
|
||||
val city = selectedTile.getCity() ?: return@ConsoleAction "No city for selected tile"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No tile selected")
|
||||
val city = selectedTile.getCity() ?: return@ConsoleAction DevConsoleResponse.error("No city for selected tile")
|
||||
city.expansion.relinquishOwnership(selectedTile)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
})
|
||||
}
|
||||
|
||||
@ -156,47 +173,85 @@ class ConsoleTileCommands: ConsoleCommandNode {
|
||||
override val subcommands = hashMapOf<String, ConsoleCommand>(
|
||||
|
||||
"setimprovement" to ConsoleAction { console, params ->
|
||||
if (params.size != 1 && params.size != 2) return@ConsoleAction "Format: tile setimprovement <improvementName> [<civName>]"
|
||||
if (params.size != 1 && params.size != 2)
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: tile setimprovement <improvementName> [<civName>]")
|
||||
val selectedTile = console.screen.mapHolder.selectedTile
|
||||
?: return@ConsoleAction "No tile selected"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No tile selected")
|
||||
val improvement = console.gameInfo.ruleset.tileImprovements.values.firstOrNull {
|
||||
it.name.toCliInput() == params[0]
|
||||
} ?: return@ConsoleAction "Unknown improvement"
|
||||
} ?: return@ConsoleAction DevConsoleResponse.error("Unknown improvement")
|
||||
var civ:Civilization? = null
|
||||
if (params.size == 2){
|
||||
civ = console.getCivByName(params[1]) ?: return@ConsoleAction "Unknown civ"
|
||||
civ = console.getCivByName(params[1])
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown civ")
|
||||
}
|
||||
selectedTile.improvementFunctions.changeImprovement(improvement.name, civ)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"removeimprovement" to ConsoleAction { console, params ->
|
||||
if (params.isNotEmpty())
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: tile removeimprovement")
|
||||
val selectedTile = console.screen.mapHolder.selectedTile
|
||||
?: return@ConsoleAction "No tile selected"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No tile selected")
|
||||
selectedTile.improvementFunctions.changeImprovement(null)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"addfeature" to ConsoleAction { console, params ->
|
||||
if (params.size != 1)
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: tile addfeature <featureName>")
|
||||
val selectedTile = console.screen.mapHolder.selectedTile
|
||||
?: return@ConsoleAction "No tile selected"
|
||||
if (params.size != 1) return@ConsoleAction "Format: tile addfeature <featureName>"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No tile selected")
|
||||
val feature = console.gameInfo.ruleset.terrains.values
|
||||
.firstOrNull { it.type == TerrainType.TerrainFeature && it.name.toCliInput() == params[0] }
|
||||
?: return@ConsoleAction "Unknown feature"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown feature")
|
||||
selectedTile.addTerrainFeature(feature.name)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
},
|
||||
|
||||
"removefeature" to ConsoleAction { console, params ->
|
||||
if (params.size != 1)
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: tile addfeature <featureName>")
|
||||
val selectedTile = console.screen.mapHolder.selectedTile
|
||||
?: return@ConsoleAction "No tile selected"
|
||||
if (params.size != 1) return@ConsoleAction "Format: tile addfeature <featureName>"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("No tile selected")
|
||||
val feature = console.gameInfo.ruleset.terrains.values
|
||||
.firstOrNull { it.type == TerrainType.TerrainFeature && it.name.toCliInput() == params[0] }
|
||||
?: return@ConsoleAction "Unknown feature"
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown feature")
|
||||
selectedTile.removeTerrainFeature(feature.name)
|
||||
return@ConsoleAction null
|
||||
return@ConsoleAction DevConsoleResponse.OK
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
class ConsoleCivCommands : ConsoleCommandNode {
|
||||
override val subcommands = hashMapOf<String, ConsoleCommand>(
|
||||
"add" to ConsoleAction { console, params ->
|
||||
var statPos = 0
|
||||
if (params.size !in 2..3)
|
||||
return@ConsoleAction DevConsoleResponse.hint("Format: civ add [civ] <stat> <amount>")
|
||||
val civ = if (params.size == 2) console.screen.selectedCiv
|
||||
else {
|
||||
statPos++
|
||||
console.getCivByName(params[0])
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Unknown civ")
|
||||
}
|
||||
val amount = params[statPos+1].toIntOrNull()
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Whut? \"${params[statPos+1]}\" is not a number!")
|
||||
val stat = Stat.safeValueOf(params[statPos].replaceFirstChar(Char::titlecase))
|
||||
?: return@ConsoleAction DevConsoleResponse.error("Whut? \"${params[statPos]}\" is not a Stat!")
|
||||
if (stat !in Stat.statsWithCivWideField)
|
||||
return@ConsoleAction DevConsoleResponse.error("$stat is not civ-wide")
|
||||
civ.addStat(stat, amount)
|
||||
DevConsoleResponse.OK
|
||||
}
|
||||
)
|
||||
|
||||
override fun autocomplete(params: List<String>): String? {
|
||||
if (params.size == 2 && params[0] == "add")
|
||||
return Stat.names()
|
||||
.firstOrNull { it.lowercase().startsWith(params[1]) }
|
||||
?.drop(params[1].length)
|
||||
return super.autocomplete(params)
|
||||
}
|
||||
}
|
||||
|
@ -17,26 +17,25 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
companion object {
|
||||
val history = ArrayList<String>()
|
||||
}
|
||||
private var currentHistoryEntry = history.size
|
||||
|
||||
val textField = TextField("", BaseScreen.skin)
|
||||
private val textField = TextField("", BaseScreen.skin)
|
||||
private val responseLabel = "".toLabel(Color.RED)
|
||||
|
||||
private val commandRoot = ConsoleCommandRoot()
|
||||
internal val gameInfo = screen.gameInfo
|
||||
|
||||
init {
|
||||
add(textField).width(stageToShowOn.width / 2).row()
|
||||
val label = "".toLabel(Color.RED)
|
||||
add(label)
|
||||
textField.keyShortcuts.add(Input.Keys.ENTER) {
|
||||
val handleCommandResponse = handleCommand()
|
||||
if (handleCommandResponse == null) {
|
||||
screen.shouldUpdate = true
|
||||
history.add(textField.text)
|
||||
close()
|
||||
}
|
||||
else label.setText(handleCommandResponse)
|
||||
}
|
||||
textField.keyShortcuts.add(Input.Keys.ENTER, ::onEnter)
|
||||
|
||||
// Without this, console popup will always contain a `
|
||||
textField.addAction(Actions.delay(0.05f, Actions.run { textField.text = "" }))
|
||||
|
||||
add(responseLabel)
|
||||
|
||||
open(true)
|
||||
|
||||
keyShortcuts.add(KeyCharAndCode.ESC) { close() }
|
||||
|
||||
keyShortcuts.add(KeyCharAndCode.TAB) {
|
||||
@ -45,7 +44,6 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
}
|
||||
|
||||
if (history.isNotEmpty()) {
|
||||
var currentHistoryEntry = history.size
|
||||
keyShortcuts.add(Input.Keys.UP) {
|
||||
if (currentHistoryEntry > 0) currentHistoryEntry--
|
||||
textField.text = history[currentHistoryEntry]
|
||||
@ -58,21 +56,35 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
textField.cursorPosition = textField.text.length
|
||||
}
|
||||
}
|
||||
|
||||
screen.stage.keyboardFocus = textField
|
||||
}
|
||||
|
||||
private fun getParams(text:String) = text.split(" ").filter { it.isNotEmpty() }.map { it.lowercase() }
|
||||
private fun onEnter() {
|
||||
val handleCommandResponse = handleCommand()
|
||||
if (handleCommandResponse.isOK) {
|
||||
screen.shouldUpdate = true
|
||||
history.add(textField.text)
|
||||
close()
|
||||
return
|
||||
}
|
||||
responseLabel.setText(handleCommandResponse.message)
|
||||
responseLabel.style.fontColor = handleCommandResponse.color
|
||||
}
|
||||
|
||||
private fun handleCommand(): String? {
|
||||
private fun getParams(text: String) = text.split(" ").filter { it.isNotEmpty() }.map { it.lowercase() }
|
||||
|
||||
private fun handleCommand(): DevConsoleResponse {
|
||||
val params = getParams(textField.text)
|
||||
return ConsoleCommandRoot().handle(this, params)
|
||||
return commandRoot.handle(this, params)
|
||||
}
|
||||
|
||||
private fun getAutocomplete():String {
|
||||
private fun getAutocomplete(): String {
|
||||
val params = getParams(textField.text)
|
||||
return ConsoleCommandRoot().autocomplete(params) ?: ""
|
||||
return commandRoot.autocomplete(params) ?: ""
|
||||
}
|
||||
|
||||
internal fun getCivByName(name:String) = gameInfo.civilizations.firstOrNull { it.civName.toCliInput() == name }
|
||||
internal fun getCivByName(name: String) = gameInfo.civilizations.firstOrNull { it.civName.toCliInput() == name }
|
||||
|
||||
internal fun getSelectedUnit(): MapUnit? {
|
||||
val selectedTile = screen.mapHolder.selectedTile ?: return null
|
||||
|
@ -0,0 +1,18 @@
|
||||
|
||||
package com.unciv.ui.screens.devconsole
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
|
||||
@Suppress("DataClassPrivateConstructor") // abuser need to find copy() first
|
||||
data class DevConsoleResponse private constructor (
|
||||
val color: Color,
|
||||
val message: String? = null,
|
||||
val isOK: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
val OK = DevConsoleResponse(Color.GREEN, isOK = true)
|
||||
fun ok(message: String) = DevConsoleResponse(Color.GREEN, message, true)
|
||||
fun error(message: String) = DevConsoleResponse(Color.RED, message)
|
||||
fun hint(message: String) = DevConsoleResponse(Color.GOLD, message)
|
||||
}
|
||||
}
|
@ -196,7 +196,6 @@ class WorldScreen(
|
||||
// No cheating unless you're by yourself
|
||||
if (gameInfo.civilizations.count { it.isHuman() } > 1) return@add
|
||||
val consolePopup = DevConsolePopup(this)
|
||||
stage.keyboardFocus = consolePopup.textField
|
||||
}
|
||||
|
||||
addKeyboardListener() // for map panning by W,S,A,D
|
||||
|
Loading…
Reference in New Issue
Block a user