mirror of
https://github.com/yairm210/Unciv.git
synced 2025-03-09 04:09:35 +07:00
Fix ModOptions unique parameter types not checked and "uniquetype" ModOptionsConstants (#10930)
* Kill evil ModOptionsConstants * UniqueFlag to EnumSet and add `NoConditionals` * Linting or import reorder * Fix ModOptions unique parameter types not checked * ModOptions Unique to suppress validation warnings * Silence spurious RulesetValidator complaints about Denmark * Revert "ModOptions Unique to suppress validation warnings"
This commit is contained in:
parent
9e9ffa51d4
commit
d25b1c8c41
@ -24,12 +24,12 @@ import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.civilization.managers.TechManager
|
||||
import com.unciv.logic.civilization.managers.TurnManager
|
||||
import com.unciv.logic.civilization.managers.VictoryManager
|
||||
import com.unciv.logic.github.Github.repoNameToFolderName
|
||||
import com.unciv.logic.map.CityDistanceData
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.Religion
|
||||
import com.unciv.models.metadata.GameParameters
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.Speed
|
||||
@ -39,7 +39,6 @@ import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.audio.MusicMood
|
||||
import com.unciv.ui.audio.MusicTrackChooserFlags
|
||||
import com.unciv.logic.github.Github.repoNameToFolderName
|
||||
import com.unciv.ui.screens.savescreens.Gzip
|
||||
import com.unciv.ui.screens.worldscreen.status.NextTurnProgress
|
||||
import com.unciv.utils.DebugUtils
|
||||
@ -287,7 +286,7 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
||||
|
||||
fun isReligionEnabled(): Boolean {
|
||||
val religionDisabledByRuleset = (ruleset.eras[gameParameters.startingEra]!!.hasUnique(UniqueType.DisablesReligion)
|
||||
|| ruleset.modOptions.uniques.contains(ModOptionsConstants.disableReligion))
|
||||
|| ruleset.modOptions.hasUnique(UniqueType.DisableReligion))
|
||||
return !religionDisabledByRuleset
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.metadata.GameParameters
|
||||
import com.unciv.models.metadata.GameSetupInfo
|
||||
import com.unciv.models.metadata.Player
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
@ -483,7 +482,7 @@ object GameStarter {
|
||||
settlerLikeUnits: Map<String, BaseUnit>
|
||||
) {
|
||||
// Adjust starting units for city states
|
||||
if (civ.isCityState() && !gameInfo.ruleset.modOptions.uniques.contains(ModOptionsConstants.allowCityStatesSpawnUnits)) {
|
||||
if (civ.isCityState() && !gameInfo.ruleset.modOptions.hasUnique(UniqueType.AllowCityStatesSpawnUnits)) {
|
||||
val startingSettlers = startingUnits.filter { settlerLikeUnits.contains(it) }
|
||||
|
||||
startingUnits.clear()
|
||||
|
@ -17,7 +17,6 @@ import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.logic.civilization.managers.EspionageManager
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.models.ruleset.MilestoneType
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.Policy
|
||||
import com.unciv.models.ruleset.PolicyBranch
|
||||
import com.unciv.models.ruleset.Victory
|
||||
@ -41,7 +40,7 @@ object NextTurnAutomation {
|
||||
TradeAutomation.respondToTradeRequests(civInfo)
|
||||
|
||||
if (civInfo.isMajorCiv()) {
|
||||
if (!civInfo.gameInfo.ruleset.modOptions.hasUnique(ModOptionsConstants.diplomaticRelationshipsCannotChange)) {
|
||||
if (!civInfo.gameInfo.ruleset.modOptions.hasUnique(UniqueType.DiplomaticRelationshipsCannotChange)) {
|
||||
DiplomacyAutomation.declareWar(civInfo)
|
||||
DiplomacyAutomation.offerPeaceTreaty(civInfo)
|
||||
DiplomacyAutomation.offerDeclarationOfFriendship(civInfo)
|
||||
@ -467,10 +466,10 @@ object NextTurnAutomation {
|
||||
val bestCity = civInfo.cities.filterNot { it.isPuppet }
|
||||
// If we can build workers, then we want AT LEAST 2 improvements, OR a worker nearby.
|
||||
// Otherwise, AI tries to produce settlers when it can hardly sustain itself
|
||||
.filter {
|
||||
.filter { city ->
|
||||
!workersBuildableForThisCiv
|
||||
|| it.getCenterTile().getTilesInDistance(2).count { it.improvement!=null } > 1
|
||||
|| it.getCenterTile().getTilesInDistance(3).any { it.civilianUnit?.hasUnique(UniqueType.BuildImprovements)==true }
|
||||
|| city.getCenterTile().getTilesInDistance(2).count { it.improvement!=null } > 1
|
||||
|| city.getCenterTile().getTilesInDistance(3).any { it.civilianUnit?.hasUnique(UniqueType.BuildImprovements)==true }
|
||||
}
|
||||
.maxByOrNull { it.cityStats.currentCityStats.production }
|
||||
?: return
|
||||
|
@ -18,7 +18,6 @@ import com.unciv.logic.map.tile.RoadStatus
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.Counter
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
@ -35,7 +34,6 @@ enum class CityFlags {
|
||||
|
||||
|
||||
class City : IsPartOfGameInfoSerialization {
|
||||
@Suppress("JoinDeclarationAndAssignment")
|
||||
@Transient
|
||||
lateinit var civ: Civilization
|
||||
|
||||
@ -307,8 +305,8 @@ class City : IsPartOfGameInfoSerialization {
|
||||
fun canBeDestroyed(justCaptured: Boolean = false): Boolean {
|
||||
if (civ.gameInfo.gameParameters.noCityRazing) return false
|
||||
|
||||
val allowRazeCapital = civ.gameInfo.ruleset.modOptions.uniques.contains(ModOptionsConstants.allowRazeCapital)
|
||||
val allowRazeHolyCity = civ.gameInfo.ruleset.modOptions.uniques.contains(ModOptionsConstants.allowRazeHolyCity)
|
||||
val allowRazeCapital = civ.gameInfo.ruleset.modOptions.hasUnique(UniqueType.AllowRazeCapital)
|
||||
val allowRazeHolyCity = civ.gameInfo.ruleset.modOptions.hasUnique(UniqueType.AllowRazeHolyCity)
|
||||
|
||||
if (isOriginalCapital && !allowRazeCapital) return false
|
||||
if (isHolyCity() && !allowRazeHolyCity) return false
|
||||
|
@ -5,7 +5,6 @@ import com.unciv.models.Counter
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.IConstruction
|
||||
import com.unciv.models.ruleset.INonPerpetualConstruction
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
@ -523,7 +522,7 @@ class CityStats(val city: City) {
|
||||
}
|
||||
|
||||
// AFTER we've gotten all the gold stats figured out, only THEN do we plonk that gold into Science
|
||||
if (city.getRuleset().modOptions.uniques.contains(ModOptionsConstants.convertGoldToScience)) {
|
||||
if (city.getRuleset().modOptions.hasUnique(UniqueType.ConvertGoldToScience)) {
|
||||
val amountConverted = (newFinalStatList.values.sumOf { it.gold.toDouble() }
|
||||
* city.civ.tech.goldPercentConvertedToScience).toInt().toFloat()
|
||||
if (amountConverted > 0) // Don't want you converting negative gold to negative science yaknow
|
||||
|
@ -15,7 +15,6 @@ import com.unciv.logic.civilization.diplomacy.DiplomacyTurnManager.nextTurn
|
||||
import com.unciv.logic.map.mapunit.UnitTurnManager
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.logic.trade.TradeEvaluation
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueTriggerActivation
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
@ -42,7 +41,6 @@ class TurnManager(val civInfo: Civilization) {
|
||||
if (civInfo.cities.isNotEmpty() && civInfo.gameInfo.ruleset.technologies.isNotEmpty())
|
||||
civInfo.tech.updateResearchProgress()
|
||||
|
||||
|
||||
civInfo.cache.updateCivResources() // If you offered a trade last turn, this turn it will have been accepted/declined
|
||||
for (stockpiledResource in civInfo.getCivResourceSupply().filter { it.resource.isStockpiled() })
|
||||
civInfo.resourceStockpiles.add(stockpiledResource.resource.name, stockpiledResource.amount)
|
||||
@ -52,8 +50,7 @@ class TurnManager(val civInfo: Civilization) {
|
||||
civInfo.updateStatsForNextTurn() // for things that change when turn passes e.g. golden age, city state influence
|
||||
|
||||
// Do this after updateStatsForNextTurn but before cities.startTurn
|
||||
if (civInfo.playerType == PlayerType.AI && civInfo.gameInfo.ruleset.modOptions.uniques.contains(
|
||||
ModOptionsConstants.convertGoldToScience))
|
||||
if (civInfo.playerType == PlayerType.AI && civInfo.gameInfo.ruleset.modOptions.hasUnique(UniqueType.ConvertGoldToScience))
|
||||
NextTurnAutomation.automateGoldToSciencePercentage(civInfo)
|
||||
|
||||
// Generate great people at the start of the turn,
|
||||
|
@ -8,7 +8,6 @@ import com.unciv.logic.city.City
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
@ -64,7 +63,7 @@ class TradeEvaluation {
|
||||
}
|
||||
|
||||
fun getTradeAcceptability(trade: Trade, evaluator: Civilization, tradePartner: Civilization): Int {
|
||||
val citiesAskedToSurrender = trade.ourOffers.filter { it.type == TradeType.City }.count()
|
||||
val citiesAskedToSurrender = trade.ourOffers.count { it.type == TradeType.City }
|
||||
val maxCitiesToSurrender = ceil(evaluator.cities.size.toFloat() / 5).toInt()
|
||||
if (citiesAskedToSurrender > maxCitiesToSurrender) {
|
||||
return Int.MIN_VALUE
|
||||
@ -100,7 +99,7 @@ class TradeEvaluation {
|
||||
return evaluateBuyCost(offer, civInfo, tradePartner)
|
||||
}
|
||||
|
||||
fun evaluateBuyCost(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int {
|
||||
private fun evaluateBuyCost(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int {
|
||||
when (offer.type) {
|
||||
TradeType.Gold -> return offer.amount
|
||||
// GPT loses 1% of value for each 'future' turn, meaning: gold now is more valuable than gold in the future
|
||||
@ -151,13 +150,13 @@ class TradeEvaluation {
|
||||
val civToDeclareWarOn = civInfo.gameInfo.getCivilization(offer.name)
|
||||
val threatToThem = Automation.threatAssessment(civInfo, civToDeclareWarOn)
|
||||
|
||||
if (!civInfo.isAtWarWith(civToDeclareWarOn)) return 0 // why should we pay you to go fight someone...?
|
||||
return if (!civInfo.isAtWarWith(civToDeclareWarOn)) 0 // why should we pay you to go fight someone...?
|
||||
else when (threatToThem) {
|
||||
ThreatLevel.VeryLow -> return 0
|
||||
ThreatLevel.Low -> return 0
|
||||
ThreatLevel.Medium -> return 100
|
||||
ThreatLevel.High -> return 500
|
||||
ThreatLevel.VeryHigh -> return 1000
|
||||
ThreatLevel.VeryLow -> 0
|
||||
ThreatLevel.Low -> 0
|
||||
ThreatLevel.Medium -> 100
|
||||
ThreatLevel.High -> 500
|
||||
ThreatLevel.VeryHigh -> 1000
|
||||
}
|
||||
}
|
||||
TradeType.City -> {
|
||||
@ -209,7 +208,7 @@ class TradeEvaluation {
|
||||
return evaluateSellCost(offer, civInfo, tradePartner)
|
||||
}
|
||||
|
||||
fun evaluateSellCost(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int {
|
||||
private fun evaluateSellCost(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization): Int {
|
||||
when (offer.type) {
|
||||
TradeType.Gold -> return offer.amount
|
||||
TradeType.Gold_Per_Turn -> return offer.amount * offer.duration
|
||||
@ -307,7 +306,7 @@ class TradeEvaluation {
|
||||
* This returns how much one gold is worth now in comparison to starting out the game
|
||||
* Gold is worth less as the civilization has a higher income
|
||||
*/
|
||||
fun getGoldInflation(civInfo: Civilization): Double {
|
||||
private fun getGoldInflation(civInfo: Civilization): Double {
|
||||
val modifier = 1000.0
|
||||
val goldPerTurn = civInfo.stats.statsForNextTurn.gold.toDouble()
|
||||
// To visualise the function, plug this into a 2d graphing calculator \frac{1000}{x^{1.2}+1.11*1000}
|
||||
@ -371,7 +370,7 @@ class TradeEvaluation {
|
||||
}
|
||||
|
||||
private fun introductionValue(ruleSet: Ruleset): Int {
|
||||
val unique = ruleSet.modOptions.getMatchingUniques(ModOptionsConstants.tradeCivIntroductions).firstOrNull()
|
||||
val unique = ruleSet.modOptions.getMatchingUniques(UniqueType.TradeCivIntroductions).firstOrNull()
|
||||
?: return 0
|
||||
return unique.params[0].toInt()
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ import com.unciv.logic.civilization.PopupAlert
|
||||
import com.unciv.logic.civilization.diplomacy.CityStateFunctions
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
|
||||
@ -59,7 +57,7 @@ class TradeLogic(val ourCivilization:Civilization, val otherCivilization: Civili
|
||||
val otherCivsWeKnow = civInfo.getKnownCivs()
|
||||
.filter { it.civName != otherCivilization.civName && it.isMajorCiv() && !it.isDefeated() }
|
||||
|
||||
if (civInfo.gameInfo.ruleset.modOptions.hasUnique(ModOptionsConstants.tradeCivIntroductions)) {
|
||||
if (civInfo.gameInfo.ruleset.modOptions.hasUnique(UniqueType.TradeCivIntroductions)) {
|
||||
val civsWeKnowAndTheyDont = otherCivsWeKnow
|
||||
.filter { !otherCivilization.diplomacy.containsKey(it.civName) && !it.isDefeated() }
|
||||
for (thirdCiv in civsWeKnowAndTheyDont) {
|
||||
@ -68,7 +66,7 @@ class TradeLogic(val ourCivilization:Civilization, val otherCivilization: Civili
|
||||
}
|
||||
|
||||
if (!civInfo.isCityState() && !otherCivilization.isCityState()
|
||||
&& !civInfo.gameInfo.ruleset.modOptions.hasUnique(ModOptionsConstants.diplomaticRelationshipsCannotChange)) {
|
||||
&& !civInfo.gameInfo.ruleset.modOptions.hasUnique(UniqueType.DiplomaticRelationshipsCannotChange)) {
|
||||
val civsWeBothKnow = otherCivsWeKnow
|
||||
.filter { otherCivilization.diplomacy.containsKey(it.civName) }
|
||||
val civsWeArentAtWarWith = civsWeBothKnow
|
||||
@ -134,7 +132,7 @@ class TradeLogic(val ourCivilization:Civilization, val otherCivilization: Civili
|
||||
from.getDiplomacyManager(to)
|
||||
.setFlag(DiplomacyFlags.ResearchAgreement, offer.duration)
|
||||
}
|
||||
if (offer.name == Constants.defensivePact) to.getDiplomacyManager(from).signDefensivePact(offer.duration);
|
||||
if (offer.name == Constants.defensivePact) to.getDiplomacyManager(from).signDefensivePact(offer.duration)
|
||||
}
|
||||
TradeType.Introduction -> to.diplomacyFunctions.makeCivilizationsMeet(to.gameInfo.getCivilization(offer.name))
|
||||
TradeType.WarDeclaration -> {
|
||||
|
@ -6,16 +6,6 @@ import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueMap
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
|
||||
object ModOptionsConstants {
|
||||
const val diplomaticRelationshipsCannotChange = "Diplomatic relationships cannot change"
|
||||
const val convertGoldToScience = "Can convert gold to science with sliders"
|
||||
const val allowCityStatesSpawnUnits = "Allow City States to spawn with additional units"
|
||||
const val tradeCivIntroductions = "Can trade civilization introductions for [] Gold"
|
||||
const val disableReligion = "Disable religion"
|
||||
const val allowRazeCapital = "Allow raze capital"
|
||||
const val allowRazeHolyCity = "Allow raze holy city"
|
||||
}
|
||||
|
||||
class ModOptions : IHasUniques {
|
||||
//region Modder choices
|
||||
var isBaseRuleset = false
|
||||
|
@ -1,9 +1,14 @@
|
||||
package com.unciv.models.ruleset.unique
|
||||
|
||||
import java.util.EnumSet
|
||||
|
||||
enum class UniqueFlag {
|
||||
HiddenToUsers,
|
||||
NoConditionals,
|
||||
;
|
||||
companion object {
|
||||
val setOfHiddenToUsers = listOf(HiddenToUsers)
|
||||
val none: EnumSet<UniqueFlag> = EnumSet.noneOf(UniqueFlag::class.java)
|
||||
val setOfHiddenToUsers: EnumSet<UniqueFlag> = EnumSet.of(HiddenToUsers)
|
||||
val setOfNoConditionals: EnumSet<UniqueFlag> = EnumSet.of(NoConditionals)
|
||||
}
|
||||
}
|
||||
|
@ -588,9 +588,14 @@ enum class UniqueParameterType(
|
||||
/** Mod declarative compatibility: Define Mod relations by their name. */
|
||||
ModName("modFilter", "DeCiv Redux", """A Mod name, case-sensitive _or_ a simple wildcard filter beginning and ending in an Asterisk, case-insensitive""", "Mod name filter") {
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueParameterErrorSeverity? =
|
||||
if ('-' !in parameterText && ('*' !in parameterText || parameterText.matches(Regex("""^\*[^*]+\*$""")))) null
|
||||
else UniqueType.UniqueParameterErrorSeverity.RulesetInvariant
|
||||
UniqueType.UniqueParameterErrorSeverity? = when {
|
||||
BaseRuleset.values().any { it.fullName == parameterText } -> null // Only Builtin ruleset names can contain '-'
|
||||
parameterText == "*Civ V -*" || parameterText == "*Civ V - *" -> null // Wildcard filter for builtin
|
||||
'-' in parameterText -> UniqueType.UniqueParameterErrorSeverity.RulesetInvariant
|
||||
parameterText.matches(Regex("""^\*[^*]+\*$""")) -> null
|
||||
parameterText.startsWith('*') || parameterText.endsWith('*') -> UniqueType.UniqueParameterErrorSeverity.RulesetInvariant
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getTranslationWriterStringsForOutput() = scanExistingValues(this)
|
||||
},
|
||||
|
@ -5,6 +5,7 @@ import com.unciv.models.ruleset.validation.RulesetErrorSeverity
|
||||
import com.unciv.models.ruleset.validation.RulesetValidator
|
||||
import com.unciv.models.translations.getPlaceholderParameters
|
||||
import com.unciv.models.translations.getPlaceholderText
|
||||
import java.util.EnumSet
|
||||
|
||||
// I didn't put this in a companion object because APPARENTLY doing that means you can't use it in the init function.
|
||||
private val numberRegex = Regex("\\d+$") // Any number of trailing digits
|
||||
@ -12,7 +13,7 @@ private val numberRegex = Regex("\\d+$") // Any number of trailing digits
|
||||
enum class UniqueType(
|
||||
val text: String,
|
||||
vararg targets: UniqueTarget,
|
||||
val flags: List<UniqueFlag> = emptyList(),
|
||||
val flags: EnumSet<UniqueFlag> = UniqueFlag.none,
|
||||
val docDescription: String? = null
|
||||
) {
|
||||
|
||||
@ -807,17 +808,27 @@ enum class UniqueType(
|
||||
Comment("Comment [comment]", *UniqueTarget.Displayable,
|
||||
docDescription = "Allows displaying arbitrary text in a Unique listing. Only the text within the '[]' brackets will be displayed, the rest serves to allow Ruleset validation to recognize the intent."),
|
||||
|
||||
// Formerly `ModOptionsConstants`
|
||||
DiplomaticRelationshipsCannotChange("Diplomatic relationships cannot change", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
ConvertGoldToScience("Can convert gold to science with sliders", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
AllowCityStatesSpawnUnits("Allow City States to spawn with additional units", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
TradeCivIntroductions("Can trade civilization introductions for [positiveAmount] Gold", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
DisableReligion("Disable religion", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
AllowRazeCapital("Allow raze capital", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
AllowRazeHolyCity("Allow raze holy city", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
|
||||
// Declarative Mod compatibility (see [ModCompatibility]):
|
||||
// Note there is currently no display for these, but UniqueFlag.HiddenToUsers is not set.
|
||||
// That means we auto-template and ask our translators for a translation that is currently unused.
|
||||
//todo To think over - leave as is for future use or remove templates and translations by adding the flag?
|
||||
ModIncompatibleWith("Mod is incompatible with [modFilter]", UniqueTarget.ModOptions,
|
||||
|
||||
ModIncompatibleWith("Mod is incompatible with [modFilter]", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals,
|
||||
docDescription = "Specifies that your Mod is incompatible with another. Always treated symmetrically, and cannot be overridden by the Mod you are declaring as incompatible."),
|
||||
ModRequires("Mod requires [modFilter]", UniqueTarget.ModOptions,
|
||||
ModRequires("Mod requires [modFilter]", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals,
|
||||
docDescription = "Specifies that your Extension Mod is only available if any other Mod matching the filter is active."),
|
||||
ModIsAudioVisualOnly("Should only be used as permanent audiovisual mod", UniqueTarget.ModOptions),
|
||||
ModIsAudioVisual("Can be used as permanent audiovisual mod", UniqueTarget.ModOptions),
|
||||
ModIsNotAudioVisual("Cannot be used as permanent audiovisual mod", UniqueTarget.ModOptions),
|
||||
ModIsAudioVisualOnly("Should only be used as permanent audiovisual mod", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
ModIsAudioVisual("Can be used as permanent audiovisual mod", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
ModIsNotAudioVisual("Cannot be used as permanent audiovisual mod", UniqueTarget.ModOptions, flags = UniqueFlag.setOfNoConditionals),
|
||||
|
||||
// endregion
|
||||
|
||||
|
@ -39,7 +39,7 @@ class RulesetValidator(val ruleset: Ruleset) {
|
||||
val lines = RulesetErrorList()
|
||||
|
||||
// When not checking the entire ruleset, we can only really detect ruleset-invariant errors in uniques
|
||||
addModOptionsErrors(lines)
|
||||
addModOptionsErrors(lines, tryFixUnknownUniques)
|
||||
uniqueValidator.checkUniques(ruleset.globalUniques, lines, false, tryFixUnknownUniques)
|
||||
addUnitErrorsRulesetInvariant(lines, tryFixUnknownUniques)
|
||||
addTechErrorsRulesetInvariant(lines, tryFixUnknownUniques)
|
||||
@ -63,7 +63,7 @@ class RulesetValidator(val ruleset: Ruleset) {
|
||||
uniqueValidator.populateFilteringUniqueHashsets()
|
||||
|
||||
val lines = RulesetErrorList()
|
||||
addModOptionsErrors(lines)
|
||||
addModOptionsErrors(lines, tryFixUnknownUniques)
|
||||
uniqueValidator.checkUniques(ruleset.globalUniques, lines, true, tryFixUnknownUniques)
|
||||
|
||||
addUnitErrorsBaseRuleset(lines, tryFixUnknownUniques)
|
||||
@ -94,8 +94,12 @@ class RulesetValidator(val ruleset: Ruleset) {
|
||||
return lines
|
||||
}
|
||||
|
||||
private fun addModOptionsErrors(lines: RulesetErrorList) {
|
||||
if (ruleset.name.isBlank()) return // These tests don't make sense for combined rulesets
|
||||
private fun addModOptionsErrors(lines: RulesetErrorList, tryFixUnknownUniques: Boolean) {
|
||||
// Basic Unique validation (type, target, parameters) should always run.
|
||||
// Using reportRulesetSpecificErrors=true as ModOptions never should use Uniques depending on objects from a base ruleset anyway.
|
||||
uniqueValidator.checkUniques(ruleset.modOptions, lines, reportRulesetSpecificErrors = true, tryFixUnknownUniques)
|
||||
|
||||
if (ruleset.name.isBlank()) return // The rest of these tests don't make sense for combined rulesets
|
||||
|
||||
val audioVisualUniqueTypes = setOf(
|
||||
UniqueType.ModIsAudioVisual,
|
||||
@ -825,6 +829,4 @@ class RulesetValidator(val ruleset: Ruleset) {
|
||||
recursiveCheck(hashSetOf(), promotion, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.unique.IHasUniques
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueComplianceError
|
||||
import com.unciv.models.ruleset.unique.UniqueFlag
|
||||
import com.unciv.models.ruleset.unique.UniqueParameterType
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
@ -84,7 +85,10 @@ class UniqueValidator(val ruleset: Ruleset) {
|
||||
addConditionalErrors(conditional, rulesetErrors, prefix, unique, reportRulesetSpecificErrors)
|
||||
}
|
||||
|
||||
if (unique.conditionals.any() && unique.type in MapUnitCache.UnitMovementUniques)
|
||||
if (unique.type in MapUnitCache.UnitMovementUniques
|
||||
&& unique.conditionals.any { it.type != UniqueType.ConditionalOurUnit || it.params[0] != "All" }
|
||||
)
|
||||
// (Stay silent if the only conditional is `<for [All] units>` - as in G&K Denmark)
|
||||
// Not necessarily even a problem, but yes something mod maker should be aware of
|
||||
rulesetErrors.add("$prefix unique \"${unique.text}\" contains a conditional on a unit movement unique. " +
|
||||
"Due to performance considerations, this unique is cached on the unit," +
|
||||
@ -105,6 +109,15 @@ class UniqueValidator(val ruleset: Ruleset) {
|
||||
unique: Unique,
|
||||
reportRulesetSpecificErrors: Boolean
|
||||
) {
|
||||
if (unique.hasFlag(UniqueFlag.NoConditionals)) {
|
||||
rulesetErrors.add(
|
||||
"$prefix unique \"${unique.text}\" contains the conditional \"${conditional.text}\"," +
|
||||
" but the unique does not accept conditionals!",
|
||||
RulesetErrorSeverity.Error
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (conditional.type == null) {
|
||||
rulesetErrors.add(
|
||||
"$prefix unique \"${unique.text}\" contains the conditional \"${conditional.text}\"," +
|
||||
|
@ -18,7 +18,6 @@ import com.unciv.logic.civilization.managers.AssignedQuest
|
||||
import com.unciv.logic.trade.TradeLogic
|
||||
import com.unciv.logic.trade.TradeOffer
|
||||
import com.unciv.logic.trade.TradeType
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.Quest
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
@ -70,7 +69,7 @@ class CityStateDiplomacyTable(private val diplomacyScreen: DiplomacyScreen) {
|
||||
if (diplomacyScreen.isNotPlayersTurn() || viewingCiv.isAtWarWith(otherCiv)) demandTributeButton.disable()
|
||||
|
||||
val diplomacyManager = viewingCiv.getDiplomacyManager(otherCiv)
|
||||
if (!viewingCiv.gameInfo.ruleset.modOptions.uniques.contains(ModOptionsConstants.diplomaticRelationshipsCannotChange)) {
|
||||
if (!viewingCiv.gameInfo.ruleset.modOptions.hasUnique(UniqueType.DiplomaticRelationshipsCannotChange)) {
|
||||
if (viewingCiv.isAtWarWith(otherCiv))
|
||||
diplomacyTable.add(getNegotiatePeaceCityStateButton(otherCiv, diplomacyManager)).row()
|
||||
else diplomacyTable.add(diplomacyScreen.getDeclareWarButton(diplomacyManager, otherCiv)).row()
|
||||
|
@ -14,7 +14,7 @@ import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.logic.trade.TradeOffer
|
||||
import com.unciv.logic.trade.TradeType
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.extensions.addSeparator
|
||||
import com.unciv.ui.components.extensions.disable
|
||||
@ -48,7 +48,7 @@ class MajorCivDiplomacyTable(private val diplomacyScreen: DiplomacyScreen) {
|
||||
diplomacyTable.addSeparator()
|
||||
|
||||
val diplomaticRelationshipsCanChange =
|
||||
!viewingCiv.gameInfo.ruleset.modOptions.uniques.contains(ModOptionsConstants.diplomaticRelationshipsCannotChange)
|
||||
!viewingCiv.gameInfo.ruleset.modOptions.hasUnique(UniqueType.DiplomaticRelationshipsCannotChange)
|
||||
|
||||
val diplomacyManager = viewingCiv.getDiplomacyManager(otherCiv)
|
||||
|
||||
|
@ -5,15 +5,14 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.Constants
|
||||
import com.unciv.GUI
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.StatMap
|
||||
import com.unciv.ui.components.widgets.TabbedPager
|
||||
import com.unciv.ui.components.widgets.UncivSlider
|
||||
import com.unciv.ui.components.extensions.addSeparator
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.components.widgets.TabbedPager
|
||||
import com.unciv.ui.components.widgets.UncivSlider
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import com.unciv.ui.screens.civilopediascreen.MarkupRenderer
|
||||
@ -53,7 +52,7 @@ class StatsOverviewTab(
|
||||
unhappinessTable.update()
|
||||
|
||||
goldAndSliderTable.add(goldTable).row()
|
||||
if (gameInfo.ruleset.modOptions.uniques.contains(ModOptionsConstants.convertGoldToScience))
|
||||
if (gameInfo.ruleset.modOptions.hasUnique(UniqueType.ConvertGoldToScience))
|
||||
goldAndSliderTable.addGoldSlider()
|
||||
|
||||
update()
|
||||
|
@ -1805,6 +1805,29 @@ Due to performance considerations, this unique is cached, thus conditionals may
|
||||
Applicable to: CityState
|
||||
|
||||
## ModOptions uniques
|
||||
??? example "Diplomatic relationships cannot change"
|
||||
Applicable to: ModOptions
|
||||
|
||||
??? example "Can convert gold to science with sliders"
|
||||
Applicable to: ModOptions
|
||||
|
||||
??? example "Allow City States to spawn with additional units"
|
||||
Applicable to: ModOptions
|
||||
|
||||
??? example "Can trade civilization introductions for [positiveAmount] Gold"
|
||||
Example: "Can trade civilization introductions for [3] Gold"
|
||||
|
||||
Applicable to: ModOptions
|
||||
|
||||
??? example "Disable religion"
|
||||
Applicable to: ModOptions
|
||||
|
||||
??? example "Allow raze capital"
|
||||
Applicable to: ModOptions
|
||||
|
||||
??? example "Allow raze holy city"
|
||||
Applicable to: ModOptions
|
||||
|
||||
??? example "Mod is incompatible with [modFilter]"
|
||||
Specifies that your Mod is incompatible with another. Always treated symmetrically, and cannot be overridden by the Mod you are declaring as incompatible.
|
||||
Example: "Mod is incompatible with [DeCiv Redux]"
|
||||
|
Loading…
Reference in New Issue
Block a user