Support more freely modded Worker-like units (#6339)

* TileImprovementTime UniqueType supports UniqueTarget.Unit

* Reduce UniqueType.ConstructImprovementConsumingUnit hardcoded Great People behaviour

* Some linting

Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
SomeTroglodyte
2022-03-13 21:49:54 +01:00
committed by GitHub
parent 97787bd397
commit 4ffb21f525
6 changed files with 113 additions and 103 deletions

View File

@ -146,7 +146,7 @@ class WorkerAutomation(
&& tileCanBeImproved(unit, currentTile)) { && tileCanBeImproved(unit, currentTile)) {
if (WorkerAutomationConst.consoleOutput) if (WorkerAutomationConst.consoleOutput)
println("WorkerAutomation: ${unit.label()} -> start improving $currentTile") println("WorkerAutomation: ${unit.label()} -> start improving $currentTile")
return currentTile.startWorkingOnImprovement(chooseImprovement(unit, currentTile)!!, civInfo) return currentTile.startWorkingOnImprovement(chooseImprovement(unit, currentTile)!!, civInfo, unit)
} }
if (currentTile.improvementInProgress != null) return // we're working! if (currentTile.improvementInProgress != null) return // we're working!
@ -206,7 +206,7 @@ class WorkerAutomation(
BFS(toConnectTile, isCandidateTilePredicate).apply { BFS(toConnectTile, isCandidateTilePredicate).apply {
maxSize = HexMath.getNumberOfTilesInHexagon( maxSize = HexMath.getNumberOfTilesInHexagon(
WorkerAutomationConst.maxBfsReachPadding + WorkerAutomationConst.maxBfsReachPadding +
tilesOfConnectedCities.map { it.aerialDistanceTo(toConnectTile) }.minOrNull()!! tilesOfConnectedCities.minOf { it.aerialDistanceTo(toConnectTile) }
) )
bfsCache[toConnectTile.position] = this@apply bfsCache[toConnectTile.position] = this@apply
} }
@ -234,7 +234,7 @@ class WorkerAutomation(
if (unit.currentMovement > 0 && currentTile == tileToConstructRoadOn if (unit.currentMovement > 0 && currentTile == tileToConstructRoadOn
&& currentTile.improvementInProgress != bestRoadAvailable.name) { && currentTile.improvementInProgress != bestRoadAvailable.name) {
val improvement = bestRoadAvailable.improvement(ruleSet)!! val improvement = bestRoadAvailable.improvement(ruleSet)!!
tileToConstructRoadOn.startWorkingOnImprovement(improvement, civInfo) tileToConstructRoadOn.startWorkingOnImprovement(improvement, civInfo, unit)
} }
if (WorkerAutomationConst.consoleOutput) if (WorkerAutomationConst.consoleOutput)
println("WorkerAutomation: ${unit.label()} -> connect city ${bfs.startingPoint.getCity()?.name} to ${cityTile.getCity()!!.name} on $tileToConstructRoadOn") println("WorkerAutomation: ${unit.label()} -> connect city ${bfs.startingPoint.getCity()?.name} to ${cityTile.getCity()!!.name} on $tileToConstructRoadOn")

View File

@ -193,12 +193,12 @@ open class TileInfo {
// We have to .toList() so that the values are stored together once for caching, // We have to .toList() so that the values are stored together once for caching,
// and the toSequence so that aggregations (like neighbors.flatMap{it.units} don't take up their own space // and the toSequence so that aggregations (like neighbors.flatMap{it.units} don't take up their own space
/** Returns the left shared neighbor of [this] and [neighbor] (relative to the view direction [this]->[neighbor]), or null if there is no such tile. */ /** Returns the left shared neighbor of `this` and [neighbor] (relative to the view direction `this`->[neighbor]), or null if there is no such tile. */
fun getLeftSharedNeighbor(neighbor: TileInfo): TileInfo? { fun getLeftSharedNeighbor(neighbor: TileInfo): TileInfo? {
return tileMap.getClockPositionNeighborTile(this,(tileMap.getNeighborTileClockPosition(this, neighbor) - 2) % 12) return tileMap.getClockPositionNeighborTile(this,(tileMap.getNeighborTileClockPosition(this, neighbor) - 2) % 12)
} }
/** Returns the right shared neighbor [this] and [neighbor] (relative to the view direction [this]->[neighbor]), or null if there is no such tile. */ /** Returns the right shared neighbor of `this` and [neighbor] (relative to the view direction `this`->[neighbor]), or null if there is no such tile. */
fun getRightSharedNeighbor(neighbor: TileInfo): TileInfo? { fun getRightSharedNeighbor(neighbor: TileInfo): TileInfo? {
return tileMap.getClockPositionNeighborTile(this,(tileMap.getNeighborTileClockPosition(this, neighbor) + 2) % 12) return tileMap.getClockPositionNeighborTile(this,(tileMap.getNeighborTileClockPosition(this, neighbor) + 2) % 12)
} }
@ -262,8 +262,8 @@ open class TileInfo {
fun getTileStats(city: CityInfo?, observingCiv: CivilizationInfo?): Stats { fun getTileStats(city: CityInfo?, observingCiv: CivilizationInfo?): Stats {
var stats = getBaseTerrain().cloneStats() var stats = getBaseTerrain().cloneStats()
val stateForConditionals = StateForConditionals(civInfo = observingCiv, cityInfo = city, tile = this); val stateForConditionals = StateForConditionals(civInfo = observingCiv, cityInfo = city, tile = this)
for (terrainFeatureBase in terrainFeatureObjects) { for (terrainFeatureBase in terrainFeatureObjects) {
when { when {
@ -897,9 +897,10 @@ open class TileInfo {
} }
} }
fun startWorkingOnImprovement(improvement: TileImprovement, civInfo: CivilizationInfo) { fun startWorkingOnImprovement(improvement: TileImprovement, civInfo: CivilizationInfo, unit: MapUnit) {
improvementInProgress = improvement.name improvementInProgress = improvement.name
turnsToImprovement = if (civInfo.gameInfo.gameParameters.godMode) 1 else improvement.getTurnsToBuild(civInfo) turnsToImprovement = if (civInfo.gameInfo.gameParameters.godMode) 1
else improvement.getTurnsToBuild(civInfo, unit)
} }
fun stopWorkingOnImprovement() { fun stopWorkingOnImprovement() {

View File

@ -2,10 +2,12 @@ package com.unciv.models.ruleset.tile
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.RoadStatus
import com.unciv.models.ruleset.Belief import com.unciv.models.ruleset.Belief
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetStatsObject import com.unciv.models.ruleset.RulesetStatsObject
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueTarget import com.unciv.models.ruleset.unique.UniqueTarget
import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
@ -24,14 +26,14 @@ class TileImprovement : RulesetStatsObject() {
val turnsToBuild: Int = 0 // This is the base cost. val turnsToBuild: Int = 0 // This is the base cost.
fun getTurnsToBuild(civInfo: CivilizationInfo): Int { fun getTurnsToBuild(civInfo: CivilizationInfo, unit: MapUnit?): Int {
var realTurnsToBuild = turnsToBuild.toFloat() * civInfo.gameInfo.gameParameters.gameSpeed.modifier val state = StateForConditionals(civInfo, unit = unit)
for (unique in civInfo.getMatchingUniques(UniqueType.TileImprovementTime)) { val uniques = civInfo.getMatchingUniques(UniqueType.TileImprovementTime, state) +
realTurnsToBuild *= unique.params[0].toPercent() (unit?.getMatchingUniques(UniqueType.TileImprovementTime, state) ?: sequenceOf())
} return uniques.fold(turnsToBuild.toFloat() * civInfo.gameInfo.gameParameters.gameSpeed.modifier) {
it, unique -> it * unique.params[0].toPercent()
}.roundToInt().coerceAtLeast(1)
// In some weird cases it was possible for something to take 0 turns, leading to it instead never finishing // In some weird cases it was possible for something to take 0 turns, leading to it instead never finishing
if (realTurnsToBuild < 1) realTurnsToBuild = 1f
return realTurnsToBuild.roundToInt()
} }
fun getDescription(ruleset: Ruleset): String { fun getDescription(ruleset: Ruleset): String {
@ -74,7 +76,7 @@ class TileImprovement : RulesetStatsObject() {
* a terrain feature, thus the unique name. * a terrain feature, thus the unique name.
*/ */
fun isAllowedOnFeature(name: String) = getMatchingUniques(UniqueType.NoFeatureRemovalNeeded).any { it.params[0] == name } fun isAllowedOnFeature(name: String) = getMatchingUniques(UniqueType.NoFeatureRemovalNeeded).any { it.params[0] == name }
fun matchesFilter(filter: String): Boolean { fun matchesFilter(filter: String): Boolean {
return when (filter) { return when (filter) {
name -> true name -> true

View File

@ -79,7 +79,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
StatsPerPopulation("[stats] per [amount] population [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), StatsPerPopulation("[stats] per [amount] population [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
// ToDo: Reword to `[stats] <in cities with [amount] or more population>` for consistency with other conditionals // ToDo: Reword to `[stats] <in cities with [amount] or more population>` for consistency with other conditionals
StatsFromXPopulation("[stats] in cities with [amount] or more population", UniqueTarget.Global, UniqueTarget.FollowerBelief), StatsFromXPopulation("[stats] in cities with [amount] or more population", UniqueTarget.Global, UniqueTarget.FollowerBelief),
StatsFromCitiesOnSpecificTiles("[stats] in cities on [terrainFilter] tiles", UniqueTarget.Global, UniqueTarget.FollowerBelief), StatsFromCitiesOnSpecificTiles("[stats] in cities on [terrainFilter] tiles", UniqueTarget.Global, UniqueTarget.FollowerBelief),
StatsFromBuildings("[stats] from all [buildingFilter] buildings", UniqueTarget.Global, UniqueTarget.FollowerBelief), StatsFromBuildings("[stats] from all [buildingFilter] buildings", UniqueTarget.Global, UniqueTarget.FollowerBelief),
StatsSpendingGreatPeople("[stats] whenever a Great Person is expended", UniqueTarget.Global), StatsSpendingGreatPeople("[stats] whenever a Great Person is expended", UniqueTarget.Global),
@ -98,7 +98,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
StatPercentFromReligionFollowers("[amount]% [stat] from every follower, up to [amount]%", UniqueTarget.FollowerBelief), StatPercentFromReligionFollowers("[amount]% [stat] from every follower, up to [amount]%", UniqueTarget.FollowerBelief),
BonusStatsFromCityStates("[amount]% [stat] from City-States", UniqueTarget.Global), BonusStatsFromCityStates("[amount]% [stat] from City-States", UniqueTarget.Global),
GoldBonusFromTradeRouts("Gold from all trade routes +25%", UniqueTarget.Global), GoldBonusFromTradeRouts("Gold from all trade routes +25%", UniqueTarget.Global),
NullifiesStat("Nullifies [stat] [cityFilter]", UniqueTarget.Global), NullifiesStat("Nullifies [stat] [cityFilter]", UniqueTarget.Global),
NullifiesGrowth("Nullifies Growth [cityFilter]", UniqueTarget.Global), NullifiesGrowth("Nullifies Growth [cityFilter]", UniqueTarget.Global),
@ -123,7 +123,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
CityStateUniqueLuxury("Provides a unique luxury", UniqueTarget.CityState), // No conditional support as of yet CityStateUniqueLuxury("Provides a unique luxury", UniqueTarget.CityState), // No conditional support as of yet
CityStateGiftedUnitsStartWithXp("Military Units gifted from City-States start with [amount] XP", UniqueTarget.Global), CityStateGiftedUnitsStartWithXp("Military Units gifted from City-States start with [amount] XP", UniqueTarget.Global),
CityStateMoreGiftedUnits("Militaristic City-States grant units [amount] times as fast when you are at war with a common nation", UniqueTarget.Global), CityStateMoreGiftedUnits("Militaristic City-States grant units [amount] times as fast when you are at war with a common nation", UniqueTarget.Global),
CityStateGoldGiftsProvideMoreInfluence("Gifts of Gold to City-States generate [amount]% more Influence", UniqueTarget.Global), CityStateGoldGiftsProvideMoreInfluence("Gifts of Gold to City-States generate [amount]% more Influence", UniqueTarget.Global),
CityStateCanBeBoughtForGold("Can spend Gold to annex or puppet a City-State that has been your ally for [amount] turns.", UniqueTarget.Global), CityStateCanBeBoughtForGold("Can spend Gold to annex or puppet a City-State that has been your ally for [amount] turns.", UniqueTarget.Global),
CityStateTerritoryAlwaysFriendly("City-State territory always counts as friendly territory", UniqueTarget.Global), CityStateTerritoryAlwaysFriendly("City-State territory always counts as friendly territory", UniqueTarget.Global),
@ -132,14 +132,14 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
CityStateDeprecated("Will not be chosen for new games", UniqueTarget.Nation), // implemented for CS only for now CityStateDeprecated("Will not be chosen for new games", UniqueTarget.Nation), // implemented for CS only for now
CityStateInfluenceDegradation("[amount]% City-State Influence degradation", UniqueTarget.Global), CityStateInfluenceDegradation("[amount]% City-State Influence degradation", UniqueTarget.Global),
CityStateRestingPoint("Resting point for Influence with City-States is increased by [amount]", UniqueTarget.Global), CityStateRestingPoint("Resting point for Influence with City-States is increased by [amount]", UniqueTarget.Global),
CityStateStatPercent("Allied City-States provide [stat] equal to [amount]% of what they produce for themselves", UniqueTarget.Global), CityStateStatPercent("Allied City-States provide [stat] equal to [amount]% of what they produce for themselves", UniqueTarget.Global),
CityStateResources("[amount]% resources gifted by City-States", UniqueTarget.Global), CityStateResources("[amount]% resources gifted by City-States", UniqueTarget.Global),
CityStateLuxuryHappiness("[amount]% Happiness from luxury resources gifted by City-States", UniqueTarget.Global), CityStateLuxuryHappiness("[amount]% Happiness from luxury resources gifted by City-States", UniqueTarget.Global),
CityStateInfluenceRecoversTwiceNormalRate("City-State Influence recovers at twice the normal rate", UniqueTarget.Global), CityStateInfluenceRecoversTwiceNormalRate("City-State Influence recovers at twice the normal rate", UniqueTarget.Global),
// endregion // endregion
/////// region Other global uniques /////// region Other global uniques
FreeUnits("[amount] units cost no maintenance", UniqueTarget.Global), FreeUnits("[amount] units cost no maintenance", UniqueTarget.Global),
@ -173,15 +173,15 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
BuyBuildingsForAmountStat("May buy [buildingFilter] buildings for [amount] [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), BuyBuildingsForAmountStat("May buy [buildingFilter] buildings for [amount] [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyUnitsWithStat("May buy [baseUnitFilter] units with [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), BuyUnitsWithStat("May buy [baseUnitFilter] units with [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyBuildingsWithStat("May buy [buildingFilter] buildings with [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), BuyBuildingsWithStat("May buy [buildingFilter] buildings with [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyUnitsByProductionCost("May buy [baseUnitFilter] units with [stat] for [amount] times their normal Production cost", UniqueTarget.FollowerBelief, UniqueTarget.Global), BuyUnitsByProductionCost("May buy [baseUnitFilter] units with [stat] for [amount] times their normal Production cost", UniqueTarget.FollowerBelief, UniqueTarget.Global),
BuyBuildingsByProductionCost("May buy [buildingFilter] buildings with [stat] for [amount] times their normal Production cost", UniqueTarget.FollowerBelief, UniqueTarget.Global), BuyBuildingsByProductionCost("May buy [buildingFilter] buildings with [stat] for [amount] times their normal Production cost", UniqueTarget.FollowerBelief, UniqueTarget.Global),
// ToDo: Unify // ToDo: Unify
EnablesGoldProduction("Enables conversion of city production to gold", UniqueTarget.Global), EnablesGoldProduction("Enables conversion of city production to gold", UniqueTarget.Global),
EnablesScienceProduction("Enables conversion of city production to science", UniqueTarget.Global), EnablesScienceProduction("Enables conversion of city production to science", UniqueTarget.Global),
BuyItemsDiscount("[stat] cost of purchasing items in cities [amount]%", UniqueTarget.Global, UniqueTarget.FollowerBelief), BuyItemsDiscount("[stat] cost of purchasing items in cities [amount]%", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyBuildingsDiscount("[stat] cost of purchasing [buildingFilter] buildings [amount]%", UniqueTarget.Global, UniqueTarget.FollowerBelief), BuyBuildingsDiscount("[stat] cost of purchasing [buildingFilter] buildings [amount]%", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyUnitsDiscount("[stat] cost of purchasing [baseUnitFilter] units [amount]%", UniqueTarget.Global, UniqueTarget.FollowerBelief), BuyUnitsDiscount("[stat] cost of purchasing [baseUnitFilter] units [amount]%", UniqueTarget.Global, UniqueTarget.FollowerBelief),
@ -189,10 +189,10 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
// Should be replaced with moddable improvements when roads become moddable // Should be replaced with moddable improvements when roads become moddable
RoadMovementSpeed("Improves movement speed on roads",UniqueTarget.Global), RoadMovementSpeed("Improves movement speed on roads",UniqueTarget.Global),
RoadsConnectAcrossRivers("Roads connect tiles across rivers", UniqueTarget.Global), RoadsConnectAcrossRivers("Roads connect tiles across rivers", UniqueTarget.Global),
RoadMaintenance("[amount]% maintenance on road & railroads", UniqueTarget.Global), RoadMaintenance("[amount]% maintenance on road & railroads", UniqueTarget.Global),
BuildingMaintenance("[amount]% maintenance cost for buildings [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), BuildingMaintenance("[amount]% maintenance cost for buildings [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
// This should probably support conditionals, e.g. <after discovering [tech]> // This should probably support conditionals, e.g. <after discovering [tech]>
MayanGainGreatPerson("Receive a free Great Person at the end of every [comment] (every 394 years), after researching [tech]. Each bonus person can only be chosen once.", UniqueTarget.Global), MayanGainGreatPerson("Receive a free Great Person at the end of every [comment] (every 394 years), after researching [tech]. Each bonus person can only be chosen once.", UniqueTarget.Global),
MayanCalendarDisplay("Once The Long Count activates, the year on the world screen displays as the traditional Mayan Long Count.", UniqueTarget.Global), MayanCalendarDisplay("Once The Long Count activates, the year on the world screen displays as the traditional Mayan Long Count.", UniqueTarget.Global),
@ -202,17 +202,17 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
LessPolicyCostFromCities("Each city founded increases culture cost of policies [amount]% less than normal", UniqueTarget.Global), LessPolicyCostFromCities("Each city founded increases culture cost of policies [amount]% less than normal", UniqueTarget.Global),
LessPolicyCost("[amount]% Culture cost of adopting new Policies", UniqueTarget.Global), LessPolicyCost("[amount]% Culture cost of adopting new Policies", UniqueTarget.Global),
StrategicResourcesIncrease("Quantity of strategic resources produced by the empire +[amount]%", UniqueTarget.Global), // used in Policy StrategicResourcesIncrease("Quantity of strategic resources produced by the empire +[amount]%", UniqueTarget.Global), // used in Policy
DoubleResourceProduced("Double quantity of [resource] produced", UniqueTarget.Global), DoubleResourceProduced("Double quantity of [resource] produced", UniqueTarget.Global),
// Todo: should probably be changed to "[stats] from every known Natural Wonder", and that'll give us the global unique as well // Todo: should probably be changed to "[stats] from every known Natural Wonder", and that'll give us the global unique as well
DoubleHappinessFromNaturalWonders("Double Happiness from Natural Wonders", UniqueTarget.Global), DoubleHappinessFromNaturalWonders("Double Happiness from Natural Wonders", UniqueTarget.Global),
EnablesConstructionOfSpaceshipParts("Enables construction of Spaceship parts", UniqueTarget.Global), EnablesConstructionOfSpaceshipParts("Enables construction of Spaceship parts", UniqueTarget.Global),
EnemyLandUnitsSpendExtraMovement("Enemy land units must spend 1 extra movement point when inside your territory (obsolete upon Dynamite)", UniqueTarget.Global), EnemyLandUnitsSpendExtraMovement("Enemy land units must spend 1 extra movement point when inside your territory (obsolete upon Dynamite)", UniqueTarget.Global),
ProductionToScienceConversionBonus("Production to science conversion in cities increased by 33%", UniqueTarget.Global), ProductionToScienceConversionBonus("Production to science conversion in cities increased by 33%", UniqueTarget.Global),
// Misc national uniques // Misc national uniques
NotifiedOfBarbarianEncampments("Notified of new Barbarian encampments", UniqueTarget.Global), NotifiedOfBarbarianEncampments("Notified of new Barbarian encampments", UniqueTarget.Global),
BorrowsCityNames("\"Borrows\" city names from other civilizations in the game", UniqueTarget.Global), BorrowsCityNames("\"Borrows\" city names from other civilizations in the game", UniqueTarget.Global),
@ -236,24 +236,25 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
// Todo can be replaced with a <within [amount] tiles of a [tileFilter] tile> conditional // Todo can be replaced with a <within [amount] tiles of a [tileFilter] tile> conditional
StrengthWithinTilesOfTile("+[amount]% Strength if within [amount] tiles of a [tileFilter]", UniqueTarget.Global), StrengthWithinTilesOfTile("+[amount]% Strength if within [amount] tiles of a [tileFilter]", UniqueTarget.Global),
StatBonusPercentFromCityStates("[amount]% [stat] from City-States", UniqueTarget.Global), StatBonusPercentFromCityStates("[amount]% [stat] from City-States", UniqueTarget.Global),
ProvidesGoldWheneverGreatPersonExpended("Provides a sum of gold each time you spend a Great Person", UniqueTarget.Global), ProvidesGoldWheneverGreatPersonExpended("Provides a sum of gold each time you spend a Great Person", UniqueTarget.Global),
ProvidesStatsWheneverGreatPersonExpended("[stats] whenever a Great Person is expended", UniqueTarget.Global), ProvidesStatsWheneverGreatPersonExpended("[stats] whenever a Great Person is expended", UniqueTarget.Global),
// Acts as a trigger - this should be generalized somehow but the current setup does not allow this // Acts as a trigger - this should be generalized somehow but the current setup does not allow this
// It would currently mean cycling through EVERY unique type to find ones with a specific conditional... // It would currently mean cycling through EVERY unique type to find ones with a specific conditional...
@Suppress("SpellCheckingInspection") // Not worth fixing
RecieveFreeUnitWhenDiscoveringTech("Receive free [baseUnitFilter] when you discover [tech]", UniqueTarget.Global), RecieveFreeUnitWhenDiscoveringTech("Receive free [baseUnitFilter] when you discover [tech]", UniqueTarget.Global),
EnablesOpenBorders("Enables Open Borders agreements", UniqueTarget.Global), EnablesOpenBorders("Enables Open Borders agreements", UniqueTarget.Global),
// Should the 'R' in 'Research agreements' be capitalized? // Should the 'R' in 'Research agreements' be capitalized?
EnablesResearchAgreements("Enables Research agreements", UniqueTarget.Global), EnablesResearchAgreements("Enables Research agreements", UniqueTarget.Global),
ScienceFromResearchAgreements("Science gained from research agreements [amount]%", UniqueTarget.Global), ScienceFromResearchAgreements("Science gained from research agreements [amount]%", UniqueTarget.Global),
TriggersVictory("Triggers victory", UniqueTarget.Global), TriggersVictory("Triggers victory", UniqueTarget.Global),
TriggersCulturalVictory("Triggers a Cultural Victory upon completion", UniqueTarget.Global), TriggersCulturalVictory("Triggers a Cultural Victory upon completion", UniqueTarget.Global),
BetterDefensiveBuildings("[amount]% City Strength from defensive buildings", UniqueTarget.Global), BetterDefensiveBuildings("[amount]% City Strength from defensive buildings", UniqueTarget.Global),
TileImprovementTime("[amount]% tile improvement construction time", UniqueTarget.Global), TileImprovementTime("[amount]% tile improvement construction time", UniqueTarget.Global, UniqueTarget.Unit),
PercentGoldFromTradeMissions("[amount]% Gold from Great Merchant trade missions", UniqueTarget.Global), PercentGoldFromTradeMissions("[amount]% Gold from Great Merchant trade missions", UniqueTarget.Global),
// Todo: Lowercase the 'U' of 'Units' in this unique // Todo: Lowercase the 'U' of 'Units' in this unique
CityHealingUnits("[mapUnitFilter] Units adjacent to this city heal [amount] HP per turn when healing", UniqueTarget.Global, UniqueTarget.FollowerBelief), CityHealingUnits("[mapUnitFilter] Units adjacent to this city heal [amount] HP per turn when healing", UniqueTarget.Global, UniqueTarget.FollowerBelief),
@ -274,7 +275,6 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
EmbarkAndEnterOcean("Can embark and move over Coasts and Oceans immediately", UniqueTarget.Global), EmbarkAndEnterOcean("Can embark and move over Coasts and Oceans immediately", UniqueTarget.Global),
PopulationLossFromNukes("Population loss from nuclear attacks [amount]% [cityFilter]", UniqueTarget.Global), PopulationLossFromNukes("Population loss from nuclear attacks [amount]% [cityFilter]", UniqueTarget.Global),
NaturalReligionSpreadStrength("[amount]% Natural religion spread [cityFilter]", UniqueTarget.FollowerBelief, UniqueTarget.Global), NaturalReligionSpreadStrength("[amount]% Natural religion spread [cityFilter]", UniqueTarget.FollowerBelief, UniqueTarget.Global),
ReligionSpreadDistance("Religion naturally spreads to cities [amount] tiles away", UniqueTarget.Global, UniqueTarget.FollowerBelief), ReligionSpreadDistance("Religion naturally spreads to cities [amount] tiles away", UniqueTarget.Global, UniqueTarget.FollowerBelief),
@ -288,14 +288,14 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
ResearchableMultipleTimes("Can be continually researched", UniqueTarget.Global), ResearchableMultipleTimes("Can be continually researched", UniqueTarget.Global),
BaseUnitSupply("[amount] Unit Supply", UniqueTarget.Global), BaseUnitSupply("[amount] Unit Supply", UniqueTarget.Global),
UnitSupplyPerPop("[amount] Unit Supply per [amount] population [cityFilter]", UniqueTarget.Global), UnitSupplyPerPop("[amount] Unit Supply per [amount] population [cityFilter]", UniqueTarget.Global),
UnitSupplyPerCity("[amount] Unit Supply per city", UniqueTarget.Global), UnitSupplyPerCity("[amount] Unit Supply per city", UniqueTarget.Global),
UnitsInCitiesNoMaintenance("Units in cities cost no Maintenance", UniqueTarget.Global), UnitsInCitiesNoMaintenance("Units in cities cost no Maintenance", UniqueTarget.Global),
SpawnRebels("Rebel units may spawn", UniqueTarget.Global), SpawnRebels("Rebel units may spawn", UniqueTarget.Global),
//endregion //endregion
//endregion Global uniques //endregion Global uniques
///////////////////////////////////////// region CONSTRUCTION UNIQUES ///////////////////////////////////////// ///////////////////////////////////////// region CONSTRUCTION UNIQUES /////////////////////////////////////////
@ -328,20 +328,20 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
" OR \"Only available <starting from the [buildingName/tech/era/policy]>\"" + " OR \"Only available <starting from the [buildingName/tech/era/policy]>\"" +
" OR \"Only available <after discovering [buildingName/tech/era/policy]>")) " OR \"Only available <after discovering [buildingName/tech/era/policy]>"))
Requires("Requires [buildingName/tech/era/policy]", UniqueTarget.Building, UniqueTarget.Unit), Requires("Requires [buildingName/tech/era/policy]", UniqueTarget.Building, UniqueTarget.Unit),
ConvertFoodToProductionWhenConstructed("Excess Food converted to Production when under construction", UniqueTarget.Building, UniqueTarget.Unit), ConvertFoodToProductionWhenConstructed("Excess Food converted to Production when under construction", UniqueTarget.Building, UniqueTarget.Unit),
RequiresPopulation("Requires at least [amount] population", UniqueTarget.Building, UniqueTarget.Unit), RequiresPopulation("Requires at least [amount] population", UniqueTarget.Building, UniqueTarget.Unit),
TriggersAlertOnStart("Triggers a global alert upon build start", UniqueTarget.Building, UniqueTarget.Unit), TriggersAlertOnStart("Triggers a global alert upon build start", UniqueTarget.Building, UniqueTarget.Unit),
TriggersAlertOnCompletion("Triggers a global alert upon completion", UniqueTarget.Building, UniqueTarget.Unit), TriggersAlertOnCompletion("Triggers a global alert upon completion", UniqueTarget.Building, UniqueTarget.Unit),
//endregion //endregion
///////////////////////////////////////// region BUILDING UNIQUES ///////////////////////////////////////// ///////////////////////////////////////// region BUILDING UNIQUES /////////////////////////////////////////
CostIncreasesPerCity("Cost increases by [amount] per owned city", UniqueTarget.Building), CostIncreasesPerCity("Cost increases by [amount] per owned city", UniqueTarget.Building),
@Deprecated("as of 3.19.9", ReplaceWith("Only available <in cities without a [buildingName]>")) @Deprecated("as of 3.19.9", ReplaceWith("Only available <in cities without a [buildingName]>"))
CannotBeBuiltWith("Cannot be built with [buildingName]", UniqueTarget.Building), CannotBeBuiltWith("Cannot be built with [buildingName]", UniqueTarget.Building),
@Deprecated("as of 3.19.9", ReplaceWith("Only available <in cities with a [buildingName]>")) @Deprecated("as of 3.19.9", ReplaceWith("Only available <in cities with a [buildingName]>"))
@ -371,24 +371,24 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
ObsoleteWith("Obsolete with [tech]", UniqueTarget.Building, UniqueTarget.Resource, UniqueTarget.Improvement), ObsoleteWith("Obsolete with [tech]", UniqueTarget.Building, UniqueTarget.Resource, UniqueTarget.Improvement),
IndicatesCapital("Indicates the capital city", UniqueTarget.Building), IndicatesCapital("Indicates the capital city", UniqueTarget.Building),
ProvidesExtraLuxuryFromCityResources("Provides 1 extra copy of each improved luxury resource near this City", UniqueTarget.Building), ProvidesExtraLuxuryFromCityResources("Provides 1 extra copy of each improved luxury resource near this City", UniqueTarget.Building),
DestroyedWhenCityCaptured("Destroyed when the city is captured", UniqueTarget.Building), DestroyedWhenCityCaptured("Destroyed when the city is captured", UniqueTarget.Building),
NotDestroyedWhenCityCaptured("Never destroyed when the city is captured", UniqueTarget.Building), NotDestroyedWhenCityCaptured("Never destroyed when the city is captured", UniqueTarget.Building),
DoublesGoldFromCapturingCity("Doubles Gold given to enemy if city is captured", UniqueTarget.Building), DoublesGoldFromCapturingCity("Doubles Gold given to enemy if city is captured", UniqueTarget.Building),
RemoveAnnexUnhappiness("Remove extra unhappiness from annexed cities", UniqueTarget.Building), RemoveAnnexUnhappiness("Remove extra unhappiness from annexed cities", UniqueTarget.Building),
//endregion //endregion
///////////////////////////////////////// region UNIT UNIQUES ///////////////////////////////////////// ///////////////////////////////////////// region UNIT UNIQUES /////////////////////////////////////////
FoundCity("Founds a new city", UniqueTarget.Unit), FoundCity("Founds a new city", UniqueTarget.Unit),
ConstructImprovementConsumingUnit("Can construct [improvementName]", UniqueTarget.Unit), ConstructImprovementConsumingUnit("Can construct [improvementName]", UniqueTarget.Unit),
BuildImprovements("Can build [improvementFilter/terrainFilter] improvements on tiles", UniqueTarget.Unit), BuildImprovements("Can build [improvementFilter/terrainFilter] improvements on tiles", UniqueTarget.Unit),
CreateWaterImprovements("May create improvements on water resources", UniqueTarget.Unit), CreateWaterImprovements("May create improvements on water resources", UniqueTarget.Unit),
Strength("[amount]% Strength", UniqueTarget.Unit, UniqueTarget.Global), Strength("[amount]% Strength", UniqueTarget.Unit, UniqueTarget.Global),
StrengthNearCapital("[amount]% Strength decreasing with distance from the capital", UniqueTarget.Unit, UniqueTarget.Global), StrengthNearCapital("[amount]% Strength decreasing with distance from the capital", UniqueTarget.Unit, UniqueTarget.Global),
FlankAttackBonus("[amount]% to Flank Attack bonuses", UniqueTarget.Unit, UniqueTarget.Global), FlankAttackBonus("[amount]% to Flank Attack bonuses", UniqueTarget.Unit, UniqueTarget.Global),
@ -400,10 +400,10 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
Range("[amount] Range", UniqueTarget.Unit, UniqueTarget.Global), Range("[amount] Range", UniqueTarget.Unit, UniqueTarget.Global),
Heal("[amount] HP when healing", UniqueTarget.Unit, UniqueTarget.Global), Heal("[amount] HP when healing", UniqueTarget.Unit, UniqueTarget.Global),
SpreadReligionStrength("[amount]% Spread Religion Strength", UniqueTarget.Unit, UniqueTarget.Global), SpreadReligionStrength("[amount]% Spread Religion Strength", UniqueTarget.Unit, UniqueTarget.Global),
MayFoundReligion("May found a religion", UniqueTarget.Unit), MayFoundReligion("May found a religion", UniqueTarget.Unit),
MayEnhanceReligion("May enhance a religion", UniqueTarget.Unit), MayEnhanceReligion("May enhance a religion", UniqueTarget.Unit),
CanOnlyAttackUnits("Can only attack [combatantFilter] units", UniqueTarget.Unit), CanOnlyAttackUnits("Can only attack [combatantFilter] units", UniqueTarget.Unit),
CanOnlyAttackTiles("Can only attack [tileFilter] tiles", UniqueTarget.Unit), CanOnlyAttackTiles("Can only attack [tileFilter] tiles", UniqueTarget.Unit),
CannotAttack("Cannot attack", UniqueTarget.Unit), CannotAttack("Cannot attack", UniqueTarget.Unit),
@ -411,17 +411,17 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
SelfDestructs("Self-destructs when attacking", UniqueTarget.Unit), SelfDestructs("Self-destructs when attacking", UniqueTarget.Unit),
BlastRadius("Blast radius [amount]", UniqueTarget.Unit), BlastRadius("Blast radius [amount]", UniqueTarget.Unit),
IndirectFire("Ranged attacks may be performed over obstacles", UniqueTarget.Unit), IndirectFire("Ranged attacks may be performed over obstacles", UniqueTarget.Unit),
NoDefensiveTerrainBonus("No defensive terrain bonus", UniqueTarget.Unit, UniqueTarget.Global), NoDefensiveTerrainBonus("No defensive terrain bonus", UniqueTarget.Unit, UniqueTarget.Global),
NoDefensiveTerrainPenalty("No defensive terrain penalty", UniqueTarget.Unit, UniqueTarget.Global), NoDefensiveTerrainPenalty("No defensive terrain penalty", UniqueTarget.Unit, UniqueTarget.Global),
Uncapturable("Uncapturable", UniqueTarget.Unit), Uncapturable("Uncapturable", UniqueTarget.Unit),
MayWithdraw("May withdraw before melee ([amount]%)", UniqueTarget.Unit), MayWithdraw("May withdraw before melee ([amount]%)", UniqueTarget.Unit),
CannotCaptureCities("Unable to capture cities", UniqueTarget.Unit), CannotCaptureCities("Unable to capture cities", UniqueTarget.Unit),
NoMovementToPillage("No movement cost to pillage", UniqueTarget.Unit, UniqueTarget.Global), NoMovementToPillage("No movement cost to pillage", UniqueTarget.Unit, UniqueTarget.Global),
CanMoveAfterAttacking("Can move after attacking", UniqueTarget.Unit), CanMoveAfterAttacking("Can move after attacking", UniqueTarget.Unit),
MoveImmediatelyOnceBought("Can move immediately once bought", UniqueTarget.Unit), MoveImmediatelyOnceBought("Can move immediately once bought", UniqueTarget.Unit),
HealsOutsideFriendlyTerritory("May heal outside of friendly territory", UniqueTarget.Unit, UniqueTarget.Global), HealsOutsideFriendlyTerritory("May heal outside of friendly territory", UniqueTarget.Unit, UniqueTarget.Global),
HealingEffectsDoubled("All healing effects doubled", UniqueTarget.Unit, UniqueTarget.Global), HealingEffectsDoubled("All healing effects doubled", UniqueTarget.Unit, UniqueTarget.Global),
HealsAfterKilling("Heals [amount] damage if it kills a unit", UniqueTarget.Unit, UniqueTarget.Global), HealsAfterKilling("Heals [amount] damage if it kills a unit", UniqueTarget.Unit, UniqueTarget.Global),
@ -437,7 +437,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
AttackAcrossCoast("Eliminates combat penalty for attacking across a coast", UniqueTarget.Unit), AttackAcrossCoast("Eliminates combat penalty for attacking across a coast", UniqueTarget.Unit),
SixTilesAlwaysVisible("6 tiles in every direction always visible", UniqueTarget.Unit), SixTilesAlwaysVisible("6 tiles in every direction always visible", UniqueTarget.Unit),
CarryAirUnits("Can carry [amount] [mapUnitFilter] units", UniqueTarget.Unit), CarryAirUnits("Can carry [amount] [mapUnitFilter] units", UniqueTarget.Unit),
CarryExtraAirUnits("Can carry [amount] extra [mapUnitFilter] units", UniqueTarget.Unit), CarryExtraAirUnits("Can carry [amount] extra [mapUnitFilter] units", UniqueTarget.Unit),
CannotBeCarriedBy("Cannot be carried by [mapUnitFilter] units", UniqueTarget.Unit), CannotBeCarriedBy("Cannot be carried by [mapUnitFilter] units", UniqueTarget.Unit),
@ -451,16 +451,16 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
KillUnitPlunder("Earn [amount]% of killed [mapUnitFilter] unit's [costOrStrength] as [plunderableStat]", UniqueTarget.Unit, UniqueTarget.Global), KillUnitPlunder("Earn [amount]% of killed [mapUnitFilter] unit's [costOrStrength] as [plunderableStat]", UniqueTarget.Unit, UniqueTarget.Global),
KillUnitPlunderNearCity("Earn [amount]% of [mapUnitFilter] unit's [costOrStrength] as [plunderableStat] when killed within 4 tiles of a city following this religion", UniqueTarget.FollowerBelief), KillUnitPlunderNearCity("Earn [amount]% of [mapUnitFilter] unit's [costOrStrength] as [plunderableStat] when killed within 4 tiles of a city following this religion", UniqueTarget.FollowerBelief),
KillUnitCapture("May capture killed [mapUnitFilter] units", UniqueTarget.Unit), KillUnitCapture("May capture killed [mapUnitFilter] units", UniqueTarget.Unit),
FlatXPGain("[amount] XP gained from combat", UniqueTarget.Unit, UniqueTarget.Global), FlatXPGain("[amount] XP gained from combat", UniqueTarget.Unit, UniqueTarget.Global),
PercentageXPGain("[amount]% XP gained from combat", UniqueTarget.Unit, UniqueTarget.Global), PercentageXPGain("[amount]% XP gained from combat", UniqueTarget.Unit, UniqueTarget.Global),
Invisible("Invisible to others", UniqueTarget.Unit), Invisible("Invisible to others", UniqueTarget.Unit),
InvisibleToNonAdjacent("Invisible to non-adjacent units", UniqueTarget.Unit), InvisibleToNonAdjacent("Invisible to non-adjacent units", UniqueTarget.Unit),
CanSeeInvisibleUnits("Can see invisible [mapUnitFilter] units", UniqueTarget.Unit), CanSeeInvisibleUnits("Can see invisible [mapUnitFilter] units", UniqueTarget.Unit),
RuinsUpgrade("May upgrade to [baseUnitFilter] through ruins-like effects", UniqueTarget.Unit), RuinsUpgrade("May upgrade to [baseUnitFilter] through ruins-like effects", UniqueTarget.Unit),
// The following block gets cached in MapUnit for faster getMovementCostBetweenAdjacentTiles // The following block gets cached in MapUnit for faster getMovementCostBetweenAdjacentTiles
DoubleMovementOnTerrain("Double movement in [terrainFilter]", UniqueTarget.Unit), DoubleMovementOnTerrain("Double movement in [terrainFilter]", UniqueTarget.Unit),
AllTilesCost1Move("All tiles cost 1 movement", UniqueTarget.Unit), AllTilesCost1Move("All tiles cost 1 movement", UniqueTarget.Unit),
@ -474,12 +474,12 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
CanEnterForeignTilesButLosesReligiousStrength("May enter foreign tiles without open borders, but loses [amount] religious strength each turn it ends there", UniqueTarget.Unit), CanEnterForeignTilesButLosesReligiousStrength("May enter foreign tiles without open borders, but loses [amount] religious strength each turn it ends there", UniqueTarget.Unit),
CannotBeBarbarian("Never appears as a Barbarian unit", UniqueTarget.Unit, flags = UniqueFlag.setOfHiddenToUsers), CannotBeBarbarian("Never appears as a Barbarian unit", UniqueTarget.Unit, flags = UniqueFlag.setOfHiddenToUsers),
ReligiousUnit("Religious Unit", UniqueTarget.Unit), ReligiousUnit("Religious Unit", UniqueTarget.Unit),
SpaceshipPart("Spaceship part", UniqueTarget.Unit, UniqueTarget.Building), // Usage for buildings is deprecated SpaceshipPart("Spaceship part", UniqueTarget.Unit, UniqueTarget.Building), // Usage for buildings is deprecated
AddInCapital("Can be added to [comment] in the Capital", UniqueTarget.Unit), AddInCapital("Can be added to [comment] in the Capital", UniqueTarget.Unit),
//endregion //endregion
///////////////////////////////////////// region TILE UNIQUES ///////////////////////////////////////// ///////////////////////////////////////// region TILE UNIQUES /////////////////////////////////////////
@ -495,7 +495,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
// The "Except [terrainFilter]" could theoretically be implemented with a conditional // The "Except [terrainFilter]" could theoretically be implemented with a conditional
NaturalWonderConvertNeighborsExcept("Neighboring tiles except [baseTerrain] will convert to [baseTerrain]", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), NaturalWonderConvertNeighborsExcept("Neighboring tiles except [baseTerrain] will convert to [baseTerrain]", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
GrantsGoldToFirstToDiscover("Grants 500 Gold to the first civilization to discover it", UniqueTarget.Terrain), GrantsGoldToFirstToDiscover("Grants 500 Gold to the first civilization to discover it", UniqueTarget.Terrain),
// General terrain // General terrain
DamagesContainingUnits("Units ending their turn on this terrain take [amount] damage", UniqueTarget.Terrain), DamagesContainingUnits("Units ending their turn on this terrain take [amount] damage", UniqueTarget.Terrain),
TerrainGrantsPromotion("Grants [promotion] ([comment]) to adjacent [mapUnitFilter] units for the rest of the game", UniqueTarget.Terrain), TerrainGrantsPromotion("Grants [promotion] ([comment]) to adjacent [mapUnitFilter] units for the rest of the game", UniqueTarget.Terrain),
@ -505,10 +505,10 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
TileProvidesYieldWithoutPopulation("Tile provides yield without assigned population", UniqueTarget.Terrain, UniqueTarget.Improvement), TileProvidesYieldWithoutPopulation("Tile provides yield without assigned population", UniqueTarget.Terrain, UniqueTarget.Improvement),
NullifyYields("Nullifies all other stats this tile provides", UniqueTarget.Terrain), NullifyYields("Nullifies all other stats this tile provides", UniqueTarget.Terrain),
RestrictedBuildableImprovements("Only [improvementFilter] improvements may be built on this tile", UniqueTarget.Terrain), RestrictedBuildableImprovements("Only [improvementFilter] improvements may be built on this tile", UniqueTarget.Terrain),
BlocksLineOfSightAtSameElevation("Blocks line-of-sight from tiles at same elevation", UniqueTarget.Terrain), BlocksLineOfSightAtSameElevation("Blocks line-of-sight from tiles at same elevation", UniqueTarget.Terrain),
VisibilityElevation("Has an elevation of [amount] for visibility calculations", UniqueTarget.Terrain), VisibilityElevation("Has an elevation of [amount] for visibility calculations", UniqueTarget.Terrain),
OverrideFertility("Always Fertility [amount] for Map Generation", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), OverrideFertility("Always Fertility [amount] for Map Generation", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
AddFertility("[amount] to Fertility for Map Generation", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), AddFertility("[amount] to Fertility for Map Generation", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
@ -530,21 +530,21 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
ResourceFrequency("Generated on every [amount] tiles", UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers), ResourceFrequency("Generated on every [amount] tiles", UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers),
StrategicBalanceResource("Guaranteed with Strategic Balance resource option", UniqueTarget.Resource), StrategicBalanceResource("Guaranteed with Strategic Balance resource option", UniqueTarget.Resource),
NoNaturalGeneration("Doesn't generate naturally", UniqueTarget.Terrain, UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers), NoNaturalGeneration("Doesn't generate naturally", UniqueTarget.Terrain, UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers),
TileGenerationConditions("Occurs at temperature between [amount] and [amount] and humidity between [amount] and [amount]", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), TileGenerationConditions("Occurs at temperature between [amount] and [amount] and humidity between [amount] and [amount]", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
OccursInChains("Occurs in chains at high elevations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), OccursInChains("Occurs in chains at high elevations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
OccursInGroups("Occurs in groups around high elevations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), OccursInGroups("Occurs in groups around high elevations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
MajorStrategicFrequency("Every [amount] tiles with this terrain will receive a major deposit of a strategic resource.", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), MajorStrategicFrequency("Every [amount] tiles with this terrain will receive a major deposit of a strategic resource.", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
RareFeature("Rare feature", UniqueTarget.Terrain), RareFeature("Rare feature", UniqueTarget.Terrain),
ResistsNukes("Resistant to nukes", UniqueTarget.Terrain), ResistsNukes("Resistant to nukes", UniqueTarget.Terrain),
DestroyableByNukes("Can be destroyed by nukes", UniqueTarget.Terrain), DestroyableByNukes("Can be destroyed by nukes", UniqueTarget.Terrain),
FreshWater("Fresh water", UniqueTarget.Terrain), FreshWater("Fresh water", UniqueTarget.Terrain),
RoughTerrain("Rough terrain", UniqueTarget.Terrain), RoughTerrain("Rough terrain", UniqueTarget.Terrain),
/////// Resource uniques /////// Resource uniques
ResourceAmountOnTiles("Deposits in [tileFilter] tiles always provide [amount] resources", UniqueTarget.Resource), ResourceAmountOnTiles("Deposits in [tileFilter] tiles always provide [amount] resources", UniqueTarget.Resource),
CityStateOnlyResource("Can only be created by Mercantile City-States", UniqueTarget.Resource), CityStateOnlyResource("Can only be created by Mercantile City-States", UniqueTarget.Resource),
@ -560,14 +560,14 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
CanOnlyBeBuiltOnTile("Can only be built on [tileFilter] tiles", UniqueTarget.Improvement), CanOnlyBeBuiltOnTile("Can only be built on [tileFilter] tiles", UniqueTarget.Improvement),
CannotBuildOnTile("Cannot be built on [tileFilter] tiles", UniqueTarget.Improvement), CannotBuildOnTile("Cannot be built on [tileFilter] tiles", UniqueTarget.Improvement),
NoFeatureRemovalNeeded("Does not need removal of [tileFilter]", UniqueTarget.Improvement), NoFeatureRemovalNeeded("Does not need removal of [tileFilter]", UniqueTarget.Improvement),
DefensiveBonus("Gives a defensive bonus of [amount]%", UniqueTarget.Improvement), DefensiveBonus("Gives a defensive bonus of [amount]%", UniqueTarget.Improvement),
ImprovementMaintenance("Costs [amount] gold per turn when in your territory", UniqueTarget.Improvement), // Unused ImprovementMaintenance("Costs [amount] gold per turn when in your territory", UniqueTarget.Improvement), // Unused
DamagesAdjacentEnemyUnits("Adjacent enemy units ending their turn take [amount] damage", UniqueTarget.Improvement), DamagesAdjacentEnemyUnits("Adjacent enemy units ending their turn take [amount] damage", UniqueTarget.Improvement),
GreatImprovement("Great Improvement", UniqueTarget.Improvement), GreatImprovement("Great Improvement", UniqueTarget.Improvement),
IsAncientRuinsEquivalent("Provides a random bonus when entered", UniqueTarget.Improvement), IsAncientRuinsEquivalent("Provides a random bonus when entered", UniqueTarget.Improvement),
Unpillagable("Unpillagable", UniqueTarget.Improvement), Unpillagable("Unpillagable", UniqueTarget.Improvement),
Indestructible("Indestructible", UniqueTarget.Improvement), Indestructible("Indestructible", UniqueTarget.Improvement),
Irremovable("Irremovable", UniqueTarget.Improvement), Irremovable("Irremovable", UniqueTarget.Improvement),
@ -575,7 +575,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
///////////////////////////////////////// region CONDITIONALS ///////////////////////////////////////// ///////////////////////////////////////// region CONDITIONALS /////////////////////////////////////////
/////// civ conditionals /////// civ conditionals
ConditionalWar("when at war", UniqueTarget.Conditional), ConditionalWar("when at war", UniqueTarget.Conditional),
ConditionalNotWar("when not at war", UniqueTarget.Conditional), ConditionalNotWar("when not at war", UniqueTarget.Conditional),
@ -585,7 +585,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
ConditionalHappy("while the empire is happy", UniqueTarget.Conditional), ConditionalHappy("while the empire is happy", UniqueTarget.Conditional),
ConditionalBetweenHappiness("when between [amount] and [amount] Happiness", UniqueTarget.Conditional), ConditionalBetweenHappiness("when between [amount] and [amount] Happiness", UniqueTarget.Conditional),
ConditionalBelowHappiness("when below [amount] Happiness", UniqueTarget.Conditional), ConditionalBelowHappiness("when below [amount] Happiness", UniqueTarget.Conditional),
ConditionalDuringEra("during the [era]", UniqueTarget.Conditional), ConditionalDuringEra("during the [era]", UniqueTarget.Conditional),
ConditionalBeforeEra("before the [era]", UniqueTarget.Conditional), ConditionalBeforeEra("before the [era]", UniqueTarget.Conditional),
ConditionalStartingFromEra("starting from the [era]", UniqueTarget.Conditional), ConditionalStartingFromEra("starting from the [era]", UniqueTarget.Conditional),
@ -639,7 +639,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
///////////////////////////////////////// region TRIGGERED ONE-TIME ///////////////////////////////////////// ///////////////////////////////////////// region TRIGGERED ONE-TIME /////////////////////////////////////////
OneTimeFreeUnit("Free [baseUnitFilter] appears", UniqueTarget.Triggerable), // used in Policies, Buildings OneTimeFreeUnit("Free [baseUnitFilter] appears", UniqueTarget.Triggerable), // used in Policies, Buildings
OneTimeAmountFreeUnits("[amount] free [baseUnitFilter] units appear", UniqueTarget.Triggerable), // used in Buildings OneTimeAmountFreeUnits("[amount] free [baseUnitFilter] units appear", UniqueTarget.Triggerable), // used in Buildings
OneTimeFreeUnitRuins("Free [baseUnitFilter] found in the ruins", UniqueTarget.Ruins), // Differs from "Free [] appears" in that it spawns near the ruins instead of in a city OneTimeFreeUnitRuins("Free [baseUnitFilter] found in the ruins", UniqueTarget.Ruins), // Differs from "Free [] appears" in that it spawns near the ruins instead of in a city
@ -686,8 +686,8 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
HiddenAfterGreatProphet("Hidden after generating a Great Prophet", UniqueTarget.Ruins), HiddenAfterGreatProphet("Hidden after generating a Great Prophet", UniqueTarget.Ruins),
HiddenWithoutVictoryType("Hidden when [victoryType] Victory is disabled", UniqueTarget.Building, UniqueTarget.Unit, flags = UniqueFlag.setOfHiddenToUsers), HiddenWithoutVictoryType("Hidden when [victoryType] Victory is disabled", UniqueTarget.Building, UniqueTarget.Unit, flags = UniqueFlag.setOfHiddenToUsers),
HiddenFromCivilopedia("Will not be displayed in Civilopedia", *UniqueTarget.values(), flags = UniqueFlag.setOfHiddenToUsers), HiddenFromCivilopedia("Will not be displayed in Civilopedia", *UniqueTarget.values(), flags = UniqueFlag.setOfHiddenToUsers),
// endregion // endregion
// region DEPRECATED AND REMOVED // region DEPRECATED AND REMOVED

View File

@ -16,7 +16,11 @@ import com.unciv.ui.utils.*
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
import kotlin.math.roundToInt import kotlin.math.roundToInt
class ImprovementPickerScreen(val tileInfo: TileInfo, unit: MapUnit, val onAccept: ()->Unit) : PickerScreen() { class ImprovementPickerScreen(
private val tileInfo: TileInfo,
private val unit: MapUnit,
private val onAccept: ()->Unit
) : PickerScreen() {
private var selectedImprovement: TileImprovement? = null private var selectedImprovement: TileImprovement? = null
private val gameInfo = tileInfo.tileMap.gameInfo private val gameInfo = tileInfo.tileMap.gameInfo
private val ruleSet = gameInfo.ruleSet private val ruleSet = gameInfo.ruleSet
@ -32,8 +36,8 @@ class ImprovementPickerScreen(val tileInfo: TileInfo, unit: MapUnit, val onAccep
// no onAccept() - Worker can stay selected // no onAccept() - Worker can stay selected
} else { } else {
if (improvement.name != tileInfo.improvementInProgress) if (improvement.name != tileInfo.improvementInProgress)
tileInfo.startWorkingOnImprovement(improvement, currentPlayerCiv) tileInfo.startWorkingOnImprovement(improvement, currentPlayerCiv, unit)
if (tileInfo.civilianUnit != null) tileInfo.civilianUnit!!.action = null // this is to "wake up" the worker if it's sleeping unit.action = null // this is to "wake up" the worker if it's sleeping
onAccept() onAccept()
} }
game.setWorldScreen() game.setWorldScreen()
@ -51,26 +55,23 @@ class ImprovementPickerScreen(val tileInfo: TileInfo, unit: MapUnit, val onAccep
val regularImprovements = Table() val regularImprovements = Table()
regularImprovements.defaults().pad(5f) regularImprovements.defaults().pad(5f)
// clone tileInfo without "top" feature if it could be removed // clone tileInfo without "top" feature if it could be removed
// Keep this copy around for speed // Keep this copy around for speed
val tileInfoNoLast:TileInfo = tileInfo.clone() val tileInfoNoLast: TileInfo = tileInfo.clone()
if (ruleSet.tileImprovements.any { it.key == Constants.remove + tileInfoNoLast.getLastTerrain().name }) { if (Constants.remove + tileInfoNoLast.getLastTerrain().name in ruleSet.tileImprovements) {
tileInfoNoLast.removeTerrainFeature(tileInfoNoLast.getLastTerrain().name) tileInfoNoLast.removeTerrainFeature(tileInfoNoLast.getLastTerrain().name)
} }
for (improvement in ruleSet.tileImprovements.values) { for (improvement in ruleSet.tileImprovements.values) {
var suggestRemoval:Boolean = false var suggestRemoval = false
// canBuildImprovement() would allow e.g. great improvements thus we need to exclude them - except cancel // canBuildImprovement() would allow e.g. great improvements thus we need to exclude them - except cancel
if (improvement.turnsToBuild == 0 && improvement.name != Constants.cancelImprovementOrder) continue if (improvement.turnsToBuild == 0 && improvement.name != Constants.cancelImprovementOrder) continue
if (improvement.name == tileInfo.improvement) continue // also checked by canImprovementBeBuiltHere, but after more expensive tests if (improvement.name == tileInfo.improvement) continue // also checked by canImprovementBeBuiltHere, but after more expensive tests
if (!tileInfo.canBuildImprovement(improvement, currentPlayerCiv)) { if (!tileInfo.canBuildImprovement(improvement, currentPlayerCiv)) {
// if there is an improvement that could remove that terrain // if there is an improvement that could remove that terrain
if (tileInfoNoLast.canBuildImprovement(improvement, currentPlayerCiv)) { if (!tileInfoNoLast.canBuildImprovement(improvement, currentPlayerCiv)) continue
suggestRemoval = true suggestRemoval = true
} else {
continue
}
} }
if (!unit.canBuildImprovement(improvement)) continue if (!unit.canBuildImprovement(improvement)) continue
@ -94,7 +95,7 @@ class ImprovementPickerScreen(val tileInfo: TileInfo, unit: MapUnit, val onAccep
var labelText = improvement.name.tr() var labelText = improvement.name.tr()
val turnsToBuild = if (tileInfo.improvementInProgress == improvement.name) tileInfo.turnsToImprovement val turnsToBuild = if (tileInfo.improvementInProgress == improvement.name) tileInfo.turnsToImprovement
else improvement.getTurnsToBuild(currentPlayerCiv) else improvement.getTurnsToBuild(currentPlayerCiv, unit)
if (turnsToBuild > 0) labelText += " - $turnsToBuild${Fonts.turn}" if (turnsToBuild > 0) labelText += " - $turnsToBuild${Fonts.turn}"
val provideResource = tileInfo.hasViewableResource(currentPlayerCiv) && tileInfo.tileResource.improvement == improvement.name val provideResource = tileInfo.hasViewableResource(currentPlayerCiv) && tileInfo.tileResource.improvement == improvement.name
if (provideResource) labelText += "\n" + "Provides [${tileInfo.resource}]".tr() if (provideResource) labelText += "\n" + "Provides [${tileInfo.resource}]".tr()
@ -104,12 +105,11 @@ class ImprovementPickerScreen(val tileInfo: TileInfo, unit: MapUnit, val onAccep
&& improvement.name != Constants.cancelImprovementOrder) && improvement.name != Constants.cancelImprovementOrder)
if (tileInfo.improvement != null && removeImprovement) labelText += "\n" + "Replaces [${tileInfo.improvement}]".tr() if (tileInfo.improvement != null && removeImprovement) labelText += "\n" + "Replaces [${tileInfo.improvement}]".tr()
val pickNow = if (suggestRemoval) val pickNow = when {
(Constants.remove + "[" + tileInfo.getLastTerrain().name + "] first").toLabel() suggestRemoval -> "${Constants.remove}[${tileInfo.getLastTerrain().name}] first".toLabel()
else if (tileInfo.improvementInProgress != improvement.name) tileInfo.improvementInProgress != improvement.name -> "Pick now!".toLabel().onClick { accept(improvement) }
"Pick now!".toLabel().onClick { accept(improvement) } else -> "Current construction".toLabel()
else }
"Current construction".toLabel()
val statIcons = getStatIconsTable(provideResource, removeImprovement) val statIcons = getStatIconsTable(provideResource, removeImprovement)

View File

@ -635,8 +635,14 @@ object UnitActions {
title = "Create [$improvementName]", title = "Create [$improvementName]",
action = { action = {
val unitTile = unit.getTile() val unitTile = unit.getTile()
for (terrainFeature in tile.terrainFeatures.filter { unitTile.ruleset.tileImprovements.containsKey("Remove $it") }) unitTile.setTerrainFeatures(
unitTile.removeTerrainFeature(terrainFeature)// remove forest/jungle/marsh // Remove terrainFeatures that a Worker can remove
// and that aren't explicitly allowed under the improvement
unitTile.terrainFeatures.filter {
"Remove $it" !in unitTile.ruleset.tileImprovements ||
it in improvement.terrainsCanBeBuiltOn
}
)
unitTile.improvement = improvementName unitTile.improvement = improvementName
unitTile.improvementInProgress = null unitTile.improvementInProgress = null
unitTile.turnsToImprovement = 0 unitTile.turnsToImprovement = 0
@ -647,7 +653,8 @@ object UnitActions {
city.cityStats.update() city.cityStats.update()
city.civInfo.updateDetailedCivResources() city.civInfo.updateDetailedCivResources()
} }
addStatsPerGreatPersonUsage(unit) if (unit.isGreatPerson())
addStatsPerGreatPersonUsage(unit)
unit.destroy() unit.destroy()
}.takeIf { }.takeIf {
resourcesAvailable resourcesAvailable
@ -703,7 +710,7 @@ object UnitActions {
otherCiv.addNotification("[${unit.civInfo}] has stolen your territory!", unit.currentTile.position, unit.civInfo.civName, NotificationIcon.War) otherCiv.addNotification("[${unit.civInfo}] has stolen your territory!", unit.currentTile.position, unit.civInfo.civName, NotificationIcon.War)
} }
fun addStatsPerGreatPersonUsage(unit: MapUnit) { private fun addStatsPerGreatPersonUsage(unit: MapUnit) {
if (!unit.isGreatPerson()) return if (!unit.isGreatPerson()) return
val civInfo = unit.civInfo val civInfo = unit.civInfo
@ -782,7 +789,7 @@ object UnitActions {
if (getGiftAction != null) actionList += getGiftAction if (getGiftAction != null) actionList += getGiftAction
} }
fun getGiftAction(unit: MapUnit, tile: TileInfo): UnitAction? { private fun getGiftAction(unit: MapUnit, tile: TileInfo): UnitAction? {
val recipient = tile.getOwner() val recipient = tile.getOwner()
// We need to be in another civs territory. // We need to be in another civs territory.
if (recipient == null || recipient.isCurrentPlayer()) return null if (recipient == null || recipient.isCurrentPlayer()) return null
@ -824,7 +831,7 @@ object UnitActions {
return UnitAction(UnitActionType.GiftUnit, action = giftAction) return UnitAction(UnitActionType.GiftUnit, action = giftAction)
} }
fun addTriggerUniqueActions(unit: MapUnit, actionList: ArrayList<UnitAction>){ private fun addTriggerUniqueActions(unit: MapUnit, actionList: ArrayList<UnitAction>){
for (unique in unit.getUniques()) { for (unique in unit.getUniques()) {
if (!unique.conditionals.any { it.type == UniqueType.ConditionalConsumeUnit }) continue if (!unique.conditionals.any { it.type == UniqueType.ConditionalConsumeUnit }) continue
val unitAction = UnitAction(type = UnitActionType.TriggerUnique, unique.text){ val unitAction = UnitAction(type = UnitActionType.TriggerUnique, unique.text){