Creating Guard Action (#12324)

* Creating Guard Action

* Forgot Guard icon

* Rework so it doesn't show up for units that can Fortify

* Revert don't run is Fortified. We want weak Withdraw units like Scouts to flee

* Update Guard to work just like Fortify if they can Fortify

* Update tutorial notes
This commit is contained in:
itanasi 2024-10-27 23:17:24 -07:00 committed by GitHub
parent 4cfd93caf2
commit de43477072
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 59 additions and 10 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -507,6 +507,14 @@
"If the Interceptor is an Air Unit, the two units will damage each other in a straight fight with no Interception bonuses. And only the Attacking Air Sweep Unit gets any Air Sweep strength bonuses." "If the Interceptor is an Air Unit, the two units will damage each other in a straight fight with no Interception bonuses. And only the Attacking Air Sweep Unit gets any Air Sweep strength bonuses."
] ]
}, },
{
"name": "Fortify, Guard, Withdraw",
"civilopediaText": [
{"text": "Most Land units have the ability to Fortify. As they spend time without moving, they will slowly gain a Fortification Bonus to their Combat Strength representing digging in and preparing in case of an attack."},
{"text":"Some units have the ability to Withdraw before a melee attack is struck. However, perhaps you wish for them to stand their ground to protect a particular tile. If you do, then give them the Guard command and they will stay fixed until they die or you give them fresh orders."},
{"text":"This is NOT the same as Fortify but they will still gain the Fortification Bonus. They will simply not utilize their Withdraw ability. Fortified units with the Withdraw ability will try to flee when attacked."}
]
},
{ {
"name": "City Tile Blockade", "name": "City Tile Blockade",
"steps": [ "steps": [

View File

@ -1178,6 +1178,8 @@ Construct road =
Fortify = Fortify =
Fortify until healed = Fortify until healed =
Fortification = Fortification =
Guard =
Guarding =
Sleep = Sleep =
Sleep until healed = Sleep until healed =
Moving = Moving =

View File

@ -485,7 +485,7 @@ object Battle {
if (!attacker.unit.baseUnit.movesLikeAirUnits && !(attacker.isMelee() && defender.isDefeated())) if (!attacker.unit.baseUnit.movesLikeAirUnits && !(attacker.isMelee() && defender.isDefeated()))
unit.useMovementPoints(1f) unit.useMovementPoints(1f)
} else unit.currentMovement = 0f } else unit.currentMovement = 0f
if (unit.isFortified() || unit.isSleeping()) if (unit.isFortified() || unit.isSleeping() || unit.isGuarding())
attacker.unit.action = null // but not, for instance, if it's Set Up - then it should definitely keep the action! 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) { } else if (attacker is CityCombatant) {
attacker.city.attackedThisTurn = true attacker.city.attackedThisTurn = true
@ -640,7 +640,8 @@ object Battle {
if (defender.unit.isEmbarked()) return false if (defender.unit.isEmbarked()) return false
if (defender.unit.cache.cannotMove) return false if (defender.unit.cache.cannotMove) return false
if (defender.unit.isEscorting()) return false // running away and leaving the escorted unit defeats the purpose of escorting if (defender.unit.isEscorting()) return false // running away and leaving the escorted unit defeats the purpose of escorting
if (defender.unit.isGuarding()) return false // guarding this post and will fight to the death!
// Promotions have no effect as per what I could find in available documentation // Promotions have no effect as per what I could find in available documentation
val fromTile = defender.getTile() val fromTile = defender.getTile()
val attackerTile = attacker.getTile() val attackerTile = attacker.getTile()

View File

@ -226,7 +226,7 @@ object BattleDamage {
modifiers["Tile"] = (tileDefenceBonus * 100).toInt() modifiers["Tile"] = (tileDefenceBonus * 100).toInt()
if (defender.unit.isFortified()) if (defender.unit.isFortified() || defender.unit.isGuarding())
modifiers["Fortification"] = BattleConstants.FORTIFICATION_BONUS * defender.unit.getFortificationTurns() modifiers["Fortification"] = BattleConstants.FORTIFICATION_BONUS * defender.unit.getFortificationTurns()
} }

View File

@ -237,9 +237,10 @@ class MapUnit : IsPartOfGameInfoSerialization {
fun isActionUntilHealed() = action?.endsWith("until healed") == true fun isActionUntilHealed() = action?.endsWith("until healed") == true
fun isFortified() = action?.startsWith(UnitActionType.Fortify.value) == true fun isFortified() = action?.startsWith(UnitActionType.Fortify.value) == true
fun isGuarding() = action?.equals(UnitActionType.Guard.value) == true
fun isFortifyingUntilHealed() = isFortified() && isActionUntilHealed() fun isFortifyingUntilHealed() = isFortified() && isActionUntilHealed()
fun getFortificationTurns(): Int { fun getFortificationTurns(): Int {
if (!isFortified()) return 0 if (!(isFortified() || isGuarding())) return 0
return turnsFortified return turnsFortified
} }
@ -273,7 +274,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
!tile.isMarkedForCreatesOneImprovement() !tile.isMarkedForCreatesOneImprovement()
) return false ) return false
if (includeOtherEscortUnit && isEscorting() && !getOtherEscortUnit()!!.isIdle(false)) return false if (includeOtherEscortUnit && isEscorting() && !getOtherEscortUnit()!!.isIdle(false)) return false
return !(isFortified() || isExploring() || isSleeping() || isAutomated() || isMoving()) return !(isFortified() || isExploring() || isSleeping() || isAutomated() || isMoving() || isGuarding())
} }
fun getUniques(): Sequence<Unique> = tempUniquesMap.getAllUniques() fun getUniques(): Sequence<Unique> = tempUniquesMap.getAllUniques()

