mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 15:27:50 +07:00
Add a Unique allowing an Improvement to specify which Resource(s) it improves (#11718)
* Linting * Add UniqueType.ImprovesResources * Address reviews and extensive Kdoc * Oops, better include the generated stuff
This commit is contained in:
@ -290,7 +290,7 @@ class TileStatFunctions(val tile: Tile) {
|
||||
if (tile.hasViewableResource(observingCiv) && tile.tileResource.isImprovedBy(improvement.name)
|
||||
&& tile.tileResource.improvementStats != null
|
||||
)
|
||||
stats.add(tile.tileResource.improvementStats!!.clone()) // resource-specific improvement
|
||||
stats.add(tile.tileResource.improvementStats!!) // resource-specific improvement
|
||||
|
||||
val conditionalState = StateForConditionals(civInfo = observingCiv, city = city, tile = tile)
|
||||
for (unique in improvement.getMatchingUniques(UniqueType.Stats, conditionalState)) {
|
||||
|
@ -242,7 +242,7 @@ class Ruleset {
|
||||
fun allICivilopediaText(): Sequence<ICivilopediaText> =
|
||||
allRulesetObjects() + events.values + events.values.flatMap { it.choices }
|
||||
|
||||
fun load(folderHandle: FileHandle) {
|
||||
internal fun load(folderHandle: FileHandle) {
|
||||
// Note: Most files are loaded using createHashmap, which sets originRuleset automatically.
|
||||
// For other files containing IRulesetObject's we'll have to remember to do so manually - e.g. Tech.
|
||||
val modOptionsFile = folderHandle.child("ModOptions.json")
|
||||
@ -450,6 +450,8 @@ class Ruleset {
|
||||
).isEmpty()
|
||||
})
|
||||
}
|
||||
|
||||
updateResourceTransients()
|
||||
}
|
||||
}
|
||||
|
||||
@ -458,17 +460,21 @@ class Ruleset {
|
||||
* Alternatively, if you edit a tech column's building costs, you want it to affect all buildings in that column.
|
||||
* This deals with that
|
||||
* */
|
||||
fun updateBuildingCosts() {
|
||||
internal fun updateBuildingCosts() {
|
||||
for (building in buildings.values) {
|
||||
if (building.cost == -1 && building.getMatchingUniques(UniqueType.Unbuildable).none { it.conditionals.isEmpty() }) {
|
||||
val column = building.techColumn(this)
|
||||
if (column != null) {
|
||||
building.cost = if (building.isAnyWonder()) column.wonderCost else column.buildingCost
|
||||
}
|
||||
}
|
||||
if (building.cost != -1) continue
|
||||
if (building.getMatchingUniques(UniqueType.Unbuildable).any { it.conditionals.isEmpty() }) continue
|
||||
val column = building.techColumn(this) ?: continue
|
||||
building.cost = if (building.isAnyWonder()) column.wonderCost else column.buildingCost
|
||||
}
|
||||
}
|
||||
|
||||
/** Introduced to support UniqueType.ImprovesResources: gives a resource the chance to scan improvements */
|
||||
internal fun updateResourceTransients() {
|
||||
for (resource in tileResources.values)
|
||||
resource.setTransients(this)
|
||||
}
|
||||
|
||||
/** Used for displaying a RuleSet's name */
|
||||
override fun toString() = when {
|
||||
name.isNotEmpty() -> name
|
||||
|
@ -153,6 +153,7 @@ object RulesetCache : HashMap<String, Ruleset>() {
|
||||
newRuleset.mods += mod.name
|
||||
}
|
||||
newRuleset.updateBuildingCosts() // only after we've added all the mods can we calculate the building costs
|
||||
newRuleset.updateResourceTransients()
|
||||
|
||||
return newRuleset
|
||||
}
|
||||
|
@ -16,21 +16,58 @@ class TileResource : RulesetStatsObject() {
|
||||
|
||||
var resourceType: ResourceType = ResourceType.Bonus
|
||||
var terrainsCanBeFoundOn: List<String> = listOf()
|
||||
var improvement: String? = null
|
||||
|
||||
/** stats that this resource adds to a tile */
|
||||
var improvementStats: Stats? = null
|
||||
var revealedBy: String? = null
|
||||
|
||||
/** Legacy "which improvement will unlock this treausre"
|
||||
* @see improvedBy
|
||||
* @see getImprovements
|
||||
*/
|
||||
var improvement: String? = null
|
||||
/** Defines which improvement will "unlock" this resource
|
||||
* @see improvement
|
||||
* @see getImprovements
|
||||
*/
|
||||
var improvedBy: List<String> = listOf()
|
||||
|
||||
var majorDepositAmount: DepositAmount = DepositAmount()
|
||||
var minorDepositAmount: DepositAmount = DepositAmount()
|
||||
|
||||
private val _allImprovements by lazy {
|
||||
if (improvement == null) improvedBy
|
||||
else improvedBy + improvement!!
|
||||
private var improvementsInitialized = false
|
||||
/** Cache collecting [improvement], [improvedBy] and [UniqueType.ImprovesResources] uniques on the improvements themselves. */
|
||||
private val allImprovements = mutableSetOf<String>()
|
||||
private var ruleset: Ruleset? = null
|
||||
|
||||
/** Collects which improvements "unlock" this resource, caches and returns the Set.
|
||||
* - The cache is cleared after ruleset load and combine, via [setTransients].
|
||||
* @see improvement
|
||||
* @see improvedBy
|
||||
* @see UniqueType.ImprovesResources
|
||||
*/
|
||||
fun getImprovements(): Set<String> {
|
||||
if (improvementsInitialized) return allImprovements
|
||||
val ruleset = this.ruleset
|
||||
?: throw IllegalStateException("No ruleset on TileResource when initializing improvements")
|
||||
if (improvement != null) allImprovements += improvement!!
|
||||
allImprovements.addAll(improvedBy)
|
||||
for (improvement in ruleset.tileImprovements.values) {
|
||||
if (improvement.getMatchingUniques(UniqueType.ImprovesResources).none { matchesFilter(it.params[0]) }) continue
|
||||
allImprovements += improvement.name
|
||||
}
|
||||
improvementsInitialized = true
|
||||
return allImprovements
|
||||
}
|
||||
|
||||
fun getImprovements(): List<String> {
|
||||
return _allImprovements
|
||||
/** Clears the cache for [getImprovements] and saves the Ruleset the cache update will need.
|
||||
* - Doesn't evaluate the cache immediately, that would break tests as it trips the uniqueObjects lazy and tests manipulate uniques after ruleset load.
|
||||
* - called from [Ruleset.updateResourceTransients]
|
||||
*/
|
||||
fun setTransients(ruleset: Ruleset) {
|
||||
allImprovements.clear()
|
||||
improvementsInitialized = false
|
||||
this.ruleset = ruleset
|
||||
}
|
||||
|
||||
override fun getUniqueTarget() = UniqueTarget.Resource
|
||||
@ -135,12 +172,21 @@ class TileResource : RulesetStatsObject() {
|
||||
return getImprovements().contains(improvementName)
|
||||
}
|
||||
|
||||
fun getImprovingImprovement(tile: Tile, civInfo: Civilization): String? {
|
||||
/** @return Of all the potential improvements in [getImprovements], the first this [civ] can actually build, if any. */
|
||||
fun getImprovingImprovement(tile: Tile, civ: Civilization): String? {
|
||||
return getImprovements().firstOrNull {
|
||||
tile.improvementFunctions.canBuildImprovement(civInfo.gameInfo.ruleset.tileImprovements[it]!!, civInfo)
|
||||
tile.improvementFunctions.canBuildImprovement(civ.gameInfo.ruleset.tileImprovements[it]!!, civ)
|
||||
}
|
||||
}
|
||||
|
||||
fun matchesFilter(filter: String) = when (filter) {
|
||||
name -> true
|
||||
"any" -> true
|
||||
resourceType.name -> true
|
||||
in uniques -> true
|
||||
else -> improvementStats?.any { filter == it.key.name } == true
|
||||
}
|
||||
|
||||
fun generatesNaturallyOn(tile: Tile): Boolean {
|
||||
if (tile.lastTerrain.name !in terrainsCanBeFoundOn) return false
|
||||
val stateForConditionals = StateForConditionals(tile = tile)
|
||||
|
@ -419,6 +419,21 @@ enum class UniqueParameterType(
|
||||
override fun isKnownValue(parameterText: String, ruleset: Ruleset) = ruleset.tileResources[parameterText]?.isStockpiled() == true
|
||||
},
|
||||
|
||||
/** Used by [UniqueType.ImprovesResources], implemented by [com.unciv.models.ruleset.tile.TileResource.matchesFilter] */
|
||||
ResourceFilter("resourceFilter", "Strategic", "A resource name, type, 'all', or a Stat listed in the resource's improvementStats",
|
||||
severityDefault = UniqueType.UniqueParameterErrorSeverity.PossibleFilteringUnique
|
||||
) {
|
||||
private val knownValues = setOf("any") + Constants.all // "any" sounds nicer than "all" in that UniqueType
|
||||
override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when {
|
||||
parameterText in knownValues -> true
|
||||
parameterText in ruleset.tileResources -> true
|
||||
ResourceType.values().any { it.name == parameterText } -> true
|
||||
Stat.isStat(parameterText) -> true
|
||||
else -> false
|
||||
}
|
||||
override fun getTranslationWriterStringsForOutput() = knownValues
|
||||
},
|
||||
|
||||
/** Used by [UniqueType.FreeExtraBeliefs], see ReligionManager.getBeliefsToChooseAt* functions */
|
||||
BeliefTypeName("beliefType", "Follower", "'Pantheon', 'Follower', 'Founder' or 'Enhancer'",
|
||||
severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant
|
||||
|
@ -638,6 +638,10 @@ enum class UniqueType(
|
||||
PillageYieldFixed("Pillaging this improvement yields [stats]", UniqueTarget.Improvement),
|
||||
Irremovable("Irremovable", UniqueTarget.Improvement),
|
||||
AutomatedUnitsWillNotReplace("Will not be replaced by automated units", UniqueTarget.Improvement),
|
||||
ImprovesResources("Improves [resourceFilter] resource in this tile", UniqueTarget.Improvement, flags = UniqueFlag.setOfNoConditionals,
|
||||
docDescription = "This is offered as an alternative to the improvedBy field of a resource." +
|
||||
" The result will be cached within the resource definition when loading a game, without knowledge about terrain, cities, civs, units or time." +
|
||||
" Therefore, most conditionals will not work, only those **not** dependent on game state."),
|
||||
//endregion
|
||||
|
||||
/////////////////////////////////// region 07 PERSONALITY UNIQUES ////////////////////////////////////////
|
||||
|
@ -81,7 +81,7 @@ open class Stats(
|
||||
faith = 0f
|
||||
}
|
||||
|
||||
/** **Mutating function**
|
||||
/** **Mutating function** (but does **not** mutate [other])
|
||||
* Adds each value of another [Stats] instance to this one in place
|
||||
* @return this for chaining */
|
||||
fun add(other: Stats): Stats {
|
||||
|
@ -11,7 +11,7 @@ Note that all of these are case-sensitive!
|
||||
|
||||
## General Filter Rules
|
||||
|
||||
All filters except for `populationFilter` accept multiple values in the format: `{A} {B} {C}` etc, meaning "the object must match ALL of these filters"
|
||||
All filters except for `populationFilter` and `resourceFilter` accept multiple values in the format: `{A} {B} {C}` etc, meaning "the object must match ALL of these filters"
|
||||
|
||||
> Example: `[{Military} {Water}] units`, `[{Wounded} {Armor}] units`, etc.
|
||||
|
||||
@ -206,6 +206,11 @@ For example: `+1 Science`.
|
||||
|
||||
These can be strung together with ", " between them, for example: `+2 Production, +3 Food`.
|
||||
|
||||
## resourceFilter
|
||||
|
||||
At the moment, only used for the `"Improves [resourceFilter] resource in this tile"` Unique on Improvements.
|
||||
Allows filtering resources by their name, their type, or by any Stat listed in their `improvementStats` property. The `all` keyword works too.
|
||||
|
||||
## stockpiledResource
|
||||
|
||||
This indicates a text that corresponds to a custom Stockpile Resource.
|
||||
|
@ -1795,6 +1795,12 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
||||
??? example "Will not be replaced by automated units"
|
||||
Applicable to: Improvement
|
||||
|
||||
??? example "Improves [resourceFilter] resource in this tile"
|
||||
This is offered as an alternative to the improvedBy field of a resource. The result will be cached within the resource definition when loading a game, without knowledge about terrain, cities, civs, units or time. Therefore, most conditionals will not work, only those **not** dependent on game state.
|
||||
Example: "Improves [Strategic] resource in this tile"
|
||||
|
||||
Applicable to: Improvement
|
||||
|
||||
## Resource uniques
|
||||
??? example "Deposits in [tileFilter] tiles always provide [amount] resources"
|
||||
Example: "Deposits in [Farm] tiles always provide [3] resources"
|
||||
@ -2522,6 +2528,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
||||
*[promotion]: The name of any promotion.
|
||||
*[relativeAmount]: This indicates a number, usually with a + or - sign, such as `+25` (this kind of parameter is often followed by '%' which is nevertheless not part of the value).
|
||||
*[resource]: The name of any resource.
|
||||
*[resourceFilter]: A resource name, type, 'all', or a Stat listed in the resource's improvementStats.
|
||||
*[specialist]: The name of any specialist.
|
||||
*[speed]: The name of any speed.
|
||||
*[stat]: This is one of the 7 major stats in the game - `Gold`, `Science`, `Production`, `Food`, `Happiness`, `Culture` and `Faith`. Note that the stat names need to be capitalized!
|
||||
|
Reference in New Issue
Block a user