mirror of
https://github.com/yairm210/Unciv.git
synced 2025-03-11 18:39:43 +07:00
Unique enum compliance detection (#5226)
* Added basic functionality for uniques enum * Added unique type to Unique class for faster enum comparisons * And Elvis operator for unknown parameter type * Resolved #5162 - AI much less motivated to attack city-states * Whoops, wrong branch * New unique checks, with enum. * Fixed Trog's comments
This commit is contained in:
parent
65c8b40dfe
commit
34cb48aa31
@ -323,6 +323,19 @@ class Ruleset {
|
||||
for (building in buildings.values) {
|
||||
if (building.requiredTech == null && building.cost == 0 && !building.uniques.contains("Unbuildable"))
|
||||
lines += "${building.name} is buildable and therefore must either have an explicit cost or reference an existing tech!"
|
||||
|
||||
for (unique in building.uniqueObjects) {
|
||||
if (unique.type == null) continue
|
||||
val complianceErrors = unique.type.getComplianceErrors(unique, this)
|
||||
for (complianceError in complianceErrors) {
|
||||
// When not checking the entire ruleset, we can only really detect ruleset-invariant errors
|
||||
if (complianceError.errorSeverity == UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant)
|
||||
lines += "${building.name}'s unique \"${unique.text}\" contains parameter ${complianceError.parameterName}," +
|
||||
" which does not fit parameter type" +
|
||||
" ${complianceError.acceptableParameterTypes.joinToString(" or ") { it.parameterName }} !"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (nation in nations.values) {
|
||||
|
@ -1,26 +1,35 @@
|
||||
package com.unciv.models.ruleset
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.*
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.models.translations.fillPlaceholders
|
||||
import com.unciv.models.translations.getPlaceholderParameters
|
||||
import com.unciv.models.translations.getPlaceholderText
|
||||
import com.unciv.models.translations.hasPlaceholderParameters
|
||||
import com.unciv.ui.worldscreen.unit.UnitActions
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
// parameterName values should be compliant with autogenerated values in TranslationFileWriter.generateStringsFromJSONs
|
||||
// Eventually we'll merge the translation generation to take this as the source of that
|
||||
enum class UniqueParameterType(val parameterName:String, val complianceCheck:(String, Ruleset)->Boolean) {
|
||||
Number("amount", { s, r -> s.toIntOrNull() != null }),
|
||||
UnitFilter("unitType", { s, r -> r.unitTypes.containsKey(s) || unitTypeStrings.contains(s) }),
|
||||
Unknown("",{s,r -> true});
|
||||
enum class UniqueParameterType(val parameterName:String) {
|
||||
Number("amount") {
|
||||
override fun getErrorType(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueComplianceErrorSeverity? {
|
||||
return if (parameterText.toIntOrNull() != null) UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant
|
||||
else null
|
||||
}
|
||||
},
|
||||
UnitFilter("unitType") {
|
||||
override fun getErrorType(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueComplianceErrorSeverity? {
|
||||
if(ruleset.unitTypes.containsKey(parameterText) || unitTypeStrings.contains(parameterText)) return null
|
||||
return UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
||||
}
|
||||
},
|
||||
Unknown("param") {
|
||||
override fun getErrorType(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueComplianceErrorSeverity? {
|
||||
return null
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun getErrorType(parameterText:String, ruleset: Ruleset): UniqueType.UniqueComplianceErrorSeverity?
|
||||
|
||||
companion object {
|
||||
val unitTypeStrings = hashSetOf(
|
||||
@ -42,6 +51,13 @@ enum class UniqueParameterType(val parameterName:String, val complianceCheck:(St
|
||||
}
|
||||
}
|
||||
|
||||
class UniqueComplianceError(
|
||||
val parameterName: String,
|
||||
val acceptableParameterTypes: List<UniqueParameterType>,
|
||||
val errorSeverity: UniqueType.UniqueComplianceErrorSeverity
|
||||
)
|
||||
|
||||
|
||||
enum class UniqueType(val text:String) {
|
||||
|
||||
ConsumesResources("Consumes [amount] [resource]");
|
||||
@ -61,11 +77,35 @@ enum class UniqueType(val text:String) {
|
||||
|
||||
val placeholderText = text.getPlaceholderText()
|
||||
|
||||
fun checkCompliance(unique: Unique, ruleset: Ruleset): Boolean {
|
||||
for ((index, param) in unique.params.withIndex())
|
||||
if (parameterTypeMap[index].none { it.complianceCheck(param, ruleset) })
|
||||
return false
|
||||
return true
|
||||
/** Ordinal determines severity - ordered from most severe at 0 */
|
||||
enum class UniqueComplianceErrorSeverity {
|
||||
|
||||
/** This is a problem like "numbers don't parse", "stat isn't stat", "city filter not applicable" */
|
||||
RulesetInvariant,
|
||||
|
||||
/** This is a problem like "unit/resource/tech name doesn't exist in ruleset" - definite bug */
|
||||
RulesetSpecific,
|
||||
|
||||
/** This is for filters that can also potentially accept free text, like UnitFilter and TileFilter */
|
||||
WarningOnly
|
||||
}
|
||||
|
||||
/** Maps uncompliant parameters to their required types */
|
||||
fun getComplianceErrors(
|
||||
unique: Unique,
|
||||
ruleset: Ruleset
|
||||
): List<UniqueComplianceError> {
|
||||
val errorList = ArrayList<UniqueComplianceError>()
|
||||
for ((index, param) in unique.params.withIndex()) {
|
||||
val acceptableParamTypes = parameterTypeMap[index]
|
||||
val errorTypesForAcceptableParameters =
|
||||
acceptableParamTypes.map { it.getErrorType(param, ruleset) }
|
||||
if (errorTypesForAcceptableParameters.any { it == null }) continue // This matches one of the types!
|
||||
val leastSevereWarning =
|
||||
errorTypesForAcceptableParameters.maxByOrNull { it!!.ordinal }!!
|
||||
errorList += UniqueComplianceError(param, acceptableParamTypes, leastSevereWarning)
|
||||
}
|
||||
return errorList
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,8 +126,10 @@ class Unique(val text:String) {
|
||||
|
||||
|
||||
fun isOfType(uniqueType: UniqueType) = uniqueType == type
|
||||
|
||||
/** We can't save compliance errors in the unique, since it's ruleset-dependant */
|
||||
fun matches(uniqueType: UniqueType, ruleset: Ruleset) = isOfType(uniqueType)
|
||||
&& uniqueType.checkCompliance(this, ruleset)
|
||||
&& uniqueType.getComplianceErrors(this, ruleset).isEmpty()
|
||||
}
|
||||
|
||||
class UniqueMap:HashMap<String, ArrayList<Unique>>() {
|
||||
@ -103,477 +145,4 @@ class UniqueMap:HashMap<String, ArrayList<Unique>>() {
|
||||
}
|
||||
|
||||
fun getAllUniques() = this.asSequence().flatMap { it.value.asSequence() }
|
||||
}
|
||||
|
||||
// Buildings, techs, policies, ancient ruins and promotions can have 'triggered' effects
|
||||
object UniqueTriggerActivation {
|
||||
/** @return boolean whether an action was successfully preformed */
|
||||
fun triggerCivwideUnique(
|
||||
unique: Unique,
|
||||
civInfo: CivilizationInfo,
|
||||
cityInfo: CityInfo? = null,
|
||||
tile: TileInfo? = null,
|
||||
notification: String? = null
|
||||
): Boolean {
|
||||
val chosenCity =
|
||||
if (cityInfo != null) cityInfo
|
||||
else civInfo.cities.firstOrNull { it.isCapital() }
|
||||
val tileBasedRandom =
|
||||
if (tile != null) Random(tile.position.toString().hashCode())
|
||||
else Random(-550) // Very random indeed
|
||||
when (unique.placeholderText) {
|
||||
"Free [] appears" -> {
|
||||
val unitName = unique.params[0]
|
||||
val unit = civInfo.gameInfo.ruleSet.units[unitName]
|
||||
if (chosenCity == null || unit == null || (unit.uniques.contains("Founds a new city") && civInfo.isOneCityChallenger()))
|
||||
return false
|
||||
|
||||
val placedUnit = civInfo.addUnit(unitName, chosenCity)
|
||||
if (notification != null && placedUnit != null) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
placedUnit.getTile().position,
|
||||
placedUnit.name
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"[] free [] units appear" -> {
|
||||
val unitName = unique.params[1]
|
||||
val unit = civInfo.gameInfo.ruleSet.units[unitName]
|
||||
if (chosenCity == null || unit == null || (unit.uniques.contains("Founds a new city") && civInfo.isOneCityChallenger()))
|
||||
return false
|
||||
|
||||
val tilesUnitsWerePlacedOn: MutableList<Vector2> = mutableListOf()
|
||||
for (i in 1..unique.params[0].toInt()) {
|
||||
val placedUnit = civInfo.addUnit(unitName, chosenCity)
|
||||
if (placedUnit != null)
|
||||
tilesUnitsWerePlacedOn.add(placedUnit.getTile().position)
|
||||
}
|
||||
if (notification != null && tilesUnitsWerePlacedOn.isNotEmpty()) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
LocationAction(tilesUnitsWerePlacedOn),
|
||||
civInfo.getEquivalentUnit(unit).name
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
// Differs from "Free [] appears" in that it spawns near the ruins instead of in a city
|
||||
"Free [] found in the ruins" -> {
|
||||
val unit = civInfo.getEquivalentUnit(unique.params[0])
|
||||
val placingTile =
|
||||
tile ?: civInfo.cities.random().getCenterTile()
|
||||
|
||||
val placedUnit = civInfo.placeUnitNearTile(placingTile.position, unit.name)
|
||||
if (notification != null && placedUnit != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(unique.params[0])
|
||||
else notification
|
||||
civInfo.addNotification(
|
||||
notificationText,
|
||||
placedUnit.getTile().position,
|
||||
placedUnit.name
|
||||
)
|
||||
}
|
||||
|
||||
return placedUnit != null
|
||||
}
|
||||
|
||||
// spectators get all techs at start of game, and if (in a mod) a tech gives a free policy, the game gets stuck on the policy picker screen
|
||||
"Free Social Policy" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
civInfo.policies.freePolicies++
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Culture)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"[] Free Social Policies" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
civInfo.policies.freePolicies += unique.params[0].toInt()
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Culture)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"Empire enters golden age" -> {
|
||||
civInfo.goldenAges.enterGoldenAge()
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Happiness)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"Free Great Person" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
if (civInfo.isPlayerCivilization()) {
|
||||
civInfo.greatPeople.freeGreatPeople++
|
||||
if (notification != null)
|
||||
civInfo.addNotification(notification) // Anyone an idea for a good icon?
|
||||
return true
|
||||
} else {
|
||||
val greatPeople = civInfo.getGreatPeople()
|
||||
if (greatPeople.isEmpty()) return false
|
||||
var greatPerson = civInfo.getGreatPeople().random()
|
||||
|
||||
val preferredVictoryType = civInfo.victoryType()
|
||||
if (preferredVictoryType == VictoryType.Cultural) {
|
||||
val culturalGP =
|
||||
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Culture]") }
|
||||
if (culturalGP != null) greatPerson = culturalGP
|
||||
}
|
||||
if (preferredVictoryType == VictoryType.Scientific) {
|
||||
val scientificGP =
|
||||
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Science]") }
|
||||
if (scientificGP != null) greatPerson = scientificGP
|
||||
}
|
||||
|
||||
return civInfo.addUnit(greatPerson.name, chosenCity) != null
|
||||
}
|
||||
}
|
||||
"[] population []" -> {
|
||||
val citiesWithPopulationChanged: MutableList<Vector2> = mutableListOf()
|
||||
for (city in civInfo.cities) {
|
||||
if (city.matchesFilter(unique.params[1])) {
|
||||
city.population.addPopulation(unique.params[0].toInt())
|
||||
citiesWithPopulationChanged.add(city.location)
|
||||
}
|
||||
}
|
||||
if (notification != null && citiesWithPopulationChanged.isNotEmpty())
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
LocationAction(citiesWithPopulationChanged),
|
||||
NotificationIcon.Population
|
||||
)
|
||||
return citiesWithPopulationChanged.isNotEmpty()
|
||||
}
|
||||
"[] population in a random city" -> {
|
||||
if (civInfo.cities.isEmpty()) return false
|
||||
val randomCity = civInfo.cities.random(tileBasedRandom)
|
||||
randomCity.population.addPopulation(unique.params[0].toInt())
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(randomCity.name)
|
||||
else notification
|
||||
civInfo.addNotification(
|
||||
notificationText,
|
||||
randomCity.location,
|
||||
NotificationIcon.Population
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
"Free Technology" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
civInfo.tech.freeTechs += 1
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Science)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"[] Free Technologies" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
civInfo.tech.freeTechs += unique.params[0].toInt()
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Science)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"[] free random researchable Tech(s) from the []" -> {
|
||||
val researchableTechsFromThatEra = civInfo.gameInfo.ruleSet.technologies.values
|
||||
.filter {
|
||||
(it.column!!.era == unique.params[1] || unique.params[1] == "any era")
|
||||
&& civInfo.tech.canBeResearched(it.name)
|
||||
}
|
||||
if (researchableTechsFromThatEra.isEmpty()) return false
|
||||
|
||||
val techsToResearch = researchableTechsFromThatEra.shuffled(tileBasedRandom)
|
||||
.take(unique.params[0].toInt())
|
||||
for (tech in techsToResearch)
|
||||
civInfo.tech.addTechnology(tech.name)
|
||||
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(*(techsToResearch.map { it.name }
|
||||
.toTypedArray()))
|
||||
else notification
|
||||
civInfo.addNotification(notificationText, NotificationIcon.Science)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
"Quantity of strategic resources produced by the empire increased by 100%" -> {
|
||||
civInfo.updateDetailedCivResources()
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
NotificationIcon.War
|
||||
) // I'm open for better icons
|
||||
}
|
||||
return true
|
||||
}
|
||||
"+[]% attack strength to all [] Units for [] turns" -> {
|
||||
civInfo.temporaryUniques.add(Pair(unique, unique.params[2].toInt()))
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.War)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
"Reveals the entire map" -> {
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, "UnitIcons/Scout")
|
||||
}
|
||||
return civInfo.exploredTiles.addAll(
|
||||
civInfo.gameInfo.tileMap.values.asSequence().map { it.position })
|
||||
}
|
||||
|
||||
"[] units gain the [] promotion" -> {
|
||||
val filter = unique.params[0]
|
||||
val promotion = unique.params[1]
|
||||
|
||||
val promotedUnitLocations: MutableList<Vector2> = mutableListOf()
|
||||
for (unit in civInfo.getCivUnits()) {
|
||||
if (unit.matchesFilter(filter)
|
||||
&& civInfo.gameInfo.ruleSet.unitPromotions.values.any {
|
||||
it.name == promotion && unit.type.name in it.unitTypes
|
||||
}
|
||||
) {
|
||||
unit.promotions.addPromotion(promotion, isFree = true)
|
||||
promotedUnitLocations.add(unit.getTile().position)
|
||||
}
|
||||
}
|
||||
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
LocationAction(promotedUnitLocations),
|
||||
"unitPromotionIcons/${unique.params[1]}"
|
||||
)
|
||||
}
|
||||
return promotedUnitLocations.isNotEmpty()
|
||||
}
|
||||
|
||||
"Allied City-States will occasionally gift Great People" -> {
|
||||
civInfo.addFlag(
|
||||
CivFlags.CityStateGreatPersonGift.name,
|
||||
civInfo.turnsForGreatPersonFromCityState() / 2
|
||||
)
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.CityState)
|
||||
}
|
||||
return true
|
||||
}
|
||||
// The mechanics for granting great people are wonky, but basically the following happens:
|
||||
// Based on the game speed, a timer with some amount of turns is set, 40 on regular speed
|
||||
// Every turn, 1 is subtracted from this timer, as long as you have at least 1 city state ally
|
||||
// So no, the number of city-state allies does not matter for this. You have a global timer for all of them combined.
|
||||
// If the timer reaches the amount of city-state allies you have (or 10, whichever is lower), it is reset.
|
||||
// You will then receive a random great person from a random city-state you are allied to
|
||||
// The very first time after acquiring this policy, the timer is set to half of its normal value
|
||||
// This is the basics, and apart from this, there is some randomness in the exact turn count, but I don't know how much
|
||||
|
||||
// There is surprisingly little information findable online about this policy, and the civ 5 source files are
|
||||
// also quite though to search through, so this might all be incorrect.
|
||||
// For now this mechanic seems decent enough that this is fine.
|
||||
|
||||
// Note that the way this is implemented now, this unique does NOT stack
|
||||
// I could parametrize the [Allied], but eh.
|
||||
|
||||
"Gain [] []" -> {
|
||||
if (Stat.values().none { it.name == unique.params[1] }) return false
|
||||
val stat = Stat.valueOf(unique.params[1])
|
||||
|
||||
if (stat !in listOf(Stat.Gold, Stat.Faith, Stat.Science, Stat.Culture)
|
||||
|| unique.params[0].toIntOrNull() == null
|
||||
) return false
|
||||
|
||||
civInfo.addStat(stat, unique.params[0].toInt())
|
||||
if (notification != null)
|
||||
civInfo.addNotification(notification, stat.notificationIcon)
|
||||
return true
|
||||
}
|
||||
"Gain []-[] []" -> {
|
||||
if (Stat.values().none { it.name == unique.params[2] }) return false
|
||||
val stat = Stat.valueOf(unique.params[2])
|
||||
|
||||
if (stat !in listOf(Stat.Gold, Stat.Faith, Stat.Science, Stat.Culture)
|
||||
|| unique.params[0].toIntOrNull() == null
|
||||
|| unique.params[1].toIntOrNull() == null
|
||||
) return false
|
||||
|
||||
val foundStatAmount =
|
||||
(tileBasedRandom.nextInt(unique.params[0].toInt(), unique.params[1].toInt()) *
|
||||
civInfo.gameInfo.gameParameters.gameSpeed.modifier
|
||||
).toInt()
|
||||
|
||||
civInfo.addStat(
|
||||
Stat.valueOf(unique.params[2]),
|
||||
foundStatAmount
|
||||
)
|
||||
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters()) {
|
||||
notification.fillPlaceholders(foundStatAmount.toString())
|
||||
} else notification
|
||||
civInfo.addNotification(notificationText, stat.notificationIcon)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
"Gain enough Faith for a Pantheon" -> {
|
||||
if (civInfo.religionManager.religionState != ReligionState.None) return false
|
||||
val gainedFaith = civInfo.religionManager.faithForPantheon(2)
|
||||
if (gainedFaith == 0) return false
|
||||
|
||||
civInfo.addStat(Stat.Faith, gainedFaith)
|
||||
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(gainedFaith.toString())
|
||||
else notification
|
||||
civInfo.addNotification(notificationText, NotificationIcon.Faith)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
"Gain enough Faith for []% of a Great Prophet" -> {
|
||||
if (civInfo.religionManager.getGreatProphetEquivalent() == null) return false
|
||||
val gainedFaith =
|
||||
(civInfo.religionManager.faithForNextGreatProphet() * (unique.params[0].toFloat() / 100f)).toInt()
|
||||
if (gainedFaith == 0) return false
|
||||
|
||||
civInfo.addStat(Stat.Faith, gainedFaith)
|
||||
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(gainedFaith.toString())
|
||||
else notification
|
||||
civInfo.addNotification(notificationText, NotificationIcon.Faith)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
"Reveal up to [] [] within a [] tile radius" -> {
|
||||
if (tile == null) return false
|
||||
val nearbyRevealableTiles = tile
|
||||
.getTilesInDistance(unique.params[2].toInt())
|
||||
.filter {
|
||||
!civInfo.exploredTiles.contains(it.position) && it.matchesFilter(
|
||||
unique.params[1]
|
||||
)
|
||||
}
|
||||
.map { it.position }
|
||||
if (nearbyRevealableTiles.none()) return false
|
||||
civInfo.exploredTiles.addAll(nearbyRevealableTiles
|
||||
.shuffled(tileBasedRandom)
|
||||
.apply {
|
||||
if (unique.params[0] != "All") this.take(unique.params[0].toInt())
|
||||
}
|
||||
)
|
||||
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
LocationAction(nearbyRevealableTiles.toList())
|
||||
) // We really need a barbarian icon
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
"From a randomly chosen tile [] tiles away from the ruins, reveal tiles up to [] tiles away with []% chance" -> {
|
||||
if (tile == null) return false
|
||||
val revealCenter = tile.getTilesAtDistance(unique.params[0].toInt())
|
||||
.filter { it.position !in civInfo.exploredTiles }
|
||||
.toList()
|
||||
.randomOrNull(tileBasedRandom)
|
||||
if (revealCenter == null) return false
|
||||
val tilesToReveal = revealCenter
|
||||
.getTilesInDistance(unique.params[1].toInt())
|
||||
.map { it.position }
|
||||
.filter { tileBasedRandom.nextFloat() < unique.params[2].toFloat() / 100f }
|
||||
civInfo.exploredTiles.addAll(tilesToReveal)
|
||||
civInfo.updateViewableTiles()
|
||||
if (notification != null)
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
tile.position,
|
||||
"ImprovementIcons/Ancient ruins"
|
||||
)
|
||||
}
|
||||
"Triggers voting for the Diplomatic Victory" -> {
|
||||
for (civ in civInfo.gameInfo.civilizations)
|
||||
if (!civ.isBarbarian() && !civ.isSpectator())
|
||||
civ.addFlag(
|
||||
CivFlags.TurnsTillNextDiplomaticVote.name,
|
||||
civInfo.getTurnsBetweenDiplomaticVotings()
|
||||
)
|
||||
if (notification != null)
|
||||
civInfo.addNotification(notification, NotificationIcon.Diplomacy)
|
||||
return true
|
||||
}
|
||||
|
||||
"Provides the cheapest [] building in your first [] cities for free",
|
||||
"Provides a [] in your first [] cities for free" ->
|
||||
civInfo.civConstructions.tryAddFreeBuildings()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** @return boolean whether an action was successfully preformed */
|
||||
fun triggerUnitwideUnique(
|
||||
unique: Unique,
|
||||
unit: MapUnit,
|
||||
notification: String? = null
|
||||
): Boolean {
|
||||
when (unique.placeholderText) {
|
||||
"Heal this unit by [] HP" -> {
|
||||
unit.healBy(unique.params[0].toInt())
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.getTile().position) // Do we have a heal icon?
|
||||
return true
|
||||
}
|
||||
"This Unit gains [] XP" -> {
|
||||
if (!unit.baseUnit.isMilitary()) return false
|
||||
unit.promotions.XP += unique.params[0].toInt()
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.getTile().position)
|
||||
return true
|
||||
}
|
||||
"This Unit upgrades for free" -> {
|
||||
val upgradeAction = UnitActions.getUpgradeAction(unit, true)
|
||||
?: return false
|
||||
upgradeAction.action!!()
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.getTile().position)
|
||||
return true
|
||||
}
|
||||
"This Unit upgrades for free including special upgrades" -> {
|
||||
val upgradeAction = UnitActions.getAncientRuinsUpgradeAction(unit)
|
||||
?: return false
|
||||
upgradeAction.action!!()
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.getTile().position)
|
||||
return true
|
||||
}
|
||||
"This Unit gains the [] promotion" -> {
|
||||
val promotion = unit.civInfo.gameInfo.ruleSet.unitPromotions.keys.firstOrNull { it == unique.params[0] }
|
||||
if (promotion == null) return false
|
||||
unit.promotions.addPromotion(promotion, true)
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.name)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
485
core/src/com/unciv/models/ruleset/UniqueTriggerActivation.kt
Normal file
485
core/src/com/unciv/models/ruleset/UniqueTriggerActivation.kt
Normal file
@ -0,0 +1,485 @@
|
||||
package com.unciv.models.ruleset
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.*
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.fillPlaceholders
|
||||
import com.unciv.models.translations.hasPlaceholderParameters
|
||||
import com.unciv.ui.worldscreen.unit.UnitActions
|
||||
import kotlin.random.Random
|
||||
|
||||
// Buildings, techs, policies, ancient ruins and promotions can have 'triggered' effects
|
||||
object UniqueTriggerActivation {
|
||||
/** @return boolean whether an action was successfully preformed */
|
||||
fun triggerCivwideUnique(
|
||||
unique: Unique,
|
||||
civInfo: CivilizationInfo,
|
||||
cityInfo: CityInfo? = null,
|
||||
tile: TileInfo? = null,
|
||||
notification: String? = null
|
||||
): Boolean {
|
||||
val chosenCity =
|
||||
if (cityInfo != null) cityInfo
|
||||
else civInfo.cities.firstOrNull { it.isCapital() }
|
||||
val tileBasedRandom =
|
||||
if (tile != null) Random(tile.position.toString().hashCode())
|
||||
else Random(-550) // Very random indeed
|
||||
when (unique.placeholderText) {
|
||||
"Free [] appears" -> {
|
||||
val unitName = unique.params[0]
|
||||
val unit = civInfo.gameInfo.ruleSet.units[unitName]
|
||||
if (chosenCity == null || unit == null || (unit.uniques.contains("Founds a new city") && civInfo.isOneCityChallenger()))
|
||||
return false
|
||||
|
||||
val placedUnit = civInfo.addUnit(unitName, chosenCity)
|
||||
if (notification != null && placedUnit != null) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
placedUnit.getTile().position,
|
||||
placedUnit.name
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"[] free [] units appear" -> {
|
||||
val unitName = unique.params[1]
|
||||
val unit = civInfo.gameInfo.ruleSet.units[unitName]
|
||||
if (chosenCity == null || unit == null || (unit.uniques.contains("Founds a new city") && civInfo.isOneCityChallenger()))
|
||||
return false
|
||||
|
||||
val tilesUnitsWerePlacedOn: MutableList<Vector2> = mutableListOf()
|
||||
for (i in 1..unique.params[0].toInt()) {
|
||||
val placedUnit = civInfo.addUnit(unitName, chosenCity)
|
||||
if (placedUnit != null)
|
||||
tilesUnitsWerePlacedOn.add(placedUnit.getTile().position)
|
||||
}
|
||||
if (notification != null && tilesUnitsWerePlacedOn.isNotEmpty()) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
LocationAction(tilesUnitsWerePlacedOn),
|
||||
civInfo.getEquivalentUnit(unit).name
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
// Differs from "Free [] appears" in that it spawns near the ruins instead of in a city
|
||||
"Free [] found in the ruins" -> {
|
||||
val unit = civInfo.getEquivalentUnit(unique.params[0])
|
||||
val placingTile =
|
||||
tile ?: civInfo.cities.random().getCenterTile()
|
||||
|
||||
val placedUnit = civInfo.placeUnitNearTile(placingTile.position, unit.name)
|
||||
if (notification != null && placedUnit != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(unique.params[0])
|
||||
else notification
|
||||
civInfo.addNotification(
|
||||
notificationText,
|
||||
placedUnit.getTile().position,
|
||||
placedUnit.name
|
||||
)
|
||||
}
|
||||
|
||||
return placedUnit != null
|
||||
}
|
||||
|
||||
// spectators get all techs at start of game, and if (in a mod) a tech gives a free policy, the game gets stuck on the policy picker screen
|
||||
"Free Social Policy" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
civInfo.policies.freePolicies++
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Culture)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"[] Free Social Policies" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
civInfo.policies.freePolicies += unique.params[0].toInt()
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Culture)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"Empire enters golden age" -> {
|
||||
civInfo.goldenAges.enterGoldenAge()
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Happiness)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"Free Great Person" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
if (civInfo.isPlayerCivilization()) {
|
||||
civInfo.greatPeople.freeGreatPeople++
|
||||
if (notification != null)
|
||||
civInfo.addNotification(notification) // Anyone an idea for a good icon?
|
||||
return true
|
||||
} else {
|
||||
val greatPeople = civInfo.getGreatPeople()
|
||||
if (greatPeople.isEmpty()) return false
|
||||
var greatPerson = civInfo.getGreatPeople().random()
|
||||
|
||||
val preferredVictoryType = civInfo.victoryType()
|
||||
if (preferredVictoryType == VictoryType.Cultural) {
|
||||
val culturalGP =
|
||||
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Culture]") }
|
||||
if (culturalGP != null) greatPerson = culturalGP
|
||||
}
|
||||
if (preferredVictoryType == VictoryType.Scientific) {
|
||||
val scientificGP =
|
||||
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Science]") }
|
||||
if (scientificGP != null) greatPerson = scientificGP
|
||||
}
|
||||
|
||||
return civInfo.addUnit(greatPerson.name, chosenCity) != null
|
||||
}
|
||||
}
|
||||
"[] population []" -> {
|
||||
val citiesWithPopulationChanged: MutableList<Vector2> = mutableListOf()
|
||||
for (city in civInfo.cities) {
|
||||
if (city.matchesFilter(unique.params[1])) {
|
||||
city.population.addPopulation(unique.params[0].toInt())
|
||||
citiesWithPopulationChanged.add(city.location)
|
||||
}
|
||||
}
|
||||
if (notification != null && citiesWithPopulationChanged.isNotEmpty())
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
LocationAction(citiesWithPopulationChanged),
|
||||
NotificationIcon.Population
|
||||
)
|
||||
return citiesWithPopulationChanged.isNotEmpty()
|
||||
}
|
||||
"[] population in a random city" -> {
|
||||
if (civInfo.cities.isEmpty()) return false
|
||||
val randomCity = civInfo.cities.random(tileBasedRandom)
|
||||
randomCity.population.addPopulation(unique.params[0].toInt())
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(randomCity.name)
|
||||
else notification
|
||||
civInfo.addNotification(
|
||||
notificationText,
|
||||
randomCity.location,
|
||||
NotificationIcon.Population
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
"Free Technology" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
civInfo.tech.freeTechs += 1
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Science)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"[] Free Technologies" -> {
|
||||
if (civInfo.isSpectator()) return false
|
||||
civInfo.tech.freeTechs += unique.params[0].toInt()
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.Science)
|
||||
}
|
||||
return true
|
||||
}
|
||||
"[] free random researchable Tech(s) from the []" -> {
|
||||
val researchableTechsFromThatEra = civInfo.gameInfo.ruleSet.technologies.values
|
||||
.filter {
|
||||
(it.column!!.era == unique.params[1] || unique.params[1] == "any era")
|
||||
&& civInfo.tech.canBeResearched(it.name)
|
||||
}
|
||||
if (researchableTechsFromThatEra.isEmpty()) return false
|
||||
|
||||
val techsToResearch = researchableTechsFromThatEra.shuffled(tileBasedRandom)
|
||||
.take(unique.params[0].toInt())
|
||||
for (tech in techsToResearch)
|
||||
civInfo.tech.addTechnology(tech.name)
|
||||
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(*(techsToResearch.map { it.name }
|
||||
.toTypedArray()))
|
||||
else notification
|
||||
civInfo.addNotification(notificationText, NotificationIcon.Science)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
"Quantity of strategic resources produced by the empire increased by 100%" -> {
|
||||
civInfo.updateDetailedCivResources()
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
NotificationIcon.War
|
||||
) // I'm open for better icons
|
||||
}
|
||||
return true
|
||||
}
|
||||
"+[]% attack strength to all [] Units for [] turns" -> {
|
||||
civInfo.temporaryUniques.add(Pair(unique, unique.params[2].toInt()))
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.War)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
"Reveals the entire map" -> {
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, "UnitIcons/Scout")
|
||||
}
|
||||
return civInfo.exploredTiles.addAll(
|
||||
civInfo.gameInfo.tileMap.values.asSequence().map { it.position })
|
||||
}
|
||||
|
||||
"[] units gain the [] promotion" -> {
|
||||
val filter = unique.params[0]
|
||||
val promotion = unique.params[1]
|
||||
|
||||
val promotedUnitLocations: MutableList<Vector2> = mutableListOf()
|
||||
for (unit in civInfo.getCivUnits()) {
|
||||
if (unit.matchesFilter(filter)
|
||||
&& civInfo.gameInfo.ruleSet.unitPromotions.values.any {
|
||||
it.name == promotion && unit.type.name in it.unitTypes
|
||||
}
|
||||
) {
|
||||
unit.promotions.addPromotion(promotion, isFree = true)
|
||||
promotedUnitLocations.add(unit.getTile().position)
|
||||
}
|
||||
}
|
||||
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
LocationAction(promotedUnitLocations),
|
||||
"unitPromotionIcons/${unique.params[1]}"
|
||||
)
|
||||
}
|
||||
return promotedUnitLocations.isNotEmpty()
|
||||
}
|
||||
|
||||
"Allied City-States will occasionally gift Great People" -> {
|
||||
civInfo.addFlag(
|
||||
CivFlags.CityStateGreatPersonGift.name,
|
||||
civInfo.turnsForGreatPersonFromCityState() / 2
|
||||
)
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(notification, NotificationIcon.CityState)
|
||||
}
|
||||
return true
|
||||
}
|
||||
// The mechanics for granting great people are wonky, but basically the following happens:
|
||||
// Based on the game speed, a timer with some amount of turns is set, 40 on regular speed
|
||||
// Every turn, 1 is subtracted from this timer, as long as you have at least 1 city state ally
|
||||
// So no, the number of city-state allies does not matter for this. You have a global timer for all of them combined.
|
||||
// If the timer reaches the amount of city-state allies you have (or 10, whichever is lower), it is reset.
|
||||
// You will then receive a random great person from a random city-state you are allied to
|
||||
// The very first time after acquiring this policy, the timer is set to half of its normal value
|
||||
// This is the basics, and apart from this, there is some randomness in the exact turn count, but I don't know how much
|
||||
|
||||
// There is surprisingly little information findable online about this policy, and the civ 5 source files are
|
||||
// also quite though to search through, so this might all be incorrect.
|
||||
// For now this mechanic seems decent enough that this is fine.
|
||||
|
||||
// Note that the way this is implemented now, this unique does NOT stack
|
||||
// I could parametrize the [Allied], but eh.
|
||||
|
||||
"Gain [] []" -> {
|
||||
if (Stat.values().none { it.name == unique.params[1] }) return false
|
||||
val stat = Stat.valueOf(unique.params[1])
|
||||
|
||||
if (stat !in listOf(Stat.Gold, Stat.Faith, Stat.Science, Stat.Culture)
|
||||
|| unique.params[0].toIntOrNull() == null
|
||||
) return false
|
||||
|
||||
civInfo.addStat(stat, unique.params[0].toInt())
|
||||
if (notification != null)
|
||||
civInfo.addNotification(notification, stat.notificationIcon)
|
||||
return true
|
||||
}
|
||||
"Gain []-[] []" -> {
|
||||
if (Stat.values().none { it.name == unique.params[2] }) return false
|
||||
val stat = Stat.valueOf(unique.params[2])
|
||||
|
||||
if (stat !in listOf(Stat.Gold, Stat.Faith, Stat.Science, Stat.Culture)
|
||||
|| unique.params[0].toIntOrNull() == null
|
||||
|| unique.params[1].toIntOrNull() == null
|
||||
) return false
|
||||
|
||||
val foundStatAmount =
|
||||
(tileBasedRandom.nextInt(unique.params[0].toInt(), unique.params[1].toInt()) *
|
||||
civInfo.gameInfo.gameParameters.gameSpeed.modifier
|
||||
).toInt()
|
||||
|
||||
civInfo.addStat(
|
||||
Stat.valueOf(unique.params[2]),
|
||||
foundStatAmount
|
||||
)
|
||||
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters()) {
|
||||
notification.fillPlaceholders(foundStatAmount.toString())
|
||||
} else notification
|
||||
civInfo.addNotification(notificationText, stat.notificationIcon)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
"Gain enough Faith for a Pantheon" -> {
|
||||
if (civInfo.religionManager.religionState != ReligionState.None) return false
|
||||
val gainedFaith = civInfo.religionManager.faithForPantheon(2)
|
||||
if (gainedFaith == 0) return false
|
||||
|
||||
civInfo.addStat(Stat.Faith, gainedFaith)
|
||||
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(gainedFaith.toString())
|
||||
else notification
|
||||
civInfo.addNotification(notificationText, NotificationIcon.Faith)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
"Gain enough Faith for []% of a Great Prophet" -> {
|
||||
if (civInfo.religionManager.getGreatProphetEquivalent() == null) return false
|
||||
val gainedFaith =
|
||||
(civInfo.religionManager.faithForNextGreatProphet() * (unique.params[0].toFloat() / 100f)).toInt()
|
||||
if (gainedFaith == 0) return false
|
||||
|
||||
civInfo.addStat(Stat.Faith, gainedFaith)
|
||||
|
||||
if (notification != null) {
|
||||
val notificationText =
|
||||
if (notification.hasPlaceholderParameters())
|
||||
notification.fillPlaceholders(gainedFaith.toString())
|
||||
else notification
|
||||
civInfo.addNotification(notificationText, NotificationIcon.Faith)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
"Reveal up to [] [] within a [] tile radius" -> {
|
||||
if (tile == null) return false
|
||||
val nearbyRevealableTiles = tile
|
||||
.getTilesInDistance(unique.params[2].toInt())
|
||||
.filter {
|
||||
!civInfo.exploredTiles.contains(it.position) && it.matchesFilter(
|
||||
unique.params[1]
|
||||
)
|
||||
}
|
||||
.map { it.position }
|
||||
if (nearbyRevealableTiles.none()) return false
|
||||
civInfo.exploredTiles.addAll(nearbyRevealableTiles
|
||||
.shuffled(tileBasedRandom)
|
||||
.apply {
|
||||
if (unique.params[0] != "All") this.take(unique.params[0].toInt())
|
||||
}
|
||||
)
|
||||
|
||||
if (notification != null) {
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
LocationAction(nearbyRevealableTiles.toList())
|
||||
) // We really need a barbarian icon
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
"From a randomly chosen tile [] tiles away from the ruins, reveal tiles up to [] tiles away with []% chance" -> {
|
||||
if (tile == null) return false
|
||||
val revealCenter = tile.getTilesAtDistance(unique.params[0].toInt())
|
||||
.filter { it.position !in civInfo.exploredTiles }
|
||||
.toList()
|
||||
.randomOrNull(tileBasedRandom)
|
||||
if (revealCenter == null) return false
|
||||
val tilesToReveal = revealCenter
|
||||
.getTilesInDistance(unique.params[1].toInt())
|
||||
.map { it.position }
|
||||
.filter { tileBasedRandom.nextFloat() < unique.params[2].toFloat() / 100f }
|
||||
civInfo.exploredTiles.addAll(tilesToReveal)
|
||||
civInfo.updateViewableTiles()
|
||||
if (notification != null)
|
||||
civInfo.addNotification(
|
||||
notification,
|
||||
tile.position,
|
||||
"ImprovementIcons/Ancient ruins"
|
||||
)
|
||||
}
|
||||
"Triggers voting for the Diplomatic Victory" -> {
|
||||
for (civ in civInfo.gameInfo.civilizations)
|
||||
if (!civ.isBarbarian() && !civ.isSpectator())
|
||||
civ.addFlag(
|
||||
CivFlags.TurnsTillNextDiplomaticVote.name,
|
||||
civInfo.getTurnsBetweenDiplomaticVotings()
|
||||
)
|
||||
if (notification != null)
|
||||
civInfo.addNotification(notification, NotificationIcon.Diplomacy)
|
||||
return true
|
||||
}
|
||||
|
||||
"Provides the cheapest [] building in your first [] cities for free",
|
||||
"Provides a [] in your first [] cities for free" ->
|
||||
civInfo.civConstructions.tryAddFreeBuildings()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** @return boolean whether an action was successfully preformed */
|
||||
fun triggerUnitwideUnique(
|
||||
unique: Unique,
|
||||
unit: MapUnit,
|
||||
notification: String? = null
|
||||
): Boolean {
|
||||
when (unique.placeholderText) {
|
||||
"Heal this unit by [] HP" -> {
|
||||
unit.healBy(unique.params[0].toInt())
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.getTile().position) // Do we have a heal icon?
|
||||
return true
|
||||
}
|
||||
"This Unit gains [] XP" -> {
|
||||
if (!unit.baseUnit.isMilitary()) return false
|
||||
unit.promotions.XP += unique.params[0].toInt()
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.getTile().position)
|
||||
return true
|
||||
}
|
||||
"This Unit upgrades for free" -> {
|
||||
val upgradeAction = UnitActions.getUpgradeAction(unit, true)
|
||||
?: return false
|
||||
upgradeAction.action!!()
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.getTile().position)
|
||||
return true
|
||||
}
|
||||
"This Unit upgrades for free including special upgrades" -> {
|
||||
val upgradeAction = UnitActions.getAncientRuinsUpgradeAction(unit)
|
||||
?: return false
|
||||
upgradeAction.action!!()
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.getTile().position)
|
||||
return true
|
||||
}
|
||||
"This Unit gains the [] promotion" -> {
|
||||
val promotion = unit.civInfo.gameInfo.ruleSet.unitPromotions.keys.firstOrNull { it == unique.params[0] }
|
||||
if (promotion == null) return false
|
||||
unit.promotions.addPromotion(promotion, true)
|
||||
if (notification != null)
|
||||
unit.civInfo.addNotification(notification, unit.name)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user