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

This commit is contained in:
Yair Morgenstern 2018-08-16 23:33:56 +03:00
parent b4fece29e0
commit fe67fda906
28 changed files with 196 additions and 42 deletions

View File

@ -98,5 +98,14 @@ class GameInfo {
for (tile in city.getTiles()) tilesToCities.put(tile,city) 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
}
} }

View File

@ -23,7 +23,7 @@ class GameSaver {
} }
fun loadGame(GameName: String) : GameInfo { 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() game.setTransients()
return game return game
} }

View File

@ -129,6 +129,7 @@ class UnitAutomation{
} }
private fun tryAdvanceTowardsCloseEnemy(unit: MapUnit): Boolean { private fun tryAdvanceTowardsCloseEnemy(unit: MapUnit): Boolean {
// this can be sped up if we check each layer separately
var closeEnemies = unit.getTile().getTilesInDistance(5) var closeEnemies = unit.getTile().getTilesInDistance(5)
.filter{ containsAttackableEnemy(it, unit.civInfo) && unit.movementAlgs().canReach(it)} .filter{ containsAttackableEnemy(it, unit.civInfo) && unit.movementAlgs().canReach(it)}
if(unit.baseUnit().unitType.isRanged()) if(unit.baseUnit().unitType.isRanged())
@ -171,8 +172,8 @@ class UnitAutomation{
private fun tryHeadTowardsEnemyCity(unit: MapUnit): Boolean { private fun tryHeadTowardsEnemyCity(unit: MapUnit): Boolean {
if(unit.civInfo.cities.isEmpty()) return false if(unit.civInfo.cities.isEmpty()) return false
var enemyCities = unit.civInfo.exploredTiles.map { unit.civInfo.gameInfo.tileMap[it] } var enemyCities = unit.civInfo.gameInfo.civilizations.filter { unit.civInfo.isAtWarWith(it) }
.filter { it.isCityCenter() && it.getOwner() != unit.civInfo } .flatMap { it.cities }.filter { it.location in unit.civInfo.exploredTiles }.map { it.getCenterTile() }
if(unit.baseUnit().unitType.isRanged()) if(unit.baseUnit().unitType.isRanged())
enemyCities = enemyCities.filterNot { it.getCity()!!.health==1 } enemyCities = enemyCities.filterNot { it.getCity()!!.health==1 }

View File

@ -29,7 +29,7 @@ class CityConstructions {
val stats = Stats() val stats = Stats()
for (building in getBuiltBuildings()) for (building in getBuiltBuildings())
stats.add(building.getStats(cityInfo.civInfo.policies.adoptedPolicies)) 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 return stats
} }
@ -170,4 +170,12 @@ class CityConstructions {
Automation().chooseNextConstruction(this) 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 } // for json parsing, we need to have a default constructor

View File

@ -24,7 +24,7 @@ class CityExpansionManager {
fun getCultureToNextTile(): Int { fun getCultureToNextTile(): Int {
val numTilesClaimed = cityInfo.tiles.size - 7 val numTilesClaimed = cityInfo.tiles.size - 7
var cultureToNextTile = 6 * Math.pow(numTilesClaimed + 1.4813, 1.3) 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 if (cityInfo.civInfo.policies.isAdopted("Tradition")) cultureToNextTile *= 0.75
return Math.round(cultureToNextTile).toInt() return Math.round(cultureToNextTile).toInt()
} }
@ -69,4 +69,10 @@ class CityExpansionManager {
} }
} }
fun clone(): CityExpansionManager {
val toReturn = CityExpansionManager()
toReturn.cultureStored=cultureStored
return toReturn
}
} }

View File

@ -58,8 +58,7 @@ class CityInfo {
return cityResources return cityResources
} }
val buildingUniques: List<String?> fun getBuildingUniques(): List<String?> = cityConstructions.getBuiltBuildings().filter { it.unique != null }.map { it.unique }
get() = cityConstructions.getBuiltBuildings().filter { it.unique!=null }.map { it.unique }
fun getGreatPersonPoints(): Stats { fun getGreatPersonPoints(): Stats {
var greatPersonPoints = population.getSpecialists().times(3f) var greatPersonPoints = population.getSpecialists().times(3f)
@ -68,7 +67,7 @@ class CityInfo {
if (building.greatPersonPoints != null) if (building.greatPersonPoints != null)
greatPersonPoints.add(building.greatPersonPoints!!) 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) greatPersonPoints = greatPersonPoints.times(1.33f)
if (civInfo.policies.isAdopted("Entrepreneurship")) if (civInfo.policies.isAdopted("Entrepreneurship"))
greatPersonPoints.gold *= 1.25f greatPersonPoints.gold *= 1.25f
@ -173,4 +172,18 @@ class CityInfo {
} }
override fun toString(): String {return name} // for debug 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
}
} }

