Game can handle policies "disappearing" between mod versions

Added policy map to ruleset for simpler policy location
This commit is contained in:
Yair Morgenstern 2021-01-12 17:10:38 +02:00
parent 67bcc26ab7
commit a8e0f270a0
5 changed files with 82 additions and 70 deletions

View File

@ -24,6 +24,7 @@ class UncivShowableException(missingMods: String) : Exception(missingMods)
class GameInfo { class GameInfo {
@Transient @Transient
lateinit var difficultyObject: Difficulty // Since this is static game-wide, and was taking a large part of nextTurn lateinit var difficultyObject: Difficulty // Since this is static game-wide, and was taking a large part of nextTurn
@Transient @Transient
lateinit var currentPlayerCiv: CivilizationInfo // this is called thousands of times, no reason to search for it with a find{} every time lateinit var currentPlayerCiv: CivilizationInfo // this is called thousands of times, no reason to search for it with a find{} every time
@ -31,6 +32,7 @@ class GameInfo {
* that is inconsistent with the saved game on the cloud */ * that is inconsistent with the saved game on the cloud */
@Transient @Transient
var isUpToDate = false var isUpToDate = false
@Transient @Transient
lateinit var ruleSet: Ruleset lateinit var ruleSet: Ruleset
@ -356,6 +358,9 @@ class GameInfo {
for (tech in civinfo.tech.techsResearched.toList()) for (tech in civinfo.tech.techsResearched.toList())
if (!ruleSet.technologies.containsKey(tech)) if (!ruleSet.technologies.containsKey(tech))
civinfo.tech.techsResearched.remove(tech) civinfo.tech.techsResearched.remove(tech)
for (policy in civinfo.policies.adoptedPolicies.toList())
if (!ruleSet.policies.containsKey(policy))
civinfo.policies.adoptedPolicies.remove(policy)
} }
} }

View File

