From fe67fda90627e393dad92879392a81b9ede8f4d3 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Thu, 16 Aug 2018 23:33:56 +0300 Subject: [PATCH] Performance improvement - Moved all autosaving to save a *clone* of the current game in another thread, thus allowing the user to continue his game without having to wait for the game to save --- core/src/com/unciv/logic/GameInfo.kt | 9 +++++++ core/src/com/unciv/logic/GameSaver.kt | 2 +- .../unciv/logic/automation/UnitAutomation.kt | 5 ++-- .../com/unciv/logic/city/CityConstructions.kt | 10 +++++++- .../unciv/logic/city/CityExpansionManager.kt | 8 ++++++- core/src/com/unciv/logic/city/CityInfo.kt | 19 ++++++++++++--- core/src/com/unciv/logic/city/CityStats.kt | 8 +++---- .../com/unciv/logic/city/PopulationManager.kt | 9 ++++++- .../logic/civilization/CivilizationInfo.kt | 21 +++++++++++++--- .../logic/civilization/DiplomacyManager.kt | 7 ++++++ .../logic/civilization/GoldenAgeManager.kt | 12 ++++++++-- .../logic/civilization/GreatPersonManager.kt | 10 +++++++- .../unciv/logic/civilization/PolicyManager.kt | 12 +++++++++- .../unciv/logic/civilization/TechManager.kt | 9 +++++++ core/src/com/unciv/logic/map/MapUnit.kt | 24 ++++++++++++++----- core/src/com/unciv/logic/map/TileInfo.kt | 21 ++++++++++++---- core/src/com/unciv/logic/map/TileMap.kt | 6 +++++ .../unciv/logic/map/UnitMovementAlgorithms.kt | 2 +- .../src/com/unciv/logic/map/UnitPromotions.kt | 8 +++++++ core/src/com/unciv/logic/trade/Trade.kt | 7 ++++++ .../com/unciv/logic/trade/TradeOffersList.kt | 1 + .../com/unciv/models/gamebasics/Building.kt | 2 +- .../models/gamebasics/tile/TileImprovement.kt | 2 +- .../unciv/models/gamebasics/unit/BaseUnit.kt | 1 - core/src/com/unciv/ui/EmpireOverviewScreen.kt | 2 +- core/src/com/unciv/ui/VictoryScreen.kt | 2 +- .../com/unciv/ui/worldscreen/WorldScreen.kt | 17 +++++++++---- .../unciv/ui/worldscreen/unit/UnitActions.kt | 2 +- 28 files changed, 196 insertions(+), 42 deletions(-) diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 3163ade2d8..35ed11a493 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -98,5 +98,14 @@ class GameInfo { for (tile in city.getTiles()) tilesToCities.put(tile,city) } } + + fun clone():GameInfo{ + val toReturn = GameInfo() + toReturn.civilizations.addAll(civilizations.map { it.clone() }) + toReturn.tileMap=tileMap.clone() + toReturn.notifications.addAll(notifications) + toReturn.turns=turns + return toReturn + } } diff --git a/core/src/com/unciv/logic/GameSaver.kt b/core/src/com/unciv/logic/GameSaver.kt index 8814fa3611..293ce12aca 100644 --- a/core/src/com/unciv/logic/GameSaver.kt +++ b/core/src/com/unciv/logic/GameSaver.kt @@ -23,7 +23,7 @@ class GameSaver { } fun loadGame(GameName: String) : GameInfo { - val game = Json().fromJson(GameInfo::class.java, getSave(GameName).readString()) + val game = UnCivGame.Current.json.fromJson(GameInfo::class.java, getSave(GameName).readString()) game.setTransients() return game } diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 96c68fab9f..e30411447f 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -129,6 +129,7 @@ class UnitAutomation{ } private fun tryAdvanceTowardsCloseEnemy(unit: MapUnit): Boolean { + // this can be sped up if we check each layer separately var closeEnemies = unit.getTile().getTilesInDistance(5) .filter{ containsAttackableEnemy(it, unit.civInfo) && unit.movementAlgs().canReach(it)} if(unit.baseUnit().unitType.isRanged()) @@ -171,8 +172,8 @@ class UnitAutomation{ private fun tryHeadTowardsEnemyCity(unit: MapUnit): Boolean { if(unit.civInfo.cities.isEmpty()) return false - var enemyCities = unit.civInfo.exploredTiles.map { unit.civInfo.gameInfo.tileMap[it] } - .filter { it.isCityCenter() && it.getOwner() != unit.civInfo } + var enemyCities = unit.civInfo.gameInfo.civilizations.filter { unit.civInfo.isAtWarWith(it) } + .flatMap { it.cities }.filter { it.location in unit.civInfo.exploredTiles }.map { it.getCenterTile() } if(unit.baseUnit().unitType.isRanged()) enemyCities = enemyCities.filterNot { it.getCity()!!.health==1 } diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index bdeac52e5d..9a855c6c69 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -29,7 +29,7 @@ class CityConstructions { val stats = Stats() for (building in getBuiltBuildings()) stats.add(building.getStats(cityInfo.civInfo.policies.adoptedPolicies)) - stats.science += (cityInfo.buildingUniques.count({ it == "+1 Science Per 2 Population" }) * cityInfo.population.population / 2).toFloat() + stats.science += (cityInfo.getBuildingUniques().count({ it == "+1 Science Per 2 Population" }) * cityInfo.population.population / 2).toFloat() return stats } @@ -170,4 +170,12 @@ class CityConstructions { Automation().chooseNextConstruction(this) } + fun clone(): CityConstructions { + val toReturn = CityConstructions() + toReturn.currentConstruction=currentConstruction + toReturn.builtBuildings.addAll(builtBuildings) + toReturn.inProgressConstructions.putAll(inProgressConstructions) + return toReturn + } + } // for json parsing, we need to have a default constructor \ No newline at end of file diff --git a/core/src/com/unciv/logic/city/CityExpansionManager.kt b/core/src/com/unciv/logic/city/CityExpansionManager.kt index a689c8503f..5c99ab4128 100644 --- a/core/src/com/unciv/logic/city/CityExpansionManager.kt +++ b/core/src/com/unciv/logic/city/CityExpansionManager.kt @@ -24,7 +24,7 @@ class CityExpansionManager { fun getCultureToNextTile(): Int { val numTilesClaimed = cityInfo.tiles.size - 7 var cultureToNextTile = 6 * Math.pow(numTilesClaimed + 1.4813, 1.3) - if (cityInfo.civInfo.buildingUniques.contains("Cost of acquiring new tiles reduced by 25%")) cultureToNextTile *= 0.75 //Speciality of Angkor Wat + if (cityInfo.civInfo.getBuildingUniques().contains("Cost of acquiring new tiles reduced by 25%")) cultureToNextTile *= 0.75 //Speciality of Angkor Wat if (cityInfo.civInfo.policies.isAdopted("Tradition")) cultureToNextTile *= 0.75 return Math.round(cultureToNextTile).toInt() } @@ -69,4 +69,10 @@ class CityExpansionManager { } } + fun clone(): CityExpansionManager { + val toReturn = CityExpansionManager() + toReturn.cultureStored=cultureStored + return toReturn + } + } \ No newline at end of file diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 9bc4cde4fb..9ebf85b9ed 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -58,8 +58,7 @@ class CityInfo { return cityResources } - val buildingUniques: List - get() = cityConstructions.getBuiltBuildings().filter { it.unique!=null }.map { it.unique } + fun getBuildingUniques(): List = cityConstructions.getBuiltBuildings().filter { it.unique != null }.map { it.unique } fun getGreatPersonPoints(): Stats { var greatPersonPoints = population.getSpecialists().times(3f) @@ -68,7 +67,7 @@ class CityInfo { if (building.greatPersonPoints != null) greatPersonPoints.add(building.greatPersonPoints!!) - if (civInfo.buildingUniques.contains("+33% great person generation in all cities")) + if (civInfo.getBuildingUniques().contains("+33% great person generation in all cities")) greatPersonPoints = greatPersonPoints.times(1.33f) if (civInfo.policies.isAdopted("Entrepreneurship")) greatPersonPoints.gold *= 1.25f @@ -173,4 +172,18 @@ class CityInfo { } override fun toString(): String {return name} // for debug + + fun clone(): CityInfo { + val toReturn = CityInfo() + toReturn.population = population.clone() + toReturn.health=health + toReturn.name=name + toReturn.tiles.addAll(tiles) + toReturn.workedTiles.addAll(workedTiles) + toReturn.cityConstructions=cityConstructions.clone() + toReturn.expansion = expansion.clone() + toReturn.isBeingRazed=isBeingRazed + toReturn.location=location + return toReturn + } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/city/CityStats.kt b/core/src/com/unciv/logic/city/CityStats.kt index 829d3bdf9d..a7b359c359 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -32,7 +32,7 @@ class CityStats { val civInfo = cityInfo.civInfo var goldFromTradeRoute = civInfo.getCapital().population.population * 0.15 + cityInfo.population.population * 1.1 - 1 // Calculated by http://civilization.wikia.com/wiki/Trade_route_(Civ5) if (civInfo.policies.isAdopted("Trade Unions")) goldFromTradeRoute += 2.0 - if (civInfo.buildingUniques.contains("Gold from all trade routes +25%")) goldFromTradeRoute *= 1.25 // Machu Pichu speciality + if (civInfo.getBuildingUniques().contains("Gold from all trade routes +25%")) goldFromTradeRoute *= 1.25 // Machu Pichu speciality stats.gold += goldFromTradeRoute.toFloat() } return stats @@ -46,7 +46,7 @@ class CityStats { "Gold" -> stats.gold += production / 4 "Science" -> { var scienceProduced = production / 4 - if (cityInfo.civInfo.buildingUniques.contains("ScienceConversionIncrease")) scienceProduced *= 1.33f + if (cityInfo.civInfo.getBuildingUniques().contains("ScienceConversionIncrease")) scienceProduced *= 1.33f if (cityInfo.civInfo.policies.isAdopted("Rationalism")) scienceProduced *= 1.33f stats.science += scienceProduced } @@ -109,7 +109,7 @@ class CityStats { var unhappinessFromCitizens = cityInfo.population.population.toFloat() if (civInfo.policies.isAdopted("Democracy")) unhappinessFromCitizens -= cityInfo.population.getNumberOfSpecialists() * 0.5f - if (civInfo.buildingUniques.contains("Unhappiness from population decreased by 10%")) + if (civInfo.getBuildingUniques().contains("Unhappiness from population decreased by 10%")) unhappinessFromCitizens *= 0.9f if (civInfo.policies.isAdopted("Meritocracy")) unhappinessFromCitizens *= 0.95f @@ -181,7 +181,7 @@ class CityStats { private fun getStatPercentBonusesFromWonders(): Stats { val stats = Stats() - val civUniques = cityInfo.civInfo.buildingUniques + val civUniques = cityInfo.civInfo.getBuildingUniques() if (civUniques.contains("Culture in all cities increased by 25%")) stats.culture += 25f return stats } diff --git a/core/src/com/unciv/logic/city/PopulationManager.kt b/core/src/com/unciv/logic/city/PopulationManager.kt index 11af49b124..19cac08681 100644 --- a/core/src/com/unciv/logic/city/PopulationManager.kt +++ b/core/src/com/unciv/logic/city/PopulationManager.kt @@ -61,7 +61,7 @@ class PopulationManager { // growth! { foodStored -= getFoodToNextPopulation() - if (cityInfo.buildingUniques.contains("40% of food is carried over after a new citizen is born")) foodStored += (0.4f * getFoodToNextPopulation()).toInt() // Aqueduct special + if (cityInfo.getBuildingUniques().contains("40% of food is carried over after a new citizen is born")) foodStored += (0.4f * getFoodToNextPopulation()).toInt() // Aqueduct special population++ autoAssignPopulation() cityInfo.civInfo.addNotification(cityInfo.name + " {has grown}!", cityInfo.location, Color.GREEN) @@ -89,4 +89,11 @@ class PopulationManager { } } + fun clone(): PopulationManager { + val toReturn = PopulationManager() + toReturn.population=population + toReturn.foodStored=foodStored + return toReturn + } + } \ No newline at end of file diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 322ddbe60f..dba36af5fa 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -136,7 +136,7 @@ class CivilizationInfo { } } - if (buildingUniques.contains("Provides 1 happiness per social policy")) + if (getBuildingUniques().contains("Provides 1 happiness per social policy")) statMap["Policies"] = policies.getAdoptedPolicies().count { !it.endsWith("Complete") }.toFloat() return statMap @@ -151,8 +151,7 @@ class CivilizationInfo { return civResources } - val buildingUniques: List - get() = cities.flatMap{ it.cityConstructions.getBuiltBuildings().map { it.unique }.filterNotNull() }.distinct() + fun getBuildingUniques(): List = cities.flatMap { it.cityConstructions.getBuiltBuildings().map { it.unique }.filterNotNull() }.distinct() constructor() @@ -305,6 +304,22 @@ class CivilizationInfo { if(isAtWarWith(otherCiv)) return true return false } + + fun clone(): CivilizationInfo { + val toReturn = CivilizationInfo() + toReturn.exploredTiles=exploredTiles.toHashSet() + toReturn.diplomacy.putAll(diplomacy.values.map { it.clone() }.associateBy { it.otherCivName }) + toReturn.cities.addAll(cities.map { it.clone() }) + toReturn.tech = tech.clone() + toReturn.difficulty=difficulty + toReturn.policies = policies.clone() + toReturn.happiness=happiness + toReturn.greatPeople=greatPeople.clone() + toReturn.gold = gold + toReturn.goldenAges = goldenAges.clone() + toReturn.civName=civName + return toReturn + } } diff --git a/core/src/com/unciv/logic/civilization/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/DiplomacyManager.kt index 875b12c4de..593e429574 100644 --- a/core/src/com/unciv/logic/civilization/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/DiplomacyManager.kt @@ -94,4 +94,11 @@ class DiplomacyManager() { } fun canDeclareWar() = turnsToPeaceTreaty()==0 + fun clone(): DiplomacyManager { + val toReturn = DiplomacyManager() + toReturn.otherCivName=otherCivName + toReturn.diplomaticStatus=diplomaticStatus + toReturn.trades.addAll(trades.map { it.clone() }) + return toReturn + } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt b/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt index bfa36ba07e..9ae53c4b1f 100644 --- a/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt +++ b/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt @@ -2,7 +2,7 @@ package com.unciv.logic.civilization import com.badlogic.gdx.graphics.Color -class GoldenAgeManager { +class GoldenAgeManager{ @Transient lateinit var civInfo: CivilizationInfo @@ -18,7 +18,7 @@ class GoldenAgeManager { fun enterGoldenAge() { var turnsToGoldenAge = 10.0 - if (civInfo.buildingUniques.contains("Golden Age length increases +50%")) turnsToGoldenAge *= 1.5 + if (civInfo.getBuildingUniques().contains("Golden Age length increases +50%")) turnsToGoldenAge *= 1.5 if (civInfo.policies.isAdopted("Freedom Complete")) turnsToGoldenAge *= 1.5 turnsLeftForCurrentGoldenAge += turnsToGoldenAge.toInt() civInfo.addNotification("{You have entered a golden age}!", null, Color.GOLD) @@ -35,4 +35,12 @@ class GoldenAgeManager { numberOfGoldenAges++ } } + + fun clone(): GoldenAgeManager { + val toReturn = GoldenAgeManager() + toReturn.numberOfGoldenAges=numberOfGoldenAges + toReturn.storedHappiness=storedHappiness + toReturn.turnsLeftForCurrentGoldenAge=turnsLeftForCurrentGoldenAge + return toReturn + } } diff --git a/core/src/com/unciv/logic/civilization/GreatPersonManager.kt b/core/src/com/unciv/logic/civilization/GreatPersonManager.kt index 3d67720ab6..b392893efb 100644 --- a/core/src/com/unciv/logic/civilization/GreatPersonManager.kt +++ b/core/src/com/unciv/logic/civilization/GreatPersonManager.kt @@ -4,7 +4,7 @@ import com.unciv.models.stats.Stats class GreatPersonManager { private var pointsForNextGreatPerson = 100 - private val greatPersonPoints = Stats() + private var greatPersonPoints = Stats() var freeGreatPeople=0 fun getNewGreatPerson(): String? { @@ -27,4 +27,12 @@ class GreatPersonManager { greatPersonPoints.add(greatPersonPoints) } + fun clone(): GreatPersonManager { + val toReturn = GreatPersonManager() + toReturn.freeGreatPeople=freeGreatPeople + toReturn.greatPersonPoints=greatPersonPoints.clone() + toReturn.pointsForNextGreatPerson=pointsForNextGreatPerson + return toReturn + } + } \ No newline at end of file diff --git a/core/src/com/unciv/logic/civilization/PolicyManager.kt b/core/src/com/unciv/logic/civilization/PolicyManager.kt index af7fe200cc..b60ad1a8aa 100644 --- a/core/src/com/unciv/logic/civilization/PolicyManager.kt +++ b/core/src/com/unciv/logic/civilization/PolicyManager.kt @@ -23,7 +23,7 @@ class PolicyManager { var cityModifier = 0.3 * (civInfo.cities.size - 1) if (isAdopted("Representation")) cityModifier *= (2 / 3f).toDouble() if (isAdopted("Piety Complete")) baseCost *= 0.9 - if (civInfo.buildingUniques.contains("Culture cost of adopting new Policies reduced by 10%")) baseCost *= 0.9 + if (civInfo.getBuildingUniques().contains("Culture cost of adopting new Policies reduced by 10%")) baseCost *= 0.9 val cost: Int = Math.round(baseCost * (1 + cityModifier)).toInt() return cost - (cost % 5) } @@ -84,4 +84,14 @@ class PolicyManager { shouldOpenPolicyPicker = true } + fun clone(): PolicyManager { + val toReturn = PolicyManager() + toReturn.numberOfAdoptedPolicies=numberOfAdoptedPolicies + toReturn.adoptedPolicies.addAll(adoptedPolicies) + toReturn.freePolicies=freePolicies + toReturn.shouldOpenPolicyPicker=shouldOpenPolicyPicker + toReturn.storedCulture=storedCulture + return toReturn + } + } \ 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 81c1e4236f..06b5a0a4f3 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -92,6 +92,15 @@ class TechManager { city.cityConstructions.currentConstruction = currentConstructionUnit.upgradesTo!! } } + + fun clone(): TechManager { + val toReturn = TechManager() + toReturn.techsResearched.addAll(techsResearched) + toReturn.freeTechs=freeTechs + toReturn.techsInProgress.putAll(techsInProgress) + toReturn.techsToResearch.addAll(techsToResearch) + return toReturn + } } diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 9d618cc948..a1b5486041 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -14,7 +14,6 @@ class MapUnit { lateinit var owner: String lateinit var name: String - var maxMovement: Int = 0 var currentMovement: Float = 0f var health:Int = 100 var action: String? = null // work, automation, fortifying, I dunno what. @@ -23,11 +22,12 @@ class MapUnit { @Transient lateinit var baseUnit: BaseUnit fun baseUnit(): BaseUnit = baseUnit - fun getMovementString(): String = DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + maxMovement + fun getMovementString(): String = DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + getMaxMovement() @Transient internal lateinit var currentTile :TileInfo fun getTile(): TileInfo = currentTile + fun getMaxMovement() = baseUnit.movement fun getDistanceToTiles(): HashMap { val tile = getTile() @@ -63,7 +63,7 @@ class MapUnit { private fun doPostTurnAction() { if (name == "Worker" && getTile().improvementInProgress != null) workOnImprovement() - if(currentMovement==maxMovement.toFloat() + if(currentMovement== getMaxMovement().toFloat() && isFortified()){ val currentTurnsFortified = getFortificationTurns() if(currentTurnsFortified<2) action = "Fortify ${currentTurnsFortified+1}" @@ -76,7 +76,7 @@ class MapUnit { if (tile.turnsToImprovement != 0) return when { tile.improvementInProgress!!.startsWith("Remove") -> { - val tileImprovement = tile.tileImprovement + val tileImprovement = tile.getTileImprovement() if(tileImprovement!=null && tileImprovement.terrainsCanBeBuiltOn.contains(tile.terrainFeature) && !tileImprovement.terrainsCanBeBuiltOn.contains(tile.baseTerrain)) { @@ -124,14 +124,14 @@ class MapUnit { fun endTurn() { doPostTurnAction() - if(currentMovement==maxMovement.toFloat() // didn't move this turn + if(currentMovement== getMaxMovement().toFloat() // didn't move this turn || getSpecialAbilities().contains("Unit will heal every turn, even if it performs an action")){ heal() } } fun startTurn(){ - currentMovement = maxMovement.toFloat() + currentMovement = getMaxMovement().toFloat() attacksThisTurn=0 val tileOwner = getTile().getOwner() if(tileOwner!=null && !civInfo.canEnterTiles(tileOwner)) // if an enemy city expanded onto this tile while I was in it @@ -226,4 +226,16 @@ class MapUnit { if(hasUnique("+1 Range")) range++ return range } + + fun clone(): MapUnit { + val toReturn = MapUnit() + toReturn.action=action + toReturn.currentMovement=currentMovement + toReturn.name=name + toReturn.promotions=promotions.clone() + toReturn.health=health + toReturn.attacksThisTurn=attacksThisTurn + toReturn.owner=owner + return toReturn + } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index f256ecfb15..532ad1f0c7 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -43,8 +43,7 @@ open class TileInfo { fun isCityCenter(): Boolean = getCity()?.location == position - val tileImprovement: TileImprovement? - get() = if (improvement == null) null else GameBasics.TileImprovements[improvement!!] + fun getTileImprovement(): TileImprovement? = if (improvement == null) null else GameBasics.TileImprovements[improvement!!] // This is for performance - since we access the neighbors of a tile ALL THE TIME, @@ -103,7 +102,7 @@ open class TileInfo { } } - val improvement = tileImprovement + val improvement = getTileImprovement() if (improvement != null) { if (resource != null && getTileResource().improvement == improvement.name) stats.add(getTileResource().improvementStats!!) // resource-specifc improvement @@ -125,7 +124,7 @@ open class TileInfo { if (stats.production < 0) stats.production = 0f if ("Jungle" == terrainFeature && city != null - && city.buildingUniques.contains("Jungles provide +2 science")) + && city.getBuildingUniques().contains("Jungles provide +2 science")) stats.science += 2f if (stats.gold != 0f && observingCiv.goldenAges.isGoldenAge()) stats.gold++ @@ -211,4 +210,18 @@ open class TileInfo { } fun arialDistanceTo(otherTile:TileInfo) = abs(position.x-otherTile.position.x) + abs(position.y-otherTile.position.y) + fun clone(): TileInfo { + val toReturn = TileInfo() + if(civilianUnit!=null) toReturn.civilianUnit=civilianUnit!!.clone() + if(militaryUnit!=null) toReturn.militaryUnit=militaryUnit!!.clone() + toReturn.improvement=improvement + toReturn.position=position + toReturn.baseTerrain=baseTerrain + toReturn.terrainFeature=terrainFeature + toReturn.improvementInProgress=improvementInProgress + toReturn.resource=resource + toReturn.roadStatus=roadStatus + toReturn.turnsToImprovement=turnsToImprovement + return toReturn + } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index 4edfb5b799..ca4c6dcf10 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -81,4 +81,10 @@ class TileMap { return path } + fun clone(): TileMap { + val toReturn = TileMap() + toReturn.tiles.putAll(tiles.values.map { it.clone() }.associateBy{it.position.toString()}) + return toReturn + } + } \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index f3f25fcfc2..5d84801a4e 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -78,7 +78,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) { while (true) { val newTilesToCheck = ArrayList() val distanceToDestination = HashMap() - val movementThisTurn = if (distance == 1) unit.currentMovement else unit.maxMovement.toFloat() + val movementThisTurn = if (distance == 1) unit.currentMovement else unit.getMaxMovement().toFloat() for (tileToCheck in tilesToCheck) { val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn) for (reachableTile in distanceToTilesThisTurn.keys) { diff --git a/core/src/com/unciv/logic/map/UnitPromotions.kt b/core/src/com/unciv/logic/map/UnitPromotions.kt index 2c2c23e344..56ec574d4b 100644 --- a/core/src/com/unciv/logic/map/UnitPromotions.kt +++ b/core/src/com/unciv/logic/map/UnitPromotions.kt @@ -23,4 +23,12 @@ class UnitPromotions{ .filter { unit.baseUnit().unitType.toString() in it.unitTypes && it.name !in promotions } .filter { it.prerequisites.isEmpty() || it.prerequisites.any { p->p in promotions } } } + + fun clone(): UnitPromotions { + val toReturn = UnitPromotions() + toReturn.XP=XP + toReturn.promotions.addAll(promotions) + toReturn.numberOfPromotions=numberOfPromotions + return toReturn + } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/trade/Trade.kt b/core/src/com/unciv/logic/trade/Trade.kt index eefa0d5595..9f9598061d 100644 --- a/core/src/com/unciv/logic/trade/Trade.kt +++ b/core/src/com/unciv/logic/trade/Trade.kt @@ -24,4 +24,11 @@ class Trade{ return false return true } + + fun clone():Trade{ + val toReturn = Trade() + toReturn.theirOffers.addAll(theirOffers) + toReturn.ourOffers.addAll(ourOffers) + return toReturn + } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/trade/TradeOffersList.kt b/core/src/com/unciv/logic/trade/TradeOffersList.kt index f2ef0d4609..37caaa6e99 100644 --- a/core/src/com/unciv/logic/trade/TradeOffersList.kt +++ b/core/src/com/unciv/logic/trade/TradeOffersList.kt @@ -13,4 +13,5 @@ class TradeOffersList: ArrayList(){ if(equivalentOffer.amount==0) remove(equivalentOffer) return true } + } \ No newline at end of file diff --git a/core/src/com/unciv/models/gamebasics/Building.kt b/core/src/com/unciv/models/gamebasics/Building.kt index d78a481ef1..0547dfc6ad 100644 --- a/core/src/com/unciv/models/gamebasics/Building.kt +++ b/core/src/com/unciv/models/gamebasics/Building.kt @@ -186,7 +186,7 @@ class Building : NamedStats(), IConstruction{ } if ("Spaceship part" == unique) { - if (!civInfo.buildingUniques.contains("Enables construction of Spaceship parts")) return false + if (!civInfo.getBuildingUniques().contains("Enables construction of Spaceship parts")) return false if (civInfo.scienceVictory.unconstructedParts()[name] == 0) return false // Don't need to build any more of these! } return true diff --git a/core/src/com/unciv/models/gamebasics/tile/TileImprovement.kt b/core/src/com/unciv/models/gamebasics/tile/TileImprovement.kt index 26dc2f4d32..50fd1af262 100644 --- a/core/src/com/unciv/models/gamebasics/tile/TileImprovement.kt +++ b/core/src/com/unciv/models/gamebasics/tile/TileImprovement.kt @@ -18,7 +18,7 @@ class TileImprovement : NamedStats(), ICivilopedia { private val turnsToBuild: Int = 0 // This is the base cost. fun getTurnsToBuild(civInfo: CivilizationInfo): Int { var realTurnsToBuild = turnsToBuild.toFloat() - if (civInfo.buildingUniques.contains("Worker construction increased 25%, provides 2 free workers")) + if (civInfo.getBuildingUniques().contains("Worker construction increased 25%, provides 2 free workers")) realTurnsToBuild *= 0.75f if (civInfo.policies.isAdopted("Citizenship")) realTurnsToBuild *= 0.75f diff --git a/core/src/com/unciv/models/gamebasics/unit/BaseUnit.kt b/core/src/com/unciv/models/gamebasics/unit/BaseUnit.kt index 3b6be17dac..d2be30a57d 100644 --- a/core/src/com/unciv/models/gamebasics/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/gamebasics/unit/BaseUnit.kt @@ -76,7 +76,6 @@ class BaseUnit : INamed, IConstruction, ICivilopedia { fun getMapUnit(): MapUnit { val unit = MapUnit() unit.name = name - unit.maxMovement = movement unit.currentMovement = movement.toFloat() unit.setTransients() return unit diff --git a/core/src/com/unciv/ui/EmpireOverviewScreen.kt b/core/src/com/unciv/ui/EmpireOverviewScreen.kt index 9de46c360d..71db0f8ab3 100644 --- a/core/src/com/unciv/ui/EmpireOverviewScreen.kt +++ b/core/src/com/unciv/ui/EmpireOverviewScreen.kt @@ -213,7 +213,7 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){ table.add(unit.name.tr()) if(baseUnit.strength>0) table.add(baseUnit.strength.toString()) else table.add() if(baseUnit.rangedStrength>0) table.add(baseUnit.rangedStrength.toString()) else table.add() - table.add(unit.currentMovement.toString()+"/"+unit.maxMovement) + table.add(unit.currentMovement.toString()+"/"+unit.getMaxMovement()) val closestCity = unit.getTile().getTilesInDistance(3).firstOrNull{it.isCityCenter()} if (closestCity!=null) table.add(closestCity.getCity()!!.name) else table.add() table.row() diff --git a/core/src/com/unciv/ui/VictoryScreen.kt b/core/src/com/unciv/ui/VictoryScreen.kt index b6871d95e7..068eb74772 100644 --- a/core/src/com/unciv/ui/VictoryScreen.kt +++ b/core/src/com/unciv/ui/VictoryScreen.kt @@ -58,7 +58,7 @@ class VictoryScreen : PickerScreen() { fun scienceVictoryColumn():Table{ val t = Table() t.defaults().pad(5f) - t.add(getMilestone("Built Apollo Program",civInfo.buildingUniques.contains("Enables construction of Spaceship parts"))).row() + t.add(getMilestone("Built Apollo Program",civInfo.getBuildingUniques().contains("Enables construction of Spaceship parts"))).row() val scienceVictory = civInfo.scienceVictory diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index 7bcd8606ad..4a6ad29110 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -177,15 +177,23 @@ class WorldScreen : CameraStageBaseScreen() { kotlin.concurrent.thread { try { - game.gameInfo.nextTurn() - GameSaver().saveGame(game.gameInfo, "Autosave") + gameInfo.nextTurn() } catch (ex:Exception){ - UnCivGame.Current.settings.hasCrashedRecently=true - UnCivGame.Current.settings.save() + game.settings.hasCrashedRecently=true + game.settings.save() throw ex } + val gameInfoClone = gameInfo.clone() + kotlin.concurrent.thread { + // the save takes a long time( up to a second!) and we can do it while the player continues his game. + // On the other hand if we alter the game data while it's being serialized we could get a concurrent modification exception. + // So what we do is we clone all the game data and serialize the clone. + GameSaver().saveGame(gameInfoClone, "Autosave") + nextTurnButton.enable() // only enable the user to next turn once we've saved the current one + } + // If we put this BEFORE the save game, then we try to save the game... // but the main thread does other stuff, including showing tutorials which guess what? Changes the game data // BOOM! Exception! @@ -193,7 +201,6 @@ class WorldScreen : CameraStageBaseScreen() { shouldUpdate=true nextTurnButton.setText("Next turn".tr()) - nextTurnButton.enable() Gdx.input.inputProcessor = stage } } diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 9860976779..d2dabb20c7 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -78,7 +78,7 @@ class UnitActions { newunit.currentMovement=0f }, unit.civInfo.gold >= goldCostOfUpgrade - && unit.currentMovement == unit.maxMovement.toFloat() ) + && unit.currentMovement == unit.getMaxMovement().toFloat() ) } }