mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-24 10:01:39 +07:00
Console autocompletion can display *all* possibilities (#11461)
* RiverDirections is now namespaced to RiverGenerator with some pertinent helpers * Autocomplete of RiverDirections is now handled in the default autocomplete * Allow display of possible autocompletions for empty "start" * Brighten that autocomplete possibilities message * Fix a missed spot * Improve removepromotion autocomplete
This commit is contained in:
parent
7ce2913293
commit
12adc0d65a
@ -9,6 +9,12 @@ import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.utils.debug
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Handles River generation for [MapGenerator], [UniqueType.OneTimeChangeTerrain] and console.
|
||||
*
|
||||
* Map generation follows the [vertices of map hexes][RiverCoordinate]: [spawnRivers].
|
||||
* In-game new rivers work on edges: [continueRiverOn]
|
||||
*/
|
||||
class RiverGenerator(
|
||||
private val tileMap: TileMap,
|
||||
private val randomness: MapGenerationRandomness,
|
||||
@ -191,6 +197,20 @@ class RiverGenerator(
|
||||
}.count { it }
|
||||
}
|
||||
|
||||
enum class RiverDirections(private val clockPosition: Int) {
|
||||
North(12),
|
||||
NorthEast(2),
|
||||
SouthEast(4),
|
||||
South(6),
|
||||
SouthWest(8),
|
||||
NorthWest(10);
|
||||
fun getNeighborTile(selectedTile: Tile): Tile? =
|
||||
selectedTile.tileMap.getClockPositionNeighborTile(selectedTile, clockPosition)
|
||||
companion object {
|
||||
val names get() = values().map { it.name }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** [UniqueType.OneTimeChangeTerrain] tries to place a "River" feature.
|
||||
*
|
||||
|
@ -3,6 +3,7 @@ package com.unciv.ui.screens.devconsole
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.map.mapgenerator.RiverGenerator
|
||||
import com.unciv.logic.map.mapgenerator.RiverGenerator.RiverDirections
|
||||
import com.unciv.logic.map.tile.RoadStatus
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
|
||||
@ -102,32 +103,16 @@ class ConsoleTileCommands: ConsoleCommandNode {
|
||||
.filter { it.type == TerrainType.TerrainFeature }.findCliInput(param)
|
||||
?: throw ConsoleErrorException("Unknown feature")
|
||||
|
||||
private enum class RiverDirections(val clockPosition: Int) {
|
||||
North(12),
|
||||
NorthEast(2),
|
||||
SouthEast(4),
|
||||
South(6),
|
||||
SouthWest(8),
|
||||
NorthWest(10)
|
||||
}
|
||||
|
||||
private class ConsoleRiverAction(format: String, newValue: Boolean) : ConsoleAction(
|
||||
format,
|
||||
action = { console, params -> action(console, params, newValue) }
|
||||
) {
|
||||
override fun autocomplete(console: DevConsolePopup, params: List<String>): String? {
|
||||
if (params.isEmpty()) return null
|
||||
// Note this could filter which directions are allowed on the selected tile... too lazy
|
||||
val options = RiverDirections.values().map { it.name }
|
||||
return getAutocompleteString(params.last(), options, console)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun action(console: DevConsolePopup, params: List<String>, newValue: Boolean): DevConsoleResponse {
|
||||
val selectedTile = console.getSelectedTile()
|
||||
val direction = findCliInput<RiverDirections>(params[0])
|
||||
?: throw ConsoleErrorException("Unknown direction - use " + RiverDirections.values().joinToString { it.name })
|
||||
val otherTile = selectedTile.tileMap.getClockPositionNeighborTile(selectedTile, direction.clockPosition)
|
||||
?: throw ConsoleErrorException("Unknown direction - use " + RiverDirections.names.joinToString())
|
||||
val otherTile = direction.getNeighborTile(selectedTile)
|
||||
?: throw ConsoleErrorException("tile has no neighbor to the " + direction.name)
|
||||
if (!otherTile.isLand)
|
||||
throw ConsoleErrorException("there's no land to the " + direction.name)
|
||||
|
@ -25,17 +25,18 @@ class ConsoleUnitCommands : ConsoleCommandNode {
|
||||
unit.promotions.addPromotion(promotion.name, true)
|
||||
DevConsoleResponse.OK
|
||||
}) {
|
||||
override fun autocomplete(console: DevConsolePopup, params: List<String>): String? {
|
||||
if (params.isEmpty()) return null
|
||||
override fun autocomplete(console: DevConsolePopup, params: List<String>): String {
|
||||
// Note: filtering by unit.type.name in promotion.unitTypes sounds good (No [Zero]-Ability on an Archer),
|
||||
// but would also prevent promotions that can be legally obtained like Morale and Rejuvenation
|
||||
val promotions = console.getSelectedUnit().promotions.promotions
|
||||
val options = console.gameInfo.ruleset.unitPromotions.keys.asSequence()
|
||||
.filter { it !in promotions }
|
||||
.asIterable()
|
||||
return getAutocompleteString(params.last(), options, console)
|
||||
return getAutocompleteString(params.lastOrNull().orEmpty(), options, console)
|
||||
}
|
||||
},
|
||||
|
||||
"removepromotion" to ConsoleAction("unit removepromotion <promotionName>") { console, params ->
|
||||
"removepromotion" to object : ConsoleAction("unit removepromotion <promotionName>", { console, params ->
|
||||
val unit = console.getSelectedUnit()
|
||||
val promotion = unit.promotions.getPromotions().findCliInput(params[0])
|
||||
?: throw ConsoleErrorException("Promotion not found on unit")
|
||||
@ -44,6 +45,9 @@ class ConsoleUnitCommands : ConsoleCommandNode {
|
||||
unit.updateUniques()
|
||||
unit.updateVisibleTiles()
|
||||
DevConsoleResponse.OK
|
||||
}) {
|
||||
override fun autocomplete(console: DevConsolePopup, params: List<String>) =
|
||||
getAutocompleteString(params.lastOrNull().orEmpty(), console.getSelectedUnit().promotions.promotions, console)
|
||||
},
|
||||
|
||||
"setmovement" to ConsoleAction("unit setmovement [amount]") { console, params ->
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.unciv.ui.screens.devconsole
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.unciv.logic.map.mapgenerator.RiverGenerator
|
||||
import com.unciv.models.ruleset.IRulesetObject
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
import com.unciv.models.stats.Stat
|
||||
@ -25,14 +26,14 @@ internal inline fun <reified T: Enum<T>> findCliInput(param: String): T? {
|
||||
}
|
||||
|
||||
/** Returns the string to *add* to the existing command */
|
||||
internal fun getAutocompleteString(lastWord: String, allOptions: Iterable<String>, console: DevConsolePopup): String? {
|
||||
internal fun getAutocompleteString(lastWord: String, allOptions: Iterable<String>, console: DevConsolePopup): String {
|
||||
console.showResponse(null, Color.WHITE)
|
||||
|
||||
val matchingOptions = allOptions.map { it.toCliInput() }.filter { it.startsWith(lastWord.toCliInput()) }
|
||||
if (matchingOptions.isEmpty()) return null
|
||||
if (matchingOptions.isEmpty()) return ""
|
||||
if (matchingOptions.size == 1) return matchingOptions.first().drop(lastWord.length) + " "
|
||||
|
||||
console.showResponse("Matching completions: " + matchingOptions.joinToString(), Color.FOREST)
|
||||
console.showResponse("Matching completions: " + matchingOptions.joinToString(), Color.LIME.lerp(Color.OLIVE.cpy(), 0.5f))
|
||||
|
||||
val firstOption = matchingOptions.first()
|
||||
for ((index, char) in firstOption.withIndex()) {
|
||||
@ -45,11 +46,11 @@ internal fun getAutocompleteString(lastWord: String, allOptions: Iterable<String
|
||||
|
||||
interface ConsoleCommand {
|
||||
fun handle(console: DevConsolePopup, params: List<String>): DevConsoleResponse
|
||||
|
||||
/** Returns the string to *add* to the existing command.
|
||||
* The function should add a space at the end if and only if the "match" is an unambiguous choice!
|
||||
* Returning `null` means there was no match at all, while returning an empty string means the last word is a match as-is.
|
||||
*/
|
||||
fun autocomplete(console: DevConsolePopup, params: List<String>): String? = ""
|
||||
fun autocomplete(console: DevConsolePopup, params: List<String>): String = ""
|
||||
}
|
||||
|
||||
class ConsoleHintException(val hint: String) : Exception()
|
||||
@ -67,13 +68,11 @@ open class ConsoleAction(val format: String, val action: (console: DevConsolePop
|
||||
}
|
||||
}
|
||||
|
||||
override fun autocomplete(console: DevConsolePopup, params: List<String>): String? {
|
||||
if (params.isEmpty()) return null
|
||||
|
||||
override fun autocomplete(console: DevConsolePopup, params: List<String>): String {
|
||||
val formatParams = format.split(" ").drop(2).map {
|
||||
it.removeSurrounding("<",">").removeSurrounding("[","]").removeSurrounding("\"")
|
||||
}
|
||||
if (formatParams.size < params.size) return null
|
||||
if (formatParams.size < params.size) return ""
|
||||
val formatParam = formatParams[params.lastIndex]
|
||||
|
||||
val lastParam = params.last()
|
||||
@ -88,6 +87,7 @@ open class ConsoleAction(val format: String, val action: (console: DevConsolePop
|
||||
"stat" -> Stat.names()
|
||||
"religionName" -> console.gameInfo.religions.keys
|
||||
"buildingName" -> console.gameInfo.ruleset.buildings.keys
|
||||
"direction" -> RiverGenerator.RiverDirections.names
|
||||
else -> listOf()
|
||||
}
|
||||
return getAutocompleteString(lastParam, options, console)
|
||||
@ -113,9 +113,8 @@ interface ConsoleCommandNode : ConsoleCommand {
|
||||
return handler.handle(console, params.drop(1))
|
||||
}
|
||||
|
||||
override fun autocomplete(console: DevConsolePopup, params: List<String>): String? {
|
||||
if (params.isEmpty()) return null
|
||||
val firstParam = params[0]
|
||||
override fun autocomplete(console: DevConsolePopup, params: List<String>): String {
|
||||
val firstParam = params.firstOrNull().orEmpty()
|
||||
if (firstParam in subcommands) return subcommands[firstParam]!!.autocomplete(console, params.drop(1))
|
||||
return getAutocompleteString(firstParam, subcommands.keys, console)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
private var currentHistoryEntry = history.size
|
||||
|
||||
private val textField = TextField("", BaseScreen.skin)
|
||||
private val responseLabel = "".toLabel(Color.RED)
|
||||
private val responseLabel = "".toLabel(Color.RED).apply { wrap = true }
|
||||
|
||||
private val commandRoot = ConsoleCommandRoot()
|
||||
internal val gameInfo = screen.gameInfo
|
||||
@ -32,7 +32,7 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
// Without this, console popup will always contain a `
|
||||
textField.addAction(Actions.delay(0.05f, Actions.run { textField.text = "" }))
|
||||
|
||||
add(responseLabel)
|
||||
add(responseLabel).maxWidth(screen.stage.width * 0.8f)
|
||||
|
||||
open(true)
|
||||
|
||||
@ -89,7 +89,7 @@ class DevConsolePopup(val screen: WorldScreen) : Popup(screen) {
|
||||
|
||||
private fun getAutocomplete(): String {
|
||||
val params = getParams(textField.text)
|
||||
return commandRoot.autocomplete(this, params).orEmpty()
|
||||
return commandRoot.autocomplete(this, params)
|
||||
}
|
||||
|
||||
internal fun getCivByName(name: String) = gameInfo.civilizations.firstOrNull { it.civName.toCliInput() == name.toCliInput() }
|
||||
|
Loading…
Reference in New Issue
Block a user