@ -65,8 +65,7 @@ object NextTurnAutomation {
if (TradeEvaluation().isTradeAcceptable(tradeLogic.currentTrade, civInfo, otherCiv)) { if (TradeEvaluation().isTradeAcceptable(tradeLogic.currentTrade, civInfo, otherCiv)) {
tradeLogic.acceptTrade() tradeLogic.acceptTrade()
otherCiv.addNotification("[${civInfo.civName}] has accepted your trade request", Color.GOLD) otherCiv.addNotification("[${civInfo.civName}] has accepted your trade request", Color.GOLD)
} } else {
else {
otherCiv.addNotification("[${civInfo.civName}] has denied your trade request", Color.GOLD) otherCiv.addNotification("[${civInfo.civName}] has denied your trade request", Color.GOLD)
} }
} }
@ -180,8 +179,7 @@ object NextTurnAutomation {
private fun adoptPolicy(civInfo: CivilizationInfo) { private fun adoptPolicy(civInfo: CivilizationInfo) {
while (civInfo.policies.canAdoptPolicy()) { while (civInfo.policies.canAdoptPolicy()) {
val adoptablePolicies = civInfo.gameInfo.ruleSet.policyBranches.values val adoptablePolicies = civInfo.gameInfo.ruleSet.policies.values
.flatMap { it.policies.union(listOf(it)) }
.filter { civInfo.policies.isAdoptable(it) } .filter { civInfo.policies.isAdoptable(it) }
// This can happen if the player is crazy enough to have the game continue forever and he disabled cultural victory // This can happen if the player is crazy enough to have the game continue forever and he disabled cultural victory
@ -245,8 +243,10 @@ object NextTurnAutomation {
// We should A. add some sort of timer (20? 30 turns?) between luxury trade requests if they're denied - see DeclinedLuxExchange // We should A. add some sort of timer (20? 30 turns?) between luxury trade requests if they're denied - see DeclinedLuxExchange
// B. have a way for the AI to keep track of the "pending offers" - see DiplomacyManager.resourcesFromTrade // B. have a way for the AI to keep track of the "pending offers" - see DiplomacyManager.resourcesFromTrade
for (otherCiv in knownCivs.filter { it.isMajorCiv() && !it.isAtWarWith(civInfo) for (otherCiv in knownCivs.filter {
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedLuxExchange)}) { it.isMajorCiv() && !it.isAtWarWith(civInfo)
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedLuxExchange)
}) {
val relationshipLevel = civInfo.getDiplomacyManager(otherCiv).relationshipLevel() val relationshipLevel = civInfo.getDiplomacyManager(otherCiv).relationshipLevel()
if (relationshipLevel <= RelationshipLevel.Enemy) if (relationshipLevel <= RelationshipLevel.Enemy)
@ -263,11 +263,12 @@ object NextTurnAutomation {
private fun offerDeclarationOfFriendship(civInfo: CivilizationInfo) { private fun offerDeclarationOfFriendship(civInfo: CivilizationInfo) {
val civsThatWeCanDeclareFriendshipWith = civInfo.getKnownCivs() val civsThatWeCanDeclareFriendshipWith = civInfo.getKnownCivs()
.asSequence() .asSequence()
.filter { it.isMajorCiv() } .filter {
.filter { !it.isAtWarWith(civInfo) } it.isMajorCiv() && !it.isAtWarWith(civInfo)
.filter { it.getDiplomacyManager(civInfo).relationshipLevel() > RelationshipLevel.Neutral } && it.getDiplomacyManager(civInfo).relationshipLevel() > RelationshipLevel.Neutral
.filter { !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclarationOfFriendship) } && !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclarationOfFriendship)
.filter { !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.Denunceation) } && !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.Denunceation)
}
.sortedByDescending { it.getDiplomacyManager(civInfo).relationshipLevel() } .sortedByDescending { it.getDiplomacyManager(civInfo).relationshipLevel() }
for (civ in civsThatWeCanDeclareFriendshipWith) { for (civ in civsThatWeCanDeclareFriendshipWith) {
// Default setting is 5, this will be changed according to different civ. // Default setting is 5, this will be changed according to different civ.
@ -281,8 +282,10 @@ object NextTurnAutomation {
val canSignResearchAgreementCiv = civInfo.getKnownCivs() val canSignResearchAgreementCiv = civInfo.getKnownCivs()
.asSequence() .asSequence()
.filter { civInfo.canSignResearchAgreementsWith(it) .filter {
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedResearchAgreement) } civInfo.canSignResearchAgreementsWith(it)
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedResearchAgreement)
}
.sortedByDescending { it.statsForNextTurn.science } .sortedByDescending { it.statsForNextTurn.science }
for (otherCiv in canSignResearchAgreementCiv) { for (otherCiv in canSignResearchAgreementCiv) {
@ -368,8 +371,10 @@ object NextTurnAutomation {
//evaluate war //evaluate war
val enemyCivs = civInfo.getKnownCivs() val enemyCivs = civInfo.getKnownCivs()
.filterNot { it == civInfo || it.cities.isEmpty() || !civInfo.getDiplomacyManager(it).canDeclareWar() .filterNot {
|| (it.cities.none { civInfo.exploredTiles.contains(it.location) }) } it == civInfo || it.cities.isEmpty() || !civInfo.getDiplomacyManager(it).canDeclareWar()
|| (it.cities.none { civInfo.exploredTiles.contains(it.location) })
}
// If the AI declares war on a civ without knowing the location of any cities, it'll just keep amassing an army and not sending it anywhere, // If the AI declares war on a civ without knowing the location of any cities, it'll just keep amassing an army and not sending it anywhere,
// and end up at a massive disadvantage // and end up at a massive disadvantage
@ -521,7 +526,8 @@ object NextTurnAutomation {
private fun onCitySettledNearBorders(civInfo: CivilizationInfo, otherCiv: CivilizationInfo) { private fun onCitySettledNearBorders(civInfo: CivilizationInfo, otherCiv: CivilizationInfo) {
val diplomacyManager = civInfo.getDiplomacyManager(otherCiv) val diplomacyManager = civInfo.getDiplomacyManager(otherCiv)
when { when {
diplomacyManager.hasFlag(DiplomacyFlags.IgnoreThemSettlingNearUs) -> {} diplomacyManager.hasFlag(DiplomacyFlags.IgnoreThemSettlingNearUs) -> {
}
diplomacyManager.hasFlag(DiplomacyFlags.AgreedToNotSettleNearUs) -> { diplomacyManager.hasFlag(DiplomacyFlags.AgreedToNotSettleNearUs) -> {
otherCiv.popupAlerts.add(PopupAlert(AlertType.CitySettledNearOtherCivDespiteOurPromise, civInfo.civName)) otherCiv.popupAlerts.add(PopupAlert(AlertType.CitySettledNearOtherCivDespiteOurPromise, civInfo.civName))
diplomacyManager.setFlag(DiplomacyFlags.IgnoreThemSettlingNearUs, 100) diplomacyManager.setFlag(DiplomacyFlags.IgnoreThemSettlingNearUs, 100)

View File

@ -1,10 +1,8 @@
package com.unciv.logic.civilization package com.unciv.logic.civilization
import com.unciv.Constants
import com.unciv.models.ruleset.Policy import com.unciv.models.ruleset.Policy
import com.unciv.models.ruleset.UniqueMap import com.unciv.models.ruleset.UniqueMap
import com.unciv.models.ruleset.UniqueTriggerActivation import com.unciv.models.ruleset.UniqueTriggerActivation
import com.unciv.models.ruleset.VictoryType
import kotlin.math.min import kotlin.math.min
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -12,10 +10,13 @@ import kotlin.math.roundToInt
class PolicyManager { class PolicyManager {
@Transient lateinit var civInfo: CivilizationInfo @Transient
lateinit var civInfo: CivilizationInfo
// Needs to be separate from the actual adopted policies, so that // Needs to be separate from the actual adopted policies, so that
// in different game versions, policies can have different effects // in different game versions, policies can have different effects
@Transient internal val policyUniques = UniqueMap() @Transient
internal val policyUniques = UniqueMap()
var freePolicies = 0 var freePolicies = 0
var storedCulture = 0 var storedCulture = 0
@ -38,7 +39,7 @@ class PolicyManager {
return toReturn return toReturn
} }
fun getPolicyByName(name:String): Policy = getAllPolicies().first { it.name==name } fun getPolicyByName(name: String): Policy = civInfo.gameInfo.ruleSet.policies[name]!!
fun setTransients() { fun setTransients() {
for (policyName in adoptedPolicies) for (policyName in adoptedPolicies)
@ -50,9 +51,6 @@ class PolicyManager {
policyUniques.addUnique(unique) policyUniques.addUnique(unique)
} }
private fun getAllPolicies() = civInfo.gameInfo.ruleSet.policyBranches.values.asSequence()
.flatMap { it.policies.asSequence()+sequenceOf(it) }
fun startTurn() { fun startTurn() {
tryAddLegalismBuildings() tryAddLegalismBuildings()
} }
@ -110,7 +108,7 @@ class PolicyManager {
if (freePolicies == 0 && storedCulture < getCultureNeededForNextPolicy()) if (freePolicies == 0 && storedCulture < getCultureNeededForNextPolicy())
return false return false
val hasAdoptablePolicies = getAllPolicies() val hasAdoptablePolicies = civInfo.gameInfo.ruleSet.policies.values
.any { civInfo.policies.isAdoptable(it) } .any { civInfo.policies.isAdoptable(it) }
return hasAdoptablePolicies return hasAdoptablePolicies
} }
@ -138,8 +136,6 @@ class PolicyManager {
} }
} }
val hasCapital = civInfo.cities.any { it.isCapital() }
for (unique in policy.uniqueObjects) for (unique in policy.uniqueObjects)
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo) UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
@ -160,8 +156,10 @@ class PolicyManager {
val candidateCities = civInfo.cities val candidateCities = civInfo.cities
.sortedBy { it.turnAcquired } .sortedBy { it.turnAcquired }
.subList(0, min(4, civInfo.cities.size)) .subList(0, min(4, civInfo.cities.size))
.filter { it.id !in legalismState .filter {
&& it.cityConstructions.hasBuildableCultureBuilding() } it.id !in legalismState
&& it.cityConstructions.hasBuildableCultureBuilding()
}
for (city in candidateCities) { for (city in candidateCities) {
val builtBuilding = city.cityConstructions.addCultureBuilding() val builtBuilding = city.cityConstructions.addCultureBuilding()
legalismState[city.id] = builtBuilding!! legalismState[city.id] = builtBuilding!!

View File

@ -51,6 +51,7 @@ class Ruleset {
val quests = LinkedHashMap<String, Quest>() val quests = LinkedHashMap<String, Quest>()
val specialists = LinkedHashMap<String, Specialist>() val specialists = LinkedHashMap<String, Specialist>()
val policyBranches = LinkedHashMap<String, PolicyBranch>() val policyBranches = LinkedHashMap<String, PolicyBranch>()
val policies = LinkedHashMap<String, Policy>()
val difficulties = LinkedHashMap<String, Difficulty>() val difficulties = LinkedHashMap<String, Difficulty>()
val mods = LinkedHashSet<String>() val mods = LinkedHashSet<String>()
var modOptions = ModOptions() var modOptions = ModOptions()
@ -74,6 +75,7 @@ class Ruleset {
difficulties.putAll(ruleset.difficulties) difficulties.putAll(ruleset.difficulties)
nations.putAll(ruleset.nations) nations.putAll(ruleset.nations)
policyBranches.putAll(ruleset.policyBranches) policyBranches.putAll(ruleset.policyBranches)
policies.putAll(ruleset.policies)
quests.putAll(ruleset.quests) quests.putAll(ruleset.quests)
specialists.putAll(ruleset.specialists) specialists.putAll(ruleset.specialists)
technologies.putAll(ruleset.technologies) technologies.putAll(ruleset.technologies)
@ -93,6 +95,7 @@ class Ruleset {
difficulties.clear() difficulties.clear()
nations.clear() nations.clear()
policyBranches.clear() policyBranches.clear()
policies.clear()
quests.clear() quests.clear()
technologies.clear() technologies.clear()
buildings.clear() buildings.clear()
@ -159,9 +162,11 @@ class Ruleset {
for (branch in policyBranches.values) { for (branch in policyBranches.values) {
branch.requires = ArrayList() branch.requires = ArrayList()
branch.branch = branch branch.branch = branch
policies[branch.name] = branch
for (policy in branch.policies) { for (policy in branch.policies) {
policy.branch = branch policy.branch = branch
if (policy.requires == null) policy.requires = arrayListOf(branch.name) if (policy.requires == null) policy.requires = arrayListOf(branch.name)
policies[policy.name] = policy
} }
branch.policies.last().name = branch.name + " Complete" branch.policies.last().name = branch.name + " Complete"
} }

View File

@ -25,15 +25,14 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
rightSideButton.setText("{Adopt policy}\r\n(".tr() + policies.storedCulture + "/" + policies.getCultureNeededForNextPolicy() + ")") rightSideButton.setText("{Adopt policy}\r\n(".tr() + policies.storedCulture + "/" + policies.getCultureNeededForNextPolicy() + ")")
if (viewingCiv.gameInfo.ruleSet.policyBranches.values.flatMap { it.policies }.all { it.name in policies.adoptedPolicies}) if (viewingCiv.gameInfo.ruleSet.policies.values.all { it.name in policies.adoptedPolicies })
rightSideButton.setText("All policies adopted".tr()) rightSideButton.setText("All policies adopted".tr())
setDefaultCloseAction() setDefaultCloseAction()
if (policies.freePolicies > 0) { if (policies.freePolicies > 0) {
rightSideButton.setText("Adopt free policy".tr()) rightSideButton.setText("Adopt free policy".tr())
if (policies.canAdoptPolicy()) closeButton.disable() if (policies.canAdoptPolicy()) closeButton.disable()
} } else onBackButtonClicked { UncivGame.Current.setWorldScreen() }
else onBackButtonClicked { UncivGame.Current.setWorldScreen() }
rightSideButton.onClick(UncivSound.Policy) { rightSideButton.onClick(UncivSound.Policy) {
viewingCiv.policies.adopt(pickedPolicy!!) viewingCiv.policies.adopt(pickedPolicy!!)
@ -42,8 +41,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
if (game.screen !is PolicyPickerScreen || !policies.canAdoptPolicy()) { if (game.screen !is PolicyPickerScreen || !policies.canAdoptPolicy()) {
game.setWorldScreen() game.setWorldScreen()
dispose() dispose()
} } else game.setScreen(PolicyPickerScreen(worldScreen)) // update policies
else game.setScreen(PolicyPickerScreen(worldScreen)) // update policies
} }
if (!UncivGame.Current.worldScreen.canChangeState) if (!UncivGame.Current.worldScreen.canChangeState)