diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index 0137cf38cb..85396c0d67 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -143,14 +143,14 @@ class Battle(val gameInfo:GameInfo) { } if(city.cityConstructions.isBuilt("Palace")){ - city.cityConstructions.builtBuildings.remove("Palace") + city.cityConstructions.removeBuilding("Palace") if(enemyCiv.isDefeated()) { gameInfo.getPlayerCivilization() .addNotification("The civilization of [${enemyCiv.civName}] has been destroyed!", null, Color.RED) enemyCiv.getCivUnits().forEach { it.destroy() } } else if(enemyCiv.cities.isNotEmpty()){ - enemyCiv.cities.first().cityConstructions.builtBuildings.add("Palace") // relocate palace + enemyCiv.cities.first().cityConstructions.addBuilding("Palace") // relocate palace } } diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index 3df65e9d6f..267d5ea184 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -6,12 +6,15 @@ import com.unciv.models.gamebasics.Building import com.unciv.models.gamebasics.GameBasics import com.unciv.models.stats.Stats import com.unciv.ui.utils.tr +import com.unciv.ui.utils.withItem +import com.unciv.ui.utils.withoutItem import java.util.* +import kotlin.collections.ArrayList class CityConstructions { - @Transient - lateinit var cityInfo: CityInfo + @Transient lateinit var cityInfo: CityInfo + @Transient private var builtBuildingObjects=ArrayList() var builtBuildings = ArrayList() private val inProgressConstructions = HashMap() @@ -89,7 +92,7 @@ class CityConstructions { throw NotBuildingOrUnitException("$constructionName is not a building or a unit!") } - internal fun getBuiltBuildings(): List = builtBuildings.toList().map { GameBasics.Buildings[it]!! } // toList os to avoid concurrency problems + internal fun getBuiltBuildings(): List = builtBuildingObjects // toList os to avoid concurrency problems fun containsBuildingOrEquivalent(building: String): Boolean = isBuilt(building) || getBuiltBuildings().any{it.replaces==building} @@ -113,9 +116,13 @@ class CityConstructions { //endregion //region state changing functions - fun addConstruction(constructionToAdd: Int) { + fun setTransients(){ + builtBuildingObjects = ArrayList(builtBuildings.map { GameBasics.Buildings[it]!! }) + } + + fun addProduction(productionToAdd: Int) { if (!inProgressConstructions.containsKey(currentConstruction)) inProgressConstructions[currentConstruction] = 0 - inProgressConstructions[currentConstruction] = inProgressConstructions[currentConstruction]!! + constructionToAdd + inProgressConstructions[currentConstruction] = inProgressConstructions[currentConstruction]!! + productionToAdd } fun nextTurn(cityStats: Stats) { @@ -136,26 +143,41 @@ class CityConstructions { var productionToAdd = cityStats.production if(!cityInfo.civInfo.isPlayerCivilization()) productionToAdd *= cityInfo.civInfo.gameInfo.getPlayerCivilization().getDifficulty().aiConstructionModifier - addConstruction(Math.round(cityStats.production)) + addProduction(Math.round(cityStats.production)) val productionCost = construction.getProductionCost(cityInfo.civInfo.policies.adoptedPolicies) if (inProgressConstructions[currentConstruction]!! >= productionCost) { - construction.postBuildEvent(this) - inProgressConstructions.remove(currentConstruction) - - if(construction is Building && construction.isWonder && construction.requiredBuildingInAllCities==null) { - val playerCiv = cityInfo.civInfo.gameInfo.getPlayerCivilization() - val builtLocation = if(playerCiv.exploredTiles.contains(cityInfo.location)) cityInfo.name else "a faraway land" - playerCiv.addNotification("[$currentConstruction] has been built in [$builtLocation]", cityInfo.location, Color.BROWN) - } - else - cityInfo.civInfo.addNotification("[$currentConstruction] has been built in [" + cityInfo.name+"]", cityInfo.location, Color.BROWN) - - currentConstruction="" - Automation().chooseNextConstruction(this) + constructionComplete(construction) } } + fun constructionComplete(construction: IConstruction) { + construction.postBuildEvent(this) + inProgressConstructions.remove(currentConstruction) + + if (construction is Building && construction.isWonder && construction.requiredBuildingInAllCities == null) { + val playerCiv = cityInfo.civInfo.gameInfo.getPlayerCivilization() + val builtLocation = if (playerCiv.exploredTiles.contains(cityInfo.location)) cityInfo.name else "a faraway land" + playerCiv.addNotification("[$currentConstruction] has been built in [$builtLocation]", cityInfo.location, Color.BROWN) + } else + cityInfo.civInfo.addNotification("[$currentConstruction] has been built in [" + cityInfo.name + "]", cityInfo.location, Color.BROWN) + + currentConstruction = "" + Automation().chooseNextConstruction(this) + } + + fun addBuilding(buildingName:String){ + val buildingObject = GameBasics.Buildings[buildingName]!! + builtBuildingObjects = builtBuildingObjects.withItem(buildingObject) + builtBuildings.add(buildingName) + } + + fun removeBuilding(buildingName:String){ + val buildingObject = GameBasics.Buildings[buildingName]!! + builtBuildingObjects = builtBuildingObjects.withoutItem(buildingObject) + builtBuildings.remove(buildingName) + } + fun purchaseBuilding(buildingName: String) { cityInfo.civInfo.gold -= getConstruction(buildingName).getGoldCost(cityInfo.civInfo.policies.adoptedPolicies) getConstruction(buildingName).postBuildEvent(this) diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 728c88ce63..82a94a167b 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -54,7 +54,7 @@ class CityInfo { civInfo.addNotification("[$name] has been founded!", cityLocation, Color.PURPLE) if (civInfo.policies.isAdopted("Legalism") && civInfo.cities.size <= 4) cityConstructions.addCultureBuilding() if (civInfo.cities.size == 1) { - cityConstructions.builtBuildings.add("Palace") + cityConstructions.addBuilding("Palace") cityConstructions.currentConstruction = "Worker" // Default for first city only! } @@ -159,7 +159,7 @@ class CityInfo { expansion.setTransients() cityStats.cityInfo = this cityConstructions.cityInfo = this - + cityConstructions.setTransients() } fun endTurn() { @@ -178,7 +178,7 @@ class CityInfo { civInfo.addNotification("[$name] has been razed to the ground!",location, Color.RED) destroyCity() if(isCapital() && civInfo.cities.isNotEmpty()) // Yes, we actually razed the capital. Some people do this. - civInfo.cities.first().cityConstructions.builtBuildings.add("Palace") + civInfo.cities.first().cityConstructions.addBuilding("Palace") } } else population.nextTurn(stats.food) @@ -206,7 +206,7 @@ class CityInfo { // Remove all national wonders for(building in cityConstructions.getBuiltBuildings().filter { it.requiredBuildingInAllCities!=null }) - cityConstructions.builtBuildings.remove(building.name) + cityConstructions.removeBuilding(building.name) isBeingRazed=false } //endregion diff --git a/core/src/com/unciv/logic/city/CityStats.kt b/core/src/com/unciv/logic/city/CityStats.kt index 8c6a0174a7..f5cddc1f01 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -210,7 +210,7 @@ class CityStats { stats.production += 5f if(policies.contains("Warrior Code") && currentConstruction is BaseUnit && currentConstruction.unitType.isMelee()) stats.production += 20 - if (policies.contains("Reformation") && cityConstructions.builtBuildings.any { GameBasics.Buildings[it]!!.isWonder }) + if (policies.contains("Reformation") && cityConstructions.getBuiltBuildings().any { it.isWonder }) stats.culture += 33f if (policies.contains("Commerce") && cityInfo.isCapital()) stats.gold += 25f diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index 37c8292531..937c784052 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -6,6 +6,7 @@ import com.unciv.models.gamebasics.GameBasics import com.unciv.models.gamebasics.tech.Technology import com.unciv.models.gamebasics.unit.BaseUnit import com.unciv.ui.utils.tr +import com.unciv.ui.utils.withItem import java.util.* class TechManager { @@ -77,9 +78,7 @@ class TechManager { techsResearched.add(currentTechnology) // this is to avoid concurrent modification problems - val newResearchedTechnologies = ArrayList(researchedTechnologies) - newResearchedTechnologies.add(GameBasics.Technologies[currentTechnology]!!) - researchedTechnologies = newResearchedTechnologies + researchedTechnologies = researchedTechnologies.withItem(GameBasics.Technologies[currentTechnology]!!) civInfo.addNotification("Research of [$currentTechnology] has completed!", null, Color.BLUE) diff --git a/core/src/com/unciv/models/gamebasics/Building.kt b/core/src/com/unciv/models/gamebasics/Building.kt index 15d740c768..2105770139 100644 --- a/core/src/com/unciv/models/gamebasics/Building.kt +++ b/core/src/com/unciv/models/gamebasics/Building.kt @@ -214,10 +214,10 @@ class Building : NamedStats(), IConstruction{ civInfo.scienceVictory.currentParts.add(name, 1) return } - construction.builtBuildings.add(name) + construction.addBuilding(name) if (providesFreeBuilding != null && !construction.builtBuildings.contains(providesFreeBuilding!!)) - construction.builtBuildings.add(providesFreeBuilding!!) + construction.addBuilding(providesFreeBuilding!!) when { "Empire enters golden age" in uniques-> civInfo.goldenAges.enterGoldenAge() "Free Great Artist Appears" in uniques-> civInfo.addGreatPerson("Great Artist") diff --git a/core/src/com/unciv/ui/utils/CameraStageBaseScreen.kt b/core/src/com/unciv/ui/utils/CameraStageBaseScreen.kt index 1939d40a0b..9418e0ef61 100644 --- a/core/src/com/unciv/ui/utils/CameraStageBaseScreen.kt +++ b/core/src/com/unciv/ui/utils/CameraStageBaseScreen.kt @@ -203,4 +203,22 @@ fun Table.addSeparator(): Cell { val cell = add(image).colspan(columns).fill() row() return cell +} + +/** + * Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed + */ +fun ArrayList.withItem(item:T): ArrayList { + val newArrayList = ArrayList(this) + newArrayList.add(item) + return newArrayList +} + +/** + * Solves concurrent modification problems - everyone who had a reference to the previous arrayList can keep using it because it hasn't changed + */ +fun ArrayList.withoutItem(item:T): ArrayList { + val newArrayList = ArrayList(this) + newArrayList.remove(item) + return newArrayList } \ No newline at end of file diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 27d7f16d2d..a3d06864e2 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -168,7 +168,7 @@ class UnitActions { if (unit.name == "Great Engineer" && !unit.isEmbarked()) { actionList += UnitAction( "Hurry Wonder", { - tile.getCity()!!.cityConstructions.addConstruction(300 + 30 * tile.getCity()!!.population.population) //http://civilization.wikia.com/wiki/Great_engineer_(Civ5) + tile.getCity()!!.cityConstructions.addProduction(300 + 30 * tile.getCity()!!.population.population) //http://civilization.wikia.com/wiki/Great_engineer_(Civ5) unit.destroy() }, unit.currentMovement != 0f &&