diff --git a/android/assets/jsons/translationsByLanguage/English.properties b/android/assets/jsons/translationsByLanguage/English.properties index 5291b9464d..19690657ea 100644 --- a/android/assets/jsons/translationsByLanguage/English.properties +++ b/android/assets/jsons/translationsByLanguage/English.properties @@ -1380,10 +1380,12 @@ Stop automation = Construct road = # Requires translation! Fortify = +Fortify until healed = Fortify until healed # Requires translation! Fortification = # Requires translation! Sleep = +Sleep until healed = Sleep until healed # Requires translation! Moving = # Requires translation! diff --git a/android/assets/jsons/translationsByLanguage/Russian.properties b/android/assets/jsons/translationsByLanguage/Russian.properties index f82439adcb..ad3f31caf4 100644 --- a/android/assets/jsons/translationsByLanguage/Russian.properties +++ b/android/assets/jsons/translationsByLanguage/Russian.properties @@ -760,8 +760,10 @@ Automate = Автоматизировать Stop automation = Остановить автоматизацию Construct road = Построить дорогу Fortify = Укрепить +Fortify until healed = Укрепить до выздоровления Fortification = к защите Sleep = Спать +Sleep until healed = Спать до выздоровления Moving = В движении Set up = Подготовиться Upgrade to [unitType] ([goldCost] gold) = Улучшить до [unitType] ([goldCost] золота) diff --git a/android/assets/jsons/translationsByLanguage/Ukrainian.properties b/android/assets/jsons/translationsByLanguage/Ukrainian.properties index 11e1207ac5..e5ac5be28a 100644 --- a/android/assets/jsons/translationsByLanguage/Ukrainian.properties +++ b/android/assets/jsons/translationsByLanguage/Ukrainian.properties @@ -762,8 +762,10 @@ Automate = Автоматизувати Stop automation = Зупинити автоматизацію Construct road = Будувати дорогу Fortify = Укріпити +Fortify until healed = Укріпити до одужання Fortification = Фортифікація Sleep = Спати +Sleep until healed = Спати до одужання Moving = У русі Set up = Підготуватися Upgrade to [unitType] ([goldCost] gold) = Удосконалити до [unitType] ([goldCost] золота) diff --git a/android/assets/jsons/translationsByLanguage/template.properties b/android/assets/jsons/translationsByLanguage/template.properties index 259bcbf24b..2184d2f6c9 100644 --- a/android/assets/jsons/translationsByLanguage/template.properties +++ b/android/assets/jsons/translationsByLanguage/template.properties @@ -760,8 +760,10 @@ Automate = Stop automation = Construct road = Fortify = +Fortify until healed = Fortification = Sleep = +Sleep until healed = Moving = Set up = Upgrade to [unitType] ([goldCost] gold) = diff --git a/core/src/com/unciv/Constants.kt b/core/src/com/unciv/Constants.kt index d042d4f3cd..5081ec87c0 100644 --- a/core/src/com/unciv/Constants.kt +++ b/core/src/com/unciv/Constants.kt @@ -41,6 +41,7 @@ class Constants{ val unitActionSetUp = "Set Up" val unitActionSleep = "Sleep" + val unitActionSleepUntilHealed = "Sleep until healed" val unitActionAutomation = "Automate" val unitActionExplore = "Explore" val futureTech = "Future Tech" diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index da834c3f10..8d73bb7c34 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -201,7 +201,7 @@ object Battle { unit.useMovementPoints(1f) } else unit.currentMovement = 0f unit.attacksThisTurn += 1 - if (unit.isFortified() || unit.action == Constants.unitActionSleep) + if (unit.isFortified() || unit.isSleeping()) attacker.unit.action = null // but not, for instance, if it's Set Up - then it should definitely keep the action! } else if (attacker is CityCombatant) { attacker.city.attackedThisTurn = true diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index d311175263..c3885e308a 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -166,6 +166,10 @@ class MapUnit { return action?.startsWith("Fortify") == true } + fun isSleeping(): Boolean { + return action?.startsWith("Sleep") == true + } + fun getFortificationTurns(): Int { if(!isFortified()) return 0 return action!!.split(" ")[1].toInt() @@ -181,7 +185,7 @@ class MapUnit { if (name == Constants.worker && getTile().improvementInProgress != null) return false if (hasUnique("Can construct roads") && currentTile.improvementInProgress=="Road") return false if (isFortified()) return false - if (action==Constants.unitActionExplore || action==Constants.unitActionSleep + if (action==Constants.unitActionExplore || isSleeping() || action == Constants.unitActionAutomation) return false return true } @@ -268,13 +272,17 @@ class MapUnit { action = "Fortify 0" } + fun fortifyUntilHealed() { + action = "Fortify 0 until healed" + } + fun fortifyIfCan() { if (canFortify()) { fortify() } } - fun adjacentHealingBonus():Int{ + private fun adjacentHealingBonus():Int{ var healingBonus = 0 if(hasUnique("This unit and all others in adjacent tiles heal 5 additional HP per turn")) healingBonus +=5 if(hasUnique("This unit and all others in adjacent tiles heal 5 additional HP. This unit heals 5 additional HP outside of friendly territory.")) healingBonus +=5 @@ -334,10 +342,11 @@ class MapUnit { private fun doPostTurnAction() { if (name == Constants.worker && getTile().improvementInProgress != null) workOnImprovement() if(hasUnique("Can construct roads") && currentTile.improvementInProgress=="Road") workOnImprovement() - if(currentMovement== getMaxMovement().toFloat() + if(currentMovement == getMaxMovement().toFloat() && isFortified()){ val currentTurnsFortified = getFortificationTurns() - if(currentTurnsFortified<2) action = "Fortify ${currentTurnsFortified+1}" + if(currentTurnsFortified<2) + action = action!!.replace(currentTurnsFortified.toString(),(currentTurnsFortified+1).toString(), true) } } @@ -418,6 +427,10 @@ class MapUnit { || getUniques().contains("Unit will heal every turn, even if it performs an action")){ heal() } + if(action != null && health > 99) + if (action!!.endsWith(" until healed")) { + action = null // wake up when healed + } } fun startTurn(){ @@ -426,7 +439,7 @@ class MapUnit { due = true // Wake sleeping units if there's an enemy nearby - if(action==Constants.unitActionSleep && currentTile.getTilesInDistance(2).any { + if(isSleeping() && currentTile.getTilesInDistance(2).any { it.militaryUnit!=null && it.militaryUnit!!.civInfo.isAtWarWith(civInfo) }) action=null diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 931eef69bd..4019aa875a 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -235,7 +235,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) { unit.currentMovement -= distanceToTiles[destination]!!.totalDistance if (unit.currentMovement < 0.1) unit.currentMovement = 0f // silly floats which are "almost zero" - if(unit.isFortified() || unit.action==Constants.unitActionSetUp || unit.action==Constants.unitActionSleep) + if(unit.isFortified() || unit.action==Constants.unitActionSetUp || unit.isSleeping()) unit.action=null // unfortify/setup after moving // If this unit is a carrier, keep record of its air payload whereabouts. diff --git a/core/src/com/unciv/models/UnitAction.kt b/core/src/com/unciv/models/UnitAction.kt index 755b921709..d55e05b8a3 100644 --- a/core/src/com/unciv/models/UnitAction.kt +++ b/core/src/com/unciv/models/UnitAction.kt @@ -15,7 +15,9 @@ enum class UnitActionType(val value: String) { StopAutomation("Stop automation"), StopExploration("Stop exploration"), Sleep("Sleep"), + SleepUntilHealed("Sleep until healed"), Fortify("Fortify"), + FortifyUntilHealed("Fortify until healed"), Explore("Explore"), Promote("Promote"), Upgrade("Upgrade"), diff --git a/core/src/com/unciv/models/translations/Translations.kt b/core/src/com/unciv/models/translations/Translations.kt index 52a2d4b769..439097e1ed 100644 --- a/core/src/com/unciv/models/translations/Translations.kt +++ b/core/src/com/unciv/models/translations/Translations.kt @@ -16,7 +16,7 @@ class Translations : LinkedHashMap(){ return get(text)!![language]!! } - fun hasTranslation(text:String,language:String): Boolean { + private fun hasTranslation(text:String,language:String): Boolean { return containsKey(text) && get(text)!!.containsKey(language) } diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 80ecabde77..4674603bbd 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -35,31 +35,18 @@ class UnitActions { val workingOnImprovement = unit.hasUnique("Can build improvements on tiles") && unit.currentTile.hasImprovementInProgress() - if (!unit.isFortified() && (!unit.canFortify() || unit.health < 100) && unit.currentMovement > 0 - && !workingOnImprovement) { - val isSleeping = unit.action == Constants.unitActionSleep - actionList += UnitAction( - type = UnitActionType.Sleep, - canAct = !isSleeping, - isCurrentAction = isSleeping, - action = { - unit.action = Constants.unitActionSleep - unitTable.selectedUnit = null - }) + if (!unit.isFortified() && !unit.canFortify() + && unit.currentMovement > 0 && !workingOnImprovement) { + addSleepActions(actionList, unit, unitTable) } if (unit.canFortify()) { - actionList += UnitAction( - type = UnitActionType.Fortify, - canAct = unit.currentMovement > 0, - uncivSound = UncivSound.Fortify, - action = { - unit.fortify() - unitTable.selectedUnit = null - }) + addFortifyActions(actionList, unit, unitTable) } else if (unit.isFortified()) { actionList += UnitAction( - type = UnitActionType.Fortify, + type = if (unit.action!!.endsWith(" until healed")) + UnitActionType.FortifyUntilHealed else + UnitActionType.Fortify, canAct = false, isCurrentAction = true, title = "${"Fortification".tr()} ${unit.getFortificationTurns() * 20}%" @@ -320,6 +307,58 @@ class UnitActions { return actionList } + private fun addFortifyActions(actionList: ArrayList, unit: MapUnit, unitTable: UnitTable) { + + val action = UnitAction( + type = UnitActionType.Fortify, + canAct = unit.currentMovement > 0, + uncivSound = UncivSound.Fortify, + action = { + unit.fortify() + unitTable.selectedUnit = null + }) + + if (unit.health < 100) { + val actionForWounded = action.copy( + type = UnitActionType.FortifyUntilHealed, + title = UnitActionType.FortifyUntilHealed.value, + action = { + unit.fortifyUntilHealed() + unitTable.selectedUnit = null + }) + actionList += actionForWounded + } + + actionList += action + } + + private fun addSleepActions(actionList: ArrayList, unit: MapUnit, unitTable: UnitTable) { + + val isSleeping = unit.isSleeping() + + val action = UnitAction( + type = UnitActionType.Sleep, + canAct = !isSleeping, + isCurrentAction = isSleeping, + action = { + unit.action = Constants.unitActionSleep + unitTable.selectedUnit = null + }) + + if (unit.health < 100 && !isSleeping) { + val actionForWounded = action.copy( + type = UnitActionType.SleepUntilHealed, + title = UnitActionType.SleepUntilHealed.value, + action = { + unit.action = Constants.unitActionSleepUntilHealed + unitTable.selectedUnit = null + }) + actionList += actionForWounded + } + + actionList += action + } + fun canPillage(unit: MapUnit, tile: TileInfo): Boolean { if (tile.improvement == null || tile.improvement == Constants.barbarianEncampment || tile.improvement == Constants.ancientRuins diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt index b446676f79..5c39f6e576 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt @@ -18,40 +18,42 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table(){ touchable = Touchable.enabled } - fun getIconForUnitAction(unitAction:String): Actor { - if(unitAction.startsWith("Upgrade to")){ - // 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. 1 - val unitToUpgradeTo = Regex("""Upgrade to \[([^\]]*)\]""").find(unitAction)!!.groups[1]!!.value - return ImageGetter.getUnitIcon(unitToUpgradeTo) - } - when(unitAction){ - "Move unit" -> return ImageGetter.getStatIcon("Movement") - "Stop movement"-> return ImageGetter.getStatIcon("Movement").apply { color= Color.RED } - "Fortify" -> return ImageGetter.getImage("OtherIcons/Shield").apply { color= Color.BLACK } - "Promote" -> return ImageGetter.getImage("OtherIcons/Star").apply { color= Color.GOLD } - "Construct improvement" -> return ImageGetter.getUnitIcon(Constants.worker) - "Automate" -> return ImageGetter.getUnitIcon("Great Engineer") - "Stop automation" -> return ImageGetter.getImage("OtherIcons/Stop") - "Found city" -> return ImageGetter.getUnitIcon(Constants.settler) - "Hurry Research" -> return ImageGetter.getUnitIcon("Great Scientist") - "Construct Academy" -> return ImageGetter.getImprovementIcon("Academy") - "Start Golden Age" -> return ImageGetter.getUnitIcon("Great Artist") - "Construct Landmark" -> return ImageGetter.getImprovementIcon("Landmark") - "Hurry Wonder" -> return ImageGetter.getUnitIcon("Great Engineer") - "Construct Manufactory" -> return ImageGetter.getImprovementIcon("Manufactory") - "Conduct Trade Mission" -> return ImageGetter.getUnitIcon("Great Merchant") - "Construct Customs House" -> return ImageGetter.getImprovementIcon("Customs house") - "Set up" -> return ImageGetter.getUnitIcon("Catapult") - "Disband unit" -> return ImageGetter.getImage("OtherIcons/DisbandUnit") - "Sleep" -> return ImageGetter.getImage("OtherIcons/Sleep") - "Explore" -> return ImageGetter.getUnitIcon("Scout") - "Stop exploration" -> return ImageGetter.getImage("OtherIcons/Stop") - "Create Fishing Boats" -> return ImageGetter.getImprovementIcon("Fishing Boats") - "Create Oil well" -> return ImageGetter.getImprovementIcon("Oil well") - "Pillage" -> return ImageGetter.getImage("OtherIcons/Pillage") - "Construct road" -> return ImageGetter.getImprovementIcon("Road") - else -> return ImageGetter.getImage("OtherIcons/Star") + private fun getIconForUnitAction(unitAction:String): Actor { + when { + unitAction.startsWith("Upgrade to") -> { + // 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. 1 + val unitToUpgradeTo = Regex("""Upgrade to \[([^\]]*)\]""").find(unitAction)!!.groups[1]!!.value + return ImageGetter.getUnitIcon(unitToUpgradeTo) + } + unitAction.startsWith("Sleep") -> return ImageGetter.getImage("OtherIcons/Sleep") + unitAction.startsWith("Fortify") -> return ImageGetter.getImage("OtherIcons/Shield").apply { color= Color.BLACK } + else -> when(unitAction){ + "Move unit" -> return ImageGetter.getStatIcon("Movement") + "Stop movement"-> return ImageGetter.getStatIcon("Movement").apply { color= Color.RED } + "Promote" -> return ImageGetter.getImage("OtherIcons/Star").apply { color= Color.GOLD } + "Construct improvement" -> return ImageGetter.getUnitIcon(Constants.worker) + "Automate" -> return ImageGetter.getUnitIcon("Great Engineer") + "Stop automation" -> return ImageGetter.getImage("OtherIcons/Stop") + "Found city" -> return ImageGetter.getUnitIcon(Constants.settler) + "Hurry Research" -> return ImageGetter.getUnitIcon("Great Scientist") + "Construct Academy" -> return ImageGetter.getImprovementIcon("Academy") + "Start Golden Age" -> return ImageGetter.getUnitIcon("Great Artist") + "Construct Landmark" -> return ImageGetter.getImprovementIcon("Landmark") + "Hurry Wonder" -> return ImageGetter.getUnitIcon("Great Engineer") + "Construct Manufactory" -> return ImageGetter.getImprovementIcon("Manufactory") + "Conduct Trade Mission" -> return ImageGetter.getUnitIcon("Great Merchant") + "Construct Customs House" -> return ImageGetter.getImprovementIcon("Customs house") + "Set up" -> return ImageGetter.getUnitIcon("Catapult") + "Disband unit" -> return ImageGetter.getImage("OtherIcons/DisbandUnit") + "Explore" -> return ImageGetter.getUnitIcon("Scout") + "Stop exploration" -> return ImageGetter.getImage("OtherIcons/Stop") + "Create Fishing Boats" -> return ImageGetter.getImprovementIcon("Fishing Boats") + "Create Oil well" -> return ImageGetter.getImprovementIcon("Oil well") + "Pillage" -> return ImageGetter.getImage("OtherIcons/Pillage") + "Construct road" -> return ImageGetter.getImprovementIcon("Road") + else -> return ImageGetter.getImage("OtherIcons/Star") + } } } diff --git a/tests/src/com/unciv/testing/TranslationTests.kt b/tests/src/com/unciv/testing/TranslationTests.kt index 25c542cc64..b71a6ff995 100644 --- a/tests/src/com/unciv/testing/TranslationTests.kt +++ b/tests/src/com/unciv/testing/TranslationTests.kt @@ -4,6 +4,7 @@ package com.unciv.testing import com.badlogic.gdx.Gdx import com.badlogic.gdx.utils.Array import com.unciv.JsonParser +import com.unciv.models.UnitActionType import com.unciv.models.ruleset.Nation import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetCache @@ -52,6 +53,20 @@ class TranslationTests { allStringsHaveTranslation) } + @Test + fun allUnitActionsHaveTranslation() { + val actions: MutableSet = HashSet() + for (action in UnitActionType.values()) { + if (action == UnitActionType.Upgrade) + actions.add("Upgrade to [unitType] ([goldCost] gold)") + else + actions.add(action.value) + } + val allUnitActionsHaveTranslation = allStringAreTranslated(actions) + Assert.assertTrue("This test will only pass when there is a translation for all unit actions", + allUnitActionsHaveTranslation) + } + @Test fun allBuildingsHaveTranslation() { val allBuildingsHaveTranslation = allStringAreTranslated(ruleset.buildings.keys)