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.
This commit is contained in:
will-ca
2021-11-01 09:39:01 -07:00
committed by GitHub
parent 71ea8dadf7
commit d15e01d5e8
3 changed files with 61 additions and 52 deletions

View File

@ -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<TileInfo>
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() {

View File

@ -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<String, ArrayList<CityInfo>>()
@ -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) }

View File

@ -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()