diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index eec8cf8abb..fc3856443d 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -5,6 +5,7 @@ import com.unciv.GameParameters import com.unciv.logic.automation.NextTurnAutomation import com.unciv.logic.city.CityConstructions import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.logic.civilization.LocationAction import com.unciv.logic.civilization.PlayerType import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileMap @@ -97,7 +98,7 @@ class GameInfo { } else { val positions = tiles.map { it.position } - thisPlayer.addNotification("[${positions.size}] enemy units were spotted $inOrNear our territory", positions, Color.RED) + thisPlayer.addNotification("[${positions.size}] enemy units were spotted $inOrNear our territory", Color.RED, LocationAction(positions)) } } diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index 36781d00db..094db807ba 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -5,6 +5,7 @@ import com.unciv.Constants import com.unciv.logic.battle.CityCombatant import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.CityInfo +import com.unciv.logic.civilization.CityAction import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.map.TileInfo import com.unciv.models.gamebasics.unit.BaseUnit @@ -192,7 +193,7 @@ class Automation { else theChosenOne = relativeCostEffectiveness.minBy { it.remainingWork }!!.choice // ignore modifiers, go for the cheapest. currentConstruction = theChosenOne - cityInfo.civInfo.addNotification("Work has started on [$currentConstruction]", cityInfo.location, Color.BROWN) + cityInfo.civInfo.addNotification("Work has started on [$currentConstruction]", Color.BROWN, CityAction(cityInfo.location)) } } diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 03629d9bd1..94bf1ad63d 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -499,14 +499,14 @@ class CivilizationInfo { return false } - fun addNotification(text: String, location: Vector2?,color: Color) { - val locations = if(location!=null) listOf(location) else emptyList() - addNotification(text, locations, color) + fun addNotification(text: String, location: Vector2?, color: Color) { + val locations = if (location != null) listOf(location) else emptyList() + addNotification(text, color, LocationAction(locations)) } - fun addNotification(text: String, locations: List, color: Color) { - if(playerType==PlayerType.AI) return // no point in lengthening the saved game info if no one will read it - notifications.add(Notification(text, locations,color)) + fun addNotification(text: String, color: Color, action: NotificationAction?=null) { + if (playerType == PlayerType.AI) return // no point in lengthening the saved game info if no one will read it + notifications.add(Notification(text, color, action)) } fun addGreatPerson(greatPerson: String, city:CityInfo = cities.random()) { diff --git a/core/src/com/unciv/logic/civilization/Notification.kt b/core/src/com/unciv/logic/civilization/Notification.kt index 0728c38055..b5d6920aa2 100644 --- a/core/src/com/unciv/logic/civilization/Notification.kt +++ b/core/src/com/unciv/logic/civilization/Notification.kt @@ -2,17 +2,57 @@ package com.unciv.logic.civilization import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.math.Vector2 +import com.unciv.models.gamebasics.GameBasics +import com.unciv.ui.cityscreen.CityScreen +import com.unciv.ui.pickerscreens.TechPickerScreen +import com.unciv.ui.worldscreen.WorldScreen -class Notification { - var text: String = "" - var locations: ArrayList = ArrayList() - var color: Color = Color.BLACK +/** + * [action] is not realized as lambda, as it would be too easy to introduce references to objects + * there that should not be serialized to the saved game. + */ +open class Notification( + // default parameters necessary for json deserialization + var text: String = "", + var color: Color = Color.BLACK, + var action: NotificationAction? = null +) - internal constructor() // Needed for json deserialization +/** defines what to do if the user clicks on a notification */ +interface NotificationAction { + fun execute(worldScreen: WorldScreen) +} - constructor(text: String, locations: List = ArrayList(), color: Color) { - this.text = text - this.locations = ArrayList(locations) - this.color = color +/** cycle through locations */ +data class LocationAction(var locations: ArrayList = ArrayList()) : NotificationAction { + + constructor(locations: List): this(ArrayList(locations)) + + override fun execute(worldScreen: WorldScreen) { + if (locations.isNotEmpty()) { + var index = locations.indexOf(worldScreen.tileMapHolder.selectedTile?.position) + index = ++index % locations.size // cycle through locations + worldScreen.tileMapHolder.setCenterPosition(locations[index]) + } + } + +} + +/** show tech screen */ +class TechAction(val techName: String = "") : NotificationAction { + override fun execute(worldScreen: WorldScreen) { + val tech = GameBasics.Technologies[techName] + worldScreen.game.screen = TechPickerScreen(worldScreen.currentPlayerCiv, tech) } } + +/** enter city */ +data class CityAction(val city: Vector2 = Vector2.Zero): NotificationAction { + + override fun execute(worldScreen: WorldScreen) { + worldScreen.tileMapHolder.tileMap[city].getCity()?.let { + worldScreen.game.screen = CityScreen(it) + } + } + +} \ No newline at end of file diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index 3b7e10e1e8..f356231495 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -42,7 +42,11 @@ class TechManager { return (GameBasics.Technologies[techName]!!.cost * civInfo.getDifficulty().researchCostModifier).toInt() } - fun currentTechnology(): String? { + fun currentTechnology(): Technology? = currentTechnologyName()?.let { + GameBasics.Technologies[it] + } + + fun currentTechnologyName(): String? { if (techsToResearch.isEmpty()) return null else return techsToResearch[0] } @@ -91,7 +95,7 @@ class TechManager { } fun nextTurn(scienceForNewTurn: Int) { - val currentTechnology = currentTechnology() + val currentTechnology = currentTechnologyName() if (currentTechnology == null) return techsInProgress[currentTechnology] = researchOfTech(currentTechnology) + scienceForNewTurn if (techsInProgress[currentTechnology]!! < costOfTech(currentTechnology)) @@ -121,7 +125,7 @@ class TechManager { researchedTechUniques = researchedTechUniques.withItem(unique) updateTransientBooleans() - civInfo.addNotification("Research of [$techName] has completed!", null, Color.BLUE) + civInfo.addNotification("Research of [$techName] has completed!", Color.BLUE, TechAction(techName)) val currentEra = civInfo.getEra() if (previousEra < currentEra) { diff --git a/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt index 8bcf890d88..28db43bd32 100644 --- a/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt @@ -15,6 +15,7 @@ open class PickerScreen : CameraStageBaseScreen() { protected var topTable: Table var bottomTable:Table = Table() internal var splitPane: SplitPane + protected var scrollPane: ScrollPane init { bottomTable.add(closeButton).width(stage.width / 4) @@ -33,7 +34,7 @@ open class PickerScreen : CameraStageBaseScreen() { bottomTable.align(Align.center) topTable = Table() - val scrollPane = ScrollPane(topTable) + scrollPane = ScrollPane(topTable) scrollPane.setSize(stage.width, stage.height * screenSplit) diff --git a/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt index 589997e2b3..70238a6e3d 100644 --- a/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt @@ -1,5 +1,6 @@ package com.unciv.ui.pickerscreens +import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.ui.Label import com.unciv.UnCivGame @@ -13,7 +14,7 @@ import java.util.* import kotlin.collections.HashSet -class TechPickerScreen(internal val civInfo: CivilizationInfo) : PickerScreen() { +class TechPickerScreen(internal val civInfo: CivilizationInfo, centerOnTech: Technology? = null) : PickerScreen() { private var techNameToButton = HashMap() private var isFreeTechPick: Boolean = false @@ -101,6 +102,21 @@ class TechPickerScreen(internal val civInfo: CivilizationInfo) : PickerScreen() } displayTutorials("TechPickerScreen") + + // per default show current/recent technology, + // and possibly select it to show description, + // which is very helpful when just discovered and clicking the notification + val tech = centerOnTech ?: civInfo.tech.currentTechnology() + if (tech != null) { + // select only if there it doesn't mess up tempTechsToResearch + if (civInfo.tech.isResearched(tech.name) || civInfo.tech.techsToResearch.size <= 1) { + selectTechnology(tech, true) + } + else { + centerOnTechnology(tech) + } + } + } private fun setButtonsInfo() { @@ -131,9 +147,19 @@ class TechPickerScreen(internal val civInfo: CivilizationInfo) : PickerScreen() } } - private fun selectTechnology(tech: Technology?) { + private fun selectTechnology(tech: Technology?, center: Boolean = false) { + selectedTech = tech - descriptionLabel.setText(tech!!.description) + descriptionLabel.setText(tech?.description) + + if(tech==null) + return + + // center on technology + if (center) { + centerOnTechnology(tech) + } + if (isFreeTechPick) { selectTechnologyForFreeTech(tech) return @@ -153,6 +179,14 @@ class TechPickerScreen(internal val civInfo: CivilizationInfo) : PickerScreen() setButtonsInfo() } + private fun centerOnTechnology(tech: Technology) { + Gdx.app.postRunnable { + techNameToButton[tech.name]?.let { + scrollPane.scrollTo(it.x, it.y, it.width, it.height, true, true) + scrollPane.updateVisualScroll() + } + } + } private fun selectTechnologyForFreeTech(tech: Technology) { diff --git a/core/src/com/unciv/ui/worldscreen/NotificationsScroll.kt b/core/src/com/unciv/ui/worldscreen/NotificationsScroll.kt index 88833d065a..af29fa6b34 100644 --- a/core/src/com/unciv/ui/worldscreen/NotificationsScroll.kt +++ b/core/src/com/unciv/ui/worldscreen/NotificationsScroll.kt @@ -9,6 +9,9 @@ import com.unciv.ui.utils.* import kotlin.math.min class NotificationsScroll(internal val worldScreen: WorldScreen) : ScrollPane(null) { + + var notificationsHash : Int = 0 + private var notificationsTable = Table() init { @@ -17,10 +20,15 @@ class NotificationsScroll(internal val worldScreen: WorldScreen) : ScrollPane(nu } internal fun update(notifications: MutableList) { + + // no news? - keep our list as it is, especially don't reset scroll position + if(notificationsHash == notifications.hashCode()) + return + notificationsHash = notifications.hashCode() + notificationsTable.clearChildren() - for (notification in notifications.toList()) { // tolist to avoid concurrecy problems - val label = notification.text.toLabel().setFontColor(Color.BLACK) - .setFontSize(14) + for (notification in notifications.toList()) { // toList to avoid concurrency problems + val label = notification.text.toLabel().setFontColor(Color.BLACK).setFontSize(14) val listItem = Table() listItem.add(ImageGetter.getCircle() @@ -34,11 +42,7 @@ class NotificationsScroll(internal val worldScreen: WorldScreen) : ScrollPane(nu add(listItem).pad(3f) touchable = Touchable.enabled onClick { - if (notification.locations.isNotEmpty()) { - var index = notification.locations.indexOf(worldScreen.tileMapHolder.selectedTile?.position) - index = ++index % notification.locations.size // cycle through locations - worldScreen.tileMapHolder.setCenterPosition(notification.locations[index]) - } + notification.action?.execute(worldScreen) } } diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index 543f2f4fdb..d7bf60a88b 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -207,7 +207,7 @@ class WorldScreen : CameraStageBaseScreen() { techButton.add(buttonPic) } else { - val currentTech = civInfo.tech.currentTechnology()!! + val currentTech = civInfo.tech.currentTechnologyName()!! val innerButton = TechButton(currentTech,civInfo.tech) innerButton.color = colorFromRGB(7, 46, 43) techButton.add(innerButton)