Bug 1/2 of #13451 - 'connect road' acknowledges availability uniques on road/railroad

This commit is contained in:
yairm210 2025-06-17 16:06:31 +03:00
parent 41fd4ff649
commit b01e1df24d
3 changed files with 76 additions and 54 deletions

View File

@ -0,0 +1,69 @@
package com.unciv.logic.civilization.managers
import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.tile.ImprovementBuildingProblem
import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.tile.TileImprovement
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueType
object ImprovementFunctions {
/** 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: Civilization, tile: Tile? = null): Sequence<ImprovementBuildingProblem> = sequence {
val stateForConditionals = StateForConditionals(civInfo, tile = tile)
if (improvement.uniqueTo != null && !civInfo.matchesFilter(improvement.uniqueTo!!))
yield(ImprovementBuildingProblem.WrongCiv)
if (civInfo.cache.uniqueImprovements.any { it.replaces == improvement.name })
yield(ImprovementBuildingProblem.Replaced)
if (improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!))
yield(ImprovementBuildingProblem.MissingTech)
if (improvement.getMatchingUniques(UniqueType.Unbuildable, StateForConditionals.IgnoreConditionals)
.any { it.modifiers.isEmpty() })
yield(ImprovementBuildingProblem.Unbuildable)
else if (improvement.hasUnique(UniqueType.Unbuildable, stateForConditionals))
yield(ImprovementBuildingProblem.ConditionallyUnbuildable)
if (improvement.hasUnique(UniqueType.Unavailable, stateForConditionals))
yield(ImprovementBuildingProblem.ConditionallyUnbuildable)
if (improvement.getMatchingUniques(UniqueType.OnlyAvailable, 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.getResourceAmount(it.params[1]) < it.params[0].toInt() })
yield(ImprovementBuildingProblem.MissingResources)
if (improvement.getMatchingUniques(UniqueType.CostsResources)
.any { civInfo.getResourceAmount(it.params[1]) < it.params[0].toInt() *
(if (it.isModifiedByGameSpeed()) civInfo.gameInfo.speed.modifier else 1f) })
yield(ImprovementBuildingProblem.MissingResources)
if (tile != null){
if (tile.getOwner() != civInfo && !improvement.hasUnique(UniqueType.CanBuildOutsideBorders, stateForConditionals)) {
if (!improvement.hasUnique(UniqueType.CanBuildJustOutsideBorders, stateForConditionals))
yield(ImprovementBuildingProblem.OutsideBorders)
else if (tile.neighbors.none { it.getOwner() == civInfo })
yield(ImprovementBuildingProblem.NotJustOutsideBorders)
}
val knownFeatureRemovals = tile.ruleset.nonRoadTileRemovals
.filter { rulesetImprovement ->
rulesetImprovement.techRequired == null || civInfo.tech.isResearched(rulesetImprovement.techRequired!!)
}
if (!tile.improvementFunctions.canImprovementBeBuiltHere(improvement, tile.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)
}
}
}

View File

@ -537,11 +537,13 @@ class TechManager : IsPartOfGameInfoSerialization {
fun getBestRoadAvailable(): RoadStatus {
val railroadImprovement = getRuleset().railroadImprovement // May not exist in mods
if (railroadImprovement != null && (railroadImprovement.techRequired == null || isResearched(railroadImprovement.techRequired!!)))
if (railroadImprovement != null && (railroadImprovement.techRequired == null || isResearched(railroadImprovement.techRequired!!))
&& ImprovementFunctions.getImprovementBuildingProblems(railroadImprovement, civInfo).none())
return RoadStatus.Railroad
val roadImprovement = getRuleset().roadImprovement
if (roadImprovement != null && (roadImprovement.techRequired == null || isResearched(roadImprovement.techRequired!!)))
if (roadImprovement != null && (roadImprovement.techRequired == null || isResearched(roadImprovement.techRequired!!))
&& ImprovementFunctions.getImprovementBuildingProblems(roadImprovement, civInfo).none())
return RoadStatus.Road
return RoadStatus.None

View File

@ -5,6 +5,7 @@ import com.unciv.logic.civilization.Civilization
import com.unciv.logic.civilization.LocationAction
import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.managers.ImprovementFunctions
import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.models.ruleset.tile.TileImprovement
import com.unciv.models.ruleset.unique.StateForConditionals
@ -41,58 +42,8 @@ class TileImprovementFunctions(val tile: Tile) {
/** 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: Civilization): Sequence<ImprovementBuildingProblem> = sequence {
val stateForConditionals = StateForConditionals(civInfo, tile = tile)
if (improvement.uniqueTo != null && !civInfo.matchesFilter(improvement.uniqueTo!!))
yield(ImprovementBuildingProblem.WrongCiv)
if (civInfo.cache.uniqueImprovements.any { it.replaces == improvement.name })
yield(ImprovementBuildingProblem.Replaced)
if (improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!))
yield(ImprovementBuildingProblem.MissingTech)
if (improvement.getMatchingUniques(UniqueType.Unbuildable, StateForConditionals.IgnoreConditionals)
.any { it.modifiers.isEmpty() })
yield(ImprovementBuildingProblem.Unbuildable)
else if (improvement.hasUnique(UniqueType.Unbuildable, stateForConditionals))
yield(ImprovementBuildingProblem.ConditionallyUnbuildable)
if (improvement.hasUnique(UniqueType.Unavailable, stateForConditionals))
yield(ImprovementBuildingProblem.ConditionallyUnbuildable)
if (tile.getOwner() != civInfo && !improvement.hasUnique(UniqueType.CanBuildOutsideBorders, stateForConditionals)) {
if (!improvement.hasUnique(UniqueType.CanBuildJustOutsideBorders, stateForConditionals))
yield(ImprovementBuildingProblem.OutsideBorders)
else if (tile.neighbors.none { it.getOwner() == civInfo })
yield(ImprovementBuildingProblem.NotJustOutsideBorders)
}
if (improvement.getMatchingUniques(UniqueType.OnlyAvailable, 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.getResourceAmount(it.params[1]) < it.params[0].toInt() })
yield(ImprovementBuildingProblem.MissingResources)
if (improvement.getMatchingUniques(UniqueType.CostsResources)
.any { civInfo.getResourceAmount(it.params[1]) < it.params[0].toInt() *
(if (it.isModifiedByGameSpeed()) civInfo.gameInfo.speed.modifier else 1f) })
yield(ImprovementBuildingProblem.MissingResources)
val knownFeatureRemovals = tile.ruleset.nonRoadTileRemovals
.filter { rulesetImprovement ->
rulesetImprovement.techRequired == null || civInfo.tech.isResearched(rulesetImprovement.techRequired!!)
}
if (!canImprovementBeBuiltHere(improvement, tile.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)
}
fun getImprovementBuildingProblems(improvement: TileImprovement, civInfo: Civilization): Sequence<ImprovementBuildingProblem> =
ImprovementFunctions.getImprovementBuildingProblems(improvement, civInfo, tile)
/** Without regards to what CivInfo it is (so no tech requirement check), a lot of the checks are just for the improvement on the tile.
* Doubles as a check for the map editor.