Movement algorithm updated - can no longer see whether you can move to unknown tiles

This commit is contained in:
Yair Morgenstern
2021-02-13 21:50:47 +02:00
parent e92a1dd088
commit b7a6967fe6
3 changed files with 4 additions and 123 deletions

View File

@ -10,7 +10,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
// This function is called ALL THE TIME and should be as time-optimal as possible!
fun getMovementCostBetweenAdjacentTiles(from: TileInfo, to: TileInfo, civInfo: CivilizationInfo): Float {
if ((from.isLand != to.isLand) && !unit.civInfo.nation.embarkDisembarkCosts1 && unit.type.isLandUnit())
if (from.isLand != to.isLand && !unit.civInfo.nation.embarkDisembarkCosts1 && unit.type.isLandUnit())
return 100f // this is embarkment or disembarkment, and will take the entire turn
// land units will still spend all movement points to embark even with this unique
@ -58,64 +58,15 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
class ParentTileAndTotalDistance(val parentTile: TileInfo, val totalDistance: Float)
fun isUnknownTileWeShouldAssumeToBePassable(tileInfo: TileInfo) = UncivGame.Current.settings.unitMovementIncludesImpassibles
&& !unit.civInfo.exploredTiles.contains(tileInfo.position)
fun isUnknownTileWeShouldAssumeToBePassable(tileInfo: TileInfo) = !unit.civInfo.exploredTiles.contains(tileInfo.position)
fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): PathsToTilesWithinTurn {
if (UncivGame.Current.settings.unitMovementIncludesImpassibles)
return getDistanceToTilesWithinTurnIncludingUnknownImpassibles(origin, unitMovement)
val distanceToTiles = PathsToTilesWithinTurn()
if (unitMovement == 0f) return distanceToTiles
val currentUnitTile = unit.currentTile
// This is for performance, because this is called all the time
val unitTile = if(origin==currentUnitTile.position) currentUnitTile else currentUnitTile.tileMap[origin]
distanceToTiles[unitTile] = ParentTileAndTotalDistance(unitTile, 0f)
var tilesToCheck = listOf(unitTile)
while (tilesToCheck.isNotEmpty()) {
val updatedTiles = ArrayList<TileInfo>()
for (tileToCheck in tilesToCheck)
for (neighbor in tileToCheck.neighbors) {
var totalDistanceToTile: Float
if (!canPassThrough(neighbor))
totalDistanceToTile = unitMovement // Can't go here.
// The reason that we don't just "return" is so that when calculating how to reach an enemy,
// You need to assume his tile is reachable, otherwise all movement algs on reaching enemy
// cities and units goes kaput.
else {
val distanceBetweenTiles = getMovementCostBetweenAdjacentTiles(tileToCheck, neighbor, unit.civInfo)
totalDistanceToTile = distanceToTiles[tileToCheck]!!.totalDistance + distanceBetweenTiles
}
if (!distanceToTiles.containsKey(neighbor) || distanceToTiles[neighbor]!!.totalDistance > totalDistanceToTile) { // this is the new best path
if (totalDistanceToTile < unitMovement) // We can still keep moving from here!
updatedTiles += neighbor
else
totalDistanceToTile = unitMovement
// In Civ V, you can always travel between adjacent tiles, even if you don't technically
// have enough movement points - it simple depletes what you have
distanceToTiles[neighbor] = ParentTileAndTotalDistance(tileToCheck, totalDistanceToTile)
}
}
tilesToCheck = updatedTiles
}
return distanceToTiles
}
fun getDistanceToTilesWithinTurnIncludingUnknownImpassibles(origin: Vector2, unitMovement: Float): PathsToTilesWithinTurn {
val distanceToTiles = PathsToTilesWithinTurn()
if (unitMovement == 0f) return distanceToTiles
val currentUnitTile = unit.currentTile
// 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, 0f)
var tilesToCheck = listOf(unitTile)
@ -285,7 +236,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
unit.putInTile(allowedTile)
}
fun moveToTileIncludingUnknownImpassibles(destination: TileInfo) {
fun moveToTile(destination: TileInfo) {
if (destination == unit.getTile()) return // already here!
if (unit.type.isAirUnit()) { // they move differently from all other units
@ -343,73 +294,6 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
}
fun moveToTile(destination: TileInfo) {
if(UncivGame.Current.settings.unitMovementIncludesImpassibles)
return moveToTileIncludingUnknownImpassibles(destination)
if (destination == unit.getTile()) return // already here!
class CantEnterThisTileException(msg: String) : Exception(msg)
if (!canMoveTo(destination))
throw CantEnterThisTileException("$unit can't enter $destination")
if (unit.type.isAirUnit()) { // they move differently from all other units
unit.action = null
unit.removeFromTile()
unit.isTransported = false // it has left the carrier by own means
unit.putInTile(destination)
unit.currentMovement = 0f
return
}
val distanceToTiles = getDistanceToTiles()
class YouCantGetThereFromHereException(msg: String) : Exception(msg)
if (!distanceToTiles.containsKey(destination))
throw YouCantGetThereFromHereException("$unit can't get from ${unit.currentTile.position} to ${destination.position}.")
if (destination.isCityCenter() && destination.getOwner() != unit.civInfo && !destination.getCity()!!.hasJustBeenConquered)
throw Exception("This is an enemy city, you can't go here!")
if (!unit.civInfo.gameInfo.gameParameters.godMode) {
unit.currentMovement -= distanceToTiles[destination]!!.totalDistance
if (unit.currentMovement < 0.1) unit.currentMovement = 0f // silly floats which are "almost zero"
}
if (unit.isFortified() || unit.action == Constants.unitActionSetUp || unit.isSleeping())
unit.action = null // unfortify/setup after moving
// If this unit is a carrier, keep record of its air payload whereabouts.
val origin = unit.getTile()
unit.removeFromTile()
unit.putInTile(destination)
// The .toList() here is because we have a sequence that's running on the units in the tile,
// then if we move one of the units we'll get a ConcurrentModificationException, se we save them all to a list
for (payload in origin.getUnits().filter { it.isTransported && unit.canTransport(it) }.toList()) { // bring along the payloads
payload.removeFromTile()
payload.putInTile(destination)
payload.isTransported = true // restore the flag to not leave the payload in the cit
}
// Unit maintenance changed
if (unit.canGarrison()
&& (origin.isCityCenter() || destination.isCityCenter())
&& unit.civInfo.hasUnique("Units in cities cost no Maintenance")
) unit.civInfo.updateStatsForNextTurn()
// Move through all intermediate tiles to get ancient ruins, barb encampments
// and to view tiles along the way
// We only activate the moveThroughTile AFTER the putInTile because of a really weird bug -
// If you're going to (or past) a ruin, and you activate the ruin bonus, and A UNIT spawns.
// That unit could now be blocking your entrance to the destination, so the putInTile would fail! =0
// Instead, we move you to the destination directly, and only afterwards activate the various tiles on the way.
val pathToFinalTile = distanceToTiles.getPathToTile(destination)
for (tile in pathToFinalTile) {
unit.moveThroughTile(tile)
}
}
/**
* Designates whether we can enter the tile - without attacking

View File

@ -12,7 +12,6 @@ class GameSettings {
var showTileYields: Boolean = false
var checkForDueUnits: Boolean = true
var singleTapMove: Boolean = false
var unitMovementIncludesImpassibles: Boolean = false
var language: String = "English"
var resolution: String = "900x600" // Auto-detecting resolution was a BAD IDEA since it needs to be based on DPI AND resolution.
var tutorialsShown = HashSet<String>()

View File

@ -108,8 +108,6 @@ class OptionsPopup(val previousScreen:CameraStageBaseScreen) : Popup(previousScr
addYesNoRow("Check for idle units", settings.checkForDueUnits, true) { settings.checkForDueUnits = it }
addYesNoRow("Move units with a single tap", settings.singleTapMove) { settings.singleTapMove = it }
addYesNoRow("Movement assumes unknown tiles to be passable", settings.unitMovementIncludesImpassibles)
{ settings.unitMovementIncludesImpassibles = it }
addYesNoRow("Auto-assign city production", settings.autoAssignCityProduction, true) {
settings.autoAssignCityProduction = it
if (it && previousScreen is WorldScreen &&