Changed notification to accept multiple icons, like in Civ VI - this allows for more informative iconography

This commit is contained in:
Yair Morgenstern 2021-03-19 15:11:47 +02:00
parent 50d5b0d7ce
commit 4c260ca954
12 changed files with 54 additions and 56 deletions

View File

@ -9,15 +9,11 @@ import com.unciv.logic.city.PerpetualConstruction
import com.unciv.logic.civilization.* import com.unciv.logic.civilization.*
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap import com.unciv.logic.map.TileMap
import com.unciv.logic.trade.TradeOffer
import com.unciv.logic.trade.TradeType
import com.unciv.models.metadata.GameParameters import com.unciv.models.metadata.GameParameters
import com.unciv.models.ruleset.Difficulty import com.unciv.models.ruleset.Difficulty
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.ruleset.Specialist
import java.util.* import java.util.*
import kotlin.collections.ArrayList
class UncivShowableException(missingMods: String) : Exception(missingMods) class UncivShowableException(missingMods: String) : Exception(missingMods)
@ -171,11 +167,11 @@ class GameInfo {
if (tiles.size < 3) { if (tiles.size < 3) {
for (tile in tiles) { for (tile in tiles) {
val unitName = tile.militaryUnit!!.name val unitName = tile.militaryUnit!!.name
thisPlayer.addNotification("An enemy [$unitName] was spotted $inOrNear our territory", tile.position, NotificationType.War) thisPlayer.addNotification("An enemy [$unitName] was spotted $inOrNear our territory", tile.position, NotificationIcon.War)
} }
} else { } else {
val positions = tiles.map { it.position } val positions = tiles.map { it.position }
thisPlayer.addNotification("[${positions.size}] enemy units were spotted $inOrNear our territory", NotificationType.War, LocationAction(positions)) thisPlayer.addNotification("[${positions.size}] enemy units were spotted $inOrNear our territory", LocationAction(positions), NotificationIcon.War)
} }
} }

View File

@ -1,12 +1,11 @@
package com.unciv.logic.automation package com.unciv.logic.automation
import com.badlogic.gdx.graphics.Color
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.PerpetualConstruction import com.unciv.logic.city.PerpetualConstruction
import com.unciv.logic.civilization.CityAction import com.unciv.logic.civilization.CityAction
import com.unciv.logic.civilization.NotificationType import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PlayerType import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.map.BFS import com.unciv.logic.map.BFS
import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Building
@ -87,7 +86,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
// Nobody can plan 30 turns ahead, I don't care how cost-efficient you are. // Nobody can plan 30 turns ahead, I don't care how cost-efficient you are.
else theChosenOne = relativeCostEffectiveness.minBy { it.remainingWork }!!.choice else theChosenOne = relativeCostEffectiveness.minBy { it.remainingWork }!!.choice
civInfo.addNotification("Work has started on [$theChosenOne]", NotificationType.Construction, CityAction(cityInfo.location)) civInfo.addNotification("Work has started on [$theChosenOne]", CityAction(cityInfo.location), NotificationIcon.Construction)
cityConstructions.currentConstructionFromQueue = theChosenOne cityConstructions.currentConstructionFromQueue = theChosenOne
} }

View File

