Resolved #8539 - modding: "Unbuildable" accepts conditionals

This commit is contained in:
Yair Morgenstern 2023-02-02 19:18:09 +02:00
parent 127da834c3
commit 3066a50020
2 changed files with 46 additions and 48 deletions

View File

@ -3,8 +3,8 @@ package com.unciv.models.ruleset
import com.unciv.logic.city.City import com.unciv.logic.city.City
import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.INonPerpetualConstruction import com.unciv.logic.city.INonPerpetualConstruction
import com.unciv.logic.city.RejectionReasonType
import com.unciv.logic.city.RejectionReason import com.unciv.logic.city.RejectionReason
import com.unciv.logic.city.RejectionReasonType
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.models.Counter import com.unciv.models.Counter
import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.ResourceType
@ -463,32 +463,32 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
override fun getRejectionReasons(cityConstructions: CityConstructions): Sequence<RejectionReason> = sequence { override fun getRejectionReasons(cityConstructions: CityConstructions): Sequence<RejectionReason> = sequence {
val cityCenter = cityConstructions.city.getCenterTile() val cityCenter = cityConstructions.city.getCenterTile()
val civInfo = cityConstructions.city.civInfo val civ = cityConstructions.city.civInfo
val ruleSet = civInfo.gameInfo.ruleSet val ruleSet = civ.gameInfo.ruleSet
if (cityConstructions.isBuilt(name)) if (cityConstructions.isBuilt(name))
yield(RejectionReasonType.AlreadyBuilt.toInstance()) yield(RejectionReasonType.AlreadyBuilt.toInstance())
// for buildings that are created as side effects of other things, and not directly built, // for buildings that are created as side effects of other things, and not directly built,
// or for buildings that can only be bought // or for buildings that can only be bought
if (hasUnique(UniqueType.Unbuildable)) if (hasUnique(UniqueType.Unbuildable, StateForConditionals(civ, cityConstructions.city)))
yield(RejectionReasonType.Unbuildable.toInstance()) yield(RejectionReasonType.Unbuildable.toInstance())
for (unique in uniqueObjects) { for (unique in uniqueObjects) {
@Suppress("NON_EXHAUSTIVE_WHEN") @Suppress("NON_EXHAUSTIVE_WHEN")
when (unique.type) { when (unique.type) {
UniqueType.OnlyAvailableWhen-> UniqueType.OnlyAvailableWhen->
if (!unique.conditionalsApply(civInfo, cityConstructions.city)) if (!unique.conditionalsApply(civ, cityConstructions.city))
yield(RejectionReasonType.ShouldNotBeDisplayed.toInstance()) yield(RejectionReasonType.ShouldNotBeDisplayed.toInstance())
UniqueType.EnablesNuclearWeapons -> if (!cityConstructions.city.civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled) UniqueType.EnablesNuclearWeapons -> if (!cityConstructions.city.civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled)
yield(RejectionReasonType.DisabledBySetting.toInstance()) yield(RejectionReasonType.DisabledBySetting.toInstance())
UniqueType.MustBeOn -> UniqueType.MustBeOn ->
if (!cityCenter.matchesTerrainFilter(unique.params[0], civInfo)) if (!cityCenter.matchesTerrainFilter(unique.params[0], civ))
yield(RejectionReasonType.MustBeOnTile.toInstance(unique.text)) yield(RejectionReasonType.MustBeOnTile.toInstance(unique.text))
UniqueType.MustNotBeOn -> UniqueType.MustNotBeOn ->
if (cityCenter.matchesTerrainFilter(unique.params[0], civInfo)) if (cityCenter.matchesTerrainFilter(unique.params[0], civ))
yield(RejectionReasonType.MustNotBeOnTile.toInstance(unique.text)) yield(RejectionReasonType.MustNotBeOnTile.toInstance(unique.text))
UniqueType.MustBeNextTo -> UniqueType.MustBeNextTo ->
@ -496,12 +496,12 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
yield(RejectionReasonType.MustBeNextToTile.toInstance(unique.text)) yield(RejectionReasonType.MustBeNextToTile.toInstance(unique.text))
UniqueType.MustNotBeNextTo -> UniqueType.MustNotBeNextTo ->
if (cityCenter.getTilesInDistance(1).any { it.matchesFilter(unique.params[0], civInfo) }) if (cityCenter.getTilesInDistance(1).any { it.matchesFilter(unique.params[0], civ) })
yield(RejectionReasonType.MustNotBeNextToTile.toInstance(unique.text)) yield(RejectionReasonType.MustNotBeNextToTile.toInstance(unique.text))
UniqueType.MustHaveOwnedWithinTiles -> UniqueType.MustHaveOwnedWithinTiles ->
if (cityCenter.getTilesInDistance(unique.params[1].toInt()) if (cityCenter.getTilesInDistance(unique.params[1].toInt())
.none { it.matchesFilter(unique.params[0], civInfo) && it.getOwner() == cityConstructions.city.civInfo } .none { it.matchesFilter(unique.params[0], civ) && it.getOwner() == cityConstructions.city.civInfo }
) )
yield(RejectionReasonType.MustOwnTile.toInstance(unique.text)) yield(RejectionReasonType.MustOwnTile.toInstance(unique.text))
@ -510,31 +510,31 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
yield(RejectionReasonType.CanOnlyBeBuiltInSpecificCities.toInstance(unique.text)) yield(RejectionReasonType.CanOnlyBeBuiltInSpecificCities.toInstance(unique.text))
UniqueType.ObsoleteWith -> UniqueType.ObsoleteWith ->
if (civInfo.tech.isResearched(unique.params[0])) if (civ.tech.isResearched(unique.params[0]))
yield(RejectionReasonType.Obsoleted.toInstance(unique.text)) yield(RejectionReasonType.Obsoleted.toInstance(unique.text))
UniqueType.HiddenWithoutReligion -> UniqueType.HiddenWithoutReligion ->
if (!civInfo.gameInfo.isReligionEnabled()) if (!civ.gameInfo.isReligionEnabled())
yield(RejectionReasonType.DisabledBySetting.toInstance()) yield(RejectionReasonType.DisabledBySetting.toInstance())
UniqueType.MaxNumberBuildable -> UniqueType.MaxNumberBuildable ->
if (civInfo.civConstructions.countConstructedObjects(this@Building) >= unique.params[0].toInt()) if (civ.civConstructions.countConstructedObjects(this@Building) >= unique.params[0].toInt())
yield(RejectionReasonType.MaxNumberBuildable.toInstance()) yield(RejectionReasonType.MaxNumberBuildable.toInstance())
// To be replaced with `Only available <after [Apollo Project] has been build>` // To be replaced with `Only available <after [Apollo Project] has been build>`
UniqueType.SpaceshipPart -> { UniqueType.SpaceshipPart -> {
if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) if (!civ.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts))
yield(RejectionReasonType.RequiresBuildingInSomeCity.toInstance("Apollo project not built!")) yield(RejectionReasonType.RequiresBuildingInSomeCity.toInstance("Apollo project not built!"))
} }
UniqueType.RequiresBuildingInSomeCities -> { UniqueType.RequiresBuildingInSomeCities -> {
val buildingName = unique.params[0] val buildingName = unique.params[0]
val numberOfCitiesRequired = unique.params[1].toInt() val numberOfCitiesRequired = unique.params[1].toInt()
val numberOfCitiesWithBuilding = civInfo.cities.count { val numberOfCitiesWithBuilding = civ.cities.count {
it.cityConstructions.containsBuildingOrEquivalent(buildingName) it.cityConstructions.containsBuildingOrEquivalent(buildingName)
} }
if (numberOfCitiesWithBuilding < numberOfCitiesRequired) { if (numberOfCitiesWithBuilding < numberOfCitiesRequired) {
val equivalentBuildingName = civInfo.getEquivalentBuilding(buildingName).name val equivalentBuildingName = civ.getEquivalentBuilding(buildingName).name
yield( yield(
// replace with civ-specific building for user // replace with civ-specific building for user
RejectionReasonType.RequiresBuildingInAllCities.toInstance( RejectionReasonType.RequiresBuildingInAllCities.toInstance(
@ -546,15 +546,15 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
UniqueType.RequiresBuildingInAllCities -> { UniqueType.RequiresBuildingInAllCities -> {
val filter = unique.params[0] val filter = unique.params[0]
if (civInfo.gameInfo.ruleSet.buildings.containsKey(filter) if (civ.gameInfo.ruleSet.buildings.containsKey(filter)
&& civInfo.cities.any { && civ.cities.any {
!it.isPuppet && !it.cityConstructions.containsBuildingOrEquivalent(unique.params[0]) !it.isPuppet && !it.cityConstructions.containsBuildingOrEquivalent(unique.params[0])
} }
) { ) {
yield( yield(
// replace with civ-specific building for user // replace with civ-specific building for user
RejectionReasonType.RequiresBuildingInAllCities.toInstance( RejectionReasonType.RequiresBuildingInAllCities.toInstance(
"Requires a [${civInfo.getEquivalentBuilding(unique.params[0])}] in all cities" "Requires a [${civ.getEquivalentBuilding(unique.params[0])}] in all cities"
) )
) )
} }
@ -566,7 +566,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
} }
UniqueType.HiddenWithoutVictoryType -> { UniqueType.HiddenWithoutVictoryType -> {
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(unique.params[0])) if (!civ.gameInfo.gameParameters.victoryTypes.contains(unique.params[0]))
yield(RejectionReasonType.HiddenWithoutVictory.toInstance(unique.text)) yield(RejectionReasonType.HiddenWithoutVictory.toInstance(unique.text))
} }
@ -574,49 +574,49 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
} }
} }
if (uniqueTo != null && uniqueTo != civInfo.civName) if (uniqueTo != null && uniqueTo != civ.civName)
yield(RejectionReasonType.UniqueToOtherNation.toInstance("Unique to $uniqueTo")) yield(RejectionReasonType.UniqueToOtherNation.toInstance("Unique to $uniqueTo"))
if (civInfo.cache.uniqueBuildings.any { it.replaces == name }) if (civ.cache.uniqueBuildings.any { it.replaces == name })
yield(RejectionReasonType.ReplacedByOurUnique.toInstance()) yield(RejectionReasonType.ReplacedByOurUnique.toInstance())
if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!)) if (requiredTech != null && !civ.tech.isResearched(requiredTech!!))
yield(RejectionReasonType.RequiresTech.toInstance("$requiredTech not researched!")) yield(RejectionReasonType.RequiresTech.toInstance("$requiredTech not researched!"))
// Regular wonders // Regular wonders
if (isWonder) { if (isWonder) {
if (civInfo.gameInfo.getCities().any { it.cityConstructions.isBuilt(name) }) if (civ.gameInfo.getCities().any { it.cityConstructions.isBuilt(name) })
yield(RejectionReasonType.WonderAlreadyBuilt.toInstance()) yield(RejectionReasonType.WonderAlreadyBuilt.toInstance())
if (civInfo.cities.any { it != cityConstructions.city && it.cityConstructions.isBeingConstructedOrEnqueued(name) }) if (civ.cities.any { it != cityConstructions.city && it.cityConstructions.isBeingConstructedOrEnqueued(name) })
yield(RejectionReasonType.WonderBeingBuiltElsewhere.toInstance()) yield(RejectionReasonType.WonderBeingBuiltElsewhere.toInstance())
if (civInfo.isCityState()) if (civ.isCityState())
yield(RejectionReasonType.CityStateWonder.toInstance()) yield(RejectionReasonType.CityStateWonder.toInstance())
val startingEra = civInfo.gameInfo.gameParameters.startingEra val startingEra = civ.gameInfo.gameParameters.startingEra
if (name in ruleSet.eras[startingEra]!!.startingObsoleteWonders) if (name in ruleSet.eras[startingEra]!!.startingObsoleteWonders)
yield(RejectionReasonType.WonderDisabledEra.toInstance()) yield(RejectionReasonType.WonderDisabledEra.toInstance())
} }
// National wonders // National wonders
if (isNationalWonder) { if (isNationalWonder) {
if (civInfo.cities.any { it.cityConstructions.isBuilt(name) }) if (civ.cities.any { it.cityConstructions.isBuilt(name) })
yield(RejectionReasonType.NationalWonderAlreadyBuilt.toInstance()) yield(RejectionReasonType.NationalWonderAlreadyBuilt.toInstance())
if (civInfo.cities.any { it != cityConstructions.city && it.cityConstructions.isBeingConstructedOrEnqueued(name) }) if (civ.cities.any { it != cityConstructions.city && it.cityConstructions.isBeingConstructedOrEnqueued(name) })
yield(RejectionReasonType.NationalWonderBeingBuiltElsewhere.toInstance()) yield(RejectionReasonType.NationalWonderBeingBuiltElsewhere.toInstance())
if (civInfo.isCityState()) if (civ.isCityState())
yield(RejectionReasonType.CityStateNationalWonder.toInstance()) yield(RejectionReasonType.CityStateNationalWonder.toInstance())
} }
if (requiredBuilding != null && !cityConstructions.containsBuildingOrEquivalent(requiredBuilding!!)) { if (requiredBuilding != null && !cityConstructions.containsBuildingOrEquivalent(requiredBuilding!!)) {
yield(RejectionReasonType.RequiresBuildingInThisCity.toInstance("Requires a [${civInfo.getEquivalentBuilding(requiredBuilding!!)}] in this city")) yield(RejectionReasonType.RequiresBuildingInThisCity.toInstance("Requires a [${civ.getEquivalentBuilding(requiredBuilding!!)}] in this city"))
} }
for ((resource, requiredAmount) in getResourceRequirements()) { for ((resource, requiredAmount) in getResourceRequirements()) {
val availableAmount = civInfo.getCivResourcesByName()[resource]!! val availableAmount = civ.getCivResourcesByName()[resource]!!
if (availableAmount < requiredAmount) { if (availableAmount < requiredAmount) {
yield(RejectionReasonType.ConsumesResources.toInstance(resource.getNeedMoreAmountString(requiredAmount - availableAmount))) yield(RejectionReasonType.ConsumesResources.toInstance(resource.getNeedMoreAmountString(requiredAmount - availableAmount)))
} }
@ -627,7 +627,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
.any { .any {
it.resource != null it.resource != null
&& requiredNearbyImprovedResources!!.contains(it.resource!!) && requiredNearbyImprovedResources!!.contains(it.resource!!)
&& it.getOwner() == civInfo && it.getOwner() == civ
&& ((it.getUnpillagedImprovement() != null && it.tileResource.isImprovedBy(it.improvement!!)) || it.isCityCenter() && ((it.getUnpillagedImprovement() != null && it.tileResource.isImprovedBy(it.improvement!!)) || it.isCityCenter()
|| (it.getUnpillagedTileImprovement()?.isGreatImprovement() == true && it.tileResource.resourceType == ResourceType.Strategic) || (it.getUnpillagedTileImprovement()?.isGreatImprovement() == true && it.tileResource.resourceType == ResourceType.Strategic)
) )

View File

@ -3,8 +3,8 @@ package com.unciv.models.ruleset.unit
import com.unciv.logic.city.City import com.unciv.logic.city.City
import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.INonPerpetualConstruction import com.unciv.logic.city.INonPerpetualConstruction
import com.unciv.logic.city.RejectionReasonType
import com.unciv.logic.city.RejectionReason import com.unciv.logic.city.RejectionReason
import com.unciv.logic.city.RejectionReasonType
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.mapunit.MapUnit import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
@ -134,50 +134,48 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
} }
} }
yieldAll(getRejectionReasons(civInfo)) yieldAll(getRejectionReasons(civInfo, cityConstructions.city))
} }
fun getRejectionReasons(civInfo: Civilization): Sequence<RejectionReason> = sequence { fun getRejectionReasons(civ: Civilization, city:City?=null): Sequence<RejectionReason> = sequence {
val ruleSet = civInfo.gameInfo.ruleSet if (requiredTech != null && !civ.tech.isResearched(requiredTech!!))
if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!))
yield(RejectionReasonType.RequiresTech.toInstance("$requiredTech not researched")) yield(RejectionReasonType.RequiresTech.toInstance("$requiredTech not researched"))
if (obsoleteTech != null && civInfo.tech.isResearched(obsoleteTech!!)) if (obsoleteTech != null && civ.tech.isResearched(obsoleteTech!!))
yield(RejectionReasonType.Obsoleted.toInstance("Obsolete by $obsoleteTech")) yield(RejectionReasonType.Obsoleted.toInstance("Obsolete by $obsoleteTech"))
if (uniqueTo != null && uniqueTo != civInfo.civName) if (uniqueTo != null && uniqueTo != civ.civName)
yield(RejectionReasonType.UniqueToOtherNation.toInstance("Unique to $uniqueTo")) yield(RejectionReasonType.UniqueToOtherNation.toInstance("Unique to $uniqueTo"))
if (civInfo.cache.uniqueUnits.any { it.replaces == name }) if (civ.cache.uniqueUnits.any { it.replaces == name })
yield(RejectionReasonType.ReplacedByOurUnique.toInstance("Our unique unit replaces this")) yield(RejectionReasonType.ReplacedByOurUnique.toInstance("Our unique unit replaces this"))
if (!civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled && isNuclearWeapon()) if (!civ.gameInfo.gameParameters.nuclearWeaponsEnabled && isNuclearWeapon())
yield(RejectionReasonType.DisabledBySetting.toInstance()) yield(RejectionReasonType.DisabledBySetting.toInstance())
for (unique in uniqueObjects) { for (unique in uniqueObjects.filter { it.conditionalsApply(civ, city) }) {
when (unique.type) { when (unique.type) {
UniqueType.Unbuildable -> UniqueType.Unbuildable ->
yield(RejectionReasonType.Unbuildable.toInstance()) yield(RejectionReasonType.Unbuildable.toInstance())
UniqueType.FoundCity -> if (civInfo.isCityState() || civInfo.isOneCityChallenger()) UniqueType.FoundCity -> if (civ.isCityState() || civ.isOneCityChallenger())
yield(RejectionReasonType.NoSettlerForOneCityPlayers.toInstance()) yield(RejectionReasonType.NoSettlerForOneCityPlayers.toInstance())
UniqueType.MaxNumberBuildable -> if (civInfo.civConstructions.countConstructedObjects(this@BaseUnit) >= unique.params[0].toInt()) UniqueType.MaxNumberBuildable -> if (civ.civConstructions.countConstructedObjects(this@BaseUnit) >= unique.params[0].toInt())
yield(RejectionReasonType.MaxNumberBuildable.toInstance()) yield(RejectionReasonType.MaxNumberBuildable.toInstance())
else -> {} else -> {}
} }
} }
if (!civInfo.isBarbarian()) { // Barbarians don't need resources if (!civ.isBarbarian()) { // Barbarians don't need resources
for ((resource, requiredAmount) in getResourceRequirements()) { for ((resource, requiredAmount) in getResourceRequirements()) {
val availableAmount = civInfo.getCivResourcesByName()[resource]!! val availableAmount = civ.getCivResourcesByName()[resource]!!
if (availableAmount < requiredAmount) { if (availableAmount < requiredAmount) {
yield(RejectionReasonType.ConsumesResources.toInstance(resource.getNeedMoreAmountString(requiredAmount - availableAmount))) yield(RejectionReasonType.ConsumesResources.toInstance(resource.getNeedMoreAmountString(requiredAmount - availableAmount)))
} }
} }
} }
for (unique in civInfo.getMatchingUniques(UniqueType.CannotBuildUnits)) for (unique in civ.getMatchingUniques(UniqueType.CannotBuildUnits))
if (this@BaseUnit.matchesFilter(unique.params[0])) { if (this@BaseUnit.matchesFilter(unique.params[0])) {
if (unique.conditionals.any { it.type == UniqueType.ConditionalBelowHappiness }){ if (unique.conditionals.any { it.type == UniqueType.ConditionalBelowHappiness }){
yield(RejectionReasonType.CannotBeBuilt.toInstance(unique.text, true)) yield(RejectionReasonType.CannotBeBuilt.toInstance(unique.text, true))