From 1fd65b9ffa296a03e4000618e13b9bb055a8038a Mon Sep 17 00:00:00 2001 From: itanasi <44038014+itanasi@users.noreply.github.com> Date: Mon, 24 Jan 2022 02:28:46 -0800 Subject: [PATCH] Ranged capture (#5975) * Initial attempt * Allow Ranged to move into unguarded Civilian Unit * Comment for clarity * Fix unit test so that it doesn't segfault and checks you can't move into military units * Unify that all units can move on to (and through) unguarded civilians that you are at war with Add TileInfo.getUnguardedCivilian() to quickly respond if there is an unguarded Civilian on the tile Something is bugged in movement code * Fix MapUnit.moveThroughTile() so that it doesn't segfault by fixing getUnguardedCivilian() * captureCivilianUnit() call is now redundant in postBattleMoveToAttackedTile() since canMoveTo() will now return true and capture will happen during the moveToTile() call Co-authored-by: temurakami --- core/src/com/unciv/logic/battle/Battle.kt | 7 +------ core/src/com/unciv/logic/map/MapUnit.kt | 6 ++++++ core/src/com/unciv/logic/map/TileInfo.kt | 8 ++++++++ .../com/unciv/logic/map/UnitMovementAlgorithms.kt | 13 ++++++++++--- .../unciv/logic/map/UnitMovementAlgorithmsTests.kt | 12 ++++++++++++ 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index 365872caaf..67400260d2 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -361,11 +361,6 @@ object Battle { if (!attacker.isMelee()) return if (!defender.isDefeated() && defender.getCivInfo() != attacker.getCivInfo()) return - // we destroyed an enemy military unit and there was a civilian unit in the same tile as well - // this has to be checked before canMoveTo, otherwise it will return false - if (attackedTile.civilianUnit != null && attackedTile.civilianUnit!!.civInfo != attacker.getCivInfo()) - captureCivilianUnit(attacker, MapUnitCombatant(attackedTile.civilianUnit!!)) - // 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)) { // Units that can move after attacking are not affected by zone of control if the @@ -504,7 +499,7 @@ object Battle { return null } - private fun captureCivilianUnit(attacker: ICombatant, defender: MapUnitCombatant, checkDefeat: Boolean = true) { + fun captureCivilianUnit(attacker: ICombatant, defender: MapUnitCombatant, checkDefeat: Boolean = true) { // need to save this because if the unit is captured its owner wil be overwritten val defenderCiv = defender.getCivInfo() diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 44dcf8aa40..191bcde9e9 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -5,6 +5,8 @@ import com.unciv.Constants import com.unciv.UncivGame import com.unciv.logic.automation.UnitAutomation import com.unciv.logic.automation.WorkerAutomation +import com.unciv.logic.battle.Battle +import com.unciv.logic.battle.MapUnitCombatant import com.unciv.logic.city.CityInfo import com.unciv.logic.city.RejectionReason import com.unciv.logic.civilization.CivilizationInfo @@ -876,6 +878,10 @@ class MapUnit { } if (tile.improvement == Constants.barbarianEncampment && !civInfo.isBarbarian()) clearEncampment(tile) + // Capture Enemy Civilian Unit if you move on top of it + if (tile.getUnguardedCivilian() != null && civInfo.isAtWarWith(tile.getUnguardedCivilian()!!.civInfo)) { + Battle.captureCivilianUnit(MapUnitCombatant(this), MapUnitCombatant(tile.civilianUnit!!)) + } val promotionUniques = tile.neighbors .flatMap { it.getAllTerrains() } diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 9e6052d2b3..d60f09fa01 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -128,6 +128,14 @@ open class TileInfo { return null } + /** Return null if military/air units on tile, or no civilian */ + fun getUnguardedCivilian(): MapUnit? { + if (militaryUnit != null) return null + if (airUnits.isNotEmpty()) return null + if (civilianUnit != null) return civilianUnit!! + return null + } + fun getCity(): CityInfo? = owningCity fun getLastTerrain(): Terrain = when { diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 93f5e3f71a..895894c12b 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -553,7 +553,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) { return if (unit.isCivilian()) tile.civilianUnit == null && (tile.militaryUnit == null || tile.militaryUnit!!.owner == unit.owner) else - tile.militaryUnit == null && (tile.civilianUnit == null || tile.civilianUnit!!.owner == unit.owner) + // can skip checking for airUnit since not a city + tile.militaryUnit == null && (tile.civilianUnit == null || tile.civilianUnit!!.owner == unit.owner || unit.civInfo.isAtWarWith(tile.civilianUnit!!.civInfo)) } private fun canAirUnitMoveTo(tile: TileInfo, unit: MapUnit): Boolean { @@ -618,8 +619,14 @@ class UnitMovementAlgorithms(val unit:MapUnit) { if (!unit.canEnterForeignTerrain && !tile.canCivPassThrough(unit.civInfo)) return false val firstUnit = tile.getFirstUnit() - if (firstUnit != null && firstUnit.civInfo != unit.civInfo && unit.civInfo.isAtWarWith(firstUnit.civInfo)) - return false + if (firstUnit != null) { + // Allow movement through unguarded, at-war Civilian Unit. Capture on the way + if (tile.getUnguardedCivilian() != null && unit.civInfo != firstUnit.civInfo && unit.civInfo.isAtWarWith(tile.civilianUnit!!.civInfo)) + return true + // Cannot enter hostile tile with any unit in there + if (firstUnit.civInfo != unit.civInfo && unit.civInfo.isAtWarWith(firstUnit.civInfo)) + return false + } return true } diff --git a/tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt b/tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt index 562ccf5f6d..9922ceb916 100644 --- a/tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt +++ b/tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt @@ -215,6 +215,18 @@ class UnitMovementAlgorithmsTests { otherCiv.nation = Nation().apply { name = Constants.barbarians } val otherUnit = MapUnit() otherUnit.civInfo = otherCiv + otherUnit.baseUnit = BaseUnit() + // melee check + otherUnit.baseUnit.strength = 1 + tile.militaryUnit = otherUnit + + for (type in ruleSet.unitTypes) { + unit.baseUnit = BaseUnit().apply { unitType = type.key; ruleset = ruleSet } + + Assert.assertFalse("$type must not enter occupied tile", unit.movement.canPassThrough(tile)) + } + // ranged check + otherUnit.baseUnit.rangedStrength = 1 // make non-Civilian ranged tile.militaryUnit = otherUnit for (type in ruleSet.unitTypes) {