@ -6,7 +6,7 @@ import com.unciv.UncivGame
import com.unciv.logic.city.CityInfo import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.AlertType import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.NotificationType import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PopupAlert import com.unciv.logic.civilization.PopupAlert
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.RoadStatus
@ -174,7 +174,7 @@ object Battle {
else " [" + defender.getName() + "]" else " [" + defender.getName() + "]"
else " our [" + defender.getName() + "]" else " our [" + defender.getName() + "]"
val notificationString = attackerString + whatHappenedString + defenderString val notificationString = attackerString + whatHappenedString + defenderString
defender.getCivInfo().addNotification(notificationString, attackedTile.position, NotificationType.War) defender.getCivInfo().addNotification(notificationString, attackedTile.position, NotificationIcon.War)
} }
} }
@ -289,7 +289,7 @@ object Battle {
private fun conquerCity(city: CityInfo, attacker: ICombatant) { private fun conquerCity(city: CityInfo, attacker: ICombatant) {
val attackerCiv = attacker.getCivInfo() val attackerCiv = attacker.getCivInfo()
attackerCiv.addNotification("We have conquered the city of [${city.name}]!", city.location, NotificationType.War) attackerCiv.addNotification("We have conquered the city of [${city.name}]!", city.location, NotificationIcon.War)
city.getCenterTile().apply { city.getCenterTile().apply {
if (militaryUnit != null) militaryUnit!!.destroy() if (militaryUnit != null) militaryUnit!!.destroy()
@ -335,7 +335,7 @@ object Battle {
val capturedUnit = defender.unit val capturedUnit = defender.unit
capturedUnit.civInfo.addNotification("An enemy [" + attacker.getName() + "] has captured our [" + defender.getName() + "]", capturedUnit.civInfo.addNotification("An enemy [" + attacker.getName() + "] has captured our [" + defender.getName() + "]",
defender.getTile().position, NotificationType.War) defender.getTile().position, NotificationIcon.War)
// Apparently in Civ V, captured settlers are converted to workers. // Apparently in Civ V, captured settlers are converted to workers.
if (capturedUnit.name == Constants.settler) { if (capturedUnit.name == Constants.settler) {
@ -433,17 +433,17 @@ object Battle {
if (attacker.isDefeated()) { if (attacker.isDefeated()) {
attacker.getCivInfo() attacker.getCivInfo()
.addNotification("Our [$attackerName] was destroyed by an intercepting [$interceptorName]", .addNotification("Our [$attackerName] was destroyed by an intercepting [$interceptorName]",
NotificationType.War) NotificationIcon.War)
defender.getCivInfo() defender.getCivInfo()
.addNotification("Our [$interceptorName] intercepted and destroyed an enemy [$attackerName]", .addNotification("Our [$interceptorName] intercepted and destroyed an enemy [$attackerName]",
interceptor.currentTile.position, NotificationType.War) interceptor.currentTile.position, NotificationIcon.War)
} else { } else {
attacker.getCivInfo() attacker.getCivInfo()
.addNotification("Our [$attackerName] was attacked by an intercepting [$interceptorName]", .addNotification("Our [$attackerName] was attacked by an intercepting [$interceptorName]",
NotificationType.War) NotificationIcon.War)
defender.getCivInfo() defender.getCivInfo()
.addNotification("Our [$interceptorName] intercepted and attacked an enemy [$attackerName]", .addNotification("Our [$interceptorName] intercepted and attacked an enemy [$attackerName]",
interceptor.currentTile.position, NotificationType.War) interceptor.currentTile.position, NotificationIcon.War)
} }
return return
} }
@ -498,8 +498,8 @@ object Battle {
// and count 1 attack for attacker but leave it in place // and count 1 attack for attacker but leave it in place
reduceAttackerMovementPointsAndAttacks(attacker, defender) reduceAttackerMovementPointsAndAttacks(attacker, defender)
val notificationString = "[" + defendBaseUnit.name + "] withdrew from a [" + attackBaseUnit.name + "]" val notificationString = "[" + defendBaseUnit.name + "] withdrew from a [" + attackBaseUnit.name + "]"
defender.getCivInfo().addNotification(notificationString, toTile.position, NotificationType.War) defender.getCivInfo().addNotification(notificationString, toTile.position, NotificationIcon.War)
attacker.getCivInfo().addNotification(notificationString, toTile.position, NotificationType.War) attacker.getCivInfo().addNotification(notificationString, toTile.position, NotificationIcon.War)
return true return true
} }

View File

@ -3,7 +3,7 @@ package com.unciv.logic.city
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.unciv.logic.automation.ConstructionAutomation import com.unciv.logic.automation.ConstructionAutomation
import com.unciv.logic.civilization.AlertType import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.NotificationType import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.civilization.PopupAlert import com.unciv.logic.civilization.PopupAlert
import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.UniqueMap import com.unciv.models.ruleset.UniqueMap
@ -310,10 +310,10 @@ class CityConstructions {
if (civ.exploredTiles.contains(cityInfo.location)) if (civ.exploredTiles.contains(cityInfo.location))
civ.addNotification("[${construction.name}] has been built in [${cityInfo.name}]", cityInfo.location, Color.BROWN) civ.addNotification("[${construction.name}] has been built in [${cityInfo.name}]", cityInfo.location, Color.BROWN)
else else
civ.addNotification("[${construction.name}] has been built in a faraway land", null, Color.BROWN) civ.addNotification("[${construction.name}] has been built in a faraway land", "BuildingIcons/${construction.name}")
} }
} else } else
cityInfo.civInfo.addNotification("[${construction.name}] has been built in [" + cityInfo.name + "]", cityInfo.location, NotificationType.Construction) cityInfo.civInfo.addNotification("[${construction.name}] has been built in [" + cityInfo.name + "]", cityInfo.location, NotificationIcon.Construction)
} }
fun addBuilding(buildingName: String) { fun addBuilding(buildingName: String) {

View File

@ -1,8 +1,7 @@
package com.unciv.logic.city package com.unciv.logic.city
import com.badlogic.gdx.graphics.Color
import com.unciv.logic.automation.Automation import com.unciv.logic.automation.Automation
import com.unciv.logic.civilization.NotificationType import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.ui.utils.withItem import com.unciv.ui.utils.withItem
import com.unciv.ui.utils.withoutItem import com.unciv.ui.utils.withoutItem
@ -144,7 +143,7 @@ class CityExpansionManager {
fun nextTurn(culture: Float) { fun nextTurn(culture: Float) {
cultureStored += culture.toInt() cultureStored += culture.toInt()
if (cultureStored >= getCultureToNextTile() && addNewTileWithCulture()) if (cultureStored >= getCultureToNextTile() && addNewTileWithCulture())
cityInfo.civInfo.addNotification("[" + cityInfo.name + "] has expanded its borders!", cityInfo.location, NotificationType.Culture) cityInfo.civInfo.addNotification("[" + cityInfo.name + "] has expanded its borders!", cityInfo.location, NotificationIcon.Culture)
} }
fun setTransients() { fun setTransients() {

View File

@ -2,7 +2,7 @@ package com.unciv.logic.city
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.unciv.logic.automation.Automation import com.unciv.logic.automation.Automation
import com.unciv.logic.civilization.NotificationType import com.unciv.logic.civilization.NotificationIcon
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.models.Counter import com.unciv.models.Counter
import com.unciv.ui.utils.withItem import com.unciv.ui.utils.withItem
@ -65,7 +65,7 @@ class PopulationManager {
foodStored += (getFoodToNextPopulation() * percentOfFoodCarriedOver / 100f).toInt() foodStored += (getFoodToNextPopulation() * percentOfFoodCarriedOver / 100f).toInt()
population++ population++
autoAssignPopulation() autoAssignPopulation()
cityInfo.civInfo.addNotification("[${cityInfo.name}] has grown!", cityInfo.location, NotificationType.Growth) cityInfo.civInfo.addNotification("[${cityInfo.name}] has grown!", cityInfo.location, NotificationIcon.Growth)
} }
} }

View File

@ -553,9 +553,9 @@ class CivilizationInfo {
addNotification(text, color, LocationAction(locations)) addNotification(text, color, LocationAction(locations))
} }
fun addNotification(text: String, location: Vector2?, notificationType: NotificationType) { fun addNotification(text: String, location: Vector2?, notificationIcon: String) {
val locations = if (location != null) listOf(location) else emptyList() val locations = if (location != null) listOf(location) else emptyList()
addNotification(text, notificationType, LocationAction(locations)) addNotification(text, LocationAction(locations), notificationIcon)
} }
@ -564,9 +564,12 @@ class CivilizationInfo {
notifications.add(Notification(text, color, action)) notifications.add(Notification(text, color, action))
} }
fun addNotification(text: String, notificationType: NotificationType, action: NotificationAction? = null) { fun addNotification(text: String, vararg notificationIcons: String) = addNotification(text, null, *notificationIcons)
fun addNotification(text: String, action:NotificationAction?, vararg notificationIcons: String) {
if (playerType == PlayerType.AI) return // no point in lengthening the saved game info if no one will read it if (playerType == PlayerType.AI) return // no point in lengthening the saved game info if no one will read it
notifications.add(Notification(text, notificationType, action)) val arrayList = ArrayList<String>().apply { addAll(notificationIcons) }
notifications.add(Notification(text, arrayList, action))
} }
fun addUnit(unitName: String, city: CityInfo? = null) { fun addUnit(unitName: String, city: CityInfo? = null) {

View File

@ -7,13 +7,13 @@ import com.unciv.ui.pickerscreens.TechPickerScreen
import com.unciv.ui.trade.DiplomacyScreen import com.unciv.ui.trade.DiplomacyScreen
import com.unciv.ui.worldscreen.WorldScreen import com.unciv.ui.worldscreen.WorldScreen
enum class NotificationType { object NotificationIcon {
Culture, val Culture = "StatIcons/Culture"
Construction, val Construction = "StatIcons/Production"
Growth, val Growth = "StatIcons/Population"
War, val War = "OtherIcons/Pillage"
Trade, val Trade = "StatIcons/Acquire"
Science val Science = "StatIcons/Science"
} }
/** /**
@ -24,7 +24,7 @@ open class Notification() {
var text: String="" var text: String=""
var color: Color?=null var color: Color?=null
var notificationType:NotificationType?=null var icons: ArrayList<String> = ArrayList<String>() // Must be ArrayList and not List so it can be deserialized
var action: NotificationAction? = null var action: NotificationAction? = null
// default parameters necessary for json deserialization // default parameters necessary for json deserialization
@ -34,9 +34,9 @@ open class Notification() {
this.action = action this.action = action
} }
constructor(text: String, notificationType: NotificationType, action: NotificationAction? = null) : this() { constructor(text: String, notificationIcons: ArrayList<String>, action: NotificationAction? = null) : this() {
this.text = text this.text = text
this.notificationType = notificationType this.icons = notificationIcons
this.action = action this.action = action
} }

View File

@ -257,7 +257,7 @@ class TechManager {
} }
} }
for (it in getRuleset().policyBranches.values.filter { it.era == currentEra }) { for (it in getRuleset().policyBranches.values.filter { it.era == currentEra }) {
civInfo.addNotification("[" + it.name + "] policy branch unlocked!", NotificationType.Culture) civInfo.addNotification("[" + it.name + "] policy branch unlocked!", NotificationIcon.Culture)
} }
} }

View File

@ -307,8 +307,8 @@ class DiplomacyManager() {
trades.remove(trade) trades.remove(trade)
val otherCivTrades = otherCiv().getDiplomacyManager(civInfo).trades val otherCivTrades = otherCiv().getDiplomacyManager(civInfo).trades
otherCivTrades.removeAll { it.equals(trade.reverse()) } otherCivTrades.removeAll { it.equals(trade.reverse()) }
civInfo.addNotification("One of our trades with [$otherCivName] has been cut short", NotificationType.Trade) civInfo.addNotification("One of our trades with [$otherCivName] has been cut short", NotificationIcon.Trade)
otherCiv().addNotification("One of our trades with [${civInfo.civName}] has been cut short", NotificationType.Trade) otherCiv().addNotification("One of our trades with [${civInfo.civName}] has been cut short", NotificationIcon.Trade)
civInfo.updateDetailedCivResources() civInfo.updateDetailedCivResources()
} }
} }
@ -417,8 +417,8 @@ class DiplomacyManager() {
trades.remove(trade) trades.remove(trade)
for (offer in trade.ourOffers.union(trade.theirOffers).filter { it.duration == 0 }) { // this was a timed trade for (offer in trade.ourOffers.union(trade.theirOffers).filter { it.duration == 0 }) { // this was a timed trade
if (offer in trade.theirOffers) if (offer in trade.theirOffers)
civInfo.addNotification("[${offer.name}] from [$otherCivName] has ended", NotificationType.Trade) civInfo.addNotification("[${offer.name}] from [$otherCivName] has ended", NotificationIcon.Trade)
else civInfo.addNotification("[${offer.name}] to [$otherCivName] has ended", NotificationType.Trade) else civInfo.addNotification("[${offer.name}] to [$otherCivName] has ended", NotificationIcon.Trade)
civInfo.updateStatsForNextTurn() // if they were bringing us gold per turn civInfo.updateStatsForNextTurn() // if they were bringing us gold per turn
civInfo.updateDetailedCivResources() // if they were giving us resources civInfo.updateDetailedCivResources() // if they were giving us resources
@ -471,7 +471,7 @@ class DiplomacyManager() {
// Cancel all trades. // Cancel all trades.
for (trade in trades) for (trade in trades)
for (offer in trade.theirOffers.filter { it.duration > 0 }) for (offer in trade.theirOffers.filter { it.duration > 0 })
civInfo.addNotification("[" + offer.name + "] from [$otherCivName] has ended", NotificationType.Trade) civInfo.addNotification("[" + offer.name + "] from [$otherCivName] has ended", NotificationIcon.Trade)
trades.clear() trades.clear()
updateHasOpenBorders() updateHasOpenBorders()
@ -487,11 +487,11 @@ class DiplomacyManager() {
onWarDeclared() onWarDeclared()
otherCivDiplomacy.onWarDeclared() otherCivDiplomacy.onWarDeclared()
otherCiv.addNotification("[${civInfo.civName}] has declared war on us!", NotificationType.War) otherCiv.addNotification("[${civInfo.civName}] has declared war on us!", NotificationIcon.War)
otherCiv.popupAlerts.add(PopupAlert(AlertType.WarDeclaration, civInfo.civName)) otherCiv.popupAlerts.add(PopupAlert(AlertType.WarDeclaration, civInfo.civName))
getCommonKnownCivs().forEach { getCommonKnownCivs().forEach {
it.addNotification("[${civInfo.civName}] has declared war on [${otherCiv().civName}]!", NotificationType.War) it.addNotification("[${civInfo.civName}] has declared war on [${otherCiv().civName}]!", NotificationIcon.War)
} }
otherCivDiplomacy.setModifier(DiplomaticModifiers.DeclaredWarOnUs, -20f) otherCivDiplomacy.setModifier(DiplomaticModifiers.DeclaredWarOnUs, -20f)

View File

@ -7,7 +7,7 @@ import com.unciv.UncivGame
import com.unciv.logic.automation.UnitAutomation import com.unciv.logic.automation.UnitAutomation
import com.unciv.logic.automation.WorkerAutomation import com.unciv.logic.automation.WorkerAutomation
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.NotificationType import com.unciv.logic.civilization.NotificationIcon
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.Unique import com.unciv.models.ruleset.Unique
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
@ -616,7 +616,7 @@ class MapUnit {
val city = civInfo.cities.random(tileBasedRandom) val city = civInfo.cities.random(tileBasedRandom)
city.population.population++ city.population.population++
city.population.autoAssignPopulation() city.population.autoAssignPopulation()
civInfo.addNotification("We have found survivors in the ruins - population added to [" + city.name + "]", tile.position, NotificationType.Growth) civInfo.addNotification("We have found survivors in the ruins - population added to [" + city.name + "]", tile.position, NotificationIcon.Growth)
} }
val researchableAncientEraTechs = tile.tileMap.gameInfo.ruleSet.technologies.values val researchableAncientEraTechs = tile.tileMap.gameInfo.ruleSet.technologies.values
.filter { .filter {
@ -628,7 +628,7 @@ class MapUnit {
actions.add { actions.add {
val tech = researchableAncientEraTechs.random(tileBasedRandom).name val tech = researchableAncientEraTechs.random(tileBasedRandom).name
civInfo.tech.addTechnology(tech) civInfo.tech.addTechnology(tech)
civInfo.addNotification("We have discovered the lost technology of [$tech] in the ruins!", tile.position, NotificationType.Science) civInfo.addNotification("We have discovered the lost technology of [$tech] in the ruins!", tile.position, NotificationIcon.Science)
} }
@ -657,7 +657,7 @@ class MapUnit {
actions.add { actions.add {
civInfo.policies.addCulture(20) civInfo.policies.addCulture(20)
civInfo.addNotification("We have discovered cultural artifacts in the ruins! (+20 Culture)", tile.position, NotificationType.Culture) civInfo.addNotification("We have discovered cultural artifacts in the ruins! (+20 Culture)", tile.position, NotificationIcon.Culture)
} }
// Map of the surrounding area // Map of the surrounding area

View File

@ -33,9 +33,10 @@ class NotificationsScroll(internal val worldScreen: WorldScreen) : ScrollPane(nu
val label = notification.text.toLabel(Color.BLACK, 30) val label = notification.text.toLabel(Color.BLACK, 30)
val listItem = Table() val listItem = Table()
if (notification.notificationType != null) if (notification.icons.isNotEmpty())
listItem.add(ImageGetter.getImage("NotificationIcons/" + notification.notificationType!!.name)).size(20f).padRight(5f) for(icon in notification.icons)
else listItem.add(ImageGetter.getCircle() listItem.add(ImageGetter.getImage(icon)).size(20f).padRight(5f)
else if(notification.color!=null) listItem.add(ImageGetter.getCircle()
.apply { color = notification.color }).size(20f).padRight(5f) .apply { color = notification.color }).size(20f).padRight(5f)
listItem.background = ImageGetter.getRoundedEdgeTableBackground().apply { setScale(0.5f) } listItem.background = ImageGetter.getRoundedEdgeTableBackground().apply { setScale(0.5f) }
listItem.add(label) listItem.add(label)