View File

@ -32,7 +32,7 @@ class CityStats {
val civInfo = cityInfo.civInfo 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) 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.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() stats.gold += goldFromTradeRoute.toFloat()
} }
return stats return stats
@ -46,7 +46,7 @@ class CityStats {
"Gold" -> stats.gold += production / 4 "Gold" -> stats.gold += production / 4
"Science" -> { "Science" -> {
var scienceProduced = production / 4 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 if (cityInfo.civInfo.policies.isAdopted("Rationalism")) scienceProduced *= 1.33f
stats.science += scienceProduced stats.science += scienceProduced
} }
@ -109,7 +109,7 @@ class CityStats {
var unhappinessFromCitizens = cityInfo.population.population.toFloat() var unhappinessFromCitizens = cityInfo.population.population.toFloat()
if (civInfo.policies.isAdopted("Democracy")) if (civInfo.policies.isAdopted("Democracy"))
unhappinessFromCitizens -= cityInfo.population.getNumberOfSpecialists() * 0.5f 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 unhappinessFromCitizens *= 0.9f
if (civInfo.policies.isAdopted("Meritocracy")) if (civInfo.policies.isAdopted("Meritocracy"))
unhappinessFromCitizens *= 0.95f unhappinessFromCitizens *= 0.95f
@ -181,7 +181,7 @@ class CityStats {
private fun getStatPercentBonusesFromWonders(): Stats { private fun getStatPercentBonusesFromWonders(): Stats {
val stats = 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 if (civUniques.contains("Culture in all cities increased by 25%")) stats.culture += 25f
return stats return stats
} }

View File

@ -61,7 +61,7 @@ class PopulationManager {
// growth! // growth!
{ {
foodStored -= getFoodToNextPopulation() 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++ population++
autoAssignPopulation() autoAssignPopulation()
cityInfo.civInfo.addNotification(cityInfo.name + " {has grown}!", cityInfo.location, Color.GREEN) 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
}
} }

View File

@ -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() statMap["Policies"] = policies.getAdoptedPolicies().count { !it.endsWith("Complete") }.toFloat()
return statMap return statMap
@ -151,8 +151,7 @@ class CivilizationInfo {
return civResources return civResources
} }
val buildingUniques: List<String> fun getBuildingUniques(): List<String> = cities.flatMap { it.cityConstructions.getBuiltBuildings().map { it.unique }.filterNotNull() }.distinct()
get() = cities.flatMap{ it.cityConstructions.getBuiltBuildings().map { it.unique }.filterNotNull() }.distinct()
constructor() constructor()
@ -305,6 +304,22 @@ class CivilizationInfo {
if(isAtWarWith(otherCiv)) return true if(isAtWarWith(otherCiv)) return true
return false 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
}
} }

View File

@ -94,4 +94,11 @@ class DiplomacyManager() {
} }
fun canDeclareWar() = turnsToPeaceTreaty()==0 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
}
} }

View File

@ -2,7 +2,7 @@ package com.unciv.logic.civilization
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
class GoldenAgeManager { class GoldenAgeManager{
@Transient @Transient
lateinit var civInfo: CivilizationInfo lateinit var civInfo: CivilizationInfo
@ -18,7 +18,7 @@ class GoldenAgeManager {
fun enterGoldenAge() { fun enterGoldenAge() {
var turnsToGoldenAge = 10.0 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 if (civInfo.policies.isAdopted("Freedom Complete")) turnsToGoldenAge *= 1.5
turnsLeftForCurrentGoldenAge += turnsToGoldenAge.toInt() turnsLeftForCurrentGoldenAge += turnsToGoldenAge.toInt()
civInfo.addNotification("{You have entered a golden age}!", null, Color.GOLD) civInfo.addNotification("{You have entered a golden age}!", null, Color.GOLD)
@ -35,4 +35,12 @@ class GoldenAgeManager {
numberOfGoldenAges++ numberOfGoldenAges++
} }
} }
fun clone(): GoldenAgeManager {
val toReturn = GoldenAgeManager()
toReturn.numberOfGoldenAges=numberOfGoldenAges
toReturn.storedHappiness=storedHappiness
toReturn.turnsLeftForCurrentGoldenAge=turnsLeftForCurrentGoldenAge
return toReturn
}
} }

