Notifications for triggered uniques

This commit is contained in:
Yair Morgenstern 2023-01-22 12:19:34 +02:00
parent 7382da5bdf
commit 587b1f40f1
7 changed files with 146 additions and 67 deletions

View File

@ -907,6 +907,21 @@ A Great Person joins you! =
An unknown civilization has liberated [civ] =
## Trigger effects
Gained [amount] [unitName] unit(s) =
You may choose a free Policy =
You may choose [amount] free Policies =
You enter a Golden Age =
## Trigger causes
due to researching [tech] =
due to adopting [policy] =
due to discovering [naturalWonder] =
due to entering the [eraName] =
due to constructing [buildingName] =
# World Screen UI
Working... =

View File

@ -201,13 +201,14 @@ class PolicyManager : IsPartOfGameInfoSerialization {
triggerGlobalAlerts(policy, unique.params[0])
}
val triggerNotificationText = "due to adopting [${policy.name}]"
for (unique in policy.uniqueObjects)
if (unique.conditionals.none { it.type!!.targetTypes.contains(UniqueTarget.TriggerCondition) })
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo, triggerNotificationText = triggerNotificationText)
for (unique in civInfo.getTriggeredUniques(UniqueType.TriggerUponAdoptingPolicy))
if (unique.conditionals.any {it.type == UniqueType.TriggerUponAdoptingPolicy && it.params[0] == policy.name})
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo, triggerNotificationText = triggerNotificationText)
// This ALSO has the side-effect of updating the CivInfo statForNextTurn so we don't need to call it explicitly
for (cityInfo in civInfo.cities) cityInfo.cityStats.update()

View File

