From 1957c4ca80030532203fddbfbb4c6d49a9b0f98f Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Fri, 17 Aug 2018 13:36:23 +0300 Subject: [PATCH] Code reorganization - separated variables, pure functions and state-changing functions in all main logic classes --- android/build.gradle | 4 +- core/src/com/unciv/UnCivGame.kt | 3 - core/src/com/unciv/logic/GameInfo.kt | 24 +-- .../com/unciv/logic/city/CityConstructions.kt | 65 +++--- .../unciv/logic/city/CityExpansionManager.kt | 49 ++--- core/src/com/unciv/logic/city/CityInfo.kt | 117 ++++++----- core/src/com/unciv/logic/city/CityStats.kt | 47 +++-- .../com/unciv/logic/city/PopulationManager.kt | 22 +-- .../logic/civilization/CivilizationInfo.kt | 185 +++++++++--------- .../logic/civilization/DiplomacyManager.kt | 44 +++-- .../logic/civilization/GoldenAgeManager.kt | 15 +- .../logic/civilization/GreatPersonManager.kt | 15 +- .../unciv/logic/civilization/PolicyManager.kt | 9 +- .../civilization/ScienceVictoryManager.kt | 15 +- .../unciv/logic/civilization/TechManager.kt | 20 +- 15 files changed, 320 insertions(+), 314 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index fef8874ad7..44a5993461 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,8 +21,8 @@ android { applicationId "com.unciv.game" minSdkVersion 14 targetSdkVersion 26 - versionCode 121 - versionName "2.7.8" + versionCode 122 + versionName "2.7.9" } buildTypes { release { diff --git a/core/src/com/unciv/UnCivGame.kt b/core/src/com/unciv/UnCivGame.kt index 7beb1b439a..8d54d76b89 100644 --- a/core/src/com/unciv/UnCivGame.kt +++ b/core/src/com/unciv/UnCivGame.kt @@ -20,7 +20,6 @@ class UnCivGame : Game() { */ val viewEntireMapForDebug = false - lateinit var worldScreen: WorldScreen override fun create() { @@ -72,9 +71,7 @@ class UnCivGame : Game() { setWorldScreen() } - companion object { lateinit var Current: UnCivGame } - } \ No newline at end of file diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 5af30f7ab6..9e2fc0725d 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -11,16 +11,28 @@ import com.unciv.models.gamebasics.GameBasics import com.unciv.ui.utils.getRandom class GameInfo { + @Transient var tilesToCities = HashMap() + var notifications = mutableListOf() @Deprecated("As of 2.6.9") var tutorial = mutableListOf() var civilizations = mutableListOf() var tileMap: TileMap = TileMap() var turns = 0 - @Transient var tilesToCities = HashMap() + //region pure functions + fun clone():GameInfo{ + val toReturn = GameInfo() + toReturn.civilizations.addAll(civilizations.map { it.clone() }) + toReturn.tileMap=tileMap.clone() + toReturn.notifications.addAll(notifications) + toReturn.turns=turns + toReturn.setTransients() + return toReturn + } fun getPlayerCivilization(): CivilizationInfo = civilizations[0] fun getBarbarianCivilization(): CivilizationInfo = civilizations[1] + //endregion fun nextTurn() { notifications.clear() @@ -98,15 +110,5 @@ 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 - toReturn.setTransients() - return toReturn - } } diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index 9a855c6c69..5c71a31f43 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -17,6 +17,14 @@ class CityConstructions { private val inProgressConstructions = HashMap() var currentConstruction: String = "Monument" // default starting building! + //region pure functions + fun clone(): CityConstructions { + val toReturn = CityConstructions() + toReturn.currentConstruction=currentConstruction + toReturn.builtBuildings.addAll(builtBuildings) + toReturn.inProgressConstructions.putAll(inProgressConstructions) + return toReturn + } internal fun getBuildableBuildings(): List = GameBasics.Buildings.values .filter { it.isBuildable(this) } @@ -24,7 +32,6 @@ class CityConstructions { fun getConstructableUnits() = GameBasics.Units.values .filter { it.isBuildable(this) } - // Library and public school unique (not actualy unique, though...hmm) fun getStats(): Stats { val stats = Stats() for (building in getBuiltBuildings()) @@ -58,7 +65,7 @@ class CityConstructions { fun getAmountConstructedText(): String = if (SpecialConstruction.getSpecialConstructions().any { it.name== currentConstruction}) "" - else " (" + workDone(currentConstruction) + "/" + + else " (" + getWorkDone(currentConstruction) + "/" + getCurrentConstruction().getProductionCost(cityInfo.civInfo.policies.adoptedPolicies) + ")" fun getCurrentConstruction(): IConstruction = getConstruction(currentConstruction) @@ -82,6 +89,26 @@ class CityConstructions { internal fun getBuiltBuildings(): List = builtBuildings.map { GameBasics.Buildings[it]!! } + + private fun getWorkDone(constructionName: String): Int { + if (inProgressConstructions.containsKey(constructionName)) return inProgressConstructions[constructionName]!! + else return 0 + } + + fun turnsToConstruction(constructionName: String): Int { + val productionCost = getConstruction(constructionName).getProductionCost(cityInfo.civInfo.policies.adoptedPolicies) + + val workLeft = (productionCost - getWorkDone(constructionName)).toFloat() // needs to be float so that we get the cieling properly ;) + + val cityStats = cityInfo.cityStats.currentCityStats + var production = Math.round(cityStats.production) + if (constructionName == Settler) production += cityStats.food.toInt() + + return Math.ceil((workLeft / production.toDouble())).toInt() + } + //endregion + + //region state0changing functions fun addConstruction(constructionToAdd: Int) { if (!inProgressConstructions.containsKey(currentConstruction)) inProgressConstructions[currentConstruction] = 0 inProgressConstructions[currentConstruction] = inProgressConstructions[currentConstruction]!! + constructionToAdd @@ -126,23 +153,6 @@ class CityConstructions { } - private fun workDone(constructionName: String): Int { - if (inProgressConstructions.containsKey(constructionName)) return inProgressConstructions[constructionName]!! - else return 0 - } - - fun turnsToConstruction(constructionName: String): Int { - val productionCost = getConstruction(constructionName).getProductionCost(cityInfo.civInfo.policies.adoptedPolicies) - - val workLeft = (productionCost - workDone(constructionName)).toFloat() // needs to be float so that we get the cieling properly ;) - - val cityStats = cityInfo.cityStats.currentCityStats - var production = Math.round(cityStats.production) - if (constructionName == Settler) production += cityStats.food.toInt() - - return Math.ceil((workLeft / production.toDouble())).toInt() - } - fun purchaseBuilding(buildingName: String) { cityInfo.civInfo.gold -= getConstruction(buildingName).getGoldCost(cityInfo.civInfo.policies.adoptedPolicies) getConstruction(buildingName).postBuildEvent(this) @@ -161,21 +171,14 @@ class CityConstructions { Automation().chooseNextConstruction(this) } + fun chooseNextConstruction() { + Automation().chooseNextConstruction(this) + } + //endregion + companion object { internal const val Worker = "Worker" internal const val Settler = "Settler" } - fun chooseNextConstruction() { - 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 5c99ab4128..c553055aac 100644 --- a/core/src/com/unciv/logic/city/CityExpansionManager.kt +++ b/core/src/com/unciv/logic/city/CityExpansionManager.kt @@ -5,14 +5,15 @@ import com.unciv.logic.automation.Automation import com.unciv.logic.map.TileInfo class CityExpansionManager { - @Transient lateinit var cityInfo: CityInfo var cultureStored: Int = 0 - fun reset() { - cityInfo.tiles.clear() - cityInfo.getCenterTile().getTilesInDistance(1).forEach { takeOwnership(it) } + + fun clone(): CityExpansionManager { + val toReturn = CityExpansionManager() + toReturn.cultureStored=cultureStored + return toReturn } // This one has conflicting sources - @@ -29,6 +30,26 @@ class CityExpansionManager { return Math.round(cultureToNextTile).toInt() } + + fun getNewTile(): TileInfo? { + for (i in 2..5) { + val tiles = cityInfo.getCenterTile().getTilesInDistance(i).filter { + it.getOwner() != cityInfo.civInfo + && it.getTilesInDistance(1).none { tile->tile.isCityCenter() } // This SHOULD stop cities from grabbing tiles surrounding a city + } + if (tiles.isEmpty()) continue + val chosenTile = tiles.maxBy { Automation().rankTile(it,cityInfo.civInfo) } + return chosenTile + } + return null + } + + //region state-changing functions + fun reset() { + cityInfo.tiles.clear() + cityInfo.getCenterTile().getTilesInDistance(1).forEach { takeOwnership(it) } + } + private fun addNewTileWithCulture() { cultureStored -= getCultureToNextTile() @@ -48,18 +69,6 @@ class CityExpansionManager { unit.movementAlgs().teleportToClosestMoveableTile() } - fun getNewTile(): TileInfo? { - for (i in 2..5) { - val tiles = cityInfo.getCenterTile().getTilesInDistance(i).filter { - it.getOwner() != cityInfo.civInfo - && it.getTilesInDistance(1).none { tile->tile.isCityCenter() } // This SHOULD stop cities from grabbing tiles surrounding a city - } - if (tiles.isEmpty()) continue - val chosenTile = tiles.maxBy { Automation().rankTile(it,cityInfo.civInfo) } - return chosenTile - } - return null - } fun nextTurn(culture: Float) { cultureStored += culture.toInt() @@ -68,11 +77,5 @@ class CityExpansionManager { cityInfo.civInfo.addNotification(cityInfo.name + " {has expanded its borders}!", cityInfo.location, Color.PURPLE) } } - - fun clone(): CityExpansionManager { - val toReturn = CityExpansionManager() - toReturn.cultureStored=cultureStored - return toReturn - } - + //endregion } \ 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 9ebf85b9ed..3f7b91628a 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -28,16 +28,63 @@ class CityInfo { var workedTiles = HashSet() var isBeingRazed = false + + constructor() // for json parsing, we need to have a default constructor + constructor(civInfo: CivilizationInfo, cityLocation: Vector2) { + this.civInfo = civInfo + setTransients() + + // Since cities can be captures between civilizations, + // we need to check which other cities exist globally and name accordingly + val allExistingCityNames = civInfo.gameInfo.civilizations.flatMap { it.cities }.map { it.name }.toHashSet() + val probablyName = civInfo.getNation().cities.firstOrNull { !allExistingCityNames.contains(it) } + if(probablyName!=null) name=probablyName + else name = civInfo.getNation().cities.map { "New $it" }.first { !allExistingCityNames.contains(it) } + + this.location = cityLocation + civInfo.cities.add(this) + if(civInfo == civInfo.gameInfo.getPlayerCivilization()) + 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.currentConstruction = "Worker" // Default for first city only! + } + + expansion.reset() + civInfo.gameInfo.updateTilesToCities() + + val tile = getCenterTile() + tile.roadStatus = RoadStatus.Railroad + if (listOf("Forest", "Jungle", "Marsh").contains(tile.terrainFeature)) + tile.terrainFeature = null + + population.autoAssignPopulation() + cityStats.update() + } + + //region pure functions + 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 + } + internal val tileMap: TileMap get() = civInfo.gameInfo.tileMap fun getCenterTile(): TileInfo = tileMap[location] fun getTiles(): List = tiles.map { tileMap[it] } - fun getTilesInRange(): List = getCenterTile().getTilesInDistance( 3) - - // Remove resources required by buildings fun getCityResources(): Counter { val cityResources = Counter() @@ -77,42 +124,16 @@ class CityInfo { return greatPersonPoints } - constructor() // for json parsing, we need to have a default constructor + fun isCapital() = cityConstructions.isBuilt("Palace") - - constructor(civInfo: CivilizationInfo, cityLocation: Vector2) { - this.civInfo = civInfo - setTransients() - - // Since cities can be captures between civilizations, - // we need to check which other cities exist globally and name accordingly - val allExistingCityNames = civInfo.gameInfo.civilizations.flatMap { it.cities }.map { it.name }.toHashSet() - val probablyName = civInfo.getNation().cities.firstOrNull { !allExistingCityNames.contains(it) } - if(probablyName!=null) name=probablyName - else name = civInfo.getNation().cities.map { "New $it" }.first { !allExistingCityNames.contains(it) } - - this.location = cityLocation - civInfo.cities.add(this) - if(civInfo == civInfo.gameInfo.getPlayerCivilization()) - 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.currentConstruction = "Worker" // Default for first city only! - } - - expansion.reset() - civInfo.gameInfo.updateTilesToCities() - - val tile = getCenterTile() - tile.roadStatus = RoadStatus.Railroad - if (listOf("Forest", "Jungle", "Marsh").contains(tile.terrainFeature)) - tile.terrainFeature = null - - population.autoAssignPopulation() - cityStats.update() + internal fun getMaxHealth(): Int { + return 200 + cityConstructions.getBuiltBuildings().sumBy { it.cityHealth } } + override fun toString(): String {return name} // for debug + //endregion + + //region state-changing functions fun setTransients() { population.cityInfo = this expansion.cityInfo = this @@ -120,7 +141,6 @@ class CityInfo { cityConstructions.cityInfo = this } - fun endTurn() { val stats = cityStats.currentCityStats if (cityConstructions.currentConstruction == CityConstructions.Settler && stats.food > 0) { @@ -146,8 +166,6 @@ class CityInfo { population.unassignExtraPopulation() } - fun isCapital() = cityConstructions.isBuilt("Palace") - fun moveToCiv(newCivInfo: CivilizationInfo){ civInfo.cities.remove(this) newCivInfo.cities.add(this) @@ -166,24 +184,5 @@ class CityInfo { civInfo.gameInfo.updateTilesToCities() } - - internal fun getMaxHealth(): Int { - return 200 + cityConstructions.getBuiltBuildings().sumBy { it.cityHealth } - } - - 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 - } + //endregion } \ 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 a7b359c359..1507df5af7 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -19,6 +19,7 @@ class CityStats { @Transient lateinit var cityInfo: CityInfo + //region pure fuctions private fun getStatsFromTiles(): Stats { val stats = Stats() for (cell in cityInfo.getTilesInRange().filter { cityInfo.workedTiles.contains(it.position) || cityInfo.location == it.position }) @@ -38,7 +39,6 @@ class CityStats { return stats } - private fun getStatsFromProduction(production: Float): Stats { val stats = Stats() @@ -54,7 +54,6 @@ class CityStats { return stats } - private fun getStatPercentBonusesFromRailroad(): Stats { val stats = Stats() if (cityInfo.civInfo.tech.isResearched("Combustion") @@ -178,7 +177,6 @@ class CityStats { return stats } - private fun getStatPercentBonusesFromWonders(): Stats { val stats = Stats() val civUniques = cityInfo.civInfo.getBuildingUniques() @@ -208,6 +206,28 @@ class CityStats { return stats } + fun isConnectedToCapital(roadType: RoadStatus): Boolean { + if (cityInfo.civInfo.cities.count() < 2) return false// first city! + val capitalTile = cityInfo.civInfo.getCapital().getCenterTile() + val tilesReached = HashSet() + var tilesToCheck: List = listOf(cityInfo.getCenterTile()) + while (tilesToCheck.isNotEmpty()) { + val newTiles = tilesToCheck + .flatMap { it.neighbors }.distinct() + .filter { + !tilesReached.contains(it) && !tilesToCheck.contains(it) + && (roadType !== RoadStatus.Road || it.roadStatus !== RoadStatus.None) + && (roadType !== RoadStatus.Railroad || it.roadStatus === roadType) + } + + if (newTiles.contains(capitalTile)) return true + tilesReached.addAll(tilesToCheck) + tilesToCheck = newTiles + } + return false + } + //endregion + fun update() { baseStatList = LinkedHashMap() val civInfo = cityInfo.civInfo @@ -270,25 +290,4 @@ class CityStats { if(currentCityStats.production<1) currentCityStats.production=1f } - - fun isConnectedToCapital(roadType: RoadStatus): Boolean { - if (cityInfo.civInfo.cities.count() < 2) return false// first city! - val capitalTile = cityInfo.civInfo.getCapital().getCenterTile() - val tilesReached = HashSet() - var tilesToCheck: List = listOf(cityInfo.getCenterTile()) - while (tilesToCheck.isNotEmpty()) { - val newTiles = tilesToCheck - .flatMap { it.neighbors }.distinct() - .filter { - !tilesReached.contains(it) && !tilesToCheck.contains(it) - && (roadType !== RoadStatus.Road || it.roadStatus !== RoadStatus.None) - && (roadType !== RoadStatus.Railroad || it.roadStatus === roadType) - } - - if (newTiles.contains(capitalTile)) return true - tilesReached.addAll(tilesToCheck) - tilesToCheck = newTiles - } - return false - } } diff --git a/core/src/com/unciv/logic/city/PopulationManager.kt b/core/src/com/unciv/logic/city/PopulationManager.kt index 19cac08681..29484a8b2d 100644 --- a/core/src/com/unciv/logic/city/PopulationManager.kt +++ b/core/src/com/unciv/logic/city/PopulationManager.kt @@ -7,14 +7,21 @@ import com.unciv.models.stats.Stats import kotlin.math.roundToInt class PopulationManager { - @Transient lateinit var cityInfo: CityInfo + var population = 1 var foodStored = 0 - var buildingsSpecialists = HashMap() + //region pure functions + fun clone(): PopulationManager { + val toReturn = PopulationManager() + toReturn.population=population + toReturn.foodStored=foodStored + return toReturn + } + fun getSpecialists(): Stats { val allSpecialists = Stats() for (stats in buildingsSpecialists.values) @@ -27,14 +34,11 @@ class PopulationManager { return (specialists.science + specialists.production + specialists.culture + specialists.gold).toInt() } - - // 1 is the city center fun getFreePopulation(): Int { val workingPopulation = cityInfo.workedTiles.size return population - workingPopulation - getNumberOfSpecialists() } - fun getFoodToNextPopulation(): Int { // civ v math, civilization.wikia var foodRequired = 15 + 6 * (population - 1) + Math.floor(Math.pow((population - 1).toDouble(), 1.8)) @@ -43,6 +47,7 @@ class PopulationManager { return foodRequired.toInt() } + //endregion fun nextTurn(food: Float) { foodStored += food.roundToInt() @@ -89,11 +94,4 @@ 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 34973113fc..e950bc6236 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -22,30 +22,51 @@ import kotlin.math.roundToInt class CivilizationInfo { - - @Transient - lateinit var gameInfo: GameInfo + @Transient lateinit var gameInfo: GameInfo var gold = 0 var happiness = 15 var difficulty = "Chieftain" var civName = "Babylon" - var tech = TechManager() var policies = PolicyManager() var goldenAges = GoldenAgeManager() var greatPeople = GreatPersonManager() var scienceVictory = ScienceVictoryManager() var diplomacy = HashMap() - var cities = ArrayList() var exploredTiles = HashSet() + constructor() + + constructor(civName: String, startingLocation: Vector2, gameInfo: GameInfo) { + this.civName = civName + this.gameInfo = gameInfo + tech.techsResearched.add("Agriculture") + this.placeUnitNearTile(startingLocation, "Settler") + this.placeUnitNearTile(startingLocation, "Scout") + } + + 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 + } + + //region pure functions fun getDifficulty() = GameBasics.Difficulties[difficulty]!! fun getNation() = GameBasics.Nations[civName]!! - fun getCapital()=cities.first { it.isCapital() } - fun isPlayerCivilization() = gameInfo.getPlayerCivilization()==this fun isBarbarianCivilization() = gameInfo.getBarbarianCivilization()==this @@ -118,7 +139,6 @@ class CivilizationInfo { return transportationUpkeep } - // base happiness fun getHappinessForNextTurn(): HashMap { val statMap = HashMap() statMap["Base happiness"] = getDifficulty().baseHappiness.toFloat() @@ -153,17 +173,56 @@ class CivilizationInfo { fun getBuildingUniques(): List = cities.flatMap { it.cityConstructions.getBuiltBuildings().map { it.unique }.filterNotNull() }.distinct() - - constructor() - - constructor(civName: String, startingLocation: Vector2, gameInfo: GameInfo) { - this.civName = civName - this.gameInfo = gameInfo - tech.techsResearched.add("Agriculture") - this.placeUnitNearTile(startingLocation, "Settler") - this.placeUnitNearTile(startingLocation, "Scout") + fun getCivUnits(): List { + return gameInfo.tileMap.values.flatMap { it.getUnits() }.filter { it.owner==civName } } + fun getViewableTiles(): List { + var viewablePositions = emptyList() + viewablePositions += cities.flatMap { it.getTiles() } + .flatMap { it.neighbors } // tiles adjacent to city tiles + viewablePositions += getCivUnits() + .flatMap { it.getViewableTiles()} // Tiles within 2 tiles of units + viewablePositions.map { it.position }.filterNot { exploredTiles.contains(it) }.toCollection(exploredTiles) + + val viewedCivs = viewablePositions + .flatMap { it.getUnits().map { u->u.civInfo }.union(listOf(it.getOwner())) } + .filterNotNull().filterNot { it==this || it.isBarbarianCivilization() } + + for(otherCiv in viewedCivs) + if(!diplomacy.containsKey(otherCiv.civName)){ + diplomacy[otherCiv.civName] = DiplomacyManager(this@CivilizationInfo,otherCiv.civName) + .apply { diplomaticStatus = DiplomaticStatus.Peace } + otherCiv.diplomacy[civName] = DiplomacyManager(otherCiv,civName) + .apply { diplomaticStatus = DiplomaticStatus.Peace } + addNotification("We have encountered [${otherCiv.civName}]!".tr(),null, Color.GOLD) + } + + return viewablePositions.distinct() + } + + override fun toString(): String {return civName} // for debug + + fun isDefeated()= cities.isEmpty() && !getCivUnits().any{it.name=="Settler"} + fun getEra(): TechEra { + val maxEraOfTech = tech.techsResearched.map { GameBasics.Technologies[it]!! } + .map { it.era() } + .max() + if(maxEraOfTech!=null) return maxEraOfTech + else return TechEra.Ancient + } + + fun isAtWarWith(otherCiv:CivilizationInfo): Boolean { + if(otherCiv.isBarbarianCivilization() || isBarbarianCivilization()) return true + if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet + return false + return diplomacy[otherCiv.civName]!!.diplomaticStatus == DiplomaticStatus.War + } + + fun isAtWar() = diplomacy.values.any { it.diplomaticStatus==DiplomaticStatus.War && !it.otherCiv().isDefeated() } + //endregion + + //region state-changing functions fun setTransients() { goldenAges.civInfo = this policies.civInfo = this @@ -185,11 +244,6 @@ class CivilizationInfo { } } - fun addCity(location: Vector2) { - val newCity = CityInfo(this, location) - newCity.cityConstructions.chooseNextConstruction() - } - fun endTurn() { val nextTurnStats = getStatsForNextTurn() @@ -236,6 +290,17 @@ class CivilizationInfo { getCivUnits().forEach { it.startTurn() } } + fun canEnterTiles(otherCiv: CivilizationInfo): Boolean { + if(otherCiv==this) return true + if(isAtWarWith(otherCiv)) return true + return false + } + + fun addNotification(text: String, location: Vector2?,color: Color) { + if(isPlayerCivilization()) + gameInfo.notifications.add(Notification(text, location,color)) + } + fun addGreatPerson(greatPerson: String) { val randomCity = cities.getRandom() placeUnitNearTile(cities.getRandom().location, greatPerson) @@ -246,78 +311,10 @@ class CivilizationInfo { return gameInfo.tileMap.placeUnitNearTile(location, unitName, this) } - fun getCivUnits(): List { - return gameInfo.tileMap.values.flatMap { it.getUnits() }.filter { it.owner==civName } + fun addCity(location: Vector2) { + val newCity = CityInfo(this, location) + newCity.cityConstructions.chooseNextConstruction() } - fun getViewableTiles(): List { - var viewablePositions = emptyList() - viewablePositions += cities.flatMap { it.getTiles() } - .flatMap { it.neighbors } // tiles adjacent to city tiles - viewablePositions += getCivUnits() - .flatMap { it.getViewableTiles()} // Tiles within 2 tiles of units - viewablePositions.map { it.position }.filterNot { exploredTiles.contains(it) }.toCollection(exploredTiles) - - val viewedCivs = viewablePositions - .flatMap { it.getUnits().map { u->u.civInfo }.union(listOf(it.getOwner())) } - .filterNotNull().filterNot { it==this || it.isBarbarianCivilization() } - - for(otherCiv in viewedCivs) - if(!diplomacy.containsKey(otherCiv.civName)){ - diplomacy[otherCiv.civName] = DiplomacyManager(this@CivilizationInfo,otherCiv.civName) - .apply { diplomaticStatus = DiplomaticStatus.Peace } - otherCiv.diplomacy[civName] = DiplomacyManager(otherCiv,civName) - .apply { diplomaticStatus = DiplomaticStatus.Peace } - addNotification("We have encountered [${otherCiv.civName}]!".tr(),null, Color.GOLD) - } - - return viewablePositions.distinct() - } - - fun addNotification(text: String, location: Vector2?,color: Color) { - if(isPlayerCivilization()) - gameInfo.notifications.add(Notification(text, location,color)) - } - - override fun toString(): String {return civName} // for debug - - fun isDefeated()= cities.isEmpty() && !getCivUnits().any{it.name=="Settler"} - fun getEra(): TechEra { - val maxEraOfTech = tech.techsResearched.map { GameBasics.Technologies[it]!! } - .map { it.era() } - .max() - if(maxEraOfTech!=null) return maxEraOfTech - else return TechEra.Ancient - } - - fun isAtWarWith(otherCiv:CivilizationInfo): Boolean { - if(otherCiv.isBarbarianCivilization() || isBarbarianCivilization()) return true - if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet - return false - return diplomacy[otherCiv.civName]!!.diplomaticStatus == DiplomaticStatus.War - } - - fun isAtWar() = diplomacy.values.any { it.diplomaticStatus==DiplomaticStatus.War && !it.otherCiv().isDefeated() } - - fun canEnterTiles(otherCiv: CivilizationInfo): Boolean { - if(otherCiv==this) return true - 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 - } + //endregion } \ No newline at end of file diff --git a/core/src/com/unciv/logic/civilization/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/DiplomacyManager.kt index 593e429574..98225cc791 100644 --- a/core/src/com/unciv/logic/civilization/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/DiplomacyManager.kt @@ -16,13 +16,33 @@ enum class DiplomaticStatus{ class DiplomacyManager() { @Transient lateinit var civInfo: CivilizationInfo lateinit var otherCivName:String + var trades = ArrayList() + var diplomaticStatus = DiplomaticStatus.War + + fun clone(): DiplomacyManager { + val toReturn = DiplomacyManager() + toReturn.otherCivName=otherCivName + toReturn.diplomaticStatus=diplomaticStatus + toReturn.trades.addAll(trades.map { it.clone() }) + return toReturn + } constructor(civilizationInfo: CivilizationInfo, OtherCivName:String) : this() { civInfo=civilizationInfo otherCivName=OtherCivName } - var trades = ArrayList() + //region pure functions + fun turnsToPeaceTreaty(): Int { + for(trade in trades) + for(offer in trade.ourOffers) + if(offer.name=="Peace Treaty") return offer.duration + return 0 + } + + fun canDeclareWar() = turnsToPeaceTreaty()==0 + + fun otherCiv() = civInfo.gameInfo.civilizations.first{it.civName==otherCivName} fun goldPerTurn():Int{ var goldPerTurnForUs = 0 @@ -47,7 +67,9 @@ class DiplomacyManager() { } return counter } + //endregion + //region state-changing functions fun removeUntenebleTrades(){ val negativeCivResources = civInfo.getCivResources().filter { it.value<0 }.map { it.key.name } for(trade in trades.toList()) { @@ -77,28 +99,10 @@ class DiplomacyManager() { removeUntenebleTrades() } - fun otherCiv() = civInfo.gameInfo.civilizations.first{it.civName==otherCivName} - - var diplomaticStatus = DiplomaticStatus.War fun declareWar(){ diplomaticStatus = DiplomaticStatus.War otherCiv().diplomacy[civInfo.civName]!!.diplomaticStatus = DiplomaticStatus.War otherCiv().addNotification("[civName] has declared war on us!",null, Color.RED) } - - fun turnsToPeaceTreaty(): Int { - for(trade in trades) - for(offer in trade.ourOffers) - if(offer.name=="Peace Treaty") return offer.duration - return 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 - } + //endregion } \ 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 9ae53c4b1f..bf1173f632 100644 --- a/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt +++ b/core/src/com/unciv/logic/civilization/GoldenAgeManager.kt @@ -10,6 +10,14 @@ class GoldenAgeManager{ private var numberOfGoldenAges = 0 var turnsLeftForCurrentGoldenAge = 0 + fun clone(): GoldenAgeManager { + val toReturn = GoldenAgeManager() + toReturn.numberOfGoldenAges=numberOfGoldenAges + toReturn.storedHappiness=storedHappiness + toReturn.turnsLeftForCurrentGoldenAge=turnsLeftForCurrentGoldenAge + return toReturn + } + fun isGoldenAge(): Boolean = turnsLeftForCurrentGoldenAge > 0 fun happinessRequiredForNextGoldenAge(): Int { @@ -36,11 +44,4 @@ class GoldenAgeManager{ } } - 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 b392893efb..78ff6d3d03 100644 --- a/core/src/com/unciv/logic/civilization/GreatPersonManager.kt +++ b/core/src/com/unciv/logic/civilization/GreatPersonManager.kt @@ -7,6 +7,14 @@ class GreatPersonManager { private var greatPersonPoints = Stats() var freeGreatPeople=0 + fun clone(): GreatPersonManager { + val toReturn = GreatPersonManager() + toReturn.freeGreatPeople=freeGreatPeople + toReturn.greatPersonPoints=greatPersonPoints.clone() + toReturn.pointsForNextGreatPerson=pointsForNextGreatPerson + return toReturn + } + fun getNewGreatPerson(): String? { var greatPerson: String? = null when { @@ -27,12 +35,5 @@ 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 b60ad1a8aa..1ac6124bd6 100644 --- a/core/src/com/unciv/logic/civilization/PolicyManager.kt +++ b/core/src/com/unciv/logic/civilization/PolicyManager.kt @@ -28,14 +28,15 @@ class PolicyManager { return cost - (cost % 5) } - fun getAdoptedPolicies(): HashSet = adoptedPolicies fun isAdopted(policyName: String): Boolean = adoptedPolicies.contains(policyName) - fun isAdoptable(policy: Policy) = !policy.name.endsWith("Complete") - && getAdoptedPolicies().containsAll(policy.requires!!) - && policy.getBranch().era <= civInfo.getEra() + fun isAdoptable(policy: Policy): Boolean { + return (!policy.name.endsWith("Complete") + && getAdoptedPolicies().containsAll(policy.requires!!) + && policy.getBranch().era <= civInfo.getEra()) + } fun canAdoptPolicy(): Boolean = freePolicies > 0 || storedCulture >= getCultureNeededForNextPolicy() diff --git a/core/src/com/unciv/logic/civilization/ScienceVictoryManager.kt b/core/src/com/unciv/logic/civilization/ScienceVictoryManager.kt index 3fae5d6d64..f6c0177a70 100644 --- a/core/src/com/unciv/logic/civilization/ScienceVictoryManager.kt +++ b/core/src/com/unciv/logic/civilization/ScienceVictoryManager.kt @@ -3,10 +3,16 @@ package com.unciv.logic.civilization import com.unciv.models.Counter class ScienceVictoryManager { - var requiredParts = Counter() var currentParts = Counter() + init { + requiredParts.add("SS Booster", 3) + requiredParts.add("SS Cockpit", 1) + requiredParts.add("SS Engine", 1) + requiredParts.add("SS Statis Chamber", 1) + } + fun unconstructedParts(): Counter { val counter = requiredParts.clone() counter.remove(currentParts) @@ -14,11 +20,4 @@ class ScienceVictoryManager { } fun hasWon() = requiredParts.equals(currentParts) - - init { - requiredParts.add("SS Booster", 3) - requiredParts.add("SS Cockpit", 1) - requiredParts.add("SS Engine", 1) - requiredParts.add("SS Statis Chamber", 1) - } } diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index 06b5a0a4f3..97f2df4997 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -18,6 +18,16 @@ class TechManager { var techsToResearch = ArrayList() private var techsInProgress = HashMap() + //region state-changing functions + fun clone(): TechManager { + val toReturn = TechManager() + toReturn.techsResearched.addAll(techsResearched) + toReturn.freeTechs=freeTechs + toReturn.techsInProgress.putAll(techsInProgress) + toReturn.techsToResearch.addAll(techsToResearch) + return toReturn + } + private fun getCurrentTechnology(): Technology = GameBasics.Technologies[currentTechnology()]!! fun costOfTech(techName: String): Int { @@ -45,6 +55,7 @@ class TechManager { fun canBeResearched(TechName: String): Boolean { return GameBasics.Technologies[TechName]!!.prerequisites.all { isResearched(it) } } + //endregion fun nextTurn(scienceForNewTurn: Int) { val currentTechnology = currentTechnology() @@ -92,15 +103,6 @@ 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 - } }