View File

@ -4,7 +4,7 @@ import com.unciv.models.stats.Stats
class GreatPersonManager { class GreatPersonManager {
private var pointsForNextGreatPerson = 100 private var pointsForNextGreatPerson = 100
private val greatPersonPoints = Stats() private var greatPersonPoints = Stats()
var freeGreatPeople=0 var freeGreatPeople=0
fun getNewGreatPerson(): String? { fun getNewGreatPerson(): String? {
@ -27,4 +27,12 @@ class GreatPersonManager {
greatPersonPoints.add(greatPersonPoints) greatPersonPoints.add(greatPersonPoints)
} }
fun clone(): GreatPersonManager {
val toReturn = GreatPersonManager()
toReturn.freeGreatPeople=freeGreatPeople
toReturn.greatPersonPoints=greatPersonPoints.clone()
toReturn.pointsForNextGreatPerson=pointsForNextGreatPerson
return toReturn
}
} }

View File

@ -23,7 +23,7 @@ class PolicyManager {
var cityModifier = 0.3 * (civInfo.cities.size - 1) var cityModifier = 0.3 * (civInfo.cities.size - 1)
if (isAdopted("Representation")) cityModifier *= (2 / 3f).toDouble() if (isAdopted("Representation")) cityModifier *= (2 / 3f).toDouble()
if (isAdopted("Piety Complete")) baseCost *= 0.9 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() val cost: Int = Math.round(baseCost * (1 + cityModifier)).toInt()
return cost - (cost % 5) return cost - (cost % 5)
} }
@ -84,4 +84,14 @@ class PolicyManager {
shouldOpenPolicyPicker = true 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
}
} }

View File

@ -92,6 +92,15 @@ class TechManager {
city.cityConstructions.currentConstruction = currentConstructionUnit.upgradesTo!! 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
}
} }

View File

