Allow determining *if* a unique can trigger a triggerable effect, *without* actually doing so

This commit is contained in:
Yair Morgenstern 2024-02-20 10:52:28 +02:00
parent d51d032e18
commit 27465425b0

View File

@ -61,6 +61,20 @@ object UniqueTriggerActivation {
notification: String? = null, notification: String? = null,
triggerNotificationText: String? = null triggerNotificationText: String? = null
): Boolean { ): Boolean {
val function = getTriggerFunction(unique, civInfo, city, unit, tile, notification, triggerNotificationText) ?: return false
return function.invoke()
}
/** @return The action to be performed if possible, else null */
fun getTriggerFunction(
unique: Unique,
civInfo: Civilization,
city: City? = null,
unit: MapUnit? = null,
tile: Tile? = city?.getCenterTile() ?: unit?.currentTile,
notification: String? = null,
triggerNotificationText: String? = null
): (()->Boolean)? {
val relevantCity by lazy { val relevantCity by lazy {
city?: tile?.getCity() city?: tile?.getCity()
@ -68,11 +82,10 @@ object UniqueTriggerActivation {
val timingConditional = unique.conditionals.firstOrNull { it.type == UniqueType.ConditionalTimedUnique } val timingConditional = unique.conditionals.firstOrNull { it.type == UniqueType.ConditionalTimedUnique }
if (timingConditional != null) { if (timingConditional != null) {
civInfo.temporaryUniques.add(TemporaryUnique(unique, timingConditional.params[0].toInt())) return { civInfo.temporaryUniques.add(TemporaryUnique(unique, timingConditional.params[0].toInt())) }
return true
} }
if (!unique.conditionalsApply(StateForConditionals(civInfo, city, unit, tile))) return false if (!unique.conditionalsApply(StateForConditionals(civInfo, city, unit, tile))) return null
val chosenCity = relevantCity ?: val chosenCity = relevantCity ?:
civInfo.cities.firstOrNull { it.isCapital() } civInfo.cities.firstOrNull { it.isCapital() }
@ -85,16 +98,17 @@ object UniqueTriggerActivation {
when (unique.type) { when (unique.type) {
UniqueType.OneTimeFreeUnit -> { UniqueType.OneTimeFreeUnit -> {
val unitName = unique.params[0] val unitName = unique.params[0]
val baseUnit = ruleSet.units[unitName] ?: return false val baseUnit = ruleSet.units[unitName] ?: return null
val civUnit = civInfo.getEquivalentUnit(baseUnit) val civUnit = civInfo.getEquivalentUnit(baseUnit)
if (civUnit.isCityFounder() && civInfo.isOneCityChallenger()) if (civUnit.isCityFounder() && civInfo.isOneCityChallenger())
return false return null
val limit = civUnit.getMatchingUniques(UniqueType.MaxNumberBuildable) val limit = civUnit.getMatchingUniques(UniqueType.MaxNumberBuildable)
.map { it.params[0].toInt() }.minOrNull() .map { it.params[0].toInt() }.minOrNull()
if (limit != null && limit <= civInfo.units.getCivUnits().count { it.name == civUnit.name }) if (limit != null && limit <= civInfo.units.getCivUnits().count { it.name == civUnit.name })
return false return null
fun placeUnit(): Boolean {
val placedUnit = when { val placedUnit = when {
// Set unit at city if there's an explict city or if there's no tile to set at // Set unit at city if there's an explict city or if there's no tile to set at
relevantCity != null || (tile == null && civInfo.cities.isNotEmpty()) -> relevantCity != null || (tile == null && civInfo.cities.isNotEmpty()) ->
@ -104,12 +118,14 @@ object UniqueTriggerActivation {
// Else set unit unit near other units if we have no cities // Else set unit unit near other units if we have no cities
civInfo.units.getCivUnits().any() -> civInfo.units.getCivUnits().any() ->
civInfo.units.placeUnitNearTile(civInfo.units.getCivUnits().first().currentTile.position, civUnit) ?: return false civInfo.units.placeUnitNearTile(civInfo.units.getCivUnits().first().currentTile.position, civUnit) ?: return false
else -> return false else -> return false
} }
val notificationText = getNotificationText(notification, triggerNotificationText, val notificationText = getNotificationText(
"Gained [1] [${civUnit.name}] unit(s)") notification, triggerNotificationText,
?: return true "Gained [1] [${civUnit.name}] unit(s)"
)
if (notificationText != null)
civInfo.addNotification( civInfo.addNotification(
notificationText, notificationText,
MapUnitAction(placedUnit), MapUnitAction(placedUnit),
@ -118,12 +134,15 @@ object UniqueTriggerActivation {
) )
return true return true
} }
return { placeUnit() }
}
UniqueType.OneTimeAmountFreeUnits -> { UniqueType.OneTimeAmountFreeUnits -> {
val unitName = unique.params[1] val unitName = unique.params[1]
val baseUnit = ruleSet.units[unitName] ?: return false val baseUnit = ruleSet.units[unitName] ?: return null
val civUnit = civInfo.getEquivalentUnit(baseUnit) val civUnit = civInfo.getEquivalentUnit(baseUnit)
if (civUnit.isCityFounder() && civInfo.isOneCityChallenger()) if (civUnit.isCityFounder() && civInfo.isOneCityChallenger())
return false return null
val limit = civUnit.getMatchingUniques(UniqueType.MaxNumberBuildable) val limit = civUnit.getMatchingUniques(UniqueType.MaxNumberBuildable)
.map { it.params[0].toInt() }.minOrNull() .map { it.params[0].toInt() }.minOrNull()
@ -135,8 +154,9 @@ object UniqueTriggerActivation {
else -> amountFromTriggerable else -> amountFromTriggerable
} }
if (actualAmount <= 0) return false if (actualAmount <= 0) return null
fun placeUnits():Boolean {
val tilesUnitsWerePlacedOn: MutableList<Vector2> = mutableListOf() val tilesUnitsWerePlacedOn: MutableList<Vector2> = mutableListOf()
repeat(actualAmount) { repeat(actualAmount) {
val placedUnit = when { val placedUnit = when {
@ -148,6 +168,7 @@ object UniqueTriggerActivation {
// Else set unit unit near other units if we have no cities // Else set unit unit near other units if we have no cities
civInfo.units.getCivUnits().any() -> civInfo.units.getCivUnits().any() ->
civInfo.units.placeUnitNearTile(civInfo.units.getCivUnits().first().currentTile.position, civUnit) civInfo.units.placeUnitNearTile(civInfo.units.getCivUnits().first().currentTile.position, civUnit)
else -> null else -> null
} }
if (placedUnit != null) if (placedUnit != null)
@ -155,10 +176,12 @@ object UniqueTriggerActivation {
} }
if (tilesUnitsWerePlacedOn.isEmpty()) return false if (tilesUnitsWerePlacedOn.isEmpty()) return false
val notificationText = getNotificationText(notification, triggerNotificationText, val notificationText = getNotificationText(
"Gained [${tilesUnitsWerePlacedOn.size}] [${civUnit.name}] unit(s)") notification, triggerNotificationText,
?: return true "Gained [${tilesUnitsWerePlacedOn.size}] [${civUnit.name}] unit(s)"
)
if (notificationText != null)
civInfo.addNotification( civInfo.addNotification(
notificationText, notificationText,
MapUnitAction(tilesUnitsWerePlacedOn), MapUnitAction(tilesUnitsWerePlacedOn),
@ -167,6 +190,8 @@ object UniqueTriggerActivation {
) )
return true return true
} }
return { placeUnits() }
}
UniqueType.OneTimeFreeUnitRuins -> { UniqueType.OneTimeFreeUnitRuins -> {
var civUnit = civInfo.getEquivalentUnit(unique.params[0]) var civUnit = civInfo.getEquivalentUnit(unique.params[0])
if ( civUnit.isCityFounder() && civInfo.isOneCityChallenger()) { if ( civUnit.isCityFounder() && civInfo.isOneCityChallenger()) {
@ -174,13 +199,14 @@ object UniqueTriggerActivation {
.firstOrNull { .firstOrNull {
it.getMatchingUniques(UniqueType.BuildImprovements) it.getMatchingUniques(UniqueType.BuildImprovements)
.any { unique -> unique.params[0] == "Land" } .any { unique -> unique.params[0] == "Land" }
} ?: return false } ?: return null
civUnit = civInfo.getEquivalentUnit(replacementUnit.name) civUnit = civInfo.getEquivalentUnit(replacementUnit.name)
} }
val placingTile = val placingTile =
tile ?: civInfo.cities.random().getCenterTile() tile ?: civInfo.cities.random().getCenterTile()
fun placeUnit():Boolean {
val placedUnit = civInfo.units.placeUnitNearTile(placingTile.position, civUnit.name) val placedUnit = civInfo.units.placeUnitNearTile(placingTile.position, civUnit.name)
if (notification != null && placedUnit != null) { if (notification != null && placedUnit != null) {
val notificationText = val notificationText =
@ -197,62 +223,78 @@ object UniqueTriggerActivation {
placedUnit.name placedUnit.name
) )
} }
return placedUnit != null return placedUnit != null
} }
return {placeUnit()}
}
UniqueType.OneTimeFreePolicy -> { UniqueType.OneTimeFreePolicy -> {
// 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 // 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
if (civInfo.isSpectator()) return false if (civInfo.isSpectator()) return null
return {
civInfo.policies.freePolicies++ civInfo.policies.freePolicies++
val notificationText = getNotificationText(notification, triggerNotificationText, val notificationText = getNotificationText(notification, triggerNotificationText,
"You may choose a free Policy") "You may choose a free Policy")
?: return true if (notificationText != null)
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Culture) civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Culture)
return true true
}
} }
UniqueType.OneTimeAmountFreePolicies -> { UniqueType.OneTimeAmountFreePolicies -> {
if (civInfo.isSpectator()) return false if (civInfo.isSpectator()) return null
val newFreePolicies = unique.params[0].toInt() val newFreePolicies = unique.params[0].toInt()
return {
civInfo.policies.freePolicies += newFreePolicies civInfo.policies.freePolicies += newFreePolicies
val notificationText = getNotificationText(notification, triggerNotificationText, val notificationText = getNotificationText(
"You may choose [$newFreePolicies] free Policies") notification, triggerNotificationText,
?: return true "You may choose [$newFreePolicies] free Policies"
)
if (notificationText != null)
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Culture) civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Culture)
return true true
}
} }
UniqueType.OneTimeAdoptPolicy -> { UniqueType.OneTimeAdoptPolicy -> {
val policyName = unique.params[0] val policyName = unique.params[0]
if (civInfo.policies.isAdopted(policyName)) return false if (civInfo.policies.isAdopted(policyName)) return null
val policy = civInfo.gameInfo.ruleset.policies[policyName] ?: return false val policy = civInfo.gameInfo.ruleset.policies[policyName] ?: return null
return {
civInfo.policies.freePolicies++ civInfo.policies.freePolicies++
civInfo.policies.adopt(policy) civInfo.policies.adopt(policy)
val notificationText = getNotificationText(notification, triggerNotificationText, val notificationText = getNotificationText(
"You gain the [$policyName] Policy") notification, triggerNotificationText,
?: return true "You gain the [$policyName] Policy"
)
if (notificationText != null)
civInfo.addNotification(notificationText, PolicyAction(policyName), NotificationCategory.General, NotificationIcon.Culture) civInfo.addNotification(notificationText, PolicyAction(policyName), NotificationCategory.General, NotificationIcon.Culture)
return true true
}
} }
UniqueType.OneTimeEnterGoldenAge, UniqueType.OneTimeEnterGoldenAgeTurns -> { UniqueType.OneTimeEnterGoldenAge, UniqueType.OneTimeEnterGoldenAgeTurns -> {
return {
if (unique.type == UniqueType.OneTimeEnterGoldenAgeTurns) civInfo.goldenAges.enterGoldenAge(unique.params[0].toInt()) if (unique.type == UniqueType.OneTimeEnterGoldenAgeTurns) civInfo.goldenAges.enterGoldenAge(unique.params[0].toInt())
else civInfo.goldenAges.enterGoldenAge() else civInfo.goldenAges.enterGoldenAge()
val notificationText = getNotificationText(notification, triggerNotificationText, val notificationText = getNotificationText(
"You enter a Golden Age") notification, triggerNotificationText,
?: return true "You enter a Golden Age"
)
if (notificationText != null)
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Happiness) civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Happiness)
return true true
}
} }
UniqueType.OneTimeFreeGreatPerson -> { UniqueType.OneTimeFreeGreatPerson -> {
if (civInfo.isSpectator()) return false if (civInfo.isSpectator()) return null
return {
civInfo.greatPeople.freeGreatPeople++ civInfo.greatPeople.freeGreatPeople++
// Anyone an idea for a good icon? // Anyone an idea for a good icon?
if (notification != null) if (notification != null)
@ -261,28 +303,32 @@ object UniqueTriggerActivation {
if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI()) { if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI()) {
NextTurnAutomation.chooseGreatPerson(civInfo) NextTurnAutomation.chooseGreatPerson(civInfo)
} }
true
return true }
} }
UniqueType.OneTimeGainPopulation -> { UniqueType.OneTimeGainPopulation -> {
val applicableCities = val applicableCities =
if (unique.params[1] == "in this city") sequenceOf(relevantCity!!) if (unique.params[1] == "in this city") sequenceOf(relevantCity!!)
else civInfo.cities.asSequence().filter { it.matchesFilter(unique.params[1]) } else civInfo.cities.asSequence().filter { it.matchesFilter(unique.params[1]) }
if (applicableCities.none()) return null
return {
for (applicableCity in applicableCities) { for (applicableCity in applicableCities) {
applicableCity.population.addPopulation(unique.params[0].toInt()) applicableCity.population.addPopulation(unique.params[0].toInt())
} }
if (notification != null && applicableCities.any()) if (notification != null)
civInfo.addNotification( civInfo.addNotification(
notification, notification,
LocationAction(applicableCities.map { it.location }), LocationAction(applicableCities.map { it.location }),
NotificationCategory.Cities, NotificationCategory.Cities,
NotificationIcon.Population NotificationIcon.Population
) )
return applicableCities.any() true
}
} }
UniqueType.OneTimeGainPopulationRandomCity -> { UniqueType.OneTimeGainPopulationRandomCity -> {
if (civInfo.cities.isEmpty()) return false if (civInfo.cities.isEmpty()) return null
return {
val randomCity = civInfo.cities.random(tileBasedRandom) val randomCity = civInfo.cities.random(tileBasedRandom)
randomCity.population.addPopulation(unique.params[0].toInt()) randomCity.population.addPopulation(unique.params[0].toInt())
if (notification != null) { if (notification != null) {
@ -297,24 +343,27 @@ object UniqueTriggerActivation {
NotificationIcon.Population NotificationIcon.Population
) )
} }
return true true
}
} }
UniqueType.OneTimeFreeTech -> { UniqueType.OneTimeFreeTech -> {
if (civInfo.isSpectator()) return false if (civInfo.isSpectator()) return null
return {
civInfo.tech.freeTechs += 1 civInfo.tech.freeTechs += 1
if (notification != null) { if (notification != null)
civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Science) civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Science)
true
} }
return true
} }
UniqueType.OneTimeAmountFreeTechs -> { UniqueType.OneTimeAmountFreeTechs -> {
if (civInfo.isSpectator()) return false if (civInfo.isSpectator()) return null
return {
civInfo.tech.freeTechs += unique.params[0].toInt() civInfo.tech.freeTechs += unique.params[0].toInt()
if (notification != null) { if (notification != null)
civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Science) civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Science)
true
} }
return true
} }
UniqueType.OneTimeFreeTechRuins -> { UniqueType.OneTimeFreeTechRuins -> {
val researchableTechsFromThatEra = ruleSet.technologies.values val researchableTechsFromThatEra = ruleSet.technologies.values
@ -322,8 +371,9 @@ object UniqueTriggerActivation {
(it.column!!.era == unique.params[1] || unique.params[1] == "any era") (it.column!!.era == unique.params[1] || unique.params[1] == "any era")
&& civInfo.tech.canBeResearched(it.name) && civInfo.tech.canBeResearched(it.name)
} }
if (researchableTechsFromThatEra.isEmpty()) return false if (researchableTechsFromThatEra.isEmpty()) return null
return {
val techsToResearch = researchableTechsFromThatEra.shuffled(tileBasedRandom) val techsToResearch = researchableTechsFromThatEra.shuffled(tileBasedRandom)
.take(unique.params[0].toInt()) .take(unique.params[0].toInt())
for (tech in techsToResearch) for (tech in techsToResearch)
@ -339,93 +389,110 @@ object UniqueTriggerActivation {
// Relies on RulesetValidator catching <= 0! // Relies on RulesetValidator catching <= 0!
val notificationActions: Sequence<NotificationAction> = val notificationActions: Sequence<NotificationAction> =
LocationAction(tile?.position) + TechAction(techsToResearch.first().name) LocationAction(tile?.position) + TechAction(techsToResearch.first().name)
civInfo.addNotification(notificationText, notificationActions, civInfo.addNotification(
NotificationCategory.General, NotificationIcon.Science) notificationText, notificationActions,
NotificationCategory.General, NotificationIcon.Science
)
}
true
} }
return true
} }
UniqueType.OneTimeDiscoverTech -> { UniqueType.OneTimeDiscoverTech -> {
val techName = unique.params[0] val techName = unique.params[0]
if (civInfo.tech.isResearched(techName)) return false if (civInfo.tech.isResearched(techName)) return null
return {
civInfo.tech.addTechnology(techName) civInfo.tech.addTechnology(techName)
val notificationText = getNotificationText(
val notificationText = getNotificationText(notification, triggerNotificationText, notification, triggerNotificationText,
"You have discovered the secrets of [$techName]") "You have discovered the secrets of [$techName]"
?: return true )
if (notificationText != null)
civInfo.addNotification(notificationText, TechAction(techName), NotificationCategory.General, NotificationIcon.Science) civInfo.addNotification(notificationText, TechAction(techName), NotificationCategory.General, NotificationIcon.Science)
return true true
}
} }
UniqueType.StrategicResourcesIncrease -> { UniqueType.StrategicResourcesIncrease -> {
return {
civInfo.cache.updateCivResources() civInfo.cache.updateCivResources()
if (notification != null) { if (notification != null)
civInfo.addNotification( civInfo.addNotification(
notification, notification,
NotificationCategory.General, NotificationCategory.General,
NotificationIcon.Construction NotificationIcon.Construction
) )
true
} }
return true
} }
UniqueType.OneTimeProvideResources -> { UniqueType.OneTimeProvideResources -> {
val resourceName = unique.params[1] val resourceName = unique.params[1]
val resource = ruleSet.tileResources[resourceName] ?: return false val resource = ruleSet.tileResources[resourceName] ?: return null
if (!resource.isStockpiled()) return false if (!resource.isStockpiled()) return null
return {
val amount = unique.params[0].toInt() val amount = unique.params[0].toInt()
civInfo.resourceStockpiles.add(resourceName, amount) civInfo.resourceStockpiles.add(resourceName, amount)
val notificationText = getNotificationText(notification, triggerNotificationText, val notificationText = getNotificationText(
"You have gained [$amount] [$resourceName]") notification, triggerNotificationText,
?: return true "You have gained [$amount] [$resourceName]"
)
if (notificationText != null)
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Science, "ResourceIcons/$resourceName") civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Science, "ResourceIcons/$resourceName")
return true true
}
} }
UniqueType.OneTimeConsumeResources -> { UniqueType.OneTimeConsumeResources -> {
val resourceName = unique.params[1] val resourceName = unique.params[1]
val resource = ruleSet.tileResources[resourceName] ?: return false val resource = ruleSet.tileResources[resourceName] ?: return null
if (!resource.isStockpiled()) return false if (!resource.isStockpiled()) return null
return {
val amount = unique.params[0].toInt() val amount = unique.params[0].toInt()
civInfo.resourceStockpiles.add(resourceName, -amount) civInfo.resourceStockpiles.add(resourceName, -amount)
val notificationText = getNotificationText(notification, triggerNotificationText, val notificationText = getNotificationText(
"You have lost [$amount] [$resourceName]") notification, triggerNotificationText,
?: return true "You have lost [$amount] [$resourceName]"
)
if (notificationText != null)
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Science, "ResourceIcons/$resourceName") civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Science, "ResourceIcons/$resourceName")
return true true
}
} }
UniqueType.OneTimeRevealEntireMap -> { UniqueType.OneTimeRevealEntireMap -> {
return {
if (notification != null) { if (notification != null) {
civInfo.addNotification(notification, LocationAction(tile?.position), NotificationCategory.General, NotificationIcon.Scout) civInfo.addNotification(notification, LocationAction(tile?.position), NotificationCategory.General, NotificationIcon.Scout)
} }
civInfo.gameInfo.tileMap.values.asSequence() civInfo.gameInfo.tileMap.values.asSequence()
.forEach { it.setExplored(civInfo, true) } .forEach { it.setExplored(civInfo, true) }
return true true
}
} }
UniqueType.UnitsGainPromotion -> { UniqueType.UnitsGainPromotion -> {
val filter = unique.params[0] val filter = unique.params[0]
val promotion = unique.params[1] val promotion = unique.params[1]
val promotedUnitLocations: MutableList<Vector2> = mutableListOf() val unitsToPromote = civInfo.units.getCivUnits().filter { it.matchesFilter(filter) }
for (civUnit in civInfo.units.getCivUnits()) { .filter { unitToPromote ->
if (civUnit.matchesFilter(filter) ruleSet.unitPromotions.values.any {
&& ruleSet.unitPromotions.values.any { it.name == promotion && unitToPromote.type.name in it.unitTypes
it.name == promotion && civUnit.type.name in it.unitTypes
} }
) { }.toList()
if (unitsToPromote.isEmpty()) return null
return {
val promotedUnitLocations: MutableList<Vector2> = mutableListOf()
for (civUnit in unitsToPromote) {
civUnit.promotions.addPromotion(promotion, isFree = true) civUnit.promotions.addPromotion(promotion, isFree = true)
promotedUnitLocations.add(civUnit.getTile().position) promotedUnitLocations.add(civUnit.getTile().position)
} }
}
if (notification != null) { if (notification != null) {
civInfo.addNotification( civInfo.addNotification(
@ -435,7 +502,8 @@ object UniqueTriggerActivation {
"unitPromotionIcons/${unique.params[1]}" "unitPromotionIcons/${unique.params[1]}"
) )
} }
return promotedUnitLocations.isNotEmpty() true
}
} }
/** /**
@ -454,93 +522,106 @@ object UniqueTriggerActivation {
* I could parametrize the 'Allied' of the Unique text, but eh. * I could parametrize the 'Allied' of the Unique text, but eh.
*/ */
UniqueType.CityStateCanGiftGreatPeople -> { UniqueType.CityStateCanGiftGreatPeople -> {
return {
civInfo.addFlag( civInfo.addFlag(
CivFlags.CityStateGreatPersonGift.name, CivFlags.CityStateGreatPersonGift.name,
civInfo.cityStateFunctions.turnsForGreatPersonFromCityState() / 2 civInfo.cityStateFunctions.turnsForGreatPersonFromCityState() / 2
) )
if (notification != null) { if (notification != null)
civInfo.addNotification(notification, NotificationCategory.Diplomacy, NotificationIcon.CityState) civInfo.addNotification(notification, NotificationCategory.Diplomacy, NotificationIcon.CityState)
true
} }
return true
} }
UniqueType.OneTimeGainStat -> { UniqueType.OneTimeGainStat -> {
val stat = Stat.safeValueOf(unique.params[1]) ?: return false val stat = Stat.safeValueOf(unique.params[1]) ?: return null
if (stat !in Stat.statsWithCivWideField if (stat !in Stat.statsWithCivWideField
|| unique.params[0].toIntOrNull() == null || unique.params[0].toIntOrNull() == null
) return false ) return null
return {
val statAmount = unique.params[0].toInt() val statAmount = unique.params[0].toInt()
val stats = Stats().add(stat, statAmount.toFloat()) val stats = Stats().add(stat, statAmount.toFloat())
civInfo.addStats(stats) civInfo.addStats(stats)
val filledNotification = if(notification!=null && notification.hasPlaceholderParameters()) val filledNotification = if (notification != null && notification.hasPlaceholderParameters())
notification.fillPlaceholders(statAmount.toString()) notification.fillPlaceholders(statAmount.toString())
else notification else notification
val notificationText = getNotificationText(filledNotification, triggerNotificationText, val notificationText = getNotificationText(
"Gained [${stats.toStringForNotifications()}]") filledNotification, triggerNotificationText,
?: return true "Gained [${stats.toStringForNotifications()}]"
)
if (notificationText != null)
civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.General, stat.notificationIcon) civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.General, stat.notificationIcon)
return true true
}
} }
UniqueType.OneTimeGainStatSpeed -> { UniqueType.OneTimeGainStatSpeed -> {
val stat = Stat.safeValueOf(unique.params[1]) ?: return false val stat = Stat.safeValueOf(unique.params[1]) ?: return null
if (stat !in Stat.statsWithCivWideField if (stat !in Stat.statsWithCivWideField
|| unique.params[0].toIntOrNull() == null || unique.params[0].toIntOrNull() == null
) return false ) return null
return {
val statAmount = (unique.params[0].toInt() * (civInfo.gameInfo.speed.statCostModifiers[stat]!!)).roundToInt() val statAmount = (unique.params[0].toInt() * (civInfo.gameInfo.speed.statCostModifiers[stat]!!)).roundToInt()
val stats = Stats().add(stat, statAmount.toFloat()) val stats = Stats().add(stat, statAmount.toFloat())
civInfo.addStats(stats) civInfo.addStats(stats)
val filledNotification = if(notification!=null && notification.hasPlaceholderParameters()) val filledNotification = if (notification != null && notification.hasPlaceholderParameters())
notification.fillPlaceholders(statAmount.toString()) notification.fillPlaceholders(statAmount.toString())
else notification else notification
val notificationText = getNotificationText(filledNotification, triggerNotificationText, val notificationText = getNotificationText(
"Gained [${stats.toStringForNotifications()}]") filledNotification, triggerNotificationText,
?: return true "Gained [${stats.toStringForNotifications()}]"
)
if (notificationText != null)
civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.General, stat.notificationIcon) civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.General, stat.notificationIcon)
return true true
}
} }
UniqueType.OneTimeGainStatRange -> { UniqueType.OneTimeGainStatRange -> {
val stat = Stat.safeValueOf(unique.params[2]) ?: return false val stat = Stat.safeValueOf(unique.params[2]) ?: return null
if (stat !in Stat.statsWithCivWideField if (stat !in Stat.statsWithCivWideField
|| unique.params[0].toIntOrNull() == null || unique.params[0].toIntOrNull() == null
|| unique.params[1].toIntOrNull() == null || unique.params[1].toIntOrNull() == null
) return false ) return null
val finalStatAmount = (tileBasedRandom.nextInt(unique.params[0].toInt(), unique.params[1].toInt()) * val finalStatAmount = (tileBasedRandom.nextInt(unique.params[0].toInt(), unique.params[1].toInt()) *
civInfo.gameInfo.speed.statCostModifiers[stat]!!).roundToInt() civInfo.gameInfo.speed.statCostModifiers[stat]!!).roundToInt()
return {
val stats = Stats().add(stat, finalStatAmount.toFloat()) val stats = Stats().add(stat, finalStatAmount.toFloat())
civInfo.addStats(stats) civInfo.addStats(stats)
val filledNotification = if (notification!=null && notification.hasPlaceholderParameters()) val filledNotification = if (notification != null && notification.hasPlaceholderParameters())
notification.fillPlaceholders(finalStatAmount.toString()) notification.fillPlaceholders(finalStatAmount.toString())
else notification else notification
val notificationText = getNotificationText(filledNotification, triggerNotificationText, val notificationText = getNotificationText(
"Gained [${stats.toStringForNotifications()}]") filledNotification, triggerNotificationText,
?: return true "Gained [${stats.toStringForNotifications()}]"
)
if (notificationText != null)
civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.General, stat.notificationIcon) civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.General, stat.notificationIcon)
true
return true }
} }
UniqueType.OneTimeGainPantheon -> { UniqueType.OneTimeGainPantheon -> {
if (civInfo.religionManager.religionState != ReligionState.None) return false if (civInfo.religionManager.religionState != ReligionState.None) return null
val gainedFaith = civInfo.religionManager.faithForPantheon(2) val gainedFaith = civInfo.religionManager.faithForPantheon(2)
if (gainedFaith == 0) return false if (gainedFaith == 0) return null
return {
civInfo.addStat(Stat.Faith, gainedFaith) civInfo.addStat(Stat.Faith, gainedFaith)
if (notification != null) { if (notification != null) {
@ -550,15 +631,16 @@ object UniqueTriggerActivation {
else notification else notification
civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.Religion, NotificationIcon.Faith) civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.Religion, NotificationIcon.Faith)
} }
true
return true }
} }
UniqueType.OneTimeGainProphet -> { UniqueType.OneTimeGainProphet -> {
if (civInfo.religionManager.getGreatProphetEquivalent() == null) return false if (civInfo.religionManager.getGreatProphetEquivalent() == null) return null
val gainedFaith = val gainedFaith =
(civInfo.religionManager.faithForNextGreatProphet() * (unique.params[0].toFloat() / 100f)).toInt() (civInfo.religionManager.faithForNextGreatProphet() * (unique.params[0].toFloat() / 100f)).toInt()
if (gainedFaith == 0) return false if (gainedFaith == 0) return null
return {
civInfo.addStat(Stat.Faith, gainedFaith) civInfo.addStat(Stat.Faith, gainedFaith)
if (notification != null) { if (notification != null) {
@ -568,30 +650,30 @@ object UniqueTriggerActivation {
else notification else notification
civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.Religion, NotificationIcon.Faith) civInfo.addNotification(notificationText, LocationAction(tile?.position), NotificationCategory.Religion, NotificationIcon.Faith)
} }
true
return true }
} }
UniqueType.OneTimeFreeBelief -> { UniqueType.OneTimeFreeBelief -> {
if (!civInfo.isMajorCiv()) return false if (!civInfo.isMajorCiv()) return null
val beliefType = BeliefType.valueOf(unique.params[0]) val beliefType = BeliefType.valueOf(unique.params[0])
val religionManager = civInfo.religionManager val religionManager = civInfo.religionManager
if ((beliefType != BeliefType.Pantheon && beliefType != BeliefType.Any) if ((beliefType != BeliefType.Pantheon && beliefType != BeliefType.Any)
&& religionManager.religionState <= ReligionState.Pantheon) && religionManager.religionState <= ReligionState.Pantheon)
return false // situation where we're trying to add a formal religion belief to a civ that hasn't founded a religion return null // situation where we're trying to add a formal religion belief to a civ that hasn't founded a religion
if (religionManager.numberOfBeliefsAvailable(beliefType) == 0) if (religionManager.numberOfBeliefsAvailable(beliefType) == 0)
return false // no more available beliefs of this type return null // no more available beliefs of this type
return {
if (beliefType == BeliefType.Any && religionManager.religionState <= ReligionState.Pantheon) if (beliefType == BeliefType.Any && religionManager.religionState <= ReligionState.Pantheon)
religionManager.freeBeliefs.add(BeliefType.Pantheon.name, 1) // add pantheon instead of any type religionManager.freeBeliefs.add(BeliefType.Pantheon.name, 1) // add pantheon instead of any type
else else
religionManager.freeBeliefs.add(beliefType.name, 1) religionManager.freeBeliefs.add(beliefType.name, 1)
return true true
}
} }
UniqueType.OneTimeRevealSpecificMapTiles -> { UniqueType.OneTimeRevealSpecificMapTiles -> {
if (tile == null) return null
if (tile == null)
return false
// "Reveal up to [amount/'all'] [tileFilter] within a [amount] tile radius" // "Reveal up to [amount/'all'] [tileFilter] within a [amount] tile radius"
val amount = unique.params[0] val amount = unique.params[0]
@ -605,13 +687,14 @@ object UniqueTriggerActivation {
.filter { !it.isExplored(civInfo) && it.matchesFilter(filter) } .filter { !it.isExplored(civInfo) && it.matchesFilter(filter) }
if (explorableTiles.none()) if (explorableTiles.none())
return false return null
if (!isAll) { if (!isAll) {
explorableTiles.shuffled(tileBasedRandom) explorableTiles.shuffled(tileBasedRandom)
explorableTiles = explorableTiles.take(amount.toInt()) explorableTiles = explorableTiles.take(amount.toInt())
} }
return {
for (explorableTile in explorableTiles) { for (explorableTile in explorableTiles) {
explorableTile.setExplored(civInfo, true) explorableTile.setExplored(civInfo, true)
positions += explorableTile.position positions += explorableTile.position
@ -630,16 +713,14 @@ object UniqueTriggerActivation {
NotificationIcon.Barbarians else NotificationIcon.Scout NotificationIcon.Barbarians else NotificationIcon.Scout
) )
} }
true
return true }
} }
UniqueType.OneTimeRevealCrudeMap -> { UniqueType.OneTimeRevealCrudeMap -> {
if (tile == null) if (tile == null) return null
return false
// "From a randomly chosen tile [amount] tiles away from the ruins, // "From a randomly chosen tile [amount] tiles away from the ruins,
// reveal tiles up to [amount] tiles away with [amount]% chance" // reveal tiles up to [amount] tiles away with [amount]% chance"
val distance = unique.params[0].toInt() val distance = unique.params[0].toInt()
val radius = unique.params[1].toInt() val radius = unique.params[1].toInt()
val chance = unique.params[2].toFloat() / 100f val chance = unique.params[2].toFloat() / 100f
@ -648,7 +729,9 @@ object UniqueTriggerActivation {
.filter { !it.isExplored(civInfo) } .filter { !it.isExplored(civInfo) }
.toList() .toList()
.randomOrNull(tileBasedRandom) .randomOrNull(tileBasedRandom)
?: return false ?: return null
return {
revealCenter.getTilesInDistance(radius) revealCenter.getTilesInDistance(radius)
.filter { tileBasedRandom.nextFloat() < chance } .filter { tileBasedRandom.nextFloat() < chance }
.forEach { it.setExplored(civInfo, true) } .forEach { it.setExplored(civInfo, true) }
@ -660,10 +743,12 @@ object UniqueTriggerActivation {
NotificationCategory.General, NotificationCategory.General,
NotificationIcon.Ruins NotificationIcon.Ruins
) )
return true true
}
} }
UniqueType.OneTimeTriggerVoting -> { UniqueType.OneTimeTriggerVoting -> {
return {
for (civ in civInfo.gameInfo.civilizations) for (civ in civInfo.gameInfo.civilizations)
if (!civ.isBarbarian() && !civ.isSpectator()) if (!civ.isBarbarian() && !civ.isSpectator())
civ.addFlag( civ.addFlag(
@ -672,12 +757,15 @@ object UniqueTriggerActivation {
) )
if (notification != null) if (notification != null)
civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Diplomacy) civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Diplomacy)
return true true
}
} }
UniqueType.OneTimeGlobalSpiesWhenEnteringEra -> { UniqueType.OneTimeGlobalSpiesWhenEnteringEra -> {
if (!civInfo.isMajorCiv()) return false if (!civInfo.isMajorCiv()) return null
if (!civInfo.gameInfo.isEspionageEnabled()) return false if (!civInfo.gameInfo.isEspionageEnabled()) return null
return {
val currentEra = civInfo.getEra().name val currentEra = civInfo.getEra().name
for (otherCiv in civInfo.gameInfo.getAliveMajorCivs()) { for (otherCiv in civInfo.gameInfo.getAliveMajorCivs()) {
if (currentEra !in otherCiv.espionageManager.erasSpyEarnedFor) { if (currentEra !in otherCiv.espionageManager.erasSpyEarnedFor) {
@ -687,10 +775,15 @@ object UniqueTriggerActivation {
// We don't tell which civilization entered the new era, as that is done in the notification directly above this one // We don't tell which civilization entered the new era, as that is done in the notification directly above this one
otherCiv.addNotification("We have recruited [${spyName}] as a spy!", NotificationCategory.Espionage, NotificationIcon.Spy) otherCiv.addNotification("We have recruited [${spyName}] as a spy!", NotificationCategory.Espionage, NotificationIcon.Spy)
else else
otherCiv.addNotification("After an unknown civilization entered the [${currentEra}], we have recruited [${spyName}] as a spy!", NotificationCategory.Espionage, NotificationIcon.Spy) otherCiv.addNotification(
"After an unknown civilization entered the [${currentEra}], we have recruited [${spyName}] as a spy!",
NotificationCategory.Espionage,
NotificationIcon.Spy
)
} }
} }
return true true
}
} }
UniqueType.GainFreeBuildings -> { UniqueType.GainFreeBuildings -> {
@ -698,119 +791,144 @@ object UniqueTriggerActivation {
val applicableCities = val applicableCities =
if (unique.params[1] == "in this city") sequenceOf(relevantCity!!) if (unique.params[1] == "in this city") sequenceOf(relevantCity!!)
else civInfo.cities.asSequence().filter { it.matchesFilter(unique.params[1]) } else civInfo.cities.asSequence().filter { it.matchesFilter(unique.params[1]) }
if (applicableCities.none()) return null
return {
for (applicableCity in applicableCities) { for (applicableCity in applicableCities) {
applicableCity.cityConstructions.freeBuildingsProvidedFromThisCity.addToMapOfSets(applicableCity.id, freeBuilding.name) applicableCity.cityConstructions.freeBuildingsProvidedFromThisCity.addToMapOfSets(applicableCity.id, freeBuilding.name)
if (applicableCity.cityConstructions.containsBuildingOrEquivalent(freeBuilding.name)) continue if (applicableCity.cityConstructions.containsBuildingOrEquivalent(freeBuilding.name)) continue
applicableCity.cityConstructions.constructionComplete(freeBuilding) applicableCity.cityConstructions.constructionComplete(freeBuilding)
} }
return true true
}
} }
UniqueType.FreeStatBuildings -> { UniqueType.FreeStatBuildings -> {
val stat = Stat.safeValueOf(unique.params[0]) ?: return false val stat = Stat.safeValueOf(unique.params[0]) ?: return null
return {
civInfo.civConstructions.addFreeStatBuildings(stat, unique.params[1].toInt()) civInfo.civConstructions.addFreeStatBuildings(stat, unique.params[1].toInt())
return true true
}
} }
UniqueType.FreeSpecificBuildings ->{ UniqueType.FreeSpecificBuildings ->{
val building = ruleSet.buildings[unique.params[0]] ?: return false val building = ruleSet.buildings[unique.params[0]] ?: return null
return {
civInfo.civConstructions.addFreeBuildings(building, unique.params[1].toInt()) civInfo.civConstructions.addFreeBuildings(building, unique.params[1].toInt())
return true true
}
} }
UniqueType.RemoveBuilding -> { UniqueType.RemoveBuilding -> {
val applicableCities = val applicableCities =
if (unique.params[1] == "in this city") sequenceOf(relevantCity!!) if (unique.params[1] == "in this city") sequenceOf(relevantCity!!)
else civInfo.cities.asSequence().filter { it.matchesFilter(unique.params[1]) } else civInfo.cities.asSequence().filter { it.matchesFilter(unique.params[1]) }
if (applicableCities.none()) return null
return {
for (applicableCity in applicableCities) { for (applicableCity in applicableCities) {
val buildingsToRemove = applicableCity.cityConstructions.getBuiltBuildings().filter { val buildingsToRemove = applicableCity.cityConstructions.getBuiltBuildings().filter {
it.matchesFilter(unique.params[0]) it.matchesFilter(unique.params[0])
}.toSet() }.toSet()
applicableCity.cityConstructions.removeBuildings(buildingsToRemove) applicableCity.cityConstructions.removeBuildings(buildingsToRemove)
} }
true
return true }
} }
UniqueType.SellBuilding -> { UniqueType.SellBuilding -> {
val applicableCities = val applicableCities =
if (unique.params[1] == "in this city") sequenceOf(relevantCity!!) if (unique.params[1] == "in this city") sequenceOf(relevantCity!!)
else civInfo.cities.asSequence().filter { it.matchesFilter(unique.params[1]) } else civInfo.cities.asSequence().filter { it.matchesFilter(unique.params[1]) }
if (applicableCities.none()) return null
return {
for (applicableCity in applicableCities) { for (applicableCity in applicableCities) {
val buildingsToSell = applicableCity.cityConstructions.getBuiltBuildings().filter { val buildingsToSell = applicableCity.cityConstructions.getBuiltBuildings().filter {
it.matchesFilter(unique.params[0]) && it.isSellable() it.matchesFilter(unique.params[0]) && it.isSellable()
} }
for (building in buildingsToSell) { for (building in buildingsToSell) applicableCity.sellBuilding(building)
applicableCity.sellBuilding(building)
} }
true
} }
return true
} }
UniqueType.OneTimeUnitHeal -> { UniqueType.OneTimeUnitHeal -> {
if (unit == null) return false if (unit == null) return null
if (unit.health == 100)
return {
unit.healBy(unique.params[0].toInt()) unit.healBy(unique.params[0].toInt())
if (notification != null) if (notification != null)
unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units) // Do we have a heal icon? unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units) // Do we have a heal icon?
return true true
}
} }
UniqueType.OneTimeUnitDamage -> { UniqueType.OneTimeUnitDamage -> {
if (unit == null) return false if (unit == null) return null
return {
MapUnitCombatant(unit).takeDamage(unique.params[0].toInt()) MapUnitCombatant(unit).takeDamage(unique.params[0].toInt())
if (notification != null) if (notification != null)
unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units) // Do we have a heal icon? unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units) // Do we have a heal icon?
return true true
}
} }
UniqueType.OneTimeUnitGainXP -> { UniqueType.OneTimeUnitGainXP -> {
if (unit == null) return false if (unit == null) return null
if (!unit.baseUnit.isMilitary()) return false if (!unit.baseUnit.isMilitary()) return null
return {
unit.promotions.XP += unique.params[0].toInt() unit.promotions.XP += unique.params[0].toInt()
if (notification != null) if (notification != null)
unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units) unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units)
return true true
}
} }
UniqueType.OneTimeUnitUpgrade -> { UniqueType.OneTimeUnitUpgrade -> {
if (unit == null) return false if (unit == null) return null
val upgradeAction = UnitActionsUpgrade.getFreeUpgradeAction(unit) val upgradeAction = UnitActionsUpgrade.getFreeUpgradeAction(unit)
if (upgradeAction.none()) return false if (upgradeAction.none()) return null
return {
(upgradeAction.minBy { (it as UpgradeUnitAction).unitToUpgradeTo.cost }).action!!() (upgradeAction.minBy { (it as UpgradeUnitAction).unitToUpgradeTo.cost }).action!!()
if (notification != null) if (notification != null)
unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units) unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units)
return true true
}
} }
UniqueType.OneTimeUnitSpecialUpgrade -> { UniqueType.OneTimeUnitSpecialUpgrade -> {
if (unit == null) return false if (unit == null) return null
val upgradeAction = UnitActionsUpgrade.getAncientRuinsUpgradeAction(unit) val upgradeAction = UnitActionsUpgrade.getAncientRuinsUpgradeAction(unit)
if (upgradeAction.none()) return false if (upgradeAction.none()) return null
return {
(upgradeAction.minBy { (it as UpgradeUnitAction).unitToUpgradeTo.cost }).action!!() (upgradeAction.minBy { (it as UpgradeUnitAction).unitToUpgradeTo.cost }).action!!()
if (notification != null) if (notification != null)
unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units) unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units)
return true true
}
} }
UniqueType.OneTimeUnitGainPromotion -> { UniqueType.OneTimeUnitGainPromotion -> {
if (unit == null) return false if (unit == null) return null
val promotion = unit.civ.gameInfo.ruleset.unitPromotions.keys val promotion = unit.civ.gameInfo.ruleset.unitPromotions.keys
.firstOrNull { it == unique.params[0] } .firstOrNull { it == unique.params[0] }
?: return false ?: return null
return {
unit.promotions.addPromotion(promotion, true) unit.promotions.addPromotion(promotion, true)
if (notification != null) if (notification != null)
unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units, unit.name) unit.civ.addNotification(notification, unit.getTile().position, NotificationCategory.Units, unit.name)
return true true
}
} }
UniqueType.OneTimeUnitRemovePromotion -> { UniqueType.OneTimeUnitRemovePromotion -> {
if (unit == null) return false if (unit == null) return null
val promotion = unit.civ.gameInfo.ruleset.unitPromotions.keys val promotion = unit.civ.gameInfo.ruleset.unitPromotions.keys
.firstOrNull { it == unique.params[0]} .firstOrNull { it == unique.params[0]}
?: return false ?: return null
return {
unit.promotions.removePromotion(promotion) unit.promotions.removePromotion(promotion)
return true true
}
} }
else -> {} else -> {}
} }
return false return null
} }
private fun getNotificationText(notification: String?, triggerNotificationText: String?, effectNotificationText: String): String? { private fun getNotificationText(notification: String?, triggerNotificationText: String?, effectNotificationText: String): String? {