diff --git a/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt b/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt index 31a3e69876..bb61d0b19d 100644 --- a/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt @@ -166,8 +166,9 @@ class PolicyManager : IsPartOfGameInfoSerialization { if (policy.policyBranchType == PolicyBranchType.BranchComplete) return false if (!getAdoptedPolicies().containsAll(policy.requires!!)) return false if (checkEra && civInfo.gameInfo.ruleset.eras[policy.branch.era]!!.eraNumber > civInfo.getEraNumber()) return false - if (policy.uniqueObjects.filter { it.type == UniqueType.OnlyAvailable } + if (policy.getMatchingUniques(UniqueType.OnlyAvailable, StateForConditionals.IgnoreConditionals) .any { !it.conditionalsApply(civInfo) }) return false + if (policy.hasUnique(UniqueType.Unavailable)) return false return true } diff --git a/core/src/com/unciv/logic/civilization/managers/RuinsManager.kt b/core/src/com/unciv/logic/civilization/managers/RuinsManager.kt index c3b84e65dd..98470041df 100644 --- a/core/src/com/unciv/logic/civilization/managers/RuinsManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/RuinsManager.kt @@ -31,18 +31,8 @@ class RuinsManager( } private fun getShuffledPossibleRewards(triggeringUnit: MapUnit): Iterable { - val stateForOnlyAvailableWhen = StateForConditionals(civInfo, unit = triggeringUnit, tile = triggeringUnit.getTile()) val candidates = - validRewards.asSequence() - // Filter out what shouldn't be considered right now, before the random choice - .filterNot { possibleReward -> - possibleReward.name in lastChosenRewards - || civInfo.gameInfo.difficulty in possibleReward.excludedDifficulties - || possibleReward.hasUnique(UniqueType.HiddenWithoutReligion) && !civInfo.gameInfo.isReligionEnabled() - || possibleReward.hasUnique(UniqueType.HiddenAfterGreatProphet) && civInfo.religionManager.greatProphetsEarned() > 0 - || possibleReward.getMatchingUniques(UniqueType.OnlyAvailable, StateForConditionals.IgnoreConditionals) - .any { !it.conditionalsApply(stateForOnlyAvailableWhen) } - } + validRewards.asSequence().filter { isPossibleReward(it, triggeringUnit) } // This might be a dirty way to do this, but it works (we do have randomWeighted in CollectionExtensions, but below we // need to choose another when the first choice's TriggerActivations report failure, and that's simpler this way) // For each possible reward, this feeds (reward.weight) copies of this reward to the overall Sequence to implement 'weight'. @@ -55,6 +45,18 @@ class RuinsManager( return candidates } + private fun isPossibleReward(ruinReward: RuinReward, unit: MapUnit): Boolean { + if (ruinReward.name in lastChosenRewards) return false + if (civInfo.gameInfo.difficulty in ruinReward.excludedDifficulties) return false + val stateForConditionals = StateForConditionals(civInfo, unit = unit, tile = unit.getTile()) + if (ruinReward.hasUnique(UniqueType.HiddenWithoutReligion, stateForConditionals) && !civInfo.gameInfo.isReligionEnabled()) return false + if (ruinReward.hasUnique(UniqueType.HiddenAfterGreatProphet, stateForConditionals) && civInfo.religionManager.greatProphetsEarned() > 0) return false + if (ruinReward.hasUnique(UniqueType.Unavailable, stateForConditionals)) return false + if (ruinReward.getMatchingUniques(UniqueType.OnlyAvailable, StateForConditionals.IgnoreConditionals) + .any { !it.conditionalsApply(stateForConditionals) }) return false + return true + } + fun selectNextRuinsReward(triggeringUnit: MapUnit) { for (possibleReward in getShuffledPossibleRewards(triggeringUnit)) { var atLeastOneUniqueHadEffect = false diff --git a/core/src/com/unciv/logic/civilization/managers/TechManager.kt b/core/src/com/unciv/logic/civilization/managers/TechManager.kt index 7c0e3f2aeb..04d7c0653f 100644 --- a/core/src/com/unciv/logic/civilization/managers/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/TechManager.kt @@ -161,8 +161,9 @@ class TechManager : IsPartOfGameInfoSerialization { fun canBeResearched(techName: String): Boolean { val tech = getRuleset().technologies[techName]!! - if (tech.uniqueObjects.any { it.type == UniqueType.OnlyAvailable && !it.conditionalsApply(civInfo) }) + if (tech.getMatchingUniques(UniqueType.OnlyAvailable, StateForConditionals.IgnoreConditionals).any { !it.conditionalsApply(civInfo) }) return false + if (tech.hasUnique(UniqueType.Unavailable, StateForConditionals(civInfo))) return false if (isResearched(tech.name) && !tech.isContinuallyResearchable()) return false diff --git a/core/src/com/unciv/logic/map/mapunit/UnitPromotions.kt b/core/src/com/unciv/logic/map/mapunit/UnitPromotions.kt index 895cf68021..4fdbdeead2 100644 --- a/core/src/com/unciv/logic/map/mapunit/UnitPromotions.kt +++ b/core/src/com/unciv/logic/map/mapunit/UnitPromotions.kt @@ -113,14 +113,18 @@ class UnitPromotions : IsPartOfGameInfoSerialization { * Checks unit type, already acquired promotions, prerequisites and incompatibility uniques. */ fun getAvailablePromotions(): Sequence { - return unit.civ.gameInfo.ruleset.unitPromotions.values - .asSequence() - .filter { unit.type.name in it.unitTypes && it.name !in promotions } - .filter { it.prerequisites.isEmpty() || it.prerequisites.any { p->p in promotions } } - .filter { promotion -> promotion.uniqueObjects - .none { it.type == UniqueType.OnlyAvailable - && !it.conditionalsApply(StateForConditionals(unit.civ, unit = unit)) } - } + return unit.civ.gameInfo.ruleset.unitPromotions.values.asSequence().filter { isAvailable(it) } + } + + private fun isAvailable(promotion: Promotion):Boolean { + if (promotion.name in promotions) return false + if (unit.type.name !in promotion.unitTypes) return false + if (promotion.prerequisites.isNotEmpty() && promotion.prerequisites.none { it in promotions }) return false + val stateForConditionals = StateForConditionals(unit.civ, unit = unit) + if (promotion.hasUnique(UniqueType.Unavailable, stateForConditionals)) return false + if (promotion.getMatchingUniques(UniqueType.OnlyAvailable, StateForConditionals.IgnoreConditionals) + .any { !it.conditionalsApply(stateForConditionals) }) return false + return true } fun clone(): UnitPromotions { diff --git a/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt b/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt index a6a8f2a947..64f342c656 100644 --- a/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt +++ b/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt @@ -53,6 +53,9 @@ class TileInfoImprovementFunctions(val tile: Tile) { 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) diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index 4ab60a5a95..83fa46b665 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -255,6 +255,10 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction { if (!unique.conditionalsApply(civ, cityConstructions.city)) yield(RejectionReasonType.ShouldNotBeDisplayed.toInstance()) + UniqueType.Unavailable -> + if (!unique.conditionalsApply(civ, cityConstructions.city)) + yield(RejectionReasonType.ShouldNotBeDisplayed.toInstance()) + UniqueType.RequiresPopulation -> if (unique.params[0].toInt() > cityConstructions.city.population.population) yield(RejectionReasonType.PopulationRequirement.toInstance(unique.text)) diff --git a/core/src/com/unciv/models/ruleset/Policy.kt b/core/src/com/unciv/models/ruleset/Policy.kt index 1db07a357c..c4bf3bb155 100644 --- a/core/src/com/unciv/models/ruleset/Policy.kt +++ b/core/src/com/unciv/models/ruleset/Policy.kt @@ -93,7 +93,9 @@ open class Policy : RulesetObject() { fun isEnabledByPolicy(rulesetObject: IRulesetObject) = rulesetObject.getMatchingUniques(UniqueType.OnlyAvailable, StateForConditionals.IgnoreConditionals).any { it.conditionals.any { it.type == UniqueType.ConditionalAfterPolicyOrBelief && it.params[0] == name - } } + } } || rulesetObject.getMatchingUniques(UniqueType.Unavailable).any { it.conditionals.any { + it.type == UniqueType.ConditionalBeforePolicyOrBelief && it.params[0] == name + }} val enabledBuildings = ruleset.buildings.values.filter { isEnabledByPolicy(it) } val enabledUnits = ruleset.units.values.filter { isEnabledByPolicy(it) } @@ -110,6 +112,8 @@ open class Policy : RulesetObject() { fun isDisabledByPolicy(rulesetObject: IRulesetObject) = rulesetObject.getMatchingUniques(UniqueType.OnlyAvailable, StateForConditionals.IgnoreConditionals).any { it.conditionals.any { it.type == UniqueType.ConditionalBeforePolicyOrBelief && it.params[0] == name + } } || rulesetObject.getMatchingUniques(UniqueType.Unavailable).any { it.conditionals.any { + it.type == UniqueType.ConditionalAfterPolicyOrBelief && it.params[0] == name } } diff --git a/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt b/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt index 53bbb0120f..394fdcaa22 100644 --- a/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt +++ b/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt @@ -54,8 +54,8 @@ interface IHasUniques : INamed { // Currently an OnlyAvailableWhen can have multiple conditionals, implicitly a conjunction. // Therefore, if any of its several conditionals is a ConditionalTech, then that tech is required. .flatMap{ it.conditionals } - .filter{ it.isOfType(UniqueType.ConditionalTech) } - .map{ it.params[0] } + .filter{ it.type == UniqueType.ConditionalTech } + .map { it.params[0] } } fun legacyRequiredTechs(): Sequence = sequenceOf() diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index 5b56056043..da10ecfcdb 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -263,8 +263,11 @@ enum class UniqueType( MaxNumberBuildable("Limited to [amount] per Civilization", UniqueTarget.Building, UniqueTarget.Unit), HiddenBeforeAmountPolicies("Hidden until [amount] social policy branches have been completed", UniqueTarget.Building, UniqueTarget.Unit), // Meant to be used together with conditionals, like "Only available " + /** A special unique, as it only activates when it has conditionals that *do not* apply */ OnlyAvailable("Only available", UniqueTarget.Unit, UniqueTarget.Building, UniqueTarget.Improvement, UniqueTarget.Policy, UniqueTarget.Tech, UniqueTarget.Promotion, UniqueTarget.Ruins, UniqueTarget.FollowerBelief, UniqueTarget.FounderBelief), + Unavailable("Unavailable", UniqueTarget.Unit, UniqueTarget.Building, UniqueTarget.Improvement, + UniqueTarget.Policy, UniqueTarget.Tech, UniqueTarget.Promotion, UniqueTarget.Ruins), ConvertFoodToProductionWhenConstructed("Excess Food converted to Production when under construction", UniqueTarget.Building, UniqueTarget.Unit), RequiresPopulation("Requires at least [amount] population", UniqueTarget.Building, UniqueTarget.Unit), diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index 19ab05fc7c..78b857b2e8 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -163,6 +163,9 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction { if (!unique.conditionalsApply(civInfo, cityConstructions.city)) yield(RejectionReasonType.ShouldNotBeDisplayed.toInstance()) + for (unique in getMatchingUniques(UniqueType.Unavailable, StateForConditionals(civInfo, cityConstructions.city))) + yield(RejectionReasonType.ShouldNotBeDisplayed.toInstance()) + for (unique in getMatchingUniques(UniqueType.RequiresPopulation)) if (unique.params[0].toInt() > cityConstructions.city.population.population) yield(RejectionReasonType.PopulationRequirement.toInstance(unique.text))