mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-23 11:34:54 -04:00
Better "Withdraws before melee combat" unique
This commit is contained in:
parent
d075ad0100
commit
54d872045c
@ -125,11 +125,21 @@ object Battle {
|
|||||||
|
|
||||||
// Withdraw from melee ability
|
// Withdraw from melee ability
|
||||||
if (attacker is MapUnitCombatant && attacker.isMelee() && defender is MapUnitCombatant) {
|
if (attacker is MapUnitCombatant && attacker.isMelee() && defender is MapUnitCombatant) {
|
||||||
val withdrawUniques = defender.unit.getMatchingUniques(UniqueType.MayWithdraw)
|
val withdrawChance =
|
||||||
val combinedProbabilityToStayPut = withdrawUniques.fold(100) { probabilityToStayPut, unique -> probabilityToStayPut * (100-unique.params[0].toInt()) / 100 }
|
if (defender.unit.hasUnique(UniqueType.WithdrawsBeforeMeleeCombat, stateForConditionals = StateForConditionals(
|
||||||
val baseWithdrawChance = 100 - combinedProbabilityToStayPut
|
civInfo = defender.getCivInfo(),
|
||||||
// If a mod allows multiple withdraw properties, they stack multiplicatively
|
ourCombatant = defender,
|
||||||
if (baseWithdrawChance != 0 && doWithdrawFromMeleeAbility(attacker, defender, baseWithdrawChance))
|
theirCombatant = attacker,
|
||||||
|
tile = attackedTile
|
||||||
|
))
|
||||||
|
) 100
|
||||||
|
|
||||||
|
else 100 - defender.unit.getMatchingUniques(UniqueType.MayWithdraw)
|
||||||
|
.fold(100) { probabilityToWithdraw, unique ->
|
||||||
|
probabilityToWithdraw * (100 - unique.params[0].toInt()) / 100
|
||||||
|
}
|
||||||
|
|
||||||
|
if (withdrawChance != 0 && doWithdrawFromMeleeAbility(attacker, defender, withdrawChance))
|
||||||
return DamageDealt.None
|
return DamageDealt.None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,44 +625,29 @@ object Battle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doWithdrawFromMeleeAbility(attacker: ICombatant, defender: ICombatant, baseWithdrawChance: Int): Boolean {
|
private fun doWithdrawFromMeleeAbility(attacker: MapUnitCombatant, defender: MapUnitCombatant, withdrawChance: Int): Boolean {
|
||||||
if (baseWithdrawChance == 0) return false
|
if (withdrawChance == 0) return false
|
||||||
// Some notes...
|
|
||||||
// unit.getUniques() is a union of BaseUnit uniques and Promotion effects.
|
|
||||||
// according to some strategy guide the Slinger's withdraw ability is inherited on upgrade,
|
|
||||||
// according to the Ironclad entry of the wiki the Caravel's is lost on upgrade.
|
|
||||||
// therefore: Implement the flag as unique for the Caravel and Destroyer, as promotion for the Slinger.
|
|
||||||
if (attacker !is MapUnitCombatant) return false // allow simple access to unit property
|
|
||||||
if (defender !is MapUnitCombatant) return false
|
|
||||||
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
|
||||||
|
|
||||||
|
// This is where the chance comes into play
|
||||||
|
if (Random( // 'randomness' is consistent for turn and tile, to avoid save-scumming
|
||||||
|
attacker.getCivInfo().gameInfo.turns * defender.getTile().position.hashCode().toLong()
|
||||||
|
).nextInt(100) > withdrawChance) return false
|
||||||
|
|
||||||
// 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 attackBaseUnit = attacker.unit.baseUnit
|
|
||||||
val defendBaseUnit = defender.unit.baseUnit
|
|
||||||
val fromTile = defender.getTile()
|
val fromTile = defender.getTile()
|
||||||
val attTile = attacker.getTile()
|
val attackerTile = attacker.getTile()
|
||||||
|
|
||||||
fun canNotWithdrawTo(tile: Tile): Boolean { // if the tile is what the defender can't withdraw to, this fun will return true
|
fun canNotWithdrawTo(tile: Tile): Boolean { // if the tile is what the defender can't withdraw to, this fun will return true
|
||||||
return !defender.unit.movement.canMoveTo(tile)
|
return !defender.unit.movement.canMoveTo(tile)
|
||||||
|| defendBaseUnit.isLandUnit() && !tile.isLand // forbid retreat from land to sea - embarked already excluded
|
|| defender.isLandUnit() && !tile.isLand // forbid retreat from land to sea - embarked already excluded
|
||||||
|| tile.isCityCenter() && tile.getOwner() != defender.getCivInfo() // forbid retreat into the city which doesn't belong to the defender
|
|| tile.isCityCenter() && tile.getOwner() != defender.getCivInfo() // forbid retreat into the city which doesn't belong to the defender
|
||||||
}
|
}
|
||||||
/* Calculate success chance: Base chance from json, calculation method from https://www.bilibili.com/read/cv2216728
|
|
||||||
In general, except attacker's tile, 5 tiles neighbors the defender :
|
val firstCandidateTiles = fromTile.neighbors.filterNot { it == attackerTile || it in attackerTile.neighbors }
|
||||||
2 of which are also attacker's neighbors ( we call them 2-Tiles) and the other 3 aren't (we call them 3-Tiles).
|
|
||||||
Withdraw chance depends on 2 factors : attacker's movement and how many tiles in 3-Tiles the defender can't withdraw to.
|
|
||||||
If the defender can withdraw, at first we choose a tile as toTile from 3-Tiles the defender can withdraw to.
|
|
||||||
If 3-Tiles the defender can withdraw to is null, we choose this from 2-Tiles the defender can withdraw to.
|
|
||||||
If 2-Tiles the defender can withdraw to is also null, we return false.
|
|
||||||
*/
|
|
||||||
val percentChance = baseWithdrawChance - max(0, (attackBaseUnit.movement-2)) * 20 -
|
|
||||||
fromTile.neighbors.filterNot { it == attTile || it in attTile.neighbors }.count { canNotWithdrawTo(it) } * 20
|
|
||||||
// Get a random number in [0,100) : if the number <= percentChance, defender will withdraw from melee
|
|
||||||
if (Random( // 'randomness' is consistent for turn and tile, to avoid save-scumming
|
|
||||||
(attacker.getCivInfo().gameInfo.turns * defender.getTile().hashCode()).toLong()
|
|
||||||
).nextInt(100) > percentChance) return false
|
|
||||||
val firstCandidateTiles = fromTile.neighbors.filterNot { it == attTile || it in attTile.neighbors }
|
|
||||||
.filterNot { canNotWithdrawTo(it) }
|
.filterNot { canNotWithdrawTo(it) }
|
||||||
val secondCandidateTiles = fromTile.neighbors.filter { it in attTile.neighbors }
|
val secondCandidateTiles = fromTile.neighbors.filter { it in attackerTile.neighbors }
|
||||||
.filterNot { canNotWithdrawTo(it) }
|
.filterNot { canNotWithdrawTo(it) }
|
||||||
val toTile: Tile = when {
|
val toTile: Tile = when {
|
||||||
firstCandidateTiles.any() -> firstCandidateTiles.toList().random()
|
firstCandidateTiles.any() -> firstCandidateTiles.toList().random()
|
||||||
@ -667,11 +662,12 @@ object Battle {
|
|||||||
// and count 1 attack for attacker but leave it in place
|
// and count 1 attack for attacker but leave it in place
|
||||||
reduceAttackerMovementPointsAndAttacks(attacker, defender)
|
reduceAttackerMovementPointsAndAttacks(attacker, defender)
|
||||||
|
|
||||||
val attackingUnit = attackBaseUnit.name; val defendingUnit = defendBaseUnit.name
|
val attackerName = attacker.getName()
|
||||||
val notificationString = "[$defendingUnit] withdrew from a [$attackingUnit]"
|
val defenderName = defender.getName()
|
||||||
|
val notificationString = "[$defenderName] withdrew from a [$attackerName]"
|
||||||
val locations = LocationAction(toTile.position, attacker.getTile().position)
|
val locations = LocationAction(toTile.position, attacker.getTile().position)
|
||||||
defender.getCivInfo().addNotification(notificationString, locations, NotificationCategory.War, defendingUnit, NotificationIcon.War, attackingUnit)
|
defender.getCivInfo().addNotification(notificationString, locations, NotificationCategory.War, defenderName, NotificationIcon.War, attackerName)
|
||||||
attacker.getCivInfo().addNotification(notificationString, locations, NotificationCategory.War, defendingUnit, NotificationIcon.War, attackingUnit)
|
attacker.getCivInfo().addNotification(notificationString, locations, NotificationCategory.War, defenderName, NotificationIcon.War, attackerName)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +411,8 @@ enum class UniqueType(
|
|||||||
@Deprecated("As of 4.12.4", ReplaceWith("No damage penalty for wounded units"))
|
@Deprecated("As of 4.12.4", ReplaceWith("No damage penalty for wounded units"))
|
||||||
NoDamagePenalty("Damage is ignored when determining unit Strength", UniqueTarget.Unit, UniqueTarget.Global),
|
NoDamagePenalty("Damage is ignored when determining unit Strength", UniqueTarget.Unit, UniqueTarget.Global),
|
||||||
Uncapturable("Uncapturable", UniqueTarget.Unit),
|
Uncapturable("Uncapturable", UniqueTarget.Unit),
|
||||||
// Replace with "Withdraws before melee combat <with [amount]% chance>"?
|
WithdrawsBeforeMeleeCombat("Withdraws before melee combat", UniqueTarget.Unit),
|
||||||
|
@Deprecated("As of 4.12.4", ReplaceWith("Withdraws before melee combat <with [amount]% chance>"))
|
||||||
MayWithdraw("May withdraw before melee ([amount]%)", UniqueTarget.Unit),
|
MayWithdraw("May withdraw before melee ([amount]%)", UniqueTarget.Unit),
|
||||||
CannotCaptureCities("Unable to capture cities", UniqueTarget.Unit),
|
CannotCaptureCities("Unable to capture cities", UniqueTarget.Unit),
|
||||||
CannotPillage("Unable to pillage tiles", UniqueTarget.Unit),
|
CannotPillage("Unable to pillage tiles", UniqueTarget.Unit),
|
||||||
|
@ -876,7 +876,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
|||||||
??? example "No defensive terrain penalty"
|
??? example "No defensive terrain penalty"
|
||||||
Applicable to: Global, Unit
|
Applicable to: Global, Unit
|
||||||
|
|
||||||
??? example "Damage is ignored when determining unit Strength"
|
??? example "No damage penalty for wounded units"
|
||||||
Applicable to: Global, Unit
|
Applicable to: Global, Unit
|
||||||
|
|
||||||
??? example "No movement cost to pillage"
|
??? example "No movement cost to pillage"
|
||||||
@ -1343,9 +1343,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
|||||||
??? example "Uncapturable"
|
??? example "Uncapturable"
|
||||||
Applicable to: Unit
|
Applicable to: Unit
|
||||||
|
|
||||||
??? example "May withdraw before melee ([amount]%)"
|
??? example "Withdraws before melee combat"
|
||||||
Example: "May withdraw before melee ([3]%)"
|
|
||||||
|
|
||||||
Applicable to: Unit
|
Applicable to: Unit
|
||||||
|
|
||||||
??? example "Unable to capture cities"
|
??? example "Unable to capture cities"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user