Chore: Moved tile improvement functions to separate class

This commit is contained in:
Yair Morgenstern
2023-01-19 21:32:58 +02:00
parent 504a15b9a5
commit 69393eafce
14 changed files with 209 additions and 207 deletions

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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)
}

View File

@ -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
}
}
}

View File

@ -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.

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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]" })

View File

@ -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 {

View File

@ -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()

View File

@ -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()

View File

@ -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)
}
}