Code reorganization - separated variables, pure functions and state-changing functions in all main logic classes

This commit is contained in:
Yair Morgenstern 2018-08-17 13:36:23 +03:00
parent 749ff90fe0
commit 1957c4ca80
15 changed files with 320 additions and 314 deletions

View File

@ -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 {

View File

@ -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
}
}

View File

@ -11,16 +11,28 @@ import com.unciv.models.gamebasics.GameBasics
import com.unciv.ui.utils.getRandom
class GameInfo {
@Transient var tilesToCities = HashMap<TileInfo,CityInfo>()
var notifications = mutableListOf<Notification>()
@Deprecated("As of 2.6.9") var tutorial = mutableListOf<String>()
var civilizations = mutableListOf<CivilizationInfo>()
var tileMap: TileMap = TileMap()
var turns = 0
@Transient var tilesToCities = HashMap<TileInfo,CityInfo>()
//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
}
}

View File

@ -17,6 +17,14 @@ class CityConstructions {
private val inProgressConstructions = HashMap<String, Int>()
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<Building> = 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<Building> = 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

View File

@ -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
}

View File

@ -28,16 +28,63 @@ class CityInfo {
var workedTiles = HashSet<Vector2>()
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<TileInfo> = tiles.map { tileMap[it] }
fun getTilesInRange(): List<TileInfo> = getCenterTile().getTilesInDistance( 3)
// Remove resources required by buildings
fun getCityResources(): Counter<TileResource> {
val cityResources = Counter<TileResource>()
@ -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
}

View File

@ -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<TileInfo>()
var tilesToCheck: List<TileInfo> = 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<String, Stats>()
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<TileInfo>()
var tilesToCheck: List<TileInfo> = 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
}
}

View File

@ -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<String, Stats>()
//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
}
}

View File

@ -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<String,DiplomacyManager>()
var cities = ArrayList<CityInfo>()
var exploredTiles = HashSet<Vector2>()
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<String, Float> {
val statMap = HashMap<String,Float>()
statMap["Base happiness"] = getDifficulty().baseHappiness.toFloat()
@ -153,17 +173,56 @@ class CivilizationInfo {
fun getBuildingUniques(): List<String> = 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<MapUnit> {
return gameInfo.tileMap.values.flatMap { it.getUnits() }.filter { it.owner==civName }
}
fun getViewableTiles(): List<TileInfo> {
var viewablePositions = emptyList<TileInfo>()
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<MapUnit> {
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<TileInfo> {
var viewablePositions = emptyList<TileInfo>()
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
}

View File

@ -16,13 +16,33 @@ enum class DiplomaticStatus{
class DiplomacyManager() {
@Transient lateinit var civInfo: CivilizationInfo
lateinit var otherCivName:String
var trades = ArrayList<Trade>()
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<Trade>()
//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
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -28,14 +28,15 @@ class PolicyManager {
return cost - (cost % 5)
}
fun getAdoptedPolicies(): HashSet<String> = 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()

View File

@ -3,10 +3,16 @@ package com.unciv.logic.civilization
import com.unciv.models.Counter
class ScienceVictoryManager {
var requiredParts = Counter<String>()
var currentParts = Counter<String>()
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<String> {
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)
}
}

View File

@ -18,6 +18,16 @@ class TechManager {
var techsToResearch = ArrayList<String>()
private var techsInProgress = HashMap<String, Int>()
//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
}
}