View File

@ -24,10 +24,12 @@ class UnitTurnManager(val unit: MapUnit) {
tile.getCity()?.shouldReassignPopulation = true tile.getCity()?.shouldReassignPopulation = true
} }
if (!unit.hasUnitMovedThisTurn() && unit.isFortified() && unit.turnsFortified < 2) { if (!unit.hasUnitMovedThisTurn()
&& (unit.isFortified() || (unit.isGuarding() && unit.canFortify()))
&& unit.turnsFortified < 2) {
unit.turnsFortified++ unit.turnsFortified++
} }
if (!unit.isFortified()) if (!unit.isFortified() && !unit.isGuarding())
unit.turnsFortified = 0 unit.turnsFortified = 0
if (!unit.hasUnitMovedThisTurn() || unit.hasUnique(UniqueType.HealsEvenAfterAction)) if (!unit.hasUnitMovedThisTurn() || unit.hasUnique(UniqueType.HealsEvenAfterAction))

View File

@ -105,5 +105,8 @@ class UnitUpgradeManager(val unit: MapUnit) {
// wake up if lost ability to fortify // wake up if lost ability to fortify
if (newUnit.isFortified() && !newUnit.canFortify(ignoreAlreadyFortified = true)) if (newUnit.isFortified() && !newUnit.canFortify(ignoreAlreadyFortified = true))
newUnit.action = null newUnit.action = null
// wake up from Guarding if can't Withdraw
if (newUnit.isGuarding() && !newUnit.hasUnique(UniqueType.WithdrawsBeforeMeleeCombat))
newUnit.action = null
} }
} }

View File

