mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-10 15:59:33 +07:00
Refactor more code, hopefully increasing maintainability (#5062)
* Fixed great person gift formula, confusing boolean, "great person" filter * Refactored getRejectionReason to return a hashSet of reasons instead of a random one
This commit is contained in:
@ -1064,7 +1064,8 @@
|
||||
"name": "Apollo Program",
|
||||
"cost": 750,
|
||||
"isNationalWonder": true,
|
||||
"uniques": ["Enables construction of Spaceship parts", "Triggers a global alert upon completion"],
|
||||
"uniques": ["Enables construction of Spaceship parts", "Triggers a global alert upon completion",
|
||||
"Hidden when [Scientific] Victory is disabled"],
|
||||
"requiredTech": "Rocketry"
|
||||
},
|
||||
|
||||
@ -1091,7 +1092,7 @@
|
||||
"name": "SS Cockpit",
|
||||
"requiredResource": "Aluminum",
|
||||
"requiredTech": "Satellites",
|
||||
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
|
||||
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased", "Hidden when [Scientific] Victory is disabled"]
|
||||
},
|
||||
{
|
||||
"name": "Hubble Space Telescope",
|
||||
@ -1109,7 +1110,7 @@
|
||||
"name": "SS Booster",
|
||||
"requiredResource": "Aluminum",
|
||||
"requiredTech": "Advanced Ballistics",
|
||||
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
|
||||
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased", "Hidden when [Scientific] Victory is disabled"]
|
||||
},
|
||||
{
|
||||
"name": "Spaceship Factory",
|
||||
@ -1136,13 +1137,13 @@
|
||||
"name": "SS Engine",
|
||||
"requiredResource": "Aluminum",
|
||||
"requiredTech": "Particle Physics",
|
||||
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
|
||||
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased", "Hidden when [Scientific] Victory is disabled"]
|
||||
},
|
||||
{
|
||||
"name": "SS Stasis Chamber",
|
||||
"requiredResource": "Aluminum",
|
||||
"requiredTech": "Nanotechnology",
|
||||
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
|
||||
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased", "Hidden when [Scientific] Victory is disabled"]
|
||||
},
|
||||
|
||||
// All Eras
|
||||
|
@ -375,14 +375,10 @@ class CityConstructions {
|
||||
// Perpetual constructions should always still be valid (I hope)
|
||||
if (construction is PerpetualConstruction) continue
|
||||
|
||||
val rejectionReason =
|
||||
(construction as INonPerpetualConstruction).getRejectionReason(this)
|
||||
val rejectionReasons =
|
||||
(construction as INonPerpetualConstruction).getRejectionReasons(this)
|
||||
|
||||
if (rejectionReason.endsWith("lready built")
|
||||
|| rejectionReason.startsWith("Cannot be built with")
|
||||
|| rejectionReason.startsWith("Don't need to build any more")
|
||||
|| rejectionReason.startsWith("Obsolete")
|
||||
) {
|
||||
if (rejectionReasons.hasAReasonToBeRemovedFromQueue()) {
|
||||
if (construction is Building) {
|
||||
// Production put into wonders gets refunded
|
||||
if (construction.isWonder && getWorkDone(constructionName) != 0) {
|
||||
@ -392,7 +388,7 @@ class CityConstructions {
|
||||
}
|
||||
} else if (construction is BaseUnit) {
|
||||
// Production put into upgradable units gets put into upgraded version
|
||||
if (rejectionReason.startsWith("Obsolete") && construction.upgradesTo != null) {
|
||||
if (rejectionReasons.all { it == RejectionReason.Obsoleted } && construction.upgradesTo != null) {
|
||||
// I'd love to use the '+=' operator but since 'inProgressConstructions[...]' can be null, kotlin doesn't allow me to
|
||||
if (!inProgressConstructions.contains(construction.upgradesTo)) {
|
||||
inProgressConstructions[construction.upgradesTo!!] = getWorkDone(constructionName)
|
||||
|
@ -21,7 +21,7 @@ interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
||||
|
||||
fun getProductionCost(civInfo: CivilizationInfo): Int
|
||||
fun getStatBuyCost(cityInfo: CityInfo, stat: Stat): Int?
|
||||
fun getRejectionReason(cityConstructions: CityConstructions): String
|
||||
fun getRejectionReasons(cityConstructions: CityConstructions): RejectionReasons
|
||||
fun postBuildEvent(cityConstructions: CityConstructions, boughtWith: Stat? = null): Boolean // Yes I'm hilarious.
|
||||
|
||||
fun getMatchingUniques(uniqueTemplate: String): Sequence<Unique> {
|
||||
@ -31,26 +31,25 @@ interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
||||
return uniqueObjects.any { it.placeholderText == uniqueTemplate }
|
||||
}
|
||||
|
||||
fun canBePurchasedWithStat(cityInfo: CityInfo, stat: Stat, ignoreCityRequirements: Boolean = false): Boolean {
|
||||
fun canBePurchasedWithStat(cityInfo: CityInfo?, stat: Stat): Boolean {
|
||||
if (stat in listOf(Stat.Production, Stat.Happiness)) return false
|
||||
if ("Cannot be purchased" in uniques) return false
|
||||
if (stat == Stat.Gold) return !uniques.contains("Unbuildable")
|
||||
// Can be purchased with [Stat] [cityFilter]
|
||||
if (getMatchingUniques("Can be purchased with [] []")
|
||||
.any { it.params[0] == stat.name && (ignoreCityRequirements || cityInfo.matchesFilter(it.params[1])) }
|
||||
.any { it.params[0] == stat.name && (cityInfo != null && cityInfo.matchesFilter(it.params[1])) }
|
||||
) return true
|
||||
// Can be purchased for [amount] [Stat] [cityFilter]
|
||||
if (getMatchingUniques("Can be purchased for [] [] []")
|
||||
.any { it.params[1] == stat.name && ( ignoreCityRequirements || cityInfo.matchesFilter(it.params[2])) }
|
||||
.any { it.params[1] == stat.name && (cityInfo != null && cityInfo.matchesFilter(it.params[2])) }
|
||||
) return true
|
||||
return false
|
||||
}
|
||||
|
||||
/** Checks if the construction should be purchasable, not whether it can be bought with a stat at all */
|
||||
fun isPurchasable(cityConstructions: CityConstructions): Boolean {
|
||||
val rejectionReason = getRejectionReason(cityConstructions)
|
||||
return rejectionReason == ""
|
||||
|| rejectionReason == "Can only be purchased"
|
||||
val rejectionReasons = getRejectionReasons(cityConstructions)
|
||||
return rejectionReasons.all { it == RejectionReason.Unbuildable }
|
||||
}
|
||||
|
||||
fun canBePurchasedWithAnyStat(cityInfo: CityInfo): Boolean {
|
||||
@ -81,6 +80,100 @@ interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
||||
|
||||
|
||||
|
||||
|
||||
class RejectionReasons(): HashSet<RejectionReason>() {
|
||||
private val techPolicyEraWonderRequirements = hashSetOf(
|
||||
RejectionReason.Obsoleted,
|
||||
RejectionReason.RequiresTech,
|
||||
RejectionReason.RequiresPolicy,
|
||||
RejectionReason.MorePolicyBranches,
|
||||
RejectionReason.RequiresBuildingInSomeCity
|
||||
)
|
||||
fun filterTechPolicyEraWonderRequirements(): HashSet<RejectionReason> {
|
||||
return filterNot { it in techPolicyEraWonderRequirements }.toHashSet()
|
||||
}
|
||||
|
||||
private val reasonsToDefinitivelyRemoveFromQueue = hashSetOf(
|
||||
RejectionReason.Obsoleted,
|
||||
RejectionReason.WonderAlreadyBuilt,
|
||||
RejectionReason.NationalWonderAlreadyBuilt,
|
||||
RejectionReason.CannotBeBuiltWith,
|
||||
RejectionReason.ReachedBuildCap
|
||||
)
|
||||
fun hasAReasonToBeRemovedFromQueue(): Boolean {
|
||||
return any { it in reasonsToDefinitivelyRemoveFromQueue }
|
||||
}
|
||||
|
||||
private val orderOfErrorMessages = listOf(
|
||||
RejectionReason.WonderBeingBuiltElsewhere,
|
||||
RejectionReason.NationalWonderBeingBuiltElsewhere,
|
||||
RejectionReason.RequiresBuildingInAllCities,
|
||||
RejectionReason.RequiresBuildingInThisCity,
|
||||
RejectionReason.RequiresBuildingInSomeCity,
|
||||
RejectionReason.PopulationRequirement,
|
||||
RejectionReason.ConsumesResources,
|
||||
RejectionReason.CanOnlyBePurchased
|
||||
)
|
||||
fun getMostImportantRejectionReason(): String? {
|
||||
return orderOfErrorMessages.firstOrNull { it in this }?.errorMessage
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum class RejectionReason(val shouldShow: Boolean, var errorMessage: String) {
|
||||
AlreadyBuilt(false, "Building already built in this city"),
|
||||
Unbuildable(false, "Unbuildable"),
|
||||
CanOnlyBePurchased(true, "Can only be purchased"),
|
||||
ShouldNotBeDisplayed(false, "Should not be displayed"),
|
||||
|
||||
DisabledBySetting(false, "Disabled by setting"),
|
||||
HiddenWithoutVictory(false, "Hidden because a victory type has been disabled"),
|
||||
|
||||
MustBeOnTile(false, "Must be on a specific tile"),
|
||||
MustNotBeOnTile(false, "Must not be on a specific tile"),
|
||||
MustBeNextToTile(false, "Must be next to a specific tile"),
|
||||
MustNotBeNextToTile(false, "Must not be next to a specific tile"),
|
||||
MustOwnTile(false, "Must own a specific tile closeby"),
|
||||
WaterUnitsInCoastalCities(false, "May only built water units in coastal cities"),
|
||||
CanOnlyBeBuiltInSpecificCities(false, "Can only be built in specific cities"),
|
||||
|
||||
UniqueToOtherNation(false, "Unique to another nation"),
|
||||
ReplacedByOurUnique(false, "Our unique replaces this"),
|
||||
|
||||
Obsoleted(false, "Obsolete"),
|
||||
RequiresTech(false, "Required tech not researched"),
|
||||
RequiresPolicy(false, "Requires a specific policy!"),
|
||||
UnlockedWithEra(false, "Unlocked when reacing a specific era"),
|
||||
MorePolicyBranches(false, "Hidden until more policy branches are fully adopted"),
|
||||
|
||||
RequiresNearbyResource(false, "Requires a certain resource being exploited nearby"),
|
||||
InvalidRequiredBuilding(false, "Required building does not exist in ruleSet!"),
|
||||
CannotBeBuiltWith(false, "Cannot be built at the same time as another building already built"),
|
||||
|
||||
RequiresBuildingInThisCity(true, "Requires a specific building in this city!"),
|
||||
RequiresBuildingInAllCities(true, "Requires a specific building in all cities!"),
|
||||
RequiresBuildingInSomeCity(true, "Requires a specific building anywhere in your empire!"),
|
||||
|
||||
WonderAlreadyBuilt(false, "Wonder already built"),
|
||||
NationalWonderAlreadyBuilt(false, "National Wonder already built"),
|
||||
WonderBeingBuiltElsewhere(true, "Wonder is being built elsewhere"),
|
||||
NationalWonderBeingBuiltElsewhere(true, "National Wonder is being built elsewhere"),
|
||||
CityStateWonder(false, "No Wonders for city-states"),
|
||||
CityStateNationalWonder(false, "No National Wonders for city-states"),
|
||||
WonderDisabledEra(false, "This Wonder is disabled when starting in this era"),
|
||||
|
||||
ReachedBuildCap(false, "Don't need to build any more of these!"),
|
||||
|
||||
ConsumesResources(true, "Consumes resources which you are lacking"),
|
||||
|
||||
PopulationRequirement(true, "Requires more population"),
|
||||
|
||||
NoSettlerForOneCityPlayers(false, "No settlers for city-states or one-city challangers");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
open class PerpetualConstruction(override var name: String, val description: String) : IConstruction {
|
||||
|
||||
override fun shouldBeDisplayed(cityConstructions: CityConstructions) = isBuildable(cityConstructions)
|
||||
|
@ -927,10 +927,7 @@ class CivilizationInfo {
|
||||
addNotification( "[${givingCityState.civName}] gave us a [${giftedUnit.name}] as a gift!", locations, givingCityState.civName, giftedUnit.name)
|
||||
}
|
||||
|
||||
fun turnsForGreatPersonFromCityState(): Int = ((40 + -2 + Random().nextInt(5)) * gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
||||
// There seems to be some randomness in the amount of turns between receiving each great person,
|
||||
// but I have no idea what the actual lower and upper bound are, so this is just an approximation
|
||||
|
||||
fun turnsForGreatPersonFromCityState(): Int = ((37 + Random().nextInt(7)) * gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
||||
|
||||
fun getAllyCiv() = allyCivName
|
||||
|
||||
|
@ -2,11 +2,10 @@ package com.unciv.models.ruleset
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.city.INonPerpetualConstruction
|
||||
import com.unciv.logic.city.*
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.Counter
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.stats.NamedStats
|
||||
import com.unciv.models.stats.Stat
|
||||
@ -228,7 +227,7 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText {
|
||||
|
||||
if (cost > 0) {
|
||||
val stats = mutableListOf("$cost${Fonts.production}")
|
||||
if (canBePurchasedWithStat(CityInfo(), Stat.Gold, true)) {
|
||||
if (canBePurchasedWithStat(null, Stat.Gold)) {
|
||||
stats += "${getBaseGoldCost(UncivGame.Current.gameInfo.currentPlayerCiv).toInt() / 10 * 10}${Fonts.gold}"
|
||||
}
|
||||
textList += FormattedLine(stats.joinToString(", ", "{Cost}: "))
|
||||
@ -352,13 +351,13 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText {
|
||||
}
|
||||
|
||||
|
||||
override fun canBePurchasedWithStat(cityInfo: CityInfo, stat: Stat, ignoreCityRequirements: Boolean): Boolean {
|
||||
override fun canBePurchasedWithStat(cityInfo: CityInfo?, stat: Stat): Boolean {
|
||||
if (stat == Stat.Gold && isAnyWonder()) return false
|
||||
// May buy [buildingFilter] buildings for [amount] [Stat] [cityFilter]
|
||||
if (!ignoreCityRequirements && cityInfo.getMatchingUniques("May buy [] buildings for [] [] []")
|
||||
if (cityInfo != null && cityInfo.getMatchingUniques("May buy [] buildings for [] [] []")
|
||||
.any { it.params[2] == stat.name && matchesFilter(it.params[0]) && cityInfo.matchesFilter(it.params[3]) }
|
||||
) return true
|
||||
return super.canBePurchasedWithStat(cityInfo, stat, ignoreCityRequirements)
|
||||
return super.canBePurchasedWithStat(cityInfo, stat)
|
||||
}
|
||||
|
||||
override fun getBaseBuyCost(cityInfo: CityInfo, stat: Stat): Int? {
|
||||
@ -408,192 +407,264 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText {
|
||||
override fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean {
|
||||
if (cityConstructions.isBeingConstructedOrEnqueued(name))
|
||||
return false
|
||||
val rejectionReason = getRejectionReason(cityConstructions)
|
||||
return rejectionReason == ""
|
||||
|| rejectionReason.startsWith("Requires")
|
||||
|| rejectionReason.startsWith("Consumes")
|
||||
|| rejectionReason.endsWith("Wonder is being built elsewhere")
|
||||
|| rejectionReason == "Can only be purchased"
|
||||
val rejectionReasons = getRejectionReasons(cityConstructions)
|
||||
return rejectionReasons.none { !it.shouldShow }
|
||||
|| (
|
||||
canBePurchasedWithAnyStat(cityConstructions.cityInfo)
|
||||
&& rejectionReasons.all { it == RejectionReason.Unbuildable }
|
||||
)
|
||||
}
|
||||
|
||||
override fun getRejectionReason(construction: CityConstructions): String {
|
||||
if (construction.isBuilt(name)) return "Already built"
|
||||
// for buildings that are created as side effects of other things, and not directly built
|
||||
// unless they can be bought with faith
|
||||
if (uniques.contains("Unbuildable")) {
|
||||
if (canBePurchasedWithAnyStat(construction.cityInfo))
|
||||
return "Can only be purchased"
|
||||
return "Unbuildable"
|
||||
}
|
||||
override fun getRejectionReasons(cityConstructions: CityConstructions): RejectionReasons {
|
||||
val rejectionReasons = RejectionReasons()
|
||||
val cityCenter = cityConstructions.cityInfo.getCenterTile()
|
||||
val civInfo = cityConstructions.cityInfo.civInfo
|
||||
val ruleSet = civInfo.gameInfo.ruleSet
|
||||
|
||||
if (cityConstructions.isBuilt(name))
|
||||
rejectionReasons.add(RejectionReason.AlreadyBuilt)
|
||||
// for buildings that are created as side effects of other things, and not directly built,
|
||||
// or for buildings that can only be bought
|
||||
if (uniques.contains("Unbuildable"))
|
||||
rejectionReasons.add(RejectionReason.Unbuildable)
|
||||
|
||||
val cityCenter = construction.cityInfo.getCenterTile()
|
||||
val civInfo = construction.cityInfo.civInfo
|
||||
|
||||
// This overrides the others
|
||||
if (uniqueObjects
|
||||
.any {
|
||||
it.placeholderText == "Not displayed as an available construction unless [] is built"
|
||||
&& !construction.containsBuildingOrEquivalent(it.params[0])
|
||||
for (unique in uniqueObjects) {
|
||||
when (unique.placeholderText) {
|
||||
// Deprecated since 3.16.11, replace with "Not displayed [...] construction without []"
|
||||
"Not displayed as an available construction unless [] is built" ->
|
||||
if (!cityConstructions.containsBuildingOrEquivalent(unique.params[0]))
|
||||
rejectionReasons.add(RejectionReason.ShouldNotBeDisplayed)
|
||||
//
|
||||
|
||||
"Not displayed as an available construction without []" ->
|
||||
if (unique.params[0] in ruleSet.tileResources && !civInfo.hasResource(unique.params[0])
|
||||
|| unique.params[0] in ruleSet.buildings && !cityConstructions.containsBuildingOrEquivalent(unique.params[0])
|
||||
|| unique.params[0] in ruleSet.technologies && !civInfo.tech.isResearched(unique.params[0])
|
||||
|| unique.params[0] in ruleSet.policies && !civInfo.policies.isAdopted(unique.params[0])
|
||||
)
|
||||
rejectionReasons.add(RejectionReason.ShouldNotBeDisplayed)
|
||||
|
||||
"Enables nuclear weapon" -> if (!cityConstructions.cityInfo.civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled)
|
||||
rejectionReasons.add(RejectionReason.DisabledBySetting)
|
||||
|
||||
"Must be on []" ->
|
||||
if (!cityCenter.matchesTerrainFilter(unique.params[0], civInfo))
|
||||
rejectionReasons.add(RejectionReason.MustBeOnTile.apply { errorMessage = unique.text })
|
||||
|
||||
"Must not be on []" ->
|
||||
if (cityCenter.matchesTerrainFilter(unique.params[0], civInfo))
|
||||
rejectionReasons.add(RejectionReason.MustNotBeOnTile.apply { errorMessage = unique.text })
|
||||
|
||||
"Must be next to []" ->
|
||||
if (// Fresh water is special, in that rivers are not tiles themselves but also fit the filter.
|
||||
!(unique.params[0] == "Fresh water" && cityCenter.isAdjacentToRiver())
|
||||
&& cityCenter.getTilesInDistance(1).none { it.matchesFilter(unique.params[0], civInfo) }
|
||||
)
|
||||
rejectionReasons.add(RejectionReason.MustBeNextToTile.apply { errorMessage = unique.text })
|
||||
|
||||
"Must not be next to []" ->
|
||||
if (cityCenter.getTilesInDistance(1).any { it.matchesFilter(unique.params[0], civInfo) })
|
||||
rejectionReasons.add(RejectionReason.MustNotBeNextToTile.apply { errorMessage = unique.text })
|
||||
|
||||
"Must have an owned [] within [] tiles" ->
|
||||
if (cityCenter.getTilesInDistance(unique.params[1].toInt())
|
||||
.none { it.matchesFilter(unique.params[0], civInfo) && it.getOwner() == cityConstructions.cityInfo.civInfo }
|
||||
)
|
||||
rejectionReasons.add(RejectionReason.MustOwnTile.apply { errorMessage = unique.text })
|
||||
|
||||
// Deprecated since 3.16.11
|
||||
"Can only be built in annexed cities" ->
|
||||
if (
|
||||
cityConstructions.cityInfo.isPuppet
|
||||
|| cityConstructions.cityInfo.civInfo.civName == cityConstructions.cityInfo.foundingCiv
|
||||
)
|
||||
rejectionReasons.add(RejectionReason.CanOnlyBeBuiltInSpecificCities.apply { errorMessage = unique.text })
|
||||
//
|
||||
|
||||
"Can only be built []" ->
|
||||
if (!cityConstructions.cityInfo.matchesFilter(unique.params[0]))
|
||||
rejectionReasons.add(RejectionReason.CanOnlyBeBuiltInSpecificCities.apply { errorMessage = unique.text })
|
||||
|
||||
"Obsolete with []" ->
|
||||
if (civInfo.tech.isResearched(unique.params[0]))
|
||||
rejectionReasons.add(RejectionReason.Obsoleted.apply { errorMessage = unique.text })
|
||||
|
||||
Constants.hiddenWithoutReligionUnique ->
|
||||
if (!civInfo.gameInfo.hasReligionEnabled())
|
||||
rejectionReasons.add(RejectionReason.DisabledBySetting)
|
||||
}
|
||||
) return "Should not be displayed"
|
||||
|
||||
for (unique in uniqueObjects.filter { it.placeholderText == "Not displayed as an available construction without []" }) {
|
||||
val filter = unique.params[0]
|
||||
if (filter in civInfo.gameInfo.ruleSet.tileResources && !construction.cityInfo.civInfo.hasResource(filter)
|
||||
|| filter in civInfo.gameInfo.ruleSet.buildings && !construction.containsBuildingOrEquivalent(filter))
|
||||
return "Should not be displayed"
|
||||
}
|
||||
|
||||
for (unique in uniqueObjects) when (unique.placeholderText) {
|
||||
"Enables nuclear weapon" -> if(!construction.cityInfo.civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled) return "Disabled by setting"
|
||||
"Must be on []" -> if (!cityCenter.matchesTerrainFilter(unique.params[0], civInfo)) return unique.text
|
||||
"Must not be on []" -> if (cityCenter.matchesTerrainFilter(unique.params[0], civInfo)) return unique.text
|
||||
"Must be next to []" -> if (!(unique.params[0] == "Fresh water" && cityCenter.isAdjacentToRiver()) // Fresh water is special, in that rivers are not tiles themselves but also fit the filter.
|
||||
&& cityCenter.getTilesInDistance(1).none { it.matchesFilter(unique.params[0], civInfo) }) return unique.text
|
||||
"Must not be next to []" -> if (cityCenter.getTilesInDistance(1).any { it.matchesFilter(unique.params[0], civInfo) }) return unique.text
|
||||
"Must have an owned [] within [] tiles" -> if (cityCenter.getTilesInDistance(unique.params[1].toInt()).none {
|
||||
it.matchesFilter(unique.params[0], civInfo) && it.getOwner() == construction.cityInfo.civInfo
|
||||
}) return unique.text
|
||||
// Deprecated since 3.16.11
|
||||
"Can only be built in annexed cities" -> if (construction.cityInfo.isPuppet
|
||||
|| construction.cityInfo.civInfo.civName == construction.cityInfo.foundingCiv) return unique.text
|
||||
//
|
||||
"Can only be built []" -> if (!construction.cityInfo.matchesFilter(unique.params[0])) return unique.text
|
||||
"Obsolete with []" -> if (civInfo.tech.isResearched(unique.params[0])) return unique.text
|
||||
Constants.hiddenWithoutReligionUnique -> if (!civInfo.gameInfo.hasReligionEnabled()) return unique.text
|
||||
}
|
||||
|
||||
if (uniqueTo != null && uniqueTo != civInfo.civName) return "Unique to $uniqueTo"
|
||||
if (uniqueTo != null && uniqueTo != civInfo.civName)
|
||||
rejectionReasons.add(RejectionReason.UniqueToOtherNation.apply { errorMessage = "Unique to $uniqueTo"})
|
||||
|
||||
if (civInfo.gameInfo.ruleSet.buildings.values.any { it.uniqueTo == civInfo.civName && it.replaces == name })
|
||||
return "Our unique building replaces this"
|
||||
if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!)) return "$requiredTech not researched"
|
||||
rejectionReasons.add(RejectionReason.ReplacedByOurUnique)
|
||||
|
||||
if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!))
|
||||
rejectionReasons.add(RejectionReason.RequiresTech.apply { "$requiredTech not researched!"})
|
||||
|
||||
for (unique in uniqueObjects.filter { it.placeholderText == "Unlocked with []" })
|
||||
if (civInfo.tech.researchedTechnologies.none { it.era() == unique.params[0] || it.name == unique.params[0] }
|
||||
&& !civInfo.policies.isAdopted(unique.params[0]))
|
||||
return unique.text
|
||||
for (unique in uniqueObjects) {
|
||||
if (unique.placeholderText != "Unlocked with []" && unique.placeholderText != "Requires []") continue
|
||||
val filter = unique.params[0]
|
||||
when {
|
||||
ruleSet.technologies.contains(filter) ->
|
||||
if (!civInfo.tech.isResearched(filter))
|
||||
rejectionReasons.add(RejectionReason.RequiresTech.apply { errorMessage = unique.text })
|
||||
ruleSet.policies.contains(filter) ->
|
||||
if (!civInfo.policies.isAdopted(filter))
|
||||
rejectionReasons.add(RejectionReason.RequiresPolicy.apply { errorMessage = unique.text })
|
||||
// ToDo: Fix this when eras.json is required
|
||||
ruleSet.getEraNumber(filter) != -1 ->
|
||||
if (civInfo.getEraNumber() < ruleSet.getEraNumber(filter))
|
||||
rejectionReasons.add(RejectionReason.UnlockedWithEra.apply { errorMessage = unique.text })
|
||||
ruleSet.buildings.contains(filter) ->
|
||||
if (civInfo.cities.none { it.cityConstructions.containsBuildingOrEquivalent(filter) })
|
||||
rejectionReasons.add(RejectionReason.RequiresBuildingInSomeCity.apply { errorMessage = unique.text })
|
||||
}
|
||||
}
|
||||
|
||||
// Regular wonders
|
||||
if (isWonder) {
|
||||
if (civInfo.gameInfo.getCities().any { it.cityConstructions.isBuilt(name) })
|
||||
return "Wonder is already built"
|
||||
rejectionReasons.add(RejectionReason.WonderAlreadyBuilt)
|
||||
|
||||
if (civInfo.cities.any { it != construction.cityInfo && it.cityConstructions.isBeingConstructedOrEnqueued(name) })
|
||||
return "Wonder is being built elsewhere"
|
||||
if (civInfo.cities.any { it != cityConstructions.cityInfo && it.cityConstructions.isBeingConstructedOrEnqueued(name) })
|
||||
rejectionReasons.add(RejectionReason.WonderBeingBuiltElsewhere)
|
||||
|
||||
if (civInfo.isCityState())
|
||||
return "No world wonders for city-states"
|
||||
rejectionReasons.add(RejectionReason.CityStateWonder)
|
||||
|
||||
val ruleSet = civInfo.gameInfo.ruleSet
|
||||
val startingEra = civInfo.gameInfo.gameParameters.startingEra
|
||||
if (startingEra in ruleSet.eras && name in ruleSet.eras[startingEra]!!.startingObsoleteWonders)
|
||||
return "Wonder is disabled when starting in this era"
|
||||
rejectionReasons.add(RejectionReason.WonderDisabledEra)
|
||||
}
|
||||
|
||||
|
||||
// National wonders
|
||||
if (isNationalWonder) {
|
||||
if (civInfo.cities.any { it.cityConstructions.isBuilt(name) })
|
||||
return "National Wonder is already built"
|
||||
if (requiredBuildingInAllCities != null && civInfo.gameInfo.ruleSet.buildings[requiredBuildingInAllCities!!] == null)
|
||||
return "Required building in all cities does not exist in the ruleset!"
|
||||
if (requiredBuildingInAllCities != null
|
||||
rejectionReasons.add(RejectionReason.NationalWonderAlreadyBuilt)
|
||||
|
||||
if (requiredBuildingInAllCities != null && civInfo.gameInfo.ruleSet.buildings[requiredBuildingInAllCities!!] == null) {
|
||||
rejectionReasons.add(RejectionReason.InvalidRequiredBuilding)
|
||||
} else {
|
||||
if (requiredBuildingInAllCities != null
|
||||
&& civInfo.cities.any {
|
||||
!it.isPuppet && !it.cityConstructions
|
||||
.containsBuildingOrEquivalent(requiredBuildingInAllCities!!)
|
||||
})
|
||||
return "Requires a [${civInfo.getEquivalentBuilding(requiredBuildingInAllCities!!)}] in all cities"
|
||||
if (civInfo.cities.any { it != construction.cityInfo && it.cityConstructions.isBeingConstructedOrEnqueued(name) })
|
||||
return "National Wonder is being built elsewhere"
|
||||
if (civInfo.isCityState())
|
||||
return "No national wonders for city-states"
|
||||
.containsBuildingOrEquivalent(requiredBuildingInAllCities!!)
|
||||
}
|
||||
) {
|
||||
rejectionReasons.add(RejectionReason.RequiresBuildingInAllCities
|
||||
.apply { errorMessage = "Requires a [${civInfo.getEquivalentBuilding(requiredBuildingInAllCities!!)}] in all cities"})
|
||||
}
|
||||
|
||||
if (civInfo.cities.any { it != cityConstructions.cityInfo && it.cityConstructions.isBeingConstructedOrEnqueued(name) })
|
||||
rejectionReasons.add(RejectionReason.NationalWonderBeingBuiltElsewhere)
|
||||
|
||||
if (civInfo.isCityState())
|
||||
rejectionReasons.add(RejectionReason.CityStateNationalWonder)
|
||||
}
|
||||
}
|
||||
|
||||
if ("Spaceship part" in uniques) {
|
||||
if (!civInfo.hasUnique("Enables construction of Spaceship parts")) return "Apollo project not built!"
|
||||
if (civInfo.victoryManager.unconstructedSpaceshipParts()[name] == 0) return "Don't need to build any more of these!"
|
||||
if (!civInfo.hasUnique("Enables construction of Spaceship parts"))
|
||||
rejectionReasons.add(
|
||||
RejectionReason.RequiresBuildingInSomeCity.apply { errorMessage = "Apollo project not built!" }
|
||||
)
|
||||
|
||||
if (civInfo.victoryManager.unconstructedSpaceshipParts()[name] == 0)
|
||||
rejectionReasons.add(RejectionReason.ReachedBuildCap)
|
||||
}
|
||||
|
||||
for (unique in uniqueObjects) when (unique.placeholderText) {
|
||||
"Requires []" -> {
|
||||
val filter = unique.params[0]
|
||||
if (filter in civInfo.gameInfo.ruleSet.buildings) {
|
||||
if (civInfo.cities.none { it.cityConstructions.containsBuildingOrEquivalent(filter) }) return unique.text // Wonder is not built
|
||||
} else if (!civInfo.policies.isAdopted(filter)) return "Policy is not adopted" // this reason should not be displayed
|
||||
}
|
||||
|
||||
"Requires a [] in this city" -> {
|
||||
val filter = unique.params[0]
|
||||
if (civInfo.gameInfo.ruleSet.buildings.containsKey(filter)
|
||||
&& !construction.containsBuildingOrEquivalent(filter))
|
||||
return "Requires a [${civInfo.getEquivalentBuilding(filter)}] in this city" // replace with civ-specific building for user
|
||||
if (civInfo.gameInfo.ruleSet.buildings.containsKey(filter) && !cityConstructions.containsBuildingOrEquivalent(filter))
|
||||
rejectionReasons.add(
|
||||
// replace with civ-specific building for user
|
||||
RejectionReason.RequiresBuildingInThisCity.apply { errorMessage = "Requires a [${civInfo.getEquivalentBuilding(filter)}] in this city" }
|
||||
)
|
||||
}
|
||||
|
||||
"Requires a [] in all cities" -> {
|
||||
val filter = unique.params[0]
|
||||
if (civInfo.gameInfo.ruleSet.buildings.containsKey(filter)
|
||||
&& civInfo.cities.any { !it.isPuppet && !it.cityConstructions.containsBuildingOrEquivalent(unique.params[0]) })
|
||||
return "Requires a [${civInfo.getEquivalentBuilding(unique.params[0])}] in all cities" // replace with civ-specific building for user
|
||||
}
|
||||
"Hidden until [] social policy branches have been completed" -> {
|
||||
if (construction.cityInfo.civInfo.getCompletedPolicyBranchesCount() < unique.params[0].toInt()) {
|
||||
return "Should not be displayed"
|
||||
if (civInfo.gameInfo.ruleSet.buildings.containsKey(filter)
|
||||
&& civInfo.cities.any {
|
||||
!it.isPuppet && !it.cityConstructions.containsBuildingOrEquivalent(unique.params[0])
|
||||
}
|
||||
) {
|
||||
rejectionReasons.add(
|
||||
// replace with civ-specific building for user
|
||||
RejectionReason.RequiresBuildingInAllCities.apply {
|
||||
errorMessage = "Requires a [${civInfo.getEquivalentBuilding(unique.params[0])}] in all cities"
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"Hidden until [] social policy branches have been completed" -> {
|
||||
if (cityConstructions.cityInfo.civInfo.getCompletedPolicyBranchesCount() < unique.params[0].toInt())
|
||||
rejectionReasons.add(RejectionReason.MorePolicyBranches.apply { errorMessage = unique.text })
|
||||
}
|
||||
"Hidden when [] Victory is disabled" -> {
|
||||
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.valueOf(unique.params[0]))) {
|
||||
return unique.text
|
||||
}
|
||||
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.valueOf(unique.params[0])))
|
||||
rejectionReasons.add(RejectionReason.HiddenWithoutVictory.apply { errorMessage = unique.text })
|
||||
}
|
||||
// Deprecated since 3.15.14
|
||||
"Hidden when cultural victory is disabled" -> {
|
||||
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.Cultural)) {
|
||||
return unique.text
|
||||
}
|
||||
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.Cultural))
|
||||
rejectionReasons.add(RejectionReason.HiddenWithoutVictory.apply { errorMessage = unique.text })
|
||||
}
|
||||
//
|
||||
}
|
||||
|
||||
if (requiredBuilding != null && !construction.containsBuildingOrEquivalent(requiredBuilding!!)) {
|
||||
if (!civInfo.gameInfo.ruleSet.buildings.containsKey(requiredBuilding!!))
|
||||
return "Requires a [${requiredBuilding}] in this city, which doesn't seem to exist in this ruleset!"
|
||||
return "Requires a [${civInfo.getEquivalentBuilding(requiredBuilding!!)}] in this city"
|
||||
if (requiredBuilding != null && !cityConstructions.containsBuildingOrEquivalent(requiredBuilding!!)) {
|
||||
if (!civInfo.gameInfo.ruleSet.buildings.containsKey(requiredBuilding!!)) {
|
||||
rejectionReasons.add(
|
||||
RejectionReason.InvalidRequiredBuilding
|
||||
.apply { errorMessage = "Requires a [${requiredBuilding}] in this city, which doesn't seem to exist in this ruleset!" }
|
||||
)
|
||||
} else {
|
||||
rejectionReasons.add(
|
||||
RejectionReason.RequiresBuildingInThisCity.apply { errorMessage = "Requires a [${civInfo.getEquivalentBuilding(requiredBuilding!!)}] in this city"}
|
||||
)
|
||||
}
|
||||
}
|
||||
// cannotBeBuiltWith is Deprecated as of 3.15.19
|
||||
val cannotBeBuiltWith = uniqueObjects
|
||||
.firstOrNull { it.placeholderText == "Cannot be built with []" }
|
||||
?.params?.get(0)
|
||||
?: this.cannotBeBuiltWith
|
||||
if (cannotBeBuiltWith != null && construction.isBuilt(cannotBeBuiltWith))
|
||||
return "Cannot be built with [$cannotBeBuiltWith]"
|
||||
if (cannotBeBuiltWith != null && cityConstructions.isBuilt(cannotBeBuiltWith))
|
||||
rejectionReasons.add(RejectionReason.CannotBeBuiltWith.apply { errorMessage = "Cannot be built with [$cannotBeBuiltWith]" })
|
||||
|
||||
for ((resource, amount) in getResourceRequirements())
|
||||
if (civInfo.getCivResourcesByName()[resource]!! < amount) {
|
||||
return if (amount == 1) "Consumes 1 [$resource]" // Again, to preserve existing translations
|
||||
else "Consumes [$amount] [$resource]"
|
||||
rejectionReasons.add(RejectionReason.ConsumesResources.apply {
|
||||
errorMessage = "Consumes [$amount] [$resource]"
|
||||
})
|
||||
}
|
||||
|
||||
if (requiredNearbyImprovedResources != null) {
|
||||
val containsResourceWithImprovement = construction.cityInfo.getWorkableTiles()
|
||||
.any {
|
||||
it.resource != null
|
||||
&& requiredNearbyImprovedResources!!.contains(it.resource!!)
|
||||
&& it.getOwner() == civInfo
|
||||
&& (it.getTileResource().improvement == it.improvement || it.getTileImprovement()?.isGreatImprovement() == true || it.isCityCenter())
|
||||
}
|
||||
if (!containsResourceWithImprovement) return "Nearby $requiredNearbyImprovedResources required"
|
||||
val containsResourceWithImprovement = cityConstructions.cityInfo.getWorkableTiles()
|
||||
.any {
|
||||
it.resource != null
|
||||
&& requiredNearbyImprovedResources!!.contains(it.resource!!)
|
||||
&& it.getOwner() == civInfo
|
||||
&& (it.getTileResource().improvement == it.improvement || it.isCityCenter()
|
||||
|| (it.getTileImprovement()?.isGreatImprovement() == true && it.getTileResource().resourceType == ResourceType.Strategic)
|
||||
)
|
||||
}
|
||||
if (!containsResourceWithImprovement)
|
||||
rejectionReasons.add(RejectionReason.RequiresNearbyResource.apply { errorMessage = "Nearby $requiredNearbyImprovedResources required" })
|
||||
}
|
||||
|
||||
|
||||
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.Scientific)
|
||||
&& "Enables construction of Spaceship parts" in uniques)
|
||||
return "Can't construct spaceship parts if scientific victory is not enabled!"
|
||||
|
||||
return ""
|
||||
|
||||
return rejectionReasons
|
||||
}
|
||||
|
||||
override fun isBuildable(cityConstructions: CityConstructions): Boolean =
|
||||
getRejectionReason(cityConstructions) == ""
|
||||
getRejectionReasons(cityConstructions).isEmpty()
|
||||
|
||||
override fun postBuildEvent(cityConstructions: CityConstructions, boughtWith: Stat?): Boolean {
|
||||
val civInfo = cityConstructions.cityInfo.civInfo
|
||||
|
@ -2,9 +2,7 @@ package com.unciv.models.ruleset.unit
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.city.INonPerpetualConstruction
|
||||
import com.unciv.logic.city.*
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
@ -126,7 +124,7 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
|
||||
if (cost > 0) {
|
||||
stats.clear()
|
||||
stats += "$cost${Fonts.production}"
|
||||
if (canBePurchasedWithStat(CityInfo(), Stat.Gold, true))
|
||||
if (canBePurchasedWithStat(null, Stat.Gold))
|
||||
stats += "${getBaseGoldCost(UncivGame.Current.gameInfo.currentPlayerCiv).toInt() / 10 * 10}${Fonts.gold}"
|
||||
textList += FormattedLine(stats.joinToString(", ", "{Cost}: "))
|
||||
}
|
||||
@ -216,13 +214,9 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
|
||||
return productionCost.toInt()
|
||||
}
|
||||
|
||||
override fun canBePurchasedWithStat(
|
||||
cityInfo: CityInfo,
|
||||
stat: Stat,
|
||||
ignoreCityRequirements: Boolean
|
||||
): Boolean {
|
||||
override fun canBePurchasedWithStat(cityInfo: CityInfo?, stat: Stat): Boolean {
|
||||
// May buy [unitFilter] units for [amount] [Stat] starting from the [eraName] at an increasing price ([amount])
|
||||
if (cityInfo.civInfo.getMatchingUniques("May buy [] units for [] [] [] starting from the [] at an increasing price ([])")
|
||||
if (cityInfo != null && cityInfo.civInfo.getMatchingUniques("May buy [] units for [] [] [] starting from the [] at an increasing price ([])")
|
||||
.any {
|
||||
matchesFilter(it.params[0])
|
||||
&& cityInfo.matchesFilter(it.params[3])
|
||||
@ -231,7 +225,7 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
|
||||
}
|
||||
) return true
|
||||
|
||||
return super.canBePurchasedWithStat(cityInfo, stat, ignoreCityRequirements)
|
||||
return super.canBePurchasedWithStat(cityInfo, stat)
|
||||
}
|
||||
|
||||
private fun getCostForConstructionsIncreasingInPrice(baseCost: Int, increaseCost: Int, previouslyBought: Int): Int {
|
||||
@ -285,84 +279,103 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
|
||||
fun getDisbandGold(civInfo: CivilizationInfo) = getBaseGoldCost(civInfo).toInt() / 20
|
||||
|
||||
override fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean {
|
||||
val rejectionReason = getRejectionReason(cityConstructions)
|
||||
return rejectionReason == ""
|
||||
|| rejectionReason.startsWith("Requires")
|
||||
|| rejectionReason.startsWith("Consumes")
|
||||
|| rejectionReason == "Can only be purchased"
|
||||
val rejectionReasons = getRejectionReasons(cityConstructions)
|
||||
return rejectionReasons.none { !it.shouldShow }
|
||||
|| (
|
||||
canBePurchasedWithAnyStat(cityConstructions.cityInfo)
|
||||
&& rejectionReasons.all { it == RejectionReason.Unbuildable }
|
||||
)
|
||||
}
|
||||
|
||||
override fun getRejectionReason(cityConstructions: CityConstructions): String {
|
||||
override fun getRejectionReasons(cityConstructions: CityConstructions): RejectionReasons {
|
||||
val rejectionReasons = RejectionReasons()
|
||||
if (isWaterUnit() && !cityConstructions.cityInfo.isCoastal())
|
||||
return "Can only build water units in coastal cities"
|
||||
rejectionReasons.add(RejectionReason.WaterUnitsInCoastalCities)
|
||||
val civInfo = cityConstructions.cityInfo.civInfo
|
||||
for (unique in uniqueObjects.filter { it.placeholderText == "Not displayed as an available construction without []" }) {
|
||||
val filter = unique.params[0]
|
||||
if (filter in civInfo.gameInfo.ruleSet.tileResources && !civInfo.hasResource(filter)
|
||||
|| filter in civInfo.gameInfo.ruleSet.buildings && !cityConstructions.containsBuildingOrEquivalent(filter))
|
||||
return "Should not be displayed"
|
||||
rejectionReasons.add(RejectionReason.ShouldNotBeDisplayed)
|
||||
}
|
||||
val civRejectionReason = getRejectionReason(civInfo)
|
||||
if (civRejectionReason != "") {
|
||||
if (civRejectionReason == "Unbuildable" && canBePurchasedWithAnyStat(cityConstructions.cityInfo))
|
||||
return "Can only be purchased"
|
||||
return civRejectionReason
|
||||
val civRejectionReasons = getRejectionReasons(civInfo)
|
||||
if (civRejectionReasons.isNotEmpty()) {
|
||||
rejectionReasons.addAll(civRejectionReasons)
|
||||
}
|
||||
for (unique in uniqueObjects.filter { it.placeholderText == "Requires at least [] population" })
|
||||
if (unique.params[0].toInt() > cityConstructions.cityInfo.population.population)
|
||||
return unique.text
|
||||
return ""
|
||||
rejectionReasons.add(RejectionReason.PopulationRequirement)
|
||||
return rejectionReasons
|
||||
}
|
||||
|
||||
/** @param ignoreTechPolicyRequirements: its `true` value is used when upgrading via ancient ruins,
|
||||
* as there we don't care whether we have the required tech, policy or building for the unit,
|
||||
* but do still care whether we have the resources required for the unit
|
||||
*/
|
||||
fun getRejectionReason(civInfo: CivilizationInfo, ignoreTechPolicyRequirements: Boolean = false): String {
|
||||
if (uniques.contains("Unbuildable")) return "Unbuildable"
|
||||
if (!ignoreTechPolicyRequirements && requiredTech != null && !civInfo.tech.isResearched(requiredTech!!)) return "$requiredTech not researched"
|
||||
if (!ignoreTechPolicyRequirements && obsoleteTech != null && civInfo.tech.isResearched(obsoleteTech!!)) return "Obsolete by $obsoleteTech"
|
||||
if (uniqueTo != null && uniqueTo != civInfo.civName) return "Unique to $uniqueTo"
|
||||
if (civInfo.gameInfo.ruleSet.units.values.any { it.uniqueTo == civInfo.civName && it.replaces == name })
|
||||
return "Our unique unit replaces this"
|
||||
if (!civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled && isNuclearWeapon()
|
||||
) return "Disabled by setting"
|
||||
fun getRejectionReasons(civInfo: CivilizationInfo): RejectionReasons {
|
||||
val rejectionReasons = RejectionReasons()
|
||||
val ruleSet = civInfo.gameInfo.ruleSet
|
||||
|
||||
if (uniques.contains("Unbuildable"))
|
||||
rejectionReasons.add(RejectionReason.Unbuildable)
|
||||
|
||||
if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!))
|
||||
rejectionReasons.add(RejectionReason.RequiresTech.apply { this.errorMessage = "$requiredTech not researched" })
|
||||
if (obsoleteTech != null && civInfo.tech.isResearched(obsoleteTech!!))
|
||||
rejectionReasons.add(RejectionReason.Obsoleted.apply { this.errorMessage = "Obsolete by $obsoleteTech" })
|
||||
|
||||
if (uniqueTo != null && uniqueTo != civInfo.civName)
|
||||
rejectionReasons.add(RejectionReason.UniqueToOtherNation.apply { this.errorMessage = "Unique to $uniqueTo" })
|
||||
if (ruleSet.units.values.any { it.uniqueTo == civInfo.civName && it.replaces == name })
|
||||
rejectionReasons.add(RejectionReason.ReplacedByOurUnique.apply { this.errorMessage = "Our unique unit replaces this" })
|
||||
|
||||
if (!civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled && isNuclearWeapon())
|
||||
rejectionReasons.add(RejectionReason.DisabledBySetting)
|
||||
|
||||
for (unique in uniqueObjects.filter { it.placeholderText == "Unlocked with []" })
|
||||
// ToDo: Clean this up when eras.json is required
|
||||
if ((civInfo.gameInfo.ruleSet.getEraNumber(unique.params[0]) != -1 && civInfo.getEraNumber() >= civInfo.gameInfo.ruleSet.getEraNumber(unique.params[0]))
|
||||
|| civInfo.hasTechOrPolicy(unique.params[0])
|
||||
) return unique.text
|
||||
|
||||
for (unique in uniqueObjects.filter { it.placeholderText == "Requires []" }) {
|
||||
for (unique in uniqueObjects) {
|
||||
if (unique.placeholderText != "Unlocked with []" && unique.placeholderText != "Requires []") continue
|
||||
val filter = unique.params[0]
|
||||
if (!ignoreTechPolicyRequirements && filter in civInfo.gameInfo.ruleSet.buildings) {
|
||||
if (civInfo.cities.none { it.cityConstructions.containsBuildingOrEquivalent(filter) }) return unique.text // Wonder is not built
|
||||
} else if (!ignoreTechPolicyRequirements && !civInfo.policies.isAdopted(filter)) return "Policy is not adopted"
|
||||
when {
|
||||
ruleSet.technologies.contains(filter) ->
|
||||
if (!civInfo.tech.isResearched(filter))
|
||||
rejectionReasons.add(RejectionReason.RequiresTech.apply { errorMessage = unique.text })
|
||||
ruleSet.policies.contains(filter) ->
|
||||
if (!civInfo.policies.isAdopted(filter))
|
||||
rejectionReasons.add(RejectionReason.RequiresPolicy.apply { errorMessage = unique.text })
|
||||
// ToDo: Fix this when eras.json is required
|
||||
ruleSet.getEraNumber(filter) != -1 ->
|
||||
if (civInfo.getEraNumber() < ruleSet.getEraNumber(filter))
|
||||
rejectionReasons.add(RejectionReason.UnlockedWithEra.apply { errorMessage = unique.text })
|
||||
ruleSet.buildings.contains(filter) ->
|
||||
if (civInfo.cities.none { it.cityConstructions.containsBuildingOrEquivalent(filter) })
|
||||
rejectionReasons.add(RejectionReason.RequiresBuildingInSomeCity.apply { errorMessage = unique.text })
|
||||
}
|
||||
}
|
||||
|
||||
for ((resource, amount) in getResourceRequirements())
|
||||
if (civInfo.getCivResourcesByName()[resource]!! < amount) {
|
||||
return if (amount == 1) "Consumes 1 [$resource]" // Again, to preserve existing translations
|
||||
else "Consumes [$amount] [$resource]"
|
||||
rejectionReasons.add(RejectionReason.ConsumesResources.apply {
|
||||
errorMessage = "Consumes [$amount] [$resource]"
|
||||
})
|
||||
}
|
||||
|
||||
if (uniques.contains(Constants.settlerUnique) && civInfo.isCityState()) return "No settler for city-states"
|
||||
if (uniques.contains(Constants.settlerUnique) && civInfo.isOneCityChallenger()) return "No settler for players in One City Challenge"
|
||||
return ""
|
||||
|
||||
if (uniques.contains(Constants.settlerUnique) &&
|
||||
(civInfo.isCityState() || civInfo.isOneCityChallenger())
|
||||
)
|
||||
rejectionReasons.add(RejectionReason.NoSettlerForOneCityPlayers)
|
||||
return rejectionReasons
|
||||
}
|
||||
|
||||
fun isBuildable(civInfo: CivilizationInfo) = getRejectionReason(civInfo) == ""
|
||||
fun isBuildable(civInfo: CivilizationInfo) = getRejectionReasons(civInfo).isEmpty()
|
||||
|
||||
override fun isBuildable(cityConstructions: CityConstructions): Boolean {
|
||||
return getRejectionReason(cityConstructions) == ""
|
||||
return getRejectionReasons(cityConstructions).isEmpty()
|
||||
}
|
||||
|
||||
fun isBuildableIgnoringTechs(civInfo: CivilizationInfo): Boolean {
|
||||
val rejectionReasons = getRejectionReasons(civInfo)
|
||||
return rejectionReasons.filterTechPolicyEraWonderRequirements().isEmpty()
|
||||
}
|
||||
|
||||
/** Preemptively as in: buildable without actually having the tech and/or policy required for it.
|
||||
* Still checks for resource use and other things
|
||||
*/
|
||||
fun isBuildableIgnoringTechs(civInfo: CivilizationInfo) =
|
||||
getRejectionReason(civInfo, true) == ""
|
||||
|
||||
override fun postBuildEvent(cityConstructions: CityConstructions, boughtWith: Stat?): Boolean {
|
||||
val civInfo = cityConstructions.cityInfo.civInfo
|
||||
@ -466,8 +479,10 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
|
||||
"non-air" -> !movesLikeAirUnits()
|
||||
|
||||
"Nuclear Weapon" -> isNuclearWeapon()
|
||||
"Great Person", "Great" -> isGreatPerson()
|
||||
// Deprecated as of 3.15.2
|
||||
"military water" -> isMilitary() && isWaterUnit()
|
||||
"military water" -> isMilitary() && isWaterUnit()
|
||||
//
|
||||
else -> {
|
||||
if (getType().matchesFilter(filter)) return true
|
||||
if (
|
||||
|
@ -165,19 +165,30 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
val useStoredProduction = entry is Building || !cityConstructions.isBeingConstructedOrEnqueued(entry.name)
|
||||
var buttonText = entry.name.tr() + cityConstructions.getTurnsToConstructionString(entry.name, useStoredProduction)
|
||||
for ((resource, amount) in entry.getResourceRequirements()) {
|
||||
buttonText += "\n" + (if (amount == 1) "Consumes 1 [$resource]"
|
||||
else "Consumes [$amount] [$resource]").tr()
|
||||
buttonText += "\n" + (
|
||||
if (amount == 1) "Consumes 1 [$resource]"
|
||||
else "Consumes [$amount] [$resource]"
|
||||
).tr()
|
||||
}
|
||||
|
||||
constructionButtonDTOList.add(ConstructionButtonDTO(entry, buttonText,
|
||||
entry.getRejectionReason(cityConstructions)))
|
||||
constructionButtonDTOList.add(
|
||||
ConstructionButtonDTO(
|
||||
entry,
|
||||
buttonText,
|
||||
entry.getRejectionReasons(cityConstructions).getMostImportantRejectionReason()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
for (specialConstruction in PerpetualConstruction.perpetualConstructionsMap.values
|
||||
.filter { it.shouldBeDisplayed(cityConstructions) }) {
|
||||
constructionButtonDTOList.add(ConstructionButtonDTO(specialConstruction,
|
||||
"Produce [${specialConstruction.name}]".tr()
|
||||
+ specialConstruction.getProductionTooltip(city)))
|
||||
.filter { it.shouldBeDisplayed(cityConstructions) }
|
||||
) {
|
||||
constructionButtonDTOList.add(
|
||||
ConstructionButtonDTO(
|
||||
specialConstruction,
|
||||
"Produce [${specialConstruction.name}]".tr() + specialConstruction.getProductionTooltip(city)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return constructionButtonDTOList
|
||||
@ -297,7 +308,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
Color.BROWN.cpy().lerp(Color.WHITE, 0.5f), Color.WHITE)
|
||||
}
|
||||
|
||||
private class ConstructionButtonDTO(val construction: IConstruction, val buttonText: String, val rejectionReason: String = "")
|
||||
private class ConstructionButtonDTO(val construction: IConstruction, val buttonText: String, val rejectionReason: String? = null)
|
||||
|
||||
private fun getConstructionButton(constructionButtonDTO: ConstructionButtonDTO): Table {
|
||||
val construction = constructionButtonDTO.construction
|
||||
@ -325,7 +336,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
pickConstructionButton.row()
|
||||
|
||||
// no rejection reason means we can build it!
|
||||
if (constructionButtonDTO.rejectionReason != "") {
|
||||
if (constructionButtonDTO.rejectionReason != null) {
|
||||
pickConstructionButton.color = Color.GRAY
|
||||
pickConstructionButton.add(constructionButtonDTO.rejectionReason.toLabel(Color.RED).apply { wrap = true })
|
||||
.colspan(pickConstructionButton.columns).fillX().left().padTop(2f)
|
||||
|
@ -769,8 +769,7 @@ object UnitActions {
|
||||
val giftAction = {
|
||||
if (recipient.isCityState()) {
|
||||
for (unique in unit.civInfo.getMatchingUniques("Gain [] Influence with a [] gift to a City-State")) {
|
||||
if ((unit.isGreatPerson() && unique.params[1] == "Great Person")
|
||||
|| unit.matchesFilter(unique.params[1])
|
||||
if (unit.matchesFilter(unique.params[1])
|
||||
) {
|
||||
recipient.getDiplomacyManager(unit.civInfo).addInfluence(unique.params[0].toFloat() - 5f)
|
||||
break
|
||||
|
Reference in New Issue
Block a user