From d15e01d5e8da23ae2ca2c3107e6ec89f1d03f22c Mon Sep 17 00:00:00 2001 From: will-ca Date: Mon, 1 Nov 2021 09:39:01 -0700 Subject: [PATCH] Show notification to cycle through visible resources when clicking on resource icon in Resource Overview. (#5603) * Show notif to cycle through resource tiles when tapping on icon in Resource Overview. * Make resource reveal notification more configurable, and move completely to `GameInfo`. * Make resource reveal notification loop through all explored tiles, instead of just visible tiles. * Have resource discovery notif cycle through matching CS centers for CS-only Luxuries. * Remove commented lines. * Remove extra comma. * Use Sequence in resource notif. --- core/src/com/unciv/logic/GameInfo.kt | 53 +++++++++++++++++++ .../unciv/logic/civilization/TechManager.kt | 46 ++-------------- .../overviewscreen/ResourcesOverviewTable.kt | 14 ++--- 3 files changed, 61 insertions(+), 52 deletions(-) diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 293590e935..8d7a7d8d70 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -7,6 +7,7 @@ import com.unciv.logic.BackwardCompatibility.replaceDiplomacyFlag import com.unciv.logic.automation.NextTurnAutomation import com.unciv.logic.civilization.* import com.unciv.logic.civilization.diplomacy.DiplomacyFlags +import com.unciv.logic.city.CityInfo import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileMap import com.unciv.models.Religion @@ -16,6 +17,7 @@ import com.unciv.models.ruleset.Difficulty import com.unciv.models.ruleset.ModOptionsConstants import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetCache +import com.unciv.models.ruleset.unique.UniqueType import com.unciv.ui.audio.MusicMood import com.unciv.ui.audio.MusicTrackChooserFlags import java.util.* @@ -292,6 +294,57 @@ class GameInfo { } } + fun notifyExploredResources(civInfo: CivilizationInfo, resourceName: String, maxDistance: Int, showForeign: Boolean) { + // Calling with `maxDistance = 0` removes distance limitation. + data class CityTileAndDistance(val city: CityInfo, val tile: TileInfo, val distance: Int) + + var exploredRevealTiles:Sequence + + if (ruleSet.tileResources[resourceName]!!.hasUnique(UniqueType.CityStateOnlyResource)) { + // Look for matching mercantile CS centers + exploredRevealTiles = getAliveCityStates() + .asSequence() + .filter { it.cityStateResource == resourceName } + .map { it.getCapital().getCenterTile() } + } else { + exploredRevealTiles = tileMap.values + .asSequence() + .filter { it.resource == resourceName } + } + + val exploredRevealInfo = exploredRevealTiles + .filter { it.position in civInfo.exploredTiles } + .flatMap { tile -> civInfo.cities.asSequence() + .map { + // build a full cross join all revealed tiles * civ's cities (should rarely surpass a few hundred) + // cache distance for each pair as sort will call it ~ 2n log n times + // should still be cheaper than looking up 'the' closest city per reveal tile before sorting + city -> CityTileAndDistance(city, tile, tile.aerialDistanceTo(city.getCenterTile())) + } + } + .filter { (maxDistance == 0 || it.distance <= maxDistance) && (showForeign || it.tile.getOwner() == null || it.tile.getOwner() == civInfo) } + .sortedWith ( compareBy { it.distance } ) + .distinctBy { it.tile } + + val chosenCity = exploredRevealInfo.firstOrNull()?.city ?: return + val positions = exploredRevealInfo + // re-sort to a more pleasant display order + .sortedWith(compareBy{ it.tile.aerialDistanceTo(chosenCity.getCenterTile()) }) + .map { it.tile.position } + .toList() // explicit materialization of sequence to satisfy addNotification overload + + val text = if(positions.size==1) + "[$resourceName] revealed near [${chosenCity.name}]" + else + "[${positions.size}] sources of [$resourceName] revealed, e.g. near [${chosenCity.name}]" + + civInfo.addNotification( + text, + LocationAction(positions), + "ResourceIcons/$resourceName" + ) + } + // All cross-game data which needs to be altered (e.g. when removing or changing a name of a building/tech) // will be done here, and not in CivInfo.setTransients or CityInfo fun setTransients() { diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index 5b32c5372a..ed868f6751 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -262,7 +262,11 @@ class TechManager { } } - if (civInfo.playerType == PlayerType.Human) notifyRevealedResources(techName) + if (civInfo.playerType == PlayerType.Human) { + for (revealedResource in getRuleset().tileResources.values.filter { techName == it.revealedBy }) { + civInfo.gameInfo.notifyExploredResources(civInfo, revealedResource.name, 5, false) + } + } val obsoleteUnits = getRuleset().units.values.filter { it.obsoleteTech == techName }.map { it.name } val unitUpgrades = HashMap>() @@ -323,46 +327,6 @@ class TechManager { techUniques.addUnique(unique) } - private fun notifyRevealedResources(techName: String) { - data class CityTileAndDistance(val city: CityInfo, val tile: TileInfo, val distance: Int) - - for (revealedResource in getRuleset().tileResources.values.filter { techName == it.revealedBy }) { - val revealedName = revealedResource.name - - val visibleRevealTiles = civInfo.viewableTiles.asSequence() - .filter { it.resource == revealedName } - .flatMap { tile -> civInfo.cities.asSequence() - .map { - // build a full cross join all revealed tiles * civ's cities (should rarely surpass a few hundred) - // cache distance for each pair as sort will call it ~ 2n log n times - // should still be cheaper than looking up 'the' closest city per reveal tile before sorting - city -> CityTileAndDistance(city, tile, tile.aerialDistanceTo(city.getCenterTile())) - } - } - .filter { it.distance <= 5 && (it.tile.getOwner() == null || it.tile.getOwner() == civInfo) } - .sortedWith ( compareBy { it.distance } ) - .distinctBy { it.tile } - - val chosenCity = visibleRevealTiles.firstOrNull()?.city ?: continue - val positions = visibleRevealTiles - // re-sort to a more pleasant display order - .sortedWith(compareBy{ it.tile.aerialDistanceTo(chosenCity.getCenterTile()) }) - .map { it.tile.position } - .toList() // explicit materialization of sequence to satisfy addNotification overload - - val text = if(positions.size==1) - "[$revealedName] revealed near [${chosenCity.name}]" - else - "[${positions.size}] sources of [$revealedName] revealed, e.g. near [${chosenCity.name}]" - - civInfo.addNotification( - text, - LocationAction(positions), - "ResourceIcons/$revealedName" - ) - } - } - fun setTransients() { researchedTechnologies.addAll(techsResearched.map { getRuleset().technologies[it]!! }) researchedTechnologies.forEach { addTechToTransients(it) } diff --git a/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt index 0a1f393e47..d1460dc821 100644 --- a/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt @@ -31,25 +31,20 @@ class ResourcesOverviewTable ( .filter { it.resourceType != ResourceType.Bonus }.distinct() .sortedWith(compareBy({ it.resourceType }, { it.name.tr() })) - var visibleLabel: Label? = null for (resource in resources) { // Create a group of label and icon for each resource. val resourceImage = ImageGetter.getResourceImage(resource.name, 50f) - val resourceLabel = resource.name.toLabel() val labelPadding = 10f // Using a table here leads to spacing issues // due to different label lengths. val holder = Group() resourceImage.onClick { - if (visibleLabel != null) - visibleLabel!!.isVisible = false - resourceLabel.isVisible = true - visibleLabel = resourceLabel + viewingPlayer.gameInfo.notifyExploredResources(viewingPlayer, resource.name, 0, true) + overviewScreen.game.setWorldScreen() } holder.addActor(resourceImage) - holder.addActor(resourceLabel) holder.setSize(resourceImage.width, - resourceImage.height + resourceLabel.height + labelPadding) + resourceImage.height + labelPadding) // Center-align all labels, but right-align the last couple resources' labels // because they may get clipped otherwise. The leftmost label should be fine // center-aligned (if there are more than 2 resources), because the left side @@ -58,9 +53,6 @@ class ResourcesOverviewTable ( (resources.indexOf(resource) + 2 >= resources.count()) -> 1 else -> 2 } - resourceLabel.moveBy((resourceImage.width - resourceLabel.width) / alignFactor, - resourceImage.height + labelPadding) - resourceLabel.isVisible = false add(holder) } addSeparator()