mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-22 20:49:36 +07:00
Ui improvements (#779)
* Notifications scroll: keep scrolling position on game update if it's still the same list * tech picker shows current/recent technology centered on the screen * using NotificationAction interface to attach various actions to Notifications * tech notifications: center on tech that was discovered
This commit is contained in:
parent
22c45af5d8
commit
95a3a65c34
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Vector2>, 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()) {
|
||||
|
@ -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<Vector2> = 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<Vector2> = ArrayList(), color: Color) {
|
||||
this.text = text
|
||||
this.locations = ArrayList(locations)
|
||||
this.color = color
|
||||
/** cycle through locations */
|
||||
data class LocationAction(var locations: ArrayList<Vector2> = ArrayList()) : NotificationAction {
|
||||
|
||||
constructor(locations: List<Vector2>): 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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<String, TechButton>()
|
||||
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) {
|
||||
|
@ -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<Notification>) {
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user