mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-10 23:37:31 +07:00
Enumified all remaining resource & improvement uniques (#5523)
* Added conditionals & enumified improvement stat uniques * Enumified all the other uniques * Fixed bug where improvemen stat icons didn't show up in tech tree & reviews
This commit is contained in:
parent
e9d36ea0ce
commit
b66443574e
@ -390,7 +390,7 @@
|
||||
"culture": 1,
|
||||
"isWonder": true,
|
||||
"greatPersonPoints": {"Great Engineer": 1},
|
||||
"uniques": ["Must be next to [Desert]", "[+1 Food, +1 Production, +1 Gold] from [Desert] tiles without [Flood plains] [in this city]", "Gain a free [Amphitheater] [in this city]", "[+6 Culture] once [Archaeology] is discovered"],
|
||||
"uniques": ["Must be next to [Desert]", "[+1 Food, +1 Production, +1 Gold] from [Desert] tiles without [Flood plains] [in this city]", "Gain a free [Amphitheater] [in this city]", "[+6 Culture] <after discovering [Archaeology]>"],
|
||||
"requiredTech": "Currency",
|
||||
"quote": "'...who drinks the water I shall give him, says the Lord, will have a spring inside him welling up for eternal life. Let them bring me to your holy mountain in the place where you dwell. Across the desert and through the mountain to the Canyon of the Crescent Moon...' - Indiana Jones"
|
||||
},
|
||||
@ -603,7 +603,7 @@
|
||||
"culture": 2,
|
||||
"hurryCostModifier": 25,
|
||||
"requiredBuilding": "Walls",
|
||||
"uniques": ["[+1 Gold] once [Flight] is discovered", "Destroyed when the city is captured"],
|
||||
"uniques": ["[+1 Gold] <after discovering [Flight]>", "Destroyed when the city is captured"],
|
||||
"requiredTech": "Chivalry"
|
||||
},
|
||||
{
|
||||
|
@ -7,8 +7,8 @@
|
||||
"turnsToBuild": 7,
|
||||
"techRequired": "Agriculture",
|
||||
"uniques": ["Can also be built on tiles adjacent to fresh water",
|
||||
"[+1 Food] on [Fresh water] tiles once [Civil Service] is discovered",
|
||||
"[+1 Food] on [non-fresh water] tiles once [Fertilizer] is discovered"],
|
||||
"[+1 Food] from [Fresh water] tiles <after discovering [Civil Service]>",
|
||||
"[+1 Food] from [non-fresh water] tiles <after discovering [Fertilizer]>"],
|
||||
"shortcutKey": "F"
|
||||
},
|
||||
{
|
||||
@ -17,7 +17,7 @@
|
||||
"production": 1,
|
||||
"turnsToBuild": 7,
|
||||
"techRequired": "Construction",
|
||||
"uniques": ["[+1 Production] once [Scientific Theory] is discovered"],
|
||||
"uniques": ["[+1 Production] <after discovering [Scientific Theory]>"],
|
||||
"shortcutKey": "L"
|
||||
},
|
||||
{
|
||||
@ -26,7 +26,7 @@
|
||||
"production": 1,
|
||||
"turnsToBuild": 7,
|
||||
"techRequired": "Mining",
|
||||
"uniques": ["[+1 Production] once [Chemistry] is discovered"],
|
||||
"uniques": ["[+1 Production] <after discovering [Chemistry]>"],
|
||||
"shortcutKey": "M"
|
||||
},
|
||||
{
|
||||
@ -35,7 +35,7 @@
|
||||
"gold": 1,
|
||||
"turnsToBuild": 7,
|
||||
"techRequired": "Guilds",
|
||||
"uniques": ["[+1 Gold] once [Economics] is discovered"],
|
||||
"uniques": ["[+1 Gold] <after discovering [Economics]>"],
|
||||
"shortcutKey": "T"
|
||||
},
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
"name": "Camp",
|
||||
"turnsToBuild": 7,
|
||||
"techRequired": "Trapping",
|
||||
"uniques": ["Does not need removal of [Forest]","Does not need removal of [Jungle]","[+1 Gold] once [Economics] is discovered"],
|
||||
"uniques": ["Does not need removal of [Forest]","Does not need removal of [Jungle]","[+1 Gold] <after discovering [Economics]>"],
|
||||
"shortcutKey": "C"
|
||||
},
|
||||
{
|
||||
@ -59,7 +59,7 @@
|
||||
"name": "Pasture",
|
||||
"turnsToBuild": 8,
|
||||
"techRequired": "Animal Husbandry",
|
||||
"uniques": ["[+1 Food] once [Fertilizer] is discovered"],
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>"],
|
||||
"shortcutKey": "P"
|
||||
},
|
||||
{
|
||||
@ -67,14 +67,14 @@
|
||||
"turnsToBuild": 6,
|
||||
"gold": 1,
|
||||
"techRequired": "Calendar",
|
||||
"uniques": ["[+1 Food] once [Fertilizer] is discovered"],
|
||||
"uniques": ["[+1 Food] <after discovering [Fertilizer]>"],
|
||||
"shortcutKey": "P"
|
||||
},
|
||||
{
|
||||
"name": "Quarry",
|
||||
"turnsToBuild": 8,
|
||||
"techRequired": "Masonry",
|
||||
"uniques": ["[+1 Production] once [Chemistry] is discovered"],
|
||||
"uniques": ["[+1 Production] <after discovering [Chemistry]>"],
|
||||
"shortcutKey": "Q"
|
||||
},
|
||||
{
|
||||
@ -82,7 +82,7 @@
|
||||
"terrainsCanBeBuiltOn": ["Coast"],
|
||||
"food": 1,
|
||||
"techRequired": "Sailing",
|
||||
"uniques": ["[+1 Gold] once [Compass] is discovered"]
|
||||
"uniques": ["[+1 Gold] <after discovering [Compass]>"]
|
||||
},
|
||||
|
||||
// Military improvement
|
||||
@ -100,6 +100,7 @@
|
||||
"name": "Road",
|
||||
"turnsToBuild": 4,
|
||||
"techRequired": "The Wheel",
|
||||
// "Costs [1] gold per turn when in your territory" does nothing and is only to inform the user
|
||||
"uniques": ["Can be built outside your borders", "Costs [1] gold per turn when in your territory"],
|
||||
"shortcutKey": "R",
|
||||
"civilopediaText": [
|
||||
@ -173,7 +174,7 @@
|
||||
{
|
||||
"name": "Academy",
|
||||
"science": 8,
|
||||
"uniques": ["Great Improvement", "[+2 Science] once [Scientific Theory] is discovered", "[+2 Science] once [Atomic Theory] is discovered"]
|
||||
"uniques": ["Great Improvement", "[+2 Science] <after discovering [Scientific Theory]>", "[+2 Science] <after discovering [Atomic Theory]>"]
|
||||
},
|
||||
{
|
||||
"name": "Landmark",
|
||||
@ -183,12 +184,12 @@
|
||||
{
|
||||
"name": "Manufactory",
|
||||
"production": 4,
|
||||
"uniques": ["Great Improvement", "[+1 Production] once [Chemistry] is discovered"]
|
||||
"uniques": ["Great Improvement", "[+1 Production] <after discovering [Chemistry]>"]
|
||||
},
|
||||
{
|
||||
"name": "Customs house",
|
||||
"gold": 4,
|
||||
"uniques": ["Great Improvement", "[+1 Gold] once [Economics] is discovered"]
|
||||
"uniques": ["Great Improvement", "[+1 Gold] <after discovering [Economics]>"]
|
||||
},
|
||||
{
|
||||
"name": "Holy site",
|
||||
@ -197,7 +198,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Citadel",
|
||||
"uniques": ["Great Improvement", "Gives a defensive bonus of [100]%", "Deal 30 damage to adjacent enemy units", "Can be built just outside your borders"],
|
||||
"uniques": ["Great Improvement", "Gives a defensive bonus of [100]%", "Adjacent enemy units ending their turn take [30] damage", "Can be built just outside your borders"],
|
||||
"civilopediaText": [{"text":"Constructing it will take over the tiles around it and assign them to your closest city"}]
|
||||
},
|
||||
|
||||
@ -207,7 +208,7 @@
|
||||
"uniqueTo": "Polynesia",
|
||||
"culture": 1,
|
||||
"turnsToBuild": 4,
|
||||
"uniques": ["Can only be built on [Coastal] tiles", "[+1 Culture] for each adjacent [Moai]", "[+1 Gold] once [Flight] is discovered"],
|
||||
"uniques": ["Can only be built on [Coastal] tiles", "[+1 Culture] for each adjacent [Moai]", "[+1 Gold] <after discovering [Flight]>"],
|
||||
"techRequired": "Construction",
|
||||
"shortcutKey": "M"
|
||||
},
|
||||
@ -219,8 +220,8 @@
|
||||
"turnsToBuild": 7,
|
||||
"uniques": ["Cannot be built on [Bonus resource] tiles",
|
||||
"[+1 Food] for each adjacent [Mountain]",
|
||||
"[+1 Food] on [Fresh water] tiles once [Civil Service] is discovered",
|
||||
"[+1 Food] on [non-fresh water] tiles once [Fertilizer] is discovered"],
|
||||
"[+1 Food] on [Fresh water] tiles <after discovering [Civil Service]>",
|
||||
"[+1 Food] on [non-fresh water] tiles <after discovering[Fertilizer]>"],
|
||||
"techRequired": "Construction",
|
||||
"shortcutKey": "F"
|
||||
},
|
||||
@ -230,7 +231,7 @@
|
||||
"food": 3,
|
||||
"terrainsCanBeBuiltOn": ["Marsh", "Flood plains"],
|
||||
"turnsToBuild": 7,
|
||||
"uniques": ["[+1 Production, +2 Gold] once [Economics] is discovered"],
|
||||
"uniques": ["[+1 Production, +2 Gold] <after discovering [Economics]>"],
|
||||
"techRequired": "Guilds",
|
||||
"shortcutKey": "F"
|
||||
},
|
||||
|
@ -324,5 +324,5 @@
|
||||
"gold": 2,
|
||||
"improvement": "Camp",
|
||||
"improvementStats": {"gold": 1}
|
||||
},
|
||||
}
|
||||
]
|
||||
|
@ -212,7 +212,7 @@ object GameStarter {
|
||||
|
||||
val allMercantileResources = ruleset.tileResources.values.filter {
|
||||
it.unique == "Can only be created by Mercantile City-States" // Deprecated as of 3.16.16
|
||||
|| it.uniques.contains("Can only be created by Mercantile City-States") }.map { it.name }
|
||||
|| it.hasUnique(UniqueType.CityStateOnlyResource) }.map { it.name }
|
||||
|
||||
|
||||
val unusedMercantileResources = Stack<String>()
|
||||
|
@ -695,7 +695,7 @@ object Battle {
|
||||
}
|
||||
|
||||
// Remove improvements, add fallout
|
||||
if (tile.improvement != null && !tile.getTileImprovement()!!.hasUnique("Indestructible")) {
|
||||
if (tile.improvement != null && !tile.getTileImprovement()!!.hasUnique(UniqueType.Indestructible)) {
|
||||
tile.improvement = null
|
||||
}
|
||||
tile.improvementInProgress = null
|
||||
@ -758,7 +758,7 @@ object Battle {
|
||||
}
|
||||
|
||||
// Remove improvements
|
||||
if (tile.improvement != null && !tile.getTileImprovement()!!.hasUnique("Indestructible")) {
|
||||
if (tile.improvement != null && !tile.getTileImprovement()!!.hasUnique(UniqueType.Indestructible)) {
|
||||
tile.improvement = null
|
||||
}
|
||||
tile.improvementInProgress = null
|
||||
|
@ -116,10 +116,12 @@ class CityConstructions {
|
||||
stats.add(unique.stats.times(cityInfo.population.population / unique.params[1].toFloat()))
|
||||
}
|
||||
|
||||
for (unique in cityInfo.getLocalMatchingUniques("[] once [] is discovered")) {
|
||||
if (cityInfo.civInfo.tech.isResearched(unique.params[1]))
|
||||
stats.add(unique.stats)
|
||||
}
|
||||
// Deprecated since 3.17.11
|
||||
for (unique in cityInfo.getLocalMatchingUniques(UniqueType.StatsWithTech)) {
|
||||
if (cityInfo.civInfo.tech.isResearched(unique.params[1]))
|
||||
stats.add(unique.stats)
|
||||
}
|
||||
//
|
||||
|
||||
return stats
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
for (tech in startingTechs)
|
||||
civInfo.tech.techsResearched.add(tech.name) // can't be .addTechnology because the civInfo isn't assigned yet
|
||||
|
||||
val allMercantileResources = ruleset.tileResources.values.filter { it.hasUnique("Can only be created by Mercantile City-States") }.map { it.name }
|
||||
val allMercantileResources = ruleset.tileResources.values.filter { it.hasUnique(UniqueType.CityStateOnlyResource) }.map { it.name }
|
||||
val allPossibleBonuses = HashSet<Unique>() // We look through these to determine what kind of city state we are
|
||||
var fallback = false
|
||||
for (era in ruleset.eras.values) {
|
||||
|
@ -1014,15 +1014,16 @@ class MapUnit {
|
||||
private fun doCitadelDamage() {
|
||||
// Check for Citadel damage - note: 'Damage does not stack with other Citadels'
|
||||
val citadelTile = currentTile.neighbors
|
||||
.firstOrNull {
|
||||
it.getOwner() != null && civInfo.isAtWarWith(it.getOwner()!!) &&
|
||||
with(it.getTileImprovement()) {
|
||||
this != null && this.hasUnique("Deal 30 damage to adjacent enemy units")
|
||||
}
|
||||
.filter {
|
||||
it.getOwner() != null && civInfo.isAtWarWith(it.getOwner()!!) && it.improvement != null
|
||||
}.maxByOrNull { tile ->
|
||||
tile.getTileImprovement()!!
|
||||
.getMatchingUniques(UniqueType.DamagesAdjacentEnemyUnits)
|
||||
.sumOf { it.params[0].toInt() }
|
||||
}
|
||||
|
||||
if (citadelTile != null) {
|
||||
health -= 30
|
||||
health -= citadelTile.getTileImprovement()!!.getMatchingUniques(UniqueType.DamagesAdjacentEnemyUnits).sumOf { it.params[0].toInt() }
|
||||
val locations = LocationAction(listOf(citadelTile.position, currentTile.position))
|
||||
if (health <= 0) {
|
||||
civInfo.addNotification(
|
||||
|
@ -10,6 +10,7 @@ import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.tile.*
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
@ -354,21 +355,31 @@ open class TileInfo {
|
||||
}
|
||||
|
||||
fun getImprovementStats(improvement: TileImprovement, observingCiv: CivilizationInfo, city: CityInfo?): Stats {
|
||||
val stats = improvement.clone() // clones the stats of the improvement, not the improvement itself
|
||||
val stats = improvement.cloneStats()
|
||||
if (hasViewableResource(observingCiv) && tileResource.improvement == improvement.name)
|
||||
stats.add(tileResource.improvementStats!!.clone()) // resource-specific improvement
|
||||
|
||||
for (unique in improvement.uniqueObjects)
|
||||
if (unique.placeholderText == "[] once [] is discovered" && observingCiv.tech.isResearched(unique.params[1]))
|
||||
stats.add(unique.stats)
|
||||
// Deprecated since 3.17.10
|
||||
for (unique in improvement.getMatchingUniques(UniqueType.StatsWithTech)) {
|
||||
if (observingCiv.tech.isResearched(unique.params[1]))
|
||||
stats.add(unique.stats)
|
||||
}
|
||||
//
|
||||
|
||||
for (unique in improvement.getMatchingUniques(UniqueType.Stats, StateForConditionals(civInfo = observingCiv, cityInfo = city))) {
|
||||
stats.add(unique.stats)
|
||||
}
|
||||
|
||||
if (city != null) {
|
||||
val tileUniques = city.getMatchingUniques(UniqueType.StatsFromTiles)
|
||||
val tileUniques = city.getMatchingUniques(UniqueType.StatsFromTiles, StateForConditionals(civInfo = observingCiv, cityInfo = city))
|
||||
.filter { city.matchesFilter(it.params[2]) }
|
||||
val improvementUniques = improvement.uniqueObjects.filter {
|
||||
it.placeholderText == "[] on [] tiles once [] is discovered"
|
||||
&& observingCiv.tech.isResearched(it.params[2])
|
||||
}
|
||||
val improvementUniques =
|
||||
// Deprecated since 3.17.10
|
||||
improvement.getMatchingUniques(UniqueType.StatsOnTileWithTech)
|
||||
.filter { observingCiv.tech.isResearched(it.params[2]) } +
|
||||
//
|
||||
improvement.getMatchingUniques(UniqueType.ImprovementStatsOnTile, StateForConditionals(civInfo = observingCiv, cityInfo = city))
|
||||
|
||||
for (unique in tileUniques + improvementUniques) {
|
||||
if (improvement.matchesFilter(unique.params[1])
|
||||
// Freshwater and non-freshwater cannot be moved to matchesUniqueFilter since that creates an endless feedback.
|
||||
@ -385,15 +396,14 @@ open class TileInfo {
|
||||
}
|
||||
}
|
||||
|
||||
for (unique in improvement.uniqueObjects)
|
||||
if (unique.placeholderText == "[] for each adjacent []") {
|
||||
val adjacent = unique.params[1]
|
||||
val numberOfBonuses = neighbors.count {
|
||||
it.matchesFilter(adjacent, observingCiv)
|
||||
|| it.roadStatus.name == adjacent
|
||||
}
|
||||
stats.add(unique.stats.times(numberOfBonuses.toFloat()))
|
||||
for (unique in improvement.getMatchingUniques(UniqueType.ImprovementStatsForAdjacencies)) {
|
||||
val adjacent = unique.params[1]
|
||||
val numberOfBonuses = neighbors.count {
|
||||
it.matchesFilter(adjacent, observingCiv)
|
||||
|| it.roadStatus.name == adjacent
|
||||
}
|
||||
stats.add(unique.stats.times(numberOfBonuses.toFloat()))
|
||||
}
|
||||
|
||||
for (unique in observingCiv.getMatchingUniques("+[]% yield from every []"))
|
||||
if (improvement.matchesFilter(unique.params[1]))
|
||||
@ -408,16 +418,16 @@ open class TileInfo {
|
||||
improvement.uniqueTo != null && improvement.uniqueTo != civInfo.civName -> false
|
||||
improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!) -> false
|
||||
getOwner() != civInfo && !(
|
||||
improvement.hasUnique("Can be built outside your borders")
|
||||
// citadel can be built only next to or within own borders
|
||||
|| improvement.hasUnique("Can be built just outside your borders")
|
||||
&& neighbors.any { it.getOwner() == civInfo } && civInfo.cities.isNotEmpty()
|
||||
) -> false
|
||||
improvement.hasUnique(UniqueType.CanBuildOutsideBorders)
|
||||
|| ( // citadel can be built only next to or within own borders
|
||||
improvement.hasUnique(UniqueType.CanBuildJustOutsideBorders)
|
||||
&& neighbors.any { it.getOwner() == civInfo } && civInfo.cities.isNotEmpty()
|
||||
)
|
||||
) -> false
|
||||
improvement.uniqueObjects.any {
|
||||
it.placeholderText == "Obsolete with []" && civInfo.tech.isResearched(it.params[0])
|
||||
} -> return false
|
||||
improvement.uniqueObjects.any {
|
||||
it.placeholderText == "Cannot be built on [] tiles until [] is discovered" &&
|
||||
improvement.getMatchingUniques(UniqueType.RequiresTechToBuildOnTile).any {
|
||||
matchesTerrainFilter(it.params[0]) && !civInfo.tech.isResearched(it.params[1])
|
||||
} -> false
|
||||
improvement.uniqueObjects.any {
|
||||
@ -437,8 +447,8 @@ open class TileInfo {
|
||||
return when {
|
||||
improvement.name == this.improvement -> false
|
||||
isCityCenter() -> false
|
||||
improvement.uniqueObjects.filter { it.placeholderText == "Cannot be built on [] tiles" }.any {
|
||||
unique -> matchesTerrainFilter(unique.params[0])
|
||||
improvement.getMatchingUniques(UniqueType.CannotBuildOnTile).any {
|
||||
unique -> matchesTerrainFilter(unique.params[0])
|
||||
} -> false
|
||||
|
||||
// Road improvements can change on tiles with irremovable improvements - nothing else can, though.
|
||||
@ -463,7 +473,7 @@ open class TileInfo {
|
||||
improvement.name == roadStatus.removeAction -> true
|
||||
topTerrain.unbuildable && !improvement.isAllowedOnFeature(topTerrain.name) -> false
|
||||
// DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests.
|
||||
improvement.hasUnique("Can also be built on tiles adjacent to fresh water") && isAdjacentToFreshwater -> true
|
||||
improvement.hasUnique(UniqueType.ImprovementBuildableByFreshWater) && isAdjacentToFreshwater -> true
|
||||
|
||||
// If an unique of this type exists, we want all to match (e.g. Hill _and_ Forest would be meaningful).
|
||||
improvement.uniqueObjects.filter { it.placeholderText == "Can only be built on [] tiles" }.let {
|
||||
@ -544,9 +554,8 @@ open class TileInfo {
|
||||
var bonus = getLastTerrain().defenceBonus
|
||||
val tileImprovement = getTileImprovement()
|
||||
if (tileImprovement != null) {
|
||||
for (unique in tileImprovement.uniqueObjects)
|
||||
if (unique.placeholderText == "Gives a defensive bonus of []%")
|
||||
bonus += unique.params[0].toFloat() / 100
|
||||
for (unique in tileImprovement.getMatchingUniques(UniqueType.DefensiveBonus))
|
||||
bonus += unique.params[0].toFloat() / 100
|
||||
}
|
||||
return bonus
|
||||
}
|
||||
@ -761,7 +770,7 @@ open class TileInfo {
|
||||
|
||||
if (newResource.resourceType != ResourceType.Strategic) return
|
||||
|
||||
for (unique in newResource.getMatchingUniques(UniqueType.OverrideDepositAmountOnTileFilter)) {
|
||||
for (unique in newResource.getMatchingUniques(UniqueType.ResourceAmountOnTiles)) {
|
||||
if (matchesTerrainFilter(unique.params[0])) {
|
||||
resourceAmount = unique.params[1].toInt()
|
||||
return
|
||||
|
@ -6,11 +6,10 @@ import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.CityStateType
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.squareBraceRegex
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.civilopedia.ICivilopediaText
|
||||
import com.unciv.ui.utils.Fonts
|
||||
import com.unciv.ui.utils.colorFromRGB
|
||||
|
||||
@ -177,7 +176,7 @@ class Nation : RulesetObject() {
|
||||
if (showResources) {
|
||||
val allMercantileResources = ruleset.tileResources.values
|
||||
.filter { it.unique == "Can only be created by Mercantile City-States" // Deprecated 3.16.16
|
||||
|| it.uniques.contains("Can only be created by Mercantile City-States") }
|
||||
|| it.hasUnique(UniqueType.CityStateOnlyResource) }
|
||||
|
||||
if (allMercantileResources.isNotEmpty()) {
|
||||
textList += FormattedLine()
|
||||
|
@ -36,13 +36,26 @@ class Technology: RulesetObject() {
|
||||
val lineList = ArrayList<String>() // more readable than StringBuilder, with same performance for our use-case
|
||||
for (unique in uniques) lineList += unique.tr()
|
||||
|
||||
for (improvement in ruleset.tileImprovements.values)
|
||||
for (improvement in ruleset.tileImprovements.values) {
|
||||
for (unique in improvement.uniqueObjects) {
|
||||
if (unique.placeholderText == "[] once [] is discovered" && unique.params.last() == name)
|
||||
// Deprecated since 3.17.10
|
||||
if (unique.isOfType(UniqueType.StatsWithTech) && unique.params.last() == name)
|
||||
lineList += "[${unique.params[0]}] from every [${improvement.name}]"
|
||||
else if (unique.isOfType(UniqueType.StatsOnTileWithTech) && unique.params.last() == name)
|
||||
lineList += "[${unique.params[0]}] from every [${improvement.name}] on [${unique.params[1]}] tiles"
|
||||
else
|
||||
//
|
||||
if (unique.isOfType(UniqueType.Stats)) {
|
||||
val requiredTech = unique.conditionals.firstOrNull { it.isOfType(UniqueType.ConditionalTech) }?.params?.get(0)
|
||||
if (requiredTech != name) continue
|
||||
lineList += "[${unique.params[0]}] from every [${improvement.name}]"
|
||||
else if (unique.placeholderText == "[] on [] tiles once [] is discovered" && unique.params.last() == name)
|
||||
} else if (unique.isOfType(UniqueType.ImprovementStatsOnTile)) {
|
||||
val requiredTech = unique.conditionals.firstOrNull { it.isOfType(UniqueType.ConditionalTech) }?.params?.get(0)
|
||||
if (requiredTech != name) continue
|
||||
lineList += "[${unique.params[0]}] from every [${improvement.name}] on [${unique.params[1]}] tiles"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val viewingCiv = UncivGame.Current.worldScreen.viewingCiv
|
||||
val enabledUnits = getEnabledUnits(viewingCiv)
|
||||
@ -176,12 +189,26 @@ class Technology: RulesetObject() {
|
||||
var wantEmpty = true
|
||||
for (improvement in ruleset.tileImprovements.values)
|
||||
for (unique in improvement.uniqueObjects) {
|
||||
if (unique.placeholderText == "[] once [] is discovered" && unique.params.last() == name) {
|
||||
if (wantEmpty) { lineList += FormattedLine(); wantEmpty = false }
|
||||
// Deprecated since 3.17.10
|
||||
if (unique.isOfType(UniqueType.StatsWithTech) && unique.params.last() == name) {
|
||||
if (wantEmpty) { lineList += FormattedLine(); wantEmpty = false }
|
||||
lineList += FormattedLine("[${unique.params[0]}] from every [${improvement.name}]",
|
||||
link = improvement.makeLink())
|
||||
} else if (unique.isOfType(UniqueType.StatsOnTileWithTech) && unique.params.last() == name) {
|
||||
if (wantEmpty) { lineList += FormattedLine(); wantEmpty = false }
|
||||
lineList += FormattedLine("[${unique.params[0]}] from every [${improvement.name}] on [${unique.params[1]}] tiles",
|
||||
link = improvement.makeLink())
|
||||
}
|
||||
else
|
||||
//
|
||||
if (unique.isOfType(UniqueType.Stats)) {
|
||||
val requiredTech = unique.conditionals.firstOrNull { it.isOfType(UniqueType.ConditionalTech) }?.params?.get(0)
|
||||
if (requiredTech != name) continue
|
||||
lineList += FormattedLine("[${unique.params[0]}] from every [${improvement.name}]",
|
||||
link = improvement.makeLink())
|
||||
} else if (unique.placeholderText == "[] on [] tiles once [] is discovered" && unique.params.last() == name) {
|
||||
if (wantEmpty) { lineList += FormattedLine(); wantEmpty = false }
|
||||
} else if (unique.placeholderText == "[] on [] tiles") {
|
||||
val requiredTech = unique.conditionals.firstOrNull { it.isOfType(UniqueType.ConditionalTech) }?.params?.get(0)
|
||||
if (requiredTech != name) continue
|
||||
lineList += FormattedLine("[${unique.params[0]}] from every [${improvement.name}] on [${unique.params[1]}] tiles",
|
||||
link = improvement.makeLink())
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
package com.unciv.models.ruleset.tile
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.models.ruleset.Belief
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetStatsObject
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.tr
|
||||
@ -67,9 +65,9 @@ class TileImprovement : RulesetStatsObject() {
|
||||
return lines.joinToString("\n")
|
||||
}
|
||||
|
||||
fun isGreatImprovement() = hasUnique("Great Improvement")
|
||||
fun isGreatImprovement() = hasUnique(UniqueType.GreatImprovement)
|
||||
fun isRoad() = RoadStatus.values().any { it != RoadStatus.None && it.name == this.name }
|
||||
fun isAncientRuinsEquivalent() = hasUnique("Provides a random bonus when entered")
|
||||
fun isAncientRuinsEquivalent() = hasUnique(UniqueType.IsAncientRuinsEquivalent)
|
||||
|
||||
/**
|
||||
* Check: Is this improvement allowed on a [given][name] terrain feature?
|
||||
@ -81,11 +79,7 @@ class TileImprovement : RulesetStatsObject() {
|
||||
* so this check is done in conjunction - for the user, success means he does not need to remove
|
||||
* a terrain feature, thus the unique name.
|
||||
*/
|
||||
fun isAllowedOnFeature(name: String): Boolean {
|
||||
return uniqueObjects.filter { it.placeholderText == "Does not need removal of []"
|
||||
&& it.params[0] == name
|
||||
}.any()
|
||||
}
|
||||
fun isAllowedOnFeature(name: String) = getMatchingUniques(UniqueType.NoFeatureRemovalNeeded).any { it.params[0] == name }
|
||||
|
||||
fun matchesFilter(filter: String): Boolean {
|
||||
return when (filter) {
|
||||
|
@ -5,7 +5,6 @@ import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.models.translations.*
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
|
||||
|
||||
class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val sourceObjectName: String? = null) {
|
||||
@ -22,6 +21,8 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
|
||||
}
|
||||
val conditionals: List<Unique> = text.getConditionals()
|
||||
|
||||
val allParams = params + conditionals.flatMap { it.params }
|
||||
|
||||
fun isOfType(uniqueType: UniqueType) = uniqueType == type
|
||||
|
||||
fun conditionalsApply(civInfo: CivilizationInfo? = null, city: CityInfo? = null): Boolean {
|
||||
@ -53,7 +54,15 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
|
||||
state.civInfo != null && state.civInfo.getEraNumber() >= state.civInfo.gameInfo.ruleSet.eras[condition.params[0]]!!.eraNumber
|
||||
UniqueType.ConditionalDuringEra ->
|
||||
state.civInfo != null && state.civInfo.getEraNumber() == state.civInfo.gameInfo.ruleSet.eras[condition.params[0]]!!.eraNumber
|
||||
|
||||
UniqueType.ConditionalTech ->
|
||||
state.civInfo != null && state.civInfo.tech.isResearched(condition.params[0])
|
||||
UniqueType.ConditionalNoTech ->
|
||||
state.civInfo != null && !state.civInfo.tech.isResearched(condition.params[0])
|
||||
UniqueType.ConditionalPolicy ->
|
||||
state.civInfo != null && state.civInfo.policies.isAdopted(condition.params[0])
|
||||
UniqueType.ConditionalNoPolicy ->
|
||||
state.civInfo != null && !state.civInfo.policies.isAdopted(condition.params[0])
|
||||
|
||||
UniqueType.ConditionalSpecialistCount ->
|
||||
state.cityInfo != null && state.cityInfo.population.getNumberOfSpecialists() >= condition.params[0].toInt()
|
||||
|
||||
|
@ -222,6 +222,17 @@ enum class UniqueParameterType(val parameterName:String) {
|
||||
else -> UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
||||
}
|
||||
},
|
||||
Policy("policy") {
|
||||
override fun getErrorSeverity(
|
||||
parameterText: String,
|
||||
ruleset: Ruleset
|
||||
): UniqueType.UniqueComplianceErrorSeverity? {
|
||||
return when (parameterText) {
|
||||
in ruleset.policies -> null
|
||||
else -> UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
||||
}
|
||||
}
|
||||
},
|
||||
/** Behaves like [Unknown], but states explicitly the parameter is OK and its contents are ignored */
|
||||
Comment("comment") {
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
|
@ -62,7 +62,7 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
|
||||
|
||||
/////// Stat providing uniques
|
||||
|
||||
Stats("[stats]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
|
||||
Stats("[stats]", UniqueTarget.Global, UniqueTarget.FollowerBelief, UniqueTarget.Improvement),
|
||||
StatsPerCity("[stats] [cityFilter]", UniqueTarget.Global),
|
||||
@Deprecated("As of 3.16.16 - removed as of 3.17.11", ReplaceWith("[stats] <if this city has at least [amount] specialists>"), DeprecationLevel.ERROR)
|
||||
StatBonusForNumberOfSpecialists("[stats] if this city has at least [amount] specialists", UniqueTarget.Global),
|
||||
@ -175,7 +175,6 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
|
||||
RequiresAnotherBuilding("Requires a [buildingName] in this city", UniqueTarget.Building),
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////// UNIT UNIQUES /////////////////////////////////////////
|
||||
|
||||
FoundCity("Founds a new city", UniqueTarget.Unit),
|
||||
@ -306,13 +305,41 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
|
||||
FreshWater("Fresh water", UniqueTarget.Terrain),
|
||||
RoughTerrain("Rough terrain", UniqueTarget.Terrain),
|
||||
|
||||
// Resource uniques
|
||||
OverrideDepositAmountOnTileFilter("Deposits in [tileFilter] tiles always provide [amount] resources", UniqueTarget.Resource),
|
||||
/////// Resource uniques
|
||||
ResourceAmountOnTiles("Deposits in [tileFilter] tiles always provide [amount] resources", UniqueTarget.Resource),
|
||||
CityStateOnlyResource("Can only be created by Mercantile City-States", UniqueTarget.Resource),
|
||||
|
||||
////// Improvement uniques
|
||||
ImprovementBuildableByFreshWater("Can also be built on tiles adjacent to fresh water", UniqueTarget.Improvement),
|
||||
ImprovementStatsOnTile("[stats] from [tileFilter] tiles", UniqueTarget.Improvement),
|
||||
@Deprecated("As of 3.17.10", ReplaceWith("[stats] from [tileFilter] tiles <after discovering [tech]>"), DeprecationLevel.WARNING)
|
||||
StatsOnTileWithTech("[stats] on [tileFilter] tiles once [tech] is discovered", UniqueTarget.Improvement),
|
||||
@Deprecated("As of 3.17.10", ReplaceWith("[stats] <after discovering [tech]>"), DeprecationLevel.WARNING)
|
||||
StatsWithTech("[stats] once [tech] is discovered", UniqueTarget.Improvement, UniqueTarget.Building),
|
||||
ImprovementStatsForAdjacencies("[stats] for each adjacent [tileFilter]", UniqueTarget.Improvement),
|
||||
|
||||
CanBuildOutsideBorders("Can be built outside your borders", UniqueTarget.Improvement),
|
||||
CanBuildJustOutsideBorders("Can be built just outside your borders", UniqueTarget.Improvement),
|
||||
RequiresTechToBuildOnTile("Cannot be built on [tileFilter] tiles until [tech] is discovered", UniqueTarget.Improvement),
|
||||
CannotBuildOnTile("Cannot be built on [tileFilter] tiles", UniqueTarget.Improvement),
|
||||
NoFeatureRemovalNeeded("Does not need removal of [tileFilter]", UniqueTarget.Improvement),
|
||||
|
||||
DefensiveBonus("Gives a defensive bonus of [amount]%", UniqueTarget.Improvement),
|
||||
ImprovementMaintenance("Costs [amount] gold per turn when in your territory", UniqueTarget.Improvement), // Unused
|
||||
DamagesAdjacentEnemyUnits("Deal [amount] damage to adjacent enemy units", UniqueTarget.Improvement),
|
||||
@Deprecated("As of 3.17.10", ReplaceWith("Adjacent enemy units ending their turn take [30] damage"), DeprecationLevel.WARNING)
|
||||
DamagesAdjacentEnemyUnitsForExactlyThirtyDamage("Deal 30 damage to adjacent enemy units", UniqueTarget.Improvement),
|
||||
|
||||
GreatImprovement("Great Improvement", UniqueTarget.Improvement),
|
||||
IsAncientRuinsEquivalent("Provides a random bonus when entered", UniqueTarget.Improvement),
|
||||
|
||||
Unpillagable("Unpillagable", UniqueTarget.Improvement),
|
||||
Indestructible("Indestructible", UniqueTarget.Improvement),
|
||||
|
||||
///////////////////////////////////////// CONDITIONALS /////////////////////////////////////////
|
||||
|
||||
|
||||
// civ conditionals
|
||||
/////// civ conditionals
|
||||
ConditionalWar("when at war", UniqueTarget.Conditional),
|
||||
ConditionalNotWar("when not at war", UniqueTarget.Conditional),
|
||||
ConditionalHappy("while the empire is happy", UniqueTarget.Conditional),
|
||||
@ -321,11 +348,16 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
|
||||
ConditionalDuringEra("during the [era]", UniqueTarget.Conditional),
|
||||
ConditionalBeforeEra("before the [era]", UniqueTarget.Conditional),
|
||||
ConditionalStartingFromEra("starting from the [era]", UniqueTarget.Conditional),
|
||||
|
||||
ConditionalTech("after discovering [tech]", UniqueTarget.Conditional),
|
||||
ConditionalNoTech("before discovering [tech]", UniqueTarget.Conditional),
|
||||
ConditionalPolicy("after adopting [policy]", UniqueTarget.Conditional),
|
||||
ConditionalNoPolicy("before adopting [policy]", UniqueTarget.Conditional),
|
||||
|
||||
// city conditionals
|
||||
/////// city conditionals
|
||||
ConditionalSpecialistCount("if this city has at least [amount] specialists", UniqueTarget.Conditional),
|
||||
|
||||
// unit conditionals
|
||||
/////// unit conditionals
|
||||
ConditionalOurUnit("for [mapUnitFilter] units", UniqueTarget.Conditional),
|
||||
ConditionalVsCity("vs cities", UniqueTarget.Conditional),
|
||||
ConditionalVsUnits("vs [mapUnitFilter] units", UniqueTarget.Conditional),
|
||||
@ -333,9 +365,8 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
|
||||
ConditionalAttacking("when attacking", UniqueTarget.Conditional),
|
||||
ConditionalDefending("when defending", UniqueTarget.Conditional),
|
||||
ConditionalInTiles("when fighting in [tileFilter] tiles", UniqueTarget.Conditional),
|
||||
// ConditionalIntercepting("when intercepting", UniqueTarget.Conditional),
|
||||
|
||||
// tile conditionals
|
||||
/////// tile conditionals
|
||||
ConditionalNeighborTiles("with [amount] to [amount] neighboring [tileFilter] tiles", UniqueTarget.Conditional),
|
||||
ConditionalNeighborTilesAnd("with [amount] to [amount] neighboring [tileFilter] [tileFilter] tiles", UniqueTarget.Conditional),
|
||||
|
||||
|
@ -7,4 +7,8 @@ open class NamedStats : Stats(), INamed {
|
||||
override fun toString(): String {
|
||||
return name
|
||||
}
|
||||
|
||||
fun cloneStats(): Stats {
|
||||
return clone()
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ class Translations : LinkedHashMap<String, TranslationEntry>(){
|
||||
// Whenever this string is changed, it should also be changed in the translation files!
|
||||
// It is mostly used as the template for translating the order of conditionals
|
||||
const val englishConditionalOrderingString =
|
||||
"<for [mapUnitFilter] units> <vs cities> <vs [mapUnitFilter] units> <when fighting in [tileFilter] tiles> <when attacking> <when defending> <if this city has at least [amount] specialists> <when at war> <when not at war> <while the empire is happy> <during a Golden Age> <during the [era]> <before the [era]> <starting from the [era]>"
|
||||
"<for [mapUnitFilter] units> <vs cities> <vs [mapUnitFilter] units> <when fighting in [tileFilter] tiles> <when attacking> <when defending> <if this city has at least [amount] specialists> <when at war> <when not at war> <while the empire is happy> <during a Golden Age> <during the [era]> <before the [era]> <starting from the [era]> <with [techOrPolicy]> <without [techOrPolicy]>"
|
||||
const val conditionalUniqueOrderString = "ConditionalsPlacement"
|
||||
const val shouldCapitalizeString = "StartWithCapitalLetter"
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ class FormattedLine (
|
||||
val ruleSet = getCurrentRuleset()
|
||||
if (allObjectNamesCategoryMap == null || rulesetCachedInNameMap !== ruleSet)
|
||||
allObjectNamesCategoryMap = initNamesCategoryMap(ruleSet)
|
||||
for (parameter in unique.params) {
|
||||
for (parameter in unique.params + unique.conditionals.flatMap { it.params }) {
|
||||
val category = allObjectNamesCategoryMap!![parameter] ?: continue
|
||||
return category.name + "/" + parameter
|
||||
}
|
||||
|
@ -65,12 +65,14 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS
|
||||
})
|
||||
|
||||
for (improvement in ruleset.tileImprovements.values
|
||||
.filter {
|
||||
it.techRequired == techName || it.uniqueObjects.any { u -> u.params.contains(techName) }
|
||||
|| it.uniqueObjects.any { it.placeholderText == "[] once [] is discovered" && it.params[1] == techName }
|
||||
}
|
||||
.filter { it.uniqueTo == null || it.uniqueTo == civName })
|
||||
.filter {
|
||||
it.techRequired == techName
|
||||
|| it.uniqueObjects.any { u -> u.allParams.contains(techName) }
|
||||
}
|
||||
.filter { it.uniqueTo == null || it.uniqueTo == civName }
|
||||
) {
|
||||
techEnabledIcons.add(ImageGetter.getImprovementIcon(improvement.name, techIconSize))
|
||||
}
|
||||
|
||||
|
||||
for (resource in ruleset.tileResources.values.filter { it.revealedBy == techName })
|
||||
@ -78,7 +80,7 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS
|
||||
|
||||
for (unique in tech.uniques)
|
||||
techEnabledIcons.add(ImageGetter.getImage("OtherIcons/Star")
|
||||
.apply { color = Color.BLACK }.surroundWithCircle(techIconSize))
|
||||
.apply { color = Color.BLACK }.surroundWithCircle(techIconSize))
|
||||
|
||||
if (isWorldScreen) rightSide.add(techEnabledIcons)
|
||||
else rightSide.add(techEnabledIcons)
|
||||
|
@ -748,7 +748,7 @@ object UnitActions {
|
||||
fun canPillage(unit: MapUnit, tile: TileInfo): Boolean {
|
||||
val tileImprovement = tile.getTileImprovement()
|
||||
// City ruins, Ancient Ruins, Barbarian Camp, City Center marked in json
|
||||
if (tileImprovement == null || tileImprovement.hasUnique("Unpillagable")) return false
|
||||
if (tileImprovement == null || tileImprovement.hasUnique(UniqueType.Unpillagable)) return false
|
||||
val tileOwner = tile.getOwner()
|
||||
// Can't pillage friendly tiles, just like you can't attack them - it's an 'act of war' thing
|
||||
return tileOwner == null || unit.civInfo.isAtWarWith(tileOwner)
|
||||
|
Loading…
Reference in New Issue
Block a user