@ -258,13 +258,15 @@ class TechManager : IsPartOfGameInfoSerialization {
repeatingTechsResearched++
researchedTechnologies = researchedTechnologies.withItem(newTech)
addTechToTransients(newTech)
val triggerNotificationText = "due to researching [$techName]"
for (unique in newTech.uniqueObjects)
if (unique.conditionals.none { it.type!!.targetTypes.contains(UniqueTarget.TriggerCondition) })
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo, triggerNotificationText = triggerNotificationText)
for (unique in civInfo.getTriggeredUniques(UniqueType.TriggerUponResearch))
if (unique.conditionals.any {it.type == UniqueType.TriggerUponResearch && it.params[0] == techName})
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo, triggerNotificationText = triggerNotificationText)
updateTransientBooleans()
@ -284,11 +286,29 @@ class TechManager : IsPartOfGameInfoSerialization {
}
}
val obsoleteUnits = getRuleset().units.values.filter { it.obsoleteTech == techName }.map { it.name }
obsoleteOldUnits(techName)
for (unique in civInfo.getMatchingUniques(UniqueType.ReceiveFreeUnitWhenDiscoveringTech)) {
if (unique.params[1] != techName) continue
civInfo.units.addUnit(unique.params[0])
}
for (unique in civInfo.getMatchingUniques(UniqueType.MayanGainGreatPerson)) {
if (unique.params[1] != techName) continue
civInfo.addNotification("You have unlocked [The Long Count]!",
MayaLongCountAction(), NotificationCategory.General, MayaCalendar.notificationIcon)
}
moveToNewEra()
}
private fun obsoleteOldUnits(techName: String) {
val obsoleteUnits =
getRuleset().units.values.filter { it.obsoleteTech == techName }.map { it.name }
val unitUpgrades = HashMap<String, HashSet<City>>()
for (city in civInfo.cities) {
// Do not use replaceAll - that's a Java 8 feature and will fail on older phones!
val oldQueue = city.cityConstructions.constructionQueue.toList() // copy, since we're changing the queue
val oldQueue =
city.cityConstructions.constructionQueue.toList() // copy, since we're changing the queue
city.cityConstructions.constructionQueue.clear()
for (constructionName in oldQueue) {
if (constructionName in obsoleteUnits) {
@ -310,13 +330,18 @@ class TechManager : IsPartOfGameInfoSerialization {
if (cities.size == 1) {
val city = cities.first()
if (construction is BaseUnit && construction.upgradesTo != null) {
val text = "[${city.name}] changed production from [$unit] to [${construction.upgradesTo!!}]"
civInfo.addNotification(text, city.location,
val text =
"[${city.name}] changed production from [$unit] to [${construction.upgradesTo!!}]"
civInfo.addNotification(
text, city.location,
NotificationCategory.Production, unit,
NotificationIcon.Construction, construction.upgradesTo!!)
NotificationIcon.Construction, construction.upgradesTo!!
)
} else {
val text = "[$unit] has become obsolete and was removed from the queue in [${city.name}]!"
civInfo.addNotification(text, city.location,
val text =
"[$unit] has become obsolete and was removed from the queue in [${city.name}]!"
civInfo.addNotification(
text, city.location,
NotificationCategory.Production,
NotificationIcon.Construction
)
@ -324,47 +349,49 @@ class TechManager : IsPartOfGameInfoSerialization {
} else {
val locationAction = LocationAction(cities.asSequence().map { it.location })
if (construction is BaseUnit && construction.upgradesTo != null) {
val text = "[${cities.size}] cities changed production from [$unit] to [${construction.upgradesTo!!}]"
civInfo.addNotification(text, locationAction,
val text =
"[${cities.size}] cities changed production from [$unit] to [${construction.upgradesTo!!}]"
civInfo.addNotification(
text, locationAction,
NotificationCategory.Production, unit,
NotificationIcon.Construction, construction.upgradesTo!!)
NotificationIcon.Construction, construction.upgradesTo!!
)
} else {
val text = "[$unit] has become obsolete and was removed from the queue in [${cities.size}] cities!"
civInfo.addNotification(text, locationAction,
val text =
"[$unit] has become obsolete and was removed from the queue in [${cities.size}] cities!"
civInfo.addNotification(
text, locationAction,
NotificationCategory.Production,
NotificationIcon.Construction
)
}
}
}
}
for (unique in civInfo.getMatchingUniques(UniqueType.ReceiveFreeUnitWhenDiscoveringTech)) {
if (unique.params[1] != techName) continue
civInfo.units.addUnit(unique.params[0])
}
for (unique in civInfo.getMatchingUniques(UniqueType.MayanGainGreatPerson)) {
if (unique.params[1] != techName) continue
civInfo.addNotification("You have unlocked [The Long Count]!",
MayaLongCountAction(), NotificationCategory.General, MayaCalendar.notificationIcon)
}
private fun moveToNewEra() {
val previousEra = civInfo.getEra()
updateEra()
val currentEra = civInfo.getEra()
if (previousEra != currentEra) {
civInfo.addNotification("You have entered the [$currentEra]!",
civInfo.addNotification(
"You have entered the [$currentEra]!",
NotificationCategory.General,
NotificationIcon.Science
)
if (civInfo.isMajorCiv()) {
for (knownCiv in civInfo.getKnownCivs()) {
knownCiv.addNotification("[${civInfo.civName}] has entered the [$currentEra]!",
knownCiv.addNotification(
"[${civInfo.civName}] has entered the [$currentEra]!",
NotificationCategory.General, civInfo.civName, NotificationIcon.Science
)
}
}
for (policyBranch in getRuleset().policyBranches.values.filter { it.era == currentEra.name && civInfo.policies.isAdoptable(it) }) {
civInfo.addNotification("[${policyBranch.name}] policy branch unlocked!",
for (policyBranch in getRuleset().policyBranches.values.filter {
it.era == currentEra.name && civInfo.policies.isAdoptable(it)
}) {
civInfo.addNotification(
"[${policyBranch.name}] policy branch unlocked!",
NotificationCategory.General,
NotificationIcon.Culture
)
@ -373,15 +400,26 @@ class TechManager : IsPartOfGameInfoSerialization {
val erasPassed = getRuleset().eras.values
.filter { it.eraNumber > previousEra.eraNumber && it.eraNumber <= currentEra.eraNumber }
.sortedBy { it.eraNumber }
for (era in erasPassed)
for (unique in era.uniqueObjects)
if (unique.conditionals.none { it.type!!.targetTypes.contains(UniqueTarget.TriggerCondition) })
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
UniqueTriggerActivation.triggerCivwideUnique(
unique,
civInfo,
triggerNotificationText = "due to entering the [${era.name}]"
)
val eraNames = erasPassed.map { it.name }.toHashSet()
for (unique in civInfo.getTriggeredUniques(UniqueType.TriggerUponEnteringEra))
if (unique.conditionals.any {it.type == UniqueType.TriggerUponEnteringEra && it.params[0] in eraNames})
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
for (eraName in eraNames)
if (unique.conditionals.any { it.type == UniqueType.TriggerUponEnteringEra && it.params[0] == eraName })
UniqueTriggerActivation.triggerCivwideUnique(
unique,
civInfo,
triggerNotificationText = "due to entering the [$eraName]"
)
}
}

View File

@ -177,6 +177,7 @@ class CivInfoTransientCache(val civInfo: Civilization) {
goldGained += 500
}
if (civInfo.hasUnique(UniqueType.GoldWhenDiscoveringNaturalWonder)) {
goldGained += if (discoveredNaturalWonders.contains(tile.naturalWonder!!)) 100 else 500
}

View File

@ -74,10 +74,10 @@ class TileMap : IsPartOfGameInfoSerialization {
val maxLongitude: Float by lazy { if (values.isEmpty()) 0f else values.maxOf { abs(it.longitude) } }
@delegate:Transient
val naturalWonders: List<String> by lazy { tileList.asSequence().filter { it.isNaturalWonder() }.map { it.naturalWonder!! }.distinct().toList() }
val naturalWonders: Set<String> by lazy { tileList.asSequence().filter { it.isNaturalWonder() }.map { it.naturalWonder!! }.toSet() }
@delegate:Transient
val resources: List<String> by lazy { tileList.asSequence().filter { it.resource != null }.map { it.resource!! }.distinct().toList() }
val resources: Set<String> by lazy { tileList.asSequence().filter { it.resource != null }.map { it.resource!! }.toSet() }
// Excluded from Serialization by having no own backing field
val values: Collection<Tile>

View File

@ -1,7 +1,7 @@
package com.unciv.models.ruleset
import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.City
import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.INonPerpetualConstruction
import com.unciv.logic.city.RejectionReason
import com.unciv.logic.city.RejectionReasons
@ -666,19 +666,22 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
// "Provides a free [buildingName] [cityFilter]"
cityConstructions.addFreeBuildings()
val triggerNotificationText ="due to constructing [$name]"
for (unique in uniqueObjects)
if (unique.conditionals.none { it.type!!.targetTypes.contains(UniqueTarget.TriggerCondition) })
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo, cityConstructions.city)
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo, cityConstructions.city, triggerNotificationText = triggerNotificationText)
for (unique in civInfo.getTriggeredUniques(UniqueType.TriggerUponConstructingBuilding, StateForConditionals(civInfo, cityInfo)))
if (unique.conditionals.any {it.type == UniqueType.TriggerUponConstructingBuilding && matchesFilter(it.params[0])})
UniqueTriggerActivation.triggerCivwideUnique(unique, cityInfo.civInfo, cityInfo)
UniqueTriggerActivation.triggerCivwideUnique(unique, cityInfo.civInfo, cityInfo, triggerNotificationText = triggerNotificationText)
for (unique in civInfo.getTriggeredUniques(UniqueType.TriggerUponConstructingBuildingCityFilter, StateForConditionals(cityInfo.civInfo, cityInfo)))
if (unique.conditionals.any {it.type == UniqueType.TriggerUponConstructingBuildingCityFilter
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[1])})
UniqueTriggerActivation.triggerCivwideUnique(unique, cityInfo.civInfo, cityInfo)
UniqueTriggerActivation.triggerCivwideUnique(unique, cityInfo.civInfo, cityInfo, triggerNotificationText = triggerNotificationText)
if (hasUnique(UniqueType.EnemyLandUnitsSpendExtraMovement))
civInfo.cache.updateHasActiveEnemyMovementPenalty()

View File

@ -29,7 +29,8 @@ object UniqueTriggerActivation {
civInfo: Civilization,
city: City? = null,
tile: Tile? = null,
notification: String? = null
notification: String? = null,
triggerNotificationText: String? = null
): Boolean {
val timingConditional = unique.conditionals.firstOrNull { it.type == UniqueType.ConditionalTimedUnique }
if (timingConditional != null) {
@ -52,15 +53,18 @@ object UniqueTriggerActivation {
if (chosenCity == null || unit == null || (unit.hasUnique(UniqueType.FoundCity) && civInfo.isOneCityChallenger()))
return false
val placedUnit = civInfo.units.addUnit(unitName, chosenCity)
if (notification != null && placedUnit != null) {
civInfo.addNotification(
notification,
placedUnit.getTile().position,
NotificationCategory.Units,
placedUnit.name
)
}
val placedUnit = civInfo.units.addUnit(unitName, chosenCity) ?: return false
val notificationText = if (notification != null) notification
else if (triggerNotificationText != null) "{Gained [1] [$unitName] unit(s)}{ }{$triggerNotificationText}"
else return true
civInfo.addNotification(
notificationText,
placedUnit.getTile().position,
NotificationCategory.Units,
placedUnit.name
)
return true
}
UniqueType.OneTimeAmountFreeUnits -> {
@ -75,14 +79,18 @@ object UniqueTriggerActivation {
if (placedUnit != null)
tilesUnitsWerePlacedOn.add(placedUnit.getTile().position)
}
if (notification != null && tilesUnitsWerePlacedOn.isNotEmpty()) {
civInfo.addNotification(
notification,
LocationAction(tilesUnitsWerePlacedOn),
NotificationCategory.Units,
civInfo.getEquivalentUnit(unit).name
)
}
if (tilesUnitsWerePlacedOn.isEmpty()) return true
val notificationText = if (notification != null) notification
else if (triggerNotificationText!=null) "{Gained [${tilesUnitsWerePlacedOn.size}] [$unitName] unit(s)}{ }{$triggerNotificationText}"
else return true
civInfo.addNotification(
notificationText,
LocationAction(tilesUnitsWerePlacedOn),
NotificationCategory.Units,
civInfo.getEquivalentUnit(unit).name
)
return true
}
UniqueType.OneTimeFreeUnitRuins -> {
@ -117,24 +125,37 @@ object UniqueTriggerActivation {
// 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
civInfo.policies.freePolicies++
if (notification != null) {
civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Culture)
}
val notificationText = if (notification != null) notification
else if (triggerNotificationText != null) "{You may choose a free Policy}{ }{$triggerNotificationText}"
else return true
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Culture)
return true
}
UniqueType.OneTimeAmountFreePolicies -> {
if (civInfo.isSpectator()) return false
civInfo.policies.freePolicies += unique.params[0].toInt()
if (notification != null) {
civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Culture)
}
val newFreePolicies = unique.params[0].toInt()
civInfo.policies.freePolicies += newFreePolicies
val notificationText = if (notification != null) notification
else if (triggerNotificationText != null) "{You may choose [$newFreePolicies] free Policies}{ }{$triggerNotificationText}"
else return true
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Culture)
return true
}
UniqueType.OneTimeEnterGoldenAge -> {
civInfo.goldenAges.enterGoldenAge()
if (notification != null) {
civInfo.addNotification(notification, NotificationCategory.General, NotificationIcon.Happiness)
}
val notificationText = if (notification != null) notification
else if (triggerNotificationText != null) "{You enter a Golden Age}{ }{$triggerNotificationText}"
else return true
civInfo.addNotification(notificationText, NotificationCategory.General, NotificationIcon.Happiness)
return true
}