mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 15:27:50 +07:00
Made CN tower functional, and free buildings are removed on capture (#5206)
* Refactored free buildings from civ.policies to civ.constructions * Made CN tower function like in the base game * Fixed random buildings being marked culture buildings
This commit is contained in:
@ -43,7 +43,8 @@ class CityConstructions {
|
||||
val inProgressConstructions = HashMap<String, Int>()
|
||||
var currentConstructionFromQueue: String
|
||||
get() {
|
||||
if (constructionQueue.isEmpty()) return "" else return constructionQueue.first()
|
||||
return if (constructionQueue.isEmpty()) ""
|
||||
else constructionQueue.first()
|
||||
}
|
||||
set(value) {
|
||||
if (constructionQueue.isEmpty()) constructionQueue.add(value) else constructionQueue[0] = value
|
||||
@ -53,6 +54,9 @@ class CityConstructions {
|
||||
var productionOverflow = 0
|
||||
val queueMaxSize = 10
|
||||
|
||||
// Maps cities to the buildings they received
|
||||
val freeBuildingsProvidedFromThisCity: HashMap<String, HashSet<String>> = hashMapOf()
|
||||
|
||||
//region pure functions
|
||||
fun clone(): CityConstructions {
|
||||
val toReturn = CityConstructions()
|
||||
@ -61,17 +65,22 @@ class CityConstructions {
|
||||
toReturn.currentConstructionIsUserSet = currentConstructionIsUserSet
|
||||
toReturn.constructionQueue.addAll(constructionQueue)
|
||||
toReturn.productionOverflow = productionOverflow
|
||||
toReturn.freeBuildingsProvidedFromThisCity.putAll(freeBuildingsProvidedFromThisCity)
|
||||
return toReturn
|
||||
}
|
||||
|
||||
internal fun getBuildableBuildings(): Sequence<Building> = cityInfo.getRuleset().buildings.values
|
||||
.asSequence().filter { it.isBuildable(this) }
|
||||
.asSequence().filter { it.isBuildable(this) }
|
||||
|
||||
fun getConstructableUnits() = cityInfo.getRuleset().units.values
|
||||
.asSequence().filter { it.isBuildable(this) }
|
||||
.asSequence().filter { it.isBuildable(this) }
|
||||
|
||||
fun getBasicCultureBuildings() = cityInfo.getRuleset().buildings.values
|
||||
.asSequence().filter { it.culture > 0f && !it.isAnyWonder() && it.replaces == null }
|
||||
.asSequence().filter { it.culture > 0f && !it.isAnyWonder() && it.replaces == null }
|
||||
|
||||
fun getBasicStatBuildings(stat: Stat) = cityInfo.getRuleset().buildings.values
|
||||
.asSequence()
|
||||
.filter { !it.isAnyWonder() && it.replaces == null && it.getStats(null)[stat] > 0f }
|
||||
|
||||
/**
|
||||
* @return [Stats] provided by all built buildings in city plus the bonus from Library
|
||||
@ -121,13 +130,14 @@ class CityConstructions {
|
||||
*/
|
||||
fun getMaintenanceCosts(): Int {
|
||||
var maintenanceCost = 0
|
||||
// We cache this to increase performance
|
||||
val freeBuildings = cityInfo.civInfo.policies.getListOfFreeBuildings(cityInfo.id)
|
||||
val freeBuildings = cityInfo.civInfo.civConstructions.getFreeBuildings(cityInfo.id)
|
||||
|
||||
for (building in getBuiltBuildings()) {
|
||||
if (building.name !in freeBuildings) {
|
||||
maintenanceCost += building.maintenance
|
||||
}
|
||||
}
|
||||
|
||||
return maintenanceCost
|
||||
}
|
||||
|
||||
@ -151,7 +161,27 @@ class CityConstructions {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun addFreeBuildings() {
|
||||
// "Provides a free [buildingName] [cityFilter]"
|
||||
for (unique in cityInfo.getMatchingUniques("Provides a free [] []")) {
|
||||
val freeBuildingName = cityInfo.civInfo.getEquivalentBuilding(unique.params[0]).name
|
||||
val citiesThatApply = when (unique.params[1]) {
|
||||
"in this city" -> listOf(cityInfo)
|
||||
"in other cities" -> cityInfo.civInfo.cities.filter { it !== cityInfo }
|
||||
else -> cityInfo.civInfo.cities.filter { it.matchesFilter(unique.params[1]) }
|
||||
}
|
||||
|
||||
for (city in citiesThatApply) {
|
||||
if (city.cityConstructions.containsBuildingOrEquivalent(freeBuildingName)) continue
|
||||
city.cityConstructions.addBuilding(freeBuildingName)
|
||||
if (city.id !in freeBuildingsProvidedFromThisCity)
|
||||
freeBuildingsProvidedFromThisCity[city.id] = hashSetOf()
|
||||
|
||||
freeBuildingsProvidedFromThisCity[city.id]!!.add(freeBuildingName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @constructionName needs to be a non-perpetual construction, else an empty string is returned */
|
||||
internal fun getTurnsToConstructionString(constructionName: String, useStoredProduction:Boolean = true): String {
|
||||
@ -173,20 +203,6 @@ class CityConstructions {
|
||||
return lines.joinToString("\n", "\n")
|
||||
}
|
||||
|
||||
// This function appears unused, can it be removed?
|
||||
fun getProductionForTileInfo(): String {
|
||||
/* this is because there were rare errors that I assume were caused because
|
||||
currentConstruction changed on another thread */
|
||||
val currentConstructionSnapshot = currentConstructionFromQueue
|
||||
var result = currentConstructionSnapshot.tr()
|
||||
if (currentConstructionSnapshot != ""
|
||||
&& !PerpetualConstruction.perpetualConstructionsMap.containsKey(currentConstructionSnapshot)) {
|
||||
val turnsLeft = turnsToConstruction(currentConstructionSnapshot)
|
||||
result += " - $turnsLeft${Fonts.turn}"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun getProductionMarkup(ruleset: Ruleset): FormattedLine {
|
||||
val currentConstructionSnapshot = currentConstructionFromQueue
|
||||
if (currentConstructionSnapshot.isEmpty()) return FormattedLine()
|
||||
@ -519,8 +535,7 @@ class CityConstructions {
|
||||
&& it.params[2] == stat.name
|
||||
}
|
||||
) {
|
||||
cityInfo.civInfo.boughtConstructionsWithGloballyIncreasingPrice[constructionName] =
|
||||
(cityInfo.civInfo.boughtConstructionsWithGloballyIncreasingPrice[constructionName] ?: 0) + 1
|
||||
cityInfo.civInfo.civConstructions.boughtItemsWithIncreasingPrice.add(constructionName, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -530,26 +545,26 @@ class CityConstructions {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun hasBuildableCultureBuilding(): Boolean {
|
||||
return getBasicCultureBuildings()
|
||||
.map { cityInfo.civInfo.getEquivalentBuilding(it.name) }
|
||||
.filter { it.isBuildable(this) || isBeingConstructedOrEnqueued(it.name) }
|
||||
.any()
|
||||
|
||||
fun hasBuildableStatBuildings(stat: Stat): Boolean {
|
||||
return getBasicStatBuildings(stat)
|
||||
.map { cityInfo.civInfo.getEquivalentBuilding(it.name) }
|
||||
.filter { it.isBuildable(this) || isBeingConstructedOrEnqueued(it.name) }
|
||||
.any()
|
||||
}
|
||||
|
||||
fun addCultureBuilding(): String? {
|
||||
val buildableCultureBuildings = getBasicCultureBuildings()
|
||||
.map { cityInfo.civInfo.getEquivalentBuilding(it.name) }
|
||||
.filter { it.isBuildable(this) || isBeingConstructedOrEnqueued(it.name) }
|
||||
fun addCheapestBuildableStatBuilding(stat: Stat): String? {
|
||||
val cheapestBuildableStatBuilding = getBasicStatBuildings(stat)
|
||||
.map { cityInfo.civInfo.getEquivalentBuilding(it.name) }
|
||||
.filter { it.isBuildable(this) || isBeingConstructedOrEnqueued(it.name) }
|
||||
.minByOrNull { it.cost }?.name
|
||||
|
||||
if (!buildableCultureBuildings.any())
|
||||
if (cheapestBuildableStatBuilding == null)
|
||||
return null
|
||||
|
||||
val cultureBuildingToBuild = buildableCultureBuildings.minByOrNull { it.cost }!!.name
|
||||
constructionComplete(getConstruction(cultureBuildingToBuild) as INonPerpetualConstruction)
|
||||
constructionComplete(getConstruction(cheapestBuildableStatBuilding) as INonPerpetualConstruction)
|
||||
|
||||
return cultureBuildingToBuild
|
||||
return cheapestBuildableStatBuilding
|
||||
}
|
||||
|
||||
private fun removeCurrentConstruction() = removeFromQueue(0, true)
|
||||
|
@ -144,7 +144,7 @@ class CityInfo {
|
||||
cityConstructions.addBuilding(uniqueBuilding.name)
|
||||
}
|
||||
|
||||
civInfo.policies.tryToAddPolicyBuildings()
|
||||
civInfo.civConstructions.tryAddFreeBuildings()
|
||||
|
||||
for (unique in getMatchingUniques("Gain a free [] []")) {
|
||||
val freeBuildingName = unique.params[0]
|
||||
@ -467,6 +467,7 @@ class CityInfo {
|
||||
// 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
|
||||
@ -632,8 +633,8 @@ class CityInfo {
|
||||
fun matchesFilter(filter: String, viewingCiv: CivilizationInfo = civInfo): Boolean {
|
||||
return when (filter) {
|
||||
"in this city" -> true
|
||||
"in all cities" -> true // Filtered by the way uniques our found
|
||||
"in other cities" -> true // Filtered by the way uniques our found
|
||||
"in all cities" -> true // Filtered by the way uniques are found
|
||||
"in other cities" -> true // Filtered by the way uniques are found
|
||||
"in all coastal cities" -> isCoastal()
|
||||
"in capital" -> isCapital()
|
||||
"in all non-occupied cities" -> !cityStats.hasExtraAnnexUnhappiness() || isPuppet
|
||||
|
@ -32,7 +32,8 @@ class CityInfoConquestFunctions(val city: CityInfo){
|
||||
}
|
||||
|
||||
private fun destroyBuildingsOnCapture() {
|
||||
city.apply {
|
||||
city.apply {
|
||||
// Possibly remove other buildings
|
||||
for (building in cityConstructions.getBuiltBuildings()) {
|
||||
when {
|
||||
building.hasUnique("Never destroyed when the city is captured") || building.isWonder -> continue
|
||||
@ -47,6 +48,32 @@ class CityInfoConquestFunctions(val city: CityInfo){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeBuildingsOnMoveToCiv(oldCiv: CivilizationInfo) {
|
||||
city.apply {
|
||||
// Remove all buildings provided for free to this city
|
||||
for (building in civInfo.civConstructions.getFreeBuildings(id)) {
|
||||
cityConstructions.removeBuilding(building)
|
||||
}
|
||||
|
||||
// Remove all buildings provided for free from here to other cities (e.g. CN Tower)
|
||||
println("Removing buildings: ${cityConstructions.freeBuildingsProvidedFromThisCity}")
|
||||
for ((cityId, buildings) in cityConstructions.freeBuildingsProvidedFromThisCity) {
|
||||
val city = oldCiv.cities.firstOrNull { it.id == cityId } ?: continue
|
||||
println("Removing buildings $buildings from city ${city.name}")
|
||||
for (building in buildings) {
|
||||
city.cityConstructions.removeBuilding(building)
|
||||
}
|
||||
}
|
||||
cityConstructions.freeBuildingsProvidedFromThisCity.clear()
|
||||
|
||||
// Remove national wonders
|
||||
for (building in cityConstructions.getBuiltBuildings()) {
|
||||
if (building.isNationalWonder && !building.hasUnique("Never destroyed when the city is captured"))
|
||||
cityConstructions.removeBuilding(building.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Function for stuff that should happen on any capture, be it puppet, annex or liberate.
|
||||
* Stuff that should happen any time a city is moved between civs, so also when trading,
|
||||
@ -223,11 +250,11 @@ class CityInfoConquestFunctions(val city: CityInfo){
|
||||
oldCiv.cities.first().cityConstructions.addBuilding(capitalCityIndicator) // relocate palace
|
||||
}
|
||||
}
|
||||
|
||||
for (building in cityConstructions.getBuiltBuildings()) {
|
||||
if (building.isNationalWonder && !building.hasUnique("Never destroyed when the city is captured"))
|
||||
cityConstructions.removeBuilding(building.name)
|
||||
}
|
||||
|
||||
// Remove their free buildings from this city and remove free buildings provided by the city from their cities
|
||||
removeBuildingsOnMoveToCiv(oldCiv)
|
||||
// Add our free buildings to this city and add free buildings provided by the city to other cities
|
||||
civInfo.civConstructions.tryAddFreeBuildings()
|
||||
|
||||
// Place palace for newCiv if this is the only city they have
|
||||
if (newCivInfo.cities.count() == 1) {
|
||||
|
165
core/src/com/unciv/logic/civilization/CivConstructions.kt
Normal file
165
core/src/com/unciv/logic/civilization/CivConstructions.kt
Normal file
@ -0,0 +1,165 @@
|
||||
package com.unciv.logic.civilization
|
||||
|
||||
import com.unciv.logic.city.INonPerpetualConstruction
|
||||
import com.unciv.models.Counter
|
||||
import com.unciv.models.stats.Stat
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
class CivConstructions() {
|
||||
|
||||
@Transient
|
||||
lateinit var civInfo: CivilizationInfo
|
||||
|
||||
// Maps objects to the amount of times bought
|
||||
val boughtItemsWithIncreasingPrice: Counter<String> = Counter()
|
||||
|
||||
// Maps to cities to all free buildings they contain
|
||||
private val freeBuildings: HashMap<String,HashSet<String>> = hashMapOf()
|
||||
|
||||
// Maps stats to the cities that have received a building of that stat
|
||||
// Android Studio says an EnumMap would be better, but that thing isn't serializable.
|
||||
// I don't know how to suppress that hint :(
|
||||
private val freeStatBuildingsProvided: HashMap<Stat, HashSet<String>> = hashMapOf()
|
||||
|
||||
// Maps buildings to the cities that have received that building
|
||||
private val freeSpecificBuildingsProvided: HashMap<String, HashSet<String>> = hashMapOf()
|
||||
|
||||
init {
|
||||
for (stat in Stat.values()) {
|
||||
freeStatBuildingsProvided[stat] = hashSetOf()
|
||||
}
|
||||
}
|
||||
|
||||
fun clone(): CivConstructions {
|
||||
val toReturn = CivConstructions()
|
||||
toReturn.civInfo = civInfo
|
||||
toReturn.freeBuildings.putAll(freeBuildings)
|
||||
toReturn.freeStatBuildingsProvided.putAll(freeStatBuildingsProvided)
|
||||
toReturn.freeSpecificBuildingsProvided.putAll(freeSpecificBuildingsProvided)
|
||||
toReturn.boughtItemsWithIncreasingPrice.add(boughtItemsWithIncreasingPrice.clone())
|
||||
return toReturn
|
||||
}
|
||||
|
||||
fun setTransients(civInfo: CivilizationInfo) {
|
||||
this.civInfo = civInfo
|
||||
|
||||
// civInfo.boughtConstructionsWithGloballyIncreasingPrice deprecated since 3.16.15, this is replacement code
|
||||
if (civInfo.boughtConstructionsWithGloballyIncreasingPrice.isNotEmpty()) {
|
||||
for (item in civInfo.boughtConstructionsWithGloballyIncreasingPrice) {
|
||||
boughtItemsWithIncreasingPrice.add(item.key, item.value)
|
||||
}
|
||||
civInfo.boughtConstructionsWithGloballyIncreasingPrice.clear()
|
||||
}
|
||||
//
|
||||
|
||||
// Deprecated variables in civ.policies since 3.16.15, this is replacement code
|
||||
if (civInfo.policies.specificBuildingsAdded.isNotEmpty()) {
|
||||
for ((building, cities) in civInfo.policies.specificBuildingsAdded) {
|
||||
for (cityId in cities) {
|
||||
if (building !in freeSpecificBuildingsProvided)
|
||||
freeSpecificBuildingsProvided[building] = hashSetOf()
|
||||
freeSpecificBuildingsProvided[building]!!.add(cityId)
|
||||
|
||||
if (cityId !in freeBuildings)
|
||||
freeBuildings[cityId] = hashSetOf()
|
||||
freeBuildings[cityId]!!.add(building)
|
||||
}
|
||||
}
|
||||
civInfo.policies.specificBuildingsAdded.clear()
|
||||
}
|
||||
|
||||
if (civInfo.policies.cultureBuildingsAdded.isNotEmpty()) {
|
||||
for ((cityId, building) in civInfo.policies.cultureBuildingsAdded) {
|
||||
freeStatBuildingsProvided[Stat.Culture]!!.add(cityId)
|
||||
|
||||
if (cityId !in freeBuildings)
|
||||
freeBuildings[cityId] = hashSetOf()
|
||||
freeBuildings[cityId]!!.add(building)
|
||||
}
|
||||
civInfo.policies.cultureBuildingsAdded.clear()
|
||||
}
|
||||
//
|
||||
}
|
||||
|
||||
fun startTurn() {
|
||||
tryAddFreeBuildings()
|
||||
}
|
||||
|
||||
fun tryAddFreeBuildings() {
|
||||
addFreeStatsBuildings()
|
||||
addFreeSpecificBuildings()
|
||||
}
|
||||
|
||||
fun getFreeBuildings(cityId: String): HashSet<String> {
|
||||
val toReturn = freeBuildings[cityId] ?: hashSetOf()
|
||||
for (city in civInfo.cities) {
|
||||
toReturn.addAll(city.cityConstructions.freeBuildingsProvidedFromThisCity[city.id] ?: hashSetOf())
|
||||
}
|
||||
return toReturn
|
||||
}
|
||||
|
||||
private fun addFreeBuilding(cityId: String, building: String) {
|
||||
if (!freeBuildings.containsKey(cityId))
|
||||
freeBuildings[cityId] = hashSetOf()
|
||||
freeBuildings[cityId]!!.add(building)
|
||||
}
|
||||
|
||||
private fun addFreeStatsBuildings() {
|
||||
val statUniquesData = civInfo.getMatchingUniques("Provides the cheapest [] building in your first [] cities for free")
|
||||
.groupBy { it.params[0] }
|
||||
.mapKeys { Stat.valueOf(it.key) }
|
||||
.mapValues { unique -> unique.value.sumOf { it.params[1].toInt() } }
|
||||
.toMutableMap()
|
||||
|
||||
// Deprecated since 3.16.15
|
||||
statUniquesData[Stat.Culture] = (statUniquesData[Stat.Culture] ?: 0) +
|
||||
civInfo.getMatchingUniques("Immediately creates the cheapest available cultural building in each of your first [] cities for free")
|
||||
.sumOf { it.params[1].toInt() }
|
||||
//
|
||||
|
||||
|
||||
for ((stat, amount) in statUniquesData) {
|
||||
addFreeStatBuildings(stat, amount)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFreeStatBuildings(stat: Stat, amount: Int) {
|
||||
for (city in civInfo.cities.take(amount)) {
|
||||
if (freeStatBuildingsProvided[stat]!!.contains(city.id) || !city.cityConstructions.hasBuildableStatBuildings(stat)) continue
|
||||
|
||||
val builtBuilding = city.cityConstructions.addCheapestBuildableStatBuilding(stat)
|
||||
if (builtBuilding != null) {
|
||||
freeStatBuildingsProvided[stat]!!.add(city.id)
|
||||
addFreeBuilding(city.id, builtBuilding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFreeSpecificBuildings() {
|
||||
val buildingsUniquesData = (civInfo.getMatchingUniques("Provides a [] in your first [] cities for free")
|
||||
// Deprecated since 3.16.15
|
||||
+ civInfo.getMatchingUniques("Immediately creates a [] in each of your first [] cities for free")
|
||||
//
|
||||
).groupBy { it.params[0] }
|
||||
.mapValues { unique -> unique.value.sumOf { it.params[1].toInt() } }
|
||||
|
||||
for ((building, amount) in buildingsUniquesData) {
|
||||
addFreeBuildings(building, amount)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFreeBuildings(building: String, amount: Int) {
|
||||
for (city in civInfo.cities.take(amount)) {
|
||||
if (freeSpecificBuildingsProvided[building]?.contains(city.id) == true || city.cityConstructions.containsBuildingOrEquivalent(building)) continue
|
||||
|
||||
(city.cityConstructions.getConstruction(building) as INonPerpetualConstruction).postBuildEvent(city.cityConstructions)
|
||||
|
||||
if (!freeSpecificBuildingsProvided.containsKey(building))
|
||||
freeSpecificBuildingsProvided[building] = hashSetOf()
|
||||
freeSpecificBuildingsProvided[building]!!.add(city.id)
|
||||
|
||||
addFreeBuilding(city.id, building)
|
||||
}
|
||||
}
|
||||
}
|
@ -101,6 +101,7 @@ class CivilizationInfo {
|
||||
var civName = ""
|
||||
var tech = TechManager()
|
||||
var policies = PolicyManager()
|
||||
var civConstructions = CivConstructions()
|
||||
var questManager = QuestManager()
|
||||
var religionManager = ReligionManager()
|
||||
var goldenAges = GoldenAgeManager()
|
||||
@ -124,8 +125,11 @@ class CivilizationInfo {
|
||||
*/
|
||||
val temporaryUniques = ArrayList<Pair<Unique, Int>>()
|
||||
|
||||
/** Maps the name of the construction to the amount of times bought */
|
||||
val boughtConstructionsWithGloballyIncreasingPrice = HashMap<String, Int>()
|
||||
// Deprecated since 3.16.15
|
||||
/** Maps the name of the construction to the amount of times bought */
|
||||
@Deprecated("Deprecated since 3.16.15", replaceWith = ReplaceWith("civWideConstructions.boughtItemsWithIncreasingPrice"))
|
||||
val boughtConstructionsWithGloballyIncreasingPrice = HashMap<String, Int>()
|
||||
//
|
||||
|
||||
// if we only use lists, and change the list each time the cities are changed,
|
||||
// we won't get concurrent modification exceptions.
|
||||
@ -153,6 +157,7 @@ class CivilizationInfo {
|
||||
toReturn.civName = civName
|
||||
toReturn.tech = tech.clone()
|
||||
toReturn.policies = policies.clone()
|
||||
toReturn.civConstructions = civConstructions.clone()
|
||||
toReturn.religionManager = religionManager.clone()
|
||||
toReturn.questManager = questManager.clone()
|
||||
toReturn.goldenAges = goldenAges.clone()
|
||||
@ -178,7 +183,9 @@ class CivilizationInfo {
|
||||
toReturn.cityStateUniqueUnit = cityStateUniqueUnit
|
||||
toReturn.flagsCountdown.putAll(flagsCountdown)
|
||||
toReturn.temporaryUniques.addAll(temporaryUniques)
|
||||
toReturn.boughtConstructionsWithGloballyIncreasingPrice.putAll(boughtConstructionsWithGloballyIncreasingPrice)
|
||||
// Deprecated since 3.16.15
|
||||
toReturn.boughtConstructionsWithGloballyIncreasingPrice.putAll(boughtConstructionsWithGloballyIncreasingPrice)
|
||||
//
|
||||
toReturn.hasEverOwnedOriginalCapital = hasEverOwnedOriginalCapital
|
||||
return toReturn
|
||||
}
|
||||
@ -557,6 +564,8 @@ class CivilizationInfo {
|
||||
fun setTransients() {
|
||||
goldenAges.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) }
|
||||
@ -602,7 +611,7 @@ class CivilizationInfo {
|
||||
fun updateDetailedCivResources() = transients().updateDetailedCivResources()
|
||||
|
||||
fun startTurn() {
|
||||
policies.startTurn()
|
||||
civConstructions.startTurn()
|
||||
updateStatsForNextTurn() // for things that change when turn passes e.g. golden age, city state influence
|
||||
|
||||
// Generate great people at the start of the turn,
|
||||
|
@ -30,8 +30,12 @@ class PolicyManager {
|
||||
var shouldOpenPolicyPicker = false
|
||||
get() = field && canAdoptPolicy()
|
||||
|
||||
private var cultureBuildingsAdded = HashMap<String, String>() // Maps cities to buildings
|
||||
private var specificBuildingsAdded = HashMap<String, MutableSet<String>>() // Maps buildings to cities
|
||||
// Deprecated since 3.16.15
|
||||
@Deprecated("Deprecated since 3.16.15", ReplaceWith("civInfo.civWideConstructions.freeStatBuildingsProvided[Stat.Culture]"))
|
||||
var cultureBuildingsAdded = HashMap<String, String>() // Maps cities to buildings
|
||||
@Deprecated("Deprecated since 3.16.15", ReplaceWith("civInfo.civWideConstructions.freeSpecificBuildingsProvided"))
|
||||
var specificBuildingsAdded = HashMap<String, MutableSet<String>>() // Maps buildings to cities
|
||||
//
|
||||
|
||||
|
||||
fun clone(): PolicyManager {
|
||||
@ -41,8 +45,10 @@ class PolicyManager {
|
||||
toReturn.freePolicies = freePolicies
|
||||
toReturn.shouldOpenPolicyPicker = shouldOpenPolicyPicker
|
||||
toReturn.storedCulture = storedCulture
|
||||
toReturn.cultureBuildingsAdded.putAll(cultureBuildingsAdded)
|
||||
toReturn.specificBuildingsAdded.putAll(specificBuildingsAdded)
|
||||
// Deprecated since 3.16.15
|
||||
toReturn.cultureBuildingsAdded.putAll(cultureBuildingsAdded)
|
||||
toReturn.specificBuildingsAdded.putAll(specificBuildingsAdded)
|
||||
//
|
||||
|
||||
return toReturn
|
||||
}
|
||||
@ -70,10 +76,6 @@ class PolicyManager {
|
||||
policyUniques.addUnique(unique)
|
||||
}
|
||||
|
||||
fun startTurn() {
|
||||
tryToAddPolicyBuildings()
|
||||
}
|
||||
|
||||
fun addCulture(culture: Int) {
|
||||
val couldAdoptPolicyBefore = canAdoptPolicy()
|
||||
storedCulture += culture
|
||||
@ -171,8 +173,6 @@ class PolicyManager {
|
||||
for (unique in policy.uniqueObjects)
|
||||
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
|
||||
|
||||
tryToAddPolicyBuildings()
|
||||
|
||||
// This ALSO has the side-effect of updating the CivInfo statForNextTurn so we don't need to call it explicitly
|
||||
for (cityInfo in civInfo.cities)
|
||||
cityInfo.cityStats.update()
|
||||
@ -180,65 +180,6 @@ class PolicyManager {
|
||||
if (!canAdoptPolicy()) shouldOpenPolicyPicker = false
|
||||
}
|
||||
|
||||
fun tryToAddPolicyBuildings() {
|
||||
tryAddCultureBuildings()
|
||||
tryAddFreeBuildings()
|
||||
}
|
||||
|
||||
private fun tryAddCultureBuildings() {
|
||||
val cultureBuildingUniques = civInfo.getMatchingUniques("Immediately creates the cheapest available cultural building in each of your first [] cities for free")
|
||||
val citiesToReceiveCultureBuilding = cultureBuildingUniques.sumOf { it.params[0].toInt() }
|
||||
if (!cultureBuildingUniques.any()) return
|
||||
if (cultureBuildingsAdded.size >= citiesToReceiveCultureBuilding) return
|
||||
|
||||
val candidateCities = civInfo.cities
|
||||
.sortedBy { it.turnAcquired }
|
||||
.subList(0, min(citiesToReceiveCultureBuilding, civInfo.cities.size))
|
||||
.filter {
|
||||
it.id !in cultureBuildingsAdded
|
||||
&& it.cityConstructions.hasBuildableCultureBuilding()
|
||||
}
|
||||
for (city in candidateCities) {
|
||||
val builtBuilding = city.cityConstructions.addCultureBuilding()
|
||||
if (builtBuilding != null) cultureBuildingsAdded[city.id] = builtBuilding
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryAddFreeBuildings() {
|
||||
val matchingUniques = civInfo.getMatchingUniques("Immediately creates a [] in each of your first [] cities for free")
|
||||
// If we have "create a free aqueduct in first 3 cities" and "create free aqueduct in first 4 cities", we do: "create free aqueduct in first 3+4=7 cities"
|
||||
val sortedUniques = matchingUniques.groupBy {it.params[0]}
|
||||
for (unique in sortedUniques) {
|
||||
tryAddSpecificBuilding(unique.key, unique.value.sumOf {it.params[1].toInt()})
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryAddSpecificBuilding(building: String, cityCount: Int) {
|
||||
if (specificBuildingsAdded[building] == null) specificBuildingsAdded[building] = mutableSetOf()
|
||||
val citiesAlreadyGivenBuilding = specificBuildingsAdded[building]
|
||||
if (citiesAlreadyGivenBuilding!!.size >= cityCount) return
|
||||
val candidateCities = civInfo.cities
|
||||
.sortedBy { it.turnAcquired }
|
||||
.subList(0, min(cityCount, civInfo.cities.size))
|
||||
.filter {
|
||||
it.id !in citiesAlreadyGivenBuilding && !it.cityConstructions.containsBuildingOrEquivalent(building)
|
||||
}
|
||||
|
||||
for (city in candidateCities) {
|
||||
(city.cityConstructions.getConstruction(building) as INonPerpetualConstruction).postBuildEvent(city.cityConstructions)
|
||||
citiesAlreadyGivenBuilding.add(city.id)
|
||||
}
|
||||
}
|
||||
|
||||
fun getListOfFreeBuildings(cityId: String): MutableSet<String> {
|
||||
val freeBuildings = cultureBuildingsAdded.filter { it.key == cityId }.values.toMutableSet()
|
||||
for (building in specificBuildingsAdded.filter { it.value.contains(cityId) }) {
|
||||
freeBuildings.add(building.key)
|
||||
}
|
||||
return freeBuildings
|
||||
}
|
||||
|
||||
private fun triggerGlobalAlerts(policy: Policy, extraNotificationText: String = "") {
|
||||
var extraNotificationTextCopy = extraNotificationText
|
||||
if (extraNotificationText != "") {
|
||||
|
@ -28,7 +28,7 @@ class ReligionManager {
|
||||
// contain the master list, and the ReligionManagers retrieve it from there every time the game loads.
|
||||
|
||||
// Deprecated since 3.16.13
|
||||
@Deprecated("Replace by adding to `civInfo.boughtConstructionsWithGloballyIncreasingPrice`")
|
||||
@Deprecated("Replace by adding to `civInfo.civWideConstructions.boughtItemsWithIncreasingPrice`")
|
||||
var greatProphetsEarned = 0
|
||||
private set
|
||||
//
|
||||
@ -67,7 +67,7 @@ class ReligionManager {
|
||||
|
||||
// greatProphetsEarned deprecated since 3.16.13, replacement code
|
||||
if (greatProphetsEarned != 0) {
|
||||
civInfo.boughtConstructionsWithGloballyIncreasingPrice[getGreatProphetEquivalent()!!] = greatProphetsEarned
|
||||
civInfo.civConstructions.boughtItemsWithIncreasingPrice[getGreatProphetEquivalent()!!] = greatProphetsEarned
|
||||
greatProphetsEarned = 0
|
||||
}
|
||||
//
|
||||
@ -115,7 +115,7 @@ class ReligionManager {
|
||||
// https://www.reddit.com/r/civ/comments/2m82wu/can_anyone_detail_the_finer_points_of_great/
|
||||
// Game files (globaldefines.xml)
|
||||
fun faithForNextGreatProphet(): Int {
|
||||
val greatProphetsEarned = civInfo.boughtConstructionsWithGloballyIncreasingPrice[getGreatProphetEquivalent()!!] ?: 0
|
||||
val greatProphetsEarned = civInfo.civConstructions.boughtItemsWithIncreasingPrice[getGreatProphetEquivalent()!!] ?: 0
|
||||
|
||||
var faithCost =
|
||||
(200 + 100 * greatProphetsEarned * (greatProphetsEarned + 1) / 2f) *
|
||||
@ -152,8 +152,7 @@ class ReligionManager {
|
||||
val prophet = civInfo.addUnit(prophetUnitName, birthCity) ?: return
|
||||
prophet.religion = religion!!.name
|
||||
storedFaith -= faithForNextGreatProphet()
|
||||
civInfo.boughtConstructionsWithGloballyIncreasingPrice[prophetUnitName] =
|
||||
(civInfo.boughtConstructionsWithGloballyIncreasingPrice[prophetUnitName] ?: 0) + 1
|
||||
civInfo.civConstructions.boughtItemsWithIncreasingPrice.add(prophetUnitName, 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ class RuinsManager {
|
||||
if (civInfo.gameInfo.difficulty in possibleReward.excludedDifficulties) continue
|
||||
if (Constants.hiddenWithoutReligionUnique in possibleReward.uniques && !civInfo.gameInfo.hasReligionEnabled()) continue
|
||||
if ("Hidden after generating a Great Prophet" in possibleReward.uniques
|
||||
&& civInfo.boughtConstructionsWithGloballyIncreasingPrice[civInfo.religionManager.getGreatProphetEquivalent()] ?: 0 > 0
|
||||
&& civInfo.civConstructions.boughtItemsWithIncreasingPrice[civInfo.religionManager.getGreatProphetEquivalent()] ?: 0 > 0
|
||||
) continue
|
||||
if (possibleReward.uniqueObjects.any { unique ->
|
||||
unique.placeholderText == "Only available after [] turns"
|
||||
|
@ -151,7 +151,8 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText {
|
||||
}
|
||||
|
||||
fun getStats(city: CityInfo?): Stats {
|
||||
val stats = this.clone()
|
||||
// Calls the clone function of the NamedStats this class is derived from, not a clone function of this class
|
||||
val stats = this.clone()
|
||||
if (city == null) return stats
|
||||
val civInfo = city.civInfo
|
||||
|
||||
@ -651,20 +652,7 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText {
|
||||
}
|
||||
|
||||
// "Provides a free [buildingName] [cityFilter]"
|
||||
val freeBuildingUniques = uniqueObjects.asSequence().filter { it.placeholderText=="Provides a free [] []" }
|
||||
|
||||
for (unique in freeBuildingUniques) {
|
||||
val affectedCities =
|
||||
if (unique.params[1] == "in this city") sequenceOf(cityConstructions.cityInfo)
|
||||
else civInfo.cities.asSequence().filter { it.matchesFilter(unique.params[1]) }
|
||||
|
||||
val freeBuildingName = civInfo.getEquivalentBuilding(unique.params[0]).name
|
||||
|
||||
for (city in affectedCities) {
|
||||
if (cityConstructions.containsBuildingOrEquivalent(freeBuildingName)) continue
|
||||
cityConstructions.addBuilding(freeBuildingName)
|
||||
}
|
||||
}
|
||||
cityConstructions.addFreeBuildings()
|
||||
|
||||
for (unique in uniqueObjects)
|
||||
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo, cityConstructions.cityInfo)
|
||||
|
@ -457,6 +457,10 @@ object UniqueTriggerActivation {
|
||||
civInfo.addNotification(notification, NotificationIcon.Diplomacy)
|
||||
return true
|
||||
}
|
||||
|
||||
"Provides the cheapest [] building in your first [] cities for free",
|
||||
"Provides a [] in your first [] cities for free" ->
|
||||
civInfo.civConstructions.tryAddFreeBuildings()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.civilopedia.ICivilopediaText
|
||||
import com.unciv.ui.utils.Fonts
|
||||
import com.unciv.ui.utils.toPercent
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
@ -254,7 +253,7 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
|
||||
getCostForConstructionsIncreasingInPrice(
|
||||
it.params[1].toInt(),
|
||||
it.params[5].toInt(),
|
||||
cityInfo.civInfo.boughtConstructionsWithGloballyIncreasingPrice[name] ?: 0
|
||||
cityInfo.civInfo.civConstructions.boughtItemsWithIncreasingPrice[name] ?: 0
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -267,7 +266,7 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
|
||||
getCostForConstructionsIncreasingInPrice(
|
||||
it.params[1].toInt(),
|
||||
it.params[4].toInt(),
|
||||
cityInfo.civInfo.boughtConstructionsWithGloballyIncreasingPrice[name] ?: 0
|
||||
cityInfo.civInfo.civConstructions.boughtItemsWithIncreasingPrice[name] ?: 0
|
||||
)
|
||||
}
|
||||
)
|
||||
|
Reference in New Issue
Block a user