chore: separated unit cached values into separate file

This commit is contained in:
Yair Morgenstern
2023-02-02 19:50:00 +02:00
parent 7d93b24d8c
commit e84887b8c2
13 changed files with 184 additions and 162 deletions

View File

@ -38,7 +38,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private val civUnits = civInfo.units.getCivUnits()
private val militaryUnits = civUnits.count { it.baseUnit.isMilitary() }
private val workers = civUnits.count { it.hasUniqueToBuildImprovements && it.isCivilian() }.toFloat()
private val workers = civUnits.count { it.cache.hasUniqueToBuildImprovements && it.isCivilian() }.toFloat()
private val cities = civInfo.cities.size
private val allTechsAreResearched = civInfo.gameInfo.ruleSet.technologies.values
.all { civInfo.tech.isResearched(it.name) || !civInfo.tech.canBeResearched(it.name)}

View File

@ -108,7 +108,7 @@ object SpecificUnitAutomation {
.sortedBy { it.aerialDistanceTo(unit.currentTile) }
.firstOrNull { reachableTest(it) }
?: return
if (!unit.hasCitadelPlacementUnique) {
if (!unit.cache.hasCitadelPlacementUnique) {
unit.movement.headTowards(cityToGarrison)
return
}

View File

@ -223,7 +223,7 @@ object UnitAutomation {
if (unit.hasUnique(UniqueType.FoundCity))
return SpecificUnitAutomation.automateSettlerActions(unit)
if (unit.hasUniqueToBuildImprovements)
if (unit.cache.hasUniqueToBuildImprovements)
return WorkerAutomation.automateWorkerAction(unit)
if (unit.hasUnique(UniqueType.MayFoundReligion)
@ -248,12 +248,12 @@ object UnitAutomation {
//todo this now supports "Great General"-like mod units not combining 'aura' and citadel
// abilities, but not additional capabilities if automation finds no use for those two
if (unit.hasStrengthBonusInRadiusUnique
if (unit.cache.hasStrengthBonusInRadiusUnique
&& SpecificUnitAutomation.automateGreatGeneral(unit))
return
if (unit.hasCitadelPlacementUnique && SpecificUnitAutomation.automateCitadelPlacer(unit))
if (unit.cache.hasCitadelPlacementUnique && SpecificUnitAutomation.automateCitadelPlacer(unit))
return
if (unit.hasCitadelPlacementUnique || unit.hasStrengthBonusInRadiusUnique)
if (unit.cache.hasCitadelPlacementUnique || unit.cache.hasStrengthBonusInRadiusUnique)
return SpecificUnitAutomation.automateGreatGeneralFallback(unit)
if (unit.civ.religionManager.maySpreadReligionAtAll(unit))

View File

@ -34,7 +34,7 @@ object GreatGeneralImplementation {
val unit = ourUnitCombatant.unit
val civInfo = ourUnitCombatant.unit.civ
val allGenerals = civInfo.units.getCivUnits()
.filter { it.hasStrengthBonusInRadiusUnique }
.filter { it.cache.hasStrengthBonusInRadiusUnique }
if (allGenerals.none()) return Pair("", 0)
val greatGeneral = allGenerals

View File

@ -15,7 +15,6 @@ import com.unciv.logic.map.tile.Tile
import com.unciv.models.UnitActionType
import com.unciv.models.helpers.UnitMovementMemoryType
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.tile.TerrainType
import com.unciv.models.ruleset.tile.TileImprovement
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.Unique
@ -52,83 +51,15 @@ class MapUnit : IsPartOfGameInfoSerialization {
@Transient
var isDestroyed = false
@Transient
var cache = MapUnitCache(this)
// This is saved per each unit because if we need to recalculate viewable tiles every time a unit moves,
// and we need to go over ALL the units, that's a lot of time spent on updating information we should already know!
// About 10% of total NextTurn performance time, at the time of this change!
@Transient
var viewableTiles = HashSet<Tile>()
// These are for performance improvements to getMovementCostBetweenAdjacentTiles,
// a major component of getDistanceToTilesWithinTurn,
// which in turn is a component of getShortestPath and canReach
@Transient
var ignoresTerrainCost = false
private set
@Transient
var ignoresZoneOfControl = false
private set
@Transient
var allTilesCosts1 = false
private set
@Transient
var canPassThroughImpassableTiles = false
private set
@Transient
var roughTerrainPenalty = false
private set
/** If set causes an early exit in getMovementCostBetweenAdjacentTiles
* - means no double movement uniques, roughTerrainPenalty or ignoreHillMovementCost */
@Transient
var noTerrainMovementUniques = false
private set
/** If set causes a second early exit in getMovementCostBetweenAdjacentTiles */
@Transient
var noBaseTerrainOrHillDoubleMovementUniques = false
private set
/** If set skips tile.matchesFilter tests for double movement in getMovementCostBetweenAdjacentTiles */
@Transient
var noFilteredDoubleMovementUniques = false
private set
/** Used for getMovementCostBetweenAdjacentTiles only, based on order of testing */
enum class DoubleMovementTerrainTarget { Feature, Base, Hill, Filter }
/** Mod-friendly cache of double-movement terrains */
@Transient
val doubleMovementInTerrain = HashMap<String, DoubleMovementTerrainTarget>()
@Transient
var canEnterIceTiles = false
@Transient
var cannotEnterOceanTiles = false
@Transient
var canEnterForeignTerrain: Boolean = false
@Transient
var costToDisembark: Float? = null
@Transient
var costToEmbark: Float? = null
@Transient
var paradropRange = 0
@Transient
var hasUniqueToBuildImprovements = false // not canBuildImprovements to avoid confusion
@Transient
var hasStrengthBonusInRadiusUnique = false
@Transient
var hasCitadelPlacementUnique = false
/** civName owning the unit */
lateinit var owner: String
@ -285,7 +216,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
return getMatchingUniques(uniqueType, stateForConditionals, checkCivInfoUniques).any()
}
fun updateUniques(ruleset: Ruleset) {
fun updateUniques() {
val uniques = ArrayList<Unique>()
val baseUnit = baseUnit()
uniques.addAll(baseUnit.uniqueObjects)
@ -299,48 +230,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
addUniques(uniques)
}
allTilesCosts1 = hasUnique(UniqueType.AllTilesCost1Move)
canPassThroughImpassableTiles = hasUnique(UniqueType.CanPassImpassable)
ignoresTerrainCost = hasUnique(UniqueType.IgnoresTerrainCost)
ignoresZoneOfControl = hasUnique(UniqueType.IgnoresZOC)
roughTerrainPenalty = hasUnique(UniqueType.RoughTerrainPenalty)
doubleMovementInTerrain.clear()
for (unique in getMatchingUniques(UniqueType.DoubleMovementOnTerrain)) {
val param = unique.params[0]
val terrain = ruleset.terrains[param]
doubleMovementInTerrain[param] = when {
terrain == null -> DoubleMovementTerrainTarget.Filter
terrain.name == Constants.hill -> DoubleMovementTerrainTarget.Hill
terrain.type == TerrainType.TerrainFeature -> DoubleMovementTerrainTarget.Feature
terrain.type.isBaseTerrain -> DoubleMovementTerrainTarget.Base
else -> DoubleMovementTerrainTarget.Filter
}
}
// Init shortcut flags
noTerrainMovementUniques = doubleMovementInTerrain.isEmpty() &&
!roughTerrainPenalty && !civ.nation.ignoreHillMovementCost
noBaseTerrainOrHillDoubleMovementUniques = doubleMovementInTerrain
.none { it.value != DoubleMovementTerrainTarget.Feature }
noFilteredDoubleMovementUniques = doubleMovementInTerrain
.none { it.value == DoubleMovementTerrainTarget.Filter }
costToDisembark = (getMatchingUniques(UniqueType.ReducedDisembarkCost, checkCivInfoUniques = true))
.minOfOrNull { it.params[0].toFloat() }
costToEmbark = getMatchingUniques(UniqueType.ReducedEmbarkCost, checkCivInfoUniques = true)
.minOfOrNull { it.params[0].toFloat() }
//todo: consider parameterizing [terrainFilter] in some of the following:
canEnterIceTiles = hasUnique(UniqueType.CanEnterIceTiles)
cannotEnterOceanTiles = hasUnique(UniqueType.CannotEnterOcean, StateForConditionals(civInfo=civ, unit=this))
hasUniqueToBuildImprovements = hasUnique(UniqueType.BuildImprovements)
canEnterForeignTerrain = hasUnique(UniqueType.CanEnterForeignTiles)
|| hasUnique(UniqueType.CanEnterForeignTilesButLosesReligiousStrength)
hasStrengthBonusInRadiusUnique = hasUnique(UniqueType.StrengthBonusInRadius)
hasCitadelPlacementUnique = getMatchingUniques(UniqueType.ConstructImprovementConsumingUnit)
.mapNotNull { civ.gameInfo.ruleSet.tileImprovements[it.params[0]] }
.any { it.hasUnique(UniqueType.TakesOverAdjacentTiles) }
cache.updateUniques()
}
fun copyStatisticsTo(newUnit: MapUnit) {
@ -355,7 +245,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
newUnit.promotions = promotions.clone()
newUnit.updateUniques(civ.gameInfo.ruleSet)
newUnit.updateUniques()
newUnit.updateVisibleTiles()
}
@ -368,7 +258,6 @@ class MapUnit : IsPartOfGameInfoSerialization {
movement += getMatchingUniques(UniqueType.Movement, checkCivInfoUniques = true)
.sumOf { it.params[0].toInt() }
if (movement < 1) movement = 1
return movement
@ -533,7 +422,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
baseUnit = ruleset.units[name]
?: throw java.lang.Exception("Unit $name is not found!")
updateUniques(ruleset)
updateUniques()
}
fun useMovementPoints(amount: Float) {

View File

@ -0,0 +1,128 @@
package com.unciv.logic.map.mapunit
import com.unciv.Constants
import com.unciv.models.ruleset.tile.TerrainType
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueType
class MapUnitCache(val mapUnit: MapUnit) {
// These are for performance improvements to getMovementCostBetweenAdjacentTiles,
// a major component of getDistanceToTilesWithinTurn,
// which in turn is a component of getShortestPath and canReach
@Transient
var ignoresTerrainCost = false
private set
@Transient
var ignoresZoneOfControl = false
private set
@Transient
var allTilesCosts1 = false
private set
@Transient
var canPassThroughImpassableTiles = false
private set
@Transient
var roughTerrainPenalty = false
private set
/** If set causes an early exit in getMovementCostBetweenAdjacentTiles
* - means no double movement uniques, roughTerrainPenalty or ignoreHillMovementCost */
@Transient
var noTerrainMovementUniques = false
private set
/** If set causes a second early exit in getMovementCostBetweenAdjacentTiles */
@Transient
var noBaseTerrainOrHillDoubleMovementUniques = false
private set
/** If set skips tile.matchesFilter tests for double movement in getMovementCostBetweenAdjacentTiles */
@Transient
var noFilteredDoubleMovementUniques = false
private set
/** Used for getMovementCostBetweenAdjacentTiles only, based on order of testing */
enum class DoubleMovementTerrainTarget { Feature, Base, Hill, Filter }
/** Mod-friendly cache of double-movement terrains */
@Transient
val doubleMovementInTerrain = HashMap<String, DoubleMovementTerrainTarget>()
@Transient
var canEnterIceTiles = false
@Transient
var cannotEnterOceanTiles = false
@Transient
var canEnterForeignTerrain: Boolean = false
@Transient
var costToDisembark: Float? = null
@Transient
var costToEmbark: Float? = null
@Transient
var paradropRange = 0
@Transient
var hasUniqueToBuildImprovements = false // not canBuildImprovements to avoid confusion
@Transient
var hasStrengthBonusInRadiusUnique = false
@Transient
var hasCitadelPlacementUnique = false
fun updateUniques(){
allTilesCosts1 = mapUnit.hasUnique(UniqueType.AllTilesCost1Move)
canPassThroughImpassableTiles = mapUnit.hasUnique(UniqueType.CanPassImpassable)
ignoresTerrainCost = mapUnit.hasUnique(UniqueType.IgnoresTerrainCost)
ignoresZoneOfControl = mapUnit.hasUnique(UniqueType.IgnoresZOC)
roughTerrainPenalty = mapUnit.hasUnique(UniqueType.RoughTerrainPenalty)
doubleMovementInTerrain.clear()
for (unique in mapUnit.getMatchingUniques(UniqueType.DoubleMovementOnTerrain)) {
val param = unique.params[0]
val terrain = mapUnit.currentTile.ruleset.terrains[param]
doubleMovementInTerrain[param] = when {
terrain == null -> DoubleMovementTerrainTarget.Filter
terrain.name == Constants.hill -> DoubleMovementTerrainTarget.Hill
terrain.type == TerrainType.TerrainFeature -> DoubleMovementTerrainTarget.Feature
terrain.type.isBaseTerrain -> DoubleMovementTerrainTarget.Base
else -> DoubleMovementTerrainTarget.Filter
}
}
// Init shortcut flags
noTerrainMovementUniques = doubleMovementInTerrain.isEmpty() &&
!roughTerrainPenalty && !mapUnit.civ.nation.ignoreHillMovementCost
noBaseTerrainOrHillDoubleMovementUniques = doubleMovementInTerrain
.none { it.value != DoubleMovementTerrainTarget.Feature }
noFilteredDoubleMovementUniques = doubleMovementInTerrain
.none { it.value == DoubleMovementTerrainTarget.Filter }
costToDisembark = (mapUnit.getMatchingUniques(UniqueType.ReducedDisembarkCost, checkCivInfoUniques = true))
.minOfOrNull { it.params[0].toFloat() }
costToEmbark = mapUnit.getMatchingUniques(UniqueType.ReducedEmbarkCost, checkCivInfoUniques = true)
.minOfOrNull { it.params[0].toFloat() }
//todo: consider parameterizing [terrainFilter] in some of the following:
canEnterIceTiles = mapUnit.hasUnique(UniqueType.CanEnterIceTiles)
cannotEnterOceanTiles = mapUnit.hasUnique(
UniqueType.CannotEnterOcean,
StateForConditionals(civInfo = mapUnit.civ, unit = mapUnit)
)
hasUniqueToBuildImprovements = mapUnit.hasUnique(UniqueType.BuildImprovements)
canEnterForeignTerrain = mapUnit.hasUnique(UniqueType.CanEnterForeignTiles)
|| mapUnit.hasUnique(UniqueType.CanEnterForeignTilesButLosesReligiousStrength)
hasStrengthBonusInRadiusUnique = mapUnit.hasUnique(UniqueType.StrengthBonusInRadius)
hasCitadelPlacementUnique = mapUnit.getMatchingUniques(UniqueType.ConstructImprovementConsumingUnit)
.mapNotNull { mapUnit.civ.gameInfo.ruleSet.tileImprovements[it.params[0]] }
.any { it.hasUnique(UniqueType.TakesOverAdjacentTiles) }
}
}

View File

@ -35,15 +35,15 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
): Float {
if (from.isLand != to.isLand && unit.baseUnit.isLandUnit())
return if (from.isWater && to.isLand) unit.costToDisembark ?: 100f
else unit.costToEmbark ?: 100f
return if (from.isWater && to.isLand) unit.cache.costToDisembark ?: 100f
else unit.cache.costToEmbark ?: 100f
// If the movement is affected by a Zone of Control, all movement points are expended
if (considerZoneOfControl && isMovementAffectedByZoneOfControl(from, to, civInfo))
return 100f
// land units will still spend all movement points to embark even with this unique
if (unit.allTilesCosts1)
if (unit.cache.allTilesCosts1)
return 1f
val toOwner = to.getOwner()
@ -65,36 +65,36 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
if (areConnectedByRoad && (!areConnectedByRiver || civInfo.tech.roadsConnectAcrossRivers))
return unit.civ.tech.movementSpeedOnRoads + extraCost
if (unit.ignoresTerrainCost) return 1f + extraCost
if (unit.cache.ignoresTerrainCost) return 1f + extraCost
if (areConnectedByRiver) return 100f // Rivers take the entire turn to cross
val terrainCost = to.getLastTerrain().movementCost.toFloat()
if (unit.noTerrainMovementUniques)
if (unit.cache.noTerrainMovementUniques)
return terrainCost + extraCost
if (to.terrainFeatures.any { unit.doubleMovementInTerrain[it] == MapUnit.DoubleMovementTerrainTarget.Feature })
if (to.terrainFeatures.any { unit.cache.doubleMovementInTerrain[it] == MapUnitCache.DoubleMovementTerrainTarget.Feature })
return terrainCost * 0.5f + extraCost
if (unit.roughTerrainPenalty && to.isRoughTerrain())
if (unit.cache.roughTerrainPenalty && to.isRoughTerrain())
return 100f // units that have to spend all movement in rough terrain, have to spend all movement in rough terrain
// Placement of this 'if' based on testing, see #4232
if (civInfo.nation.ignoreHillMovementCost && to.isHill())
return 1f + extraCost // usually hills take 2 movements, so here it is 1
if (unit.noBaseTerrainOrHillDoubleMovementUniques)
if (unit.cache.noBaseTerrainOrHillDoubleMovementUniques)
return terrainCost + extraCost
if (unit.doubleMovementInTerrain[to.baseTerrain] == MapUnit.DoubleMovementTerrainTarget.Base)
if (unit.cache.doubleMovementInTerrain[to.baseTerrain] == MapUnitCache.DoubleMovementTerrainTarget.Base)
return terrainCost * 0.5f + extraCost
if (unit.doubleMovementInTerrain[Constants.hill] == MapUnit.DoubleMovementTerrainTarget.Hill && to.isHill())
if (unit.cache.doubleMovementInTerrain[Constants.hill] == MapUnitCache.DoubleMovementTerrainTarget.Hill && to.isHill())
return terrainCost * 0.5f + extraCost
if (unit.noFilteredDoubleMovementUniques)
if (unit.cache.noFilteredDoubleMovementUniques)
return terrainCost + extraCost
if (unit.doubleMovementInTerrain.any {
it.value == MapUnit.DoubleMovementTerrainTarget.Filter &&
if (unit.cache.doubleMovementInTerrain.any {
it.value == MapUnitCache.DoubleMovementTerrainTarget.Filter &&
to.matchesFilter(it.key)
})
return terrainCost * 0.5f + extraCost
@ -140,7 +140,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
// ignore zone of control, so the previous check has a much higher chance of yielding an
// early "false". If this function is going to return "true", the order doesn't matter
// anyway.
if (unit.ignoresZoneOfControl)
if (unit.cache.ignoresZoneOfControl)
return false
return true
}
@ -355,7 +355,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
if (unit.baseUnit.movesLikeAirUnits())
return unit.currentTile.aerialDistanceTo(destination) <= unit.getMaxMovementForAirUnits()
if (unit.isPreparingParadrop())
return getDistance(unit.currentTile.position, destination.position) <= unit.paradropRange && canParadropOn(destination)
return getDistance(unit.currentTile.position, destination.position) <= unit.cache.paradropRange && canParadropOn(destination)
return getDistanceToTiles().containsKey(destination)
}
@ -365,7 +365,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
unit.baseUnit.movesLikeAirUnits() ->
unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getMaxMovementForAirUnits()))
unit.isPreparingParadrop() ->
unit.getTile().getTilesInDistance(unit.paradropRange)
unit.getTile().getTilesInDistance(unit.cache.paradropRange)
.filter { unit.movement.canParadropOn(it) }
else ->
unit.movement.getDistanceToTiles().keys.asSequence()
@ -589,7 +589,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
moveToTile(destination, considerZoneOfControl)
}
unit.updateUniques(unit.currentTile.ruleset)
unit.updateUniques()
}
/**
@ -698,7 +698,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
if (tile.isImpassible()) {
// special exception - ice tiles are technically impassible, but some units can move through them anyway
// helicopters can pass through impassable tiles like mountains
if (!unit.canPassThroughImpassableTiles && !(unit.canEnterIceTiles && tile.terrainFeatures.contains(Constants.ice))
if (!unit.cache.canPassThroughImpassableTiles && !(unit.cache.canEnterIceTiles && tile.terrainFeatures.contains(Constants.ice))
// carthage-like uniques sometimes allow passage through impassible tiles
&& !(unit.civ.passThroughImpassableUnlocked && unit.civ.passableImpassables.contains(tile.getLastTerrain().name)))
return false
@ -719,10 +719,10 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
return false
}
if (tile.isOcean && !unit.civ.tech.allUnitsCanEnterOcean) { // Apparently all Polynesian naval units can enter oceans
if (!unitSpecificAllowOcean && unit.cannotEnterOceanTiles) return false
if (!unitSpecificAllowOcean && unit.cache.cannotEnterOceanTiles) return false
}
if (!unit.canEnterForeignTerrain && !tile.canCivPassThrough(unit.civ)) return false
if (!unit.cache.canEnterForeignTerrain && !tile.canCivPassThrough(unit.civ)) return false
// The first unit is:
// 1. Either military unit
@ -802,11 +802,11 @@ class UnitMovementAlgorithms(val unit: MapUnit) {
* however ignores the diplomatic aspects of such movement like crossing closed borders.
*/
private fun getPathBetweenTiles(from: Tile, to: Tile): MutableSet<Tile> {
val tmp = unit.canEnterForeignTerrain
unit.canEnterForeignTerrain = true // the trick to ignore tiles owners
val tmp = unit.cache.canEnterForeignTerrain
unit.cache.canEnterForeignTerrain = true // the trick to ignore tiles owners
val bfs = BFS(from) { canPassThrough(it) }
bfs.stepUntilDestination(to)
unit.canEnterForeignTerrain = tmp
unit.cache.canEnterForeignTerrain = tmp
return bfs.getReachedTiles()
}

View File

@ -75,7 +75,7 @@ class UnitPromotions : IsPartOfGameInfoSerialization {
// so this has to go after the `promotions.add(promotionname)` line.
doDirectPromotionEffects(promotion)
unit.updateUniques(ruleset)
unit.updateUniques()
// Since some units get promotions upon construction, they will get the addPromotion from the unit.postBuildEvent
// upon creation, BEFORE they are assigned to a tile, so the updateVisibleTiles() would crash.

View File

@ -160,7 +160,7 @@ class UnitTurnManager(val unit: MapUnit) {
val tileOwner = unit.getTile().getOwner()
if (tileOwner != null
&& !unit.canEnterForeignTerrain
&& !unit.cache.canEnterForeignTerrain
&& !unit.civ.diplomacyFunctions.canPassThroughTiles(tileOwner)
&& !tileOwner.isCityState()) // if an enemy city expanded onto this tile while I was in it
unit.movement.teleportToClosestMoveableTile()

View File

@ -99,7 +99,7 @@ class TradeLogic(val ourCivilization:Civilization, val otherCivilization: Civili
.forEach { it.movement.teleportToClosestMoveableTile() }
for (tile in city.getTiles()) {
for (unit in tile.getUnits().toList()) {
if (!unit.civ.diplomacyFunctions.canPassThroughTiles(to) && !unit.canEnterForeignTerrain)
if (!unit.civ.diplomacyFunctions.canPassThroughTiles(to) && !unit.cache.canEnterForeignTerrain)
unit.movement.teleportToClosestMoveableTile()
}
}

View File

@ -65,8 +65,8 @@ import com.unciv.ui.worldscreen.status.MultiplayerStatusButton
import com.unciv.ui.worldscreen.status.NextTurnAction
import com.unciv.ui.worldscreen.status.NextTurnButton
import com.unciv.ui.worldscreen.status.StatusButtons
import com.unciv.ui.worldscreen.unit.actions.UnitActionsTable
import com.unciv.ui.worldscreen.unit.UnitTable
import com.unciv.ui.worldscreen.unit.actions.UnitActionsTable
import com.unciv.utils.concurrency.Concurrency
import com.unciv.utils.concurrency.launchOnGLThread
import com.unciv.utils.concurrency.launchOnThreadPool
@ -543,7 +543,7 @@ class WorldScreen(
displayTutorial(TutorialTrigger.Workers) {
gameInfo.getCurrentPlayerCivilization().units.getCivUnits().any {
it.hasUniqueToBuildImprovements && it.isCivilian() && !it.isGreatPerson()
it.cache.hasUniqueToBuildImprovements && it.isCivilian() && !it.isGreatPerson()
}
}
}

View File

@ -58,7 +58,7 @@ object UnitActions {
addAirSweepAction(unit, actionList)
addSetupAction(unit, actionList)
addFoundCityAction(unit, actionList, tile)
addBuildingImprovementsAction(unit, actionList, tile, worldScreen, unitTable)
addBuildingImprovementsAction(unit, actionList, tile, worldScreen)
addRepairAction(unit, actionList)
addCreateWaterImprovements(unit, actionList)
UnitActionsGreatPerson.addGreatPersonActions(unit, actionList, tile)
@ -255,7 +255,7 @@ object UnitActions {
val paradropUniques =
unit.getMatchingUniques(UniqueType.MayParadrop)
if (!paradropUniques.any() || unit.isEmbarked()) return
unit.paradropRange = paradropUniques.maxOfOrNull { it.params[0] }!!.toInt()
unit.cache.paradropRange = paradropUniques.maxOfOrNull { it.params[0] }!!.toInt()
actionList += UnitAction(UnitActionType.Paradrop,
isCurrentAction = unit.isPreparingParadrop(),
action = {
@ -356,8 +356,13 @@ object UnitActions {
return transformList
}
private fun addBuildingImprovementsAction(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: Tile, worldScreen: WorldScreen, unitTable: UnitTable) {
if (!unit.hasUniqueToBuildImprovements) return
private fun addBuildingImprovementsAction(
unit: MapUnit,
actionList: ArrayList<UnitAction>,
tile: Tile,
worldScreen: WorldScreen
) {
if (!unit.cache.hasUniqueToBuildImprovements) return
if (unit.isEmbarked()) return
val couldConstruct = unit.currentMovement > 0
@ -390,7 +395,7 @@ object UnitActions {
private fun addRepairAction(unit: MapUnit, actionList: ArrayList<UnitAction>) {
if (unit.currentTile.ruleset.tileImprovements[Constants.repair] == null) return
if (!unit.hasUniqueToBuildImprovements) return
if (!unit.cache.hasUniqueToBuildImprovements) return
if (unit.isEmbarked()) return
val tile = unit.getTile()
if (tile.isCityCenter()) return
@ -416,7 +421,7 @@ object UnitActions {
}
private fun addAutomateBuildingImprovementsAction(unit: MapUnit, actionList: ArrayList<UnitAction>) {
if (!unit.hasUniqueToBuildImprovements) return
if (!unit.cache.hasUniqueToBuildImprovements) return
if (unit.isAutomated()) return
actionList += UnitAction(UnitActionType.Automate,