@ -376,7 +376,7 @@ class UnitMovement(val unit: MapUnit) {
unit.removeFromTile() // we "teleport" them away unit.removeFromTile() // we "teleport" them away
unit.putInTile(allowedTile) unit.putInTile(allowedTile)
// Cancel sleep or fortification if forcibly displaced - for now, leave movement / auto / explore orders // Cancel sleep or fortification if forcibly displaced - for now, leave movement / auto / explore orders
if (unit.isSleeping() || unit.isFortified()) if (unit.isSleeping() || unit.isFortified() || unit.isGuarding())
unit.action = null unit.action = null
unit.mostRecentMoveType = UnitMovementMemoryType.UnitTeleported unit.mostRecentMoveType = UnitMovementMemoryType.UnitTeleported
@ -435,7 +435,7 @@ class UnitMovement(val unit: MapUnit) {
unit.mostRecentMoveType = UnitMovementMemoryType.UnitMoved unit.mostRecentMoveType = UnitMovementMemoryType.UnitMoved
val pathToLastReachableTile = distanceToTiles.getPathToTile(lastReachableTile) val pathToLastReachableTile = distanceToTiles.getPathToTile(lastReachableTile)
if (unit.isFortified() || unit.isSetUpForSiege() || unit.isSleeping()) if (unit.isFortified() || unit.isGuarding() || unit.isSetUpForSiege() || unit.isSleeping())
unit.action = null // un-fortify/un-setup/un-sleep after moving unit.action = null // un-fortify/un-setup/un-sleep after moving
// If this unit is a carrier, keep record of its air payload whereabouts. // If this unit is a carrier, keep record of its air payload whereabouts.

View File

@ -126,6 +126,8 @@ enum class UnitActionType(
{ ImageGetter.getUnitActionPortrait("Fortify") }, UncivSound.Fortify), { ImageGetter.getUnitActionPortrait("Fortify") }, UncivSound.Fortify),
FortifyUntilHealed("Fortify until healed", FortifyUntilHealed("Fortify until healed",
{ ImageGetter.getUnitActionPortrait("FortifyUntilHealed") }, UncivSound.Fortify), { ImageGetter.getUnitActionPortrait("FortifyUntilHealed") }, UncivSound.Fortify),
Guard("Guard",
{ ImageGetter.getUnitActionPortrait("Guard") }, UncivSound.Fortify, defaultPage = 0),
Explore("Explore", Explore("Explore",
{ ImageGetter.getUnitActionPortrait("Explore") }), { ImageGetter.getUnitActionPortrait("Explore") }),
StopExploration("Stop exploration", StopExploration("Stop exploration",

View File

@ -138,6 +138,7 @@ class UnitIconGroup(val unit: MapUnit, val size: Float) : Group() {
return when { return when {
unit.isEmbarked() -> ImageGetter.getDrawable("UnitFlagIcons/UnitFlagEmbark") unit.isEmbarked() -> ImageGetter.getDrawable("UnitFlagIcons/UnitFlagEmbark")
unit.isFortified() -> ImageGetter.getDrawable("UnitFlagIcons/UnitFlagFortify") unit.isFortified() -> ImageGetter.getDrawable("UnitFlagIcons/UnitFlagFortify")
unit.isGuarding() -> ImageGetter.getDrawable("UnitFlagIcons/UnitFlagFortify")
unit.isCivilian() -> ImageGetter.getDrawable("UnitFlagIcons/UnitFlagCivilian") unit.isCivilian() -> ImageGetter.getDrawable("UnitFlagIcons/UnitFlagCivilian")
else -> ImageGetter.getDrawable("UnitFlagIcons/UnitFlag") else -> ImageGetter.getDrawable("UnitFlagIcons/UnitFlag")
} }
@ -147,6 +148,7 @@ class UnitIconGroup(val unit: MapUnit, val size: Float) : Group() {
return when { return when {
unit.isEmbarked() -> ImageGetter.getDrawableOrNull("UnitFlagIcons/UnitFlagEmbarkInner") unit.isEmbarked() -> ImageGetter.getDrawableOrNull("UnitFlagIcons/UnitFlagEmbarkInner")
unit.isFortified() -> ImageGetter.getDrawableOrNull("UnitFlagIcons/UnitFlagFortifyInner") unit.isFortified() -> ImageGetter.getDrawableOrNull("UnitFlagIcons/UnitFlagFortifyInner")
unit.isGuarding() -> ImageGetter.getDrawableOrNull("UnitFlagIcons/UnitFlagFortifyInner")
unit.isCivilian() -> ImageGetter.getDrawableOrNull("UnitFlagIcons/UnitFlagCivilianInner") unit.isCivilian() -> ImageGetter.getDrawableOrNull("UnitFlagIcons/UnitFlagCivilianInner")
else -> ImageGetter.getDrawableOrNull("UnitFlagIcons/UnitFlagInner") else -> ImageGetter.getDrawableOrNull("UnitFlagIcons/UnitFlagInner")
} }
@ -157,6 +159,7 @@ class UnitIconGroup(val unit: MapUnit, val size: Float) : Group() {
val filename = when { val filename = when {
unit.isEmbarked() -> "UnitFlagIcons/UnitFlagMaskEmbark" unit.isEmbarked() -> "UnitFlagIcons/UnitFlagMaskEmbark"
unit.isFortified() -> "UnitFlagIcons/UnitFlagMaskFortify" unit.isFortified() -> "UnitFlagIcons/UnitFlagMaskFortify"
unit.isGuarding() -> "UnitFlagIcons/UnitFlagMaskFortify"
unit.isCivilian() -> "UnitFlagIcons/UnitFlagMaskCivilian" unit.isCivilian() -> "UnitFlagIcons/UnitFlagMaskCivilian"
else -> "UnitFlagIcons/UnitFlagMask" else -> "UnitFlagIcons/UnitFlagMask"
} }
@ -170,6 +173,7 @@ class UnitIconGroup(val unit: MapUnit, val size: Float) : Group() {
return when { return when {
unit.isEmbarked() -> ImageGetter.getImage("UnitFlagIcons/UnitFlagSelectionEmbark") unit.isEmbarked() -> ImageGetter.getImage("UnitFlagIcons/UnitFlagSelectionEmbark")
unit.isFortified() -> ImageGetter.getImage("UnitFlagIcons/UnitFlagSelectionFortify") unit.isFortified() -> ImageGetter.getImage("UnitFlagIcons/UnitFlagSelectionFortify")
unit.isGuarding() -> ImageGetter.getImage("UnitFlagIcons/UnitFlagSelectionFortify")
unit.isCivilian() -> ImageGetter.getImage("UnitFlagIcons/UnitFlagSelectionCivilian") unit.isCivilian() -> ImageGetter.getImage("UnitFlagIcons/UnitFlagSelectionCivilian")
else -> ImageGetter.getImage("UnitFlagIcons/UnitFlagSelection") else -> ImageGetter.getImage("UnitFlagIcons/UnitFlagSelection")
} }

View File

@ -57,6 +57,7 @@ open class UnitOverviewTabHelpers {
return when { return when {
unit.action == null -> workerText unit.action == null -> workerText
unit.isFortified() -> UnitActionType.Fortify.value unit.isFortified() -> UnitActionType.Fortify.value
unit.isGuarding() -> UnitActionType.Guard.value
unit.isMoving() -> "Moving" unit.isMoving() -> "Moving"
unit.isAutomated() && workerText != null -> "[$workerText] ${Fonts.automate}" unit.isAutomated() && workerText != null -> "[$workerText] ${Fonts.automate}"
else -> unit.action else -> unit.action

View File

@ -78,6 +78,7 @@ object UnitActions {
UnitActionType.Paradrop to UnitActionsFromUniques::getParadropActions, UnitActionType.Paradrop to UnitActionsFromUniques::getParadropActions,
UnitActionType.AirSweep to UnitActionsFromUniques::getAirSweepActions, UnitActionType.AirSweep to UnitActionsFromUniques::getAirSweepActions,
UnitActionType.SetUp to UnitActionsFromUniques::getSetupActions, UnitActionType.SetUp to UnitActionsFromUniques::getSetupActions,
UnitActionType.Guard to UnitActionsFromUniques::getGuardActions,
UnitActionType.FoundCity to UnitActionsFromUniques::getFoundCityActions, UnitActionType.FoundCity to UnitActionsFromUniques::getFoundCityActions,
UnitActionType.ConstructImprovement to UnitActionsFromUniques::getBuildingImprovementsActions, UnitActionType.ConstructImprovement to UnitActionsFromUniques::getBuildingImprovementsActions,
UnitActionType.ConnectRoad to UnitActionsFromUniques::getConnectRoadActions, UnitActionType.ConnectRoad to UnitActionsFromUniques::getConnectRoadActions,
@ -295,7 +296,7 @@ object UnitActions {
} }
private suspend fun SequenceScope<UnitAction>.addSleepActions(unit: MapUnit, tile: Tile) { private suspend fun SequenceScope<UnitAction>.addSleepActions(unit: MapUnit, tile: Tile) {
if (unit.isFortified() || unit.canFortify() || !unit.hasMovement()) return if (unit.isFortified() || unit.canFortify() || unit.isGuarding() || !unit.hasMovement()) return
if (tile.hasImprovementInProgress() && unit.canBuildImprovement(tile.getTileImprovementInProgress()!!)) return if (tile.hasImprovementInProgress() && unit.canBuildImprovement(tile.getTileImprovementInProgress()!!)) return
yield(UnitAction(UnitActionType.Sleep, yield(UnitAction(UnitActionType.Sleep,

View File

@ -165,6 +165,30 @@ object UnitActionsFromUniques {
)) ))
} }
// Instead of Withdrawing, stand your ground!
// Different than Fortify
internal fun getGuardActions(unit: MapUnit, tile: Tile): Sequence<UnitAction> {
if (!unit.hasUnique(UniqueType.WithdrawsBeforeMeleeCombat)) return emptySequence()
if (unit.isGuarding()) {
val title = if (unit.canFortify()) "${"Guarding".tr()} ${unit.getFortificationTurns() * 20}%" else "Guarding".tr()
return sequenceOf(UnitAction(UnitActionType.Guard,
useFrequency = 0f,
isCurrentAction = true,
title = title
))
}
if (!unit.hasMovement()) return emptySequence()
return sequenceOf(UnitAction(UnitActionType.Guard,
useFrequency = 0f,
action = {
unit.action = UnitActionType.Guard.value
}.takeIf { !unit.isGuarding() })
)
}
internal fun getTriggerUniqueActions(unit: MapUnit, tile: Tile) = sequence { internal fun getTriggerUniqueActions(unit: MapUnit, tile: Tile) = sequence {
for (unique in unit.getUniques()) { for (unique in unit.getUniques()) {
// not a unit action // not a unit action