diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 7b481d21dd..f561776835 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -785,7 +785,6 @@ Automated units can upgrade = Automated units choose promotions = Order trade offers by amount = Ask for confirmation when pressing next turn = -EXPERIMENTAL movement - use at your own risk! = Notifications log max turns = ## Language tab diff --git a/core/src/com/unciv/logic/map/mapunit/movement/UnitMovement.kt b/core/src/com/unciv/logic/map/mapunit/movement/UnitMovement.kt index cb789f95ff..eec753d6c5 100644 --- a/core/src/com/unciv/logic/map/mapunit/movement/UnitMovement.kt +++ b/core/src/com/unciv/logic/map/mapunit/movement/UnitMovement.kt @@ -4,7 +4,6 @@ package com.unciv.logic.map.mapunit.movement import com.badlogic.gdx.math.Vector2 import com.unciv.Constants -import com.unciv.UncivGame import com.unciv.logic.map.BFS import com.unciv.logic.map.HexMath.getDistance import com.unciv.logic.map.mapunit.MapUnit @@ -12,14 +11,8 @@ import com.unciv.logic.map.tile.Tile import com.unciv.models.UnitActionType import com.unciv.models.ruleset.unique.UniqueType import com.unciv.ui.components.UnitMovementMemoryType -import java.util.TreeSet -fun List.toBackwardsCompatiblePath(): List { - val backwardsCompatiblePath = this.filter { it.totalCost.movementLeft == 0f || it == this.last() } - return backwardsCompatiblePath.map { it.tile } -} - class UnitMovement(val unit: MapUnit) { private val pathfindingCache = PathfindingCache(unit) @@ -92,136 +85,6 @@ class UnitMovement(val unit: MapUnit) { return distanceToTiles } - - data class MovementStep(val previousStep: MovementStep?, val tile: Tile, val movementCost:Float, val totalCost: MovementStepTotalCost) - - - data class MovementStepTotalCost(/** Turn 0 means the initial turn */ val turn: Int, val movementLeft: Float):Comparable { - override operator fun compareTo(other: MovementStepTotalCost): Int { - if (turn != other.turn) return turn.compareTo(other.turn) - return other.movementLeft.compareTo(movementLeft) // The higher the MovementLeft, the *lower* the turn cost - } - } - - /** Problem and solution documented at https://yairm210.medium.com/multi-turn-pathfinding-7136bd0bdaf0 */ - fun getShortestPathNew(destination: Tile, considerZoneOfControl: Boolean = true, - /** For allowing optional avoid of damaging tiles, tiles outside borders, etc */ shouldAvoidTile: ((Tile) -> Boolean)? = null, - maxTurns: Int = 25, - ): List { - if (unit.cache.cannotMove) return listOf() - - val startingTile = unit.getTile() - val initialStep = MovementStep(null, startingTile, 0f, MovementStepTotalCost(0, unit.currentMovement)) - - if (startingTile.position == destination) { - // edge case that's needed, so that workers will know that they can reach their own tile. *sigh* - pathfindingCache.setShortestPathCache(destination, listOf(startingTile)) - return listOf(initialStep) - } - - - val tileToBestStep = HashMap() // contains a map of "you can get from X to Y in that turn" - tileToBestStep[startingTile] = initialStep - - val tilesToCheck = TreeSet { t: Tile, t2: Tile -> - val tStep = tileToBestStep[t]!! - val t2Step = tileToBestStep[t2]!! - // This last comparitor is REQUIRED otherwise the tree will think that tiles the same distance away are the same and will throw the second one away! - val totalCostComparison = tStep.totalCost.compareTo(t2Step.totalCost) - if (totalCostComparison != 0) return@TreeSet totalCostComparison - val aerialDistanceComparison = t.aerialDistanceTo(destination).compareTo(t2.aerialDistanceTo(destination)) - if (aerialDistanceComparison != 0) return@TreeSet aerialDistanceComparison - return@TreeSet t.position.hashCode().compareTo(t2.position.hashCode()) - } - - tilesToCheck.add(startingTile) - val canEndTurnInCache = HashMap() - val unitMaxMovement = unit.getMaxMovement().toFloat() - val movementCostCache = HashMap, Float>() - val shouldAvoidTileCache = HashMap() - val canPassThroughCache = HashMap() - - while (tilesToCheck.isNotEmpty()){ - val currentTileToCheck = tilesToCheck.pollFirst()!! - - val currentTileStep = tileToBestStep[currentTileToCheck]!! - - for (neighbor in currentTileToCheck.neighbors){ - if (shouldAvoidTile != null && shouldAvoidTileCache.getOrPut(neighbor){ - shouldAvoidTile(neighbor) - }) continue - val currentBestStepToNeighbor = tileToBestStep[neighbor] - // If this tile can't beat the current best then no point checking - if (currentBestStepToNeighbor!=null && (currentBestStepToNeighbor.totalCost < currentTileStep.totalCost)) - continue - - if (!canPassThroughCache.getOrPut(neighbor){ - canPassThrough(neighbor) - }) continue - - val movementBetweenTiles: Float = if (!neighbor.isExplored(unit.civ)) 1f // If we don't know then we just guess it to be 1. - else movementCostCache.getOrPut(currentTileToCheck to neighbor) { - MovementCost.getMovementCostBetweenAdjacentTiles(unit, currentTileToCheck, neighbor, considerZoneOfControl) - } - - val newStep = getNextStep(currentTileStep, neighbor, movementBetweenTiles, canEndTurnInCache, unitMaxMovement) - ?: continue - - - if (neighbor == destination){ - val entirePath = arrayListOf() - var currentStep = newStep - // We do NOT include the origin tile in this list - while (currentStep.previousStep != null){ - entirePath.add(currentStep) - currentStep = currentStep.previousStep!! - } - return entirePath.reversed() - } - - if (currentBestStepToNeighbor == null || - newStep.totalCost < currentBestStepToNeighbor.totalCost) { // We have a winner! - tileToBestStep[neighbor] = newStep - if (newStep.totalCost.movementLeft == 0f && newStep.totalCost.turn == maxTurns) continue // don't schedule further expansion - tilesToCheck.add(neighbor) - } - } - } - - return listOf() // no path - } - - fun getNextStep(currentTileStep:MovementStep, neighbor:Tile, movementBetweenTiles:Float, - canEndTurnInCache:HashMap, unitMaxMovement:Float):MovementStep? { - - fun canEndTurnIn(tile:Tile) = canEndTurnInCache.getOrPut(tile) { unit.movement.canMoveTo(tile) } - - fun Float.normalizeMovementLeft() = if (this > Constants.minimumMovementEpsilon) this else 0f - - val currentTile = currentTileStep.tile - - // We can move directly - if (currentTileStep.totalCost.movementLeft != 0f || canEndTurnIn(currentTile)) { - val newTotalCost = if (currentTileStep.totalCost.movementLeft == 0f) MovementStepTotalCost( - currentTileStep.totalCost.turn + 1, - (unitMaxMovement - movementBetweenTiles).normalizeMovementLeft() - ) - else MovementStepTotalCost(currentTileStep.totalCost.turn, (currentTileStep.totalCost.movementLeft - movementBetweenTiles).normalizeMovementLeft()) - return MovementStep(currentTileStep, neighbor, movementBetweenTiles, newTotalCost) - } - - // Backtracking nonsense - we CANNOT end the turn on the previous tile, AND we have no movement left, that means we need to do some alternate history - val previousStep = currentTileStep.previousStep ?: return null - if (previousStep.totalCost.movementLeft == 0f) return null // We backtracked until a previous end-of-turn and couldn't find any intermediate tiles - if (!canEndTurnIn(previousStep.tile)) return null - // We found somewhere we could have rested - let's do some alternate history! - val currentTileAlternateHistory = MovementStep(currentTileStep.previousStep, currentTile, currentTileStep.movementCost, - MovementStepTotalCost(currentTileStep.previousStep.totalCost.turn+1, (unitMaxMovement - currentTileStep.movementCost).normalizeMovementLeft())) - if (currentTileAlternateHistory.totalCost.movementLeft == 0f) return null // We tried, and even if we rest there we STILL have to stop at current tile... :/ - val newTotalCost = MovementStepTotalCost(currentTileAlternateHistory.totalCost.turn, (currentTileAlternateHistory.totalCost.movementLeft-currentTileStep.movementCost).normalizeMovementLeft()) - return MovementStep(currentTileAlternateHistory, neighbor, movementBetweenTiles, newTotalCost) - } - /** * Does not consider if the [destination] tile can actually be entered, use [canMoveTo] for that. * Returns an empty list if there's no way to get to the destination. @@ -252,19 +115,6 @@ class UnitMovement(val unit: MapUnit) { return listOf(currentTile) } - if (UncivGame.Current.settings.experimentalMovement) { - if (avoidDamagingTerrain){ - val shouldAvoidTile: (Tile) -> Boolean = if (unit.isCivilian() && unit.isAutomated()) - {{unit.getDamageFromTerrain(it) > 0 || it.isEnemyTerritory(unit.civ)}} - else {{unit.getDamageFromTerrain(it) > 0}} - return getShortestPathNew(destination, - shouldAvoidTile = shouldAvoidTile).toBackwardsCompatiblePath() - } - val shouldAvoidTile :((Tile) -> Boolean)? = if (unit.isCivilian() && unit.isAutomated()) - {{it.isEnemyTerritory(unit.civ)}} else null - return getShortestPathNew(destination, shouldAvoidTile = shouldAvoidTile).toBackwardsCompatiblePath() - } - var tilesToCheck = listOf(currentTile) val movementTreeParents = HashMap() // contains a map of "you can get from X to Y in that turn" movementTreeParents[currentTile] = null diff --git a/core/src/com/unciv/models/metadata/GameSettings.kt b/core/src/com/unciv/models/metadata/GameSettings.kt index 19af82b360..1255e2a739 100644 --- a/core/src/com/unciv/models/metadata/GameSettings.kt +++ b/core/src/com/unciv/models/metadata/GameSettings.kt @@ -32,7 +32,6 @@ class GameSettings { var showUnitMovements: Boolean = false var showSettlersSuggestedCityLocations: Boolean = true - var experimentalMovement: Boolean = false var checkForDueUnits: Boolean = true var autoUnitCycle: Boolean = true var singleTapMove: Boolean = false diff --git a/core/src/com/unciv/ui/popups/options/GameplayTab.kt b/core/src/com/unciv/ui/popups/options/GameplayTab.kt index ca961509fd..ab7cac47bb 100644 --- a/core/src/com/unciv/ui/popups/options/GameplayTab.kt +++ b/core/src/com/unciv/ui/popups/options/GameplayTab.kt @@ -53,7 +53,6 @@ fun gameplayTab( ) { settings.automatedUnitsChoosePromotions = it } optionsPopup.addCheckbox(this, "Order trade offers by amount", settings.orderTradeOffersByAmount) { settings.orderTradeOffersByAmount = it } optionsPopup.addCheckbox(this, "Ask for confirmation when pressing next turn", settings.confirmNextTurn) { settings.confirmNextTurn = it } - optionsPopup.addCheckbox(this, "EXPERIMENTAL movement - use at your own risk!", settings.experimentalMovement, true) { settings.experimentalMovement = it } addNotificationLogMaxTurnsSlider(this, settings, optionsPopup.selectBoxMinWidth) } diff --git a/docs/Modders/uniques.md b/docs/Modders/uniques.md index 846531028d..02539cd34b 100644 --- a/docs/Modders/uniques.md +++ b/docs/Modders/uniques.md @@ -936,13 +936,13 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Nation, Terrain, Improvement, Resource ??? example "Will not be displayed in Civilopedia" - Applicable to: Nation, Tech, Policy, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed + Applicable to: Nation, Tech, Policy, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed ??? example "Comment [comment]" Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent. Example: "Comment [comment]" - Applicable to: Nation, Tech, Policy, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed + Applicable to: Nation, Tech, Policy, FollowerBelief, Building, Unit, UnitType, Promotion, Terrain, Improvement, Resource, Ruins, Speed ## Era uniques ??? example "Starting in this era disables religion"