mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-24 22:59:34 +07:00
chore: Split CityInfo functions into CityTurnMAnager and CityFounder
This commit is contained in:
48
core/src/com/unciv/logic/city/CityFocus.kt
Normal file
48
core/src/com/unciv/logic/city/CityFocus.kt
Normal file
@ -0,0 +1,48 @@
|
||||
package com.unciv.logic.city
|
||||
|
||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
|
||||
// if tableEnabled == true, then Stat != null
|
||||
enum class CityFocus(val label: String, val tableEnabled: Boolean, val stat: Stat? = null) :
|
||||
IsPartOfGameInfoSerialization {
|
||||
NoFocus("Default Focus", true, null) {
|
||||
override fun getStatMultiplier(stat: Stat) = 1f // actually redundant, but that's two steps to see
|
||||
},
|
||||
FoodFocus("[${Stat.Food.name}] Focus", true, Stat.Food),
|
||||
ProductionFocus("[${Stat.Production.name}] Focus", true, Stat.Production),
|
||||
GoldFocus("[${Stat.Gold.name}] Focus", true, Stat.Gold),
|
||||
ScienceFocus("[${Stat.Science.name}] Focus", true, Stat.Science),
|
||||
CultureFocus("[${Stat.Culture.name}] Focus", true, Stat.Culture),
|
||||
GoldGrowthFocus("Gold Growth Focus", false) {
|
||||
override fun getStatMultiplier(stat: Stat) = when (stat) {
|
||||
Stat.Gold, Stat.Food -> 2f
|
||||
else -> 1f
|
||||
}
|
||||
},
|
||||
ProductionGrowthFocus("Production Growth Focus", false) {
|
||||
override fun getStatMultiplier(stat: Stat) = when (stat) {
|
||||
Stat.Production, Stat.Food -> 2f
|
||||
else -> 1f
|
||||
}
|
||||
},
|
||||
FaithFocus("[${Stat.Faith.name}] Focus", true, Stat.Faith),
|
||||
HappinessFocus("[${Stat.Happiness.name}] Focus", false, Stat.Happiness);
|
||||
//GreatPersonFocus;
|
||||
|
||||
open fun getStatMultiplier(stat: Stat) = when (this.stat) {
|
||||
stat -> 3f
|
||||
else -> 1f
|
||||
}
|
||||
|
||||
fun applyWeightTo(stats: Stats) {
|
||||
for (stat in Stat.values()) {
|
||||
stats[stat] *= getStatMultiplier(stat)
|
||||
}
|
||||
}
|
||||
|
||||
fun safeValueOf(stat: Stat): CityFocus {
|
||||
return values().firstOrNull { it.stat == stat } ?: NoFocus
|
||||
}
|
||||
}
|
@ -9,17 +9,12 @@ import com.unciv.logic.city.managers.CityInfoConquestFunctions
|
||||
import com.unciv.logic.city.managers.CityPopulationManager
|
||||
import com.unciv.logic.city.managers.CityReligionManager
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.NotificationCategory
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
import com.unciv.logic.civilization.Proximity
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.civilization.managers.ReligionState
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.Counter
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.nation.Nation
|
||||
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
@ -27,10 +22,8 @@ import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import java.util.*
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@ -40,48 +33,6 @@ enum class CityFlags {
|
||||
Resistance
|
||||
}
|
||||
|
||||
// if tableEnabled == true, then Stat != null
|
||||
enum class CityFocus(val label: String, val tableEnabled: Boolean, val stat: Stat? = null) : IsPartOfGameInfoSerialization {
|
||||
NoFocus("Default Focus", true, null) {
|
||||
override fun getStatMultiplier(stat: Stat) = 1f // actually redundant, but that's two steps to see
|
||||
},
|
||||
FoodFocus("[${Stat.Food.name}] Focus", true, Stat.Food),
|
||||
ProductionFocus("[${Stat.Production.name}] Focus", true, Stat.Production),
|
||||
GoldFocus("[${Stat.Gold.name}] Focus", true, Stat.Gold),
|
||||
ScienceFocus("[${Stat.Science.name}] Focus", true, Stat.Science),
|
||||
CultureFocus("[${Stat.Culture.name}] Focus", true, Stat.Culture),
|
||||
GoldGrowthFocus("Gold Growth Focus", false) {
|
||||
override fun getStatMultiplier(stat: Stat) = when (stat) {
|
||||
Stat.Gold, Stat.Food -> 2f
|
||||
else -> 1f
|
||||
}
|
||||
},
|
||||
ProductionGrowthFocus("Production Growth Focus", false) {
|
||||
override fun getStatMultiplier(stat: Stat) = when (stat) {
|
||||
Stat.Production, Stat.Food -> 2f
|
||||
else -> 1f
|
||||
}
|
||||
},
|
||||
FaithFocus("[${Stat.Faith.name}] Focus", true, Stat.Faith),
|
||||
HappinessFocus("[${Stat.Happiness.name}] Focus", false, Stat.Happiness);
|
||||
//GreatPersonFocus;
|
||||
|
||||
open fun getStatMultiplier(stat: Stat) = when (this.stat) {
|
||||
stat -> 3f
|
||||
else -> 1f
|
||||
}
|
||||
|
||||
fun applyWeightTo(stats: Stats) {
|
||||
for (stat in Stat.values()) {
|
||||
stats[stat] *= getStatMultiplier(stat)
|
||||
}
|
||||
}
|
||||
|
||||
fun safeValueOf(stat: Stat): CityFocus {
|
||||
return values().firstOrNull { it.stat == stat } ?: NoFocus
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CityInfo : IsPartOfGameInfoSerialization {
|
||||
@Suppress("JoinDeclarationAndAssignment")
|
||||
@ -149,199 +100,10 @@ class CityInfo : IsPartOfGameInfoSerialization {
|
||||
/** For We Love the King Day */
|
||||
var demandedResource = ""
|
||||
|
||||
private var flagsCountdown = HashMap<String, Int>()
|
||||
internal var flagsCountdown = HashMap<String, Int>()
|
||||
|
||||
fun hasDiplomaticMarriage(): Boolean = foundingCiv == ""
|
||||
|
||||
constructor() // for json parsing, we need to have a default constructor
|
||||
constructor(civInfo: CivilizationInfo, cityLocation: Vector2) { // new city!
|
||||
this.civInfo = civInfo
|
||||
foundingCiv = civInfo.civName
|
||||
turnAcquired = civInfo.gameInfo.turns
|
||||
location = cityLocation
|
||||
setTransients()
|
||||
|
||||
name = generateNewCityName(
|
||||
civInfo,
|
||||
civInfo.gameInfo.civilizations.asSequence().filter { civ -> civ.isAlive() }.toSet(),
|
||||
arrayListOf("New ", "Neo ", "Nova ", "Altera ")
|
||||
) ?: "City Without A Name"
|
||||
|
||||
isOriginalCapital = civInfo.citiesCreated == 0
|
||||
if (isOriginalCapital) {
|
||||
civInfo.hasEverOwnedOriginalCapital = true
|
||||
// if you have some culture before the 1st city is found, you may want to adopt the 1st policy
|
||||
civInfo.policies.shouldOpenPolicyPicker = true
|
||||
}
|
||||
civInfo.citiesCreated++
|
||||
|
||||
civInfo.cities = civInfo.cities.toMutableList().apply { add(this@CityInfo) }
|
||||
|
||||
val startingEra = civInfo.gameInfo.gameParameters.startingEra
|
||||
|
||||
addStartingBuildings(civInfo, startingEra)
|
||||
|
||||
expansion.reset()
|
||||
|
||||
tryUpdateRoadStatus()
|
||||
|
||||
val tile = getCenterTile()
|
||||
for (terrainFeature in tile.terrainFeatures.filter {
|
||||
getRuleset().tileImprovements.containsKey(
|
||||
"Remove $it"
|
||||
)
|
||||
})
|
||||
tile.removeTerrainFeature(terrainFeature)
|
||||
|
||||
tile.changeImprovement(null)
|
||||
tile.improvementInProgress = null
|
||||
|
||||
val ruleset = civInfo.gameInfo.ruleSet
|
||||
workedTiles = hashSetOf() //reassign 1st working tile
|
||||
|
||||
population.setPopulation(ruleset.eras[startingEra]!!.settlerPopulation)
|
||||
|
||||
if (civInfo.religionManager.religionState == ReligionState.Pantheon) {
|
||||
religion.addPressure(
|
||||
civInfo.religionManager.religion!!.name,
|
||||
200 * population.population
|
||||
)
|
||||
}
|
||||
|
||||
population.autoAssignPopulation()
|
||||
|
||||
// Update proximity rankings for all civs
|
||||
for (otherCiv in civInfo.gameInfo.getAliveMajorCivs()) {
|
||||
if (civInfo.getProximity(otherCiv) != Proximity.Neighbors) // unless already neighbors
|
||||
civInfo.cache.updateProximity(otherCiv,
|
||||
otherCiv.cache.updateProximity(civInfo))
|
||||
}
|
||||
for (otherCiv in civInfo.gameInfo.getAliveCityStates()) {
|
||||
if (civInfo.getProximity(otherCiv) != Proximity.Neighbors) // unless already neighbors
|
||||
civInfo.cache.updateProximity(otherCiv,
|
||||
otherCiv.cache.updateProximity(civInfo))
|
||||
}
|
||||
|
||||
triggerCitiesSettledNearOtherCiv()
|
||||
|
||||
civInfo.gameInfo.cityDistances.setDirty()
|
||||
}
|
||||
|
||||
private fun addStartingBuildings(civInfo: CivilizationInfo, startingEra: String) {
|
||||
val ruleset = civInfo.gameInfo.ruleSet
|
||||
if (civInfo.cities.size == 1) cityConstructions.addBuilding(capitalCityIndicator())
|
||||
|
||||
// Add buildings and pop we get from starting in this era
|
||||
for (buildingName in ruleset.eras[startingEra]!!.settlerBuildings) {
|
||||
val building = ruleset.buildings[buildingName] ?: continue
|
||||
val uniqueBuilding = civInfo.getEquivalentBuilding(building)
|
||||
if (uniqueBuilding.isBuildable(cityConstructions))
|
||||
cityConstructions.addBuilding(uniqueBuilding.name)
|
||||
}
|
||||
|
||||
civInfo.civConstructions.tryAddFreeBuildings()
|
||||
cityConstructions.addFreeBuildings()
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and returns a new city name for the [foundingCiv].
|
||||
*
|
||||
* This method attempts to return the first unused city name of the [foundingCiv], taking used
|
||||
* city names into consideration (including foreign cities). If that fails, it then checks
|
||||
* whether the civilization has [UniqueType.BorrowsCityNames] and, if true, returns a borrowed
|
||||
* name. Else, it repeatedly attaches one of the given [prefixes] to the list of names up to ten
|
||||
* times until an unused name is successfully generated. If all else fails, null is returned.
|
||||
*
|
||||
* @param foundingCiv The civilization that founded this city.
|
||||
* @param aliveCivs Every civilization currently alive.
|
||||
* @param prefixes Prefixes to add when every base name is taken, ordered.
|
||||
* @return A new city name in [String]. Null if failed to generate a name.
|
||||
*/
|
||||
private fun generateNewCityName(
|
||||
foundingCiv: CivilizationInfo,
|
||||
aliveCivs: Set<CivilizationInfo>,
|
||||
prefixes: List<String>
|
||||
): String? {
|
||||
val usedCityNames: Set<String> =
|
||||
aliveCivs.asSequence().flatMap { civilization ->
|
||||
civilization.cities.asSequence().map { city -> city.name }
|
||||
}.toSet()
|
||||
|
||||
// Attempt to return the first missing name from the list of city names
|
||||
for (cityName in foundingCiv.nation.cities) {
|
||||
if (cityName !in usedCityNames) return cityName
|
||||
}
|
||||
|
||||
// If all names are taken and this nation borrows city names,
|
||||
// return a random borrowed city name
|
||||
if (foundingCiv.hasUnique(UniqueType.BorrowsCityNames)) {
|
||||
return borrowCityName(foundingCiv, aliveCivs, usedCityNames)
|
||||
}
|
||||
|
||||
// If the nation doesn't have the unique above,
|
||||
// return the first missing name with an increasing number of prefixes attached
|
||||
// TODO: Make prefixes moddable per nation? Support suffixes?
|
||||
var candidate: String?
|
||||
for (number in (1..10)) {
|
||||
for (prefix in prefixes) {
|
||||
val currentPrefix: String = prefix.repeat(number)
|
||||
candidate = foundingCiv.nation.cities.firstOrNull { cityName ->
|
||||
(currentPrefix + cityName) !in usedCityNames
|
||||
}
|
||||
if (candidate != null) return currentPrefix + candidate
|
||||
}
|
||||
}
|
||||
|
||||
// If all else fails (by using some sort of rule set mod without city names),
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Borrows a city name from another major civilization.
|
||||
*
|
||||
* @param foundingCiv The civilization that founded this city.
|
||||
* @param aliveCivs Every civilization currently alive.
|
||||
* @param usedCityNames Every city name that have already been taken.
|
||||
* @return A new city named in [String]. Null if failed to generate a name.
|
||||
*/
|
||||
private fun borrowCityName(
|
||||
foundingCiv: CivilizationInfo,
|
||||
aliveCivs: Set<CivilizationInfo>,
|
||||
usedCityNames: Set<String>
|
||||
): String? {
|
||||
val aliveMajorNations: Sequence<Nation> =
|
||||
aliveCivs.asSequence().filter { civ -> civ.isMajorCiv() }.map { civ -> civ.nation }
|
||||
|
||||
/*
|
||||
We take the last unused city name for each other major nation in this game,
|
||||
skipping nations whose names are exhausted,
|
||||
and choose a random one from that pool if it's not empty.
|
||||
*/
|
||||
val otherMajorNations: Sequence<Nation> =
|
||||
aliveMajorNations.filter { nation -> nation != foundingCiv.nation }
|
||||
var newCityNames: Set<String> =
|
||||
otherMajorNations.mapNotNull { nation ->
|
||||
nation.cities.lastOrNull { city -> city !in usedCityNames }
|
||||
}.toSet()
|
||||
if (newCityNames.isNotEmpty()) return newCityNames.random()
|
||||
|
||||
// As per fandom wiki, once the names from the other nations in the game are exhausted,
|
||||
// names are taken from the rest of the major nations in the rule set
|
||||
val absentMajorNations: Sequence<Nation> =
|
||||
getRuleset().nations.values.asSequence().filter { nation ->
|
||||
nation.isMajorCiv() && nation !in aliveMajorNations
|
||||
}
|
||||
newCityNames =
|
||||
absentMajorNations.flatMap { nation ->
|
||||
nation.cities.asSequence().filter { city -> city !in usedCityNames }
|
||||
}.toSet()
|
||||
if (newCityNames.isNotEmpty()) return newCityNames.random()
|
||||
|
||||
// If for some reason we have used every single city name in the game,
|
||||
// (are we using some sort of rule set mod without city names?)
|
||||
return null
|
||||
}
|
||||
|
||||
//region pure functions
|
||||
fun clone(): CityInfo {
|
||||
val toReturn = CityInfo()
|
||||
@ -634,7 +396,8 @@ class CityInfo : IsPartOfGameInfoSerialization {
|
||||
//endregion
|
||||
|
||||
//region state-changing functions
|
||||
fun setTransients() {
|
||||
fun setTransients(civInfo: CivilizationInfo) {
|
||||
this.civInfo = civInfo
|
||||
tileMap = civInfo.gameInfo.tileMap
|
||||
centerTileInfo = tileMap[location]
|
||||
tilesInRange = getCenterTile().getTilesInDistance(3).toHashSet()
|
||||
@ -647,65 +410,6 @@ class CityInfo : IsPartOfGameInfoSerialization {
|
||||
espionage.setTransients(this)
|
||||
}
|
||||
|
||||
fun startTurn() {
|
||||
// Construct units at the beginning of the turn,
|
||||
// so they won't be generated out in the open and vulnerable to enemy attacks before you can control them
|
||||
cityConstructions.constructIfEnough()
|
||||
cityConstructions.addFreeBuildings()
|
||||
|
||||
cityStats.update()
|
||||
tryUpdateRoadStatus()
|
||||
attackedThisTurn = false
|
||||
|
||||
if (isPuppet) {
|
||||
cityAIFocus = CityFocus.GoldFocus
|
||||
reassignAllPopulation()
|
||||
} else if (updateCitizens) {
|
||||
reassignPopulation()
|
||||
updateCitizens = false
|
||||
}
|
||||
|
||||
// The ordering is intentional - you get a turn without WLTKD even if you have the next resource already
|
||||
if (!hasFlag(CityFlags.WeLoveTheKing))
|
||||
tryWeLoveTheKing()
|
||||
nextTurnFlags()
|
||||
|
||||
// Seed resource demand countdown
|
||||
if(demandedResource == "" && !hasFlag(CityFlags.ResourceDemand)) {
|
||||
setFlag(CityFlags.ResourceDemand,
|
||||
(if (isCapital()) 25 else 15) + Random().nextInt(10))
|
||||
}
|
||||
}
|
||||
|
||||
// cf DiplomacyManager nextTurnFlags
|
||||
private fun nextTurnFlags() {
|
||||
for (flag in flagsCountdown.keys.toList()) {
|
||||
if (flagsCountdown[flag]!! > 0)
|
||||
flagsCountdown[flag] = flagsCountdown[flag]!! - 1
|
||||
|
||||
if (flagsCountdown[flag] == 0) {
|
||||
flagsCountdown.remove(flag)
|
||||
|
||||
when (flag) {
|
||||
CityFlags.ResourceDemand.name -> {
|
||||
demandNewResource()
|
||||
}
|
||||
CityFlags.WeLoveTheKing.name -> {
|
||||
civInfo.addNotification(
|
||||
"We Love The King Day in [$name] has ended.",
|
||||
location, NotificationCategory.General, NotificationIcon.City)
|
||||
demandNewResource()
|
||||
}
|
||||
CityFlags.Resistance.name -> {
|
||||
civInfo.addNotification(
|
||||
"The resistance in [$name] has ended!",
|
||||
location, NotificationCategory.General, "StatIcons/Resistance")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setFlag(flag: CityFlags, amount: Int) {
|
||||
flagsCountdown[flag.name] = amount
|
||||
}
|
||||
@ -741,39 +445,6 @@ class CityInfo : IsPartOfGameInfoSerialization {
|
||||
population.autoAssignPopulation()
|
||||
}
|
||||
|
||||
fun endTurn() {
|
||||
val stats = cityStats.currentCityStats
|
||||
|
||||
cityConstructions.endTurn(stats)
|
||||
expansion.nextTurn(stats.culture)
|
||||
if (isBeingRazed) {
|
||||
val removedPopulation =
|
||||
1 + civInfo.getMatchingUniques(UniqueType.CitiesAreRazedXTimesFaster)
|
||||
.sumOf { it.params[0].toInt() - 1 }
|
||||
population.addPopulation(-1 * removedPopulation)
|
||||
if (population.population <= 0) {
|
||||
civInfo.addNotification(
|
||||
"[$name] has been razed to the ground!",
|
||||
location, NotificationCategory.General,
|
||||
"OtherIcons/Fire"
|
||||
)
|
||||
destroyCity()
|
||||
} else { //if not razed yet:
|
||||
if (population.foodStored >= population.getFoodToNextPopulation()) { //if surplus in the granary...
|
||||
population.foodStored =
|
||||
population.getFoodToNextPopulation() - 1 //...reduce below the new growth threshold
|
||||
}
|
||||
}
|
||||
} else population.nextTurn(foodForNextTurn())
|
||||
|
||||
// This should go after the population change, as that might impact the amount of followers in this city
|
||||
if (civInfo.gameInfo.isReligionEnabled()) religion.endTurn()
|
||||
|
||||
if (this in civInfo.cities) { // city was not destroyed
|
||||
health = min(health + 20, getMaxHealth())
|
||||
population.unassignExtraPopulation()
|
||||
}
|
||||
}
|
||||
|
||||
fun destroyCity(overrideSafeties: Boolean = false) {
|
||||
// Original capitals and holy cities cannot be destroyed,
|
||||
@ -857,62 +528,6 @@ class CityInfo : IsPartOfGameInfoSerialization {
|
||||
civInfo.cache.updateCivResources() // this building could be a resource-requiring one
|
||||
}
|
||||
|
||||
private fun demandNewResource() {
|
||||
val candidates = getRuleset().tileResources.values.filter {
|
||||
it.resourceType == ResourceType.Luxury && // Must be luxury
|
||||
!it.hasUnique(UniqueType.CityStateOnlyResource) && // Not a city-state only resource eg jewelry
|
||||
it.name != demandedResource && // Not same as last time
|
||||
!civInfo.hasResource(it.name) && // Not one we already have
|
||||
it.name in tileMap.resources && // Must exist somewhere on the map
|
||||
getCenterTile().getTilesInDistance(3).none { nearTile -> nearTile.resource == it.name } // Not in this city's radius
|
||||
}
|
||||
|
||||
val chosenResource = candidates.randomOrNull()
|
||||
/* What if we had a WLTKD before but now the player has every resource in the game? We can't
|
||||
pick a new resource, so the resource will stay stay the same and the city will demand it
|
||||
again even if the player still has it. But we shouldn't punish success. */
|
||||
if (chosenResource != null)
|
||||
demandedResource = chosenResource.name
|
||||
if (demandedResource == "") // Failed to get a valid resource, try again some time later
|
||||
setFlag(CityFlags.ResourceDemand, 15 + Random().nextInt(10))
|
||||
else
|
||||
civInfo.addNotification("[$name] demands [$demandedResource]!", location, NotificationCategory.General, NotificationIcon.City, "ResourceIcons/$demandedResource")
|
||||
}
|
||||
|
||||
private fun tryWeLoveTheKing() {
|
||||
if (demandedResource == "") return
|
||||
if (civInfo.getCivResourcesByName()[demandedResource]!! > 0) {
|
||||
setFlag(CityFlags.WeLoveTheKing, 20 + 1) // +1 because it will be decremented by 1 in the same startTurn()
|
||||
civInfo.addNotification(
|
||||
"Because they have [$demandedResource], the citizens of [$name] are celebrating We Love The King Day!",
|
||||
location, NotificationCategory.General, NotificationIcon.City, NotificationIcon.Happiness)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
When someone settles a city within 6 tiles of another civ, this makes the AI unhappy and it starts a rolling event.
|
||||
The SettledCitiesNearUs flag gets added to the AI so it knows this happened,
|
||||
and on its turn it asks the player to stop (with a DemandToStopSettlingCitiesNear alert type)
|
||||
If the player says "whatever, I'm not promising to stop", they get a -10 modifier which gradually disappears in 40 turns
|
||||
If they DO agree, then if they keep their promise for ~100 turns they get a +10 modifier for keeping the promise,
|
||||
But if they don't keep their promise they get a -20 that will only fully disappear in 160 turns.
|
||||
There's a lot of triggering going on here.
|
||||
*/
|
||||
private fun triggerCitiesSettledNearOtherCiv() {
|
||||
val citiesWithin6Tiles =
|
||||
civInfo.gameInfo.civilizations.asSequence()
|
||||
.filter { it.isMajorCiv() && it != civInfo }
|
||||
.flatMap { it.cities }
|
||||
.filter { it.getCenterTile().aerialDistanceTo(getCenterTile()) <= 6 }
|
||||
val civsWithCloseCities =
|
||||
citiesWithin6Tiles
|
||||
.map { it.civInfo }
|
||||
.distinct()
|
||||
.filter { it.knows(civInfo) && it.hasExplored(location) }
|
||||
for (otherCiv in civsWithCloseCities)
|
||||
otherCiv.getDiplomacyManager(civInfo).setFlag(DiplomacyFlags.SettledCitiesNearUs, 30)
|
||||
}
|
||||
|
||||
fun canPlaceNewUnit(construction: BaseUnit): Boolean {
|
||||
val tile = getCenterTile()
|
||||
return when {
|
||||
|
229
core/src/com/unciv/logic/city/managers/CityFounder.kt
Normal file
229
core/src/com/unciv/logic/city/managers/CityFounder.kt
Normal file
@ -0,0 +1,229 @@
|
||||
package com.unciv.logic.city.managers
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.Proximity
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.civilization.managers.ReligionState
|
||||
import com.unciv.models.ruleset.nation.Nation
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
|
||||
class CityFounder {
|
||||
fun foundCity(civInfo: CivilizationInfo, cityLocation: Vector2) :CityInfo{
|
||||
val cityInfo = CityInfo()
|
||||
|
||||
cityInfo.foundingCiv = civInfo.civName
|
||||
cityInfo.turnAcquired = civInfo.gameInfo.turns
|
||||
cityInfo.location = cityLocation
|
||||
cityInfo.setTransients(civInfo)
|
||||
|
||||
cityInfo.name = generateNewCityName(
|
||||
civInfo,
|
||||
civInfo.gameInfo.civilizations.asSequence().filter { civ -> civ.isAlive() }.toSet(),
|
||||
arrayListOf("New ", "Neo ", "Nova ", "Altera ")
|
||||
) ?: "City Without A Name"
|
||||
|
||||
cityInfo.isOriginalCapital = civInfo.citiesCreated == 0
|
||||
if (cityInfo.isOriginalCapital) {
|
||||
civInfo.hasEverOwnedOriginalCapital = true
|
||||
// if you have some culture before the 1st city is found, you may want to adopt the 1st policy
|
||||
civInfo.policies.shouldOpenPolicyPicker = true
|
||||
}
|
||||
civInfo.citiesCreated++
|
||||
|
||||
civInfo.cities = civInfo.cities.toMutableList().apply { add(cityInfo) }
|
||||
|
||||
val startingEra = civInfo.gameInfo.gameParameters.startingEra
|
||||
|
||||
addStartingBuildings(cityInfo, civInfo, startingEra)
|
||||
|
||||
cityInfo.expansion.reset()
|
||||
|
||||
cityInfo.tryUpdateRoadStatus()
|
||||
|
||||
val tile = cityInfo.getCenterTile()
|
||||
for (terrainFeature in tile.terrainFeatures.filter {
|
||||
cityInfo.getRuleset().tileImprovements.containsKey(
|
||||
"Remove $it"
|
||||
)
|
||||
})
|
||||
tile.removeTerrainFeature(terrainFeature)
|
||||
|
||||
tile.changeImprovement(null)
|
||||
tile.improvementInProgress = null
|
||||
|
||||
val ruleset = civInfo.gameInfo.ruleSet
|
||||
cityInfo.workedTiles = hashSetOf() //reassign 1st working tile
|
||||
|
||||
cityInfo.population.setPopulation(ruleset.eras[startingEra]!!.settlerPopulation)
|
||||
|
||||
if (civInfo.religionManager.religionState == ReligionState.Pantheon) {
|
||||
cityInfo.religion.addPressure(
|
||||
civInfo.religionManager.religion!!.name,
|
||||
200 * cityInfo.population.population
|
||||
)
|
||||
}
|
||||
|
||||
cityInfo.population.autoAssignPopulation()
|
||||
|
||||
// Update proximity rankings for all civs
|
||||
for (otherCiv in civInfo.gameInfo.getAliveMajorCivs()) {
|
||||
if (civInfo.getProximity(otherCiv) != Proximity.Neighbors) // unless already neighbors
|
||||
civInfo.cache.updateProximity(otherCiv,
|
||||
otherCiv.cache.updateProximity(civInfo))
|
||||
}
|
||||
for (otherCiv in civInfo.gameInfo.getAliveCityStates()) {
|
||||
if (civInfo.getProximity(otherCiv) != Proximity.Neighbors) // unless already neighbors
|
||||
civInfo.cache.updateProximity(otherCiv,
|
||||
otherCiv.cache.updateProximity(civInfo))
|
||||
}
|
||||
|
||||
triggerCitiesSettledNearOtherCiv(cityInfo)
|
||||
civInfo.gameInfo.cityDistances.setDirty()
|
||||
|
||||
return cityInfo
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates and returns a new city name for the [foundingCiv].
|
||||
*
|
||||
* This method attempts to return the first unused city name of the [foundingCiv], taking used
|
||||
* city names into consideration (including foreign cities). If that fails, it then checks
|
||||
* whether the civilization has [UniqueType.BorrowsCityNames] and, if true, returns a borrowed
|
||||
* name. Else, it repeatedly attaches one of the given [prefixes] to the list of names up to ten
|
||||
* times until an unused name is successfully generated. If all else fails, null is returned.
|
||||
*
|
||||
* @param foundingCiv The civilization that founded this city.
|
||||
* @param aliveCivs Every civilization currently alive.
|
||||
* @param prefixes Prefixes to add when every base name is taken, ordered.
|
||||
* @return A new city name in [String]. Null if failed to generate a name.
|
||||
*/
|
||||
private fun generateNewCityName(
|
||||
foundingCiv: CivilizationInfo,
|
||||
aliveCivs: Set<CivilizationInfo>,
|
||||
prefixes: List<String>
|
||||
): String? {
|
||||
val usedCityNames: Set<String> =
|
||||
aliveCivs.asSequence().flatMap { civilization ->
|
||||
civilization.cities.asSequence().map { city -> city.name }
|
||||
}.toSet()
|
||||
|
||||
// Attempt to return the first missing name from the list of city names
|
||||
for (cityName in foundingCiv.nation.cities) {
|
||||
if (cityName !in usedCityNames) return cityName
|
||||
}
|
||||
|
||||
// If all names are taken and this nation borrows city names,
|
||||
// return a random borrowed city name
|
||||
if (foundingCiv.hasUnique(UniqueType.BorrowsCityNames)) {
|
||||
return borrowCityName(foundingCiv, aliveCivs, usedCityNames)
|
||||
}
|
||||
|
||||
// If the nation doesn't have the unique above,
|
||||
// return the first missing name with an increasing number of prefixes attached
|
||||
// TODO: Make prefixes moddable per nation? Support suffixes?
|
||||
var candidate: String?
|
||||
for (number in (1..10)) {
|
||||
for (prefix in prefixes) {
|
||||
val currentPrefix: String = prefix.repeat(number)
|
||||
candidate = foundingCiv.nation.cities.firstOrNull { cityName ->
|
||||
(currentPrefix + cityName) !in usedCityNames
|
||||
}
|
||||
if (candidate != null) return currentPrefix + candidate
|
||||
}
|
||||
}
|
||||
|
||||
// If all else fails (by using some sort of rule set mod without city names),
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Borrows a city name from another major civilization.
|
||||
*
|
||||
* @param foundingCiv The civilization that founded this city.
|
||||
* @param aliveCivs Every civilization currently alive.
|
||||
* @param usedCityNames Every city name that have already been taken.
|
||||
* @return A new city named in [String]. Null if failed to generate a name.
|
||||
*/
|
||||
private fun borrowCityName(
|
||||
foundingCiv: CivilizationInfo,
|
||||
aliveCivs: Set<CivilizationInfo>,
|
||||
usedCityNames: Set<String>
|
||||
): String? {
|
||||
val aliveMajorNations: Sequence<Nation> =
|
||||
aliveCivs.asSequence().filter { civ -> civ.isMajorCiv() }.map { civ -> civ.nation }
|
||||
|
||||
/*
|
||||
We take the last unused city name for each other major nation in this game,
|
||||
skipping nations whose names are exhausted,
|
||||
and choose a random one from that pool if it's not empty.
|
||||
*/
|
||||
val otherMajorNations: Sequence<Nation> =
|
||||
aliveMajorNations.filter { nation -> nation != foundingCiv.nation }
|
||||
var newCityNames: Set<String> =
|
||||
otherMajorNations.mapNotNull { nation ->
|
||||
nation.cities.lastOrNull { city -> city !in usedCityNames }
|
||||
}.toSet()
|
||||
if (newCityNames.isNotEmpty()) return newCityNames.random()
|
||||
|
||||
// As per fandom wiki, once the names from the other nations in the game are exhausted,
|
||||
// names are taken from the rest of the major nations in the rule set
|
||||
val absentMajorNations: Sequence<Nation> =
|
||||
foundingCiv.gameInfo.ruleSet.nations.values.asSequence().filter { nation ->
|
||||
nation.isMajorCiv() && nation !in aliveMajorNations
|
||||
}
|
||||
newCityNames =
|
||||
absentMajorNations.flatMap { nation ->
|
||||
nation.cities.asSequence().filter { city -> city !in usedCityNames }
|
||||
}.toSet()
|
||||
if (newCityNames.isNotEmpty()) return newCityNames.random()
|
||||
|
||||
// If for some reason we have used every single city name in the game,
|
||||
// (are we using some sort of rule set mod without city names?)
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
private fun addStartingBuildings(cityInfo: CityInfo, civInfo: CivilizationInfo, startingEra: String) {
|
||||
val ruleset = civInfo.gameInfo.ruleSet
|
||||
if (civInfo.cities.size == 1) cityInfo.cityConstructions.addBuilding(cityInfo.capitalCityIndicator())
|
||||
|
||||
// Add buildings and pop we get from starting in this era
|
||||
for (buildingName in ruleset.eras[startingEra]!!.settlerBuildings) {
|
||||
val building = ruleset.buildings[buildingName] ?: continue
|
||||
val uniqueBuilding = civInfo.getEquivalentBuilding(building)
|
||||
if (uniqueBuilding.isBuildable(cityInfo.cityConstructions))
|
||||
cityInfo.cityConstructions.addBuilding(uniqueBuilding.name)
|
||||
}
|
||||
|
||||
civInfo.civConstructions.tryAddFreeBuildings()
|
||||
cityInfo.cityConstructions.addFreeBuildings()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
When someone settles a city within 6 tiles of another civ, this makes the AI unhappy and it starts a rolling event.
|
||||
The SettledCitiesNearUs flag gets added to the AI so it knows this happened,
|
||||
and on its turn it asks the player to stop (with a DemandToStopSettlingCitiesNear alert type)
|
||||
If the player says "whatever, I'm not promising to stop", they get a -10 modifier which gradually disappears in 40 turns
|
||||
If they DO agree, then if they keep their promise for ~100 turns they get a +10 modifier for keeping the promise,
|
||||
But if they don't keep their promise they get a -20 that will only fully disappear in 160 turns.
|
||||
There's a lot of triggering going on here.
|
||||
*/
|
||||
private fun triggerCitiesSettledNearOtherCiv(cityInfo: CityInfo) {
|
||||
val citiesWithin6Tiles =
|
||||
cityInfo.civInfo.gameInfo.civilizations.asSequence()
|
||||
.filter { it.isMajorCiv() && it != cityInfo.civInfo }
|
||||
.flatMap { it.cities }
|
||||
.filter { it.getCenterTile().aerialDistanceTo(cityInfo.getCenterTile()) <= 6 }
|
||||
val civsWithCloseCities =
|
||||
citiesWithin6Tiles
|
||||
.map { it.civInfo }
|
||||
.distinct()
|
||||
.filter { it.knows(cityInfo.civInfo) && it.hasExplored(cityInfo.location) }
|
||||
for (otherCiv in civsWithCloseCities)
|
||||
otherCiv.getDiplomacyManager(cityInfo.civInfo).setFlag(DiplomacyFlags.SettledCitiesNearUs, 30)
|
||||
}
|
||||
}
|
147
core/src/com/unciv/logic/city/managers/CityTurnManager.kt
Normal file
147
core/src/com/unciv/logic/city/managers/CityTurnManager.kt
Normal file
@ -0,0 +1,147 @@
|
||||
package com.unciv.logic.city.managers
|
||||
|
||||
import com.unciv.logic.city.CityFlags
|
||||
import com.unciv.logic.city.CityFocus
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.NotificationCategory
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
class CityTurnManager(val cityInfo: CityInfo) {
|
||||
|
||||
|
||||
fun startTurn() {
|
||||
// Construct units at the beginning of the turn,
|
||||
// so they won't be generated out in the open and vulnerable to enemy attacks before you can control them
|
||||
cityInfo.cityConstructions.constructIfEnough()
|
||||
cityInfo.cityConstructions.addFreeBuildings()
|
||||
|
||||
cityInfo.cityStats.update()
|
||||
cityInfo.tryUpdateRoadStatus()
|
||||
cityInfo.attackedThisTurn = false
|
||||
|
||||
if (cityInfo.isPuppet) {
|
||||
cityInfo.cityAIFocus = CityFocus.GoldFocus
|
||||
cityInfo.reassignAllPopulation()
|
||||
} else if (cityInfo.updateCitizens) {
|
||||
cityInfo.reassignPopulation()
|
||||
cityInfo.updateCitizens = false
|
||||
}
|
||||
|
||||
// The ordering is intentional - you get a turn without WLTKD even if you have the next resource already
|
||||
if (!cityInfo.hasFlag(CityFlags.WeLoveTheKing))
|
||||
tryWeLoveTheKing()
|
||||
nextTurnFlags()
|
||||
|
||||
// Seed resource demand countdown
|
||||
if (cityInfo.demandedResource == "" && !cityInfo.hasFlag(CityFlags.ResourceDemand)) {
|
||||
cityInfo.setFlag(
|
||||
CityFlags.ResourceDemand,
|
||||
(if (cityInfo.isCapital()) 25 else 15) + Random().nextInt(10))
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryWeLoveTheKing() {
|
||||
if (cityInfo.demandedResource == "") return
|
||||
if (cityInfo.civInfo.getCivResourcesByName()[cityInfo.demandedResource]!! > 0) {
|
||||
cityInfo.setFlag(CityFlags.WeLoveTheKing, 20 + 1) // +1 because it will be decremented by 1 in the same startTurn()
|
||||
cityInfo.civInfo.addNotification(
|
||||
"Because they have [${cityInfo.demandedResource}], the citizens of [${cityInfo.name}] are celebrating We Love The King Day!",
|
||||
cityInfo.location, NotificationCategory.General, NotificationIcon.City, NotificationIcon.Happiness)
|
||||
}
|
||||
}
|
||||
|
||||
// cf DiplomacyManager nextTurnFlags
|
||||
private fun nextTurnFlags() {
|
||||
for (flag in cityInfo.flagsCountdown.keys.toList()) {
|
||||
if (cityInfo.flagsCountdown[flag]!! > 0)
|
||||
cityInfo.flagsCountdown[flag] = cityInfo.flagsCountdown[flag]!! - 1
|
||||
|
||||
if (cityInfo.flagsCountdown[flag] == 0) {
|
||||
cityInfo.flagsCountdown.remove(flag)
|
||||
|
||||
when (flag) {
|
||||
CityFlags.ResourceDemand.name -> {
|
||||
demandNewResource()
|
||||
}
|
||||
CityFlags.WeLoveTheKing.name -> {
|
||||
cityInfo.civInfo.addNotification(
|
||||
"We Love The King Day in [${cityInfo.name}] has ended.",
|
||||
cityInfo.location, NotificationCategory.General, NotificationIcon.City)
|
||||
demandNewResource()
|
||||
}
|
||||
CityFlags.Resistance.name -> {
|
||||
cityInfo.civInfo.addNotification(
|
||||
"The resistance in [${cityInfo.name}] has ended!",
|
||||
cityInfo.location, NotificationCategory.General, "StatIcons/Resistance")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun demandNewResource() {
|
||||
val candidates = cityInfo.getRuleset().tileResources.values.filter {
|
||||
it.resourceType == ResourceType.Luxury && // Must be luxury
|
||||
!it.hasUnique(UniqueType.CityStateOnlyResource) && // Not a city-state only resource eg jewelry
|
||||
it.name != cityInfo.demandedResource && // Not same as last time
|
||||
!cityInfo.civInfo.hasResource(it.name) && // Not one we already have
|
||||
it.name in cityInfo.tileMap.resources && // Must exist somewhere on the map
|
||||
cityInfo.getCenterTile().getTilesInDistance(3).none { nearTile -> nearTile.resource == it.name } // Not in this city's radius
|
||||
}
|
||||
|
||||
val chosenResource = candidates.randomOrNull()
|
||||
/* What if we had a WLTKD before but now the player has every resource in the game? We can't
|
||||
pick a new resource, so the resource will stay stay the same and the city will demand it
|
||||
again even if the player still has it. But we shouldn't punish success. */
|
||||
if (chosenResource != null)
|
||||
cityInfo.demandedResource = chosenResource.name
|
||||
if (cityInfo.demandedResource == "") // Failed to get a valid resource, try again some time later
|
||||
cityInfo.setFlag(CityFlags.ResourceDemand, 15 + Random().nextInt(10))
|
||||
else
|
||||
cityInfo.civInfo.addNotification("[${cityInfo.name}] demands [${cityInfo.demandedResource}]!",
|
||||
cityInfo.location, NotificationCategory.General, NotificationIcon.City, "ResourceIcons/${cityInfo.demandedResource}")
|
||||
}
|
||||
|
||||
|
||||
fun endTurn() {
|
||||
val stats = cityInfo.cityStats.currentCityStats
|
||||
|
||||
cityInfo.cityConstructions.endTurn(stats)
|
||||
cityInfo.expansion.nextTurn(stats.culture)
|
||||
if (cityInfo.isBeingRazed) {
|
||||
val removedPopulation =
|
||||
1 + cityInfo.civInfo.getMatchingUniques(UniqueType.CitiesAreRazedXTimesFaster)
|
||||
.sumOf { it.params[0].toInt() - 1 }
|
||||
cityInfo.population.addPopulation(-1 * removedPopulation)
|
||||
|
||||
if (cityInfo.population.population <= 0) {
|
||||
cityInfo.civInfo.addNotification(
|
||||
"[${cityInfo.name}] has been razed to the ground!",
|
||||
cityInfo.location, NotificationCategory.General,
|
||||
"OtherIcons/Fire"
|
||||
)
|
||||
cityInfo.destroyCity()
|
||||
} else { //if not razed yet:
|
||||
if (cityInfo.population.foodStored >= cityInfo.population.getFoodToNextPopulation()) { //if surplus in the granary...
|
||||
cityInfo.population.foodStored =
|
||||
cityInfo.population.getFoodToNextPopulation() - 1 //...reduce below the new growth threshold
|
||||
}
|
||||
}
|
||||
} else cityInfo.population.nextTurn(cityInfo.foodForNextTurn())
|
||||
|
||||
// This should go after the population change, as that might impact the amount of followers in this city
|
||||
if (cityInfo.civInfo.gameInfo.isReligionEnabled()) cityInfo.religion.endTurn()
|
||||
|
||||
if (cityInfo in cityInfo.civInfo.cities) { // city was not destroyed
|
||||
cityInfo.health = min(cityInfo.health + 20, cityInfo.getMaxHealth())
|
||||
cityInfo.population.unassignExtraPopulation()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -10,6 +10,7 @@ import com.unciv.logic.UncivShowableException
|
||||
import com.unciv.logic.automation.ai.TacticalAI
|
||||
import com.unciv.logic.automation.unit.WorkerAutomation
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.city.managers.CityFounder
|
||||
import com.unciv.logic.civilization.diplomacy.CityStateFunctions
|
||||
import com.unciv.logic.civilization.diplomacy.CityStatePersonality
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFunctions
|
||||
@ -625,40 +626,22 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
|
||||
fun setTransients() {
|
||||
goldenAges.civInfo = this
|
||||
greatPeople.civInfo = this
|
||||
|
||||
civConstructions.setTransients(civInfo = this)
|
||||
|
||||
policies.civInfo = this
|
||||
if (policies.adoptedPolicies.size > 0 && policies.numberOfAdoptedPolicies == 0)
|
||||
policies.numberOfAdoptedPolicies = policies.adoptedPolicies.count { !Policy.isBranchCompleteByName(it) }
|
||||
policies.setTransients()
|
||||
|
||||
questManager.civInfo = this
|
||||
questManager.setTransients()
|
||||
|
||||
if (citiesCreated == 0 && cities.any())
|
||||
citiesCreated = cities.filter { it.name in nation.cities }.size
|
||||
|
||||
religionManager.civInfo = this // needs to be before tech, since tech setTransients looks at all uniques
|
||||
religionManager.setTransients()
|
||||
|
||||
tech.civInfo = this
|
||||
tech.setTransients()
|
||||
|
||||
policies.setTransients(this)
|
||||
questManager.setTransients(this)
|
||||
religionManager.setTransients(this) // needs to be before tech, since tech setTransients looks at all uniques
|
||||
tech.setTransients(this)
|
||||
ruinsManager.setTransients(this)
|
||||
espionageManager.setTransients(this)
|
||||
victoryManager.civInfo = this
|
||||
|
||||
for (diplomacyManager in diplomacy.values) {
|
||||
diplomacyManager.civInfo = this
|
||||
diplomacyManager.updateHasOpenBorders()
|
||||
}
|
||||
|
||||
espionageManager.setTransients(this)
|
||||
|
||||
victoryManager.civInfo = this
|
||||
|
||||
for (cityInfo in cities) {
|
||||
cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the currentPlayerCivInfo
|
||||
cityInfo.setTransients()
|
||||
cityInfo.setTransients(this) // must be before the city's setTransients because it depends on the tilemap, that comes from the currentPlayerCivInfo
|
||||
}
|
||||
|
||||
// Now that all tile transients have been updated, clean "worked" tiles that are not under the Civ's control
|
||||
@ -742,7 +725,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun addNotification(text: String, location: Vector2, category:NotificationCategory, vararg notificationIcons: String) {
|
||||
addNotification(text, LocationAction(location), category, *notificationIcons)
|
||||
}
|
||||
@ -757,7 +739,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
|
||||
fun addCity(location: Vector2) {
|
||||
val newCity = CityInfo(this, location)
|
||||
val newCity = CityFounder().foundCity(this, location)
|
||||
newCity.cityConstructions.chooseNextConstruction()
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,8 @@ class PolicyManager : IsPartOfGameInfoSerialization {
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
fun getPolicyByName(name: String): Policy = getRulesetPolicies()[name]!!
|
||||
|
||||
fun setTransients() {
|
||||
fun setTransients(civInfo: CivilizationInfo) {
|
||||
this.civInfo = civInfo
|
||||
for (policyName in adoptedPolicies) addPolicyToTransients(
|
||||
getPolicyByName(policyName)
|
||||
)
|
||||
|
@ -100,7 +100,8 @@ class QuestManager : IsPartOfGameInfoSerialization {
|
||||
return toReturn
|
||||
}
|
||||
|
||||
fun setTransients() {
|
||||
fun setTransients(civInfo: CivilizationInfo) {
|
||||
this.civInfo = civInfo
|
||||
for (quest in assignedQuests)
|
||||
quest.gameInfo = civInfo.gameInfo
|
||||
}
|
||||
|
@ -60,7 +60,8 @@ class ReligionManager : IsPartOfGameInfoSerialization {
|
||||
return clone
|
||||
}
|
||||
|
||||
fun setTransients() {
|
||||
fun setTransients(civInfo: CivilizationInfo) {
|
||||
this.civInfo = civInfo
|
||||
// Find our religion from the map of founded religions.
|
||||
// First check if there is any major religion
|
||||
religion = civInfo.gameInfo.religions.values.firstOrNull {
|
||||
|
@ -418,7 +418,8 @@ class TechManager : IsPartOfGameInfoSerialization {
|
||||
techUniques.addUniques(tech.uniqueObjects)
|
||||
}
|
||||
|
||||
fun setTransients() {
|
||||
fun setTransients(civInfo: CivilizationInfo) {
|
||||
this.civInfo = civInfo
|
||||
researchedTechnologies.addAll(techsResearched.map { getRuleset().technologies[it]!! })
|
||||
researchedTechnologies.forEach { addTechToTransients(it) }
|
||||
updateEra() // before updateTransientBooleans so era-based conditionals can work
|
||||
|
@ -3,6 +3,7 @@ package com.unciv.logic.civilization.managers
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.VictoryData
|
||||
import com.unciv.logic.automation.civilization.NextTurnAutomation
|
||||
import com.unciv.logic.city.managers.CityTurnManager
|
||||
import com.unciv.logic.civilization.AlertType
|
||||
import com.unciv.logic.civilization.CivFlags
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
@ -48,7 +49,7 @@ class TurnManager(val civInfo: CivilizationInfo) {
|
||||
civInfo.cache.updateCitiesConnectedToCapital()
|
||||
startTurnFlags()
|
||||
updateRevolts()
|
||||
for (city in civInfo.cities) city.startTurn() // Most expensive part of startTurn
|
||||
for (city in civInfo.cities) CityTurnManager(city).startTurn() // Most expensive part of startTurn
|
||||
|
||||
for (unit in civInfo.units.getCivUnits()) unit.startTurn()
|
||||
|
||||
@ -257,7 +258,7 @@ class TurnManager(val civInfo: CivilizationInfo) {
|
||||
yieldAll(civInfo.cities.filter { it.isBeingRazed })
|
||||
yieldAll(civInfo.cities.filterNot { it.isBeingRazed })
|
||||
}.toList()) { // a city can be removed while iterating (if it's being razed) so we need to iterate over a copy
|
||||
city.endTurn()
|
||||
CityTurnManager(city).endTurn()
|
||||
}
|
||||
|
||||
civInfo.temporaryUniques.endTurn()
|
||||
|
@ -9,9 +9,9 @@ import com.unciv.logic.civilization.transients.CapitalConnectionsFinder
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.ruleset.nation.Nation
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.nation.Nation
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.testing.GdxTestRunner
|
||||
@ -113,14 +113,13 @@ class CapitalConnectionsFinderTests {
|
||||
|
||||
private fun createCity(civInfo: CivilizationInfo, position: Vector2, name: String, capital: Boolean = false, hasHarbor: Boolean = false): CityInfo {
|
||||
return CityInfo().apply {
|
||||
this.civInfo = civInfo
|
||||
location = position
|
||||
if (capital)
|
||||
cityConstructions.builtBuildings.add(rules.buildings.values.first { it.hasUnique(UniqueType.IndicatesCapital) }.name)
|
||||
if (hasHarbor)
|
||||
cityConstructions.builtBuildings.add(rules.buildings.values.first { it.hasUnique(UniqueType.ConnectTradeRoutes) }.name)
|
||||
this.name = name
|
||||
setTransients()
|
||||
setTransients(civInfo)
|
||||
tilesMap[location].setOwningCity(this)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.GameInfo
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.city.managers.CityFounder
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.map.MapSizeNew
|
||||
@ -18,12 +19,12 @@ import com.unciv.models.ruleset.Belief
|
||||
import com.unciv.models.ruleset.BeliefType
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.IRulesetObject
|
||||
import com.unciv.models.ruleset.nation.Nation
|
||||
import com.unciv.models.ruleset.Policy
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.Specialist
|
||||
import com.unciv.models.ruleset.Speed
|
||||
import com.unciv.models.ruleset.nation.Nation
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
@ -133,7 +134,7 @@ class TestGame {
|
||||
replacePalace: Boolean = false,
|
||||
initialPopulation: Int = 0
|
||||
): CityInfo {
|
||||
val cityInfo = CityInfo(civInfo, tile.position)
|
||||
val cityInfo = CityFounder().foundCity(civInfo, tile.position)
|
||||
if (initialPopulation != 1)
|
||||
cityInfo.population.addPopulation(initialPopulation - 1) // With defaults this will remove population
|
||||
|
||||
|
Reference in New Issue
Block a user