diff --git a/android/Images/OtherIcons/Notifications.png b/android/Images/OtherIcons/Notifications.png new file mode 100644 index 0000000000..b28e9b4154 Binary files /dev/null and b/android/Images/OtherIcons/Notifications.png differ diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 424a8bed74..fbca4277ce 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -704,6 +704,7 @@ Continuous rendering = When disabled, saves battery life but certain animations will be suspended = Order trade offers by amount = Ask for confirmation when pressing next turn = +Notifications log max turns = Check extension mods based on: = -none- = Reload mods = @@ -1124,6 +1125,7 @@ City-State Luxuries = Occupied City = Buildings = Wonders = +Notifications = Base values = Bonuses = Final = @@ -1166,6 +1168,8 @@ Near [city] = Somewhere around [city] = Far away = Status = +Current turn = +Turn [turnNumber] = Location = Unimproved = Number of tiles with this resource\nin your territory, without an\nappropriate improvement to use it = diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 8fce56edbb..220ccd757e 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -158,11 +158,17 @@ class CivilizationInfo { var ruinsManager = RuinsManager() var diplomacy = HashMap() var proximity = HashMap() - var notifications = ArrayList() val popupAlerts = ArrayList() private var allyCivName: String? = null var naturalWonders = ArrayList() + var notifications = ArrayList() + + var notificationsLog = ArrayList() + class NotificationsLog(val turn: Int = 0) { + var notifications = ArrayList() + } + /** for trades here, ourOffers is the current civ's offers, and theirOffers is what the requesting civ offers */ val tradeRequests = ArrayList() @@ -267,6 +273,7 @@ class CivilizationInfo { toReturn.exploredTiles.addAll(gameInfo.tileMap.values.asSequence().map { it.position }.filter { it in exploredTiles }) toReturn.lastSeenImprovement.putAll(lastSeenImprovement) toReturn.notifications.addAll(notifications) + toReturn.notificationsLog.addAll(notificationsLog) toReturn.citiesCreated = citiesCreated toReturn.popupAlerts.addAll(popupAlerts) toReturn.tradeRequests.addAll(tradeRequests) @@ -912,6 +919,16 @@ class CivilizationInfo { } fun endTurn() { + val notificationsThisTurn = NotificationsLog(gameInfo.turns) + notificationsThisTurn.notifications.addAll(notifications) + + while (notificationsLog.size >= UncivGame.Current.settings.notificationsLogMaxTurns) { + notificationsLog.removeFirst() + } + + if (notificationsThisTurn.notifications.isNotEmpty()) + notificationsLog.add(notificationsThisTurn) + notifications.clear() updateStatsForNextTurn() val nextTurnStats = statsForNextTurn diff --git a/core/src/com/unciv/logic/civilization/Notification.kt b/core/src/com/unciv/logic/civilization/Notification.kt index c0c5bc9e59..0954647c32 100644 --- a/core/src/com/unciv/logic/civilization/Notification.kt +++ b/core/src/com/unciv/logic/civilization/Notification.kt @@ -1,7 +1,11 @@ package com.unciv.logic.civilization import com.badlogic.gdx.math.Vector2 +import com.badlogic.gdx.scenes.scene2d.Actor +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.models.ruleset.Ruleset import com.unciv.ui.cityscreen.CityScreen +import com.unciv.ui.images.ImageGetter import com.unciv.ui.pickerscreens.TechPickerScreen import com.unciv.ui.trade.DiplomacyScreen import com.unciv.ui.utils.MayaCalendar @@ -48,6 +52,22 @@ open class Notification() { this.icons = notificationIcons this.action = action } + + fun addNotificationIcons(ruleset: Ruleset, iconSize: Float, table: Table) { + if (icons.isEmpty()) return + for (icon in icons.reversed()) { + val image: Actor = when { + ruleset.technologies.containsKey(icon) -> ImageGetter.getTechIcon(icon) + ruleset.nations.containsKey(icon) -> ImageGetter.getNationIndicator( + ruleset.nations[icon]!!, + iconSize + ) + ruleset.units.containsKey(icon) -> ImageGetter.getUnitIcon(icon) + else -> ImageGetter.getImage(icon) + } + table.add(image).size(iconSize).padRight(5f) + } + } } /** defines what to do if the user clicks on a notification */ diff --git a/core/src/com/unciv/models/metadata/GameSettings.kt b/core/src/com/unciv/models/metadata/GameSettings.kt index d1c9e543dd..c8b32a0877 100644 --- a/core/src/com/unciv/models/metadata/GameSettings.kt +++ b/core/src/com/unciv/models/metadata/GameSettings.kt @@ -56,6 +56,8 @@ class GameSettings { var useDemographics: Boolean = false var showZoomButtons: Boolean = false + var notificationsLogMaxTurns = 5 + var androidCutout: Boolean = false var multiplayer = GameSettingsMultiplayer() diff --git a/core/src/com/unciv/ui/options/GameplayTab.kt b/core/src/com/unciv/ui/options/GameplayTab.kt index 664cda750f..49b536ca63 100644 --- a/core/src/com/unciv/ui/options/GameplayTab.kt +++ b/core/src/com/unciv/ui/options/GameplayTab.kt @@ -3,7 +3,10 @@ package com.unciv.ui.options import com.badlogic.gdx.scenes.scene2d.ui.Table import com.unciv.UncivGame import com.unciv.logic.civilization.PlayerType +import com.unciv.models.metadata.GameSettings import com.unciv.ui.utils.BaseScreen +import com.unciv.ui.utils.UncivSlider +import com.unciv.ui.utils.extensions.toLabel import com.unciv.ui.worldscreen.WorldScreen fun gameplayTab( @@ -35,4 +38,20 @@ fun gameplayTab( ) { settings.automatedWorkersReplaceImprovements = it } optionsPopup.addCheckbox(this, "Order trade offers by amount", settings.orderTradeOffersByAmount) { settings.orderTradeOffersByAmount = it } optionsPopup.addCheckbox(this, "Ask for confirmation when pressing next turn", settings.confirmNextTurn) { settings.confirmNextTurn = it } + + addNotificationLogMaxTurnsSlider(this, settings, UncivGame.Current.worldScreen, optionsPopup.selectBoxMinWidth) +} + +private fun addNotificationLogMaxTurnsSlider(table: Table, settings: GameSettings, screen: BaseScreen?, selectBoxMinWidth: Float) { + table.add("Notifications log max turns".toLabel()).left().fillX() + + val minimapSlider = UncivSlider( + 3f, 15f, 1f, + initial = settings.notificationsLogMaxTurns.toFloat() + ) { + val turns = it.toInt() + settings.notificationsLogMaxTurns = turns + settings.save() + } + table.add(minimapSlider).minWidth(selectBoxMinWidth).pad(10f).row() } diff --git a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewCategories.kt b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewCategories.kt index 5292275a7e..8b0d4b8fa7 100644 --- a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewCategories.kt +++ b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewCategories.kt @@ -1,6 +1,7 @@ package com.unciv.ui.overviewscreen import com.badlogic.gdx.utils.Align +import com.unciv.UncivGame import com.unciv.logic.civilization.CivilizationInfo import com.unciv.ui.utils.KeyCharAndCode import com.unciv.ui.overviewscreen.EmpireOverviewTab.EmpireOverviewTabPersistableData @@ -14,7 +15,7 @@ private fun Boolean.toState(): EmpireOverviewTabState = if (this) EmpireOverview /** This controls which Tabs for the [EmpireOverviewScreen] exist and their order. * * To add a Tab, build a new [EmpireOverviewTab] subclass and fill out a new entry here, that's all. - * Note the enum value's name is used as Tab caption, so if you ever need a non-alphanumeric caption please redesign to include a property for the caption. + * Note the enum value's name is used as Tab caption, so if you ever need a non-alphanumeric caption please redesign to include a property for the caption. */ enum class EmpireOverviewCategories( val iconName: String, @@ -59,7 +60,10 @@ enum class EmpireOverviewCategories( fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, _: EmpireOverviewTabPersistableData?) = WonderOverviewTab(viewingPlayer, overviewScreen), fun (viewingPlayer: CivilizationInfo) = (viewingPlayer.naturalWonders.isEmpty() && viewingPlayer.cities.isEmpty()).toState()), - ; + Notifications("OtherIcons/Notifications", 'N', Align.top, + fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, _: EmpireOverviewTabPersistableData?) + = NotificationsOverviewTable(worldScreen = UncivGame.Current.worldScreen!!, viewingPlayer, overviewScreen), + fun (_: CivilizationInfo) = EmpireOverviewTabState.Normal); constructor(iconName: String, shortcutChar: Char, scrollAlign: Int, factory: FactoryType, stateTester: StateTesterType = { _ -> EmpireOverviewTabState.Normal }) : this(iconName, KeyCharAndCode(shortcutChar), scrollAlign, factory, stateTester) diff --git a/core/src/com/unciv/ui/overviewscreen/NotificationsOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/NotificationsOverviewTable.kt new file mode 100644 index 0000000000..788547ead9 --- /dev/null +++ b/core/src/com/unciv/ui/overviewscreen/NotificationsOverviewTable.kt @@ -0,0 +1,80 @@ +package com.unciv.ui.overviewscreen + +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.scenes.scene2d.Touchable +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.UncivGame +import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.logic.civilization.Notification +import com.unciv.ui.images.ImageGetter +import com.unciv.ui.utils.BaseScreen +import com.unciv.ui.utils.WrappableLabel +import com.unciv.ui.utils.extensions.onClick +import com.unciv.ui.worldscreen.WorldScreen + +class NotificationsOverviewTable( + val worldScreen: WorldScreen, + viewingPlayer: CivilizationInfo, + overviewScreen: EmpireOverviewScreen +) : EmpireOverviewTab(viewingPlayer, overviewScreen) { + + val notificationLog = viewingPlayer.notificationsLog + + private val notificationTable = Table(BaseScreen.skin) + + val scaleFactor = 0.3f + val inverseScaleFactor = 1f / scaleFactor + private val maxWidthOfStage = 0.333f + private val maxEntryWidth = worldScreen.stage.width * maxWidthOfStage * inverseScaleFactor + + val iconSize = 20f + + init { + val tablePadding = 30f + defaults().pad(tablePadding).top() + + generateNotificationTable() + + add(notificationTable) + } + + private fun generateNotificationTable() { + if (viewingPlayer.notifications.isNotEmpty()) + notificationTable.add(notificationsArrayTable("Current", viewingPlayer.notifications)).row() + + for (notification in notificationLog.asReversed()) { + notificationTable.add(notificationsArrayTable(notification.turn.toString(), notification.notifications)) + notificationTable.padTop(20f).row() + } + } + + private fun notificationsArrayTable(index: String, notifications: ArrayList): Table { + val turnTable = Table(BaseScreen.skin) + if (index != "Current") + turnTable.add("Turn [$index]").row() + else + turnTable.add("Current turn").row() + + for (notification in notifications) { + val notificationTable = Table(BaseScreen.skin) + + val labelWidth = maxEntryWidth * notification.icons.size - 10f + val label = WrappableLabel(notification.text, labelWidth, Color.BLACK, 20) + + notificationTable.add(label) + notificationTable.background = ImageGetter.getRoundedEdgeRectangle() + notificationTable.touchable = Touchable.enabled + notificationTable.onClick { + UncivGame.Current.resetToWorldScreen() + notification.action?.execute(worldScreen) + } + + notification.addNotificationIcons(worldScreen.gameInfo.ruleSet, iconSize, notificationTable) + + turnTable.add(notificationTable).padTop(5f) + turnTable.padTop(20f).row() + } + turnTable.padTop(20f).row() + return turnTable + } +} diff --git a/core/src/com/unciv/ui/worldscreen/NotificationsScroll.kt b/core/src/com/unciv/ui/worldscreen/NotificationsScroll.kt index 253e608451..44f9c13ace 100644 --- a/core/src/com/unciv/ui/worldscreen/NotificationsScroll.kt +++ b/core/src/com/unciv/ui/worldscreen/NotificationsScroll.kt @@ -78,18 +78,7 @@ class NotificationsScroll( listItem.add(label).padRight(10f) } - if (notification.icons.isNotEmpty()) { - val ruleset = worldScreen.gameInfo.ruleSet - for (icon in notification.icons.reversed()) { - val image: Actor = when { - ruleset.technologies.containsKey(icon) -> ImageGetter.getTechIcon(icon) - ruleset.nations.containsKey(icon) -> ImageGetter.getNationIndicator(ruleset.nations[icon]!!, iconSize) - ruleset.units.containsKey(icon) -> ImageGetter.getUnitIcon(icon) - else -> ImageGetter.getImage(icon) - } - listItem.add(image).size(iconSize).padRight(5f) - } - } + notification.addNotificationIcons(worldScreen.gameInfo.ruleSet, iconSize, listItem) // using a large click area with no gap in between each message item. // this avoids accidentally clicking in between the messages, resulting in a map click