From 73aeabec2c65f0601217668dfba947ad526d4cfd Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Sun, 28 May 2023 16:51:33 +0200 Subject: [PATCH] All notifications from Overview are now temporary (#9455) * Refactor notification history re-show functionality * Refactor explored resources notification generator * Refactor "Unimproved" to use existing providesResource * Make overview resource "links" into temporary notifications * Added link to overview resource "Unimproved" column * Minor linting --- core/src/com/unciv/logic/GameInfo.kt | 42 +++++++++++++------ .../civilization/managers/TechManager.kt | 2 +- core/src/com/unciv/logic/map/tile/Tile.kt | 11 +++-- .../screens/overviewscreen/CityOverviewTab.kt | 8 ++-- .../overviewscreen/EmpireOverviewTab.kt | 12 ++++++ .../NotificationsOverviewTable.kt | 19 +++------ .../overviewscreen/ResourcesOverviewTab.kt | 40 +++++++++++------- 7 files changed, 80 insertions(+), 54 deletions(-) diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index d7cc1e6e66..f41c26c7a5 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -15,6 +15,7 @@ import com.unciv.logic.city.City import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.CivilizationInfoPreview import com.unciv.logic.civilization.LocationAction +import com.unciv.logic.civilization.Notification import com.unciv.logic.civilization.NotificationCategory import com.unciv.logic.civilization.NotificationIcon import com.unciv.logic.civilization.PlayerType @@ -437,18 +438,37 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion } } - /** Generate a notification pointing out resources. + /** Generate and show a notification pointing out resources. * Used by [addTechnology][TechManager.addTechnology] and [ResourcesOverviewTab][com.unciv.ui.screens.overviewscreen.ResourcesOverviewTab] * @param maxDistance from next City, 0 removes distance limitation. - * @param showForeign Disables filter to exclude foreign territory. + * @param filter optional tile filter predicate, e.g. to exclude foreign territory. * @return `false` if no resources were found and no notification was added. + * @see getExploredResourcesNotification */ fun notifyExploredResources( civInfo: Civilization, resourceName: String, - maxDistance: Int, - showForeign: Boolean + maxDistance: Int = Int.MAX_VALUE, + filter: (Tile) -> Boolean = { true } ): Boolean { + val notification = getExploredResourcesNotification(civInfo, resourceName, maxDistance, filter) + ?: return false + civInfo.notifications.add(notification) + return true + } + + /** Generate a notification pointing out resources. + * @param maxDistance from next City, default removes distance limitation. + * @param filter optional tile filter predicate, e.g. to exclude foreign territory. + * @return `null` if no resources were found, otherwise a Notification instance. + * @see notifyExploredResources + */ + fun getExploredResourcesNotification( + civInfo: Civilization, + resourceName: String, + maxDistance: Int = Int.MAX_VALUE, + filter: (Tile) -> Boolean = { true } + ): Notification? { data class CityTileAndDistance(val city: City, val tile: Tile, val distance: Int) val exploredRevealTiles: Sequence = @@ -476,11 +496,12 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion CityTileAndDistance(city, tile, tile.aerialDistanceTo(city.getCenterTile())) } } - .filter { (maxDistance == 0 || it.distance <= maxDistance) && (showForeign || it.tile.getOwner() == null || it.tile.getOwner() == civInfo) } + .filter { it.distance <= maxDistance && filter(it.tile) } .sortedWith(compareBy { it.distance }) .distinctBy { it.tile } - val chosenCity = exploredRevealInfo.firstOrNull()?.city ?: return false + val chosenCity = exploredRevealInfo.firstOrNull()?.city + ?: return null val positions = exploredRevealInfo // re-sort to a more pleasant display order .sortedWith(compareBy { it.tile.aerialDistanceTo(chosenCity.getCenterTile()) }) @@ -492,13 +513,8 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion else "[$positionsCount] sources of [$resourceName] revealed, e.g. near [${chosenCity.name}]" - civInfo.addNotification( - text, - LocationAction(positions), - NotificationCategory.General, - "ResourceIcons/$resourceName" - ) - return true + return Notification(text, arrayListOf("ResourceIcons/$resourceName"), + LocationAction(positions), NotificationCategory.General) } // All cross-game data which needs to be altered (e.g. when removing or changing a name of a building/tech) diff --git a/core/src/com/unciv/logic/civilization/managers/TechManager.kt b/core/src/com/unciv/logic/civilization/managers/TechManager.kt index cca3e2f19e..183b37390f 100644 --- a/core/src/com/unciv/logic/civilization/managers/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/TechManager.kt @@ -320,7 +320,7 @@ class TechManager : IsPartOfGameInfoSerialization { // notifyExploredResources scans the player's owned tiles and returns false if none // found with a revealed resource - keep this knowledge to avoid the update call. mayNeedUpdateResources = mayNeedUpdateResources || - civInfo.gameInfo.notifyExploredResources(civInfo, revealedResource.name, 5, false) + civInfo.gameInfo.notifyExploredResources(civInfo, revealedResource.name, 5) } } // At least in the case of a human player hurrying research, this civ's resource availability diff --git a/core/src/com/unciv/logic/map/tile/Tile.kt b/core/src/com/unciv/logic/map/tile/Tile.kt index 98d5fd849a..6cda58a9ff 100644 --- a/core/src/com/unciv/logic/map/tile/Tile.kt +++ b/core/src/com/unciv/logic/map/tile/Tile.kt @@ -497,13 +497,12 @@ open class Tile : IsPartOfGameInfoSerialization { if (isCityCenter()) return true val improvement = getUnpillagedTileImprovement() if (improvement != null && improvement.name in tileResource.getImprovements() - && (improvement.techRequired==null || civInfo.tech.isResearched(improvement.techRequired!!))) return true + && (improvement.techRequired == null || civInfo.tech.isResearched(improvement.techRequired!!)) + ) return true // TODO: Generic-ify to unique - if (tileResource.resourceType==ResourceType.Strategic - && improvement!=null - && improvement.isGreatImprovement()) - return true - return false + return (tileResource.resourceType == ResourceType.Strategic + && improvement != null + && improvement.isGreatImprovement()) } // This should be the only adjacency function diff --git a/core/src/com/unciv/ui/screens/overviewscreen/CityOverviewTab.kt b/core/src/com/unciv/ui/screens/overviewscreen/CityOverviewTab.kt index 9c98dcb64b..23ce1f9d64 100644 --- a/core/src/com/unciv/ui/screens/overviewscreen/CityOverviewTab.kt +++ b/core/src/com/unciv/ui/screens/overviewscreen/CityOverviewTab.kt @@ -231,11 +231,9 @@ class CityOverviewTab( city.demandedResource.isNotEmpty() -> { val image = ImageGetter.getResourcePortrait(city.demandedResource, iconSize *0.7f).apply { addTooltip("Demanding [${city.demandedResource}]", 18f, tipAlign = Align.topLeft) - onClick { - if (gameInfo.notifyExploredResources(viewingPlayer, city.demandedResource, 0, true)) { - overviewScreen.game.popScreen() - } - } + onClick { showOneTimeNotification( + gameInfo.getExploredResourcesNotification(viewingPlayer, city.demandedResource) + ) } } cityInfoTableDetails.add(image) } diff --git a/core/src/com/unciv/ui/screens/overviewscreen/EmpireOverviewTab.kt b/core/src/com/unciv/ui/screens/overviewscreen/EmpireOverviewTab.kt index 150aa20d26..b9fa86cd63 100644 --- a/core/src/com/unciv/ui/screens/overviewscreen/EmpireOverviewTab.kt +++ b/core/src/com/unciv/ui/screens/overviewscreen/EmpireOverviewTab.kt @@ -1,7 +1,10 @@ package com.unciv.ui.screens.overviewscreen import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.GUI +import com.unciv.UncivGame import com.unciv.logic.civilization.Civilization +import com.unciv.logic.civilization.Notification import com.unciv.ui.components.TabbedPager import com.unciv.ui.screens.basescreen.BaseScreen @@ -26,4 +29,13 @@ abstract class EmpireOverviewTab ( val gameInfo = viewingPlayer.gameInfo + /** Helper to show the world screen with a temporary "one-time" notification */ + // Here because it's common to notification history and resource finder + internal fun showOneTimeNotification(notification: Notification?) { + if (notification == null) return // Convenience - easier than a return@lambda for a caller + val worldScreen = GUI.getWorldScreen() + worldScreen.notificationsScroll.oneTimeNotification = notification + UncivGame.Current.resetToWorldScreen() + notification.action?.execute(worldScreen) + } } diff --git a/core/src/com/unciv/ui/screens/overviewscreen/NotificationsOverviewTable.kt b/core/src/com/unciv/ui/screens/overviewscreen/NotificationsOverviewTable.kt index 9baac6adda..3e1030a5d1 100644 --- a/core/src/com/unciv/ui/screens/overviewscreen/NotificationsOverviewTable.kt +++ b/core/src/com/unciv/ui/screens/overviewscreen/NotificationsOverviewTable.kt @@ -3,10 +3,7 @@ package com.unciv.ui.screens.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.GUI -import com.unciv.UncivGame import com.unciv.logic.civilization.Civilization -import com.unciv.logic.civilization.LocationAction import com.unciv.logic.civilization.Notification import com.unciv.logic.civilization.NotificationCategory import com.unciv.ui.components.ColorMarkupLabel @@ -37,7 +34,7 @@ class NotificationsOverviewTable( } - private val worldScreen = GUI.getWorldScreen() + private val stageWidth = overviewScreen.stage.width private val notificationLog = viewingPlayer.notificationsLog private val notificationTable = Table(BaseScreen.skin) @@ -70,9 +67,9 @@ class NotificationsOverviewTable( else "Current turn".toLabel() turnTable.add(Table().apply { - add(ImageGetter.getWhiteDot()).minHeight(2f).width(worldScreen.stage.width/4) + add(ImageGetter.getWhiteDot()).minHeight(2f).width(stageWidth / 4) add(turnLabel).pad(3f) - add(ImageGetter.getWhiteDot()).minHeight(2f).width(worldScreen.stage.width/4) + add(ImageGetter.getWhiteDot()).minHeight(2f).width(stageWidth / 4) }).row() for (category in NotificationCategory.values()){ @@ -88,17 +85,13 @@ class NotificationsOverviewTable( val label = ColorMarkupLabel(notification.text, Color.BLACK, fontSize = 20) .apply { wrap = true } - notificationTable.add(label).width(worldScreen.stage.width/2 - iconSize * notification.icons.size) + notificationTable.add(label).width(stageWidth / 2 - iconSize * notification.icons.size) notificationTable.background = BaseScreen.skinStrings.getUiBackground("OverviewScreen/NotificationOverviewTable/Notification", BaseScreen.skinStrings.roundedEdgeRectangleShape) notificationTable.touchable = Touchable.enabled if (notification.action != null) - notificationTable.onClick { - worldScreen.notificationsScroll.oneTimeNotification = notification - UncivGame.Current.resetToWorldScreen() - notification.action?.execute(worldScreen) - } + notificationTable.onClick { showOneTimeNotification(notification) } - notification.addNotificationIconsTo(notificationTable, worldScreen.gameInfo.ruleset, iconSize) + notification.addNotificationIconsTo(notificationTable, gameInfo.ruleset, iconSize) turnTable.add(notificationTable).padTop(5f) turnTable.padTop(20f).row() diff --git a/core/src/com/unciv/ui/screens/overviewscreen/ResourcesOverviewTab.kt b/core/src/com/unciv/ui/screens/overviewscreen/ResourcesOverviewTab.kt index 9f0b80dd62..b50ce16c67 100644 --- a/core/src/com/unciv/ui/screens/overviewscreen/ResourcesOverviewTab.kt +++ b/core/src/com/unciv/ui/screens/overviewscreen/ResourcesOverviewTab.kt @@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.utils.Align import com.unciv.UncivGame import com.unciv.logic.civilization.Civilization +import com.unciv.logic.map.tile.Tile import com.unciv.logic.trade.TradeType import com.unciv.models.ruleset.tile.ResourceSupplyList import com.unciv.models.ruleset.tile.ResourceType @@ -75,20 +76,28 @@ class ResourcesOverviewTab( private fun ResourceSupplyList.getLabel(resource: TileResource, origin: String): Label? { val amount = get(resource, origin)?.amount ?: return null - return if (resource.isStockpiled() && amount > 0) "+$amount".toLabel() - else amount.toLabel() + val label = if (resource.isStockpiled() && amount > 0) "+$amount".toLabel() + else amount.toLabel() + if (origin == ExtraInfoOrigin.Unimproved.name) + label.onClick { showOneTimeNotification( + gameInfo.getExploredResourcesNotification(viewingPlayer, resource.name) { + it.getOwner() == viewingPlayer && it.countAsUnimproved() + } + ) } + return label } + private fun ResourceSupplyList.getTotalLabel(resource: TileResource): Label { val total = filter { it.resource == resource }.sumOf { it.amount } return if (resource.isStockpiled() && total > 0) "+$total".toLabel() else total.toLabel() } + private fun getResourceImage(name: String) = ImageGetter.getResourcePortrait(name, iconSize).apply { - onClick { - if (viewingPlayer.gameInfo.notifyExploredResources(viewingPlayer, name, 0, true)) - overviewScreen.game.popScreen() - } + onClick { showOneTimeNotification( + gameInfo.getExploredResourcesNotification(viewingPlayer, name) + ) } } private fun TileResource.getLabel() = name.toLabel().apply { onClick { @@ -228,10 +237,15 @@ class ResourcesOverviewTab( overviewScreen.resizePage(this) // Without the height is miscalculated - shouldn't be } + private fun Tile.countAsUnimproved(): Boolean = resource != null && + tileResource.resourceType != ResourceType.Bonus && + hasViewableResource(viewingPlayer) && + !providesResources(viewingPlayer) + private fun getExtraDrilldown(): ResourceSupplyList { val newResourceSupplyList = ResourceSupplyList() for (city in viewingPlayer.cities) { - if (!city.demandedResource.isEmpty()) { + if (city.demandedResource.isNotEmpty()) { val wltkResource = gameInfo.ruleset.tileResources[city.demandedResource]!! if (city.isWeLoveTheKingDayActive()) { newResourceSupplyList.add(wltkResource, ExtraInfoOrigin.CelebratingWLKT.name) @@ -239,15 +253,9 @@ class ResourcesOverviewTab( newResourceSupplyList.add(wltkResource, ExtraInfoOrigin.DemandingWLTK.name) } } - for (tile in city.getTiles()) { - if (tile.isCityCenter()) continue - if (!tile.hasViewableResource(viewingPlayer)) continue - val tileResource = tile.tileResource - if (tileResource.resourceType == ResourceType.Bonus) continue - if (tile.getUnpillagedImprovement() != null && tileResource.isImprovedBy(tile.improvement!!)) continue - if (tileResource.resourceType == ResourceType.Strategic && tile.getTileImprovement()?.isGreatImprovement() == true) continue - newResourceSupplyList.add(tileResource, ExtraInfoOrigin.Unimproved.name) - } + for (tile in city.getTiles()) + if (tile.countAsUnimproved()) + newResourceSupplyList.add(tile.tileResource, ExtraInfoOrigin.Unimproved.name) } for (otherCiv in viewingPlayer.getKnownCivs())