Many UI updates are now done on the clone, so we won't get concurrency exceptions

This commit is contained in:
Yair Morgenstern 2018-08-25 23:15:40 +03:00
parent ce00fd43e0
commit 86464ccdb0
8 changed files with 73 additions and 47 deletions

View file

@ -41,8 +41,8 @@ class NewGameScreen: PickerScreen(){
private fun getUniqueLabel(nation: Nation): CharSequence? {
for (building in GameBasics.Buildings.values)
if (building.uniqueTo == nation.name) {
var text = building.name + " - {replaces} " + building.replaces + "\n"
val originalBuilding = GameBasics.Buildings[building.replaces]!!
var text = building.name.tr() + " - {replaces} " + building.replaces!!.tr() + "\n"
val originalBuilding = GameBasics.Buildings[building.replaces!!]!!
val originalBuildingStatMap = originalBuilding.toHashMap()
for (stat in building.toHashMap())
if (stat.value != originalBuildingStatMap[stat.key])
@ -54,8 +54,8 @@ class NewGameScreen: PickerScreen(){
for (unit in GameBasics.Units.values)
if (unit.uniqueTo == nation.name) {
var text = unit.name + " - {replaces} " + unit.replaces + "\n"
val originalUnit = GameBasics.Units[unit.replaces]!!
var text = unit.name.tr() + " - {replaces} " + unit.replaces!!.tr() + "\n"
val originalUnit = GameBasics.Units[unit.replaces!!]!!
if (unit.strength != originalUnit.strength)
text += "{Combat strength} " + unit.strength + " vs " + originalUnit.strength + "\n"
if (unit.rangedStrength!= originalUnit.rangedStrength)

View file

@ -8,6 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.unciv.UnCivGame
import com.unciv.logic.HexMath
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.TileInfo
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.addClickListener
@ -67,11 +68,10 @@ class Minimap(val tileMapHolder: TileMapHolder) : ScrollPane(null){
return true
}
})
update()
}
fun update(){
val exploredTiles = tileMapHolder.worldScreen.civInfo.exploredTiles
fun update(cloneCivilization: CivilizationInfo) {
val exploredTiles = cloneCivilization.exploredTiles
for(tileInfo in tileMapHolder.tileMap.values) {
val RGB = tileInfo.getBaseTerrain().RGB!!
val hex = tileImages[tileInfo]!!

View file

@ -8,14 +8,14 @@ import com.unciv.logic.civilization.Notification
import com.unciv.ui.utils.*
import kotlin.math.min
class NotificationsScroll(private val notifications: List<Notification>, internal val worldScreen: WorldScreen) : ScrollPane(null) {
class NotificationsScroll(internal val worldScreen: WorldScreen) : ScrollPane(null) {
private var notificationsTable = Table()
init {
widget = notificationsTable
}
internal fun update() {
internal fun update(notifications: MutableList<Notification>) {
notificationsTable.clearChildren()
for (notification in notifications) {
val label = Label(notification.text.tr(), CameraStageBaseScreen.skin).setFontColor(Color.BLACK)

View file

@ -2,6 +2,7 @@ package com.unciv.ui.worldscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.InputEvent
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
@ -15,12 +16,15 @@ import com.unciv.logic.map.TileMap
import com.unciv.models.gamebasics.unit.UnitType
import com.unciv.ui.tilegroups.WorldTileGroup
import com.unciv.ui.utils.addClickListener
import com.unciv.ui.utils.center
import com.unciv.ui.utils.colorFromRGB
class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: TileMap, internal val civInfo: CivilizationInfo) : ScrollPane(null) {
class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: TileMap) : ScrollPane(null) {
internal var selectedTile: TileInfo? = null
val tileGroups = HashMap<TileInfo, WorldTileGroup>()
var overlayActor :Actor?=null
internal fun addTiles() {
val allTiles = Group()
val groupPadding = 300f // This is so that no tile will be stuck "on the side" and be unreachable or difficult to reach
@ -35,7 +39,22 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
group.addClickListener {
worldScreen.displayTutorials("TileClicked")
if(overlayActor!=null) overlayActor!!.remove()
selectedTile = tileInfo
// val selectedUnit = worldScreen.bottomBar.unitTable.selectedUnit
// if(selectedUnit!=null && selectedUnit.getTile()!=tileInfo
// && selectedUnit.canMoveTo(tileInfo) && selectedUnit.movementAlgs().canReach(tileInfo)) {
// val size = 40f
// val moveHereGroup = Group().apply { width = size;height = size; }
// moveHereGroup.addActor(ImageGetter.getImage("OtherIcons/Circle").apply { width = size; height = size })
// moveHereGroup.addActor(ImageGetter.getStatIcon("Movement").apply { width = size / 2; height = size / 2; center(moveHereGroup) })
// if(selectedUnit.currentMovement>0)
// moveHereGroup.addClickListener { selectedUnit.movementAlgs().headTowards(tileInfo);worldScreen.update() }
// else moveHereGroup.color.a=0.5f
// addAboveGroup(group, moveHereGroup).apply { width = size; height = size }
// }
worldScreen.bottomBar.unitTable.tileSelected(tileInfo)
worldScreen.update()
}
@ -69,7 +88,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
setSize(worldScreen.stage.width, worldScreen.stage.height)
addListener(object : ActorGestureListener() {
var lastScale = 1f
internal var lastInitialDistance = 0f
var lastInitialDistance = 0f
override fun zoom(event: InputEvent?, initialDistance: Float, distance: Float) {
if (lastInitialDistance != initialDistance) {
@ -87,14 +106,23 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
layout() // Fit the scroll pane to the contents - otherwise, setScroll won't work!
}
internal fun updateTiles() {
val playerViewableTiles = civInfo.getViewableTiles().toHashSet()
private fun addAboveGroup(group:Group, actor: Actor) {
actor.center(group)
actor.x+=group.x
actor.y+=group.y+group.height
group.parent.addActor(actor)
actor.toFront()
overlayActor=actor
}
internal fun updateTiles(civInfo: CivilizationInfo) {
val playerViewableTilePositions = civInfo.getViewableTiles().map { it.position }.toHashSet()
for (WG in tileGroups.values){
WG.update(playerViewableTiles.contains(WG.tileInfo))
WG.update(playerViewableTilePositions.contains(WG.tileInfo.position))
val unitsInTile = WG.tileInfo.getUnits()
if((playerViewableTiles.contains(WG.tileInfo) || UnCivGame.Current.viewEntireMapForDebug)
&& unitsInTile.isNotEmpty() && unitsInTile.first().civInfo!=civInfo)
if((playerViewableTilePositions.contains(WG.tileInfo.position) || UnCivGame.Current.viewEntireMapForDebug)
&& unitsInTile.isNotEmpty() && !unitsInTile.first().civInfo.isPlayerCivilization())
WG.showCircle(Color.RED)
} // Display ALL viewable enemies with a red circle so that users don't need to go "hunting" for enemy units
@ -116,7 +144,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
for (tile in attackableTiles.filter {
it.getUnits().isNotEmpty()
&& it.getUnits().first().owner != unit.owner
&& (playerViewableTiles.contains(it) || UnCivGame.Current.viewEntireMapForDebug)}) {
&& (playerViewableTilePositions.contains(it.position) || UnCivGame.Current.viewEntireMapForDebug)}) {
if(unit.baseUnit().unitType== UnitType.Civilian) tileGroups[tile]!!.hideCircle()
else {
tileGroups[tile]!!.showCircle(colorFromRGB(237, 41, 57))

View file

@ -22,7 +22,7 @@ class WorldScreen : CameraStageBaseScreen() {
val gameInfo = game.gameInfo
internal val civInfo: CivilizationInfo = gameInfo.getPlayerCivilization()
val tileMapHolder: TileMapHolder = TileMapHolder(this, gameInfo.tileMap, civInfo)
val tileMapHolder: TileMapHolder = TileMapHolder(this, gameInfo.tileMap)
val minimap = Minimap(tileMapHolder)
internal var buttonScale = 0.9f
@ -42,7 +42,7 @@ class WorldScreen : CameraStageBaseScreen() {
nextTurnButton.setPosition(stage.width - nextTurnButton.width - 10f,
topBar.y - nextTurnButton.height - 10f)
notificationsScroll = NotificationsScroll(gameInfo.notifications, this)
notificationsScroll = NotificationsScroll(this)
notificationsScroll.width = stage.width/3
minimap.setSize(stage.width/5,stage.height/5)
minimap.x = stage.width - minimap.width
@ -83,8 +83,11 @@ class WorldScreen : CameraStageBaseScreen() {
fun update() {
// many of the display functions will be called with the game clone and not the actual game,
// because that's guaranteed to stay the exact same and so we won't get any concurrent modification exceptions
val gameClone = gameInfo.clone()
// so we don't get a concurrent modification exception, we clone the entire game (yes really, it's actually very fast)
val cloneCivilization = gameClone.getPlayerCivilization()
kotlin.concurrent.thread {
civInfo.happiness = gameClone.getPlayerCivilization().getHappinessForNextTurn().values.sum().toInt()
}
@ -101,27 +104,27 @@ class WorldScreen : CameraStageBaseScreen() {
}
if(!UnCivGame.Current.settings.tutorialsShown.contains("EnemyCityNeedsConqueringWithMeleeUnit")) {
for (enemyCity in civInfo.diplomacy.values.filter { it.diplomaticStatus == DiplomaticStatus.War }
for (enemyCity in cloneCivilization.diplomacy.values.filter { it.diplomaticStatus == DiplomaticStatus.War }
.map { it.otherCiv() }.flatMap { it.cities }) {
if (enemyCity.health == 1 && enemyCity.getCenterTile().getTilesInDistance(2)
.any { it.getUnits().any { unit -> unit.civInfo == civInfo } })
.any { it.getUnits().any { unit -> unit.civInfo == cloneCivilization } })
displayTutorials("EnemyCityNeedsConqueringWithMeleeUnit")
}
}
updateTechButton()
updateDiplomacyButton()
updateTechButton(cloneCivilization)
updateDiplomacyButton(cloneCivilization)
bottomBar.update(tileMapHolder.selectedTile) // has to come before tilemapholder update because the tilemapholder actions depend on the selected unit!
minimap.update()
minimap.update(cloneCivilization)
minimap.y = bottomBar.height
unitActionsTable.update(bottomBar.unitTable.selectedUnit)
unitActionsTable.y = bottomBar.height
tileMapHolder.updateTiles()
topBar.update()
notificationsScroll.update()
tileMapHolder.updateTiles(cloneCivilization)
topBar.update(cloneCivilization)
notificationsScroll.update(gameClone.notifications)
notificationsScroll.width = stage.width/3
notificationsScroll.setPosition(stage.width - notificationsScroll.width - 5f,
nextTurnButton.y - notificationsScroll.height - 5f)
@ -130,7 +133,7 @@ class WorldScreen : CameraStageBaseScreen() {
else if(civInfo.greatPeople.freeGreatPeople>0) game.screen = GreatPersonPickerScreen()
}
private fun updateDiplomacyButton() {
private fun updateDiplomacyButton(civInfo: CivilizationInfo) {
diplomacyButtonWrapper.clear()
if(civInfo.diplomacy.values.map { it.otherCiv() }
.filterNot { it.isDefeated() || it.isPlayerCivilization() || it.isBarbarianCivilization() }
@ -144,7 +147,7 @@ class WorldScreen : CameraStageBaseScreen() {
diplomacyButtonWrapper.y = techButton.y -20 - diplomacyButtonWrapper.height
}
private fun updateTechButton() {
private fun updateTechButton(civInfo: CivilizationInfo) {
techButton.isVisible = civInfo.cities.isNotEmpty()
if (civInfo.tech.currentTechnology() == null)

View file

@ -103,9 +103,7 @@ class WorldScreenTopBar(val screen: WorldScreen) : Table() {
}
internal fun update() {
val civInfo = screen.civInfo
internal fun update(civInfo: CivilizationInfo) {
val revealedStrategicResources = GameBasics.TileResources.values
.filter { it.resourceType == ResourceType.Strategic } // && }
val civResources = civInfo.getCivResources()

View file

@ -18,7 +18,6 @@ class WorldScreenDisplayOptionsTable() : PopupTable(){
val settings = UnCivGame.Current.settings
settings.save()
clear()
val tileMapHolder = UnCivGame.Current.worldScreen.tileMapHolder
if (settings.showWorkedTiles) addButton("{Hide} {worked tiles}") { settings.showWorkedTiles = false; update() }
else addButton("{Show} {worked tiles}") { settings.showWorkedTiles = true; update() }
@ -64,6 +63,6 @@ class WorldScreenDisplayOptionsTable() : PopupTable(){
pack() // Needed to show the background.
center(UnCivGame.Current.worldScreen.stage)
tileMapHolder.updateTiles()
UnCivGame.Current.worldScreen.update()
}
}

View file

@ -2,22 +2,20 @@ package com.unciv.game.desktop;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.tools.texturepacker.TexturePacker;
import com.unciv.UnCivGame;
class DesktopLauncher {
public static void main (String[] arg) {
TexturePacker.Settings settings = new TexturePacker.Settings();
settings.maxWidth = 2048;
settings.maxHeight = 2048;
settings.combineSubdirectories=true;
// This is so they don't look all pixelated
settings.filterMag = Texture.TextureFilter.MipMapLinearLinear;
settings.filterMin = Texture.TextureFilter.MipMapLinearLinear;
TexturePacker.process(settings, "../images", ".", "game");
//
// TexturePacker.Settings settings = new TexturePacker.Settings();
// settings.maxWidth = 2048;
// settings.maxHeight = 2048;
// settings.combineSubdirectories=true;
//
// // This is so they don't look all pixelated
// settings.filterMag = Texture.TextureFilter.MipMapLinearLinear;
// settings.filterMin = Texture.TextureFilter.MipMapLinearLinear;
// TexturePacker.process(settings, "../images", ".", "game");
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
new LwjglApplication(new UnCivGame(), config);