mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 22:37:02 -04:00
Fix and cache CannotMove unique (#9596)
This commit is contained in:
parent
ea505e3e97
commit
7f626083ee
@ -485,7 +485,7 @@ object Battle {
|
|||||||
private fun postBattleMoveToAttackedTile(attacker: ICombatant, defender: ICombatant, attackedTile: Tile) {
|
private fun postBattleMoveToAttackedTile(attacker: ICombatant, defender: ICombatant, attackedTile: Tile) {
|
||||||
if (!attacker.isMelee()) return
|
if (!attacker.isMelee()) return
|
||||||
if (!defender.isDefeated() && defender.getCivInfo() != attacker.getCivInfo()) return
|
if (!defender.isDefeated() && defender.getCivInfo() != attacker.getCivInfo()) return
|
||||||
if (attacker is MapUnitCombatant && attacker.hasUnique(UniqueType.CannotMove)) return
|
if (attacker is MapUnitCombatant && attacker.unit.cache.cannotMove) return
|
||||||
|
|
||||||
// This is so that if we attack e.g. a barbarian in enemy territory that we can't enter, we won't enter it
|
// This is so that if we attack e.g. a barbarian in enemy territory that we can't enter, we won't enter it
|
||||||
if ((attacker as MapUnitCombatant).unit.movement.canMoveTo(attackedTile)) {
|
if ((attacker as MapUnitCombatant).unit.movement.canMoveTo(attackedTile)) {
|
||||||
@ -1107,7 +1107,7 @@ object Battle {
|
|||||||
if (attacker !is MapUnitCombatant) return false // allow simple access to unit property
|
if (attacker !is MapUnitCombatant) return false // allow simple access to unit property
|
||||||
if (defender !is MapUnitCombatant) return false
|
if (defender !is MapUnitCombatant) return false
|
||||||
if (defender.unit.isEmbarked()) return false
|
if (defender.unit.isEmbarked()) return false
|
||||||
if (defender.hasUnique(UniqueType.CannotMove)) return false
|
if (defender.unit.cache.cannotMove) 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 attackBaseUnit = attacker.unit.baseUnit
|
||||||
val defendBaseUnit = defender.unit.baseUnit
|
val defendBaseUnit = defender.unit.baseUnit
|
||||||
|
@ -6,43 +6,40 @@ import com.unciv.models.ruleset.unique.StateForConditionals
|
|||||||
import com.unciv.models.ruleset.unique.Unique
|
import com.unciv.models.ruleset.unique.Unique
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
|
|
||||||
class MapUnitCache(val mapUnit: MapUnit) {
|
// Note: Single use in MapUnit and it's @Transient there, so no need for that here
|
||||||
|
class MapUnitCache(private val mapUnit: MapUnit) {
|
||||||
// These are for performance improvements to getMovementCostBetweenAdjacentTiles,
|
// These are for performance improvements to getMovementCostBetweenAdjacentTiles,
|
||||||
// a major component of getDistanceToTilesWithinTurn,
|
// a major component of getDistanceToTilesWithinTurn,
|
||||||
// which in turn is a component of getShortestPath and canReach
|
// which in turn is a component of getShortestPath and canReach
|
||||||
@Transient
|
|
||||||
var ignoresTerrainCost = false
|
var ignoresTerrainCost = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@Transient
|
|
||||||
var ignoresZoneOfControl = false
|
var ignoresZoneOfControl = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@Transient
|
|
||||||
var allTilesCosts1 = false
|
var allTilesCosts1 = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@Transient
|
|
||||||
var canPassThroughImpassableTiles = false
|
var canPassThroughImpassableTiles = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@Transient
|
|
||||||
var roughTerrainPenalty = false
|
var roughTerrainPenalty = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
/** `true` if movement 0 _or_ has CannotMove unique */
|
||||||
|
var cannotMove = false
|
||||||
|
private set
|
||||||
|
|
||||||
/** If set causes an early exit in getMovementCostBetweenAdjacentTiles
|
/** If set causes an early exit in getMovementCostBetweenAdjacentTiles
|
||||||
* - means no double movement uniques, roughTerrainPenalty or ignoreHillMovementCost */
|
* - means no double movement uniques, roughTerrainPenalty or ignoreHillMovementCost */
|
||||||
@Transient
|
|
||||||
var noTerrainMovementUniques = false
|
var noTerrainMovementUniques = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/** If set causes a second early exit in getMovementCostBetweenAdjacentTiles */
|
/** If set causes a second early exit in getMovementCostBetweenAdjacentTiles */
|
||||||
@Transient
|
|
||||||
var noBaseTerrainOrHillDoubleMovementUniques = false
|
var noBaseTerrainOrHillDoubleMovementUniques = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/** If set skips tile.matchesFilter tests for double movement in getMovementCostBetweenAdjacentTiles */
|
/** If set skips tile.matchesFilter tests for double movement in getMovementCostBetweenAdjacentTiles */
|
||||||
@Transient
|
|
||||||
var noFilteredDoubleMovementUniques = false
|
var noFilteredDoubleMovementUniques = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@ -50,33 +47,24 @@ class MapUnitCache(val mapUnit: MapUnit) {
|
|||||||
enum class DoubleMovementTerrainTarget { Feature, Base, Hill, Filter }
|
enum class DoubleMovementTerrainTarget { Feature, Base, Hill, Filter }
|
||||||
class DoubleMovement(val terrainTarget: DoubleMovementTerrainTarget, val unique: Unique)
|
class DoubleMovement(val terrainTarget: DoubleMovementTerrainTarget, val unique: Unique)
|
||||||
/** Mod-friendly cache of double-movement terrains */
|
/** Mod-friendly cache of double-movement terrains */
|
||||||
@Transient
|
|
||||||
val doubleMovementInTerrain = HashMap<String, DoubleMovement>()
|
val doubleMovementInTerrain = HashMap<String, DoubleMovement>()
|
||||||
|
|
||||||
@Transient
|
|
||||||
var canEnterIceTiles = false
|
var canEnterIceTiles = false
|
||||||
|
|
||||||
@Transient
|
|
||||||
var cannotEnterOceanTiles = false
|
var cannotEnterOceanTiles = false
|
||||||
|
|
||||||
@Transient
|
|
||||||
var canEnterForeignTerrain: Boolean = false
|
var canEnterForeignTerrain: Boolean = false
|
||||||
|
|
||||||
@Transient
|
|
||||||
var costToDisembark: Float? = null
|
var costToDisembark: Float? = null
|
||||||
|
|
||||||
@Transient
|
|
||||||
var costToEmbark: Float? = null
|
var costToEmbark: Float? = null
|
||||||
|
|
||||||
@Transient
|
|
||||||
var paradropRange = 0
|
var paradropRange = 0
|
||||||
|
|
||||||
@Transient
|
|
||||||
var hasUniqueToBuildImprovements = false // not canBuildImprovements to avoid confusion
|
var hasUniqueToBuildImprovements = false // not canBuildImprovements to avoid confusion
|
||||||
|
|
||||||
@Transient
|
|
||||||
var hasStrengthBonusInRadiusUnique = false
|
var hasStrengthBonusInRadiusUnique = false
|
||||||
@Transient
|
|
||||||
var hasCitadelPlacementUnique = false
|
var hasCitadelPlacementUnique = false
|
||||||
|
|
||||||
fun updateUniques(){
|
fun updateUniques(){
|
||||||
@ -86,6 +74,7 @@ class MapUnitCache(val mapUnit: MapUnit) {
|
|||||||
ignoresTerrainCost = mapUnit.hasUnique(UniqueType.IgnoresTerrainCost)
|
ignoresTerrainCost = mapUnit.hasUnique(UniqueType.IgnoresTerrainCost)
|
||||||
ignoresZoneOfControl = mapUnit.hasUnique(UniqueType.IgnoresZOC)
|
ignoresZoneOfControl = mapUnit.hasUnique(UniqueType.IgnoresZOC)
|
||||||
roughTerrainPenalty = mapUnit.hasUnique(UniqueType.RoughTerrainPenalty)
|
roughTerrainPenalty = mapUnit.hasUnique(UniqueType.RoughTerrainPenalty)
|
||||||
|
cannotMove = mapUnit.hasUnique(UniqueType.CannotMove) || mapUnit.baseUnit.movement == 0
|
||||||
|
|
||||||
doubleMovementInTerrain.clear()
|
doubleMovementInTerrain.clear()
|
||||||
for (unique in mapUnit.getMatchingUniques(UniqueType.DoubleMovementOnTerrain, stateForConditionals = StateForConditionals.IgnoreConditionals)) {
|
for (unique in mapUnit.getMatchingUniques(UniqueType.DoubleMovementOnTerrain, stateForConditionals = StateForConditionals.IgnoreConditionals)) {
|
||||||
|
@ -34,6 +34,7 @@ class UnitMovement(val unit: MapUnit) {
|
|||||||
civInfo: Civilization,
|
civInfo: Civilization,
|
||||||
considerZoneOfControl: Boolean = true
|
considerZoneOfControl: Boolean = true
|
||||||
): Float {
|
): Float {
|
||||||
|
if (unit.cache.cannotMove) return 100f
|
||||||
|
|
||||||
if (from.isLand != to.isLand && unit.baseUnit.isLandUnit())
|
if (from.isLand != to.isLand && unit.baseUnit.isLandUnit())
|
||||||
return if (from.isWater && to.isLand) unit.cache.costToDisembark ?: 100f
|
return if (from.isWater && to.isLand) unit.cache.costToDisembark ?: 100f
|
||||||
@ -187,12 +188,15 @@ class UnitMovement(val unit: MapUnit) {
|
|||||||
movementCostCache: HashMap<Pair<Tile, Tile>, Float> = HashMap()
|
movementCostCache: HashMap<Pair<Tile, Tile>, Float> = HashMap()
|
||||||
): PathsToTilesWithinTurn {
|
): PathsToTilesWithinTurn {
|
||||||
val distanceToTiles = PathsToTilesWithinTurn()
|
val distanceToTiles = PathsToTilesWithinTurn()
|
||||||
if (unitMovement == 0f) return distanceToTiles
|
|
||||||
|
|
||||||
val currentUnitTile = unit.currentTile
|
val currentUnitTile = unit.currentTile
|
||||||
// This is for performance, because this is called all the time
|
// This is for performance, because this is called all the time
|
||||||
val unitTile = if (origin == currentUnitTile.position) currentUnitTile else currentUnitTile.tileMap[origin]
|
val unitTile = if (origin == currentUnitTile.position) currentUnitTile else currentUnitTile.tileMap[origin]
|
||||||
distanceToTiles[unitTile] = ParentTileAndTotalDistance(unitTile, unitTile, 0f)
|
distanceToTiles[unitTile] = ParentTileAndTotalDistance(unitTile, unitTile, 0f)
|
||||||
|
|
||||||
|
// If I can't move my only option is to stay...
|
||||||
|
if (unitMovement == 0f || unit.cache.cannotMove) return distanceToTiles
|
||||||
|
|
||||||
var tilesToCheck = listOf(unitTile)
|
var tilesToCheck = listOf(unitTile)
|
||||||
|
|
||||||
while (tilesToCheck.isNotEmpty()) {
|
while (tilesToCheck.isNotEmpty()) {
|
||||||
@ -240,7 +244,8 @@ class UnitMovement(val unit: MapUnit) {
|
|||||||
* Returns an empty list if there's no way to get to the destination.
|
* Returns an empty list if there's no way to get to the destination.
|
||||||
*/
|
*/
|
||||||
fun getShortestPath(destination: Tile, avoidDamagingTerrain: Boolean = false): List<Tile> {
|
fun getShortestPath(destination: Tile, avoidDamagingTerrain: Boolean = false): List<Tile> {
|
||||||
if (unit.hasUnique(UniqueType.CannotMove)) return listOf()
|
if (unit.cache.cannotMove) return listOf()
|
||||||
|
|
||||||
// First try and find a path without damaging terrain
|
// First try and find a path without damaging terrain
|
||||||
if (!avoidDamagingTerrain && unit.civ.passThroughImpassableUnlocked && unit.baseUnit.isLandUnit()) {
|
if (!avoidDamagingTerrain && unit.civ.passThroughImpassableUnlocked && unit.baseUnit.isLandUnit()) {
|
||||||
val damageFreePath = getShortestPath(destination, true)
|
val damageFreePath = getShortestPath(destination, true)
|
||||||
@ -392,14 +397,14 @@ class UnitMovement(val unit: MapUnit) {
|
|||||||
/** This is performance-heavy - use as last resort, only after checking everything else!
|
/** This is performance-heavy - use as last resort, only after checking everything else!
|
||||||
* Also note that REACHABLE tiles are not necessarily tiles that the unit CAN ENTER */
|
* Also note that REACHABLE tiles are not necessarily tiles that the unit CAN ENTER */
|
||||||
fun canReach(destination: Tile): Boolean {
|
fun canReach(destination: Tile): Boolean {
|
||||||
if (unit.hasUnique(UniqueType.CannotMove)) return false
|
if (unit.cache.cannotMove) return destination == unit.getTile()
|
||||||
if (unit.baseUnit.movesLikeAirUnits() || unit.isPreparingParadrop())
|
if (unit.baseUnit.movesLikeAirUnits() || unit.isPreparingParadrop())
|
||||||
return canReachInCurrentTurn(destination)
|
return canReachInCurrentTurn(destination)
|
||||||
return getShortestPath(destination).any()
|
return getShortestPath(destination).any()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun canReachInCurrentTurn(destination: Tile): Boolean {
|
private fun canReachInCurrentTurn(destination: Tile): Boolean {
|
||||||
if (unit.hasUnique(UniqueType.CannotMove)) return false
|
if (unit.cache.cannotMove) return destination == unit.getTile()
|
||||||
if (unit.baseUnit.movesLikeAirUnits())
|
if (unit.baseUnit.movesLikeAirUnits())
|
||||||
return unit.currentTile.aerialDistanceTo(destination) <= unit.getMaxMovementForAirUnits()
|
return unit.currentTile.aerialDistanceTo(destination) <= unit.getMaxMovementForAirUnits()
|
||||||
if (unit.isPreparingParadrop())
|
if (unit.isPreparingParadrop())
|
||||||
@ -409,7 +414,7 @@ class UnitMovement(val unit: MapUnit) {
|
|||||||
|
|
||||||
fun getReachableTilesInCurrentTurn(): Sequence<Tile> {
|
fun getReachableTilesInCurrentTurn(): Sequence<Tile> {
|
||||||
return when {
|
return when {
|
||||||
unit.hasUnique(UniqueType.CannotMove) -> emptySequence()
|
unit.cache.cannotMove -> sequenceOf(unit.getTile())
|
||||||
unit.baseUnit.movesLikeAirUnits() ->
|
unit.baseUnit.movesLikeAirUnits() ->
|
||||||
unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getMaxMovementForAirUnits()))
|
unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getMaxMovementForAirUnits()))
|
||||||
unit.isPreparingParadrop() ->
|
unit.isPreparingParadrop() ->
|
||||||
@ -439,7 +444,7 @@ class UnitMovement(val unit: MapUnit) {
|
|||||||
if (unit.baseUnit.movesLikeAirUnits()) return false
|
if (unit.baseUnit.movesLikeAirUnits()) return false
|
||||||
// We can't swap with ourself
|
// We can't swap with ourself
|
||||||
if (reachableTile == unit.getTile()) return false
|
if (reachableTile == unit.getTile()) return false
|
||||||
if (unit.hasUnique(UniqueType.CannotMove)) return false
|
if (unit.cache.cannotMove) return false
|
||||||
// Check whether the tile contains a unit of the same type as us that we own and that can
|
// Check whether the tile contains a unit of the same type as us that we own and that can
|
||||||
// also reach our tile in its current turn.
|
// also reach our tile in its current turn.
|
||||||
val otherUnit = (
|
val otherUnit = (
|
||||||
@ -450,7 +455,7 @@ class UnitMovement(val unit: MapUnit) {
|
|||||||
) ?: return false
|
) ?: return false
|
||||||
val ourPosition = unit.getTile()
|
val ourPosition = unit.getTile()
|
||||||
if (otherUnit.owner != unit.owner
|
if (otherUnit.owner != unit.owner
|
||||||
|| otherUnit.hasUnique(UniqueType.CannotMove)
|
|| otherUnit.cache.cannotMove // redundant, line below would cover it too
|
||||||
|| !otherUnit.movement.canReachInCurrentTurn(ourPosition)) return false
|
|| !otherUnit.movement.canReachInCurrentTurn(ourPosition)) return false
|
||||||
// Check if we could enter their tile if they wouldn't be there
|
// Check if we could enter their tile if they wouldn't be there
|
||||||
otherUnit.removeFromTile()
|
otherUnit.removeFromTile()
|
||||||
@ -728,7 +733,7 @@ class UnitMovement(val unit: MapUnit) {
|
|||||||
|
|
||||||
// Can a paratrooper land at this tile?
|
// Can a paratrooper land at this tile?
|
||||||
fun canParadropOn(destination: Tile): Boolean {
|
fun canParadropOn(destination: Tile): Boolean {
|
||||||
if (unit.hasUnique(UniqueType.CannotMove)) return false
|
if (unit.cache.cannotMove) return false
|
||||||
// Can only move to land tiles within range that are visible and not impassible
|
// Can only move to land tiles within range that are visible and not impassible
|
||||||
// Based on some testing done in the base game
|
// Based on some testing done in the base game
|
||||||
if (!destination.isLand || destination.isImpassible() || !unit.civ.viewableTiles.contains(destination)) return false
|
if (!destination.isLand || destination.isImpassible() || !unit.civ.viewableTiles.contains(destination)) return false
|
||||||
|
@ -663,7 +663,7 @@ class WorldMapHolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add back in the red markers for Air Unit Attack range since they can't move, but can still attack
|
// Add back in the red markers for Air Unit Attack range since they can't move, but can still attack
|
||||||
if (unit.hasUnique(UniqueType.CannotMove) && isAirUnit && !unit.isPreparingAirSweep()) {
|
if (unit.cache.cannotMove && isAirUnit && !unit.isPreparingAirSweep()) {
|
||||||
val tilesInAttackRange = unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange()))
|
val tilesInAttackRange = unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange()))
|
||||||
for (tile in tilesInAttackRange) {
|
for (tile in tilesInAttackRange) {
|
||||||
// The tile is within attack range
|
// The tile is within attack range
|
||||||
|
Loading…
x
Reference in New Issue
Block a user