mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-09 15:29:32 +07:00
Speedup of canPassThrough() method (#2394)
* Unit tests for "canPathThrough" * Refactoring of canEnterTiles() and isAtWarWith() * Do not check isEmpty() twice * Using of the cached MapUnit uniques
This commit is contained in:
@ -280,9 +280,9 @@ class CivilizationInfo {
|
||||
fun isAtWarWith(otherCiv:CivilizationInfo): Boolean {
|
||||
if (otherCiv.civName == civName) return false // never at war with itself
|
||||
if (otherCiv.isBarbarian() || isBarbarian()) return true
|
||||
if (!diplomacy.containsKey(otherCiv.civName)) // not encountered yet
|
||||
return false
|
||||
return getDiplomacyManager(otherCiv).diplomaticStatus == DiplomaticStatus.War
|
||||
val diplomacyManager = diplomacy[otherCiv.civName]
|
||||
?: return false // not encountered yet
|
||||
return diplomacyManager.diplomaticStatus == DiplomaticStatus.War
|
||||
}
|
||||
|
||||
fun isAtWar() = diplomacy.values.any { it.diplomaticStatus== DiplomaticStatus.War && !it.otherCiv().isDefeated() }
|
||||
@ -453,12 +453,12 @@ class CivilizationInfo {
|
||||
|
||||
fun canEnterTiles(otherCiv: CivilizationInfo): Boolean {
|
||||
if (otherCiv==this) return true
|
||||
if(nation.isBarbarian() && gameInfo.turns >= gameInfo.difficultyObject.turnBarbariansCanEnterPlayerTiles) return true
|
||||
if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet
|
||||
return false
|
||||
if(isAtWarWith(otherCiv)) return true
|
||||
if(getDiplomacyManager(otherCiv).hasOpenBorders) return true
|
||||
return false
|
||||
if (otherCiv.isBarbarian()) return true
|
||||
if (nation.isBarbarian() && gameInfo.turns >= gameInfo.difficultyObject.turnBarbariansCanEnterPlayerTiles)
|
||||
return true
|
||||
val diplomacyManager = diplomacy[otherCiv.civName]
|
||||
?: return false // not encountered yet
|
||||
return (diplomacyManager.hasOpenBorders || diplomacyManager.diplomaticStatus == DiplomaticStatus.War)
|
||||
}
|
||||
|
||||
fun addNotification(text: String, location: Vector2?, color: Color) {
|
||||
|
@ -39,6 +39,9 @@ class MapUnit {
|
||||
@Transient var doubleMovementInCoast = false
|
||||
@Transient var doubleMovementInForestAndJungle = false
|
||||
@Transient var doubleMovementInSnowTundraAndHills = false
|
||||
@Transient var canEnterIceTiles = false
|
||||
@Transient var cannotEnterOceanTiles = false
|
||||
@Transient var cannotEnterOceanTilesUntilAstronomy = false
|
||||
|
||||
lateinit var owner: String
|
||||
lateinit var name: String
|
||||
@ -134,11 +137,14 @@ class MapUnit {
|
||||
uniques.addAll(promotions.promotions.map { currentTile.tileMap.gameInfo.ruleSet.unitPromotions[it]!!.effect })
|
||||
tempUniques = uniques
|
||||
|
||||
if("Ignores terrain cost" in uniques) ignoresTerrainCost = true
|
||||
if("Rough terrain penalty" in uniques) roughTerrainPenalty = true
|
||||
if("Double movement in coast" in uniques) doubleMovementInCoast = true
|
||||
if("Double movement rate through Forest and Jungle" in uniques) doubleMovementInForestAndJungle = true
|
||||
if("Double movement in Snow, Tundra and Hills" in uniques) doubleMovementInSnowTundraAndHills = true
|
||||
ignoresTerrainCost = ("Ignores terrain cost" in uniques)
|
||||
roughTerrainPenalty = ("Rough terrain penalty" in uniques)
|
||||
doubleMovementInCoast = ("Double movement in coast" in uniques)
|
||||
doubleMovementInForestAndJungle = ("Double movement rate through Forest and Jungle" in uniques)
|
||||
doubleMovementInSnowTundraAndHills = ("Double movement in Snow, Tundra and Hills" in uniques)
|
||||
canEnterIceTiles = ("Can enter ice tiles" in uniques)
|
||||
cannotEnterOceanTiles = ("Cannot enter ocean tiles" in uniques)
|
||||
cannotEnterOceanTilesUntilAstronomy = ("Cannot enter ocean tiles until Astronomy" in uniques)
|
||||
}
|
||||
|
||||
fun hasUnique(unique:String): Boolean {
|
||||
|
@ -325,8 +325,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
&& !(tile.isCityCenter() && tile.isCoastalTile()))
|
||||
return false
|
||||
|
||||
if (tile.terrainFeature == Constants.ice
|
||||
&& !unit.baseUnit.uniques.contains("Can enter ice tiles"))
|
||||
if (tile.terrainFeature == Constants.ice && !unit.canEnterIceTiles)
|
||||
return false
|
||||
|
||||
if (tile.isWater && unit.type.isLandUnit()) {
|
||||
@ -335,8 +334,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
return false
|
||||
}
|
||||
if (tile.isOcean && unit.civInfo.nation.unique != UniqueAbility.WAYFINDING) {
|
||||
if (unit.baseUnit.uniques.contains("Cannot enter ocean tiles")) return false
|
||||
if (unit.baseUnit.uniques.contains("Cannot enter ocean tiles until Astronomy")
|
||||
if (unit.cannotEnterOceanTiles) return false
|
||||
if (unit.cannotEnterOceanTilesUntilAstronomy
|
||||
&& !unit.civInfo.tech.isResearched("Astronomy"))
|
||||
return false
|
||||
}
|
||||
@ -350,12 +349,9 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
// AIs won't enter city-state's border.
|
||||
}
|
||||
|
||||
val unitsInTile = tile.getUnits()
|
||||
if (unitsInTile.isNotEmpty()) {
|
||||
val firstUnit = unitsInTile.first()
|
||||
if (firstUnit.civInfo != unit.civInfo && unit.civInfo.isAtWarWith(firstUnit.civInfo))
|
||||
val firstUnit = tile.getUnits().firstOrNull()
|
||||
if (firstUnit != null && firstUnit.civInfo != unit.civInfo && unit.civInfo.isAtWarWith(firstUnit.civInfo))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
238
tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt
Normal file
238
tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt
Normal file
@ -0,0 +1,238 @@
|
||||
// Taken from https://github.com/TomGrill/gdx-testing
|
||||
package com.unciv.logic.map
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyManager
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.models.ruleset.Nation
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.ruleset.unit.UnitType
|
||||
import com.unciv.testing.GdxTestRunner
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(GdxTestRunner::class)
|
||||
class UnitMovementAlgorithmsTests {
|
||||
|
||||
private var tile = TileInfo()
|
||||
private var civInfo = CivilizationInfo()
|
||||
private var ruleSet = Ruleset()
|
||||
private var unit = MapUnit()
|
||||
|
||||
@Before
|
||||
fun initTheWorld() {
|
||||
RulesetCache.loadRulesets()
|
||||
ruleSet = RulesetCache.getBaseRuleset()
|
||||
tile.ruleset = ruleSet
|
||||
civInfo.tech.techsResearched.addAll(ruleSet.technologies.keys)
|
||||
civInfo.tech.embarkedUnitsCanEnterOcean = true
|
||||
civInfo.tech.unitsCanEmbark = true
|
||||
civInfo.nation = Nation().apply {
|
||||
name = "My nation"
|
||||
cities = arrayListOf("The Capital")
|
||||
}
|
||||
unit.civInfo = civInfo
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canPassThroughPassableTerrains() {
|
||||
for (terrain in ruleSet.terrains.values) {
|
||||
tile.baseTerrain = terrain.name
|
||||
tile.setTransients()
|
||||
|
||||
unit.baseUnit = BaseUnit().apply { unitType = UnitType.Melee }
|
||||
|
||||
Assert.assertTrue(terrain.name, terrain.impassable != unit.movement.canPassThrough(tile))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unitCanEnterTheCity() {
|
||||
val map = TileMap()
|
||||
tile.baseTerrain = Constants.hill
|
||||
tile.tileMap = map
|
||||
tile.setTransients()
|
||||
|
||||
val otherTile = tile.clone()
|
||||
otherTile.baseTerrain = Constants.coast
|
||||
otherTile.position.y = 1f
|
||||
|
||||
map.tileMatrix.add(arrayListOf(tile, otherTile))
|
||||
|
||||
val city = CityInfo()
|
||||
city.location = tile.position
|
||||
city.civInfo = civInfo
|
||||
tile.owningCity = city
|
||||
|
||||
for (type in UnitType.values())
|
||||
{
|
||||
unit.owner = civInfo.civName
|
||||
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||
Assert.assertTrue(type.name, unit.movement.canPassThrough(tile))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun waterUnitCanNOTEnterLand() {
|
||||
for (terrain in ruleSet.terrains.values) {
|
||||
if (terrain.impassable) continue
|
||||
tile.baseTerrain = terrain.name
|
||||
tile.setTransients()
|
||||
|
||||
for (type in UnitType.values()) {
|
||||
if (type == UnitType.City) continue
|
||||
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||
Assert.assertTrue("%s cannot be at %s".format(type, terrain.name),
|
||||
(type.isWaterUnit() && tile.isLand) != unit.movement.canPassThrough(tile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canNOTEnterIce() {
|
||||
tile.baseTerrain = Constants.ocean
|
||||
tile.terrainFeature = Constants.ice
|
||||
tile.setTransients()
|
||||
|
||||
for (type in UnitType.values()) {
|
||||
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||
|
||||
if (type == UnitType.WaterSubmarine) {
|
||||
unit.baseUnit.uniques.add("Can enter ice tiles")
|
||||
}
|
||||
unit.updateUniques()
|
||||
|
||||
Assert.assertTrue("$type cannot be in Ice",
|
||||
(type == UnitType.WaterSubmarine) == unit.movement.canPassThrough(tile))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canNOTEnterNaturalWonder() {
|
||||
tile.baseTerrain = Constants.plains
|
||||
tile.naturalWonder = "Wonder Thunder"
|
||||
tile.setTransients()
|
||||
|
||||
for (type in UnitType.values()) {
|
||||
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||
|
||||
Assert.assertFalse("$type must not enter Wonder tile", unit.movement.canPassThrough(tile))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canNOTEnterCoastUntilProperTechIsResearched() {
|
||||
|
||||
civInfo.tech.unitsCanEmbark = false
|
||||
|
||||
tile.baseTerrain = Constants.coast
|
||||
tile.setTransients()
|
||||
|
||||
for (type in UnitType.values()) {
|
||||
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||
|
||||
Assert.assertTrue("$type cannot be in Coast",
|
||||
unit.type.isLandUnit() != unit.movement.canPassThrough(tile))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canNOTEnterOceanUntilProperTechIsResearched() {
|
||||
|
||||
civInfo.tech.embarkedUnitsCanEnterOcean = false
|
||||
|
||||
tile.baseTerrain = Constants.ocean
|
||||
tile.setTransients()
|
||||
|
||||
for (type in UnitType.values()) {
|
||||
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||
|
||||
Assert.assertTrue("$type cannot be in Ocean",
|
||||
unit.type.isLandUnit() != unit.movement.canPassThrough(tile))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canNOTEnterOceanWithLimitations() {
|
||||
|
||||
tile.baseTerrain = Constants.ocean
|
||||
tile.setTransients()
|
||||
|
||||
for (type in UnitType.values()) {
|
||||
unit.baseUnit = BaseUnit().apply {
|
||||
unitType = type
|
||||
if (type == UnitType.Melee)
|
||||
uniques.add("Cannot enter ocean tiles")
|
||||
if (type == UnitType.Ranged)
|
||||
uniques.add("Cannot enter ocean tiles until Astronomy")
|
||||
}
|
||||
unit.updateUniques()
|
||||
|
||||
Assert.assertTrue("$type cannot be in Ocean",
|
||||
(type == UnitType.Melee) != unit.movement.canPassThrough(tile))
|
||||
|
||||
civInfo.tech.techsResearched.remove("Astronomy")
|
||||
|
||||
Assert.assertTrue("$type cannot be in Ocean until Astronomy",
|
||||
(type == UnitType.Melee ||
|
||||
type == UnitType.Ranged) != unit.movement.canPassThrough(tile))
|
||||
|
||||
civInfo.tech.techsResearched.add("Astronomy")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canNOTPassThroughTileWithEnemyUnits() {
|
||||
tile.baseTerrain = Constants.grassland
|
||||
tile.setTransients()
|
||||
|
||||
val otherCiv = CivilizationInfo()
|
||||
otherCiv.civName = "Barbarians" // they are always enemies
|
||||
otherCiv.nation = Nation().apply { name = "Barbarians" }
|
||||
val otherUnit = MapUnit()
|
||||
otherUnit.civInfo = otherCiv
|
||||
tile.militaryUnit = otherUnit
|
||||
|
||||
for (type in UnitType.values()) {
|
||||
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||
|
||||
Assert.assertFalse("$type must not enter occupied tile", unit.movement.canPassThrough(tile))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun canNOTPassForeignTiles() {
|
||||
tile.baseTerrain = Constants.desert
|
||||
tile.setTransients()
|
||||
|
||||
val otherCiv = CivilizationInfo()
|
||||
otherCiv.civName = "Other civ"
|
||||
otherCiv.nation = Nation().apply { name = "Other nation" }
|
||||
|
||||
val city = CityInfo()
|
||||
city.location = tile.position.cpy().add(1f,1f)
|
||||
city.civInfo = otherCiv
|
||||
tile.owningCity = city
|
||||
|
||||
unit.baseUnit = BaseUnit().apply { unitType = UnitType.Melee }
|
||||
unit.owner = civInfo.civName
|
||||
|
||||
Assert.assertFalse("Unit must not enter other civ tile", unit.movement.canPassThrough(tile))
|
||||
|
||||
city.location = tile.position
|
||||
|
||||
Assert.assertFalse("Unit must not enter other civ city", unit.movement.canPassThrough(tile))
|
||||
|
||||
city.hasJustBeenConquered = true
|
||||
civInfo.diplomacy["Other civ"] = DiplomacyManager(otherCiv, "Other civ")
|
||||
civInfo.getDiplomacyManager(otherCiv).diplomaticStatus = DiplomaticStatus.War
|
||||
|
||||
Assert.assertTrue("Unit can capture other civ city", unit.movement.canPassThrough(tile))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user