@ -14,7 +14,6 @@ class MapUnit {
lateinit var owner: String lateinit var owner: String
lateinit var name: String lateinit var name: String
var maxMovement: Int = 0
var currentMovement: Float = 0f var currentMovement: Float = 0f
var health:Int = 100 var health:Int = 100
var action: String? = null // work, automation, fortifying, I dunno what. var action: String? = null // work, automation, fortifying, I dunno what.
@ -23,11 +22,12 @@ class MapUnit {
@Transient lateinit var baseUnit: BaseUnit @Transient lateinit var baseUnit: BaseUnit
fun baseUnit(): 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 @Transient
internal lateinit var currentTile :TileInfo internal lateinit var currentTile :TileInfo
fun getTile(): TileInfo = currentTile fun getTile(): TileInfo = currentTile
fun getMaxMovement() = baseUnit.movement
fun getDistanceToTiles(): HashMap<TileInfo, Float> { fun getDistanceToTiles(): HashMap<TileInfo, Float> {
val tile = getTile() val tile = getTile()
@ -63,7 +63,7 @@ class MapUnit {
private fun doPostTurnAction() { private fun doPostTurnAction() {
if (name == "Worker" && getTile().improvementInProgress != null) workOnImprovement() if (name == "Worker" && getTile().improvementInProgress != null) workOnImprovement()
if(currentMovement==maxMovement.toFloat() if(currentMovement== getMaxMovement().toFloat()
&& isFortified()){ && isFortified()){
val currentTurnsFortified = getFortificationTurns() val currentTurnsFortified = getFortificationTurns()
if(currentTurnsFortified<2) action = "Fortify ${currentTurnsFortified+1}" if(currentTurnsFortified<2) action = "Fortify ${currentTurnsFortified+1}"
@ -76,7 +76,7 @@ class MapUnit {
if (tile.turnsToImprovement != 0) return if (tile.turnsToImprovement != 0) return
when { when {
tile.improvementInProgress!!.startsWith("Remove") -> { tile.improvementInProgress!!.startsWith("Remove") -> {
val tileImprovement = tile.tileImprovement val tileImprovement = tile.getTileImprovement()
if(tileImprovement!=null if(tileImprovement!=null
&& tileImprovement.terrainsCanBeBuiltOn.contains(tile.terrainFeature) && tileImprovement.terrainsCanBeBuiltOn.contains(tile.terrainFeature)
&& !tileImprovement.terrainsCanBeBuiltOn.contains(tile.baseTerrain)) { && !tileImprovement.terrainsCanBeBuiltOn.contains(tile.baseTerrain)) {
@ -124,14 +124,14 @@ class MapUnit {
fun endTurn() { fun endTurn() {
doPostTurnAction() 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")){ || getSpecialAbilities().contains("Unit will heal every turn, even if it performs an action")){
heal() heal()
} }
} }
fun startTurn(){ fun startTurn(){
currentMovement = maxMovement.toFloat() currentMovement = getMaxMovement().toFloat()
attacksThisTurn=0 attacksThisTurn=0
val tileOwner = getTile().getOwner() val tileOwner = getTile().getOwner()
if(tileOwner!=null && !civInfo.canEnterTiles(tileOwner)) // if an enemy city expanded onto this tile while I was in it 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++ if(hasUnique("+1 Range")) range++
return 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
}
} }

View File

@ -43,8 +43,7 @@ open class TileInfo {
fun isCityCenter(): Boolean = getCity()?.location == position fun isCityCenter(): Boolean = getCity()?.location == position
val tileImprovement: TileImprovement? fun getTileImprovement(): TileImprovement? = if (improvement == null) null else GameBasics.TileImprovements[improvement!!]
get() = if (improvement == null) null else GameBasics.TileImprovements[improvement!!]
// This is for performance - since we access the neighbors of a tile ALL THE TIME, // 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 (improvement != null) {
if (resource != null && getTileResource().improvement == improvement.name) if (resource != null && getTileResource().improvement == improvement.name)
stats.add(getTileResource().improvementStats!!) // resource-specifc improvement stats.add(getTileResource().improvementStats!!) // resource-specifc improvement
@ -125,7 +124,7 @@ open class TileInfo {
if (stats.production < 0) stats.production = 0f if (stats.production < 0) stats.production = 0f
if ("Jungle" == terrainFeature && city != null if ("Jungle" == terrainFeature && city != null
&& city.buildingUniques.contains("Jungles provide +2 science")) && city.getBuildingUniques().contains("Jungles provide +2 science"))
stats.science += 2f stats.science += 2f
if (stats.gold != 0f && observingCiv.goldenAges.isGoldenAge()) if (stats.gold != 0f && observingCiv.goldenAges.isGoldenAge())
stats.gold++ 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 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
}
} }

View File

@ -81,4 +81,10 @@ class TileMap {
return path return path
} }
fun clone(): TileMap {
val toReturn = TileMap()
toReturn.tiles.putAll(tiles.values.map { it.clone() }.associateBy{it.position.toString()})
return toReturn
}
} }

View File

@ -78,7 +78,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
while (true) { while (true) {
val newTilesToCheck = ArrayList<TileInfo>() val newTilesToCheck = ArrayList<TileInfo>()
val distanceToDestination = HashMap<TileInfo, Float>() val distanceToDestination = HashMap<TileInfo, Float>()
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) { for (tileToCheck in tilesToCheck) {
val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn) val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn)
for (reachableTile in distanceToTilesThisTurn.keys) { for (reachableTile in distanceToTilesThisTurn.keys) {

View File

@ -23,4 +23,12 @@ class UnitPromotions{
.filter { unit.baseUnit().unitType.toString() in it.unitTypes && it.name !in promotions } .filter { unit.baseUnit().unitType.toString() in it.unitTypes && it.name !in promotions }
.filter { it.prerequisites.isEmpty() || it.prerequisites.any { p->p 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
}
} }

View File

@ -24,4 +24,11 @@ class Trade{
return false return false
return true return true
} }
fun clone():Trade{
val toReturn = Trade()
toReturn.theirOffers.addAll(theirOffers)
toReturn.ourOffers.addAll(ourOffers)
return toReturn
}
} }

View File

@ -13,4 +13,5 @@ class TradeOffersList: ArrayList<TradeOffer>(){
if(equivalentOffer.amount==0) remove(equivalentOffer) if(equivalentOffer.amount==0) remove(equivalentOffer)
return true return true
} }
} }

View File

@ -186,7 +186,7 @@ class Building : NamedStats(), IConstruction{
} }
if ("Spaceship part" == unique) { 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! if (civInfo.scienceVictory.unconstructedParts()[name] == 0) return false // Don't need to build any more of these!
} }
return true return true

View File

@ -18,7 +18,7 @@ class TileImprovement : NamedStats(), ICivilopedia {
private val turnsToBuild: Int = 0 // This is the base cost. private val turnsToBuild: Int = 0 // This is the base cost.
fun getTurnsToBuild(civInfo: CivilizationInfo): Int { fun getTurnsToBuild(civInfo: CivilizationInfo): Int {
var realTurnsToBuild = turnsToBuild.toFloat() 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 realTurnsToBuild *= 0.75f
if (civInfo.policies.isAdopted("Citizenship")) if (civInfo.policies.isAdopted("Citizenship"))
realTurnsToBuild *= 0.75f realTurnsToBuild *= 0.75f

View File

@ -76,7 +76,6 @@ class BaseUnit : INamed, IConstruction, ICivilopedia {
fun getMapUnit(): MapUnit { fun getMapUnit(): MapUnit {
val unit = MapUnit() val unit = MapUnit()
unit.name = name unit.name = name
unit.maxMovement = movement
unit.currentMovement = movement.toFloat() unit.currentMovement = movement.toFloat()
unit.setTransients() unit.setTransients()
return unit return unit

View File

@ -213,7 +213,7 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){
table.add(unit.name.tr()) table.add(unit.name.tr())
if(baseUnit.strength>0) table.add(baseUnit.strength.toString()) else table.add() if(baseUnit.strength>0) table.add(baseUnit.strength.toString()) else table.add()
if(baseUnit.rangedStrength>0) table.add(baseUnit.rangedStrength.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()} val closestCity = unit.getTile().getTilesInDistance(3).firstOrNull{it.isCityCenter()}
if (closestCity!=null) table.add(closestCity.getCity()!!.name) else table.add() if (closestCity!=null) table.add(closestCity.getCity()!!.name) else table.add()
table.row() table.row()

View File

@ -58,7 +58,7 @@ class VictoryScreen : PickerScreen() {
fun scienceVictoryColumn():Table{ fun scienceVictoryColumn():Table{
val t = Table() val t = Table()
t.defaults().pad(5f) 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 val scienceVictory = civInfo.scienceVictory

View File

@ -177,15 +177,23 @@ class WorldScreen : CameraStageBaseScreen() {
kotlin.concurrent.thread { kotlin.concurrent.thread {
try { try {
game.gameInfo.nextTurn() gameInfo.nextTurn()
GameSaver().saveGame(game.gameInfo, "Autosave")
} }
catch (ex:Exception){ catch (ex:Exception){
UnCivGame.Current.settings.hasCrashedRecently=true game.settings.hasCrashedRecently=true
UnCivGame.Current.settings.save() game.settings.save()
throw ex 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... // 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 // but the main thread does other stuff, including showing tutorials which guess what? Changes the game data
// BOOM! Exception! // BOOM! Exception!
@ -193,7 +201,6 @@ class WorldScreen : CameraStageBaseScreen() {
shouldUpdate=true shouldUpdate=true
nextTurnButton.setText("Next turn".tr()) nextTurnButton.setText("Next turn".tr())
nextTurnButton.enable()
Gdx.input.inputProcessor = stage Gdx.input.inputProcessor = stage
} }
} }

View File

@ -78,7 +78,7 @@ class UnitActions {
newunit.currentMovement=0f newunit.currentMovement=0f
}, },
unit.civInfo.gold >= goldCostOfUpgrade unit.civInfo.gold >= goldCostOfUpgrade
&& unit.currentMovement == unit.maxMovement.toFloat() ) && unit.currentMovement == unit.getMaxMovement().toFloat() )
} }
} }