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 <spellman23@gmail.com>
This commit is contained in:
itanasi 2022-01-24 02:28:46 -08:00 committed by GitHub
parent 336e190ff0
commit 1fd65b9ffa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 37 additions and 9 deletions

View File

@ -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()

View File

@ -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() }

View File

@ -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 {

View File

@ -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
}

View File

@ -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) {