mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-21 21:30:20 +07:00
Chore: Moved tile improvement functions to separate class
This commit is contained in:
@ -246,7 +246,7 @@ object Automation {
|
||||
if (!construction.hasCreateOneImprovementUnique()) return true // redundant but faster???
|
||||
val improvement = construction.getImprovementToCreate(cityInfo.getRuleset()) ?: return true
|
||||
return cityInfo.getTiles().any {
|
||||
it.canBuildImprovement(improvement, civInfo)
|
||||
it.improvementFunctions.canBuildImprovement(improvement, civInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@ -338,7 +338,7 @@ object Automation {
|
||||
/** Support [UniqueType.CreatesOneImprovement] unique - find best tile for placement automation */
|
||||
fun getTileForConstructionImprovement(cityInfo: CityInfo, improvement: TileImprovement): TileInfo? {
|
||||
return cityInfo.getTiles().filter {
|
||||
it.canBuildImprovement(improvement, cityInfo.civInfo)
|
||||
it.improvementFunctions.canBuildImprovement(improvement, cityInfo.civInfo)
|
||||
}.maxByOrNull {
|
||||
rankTileForCityWork(it, cityInfo, cityInfo.cityStats.currentCityStats)
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
.any { tile ->
|
||||
tile.hasViewableResource(civInfo) && tile.improvement == null && tile.getOwner() == civInfo
|
||||
&& tile.tileResource.getImprovements().any {
|
||||
tile.canBuildImprovement(tile.ruleset.tileImprovements[it]!!, civInfo)
|
||||
tile.improvementFunctions.canBuildImprovement(tile.ruleset.tileImprovements[it]!!, civInfo)
|
||||
}
|
||||
}
|
||||
) return
|
||||
|
@ -263,7 +263,7 @@ object SpecificUnitAutomation {
|
||||
val applicableTiles = city.getWorkableTiles().filter {
|
||||
it.isLand && it.resource == null && !it.isCityCenter()
|
||||
&& (unit.currentTile == it || unit.movement.canMoveTo(it))
|
||||
&& !it.containsGreatImprovement() && it.canBuildImprovement(improvement, unit.civInfo)
|
||||
&& !it.containsGreatImprovement() && it.improvementFunctions.canBuildImprovement(improvement, unit.civInfo)
|
||||
}
|
||||
if (applicableTiles.none()) continue
|
||||
|
||||
|
@ -331,11 +331,11 @@ class WorkerAutomation(
|
||||
if (tile.improvement == null || junkImprovement == true) {
|
||||
if (tile.improvementInProgress != null && unit.canBuildImprovement(tile.getTileImprovementInProgress()!!, tile)) return true
|
||||
val chosenImprovement = chooseImprovement(unit, tile)
|
||||
if (chosenImprovement != null && tile.canBuildImprovement(chosenImprovement, civInfo) && unit.canBuildImprovement(chosenImprovement, tile)) return true
|
||||
if (chosenImprovement != null && tile.improvementFunctions.canBuildImprovement(chosenImprovement, civInfo) && unit.canBuildImprovement(chosenImprovement, tile)) return true
|
||||
} else if (!tile.containsGreatImprovement() && tile.hasViewableResource(civInfo)
|
||||
&& tile.tileResource.isImprovedBy(tile.improvement!!)
|
||||
&& (chooseImprovement(unit, tile) // if the chosen improvement is not null and buildable
|
||||
.let { it != null && tile.canBuildImprovement(it, civInfo) && unit.canBuildImprovement(it, tile)}))
|
||||
.let { it != null && tile.improvementFunctions.canBuildImprovement(it, civInfo) && unit.canBuildImprovement(it, tile)}))
|
||||
return true
|
||||
return false // couldn't find anything to construct here
|
||||
}
|
||||
@ -365,7 +365,7 @@ class WorkerAutomation(
|
||||
|
||||
val potentialTileImprovements = ruleSet.tileImprovements.filter {
|
||||
unit.canBuildImprovement(it.value, tile)
|
||||
&& tile.canBuildImprovement(it.value, civInfo)
|
||||
&& tile.improvementFunctions.canBuildImprovement(it.value, civInfo)
|
||||
&& (it.value.uniqueTo == null || it.value.uniqueTo == unit.civInfo.civName)
|
||||
}
|
||||
if (potentialTileImprovements.isEmpty()) return null
|
||||
@ -405,7 +405,7 @@ class WorkerAutomation(
|
||||
// While AI sucks in strategical placement of forts, allow a human does it manually
|
||||
!civInfo.isHuman() && evaluateFortPlacement(tile, civInfo,false) -> Constants.fort
|
||||
// I think we can assume that the unique improvement is better
|
||||
uniqueImprovement != null && tile.canBuildImprovement(uniqueImprovement, civInfo)
|
||||
uniqueImprovement != null && tile.improvementFunctions.canBuildImprovement(uniqueImprovement, civInfo)
|
||||
-> uniqueImprovement.name
|
||||
|
||||
lastTerrain.let {
|
||||
|
@ -9,8 +9,8 @@ import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.map.HexMath
|
||||
import com.unciv.logic.map.MapResources
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.tile.Terrain
|
||||
@ -39,6 +39,9 @@ open class TileInfo : IsPartOfGameInfoSerialization {
|
||||
@Transient
|
||||
lateinit var ruleset: Ruleset // a tile can be a tile with a ruleset, even without a map.
|
||||
|
||||
@Transient
|
||||
public val improvementFunctions = TileInfoImprovementFunctions(this)
|
||||
|
||||
@Transient
|
||||
private var isCityCenterInternal = false
|
||||
|
||||
@ -749,157 +752,6 @@ open class TileInfo : IsPartOfGameInfoSerialization {
|
||||
return (neighbors + this).any { neighbor -> neighbor.matchesFilter(terrainFilter) }
|
||||
}
|
||||
|
||||
/** Returns true if the [improvement] can be built on this [TileInfo] */
|
||||
fun canBuildImprovement(improvement: TileImprovement, civInfo: CivilizationInfo): Boolean = getImprovementBuildingProblems(improvement, civInfo).none()
|
||||
|
||||
enum class ImprovementBuildingProblem {
|
||||
WrongCiv, MissingTech, Unbuildable, NotJustOutsideBorders, OutsideBorders, UnmetConditional, Obsolete, MissingResources, Other
|
||||
}
|
||||
|
||||
/** Generates a sequence of reasons that prevent building given [improvement].
|
||||
* If the sequence is empty, improvement can be built immediately.
|
||||
*/
|
||||
fun getImprovementBuildingProblems(improvement: TileImprovement, civInfo: CivilizationInfo): Sequence<ImprovementBuildingProblem> = sequence {
|
||||
val stateForConditionals = StateForConditionals(civInfo, tile = this@TileInfo)
|
||||
|
||||
if (improvement.uniqueTo != null && improvement.uniqueTo != civInfo.civName)
|
||||
yield(ImprovementBuildingProblem.WrongCiv)
|
||||
if (improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!))
|
||||
yield(ImprovementBuildingProblem.MissingTech)
|
||||
if (improvement.hasUnique(UniqueType.Unbuildable, stateForConditionals))
|
||||
yield(ImprovementBuildingProblem.Unbuildable)
|
||||
|
||||
if (getOwner() != civInfo && !improvement.hasUnique(UniqueType.CanBuildOutsideBorders, stateForConditionals)) {
|
||||
if (!improvement.hasUnique(UniqueType.CanBuildJustOutsideBorders, stateForConditionals))
|
||||
yield(ImprovementBuildingProblem.OutsideBorders)
|
||||
else if (neighbors.none { it.getOwner() == civInfo })
|
||||
yield(ImprovementBuildingProblem.NotJustOutsideBorders)
|
||||
}
|
||||
|
||||
if (improvement.getMatchingUniques(UniqueType.OnlyAvailableWhen, StateForConditionals.IgnoreConditionals).any {
|
||||
!it.conditionalsApply(stateForConditionals)
|
||||
})
|
||||
yield(ImprovementBuildingProblem.UnmetConditional)
|
||||
|
||||
if (improvement.getMatchingUniques(UniqueType.ObsoleteWith, stateForConditionals).any {
|
||||
civInfo.tech.isResearched(it.params[0])
|
||||
})
|
||||
yield(ImprovementBuildingProblem.Obsolete)
|
||||
|
||||
if (improvement.getMatchingUniques(UniqueType.ConsumesResources, stateForConditionals).any {
|
||||
civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
|
||||
})
|
||||
yield(ImprovementBuildingProblem.MissingResources)
|
||||
|
||||
val knownFeatureRemovals = ruleset.tileImprovements.values
|
||||
.filter { rulesetImprovement ->
|
||||
rulesetImprovement.name.startsWith(Constants.remove)
|
||||
&& RoadStatus.values().none { it.removeAction == rulesetImprovement.name }
|
||||
&& (rulesetImprovement.techRequired == null || civInfo.tech.isResearched(rulesetImprovement.techRequired!!))
|
||||
}
|
||||
|
||||
if (!canImprovementBeBuiltHere(improvement, hasViewableResource(civInfo), knownFeatureRemovals, stateForConditionals))
|
||||
// There are way too many conditions in that functions, besides, they are not interesting
|
||||
// at least for the current usecases. Improve if really needed.
|
||||
yield(ImprovementBuildingProblem.Other)
|
||||
}
|
||||
|
||||
/** Without regards to what CivInfo it is, a lot of the checks are just for the improvement on the tile.
|
||||
* Doubles as a check for the map editor.
|
||||
*/
|
||||
private fun canImprovementBeBuiltHere(
|
||||
improvement: TileImprovement,
|
||||
resourceIsVisible: Boolean = resource != null,
|
||||
knownFeatureRemovals: List<TileImprovement>? = null,
|
||||
stateForConditionals: StateForConditionals = StateForConditionals(tile=this)
|
||||
): Boolean {
|
||||
|
||||
fun TileImprovement.canBeBuildOnThisUnbuildableTerrain(
|
||||
knownFeatureRemovals: List<TileImprovement>? = null,
|
||||
): Boolean {
|
||||
val topTerrain = getLastTerrain()
|
||||
// We can build if we are specifically allowed to build on this terrain
|
||||
if (isAllowedOnFeature(topTerrain.name)) return true
|
||||
|
||||
// Otherwise, we can if this improvement removes the top terrain
|
||||
if (!hasUnique(UniqueType.RemovesFeaturesIfBuilt, stateForConditionals)) return false
|
||||
val removeAction = ruleset.tileImprovements[Constants.remove + topTerrain.name] ?: return false
|
||||
// and we have the tech to remove that top terrain
|
||||
if (removeAction.techRequired != null && (knownFeatureRemovals == null || removeAction !in knownFeatureRemovals)) return false
|
||||
// and we can build it on the tile without the top terrain
|
||||
val clonedTile = this@TileInfo.clone()
|
||||
clonedTile.removeTerrainFeature(topTerrain.name)
|
||||
return clonedTile.canImprovementBeBuiltHere(improvement, resourceIsVisible, knownFeatureRemovals, stateForConditionals)
|
||||
}
|
||||
|
||||
return when {
|
||||
improvement.name == this.improvement -> false
|
||||
isCityCenter() -> false
|
||||
|
||||
// First we handle a few special improvements
|
||||
|
||||
// Can only cancel if there is actually an improvement being built
|
||||
improvement.name == Constants.cancelImprovementOrder -> (this.improvementInProgress != null)
|
||||
// Can only remove roads if that road is actually there
|
||||
RoadStatus.values().any { it.removeAction == improvement.name } -> roadStatus.removeAction == improvement.name
|
||||
// Can only remove features if that feature is actually there
|
||||
improvement.name.startsWith(Constants.remove) -> terrainFeatures.any { it == improvement.name.removePrefix(Constants.remove) }
|
||||
// Can only build roads if on land and they are better than the current road
|
||||
RoadStatus.values().any { it.name == improvement.name } -> !isWater && RoadStatus.valueOf(improvement.name) > roadStatus
|
||||
|
||||
// Then we check if there is any reason to not allow this improvement to be build
|
||||
|
||||
// Can't build if there is already an irremovable improvement here
|
||||
this.improvement != null && getTileImprovement()!!.hasUnique(UniqueType.Irremovable, stateForConditionals) -> false
|
||||
|
||||
// Can't build if this terrain is unbuildable, except when we are specifically allowed to
|
||||
getLastTerrain().unbuildable && !improvement.canBeBuildOnThisUnbuildableTerrain(knownFeatureRemovals) -> false
|
||||
|
||||
// Can't build if any terrain specifically prevents building this improvement
|
||||
getTerrainMatchingUniques(UniqueType.RestrictedBuildableImprovements, stateForConditionals).any {
|
||||
unique -> !improvement.matchesFilter(unique.params[0])
|
||||
} -> false
|
||||
|
||||
// Can't build if the improvement specifically prevents building on some present feature
|
||||
improvement.getMatchingUniques(UniqueType.CannotBuildOnTile, stateForConditionals).any {
|
||||
unique -> matchesTerrainFilter(unique.params[0])
|
||||
} -> false
|
||||
|
||||
// Can't build if an improvement is only allowed to be built on specific tiles and this is not one of them
|
||||
// If multiple uniques of this type exists, we want all to match (e.g. Hill _and_ Forest would be meaningful)
|
||||
improvement.getMatchingUniques(UniqueType.CanOnlyBeBuiltOnTile, stateForConditionals).let {
|
||||
it.any() && it.any { unique -> !matchesTerrainFilter(unique.params[0]) }
|
||||
} -> false
|
||||
|
||||
// Can't build if the improvement requires an adjacent terrain that is not present
|
||||
improvement.getMatchingUniques(UniqueType.MustBeNextTo, stateForConditionals).any {
|
||||
!isAdjacentTo(it.params[0])
|
||||
} -> false
|
||||
|
||||
// Can't build it if it is only allowed to improve resources and it doesn't improve this resource
|
||||
improvement.hasUnique(UniqueType.CanOnlyImproveResource, stateForConditionals) && (
|
||||
!resourceIsVisible || !tileResource.isImprovedBy(improvement.name)
|
||||
) -> false
|
||||
|
||||
// At this point we know this is a normal improvement and that there is no reason not to allow it to be built.
|
||||
|
||||
// Lastly we check if the improvement may be built on this terrain or resource
|
||||
improvement.canBeBuiltOn(getLastTerrain().name) -> true
|
||||
isLand && improvement.canBeBuiltOn("Land") -> true
|
||||
isWater && improvement.canBeBuiltOn("Water") -> true
|
||||
// DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests.
|
||||
improvement.hasUnique(UniqueType.ImprovementBuildableByFreshWater, stateForConditionals)
|
||||
&& isAdjacentTo(Constants.freshWater) -> true
|
||||
|
||||
// I don't particularly like this check, but it is required to build mines on non-hill resources
|
||||
resourceIsVisible && tileResource.isImprovedBy(improvement.name) -> true
|
||||
// DEPRECATED since 4.0.14, REMOVE SOON:
|
||||
isLand && improvement.terrainsCanBeBuiltOn.isEmpty() && !improvement.hasUnique(UniqueType.CanOnlyImproveResource) -> true
|
||||
// No reason this improvement should be built here, so can't build it
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
/** Implements [UniqueParameterType.TileFilter][com.unciv.models.ruleset.unique.UniqueParameterType.TileFilter] */
|
||||
fun matchesFilter(filter: String, civInfo: CivilizationInfo? = null): Boolean {
|
||||
if (matchesTerrainFilter(filter, civInfo)) return true
|
||||
@ -1098,7 +950,7 @@ open class TileInfo : IsPartOfGameInfoSerialization {
|
||||
else
|
||||
FormattedLine(resource!!, link="Resource/$resource")
|
||||
if (resource != null && viewingCiv != null && hasViewableResource(viewingCiv)) {
|
||||
val resourceImprovement = tileResource.getImprovements().firstOrNull { canBuildImprovement(ruleset.tileImprovements[it]!!, viewingCiv) }
|
||||
val resourceImprovement = tileResource.getImprovements().firstOrNull { improvementFunctions.canBuildImprovement(ruleset.tileImprovements[it]!!, viewingCiv) }
|
||||
val tileImprovement = ruleset.tileImprovements[resourceImprovement]
|
||||
if (tileImprovement?.techRequired != null
|
||||
&& !viewingCiv.tech.isResearched(tileImprovement.techRequired!!)) {
|
||||
@ -1428,7 +1280,7 @@ open class TileInfo : IsPartOfGameInfoSerialization {
|
||||
return
|
||||
}
|
||||
changeImprovement(null) // Unset, and check if it can be reset. If so, do it, if not, invalid.
|
||||
if (canImprovementBeBuiltHere(improvementObject, stateForConditionals = StateForConditionals.IgnoreConditionals))
|
||||
if (improvementFunctions.canImprovementBeBuiltHere(improvementObject, stateForConditionals = StateForConditionals.IgnoreConditionals))
|
||||
changeImprovement(improvementObject.name)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,165 @@
|
||||
package com.unciv.logic.map.tile
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
|
||||
|
||||
enum class ImprovementBuildingProblem {
|
||||
WrongCiv, MissingTech, Unbuildable, NotJustOutsideBorders, OutsideBorders, UnmetConditional, Obsolete, MissingResources, Other
|
||||
}
|
||||
|
||||
class TileInfoImprovementFunctions(val tileInfo: TileInfo) {
|
||||
|
||||
/** Returns true if the [improvement] can be built on this [TileInfo] */
|
||||
fun canBuildImprovement(improvement: TileImprovement, civInfo: CivilizationInfo): Boolean = getImprovementBuildingProblems(improvement, civInfo).none()
|
||||
|
||||
/** Generates a sequence of reasons that prevent building given [improvement].
|
||||
* If the sequence is empty, improvement can be built immediately.
|
||||
*/
|
||||
fun getImprovementBuildingProblems(improvement: TileImprovement, civInfo: CivilizationInfo): Sequence<ImprovementBuildingProblem> = sequence {
|
||||
val stateForConditionals = StateForConditionals(civInfo, tile = tileInfo)
|
||||
|
||||
if (improvement.uniqueTo != null && improvement.uniqueTo != civInfo.civName)
|
||||
yield(ImprovementBuildingProblem.WrongCiv)
|
||||
if (improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!))
|
||||
yield(ImprovementBuildingProblem.MissingTech)
|
||||
if (improvement.hasUnique(UniqueType.Unbuildable, stateForConditionals))
|
||||
yield(ImprovementBuildingProblem.Unbuildable)
|
||||
|
||||
if (tileInfo.getOwner() != civInfo && !improvement.hasUnique(UniqueType.CanBuildOutsideBorders, stateForConditionals)) {
|
||||
if (!improvement.hasUnique(UniqueType.CanBuildJustOutsideBorders, stateForConditionals))
|
||||
yield(ImprovementBuildingProblem.OutsideBorders)
|
||||
else if (tileInfo.neighbors.none { it.getOwner() == civInfo })
|
||||
yield(ImprovementBuildingProblem.NotJustOutsideBorders)
|
||||
}
|
||||
|
||||
if (improvement.getMatchingUniques(UniqueType.OnlyAvailableWhen, StateForConditionals.IgnoreConditionals).any {
|
||||
!it.conditionalsApply(stateForConditionals)
|
||||
})
|
||||
yield(ImprovementBuildingProblem.UnmetConditional)
|
||||
|
||||
if (improvement.getMatchingUniques(UniqueType.ObsoleteWith, stateForConditionals).any {
|
||||
civInfo.tech.isResearched(it.params[0])
|
||||
})
|
||||
yield(ImprovementBuildingProblem.Obsolete)
|
||||
|
||||
if (improvement.getMatchingUniques(UniqueType.ConsumesResources, stateForConditionals).any {
|
||||
civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
|
||||
})
|
||||
yield(ImprovementBuildingProblem.MissingResources)
|
||||
|
||||
val knownFeatureRemovals = tileInfo.ruleset.tileImprovements.values
|
||||
.filter { rulesetImprovement ->
|
||||
rulesetImprovement.name.startsWith(Constants.remove)
|
||||
&& RoadStatus.values().none { it.removeAction == rulesetImprovement.name }
|
||||
&& (rulesetImprovement.techRequired == null || civInfo.tech.isResearched(rulesetImprovement.techRequired!!))
|
||||
}
|
||||
|
||||
if (!canImprovementBeBuiltHere(improvement, tileInfo.hasViewableResource(civInfo), knownFeatureRemovals, stateForConditionals))
|
||||
// There are way too many conditions in that functions, besides, they are not interesting
|
||||
// at least for the current usecases. Improve if really needed.
|
||||
yield(ImprovementBuildingProblem.Other)
|
||||
}
|
||||
|
||||
/** Without regards to what CivInfo it is, a lot of the checks are just for the improvement on the tile.
|
||||
* Doubles as a check for the map editor.
|
||||
*/
|
||||
internal fun canImprovementBeBuiltHere(
|
||||
improvement: TileImprovement,
|
||||
resourceIsVisible: Boolean = tileInfo.resource != null,
|
||||
knownFeatureRemovals: List<TileImprovement>? = null,
|
||||
stateForConditionals: StateForConditionals = StateForConditionals(tile=tileInfo)
|
||||
): Boolean {
|
||||
|
||||
fun TileImprovement.canBeBuildOnThisUnbuildableTerrain(
|
||||
knownFeatureRemovals: List<TileImprovement>? = null,
|
||||
): Boolean {
|
||||
val topTerrain = tileInfo.getLastTerrain()
|
||||
// We can build if we are specifically allowed to build on this terrain
|
||||
if (isAllowedOnFeature(topTerrain.name)) return true
|
||||
|
||||
// Otherwise, we can if this improvement removes the top terrain
|
||||
if (!hasUnique(UniqueType.RemovesFeaturesIfBuilt, stateForConditionals)) return false
|
||||
val removeAction = tileInfo.ruleset.tileImprovements[Constants.remove + topTerrain.name] ?: return false
|
||||
// and we have the tech to remove that top terrain
|
||||
if (removeAction.techRequired != null && (knownFeatureRemovals == null || removeAction !in knownFeatureRemovals)) return false
|
||||
// and we can build it on the tile without the top terrain
|
||||
val clonedTile = tileInfo.clone()
|
||||
clonedTile.removeTerrainFeature(topTerrain.name)
|
||||
return clonedTile.improvementFunctions.canImprovementBeBuiltHere(improvement, resourceIsVisible, knownFeatureRemovals, stateForConditionals)
|
||||
}
|
||||
|
||||
return when {
|
||||
improvement.name == tileInfo.improvement -> false
|
||||
tileInfo.isCityCenter() -> false
|
||||
|
||||
// First we handle a few special improvements
|
||||
|
||||
// Can only cancel if there is actually an improvement being built
|
||||
improvement.name == Constants.cancelImprovementOrder -> (tileInfo.improvementInProgress != null)
|
||||
// Can only remove roads if that road is actually there
|
||||
RoadStatus.values().any { it.removeAction == improvement.name } -> tileInfo.roadStatus.removeAction == improvement.name
|
||||
// Can only remove features if that feature is actually there
|
||||
improvement.name.startsWith(Constants.remove) -> tileInfo.terrainFeatures.any { it == improvement.name.removePrefix(
|
||||
Constants.remove) }
|
||||
// Can only build roads if on land and they are better than the current road
|
||||
RoadStatus.values().any { it.name == improvement.name } -> !tileInfo.isWater
|
||||
&& RoadStatus.valueOf(improvement.name) > tileInfo.roadStatus
|
||||
|
||||
// Then we check if there is any reason to not allow this improvement to be build
|
||||
|
||||
// Can't build if there is already an irremovable improvement here
|
||||
tileInfo.improvement != null && tileInfo.getTileImprovement()!!.hasUnique(UniqueType.Irremovable, stateForConditionals) -> false
|
||||
|
||||
// Can't build if this terrain is unbuildable, except when we are specifically allowed to
|
||||
tileInfo.getLastTerrain().unbuildable && !improvement.canBeBuildOnThisUnbuildableTerrain(knownFeatureRemovals) -> false
|
||||
|
||||
// Can't build if any terrain specifically prevents building this improvement
|
||||
tileInfo.getTerrainMatchingUniques(UniqueType.RestrictedBuildableImprovements, stateForConditionals).any {
|
||||
unique -> !improvement.matchesFilter(unique.params[0])
|
||||
} -> false
|
||||
|
||||
// Can't build if the improvement specifically prevents building on some present feature
|
||||
improvement.getMatchingUniques(UniqueType.CannotBuildOnTile, stateForConditionals).any {
|
||||
unique -> tileInfo.matchesTerrainFilter(unique.params[0])
|
||||
} -> false
|
||||
|
||||
// Can't build if an improvement is only allowed to be built on specific tiles and this is not one of them
|
||||
// If multiple uniques of this type exists, we want all to match (e.g. Hill _and_ Forest would be meaningful)
|
||||
improvement.getMatchingUniques(UniqueType.CanOnlyBeBuiltOnTile, stateForConditionals).let {
|
||||
it.any() && it.any { unique -> !tileInfo.matchesTerrainFilter(unique.params[0]) }
|
||||
} -> false
|
||||
|
||||
// Can't build if the improvement requires an adjacent terrain that is not present
|
||||
improvement.getMatchingUniques(UniqueType.MustBeNextTo, stateForConditionals).any {
|
||||
!tileInfo.isAdjacentTo(it.params[0])
|
||||
} -> false
|
||||
|
||||
// Can't build it if it is only allowed to improve resources and it doesn't improve this resource
|
||||
improvement.hasUnique(UniqueType.CanOnlyImproveResource, stateForConditionals) && (
|
||||
!resourceIsVisible || !tileInfo.tileResource.isImprovedBy(improvement.name)
|
||||
) -> false
|
||||
|
||||
// At this point we know this is a normal improvement and that there is no reason not to allow it to be built.
|
||||
|
||||
// Lastly we check if the improvement may be built on this terrain or resource
|
||||
improvement.canBeBuiltOn(tileInfo.getLastTerrain().name) -> true
|
||||
tileInfo.isLand && improvement.canBeBuiltOn("Land") -> true
|
||||
tileInfo.isWater && improvement.canBeBuiltOn("Water") -> true
|
||||
// DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests.
|
||||
improvement.hasUnique(UniqueType.ImprovementBuildableByFreshWater, stateForConditionals)
|
||||
&& tileInfo.isAdjacentTo(Constants.freshWater) -> true
|
||||
|
||||
// I don't particularly like this check, but it is required to build mines on non-hill resources
|
||||
resourceIsVisible && tileInfo.tileResource.isImprovedBy(improvement.name) -> true
|
||||
// DEPRECATED since 4.0.14, REMOVE SOON:
|
||||
tileInfo.isLand && improvement.terrainsCanBeBuiltOn.isEmpty() && !improvement.hasUnique(
|
||||
UniqueType.CanOnlyImproveResource) -> true
|
||||
// No reason this improvement should be built here, so can't build it
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,11 @@
|
||||
package com.unciv.models
|
||||
|
||||
import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.unciv.Constants
|
||||
import com.unciv.models.translations.getPlaceholderParameters
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.utils.KeyCharAndCode
|
||||
import com.unciv.ui.utils.extensions.surroundWithCircle
|
||||
import com.unciv.ui.utils.extensions.surroundWithThinCircle
|
||||
|
||||
|
||||
/** Unit Actions - class - carries dynamic data and actual execution.
|
||||
|
@ -131,7 +131,7 @@ class TileResource : RulesetStatsObject() {
|
||||
|
||||
fun getImprovingImprovement(tile: TileInfo, civInfo: CivilizationInfo): String? {
|
||||
return getImprovements().firstOrNull {
|
||||
tile.canBuildImprovement(civInfo.gameInfo.ruleSet.tileImprovements[it]!!, civInfo)
|
||||
tile.improvementFunctions.canBuildImprovement(civInfo.gameInfo.ruleSet.tileImprovements[it]!!, civInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ class CityScreen(
|
||||
val improvementToPlace = pickTileData!!.improvement
|
||||
return when {
|
||||
tileInfo.isMarkedForCreatesOneImprovement() -> Color.BROWN to 0.7f
|
||||
!tileInfo.canBuildImprovement(improvementToPlace, city.civInfo) -> Color.RED to 0.4f
|
||||
!tileInfo.improvementFunctions.canBuildImprovement(improvementToPlace, city.civInfo) -> Color.RED to 0.4f
|
||||
isExistingImprovementValuable(tileInfo, improvementToPlace) -> Color.ORANGE to 0.5f
|
||||
tileInfo.improvement != null -> Color.YELLOW to 0.6f
|
||||
tileInfo.turnsToImprovement > 0 -> Color.YELLOW to 0.6f
|
||||
@ -337,7 +337,7 @@ class CityScreen(
|
||||
val pickTileData = this.pickTileData!!
|
||||
this.pickTileData = null
|
||||
val improvement = pickTileData.improvement
|
||||
if (tileInfo.canBuildImprovement(improvement, cityInfo.civInfo)) {
|
||||
if (tileInfo.improvementFunctions.canBuildImprovement(improvement, cityInfo.civInfo)) {
|
||||
if (pickTileData.isBuying) {
|
||||
constructionsTable.askToBuyConstruction(pickTileData.building, pickTileData.buyStat, tileInfo)
|
||||
} else {
|
||||
|
@ -5,6 +5,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.tile.ImprovementBuildingProblem
|
||||
import com.unciv.logic.map.tile.TileInfo
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||
@ -29,13 +30,14 @@ class ImprovementPickerScreen(
|
||||
companion object {
|
||||
/** Set of resolvable improvement building problems that this class knows how to report. */
|
||||
private val reportableProblems = setOf(
|
||||
TileInfo.ImprovementBuildingProblem.MissingTech,
|
||||
TileInfo.ImprovementBuildingProblem.NotJustOutsideBorders,
|
||||
TileInfo.ImprovementBuildingProblem.OutsideBorders,
|
||||
TileInfo.ImprovementBuildingProblem.MissingResources)
|
||||
ImprovementBuildingProblem.MissingTech,
|
||||
ImprovementBuildingProblem.NotJustOutsideBorders,
|
||||
ImprovementBuildingProblem.OutsideBorders,
|
||||
ImprovementBuildingProblem.MissingResources
|
||||
)
|
||||
|
||||
/** Return true if we can report improvements associated with the [problems] (or there are no problems for it at all). */
|
||||
fun canReport(problems: Collection<TileInfo.ImprovementBuildingProblem>) = problems.all { it in reportableProblems }
|
||||
fun canReport(problems: Collection<ImprovementBuildingProblem>) = problems.all { it in reportableProblems }
|
||||
}
|
||||
|
||||
private var selectedImprovement: TileImprovement? = null
|
||||
@ -89,10 +91,10 @@ class ImprovementPickerScreen(
|
||||
if (improvement.name == tileInfo.improvement) continue // also checked by canImprovementBeBuiltHere, but after more expensive tests
|
||||
if (!unit.canBuildImprovement(improvement)) continue
|
||||
|
||||
var unbuildableBecause = tileInfo.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
|
||||
var unbuildableBecause = tileInfo.improvementFunctions.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
|
||||
if (!canReport(unbuildableBecause)) {
|
||||
// Try after pretending to have removed the top terrain layer.
|
||||
unbuildableBecause = tileInfoWithoutLastTerrain.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
|
||||
unbuildableBecause = tileInfoWithoutLastTerrain.improvementFunctions.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
|
||||
if (!canReport(unbuildableBecause)) continue
|
||||
else suggestRemoval = true
|
||||
}
|
||||
@ -107,7 +109,7 @@ class ImprovementPickerScreen(
|
||||
// *other* improvements with same shortcutKey
|
||||
.filter { it.shortcutKey == improvement.shortcutKey && it != improvement }
|
||||
// civ can build it (checks tech researched)
|
||||
.filter { tileInfo.canBuildImprovement(it, currentPlayerCiv) }
|
||||
.filter { tileInfo.improvementFunctions.canBuildImprovement(it, currentPlayerCiv) }
|
||||
// is technologically more advanced
|
||||
.filter { getRequiredTechColumn(it) > techLevel }
|
||||
.any()
|
||||
@ -131,13 +133,13 @@ class ImprovementPickerScreen(
|
||||
|
||||
if (suggestRemoval)
|
||||
proposedSolutions.add("${Constants.remove}[${tileInfo.getLastTerrain().name}] first")
|
||||
if (TileInfo.ImprovementBuildingProblem.MissingTech in unbuildableBecause)
|
||||
if (ImprovementBuildingProblem.MissingTech in unbuildableBecause)
|
||||
proposedSolutions.add("Research [${improvement.techRequired}] first")
|
||||
if (TileInfo.ImprovementBuildingProblem.NotJustOutsideBorders in unbuildableBecause)
|
||||
if (ImprovementBuildingProblem.NotJustOutsideBorders in unbuildableBecause)
|
||||
proposedSolutions.add("Have this tile close to your borders")
|
||||
if (TileInfo.ImprovementBuildingProblem.OutsideBorders in unbuildableBecause)
|
||||
if (ImprovementBuildingProblem.OutsideBorders in unbuildableBecause)
|
||||
proposedSolutions.add("Have this tile inside your empire")
|
||||
if (TileInfo.ImprovementBuildingProblem.MissingResources in unbuildableBecause) {
|
||||
if (ImprovementBuildingProblem.MissingResources in unbuildableBecause) {
|
||||
proposedSolutions.addAll(improvement.getMatchingUniques(UniqueType.ConsumesResources).filter {
|
||||
currentPlayerCiv.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
|
||||
}.map { "Acquire more [$it]" })
|
||||
|
@ -1,25 +1,11 @@
|
||||
package com.unciv.ui.pickerscreens
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.models.TutorialTrigger
|
||||
import com.unciv.models.UncivSound
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.Promotion
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.popup.AskTextPopup
|
||||
import com.unciv.ui.utils.BaseScreen
|
||||
import com.unciv.ui.utils.KeyCharAndCode
|
||||
import com.unciv.ui.utils.RecreateOnResize
|
||||
import com.unciv.ui.utils.extensions.isEnabled
|
||||
import com.unciv.ui.utils.extensions.onClick
|
||||
import com.unciv.ui.utils.extensions.surroundWithCircle
|
||||
import com.unciv.ui.utils.extensions.toLabel
|
||||
import com.unciv.ui.utils.extensions.toTextButton
|
||||
|
||||
class UnitRenamePopup(val screen: BaseScreen, val unit: MapUnit, val actionOnClose: ()->Unit) {
|
||||
init {
|
||||
|
@ -433,7 +433,7 @@ class DiplomacyScreen(
|
||||
for (improvableTile in improvableResourceTiles)
|
||||
for (tileImprovement in improvements.values)
|
||||
if (improvableTile.tileResource.isImprovedBy(tileImprovement.name)
|
||||
&& improvableTile.canBuildImprovement(tileImprovement, otherCiv)
|
||||
&& improvableTile.improvementFunctions.canBuildImprovement(tileImprovement, otherCiv)
|
||||
)
|
||||
needsImprovements = true
|
||||
|
||||
@ -495,7 +495,7 @@ class DiplomacyScreen(
|
||||
return diplomacyTable
|
||||
}
|
||||
|
||||
fun getImprovableResourceTiles(otherCiv:CivilizationInfo) = otherCiv.getCapital()!!.getTiles().filter {
|
||||
private fun getImprovableResourceTiles(otherCiv:CivilizationInfo) = otherCiv.getCapital()!!.getTiles().filter {
|
||||
it.hasViewableResource(otherCiv)
|
||||
&& it.tileResource.resourceType != ResourceType.Bonus
|
||||
&& (it.improvement == null || !it.tileResource.isImprovedBy(it.improvement!!))
|
||||
@ -512,7 +512,7 @@ class DiplomacyScreen(
|
||||
for (improvableTile in improvableResourceTiles) {
|
||||
for (tileImprovement in tileImprovements.values) {
|
||||
if (improvableTile.tileResource.isImprovedBy(tileImprovement.name)
|
||||
&& improvableTile.canBuildImprovement(tileImprovement, otherCiv)
|
||||
&& improvableTile.improvementFunctions.canBuildImprovement(tileImprovement, otherCiv)
|
||||
) {
|
||||
val improveTileButton =
|
||||
"Build [${tileImprovement}] on [${improvableTile.tileResource}] (200 Gold)".toTextButton()
|
||||
|
@ -150,7 +150,7 @@ object UnitActions {
|
||||
|
||||
val improvementName = tile.tileResource.getImprovingImprovement(tile, unit.civInfo) ?: return null
|
||||
val improvement = tile.ruleset.tileImprovements[improvementName] ?: return null
|
||||
if (!tile.canBuildImprovement(improvement, unit.civInfo)) return null
|
||||
if (!tile.improvementFunctions.canBuildImprovement(improvement, unit.civInfo)) return null
|
||||
|
||||
return UnitAction(UnitActionType.Create, "Create [$improvementName]",
|
||||
action = {
|
||||
@ -544,7 +544,7 @@ object UnitActions {
|
||||
val couldConstruct = unit.currentMovement > 0
|
||||
&& !tile.isCityCenter()
|
||||
&& unit.civInfo.gameInfo.ruleSet.tileImprovements.values.any {
|
||||
ImprovementPickerScreen.canReport(tile.getImprovementBuildingProblems(it, unit.civInfo).toSet())
|
||||
ImprovementPickerScreen.canReport(tile.improvementFunctions.getImprovementBuildingProblems(it, unit.civInfo).toSet())
|
||||
&& unit.canBuildImprovement(it)
|
||||
}
|
||||
|
||||
@ -841,7 +841,7 @@ object UnitActions {
|
||||
}.takeIf {
|
||||
resourcesAvailable
|
||||
&& unit.currentMovement > 0f
|
||||
&& tile.canBuildImprovement(improvement, unit.civInfo)
|
||||
&& tile.improvementFunctions.canBuildImprovement(improvement, unit.civInfo)
|
||||
// Next test is to prevent interfering with UniqueType.CreatesOneImprovement -
|
||||
// not pretty, but users *can* remove the building from the city queue an thus clear this:
|
||||
&& !tile.isMarkedForCreatesOneImprovement()
|
||||
|
@ -63,7 +63,7 @@ class TileImprovementConstructionTests {
|
||||
tile.setTransients()
|
||||
if (improvement.uniqueTo != null) civInfo.civName = improvement.uniqueTo!!
|
||||
|
||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||
val canBeBuilt = tile.improvementFunctions.canBuildImprovement(improvement, civInfo)
|
||||
Assert.assertTrue(improvement.name, canBeBuilt)
|
||||
}
|
||||
}
|
||||
@ -80,7 +80,7 @@ class TileImprovementConstructionTests {
|
||||
if (improvement.hasUnique(UniqueType.CanOnlyBeBuiltOnTile, StateForConditionals.IgnoreConditionals)) continue
|
||||
|
||||
tile.setTransients()
|
||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||
val canBeBuilt = tile.improvementFunctions.canBuildImprovement(improvement, civInfo)
|
||||
Assert.assertTrue(improvement.name, canBeBuilt)
|
||||
}
|
||||
}
|
||||
@ -101,7 +101,7 @@ class TileImprovementConstructionTests {
|
||||
for (improvement in ruleSet.tileImprovements.values) {
|
||||
if (!improvement.uniques.contains("Can only be built on [Coastal] tiles")) continue
|
||||
civInfo.civName = improvement.uniqueTo ?: "OtherCiv"
|
||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||
val canBeBuilt = tile.improvementFunctions.canBuildImprovement(improvement, civInfo)
|
||||
Assert.assertTrue(improvement.name, canBeBuilt)
|
||||
}
|
||||
}
|
||||
@ -114,7 +114,7 @@ class TileImprovementConstructionTests {
|
||||
for (improvement in ruleSet.tileImprovements.values) {
|
||||
if (!improvement.uniques.contains("Can only be built on [Coastal] tiles")) continue
|
||||
civInfo.civName = improvement.uniqueTo ?: "OtherCiv"
|
||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||
val canBeBuilt = tile.improvementFunctions.canBuildImprovement(improvement, civInfo)
|
||||
Assert.assertFalse(improvement.name, canBeBuilt)
|
||||
}
|
||||
}
|
||||
@ -126,7 +126,7 @@ class TileImprovementConstructionTests {
|
||||
civInfo.civName = "OtherCiv"
|
||||
val tile = getTile()
|
||||
tile.setTransients()
|
||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||
val canBeBuilt = tile.improvementFunctions.canBuildImprovement(improvement, civInfo)
|
||||
Assert.assertFalse(improvement.name, canBeBuilt)
|
||||
}
|
||||
}
|
||||
@ -146,7 +146,7 @@ class TileImprovementConstructionTests {
|
||||
tile.baseTerrain = "Plains"
|
||||
tile.resource = wrongResource.name
|
||||
tile.setTransients()
|
||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||
val canBeBuilt = tile.improvementFunctions.canBuildImprovement(improvement, civInfo)
|
||||
Assert.assertFalse(improvement.name, canBeBuilt)
|
||||
}
|
||||
}
|
||||
@ -161,7 +161,7 @@ class TileImprovementConstructionTests {
|
||||
|
||||
for (improvement in ruleSet.tileImprovements.values) {
|
||||
if (!improvement.uniques.contains("Cannot be built on [Bonus resource] tiles")) continue
|
||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||
val canBeBuilt = tile.improvementFunctions.canBuildImprovement(improvement, civInfo)
|
||||
Assert.assertFalse(improvement.name, canBeBuilt)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user