mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-25 10:26:05 +07:00
Allowed adding arbitrary global uniques to city state bonuses (#8028)
* Allowed adding arbitrary global uniques to city state bonuses * Added conditional support for city-state uniques
This commit is contained in:
parent
694e0d33fc
commit
2d5536e8be
@ -491,6 +491,13 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
||||
|
||||
for (religion in religions.values) religion.setTransients(this)
|
||||
|
||||
// must be done before updating tech manager bools, since that depends on allied city-states
|
||||
for (civInfo in civilizations)
|
||||
for (diplomacyManager in civInfo.diplomacy.values) {
|
||||
diplomacyManager.civInfo = civInfo
|
||||
diplomacyManager.updateHasOpenBorders()
|
||||
}
|
||||
|
||||
for (civInfo in civilizations) civInfo.setTransients()
|
||||
for (civInfo in civilizations) {
|
||||
civInfo.thingsToFocusOnForVictory =
|
||||
|
@ -156,9 +156,8 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
if (otherCiv.cityStateType == CityStateType.Maritime && cityInfo.isCapital())
|
||||
stats.food += 2
|
||||
} else {
|
||||
for (bonus in eraInfo.getCityStateBonuses(otherCiv.cityStateType, relationshipLevel)) {
|
||||
if (bonus.isOfType(UniqueType.CityStateStatsPerCity)
|
||||
&& cityInfo.matchesFilter(bonus.params[1])
|
||||
for (bonus in eraInfo.getCityStateBonuses(otherCiv.cityStateType, relationshipLevel, UniqueType.CityStateStatsPerCity)) {
|
||||
if (cityInfo.matchesFilter(bonus.params[1])
|
||||
&& bonus.conditionalsApply(otherCiv, cityInfo)
|
||||
) stats.add(bonus.stats)
|
||||
}
|
||||
|
@ -2,18 +2,19 @@ package com.unciv.logic.civilization
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.automation.civilization.NextTurnAutomation
|
||||
import com.unciv.logic.civilization.diplomacy.*
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
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.ui.victoryscreen.RankingType
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.collections.LinkedHashMap
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
|
||||
@ -22,40 +23,29 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
|
||||
/** Attempts to initialize the city state, returning true if successful. */
|
||||
fun initCityState(ruleset: Ruleset, startingEra: String, unusedMajorCivs: Collection<String>): Boolean {
|
||||
val cityStateType = ruleset.nations[civInfo.civName]?.cityStateType
|
||||
?: return false
|
||||
|
||||
val startingTechs = ruleset.technologies.values.filter { it.hasUnique(UniqueType.StartingTech) }
|
||||
for (tech in startingTechs)
|
||||
civInfo.tech.techsResearched.add(tech.name) // can't be .addTechnology because the civInfo isn't assigned yet
|
||||
|
||||
val allMercantileResources = ruleset.tileResources.values.filter { it.hasUnique(UniqueType.CityStateOnlyResource) }.map { it.name }
|
||||
val allPossibleBonuses = HashSet<Unique>() // We look through these to determine what kind of city state we are
|
||||
var fallback = false
|
||||
val uniqueTypes = HashSet<UniqueType>() // We look through these to determine what kind of city state we are
|
||||
for (era in ruleset.eras.values) {
|
||||
if (era.undefinedCityStateBonuses()) {
|
||||
fallback = true
|
||||
break
|
||||
}
|
||||
val allyBonuses = era.getCityStateBonuses(cityStateType, RelationshipLevel.Ally)
|
||||
val friendBonuses = era.getCityStateBonuses(cityStateType, RelationshipLevel.Friend)
|
||||
allPossibleBonuses.addAll(allyBonuses)
|
||||
allPossibleBonuses.addAll(friendBonuses)
|
||||
if (era.undefinedCityStateBonuses()) continue
|
||||
for (unique in era.friendBonusObjects.values.map { it.getAllUniques() } + era.allyBonusObjects.values.map { it.getAllUniques() })
|
||||
uniqueTypes.addAll(unique.mapNotNull { it.type })
|
||||
}
|
||||
|
||||
// CS Personality
|
||||
civInfo.cityStatePersonality = CityStatePersonality.values().random()
|
||||
|
||||
// Mercantile bonus resources
|
||||
if (allPossibleBonuses.any { it.isOfType(UniqueType.CityStateUniqueLuxury) }
|
||||
|| fallback && cityStateType == CityStateType.Mercantile) { // Fallback for badly defined Eras.json
|
||||
|
||||
if (uniqueTypes.contains(UniqueType.CityStateUniqueLuxury)) { // Fallback for badly defined Eras.json
|
||||
civInfo.cityStateResource = allMercantileResources.randomOrNull()
|
||||
}
|
||||
|
||||
// Unique unit for militaristic city-states
|
||||
if (allPossibleBonuses.any { it.isOfType(UniqueType.CityStateMilitaryUnits) }
|
||||
|| fallback && cityStateType == CityStateType.Militaristic // Fallback for badly defined Eras.json
|
||||
) {
|
||||
if (uniqueTypes.contains(UniqueType.CityStateMilitaryUnits)) {
|
||||
|
||||
val possibleUnits = ruleset.units.values.filter { it.requiredTech != null
|
||||
&& ruleset.eras[ruleset.technologies[it.requiredTech!!]!!.era()]!!.eraNumber > ruleset.eras[startingEra]!!.eraNumber // Not from the start era or before
|
||||
@ -438,15 +428,6 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
if (bonus.stats[statType] > 0 || (bonus.isOfType(UniqueType.CityStateHappiness) && statType == Stat.Happiness))
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// compatibility mode
|
||||
return when {
|
||||
civInfo.cityStateType == CityStateType.Mercantile && statType == Stat.Happiness -> true
|
||||
civInfo.cityStateType == CityStateType.Cultured && statType == Stat.Culture -> true
|
||||
civInfo.cityStateType == CityStateType.Maritime && statType == Stat.Food -> true
|
||||
civInfo.cityStateType == CityStateType.Religious && statType == Stat.Faith ->true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
@ -668,4 +649,19 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
addPositiveByResource(city.getCityResources(), Constants.cityStates)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Optimize, update whenever status changes, otherwise retain the same list
|
||||
fun getUniquesProvidedByCityStates(
|
||||
uniqueType: UniqueType,
|
||||
stateForConditionals: StateForConditionals
|
||||
):Sequence<Unique> {
|
||||
if (civInfo.isCityState()) return emptySequence()
|
||||
val era = civInfo.getEra()
|
||||
|
||||
if (era.undefinedCityStateBonuses()) return emptySequence()
|
||||
|
||||
return civInfo.getKnownCivs().asSequence().filter { it.isCityState() }
|
||||
.flatMap { era.getCityStateBonuses(it.cityStateType, civInfo.getDiplomacyManager(it).relationshipLevel(), uniqueType) }
|
||||
.filter { it.conditionalsApply(stateForConditionals) }
|
||||
}
|
||||
}
|
||||
|
@ -147,21 +147,8 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
|
||||
val eraInfo = civInfo.getEra()
|
||||
|
||||
if (!eraInfo.undefinedCityStateBonuses()) {
|
||||
for (bonus in eraInfo.getCityStateBonuses(otherCiv.cityStateType, relationshipLevel)) {
|
||||
if (bonus.isOfType(UniqueType.CityStateStatsPerTurn) && bonus.conditionalsApply(otherCiv))
|
||||
cityStateBonus.add(bonus.stats)
|
||||
}
|
||||
} else {
|
||||
// Deprecated, assume Civ V values for compatibility
|
||||
if (otherCiv.cityStateType == CityStateType.Cultured) {
|
||||
cityStateBonus.culture =
|
||||
when {
|
||||
civInfo.getEraNumber() in 0..1 -> 3f
|
||||
civInfo.getEraNumber() in 2..3 -> 6f
|
||||
else -> 13f
|
||||
}
|
||||
if (relationshipLevel == RelationshipLevel.Ally)
|
||||
cityStateBonus.culture *= 2f
|
||||
for (bonus in eraInfo.getCityStateBonuses(otherCiv.cityStateType, relationshipLevel, UniqueType.CityStateStatsPerTurn)) {
|
||||
cityStateBonus.add(bonus.stats)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -463,6 +463,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
|
||||
.filter { it.isOfType(uniqueType) && it.conditionalsApply(stateForConditionals) }
|
||||
)
|
||||
yieldAll(getEra().getMatchingUniques(uniqueType, stateForConditionals))
|
||||
yieldAll(cityStateFunctions.getUniquesProvidedByCityStates(uniqueType, stateForConditionals))
|
||||
if (religionManager.religion != null)
|
||||
yieldAll(religionManager.religion!!.getFounderUniques()
|
||||
.filter { it.isOfType(uniqueType) && it.conditionalsApply(stateForConditionals) })
|
||||
|
@ -646,11 +646,9 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization {
|
||||
|
||||
if (eraInfo.undefinedCityStateBonuses()) return
|
||||
|
||||
for (bonus in eraInfo.getCityStateBonuses(otherCiv().cityStateType, relationshipLevel())) {
|
||||
for (bonus in eraInfo.getCityStateBonuses(otherCiv().cityStateType, relationshipLevel(), UniqueType.CityStateMilitaryUnits)) {
|
||||
// Reset the countdown if it has ended, or if we have longer to go than the current maximum (can happen when going from friend to ally)
|
||||
if (bonus.isOfType(UniqueType.CityStateMilitaryUnits) &&
|
||||
(!hasFlag(DiplomacyFlags.ProvideMilitaryUnit) || getFlag(DiplomacyFlags.ProvideMilitaryUnit) > bonus.params[0].toInt())
|
||||
) {
|
||||
if (!hasFlag(DiplomacyFlags.ProvideMilitaryUnit) || getFlag(DiplomacyFlags.ProvideMilitaryUnit) > bonus.params[0].toInt()) {
|
||||
setFlag(DiplomacyFlags.ProvideMilitaryUnit, bonus.params[0].toInt() + variance)
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ package com.unciv.models.ruleset
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.unciv.logic.civilization.CityStateType
|
||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.models.ruleset.unique.IHasUniques
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueMap
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
@ -34,9 +34,9 @@ class Era : RulesetObject() {
|
||||
var friendBonus = HashMap<String, List<String>>()
|
||||
var allyBonus = HashMap<String, List<String>>()
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val friendBonusObjects: Map<CityStateType, List<Unique>> by lazy { initBonuses(friendBonus) }
|
||||
val friendBonusObjects: Map<CityStateType, UniqueMap> by lazy { initBonuses(friendBonus) }
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val allyBonusObjects: Map<CityStateType, List<Unique>> by lazy { initBonuses(allyBonus) }
|
||||
val allyBonusObjects: Map<CityStateType, UniqueMap> by lazy { initBonuses(allyBonus) }
|
||||
|
||||
private var iconRGB: List<Int>? = null
|
||||
|
||||
@ -87,20 +87,24 @@ class Era : RulesetObject() {
|
||||
}.map { it.first }.distinct()
|
||||
}
|
||||
|
||||
private fun initBonuses(bonusMap: Map<String, List<String>>): Map<CityStateType, List<Unique>> {
|
||||
val objectMap = HashMap<CityStateType, List<Unique>>()
|
||||
private fun initBonuses(bonusMap: Map<String, List<String>>): Map<CityStateType, UniqueMap> {
|
||||
val objectMap = HashMap<CityStateType, UniqueMap>()
|
||||
for ((cityStateType, bonusList) in bonusMap) {
|
||||
objectMap[CityStateType.valueOf(cityStateType)] = bonusList.map { Unique(it, UniqueTarget.CityState) }
|
||||
val cityStateTypeUniqueMap = UniqueMap()
|
||||
cityStateTypeUniqueMap.addUniques(bonusList.map { Unique(it, UniqueTarget.CityState)})
|
||||
objectMap[CityStateType.valueOf(cityStateType)] = cityStateTypeUniqueMap
|
||||
}
|
||||
return objectMap
|
||||
}
|
||||
|
||||
fun getCityStateBonuses(cityStateType: CityStateType, relationshipLevel: RelationshipLevel): List<Unique> {
|
||||
return when (relationshipLevel) {
|
||||
RelationshipLevel.Ally -> allyBonusObjects[cityStateType] ?: emptyList()
|
||||
RelationshipLevel.Friend -> friendBonusObjects[cityStateType] ?: emptyList()
|
||||
else -> emptyList()
|
||||
}
|
||||
fun getCityStateBonuses(cityStateType: CityStateType, relationshipLevel: RelationshipLevel, uniqueType:UniqueType?=null): Sequence<Unique> {
|
||||
val cityStateUniqueMap = when (relationshipLevel) {
|
||||
RelationshipLevel.Ally -> allyBonusObjects[cityStateType]
|
||||
RelationshipLevel.Friend -> friendBonusObjects[cityStateType]
|
||||
else -> null
|
||||
} ?: return emptySequence()
|
||||
return if (uniqueType == null) cityStateUniqueMap.getAllUniques()
|
||||
else cityStateUniqueMap.getUniques(uniqueType)
|
||||
}
|
||||
|
||||
/** Since 3.19.5 we have a warning for mods without bonuses, eventually we should treat such mods as providing no bonus */
|
||||
|
@ -288,7 +288,7 @@ class DiplomacyScreen(
|
||||
|
||||
/** Given a list of [bonuses], returns a list of pretty strings with updated values for Siam-like uniques
|
||||
* Assumes that each bonus contains only one stat type */
|
||||
private fun getAdjustedBonuses(bonuses: List<Unique>): List<String> {
|
||||
private fun getAdjustedBonuses(bonuses: Sequence<Unique>): List<String> {
|
||||
val bonusStrings = ArrayList<String>()
|
||||
for (bonus in bonuses) {
|
||||
var improved = false
|
||||
|
Loading…
Reference in New Issue
Block a user