diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index f91f47b46a..9a3a708644 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -902,6 +902,7 @@ Bonus when performing air sweep [bonusAmount]% = Dogfighting I = Dogfighting II = Dogfighting III = +Choose name for [unitName] = # Multiplayer Turn Checker Service diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 8f97ea4c90..e4a553a1b5 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -474,7 +474,7 @@ object UnitAutomation { if (tryGoToRuinAndEncampment(unit) && unit.currentMovement == 0f) return if (unit.health < 80 && tryHealUnit(unit)) return if (tryExplore(unit)) return - unit.civInfo.addNotification("[${unit.name}] finished exploring.", unit.currentTile.position, Color.GRAY) + unit.civInfo.addNotification("[${unit.displayName()}] finished exploring.", unit.currentTile.position, Color.GRAY) unit.action = null } diff --git a/core/src/com/unciv/logic/automation/WorkerAutomation.kt b/core/src/com/unciv/logic/automation/WorkerAutomation.kt index 574de23b09..e7d69c06bc 100644 --- a/core/src/com/unciv/logic/automation/WorkerAutomation.kt +++ b/core/src/com/unciv/logic/automation/WorkerAutomation.kt @@ -56,7 +56,7 @@ class WorkerAutomation(val unit: MapUnit) { return } - unit.civInfo.addNotification("[${unit.name}] has no work to do.", unit.currentTile.position, Color.GRAY) + unit.civInfo.addNotification("[${unit.displayName()}] has no work to do.", unit.currentTile.position, Color.GRAY) } diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 2df240a944..42be1d6e6e 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -508,7 +508,7 @@ class CivilizationInfo { val unitToDisband = civMilitaryUnits.first() unitToDisband.disband() civMilitaryUnits -= unitToDisband - val unitName = unitToDisband.name + val unitName = unitToDisband.displayName() addNotification("Cannot provide unit upkeep for [$unitName] - unit has been disbanded!", null, Color.RED) } } diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 782118a10d..261ad72253 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -14,6 +14,9 @@ import com.unciv.models.ruleset.unit.UnitType import java.text.DecimalFormat import kotlin.random.Random +/** + * The immutable properties and mutable game state of an individual unit present on the map + */ class MapUnit { @Transient @@ -57,7 +60,29 @@ class MapUnit { var cannotEnterOceanTilesUntilAstronomy = false lateinit var owner: String + + /** + * Name key of the unit, used for serialization + */ lateinit var name: String + + /** + * Name of this individual unit, usually resulting from promotion + */ + var instanceName: String? = null + + /** + * Name which should be displayed in UI + */ + fun displayName(): String { + return if(instanceName == null) { + name + } + else { + "$instanceName ($name)" + } + } + var currentMovement: Float = 0f var health: Int = 100 @@ -77,8 +102,11 @@ class MapUnit { //region pure functions fun clone(): MapUnit { val toReturn = MapUnit() - toReturn.owner = owner + toReturn.baseUnit = baseUnit toReturn.name = name + toReturn.civInfo = civInfo + toReturn.owner = owner + toReturn.instanceName = instanceName toReturn.currentMovement = currentMovement toReturn.health = health toReturn.action = action diff --git a/core/src/com/unciv/models/ruleset/Nation.kt b/core/src/com/unciv/models/ruleset/Nation.kt index f827809b8d..8999efe761 100644 --- a/core/src/com/unciv/models/ruleset/Nation.kt +++ b/core/src/com/unciv/models/ruleset/Nation.kt @@ -1,4 +1,4 @@ -package com.unciv.models.ruleset + package com.unciv.models.ruleset import com.badlogic.gdx.graphics.Color import com.unciv.Constants diff --git a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt index 14a2c4516d..0efbb8ae54 100644 --- a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt +++ b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt @@ -350,10 +350,10 @@ class EmpireOverviewScreen(private var viewingPlayer:CivilizationInfo, defaultPa table.row() table.addSeparator() - for (unit in viewingPlayer.getCivUnits().sortedWith(compareBy({ it.name }, { !it.due }, + for (unit in viewingPlayer.getCivUnits().sortedWith(compareBy({ it.displayName() }, { !it.due }, { it.currentMovement < 0.1f }, { abs(it.currentTile.position.x) + abs(it.currentTile.position.y) }))) { val baseUnit = unit.baseUnit() - val button = unit.name.toTextButton() + val button = unit.displayName().toTextButton() button.onClick { game.setWorldScreen() game.worldScreen.mapHolder.setCenterPosition(unit.currentTile.position) diff --git a/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt index 3f4a943f62..aff9468c76 100644 --- a/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt @@ -14,6 +14,11 @@ open class PickerScreen : CameraStageBaseScreen() { protected var rightSideGroup = VerticalGroup() protected var rightSideButton: TextButton private var screenSplit = 0.85f + + /** + * The table displaying the choices from which to pick (usually). + * Also the element which most of the screen realestate is devoted to displaying. + */ protected var topTable: Table var bottomTable:Table = Table() internal var splitPane: SplitPane diff --git a/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt index 955884c0f2..4c52d96d6f 100644 --- a/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt @@ -40,7 +40,8 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen() { } val canBePromoted = unit.promotions.canBePromoted() val canChangeState = game.worldScreen.canChangeState - if (!canBePromoted || !canChangeState) + val canPromoteNow = canBePromoted && canChangeState + if (!canPromoteNow) rightSideButton.disable() val availablePromotionsGroup = Table() @@ -52,6 +53,15 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen() { || unit.promotions.promotions.contains(it.name) } val unitAvailablePromotions = unit.promotions.getAvailablePromotions() + if(canPromoteNow && unit.instanceName == null) { + val renameButton = "Choose name for [${unit.name}]".toTextButton() + renameButton.touchable = Touchable.enabled + renameButton.onClick { + RenameUnitPopup(unit, this).open() + } + availablePromotionsGroup.add(renameButton) + availablePromotionsGroup.row() + } for (promotion in promotionsForUnitType) { if(promotion.name=="Heal Instantly" && unit.health==100) continue val isPromotionAvailable = promotion in unitAvailablePromotions diff --git a/core/src/com/unciv/ui/pickerscreens/RenameUnitPopup.kt b/core/src/com/unciv/ui/pickerscreens/RenameUnitPopup.kt new file mode 100644 index 0000000000..57d394509d --- /dev/null +++ b/core/src/com/unciv/ui/pickerscreens/RenameUnitPopup.kt @@ -0,0 +1,31 @@ +package com.unciv.ui.pickerscreens + +import com.badlogic.gdx.scenes.scene2d.ui.TextField +import com.unciv.logic.map.MapUnit +import com.unciv.models.translations.tr +import com.unciv.ui.utils.* + +class RenameUnitPopup(unit: MapUnit, screen: CameraStageBaseScreen): Popup(screen) { + + init { + addGoodSizedLabel("Choose name for [${unit.baseUnit.name}]").row() + + val nameField = TextField("", skin) + // Disallowed special characters determined with US keyboard and a certain binary + // copyrighted by Firaxis Games + val illegal_chars = "%*[]\"\\|<>/?" + nameField.textFieldFilter = TextField.TextFieldFilter { _, char -> !(char in illegal_chars)} + // Max name length decided arbitrarily + nameField.maxLength = 10 + add(nameField) + row() + add("OK".toTextButton().onClick { + if (nameField.text != "") { + unit.instanceName = nameField.text + } + screen.game.setScreen(PromotionPickerScreen(unit)) + }) + add("Close".toTextButton().onClick { close() }) + + } +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt index 3c437bbbc6..278bc4f264 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt @@ -110,7 +110,7 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ if(selectedUnits.size==1) { //single selected unit separator.isVisible = true val unit = selectedUnit!! - var nameLabelText = unit.name.tr() + var nameLabelText = unit.displayName().tr() if (unit.health < 100) nameLabelText += " (" + unit.health + ")" if (nameLabelText != unitNameLabel.text.toString()) { unitNameLabel.setText(nameLabelText)