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."
]
},
{
"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",
"steps": [

View File

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

View File

@ -485,7 +485,7 @@ object Battle {
if (!attacker.unit.baseUnit.movesLikeAirUnits && !(attacker.isMelee() && defender.isDefeated()))
unit.useMovementPoints(1f)
} 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!
} else if (attacker is CityCombatant) {
attacker.city.attackedThisTurn = true
@ -640,6 +640,7 @@ object Battle {
if (defender.unit.isEmbarked()) 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.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
val fromTile = defender.getTile()

View File

@ -226,7 +226,7 @@ object BattleDamage {
modifiers["Tile"] = (tileDefenceBonus * 100).toInt()
if (defender.unit.isFortified())
if (defender.unit.isFortified() || defender.unit.isGuarding())
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 isFortified() = action?.startsWith(UnitActionType.Fortify.value) == true
fun isGuarding() = action?.equals(UnitActionType.Guard.value) == true
fun isFortifyingUntilHealed() = isFortified() && isActionUntilHealed()
fun getFortificationTurns(): Int {
if (!isFortified()) return 0
if (!(isFortified() || isGuarding())) return 0
return turnsFortified
}
@ -273,7 +274,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
!tile.isMarkedForCreatesOneImprovement()
) 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()

View File

@ -24,10 +24,12 @@ class UnitTurnManager(val unit: MapUnit) {
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++
}
if (!unit.isFortified())
if (!unit.isFortified() && !unit.isGuarding())
unit.turnsFortified = 0
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
if (newUnit.isFortified() && !newUnit.canFortify(ignoreAlreadyFortified = true))
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.putInTile(allowedTile)
// 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.mostRecentMoveType = UnitMovementMemoryType.UnitTeleported
@ -435,7 +435,7 @@ class UnitMovement(val unit: MapUnit) {
unit.mostRecentMoveType = UnitMovementMemoryType.UnitMoved
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
// 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),
FortifyUntilHealed("Fortify until healed",
{ ImageGetter.getUnitActionPortrait("FortifyUntilHealed") }, UncivSound.Fortify),
Guard("Guard",
{ ImageGetter.getUnitActionPortrait("Guard") }, UncivSound.Fortify, defaultPage = 0),
Explore("Explore",
{ ImageGetter.getUnitActionPortrait("Explore") }),
StopExploration("Stop exploration",

View File

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

View File

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

View File

@ -78,6 +78,7 @@ object UnitActions {
UnitActionType.Paradrop to UnitActionsFromUniques::getParadropActions,
UnitActionType.AirSweep to UnitActionsFromUniques::getAirSweepActions,
UnitActionType.SetUp to UnitActionsFromUniques::getSetupActions,
UnitActionType.Guard to UnitActionsFromUniques::getGuardActions,
UnitActionType.FoundCity to UnitActionsFromUniques::getFoundCityActions,
UnitActionType.ConstructImprovement to UnitActionsFromUniques::getBuildingImprovementsActions,
UnitActionType.ConnectRoad to UnitActionsFromUniques::getConnectRoadActions,
@ -295,7 +296,7 @@ object UnitActions {
}
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
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 {
for (unique in unit.getUniques()) {
// not a unit action