diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index 1d74412eba..b5ee58c81d 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -291,10 +291,10 @@ object Automation { } // Assume buildings remain useful - val neededForBuilding = civInfo.lastEraResourceUsedForBuilding[resource] != null + val neededForBuilding = civInfo.cache.lastEraResourceUsedForBuilding[resource] != null // Don't care about old units - val neededForUnits = civInfo.lastEraResourceUsedForUnit[resource] != null - && civInfo.lastEraResourceUsedForUnit[resource]!! >= civInfo.getEraNumber() + val neededForUnits = civInfo.cache.lastEraResourceUsedForUnit[resource] != null + && civInfo.cache.lastEraResourceUsedForUnit[resource]!! >= civInfo.getEraNumber() // No need to save for both if (!neededForBuilding || !neededForUnits) { diff --git a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt index dac02ad49d..2846d9947e 100644 --- a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt +++ b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt @@ -697,7 +697,7 @@ object NextTurnAutomation { // Default setting is 5, this will be changed according to different civ. if ((1..10).random() > 5) continue val tradeLogic = TradeLogic(civInfo, otherCiv) - val cost = civInfo.getResearchAgreementCost() + val cost = civInfo.diplomacyFunctions.getResearchAgreementCost() tradeLogic.currentTrade.ourOffers.add(TradeOffer(Constants.researchAgreement, TradeType.Treaty, cost)) tradeLogic.currentTrade.theirOffers.add(TradeOffer(Constants.researchAgreement, TradeType.Treaty, cost)) @@ -764,7 +764,7 @@ object NextTurnAutomation { fun isTileCanMoveThrough(tileInfo: TileInfo): Boolean { val owner = tileInfo.getOwner() return !tileInfo.isImpassible() - && (owner == otherCiv || owner == null || civInfo.canPassThroughTiles(owner)) + && (owner == otherCiv || owner == null || civInfo.diplomacyFunctions.canPassThroughTiles(owner)) } val reachableEnemyCitiesBfs = BFS(civInfo.getCapital()!!.getCenterTile()) { isTileCanMoveThrough(it) } diff --git a/core/src/com/unciv/logic/city/managers/CityExpansionManager.kt b/core/src/com/unciv/logic/city/managers/CityExpansionManager.kt index a35993a752..e1aa34ffd3 100644 --- a/core/src/com/unciv/logic/city/managers/CityExpansionManager.kt +++ b/core/src/com/unciv/logic/city/managers/CityExpansionManager.kt @@ -164,7 +164,7 @@ class CityExpansionManager : IsPartOfGameInfoSerialization { cityInfo.cityStats.update() for (unit in tileInfo.getUnits().toList()) // toListed because we're modifying - if (!unit.civInfo.canPassThroughTiles(cityInfo.civInfo)) + if (!unit.civInfo.diplomacyFunctions.canPassThroughTiles(cityInfo.civInfo)) unit.movement.teleportToClosestMoveableTile() cityInfo.civInfo.cache.updateViewableTiles() diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index abd0d4c9ab..f28162c036 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -12,8 +12,8 @@ import com.unciv.logic.automation.unit.WorkerAutomation import com.unciv.logic.city.CityInfo import com.unciv.logic.civilization.diplomacy.CityStateFunctions import com.unciv.logic.civilization.diplomacy.CityStatePersonality -import com.unciv.logic.civilization.diplomacy.DiplomacyManager import com.unciv.logic.civilization.diplomacy.DiplomacyFunctions +import com.unciv.logic.civilization.diplomacy.DiplomacyManager import com.unciv.logic.civilization.diplomacy.DiplomaticStatus import com.unciv.logic.civilization.managers.EspionageManager import com.unciv.logic.civilization.managers.GoldenAgeManager @@ -29,7 +29,6 @@ import com.unciv.logic.civilization.transients.CivInfoStatsForNextTurn import com.unciv.logic.civilization.transients.CivInfoTransientCache import com.unciv.logic.map.MapUnit import com.unciv.logic.map.TileInfo -import com.unciv.logic.map.UnitMovementAlgorithms import com.unciv.logic.trade.TradeRequest import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Policy @@ -128,11 +127,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { @Transient var nonStandardTerrainDamage = false - @Transient - var lastEraResourceUsedForBuilding = HashMap() - - @Transient - val lastEraResourceUsedForUnit = HashMap() @Transient var thingsToFocusOnForVictory = setOf() @@ -678,20 +672,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { nonStandardTerrainDamage = getMatchingUniques(UniqueType.DamagesContainingUnits) .any { gameInfo.ruleSet.terrains[it.params[0]]!!.damagePerTurn != it.params[1].toInt() } - // Cache the last era each resource is used for buildings or units respectively for AI building evaluation - for (resource in gameInfo.ruleSet.tileResources.values.asSequence().filter { it.resourceType == ResourceType.Strategic }.map { it.name }) { - val applicableBuildings = gameInfo.ruleSet.buildings.values.filter { it.requiresResource(resource) && getEquivalentBuilding(it) == it } - val applicableUnits = gameInfo.ruleSet.units.values.filter { it.requiresResource(resource) && getEquivalentUnit(it) == it } - - val lastEraForBuilding = applicableBuildings.maxOfOrNull { gameInfo.ruleSet.eras[gameInfo.ruleSet.technologies[it.requiredTech]?.era()]?.eraNumber ?: 0 } - val lastEraForUnit = applicableUnits.maxOfOrNull { gameInfo.ruleSet.eras[gameInfo.ruleSet.technologies[it.requiredTech]?.era()]?.eraNumber ?: 0 } - - if (lastEraForBuilding != null) - lastEraResourceUsedForBuilding[resource] = lastEraForBuilding - if (lastEraForUnit != null) - lastEraResourceUsedForUnit[resource] = lastEraForUnit - } - hasLongCountDisplayUnique = hasUnique(UniqueType.MayanCalendarDisplay) tacticalAI.init(this) @@ -704,7 +684,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { fun hasFlag(flag: String) = flagsCountdown.contains(flag) fun getTurnsBetweenDiplomaticVotes() = (15 * gameInfo.speed.modifier).toInt() // Dunno the exact calculation, hidden in Lua files - fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name] fun getRecentBullyingCountdown() = flagsCountdown[CivFlags.RecentlyBullied.name] @@ -764,27 +743,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { } } - /** - * @returns whether units of this civilization can pass through the tiles owned by [otherCiv], - * considering only civ-wide filters. - * Use [TileInfo.canCivPassThrough] to check whether units of a civilization can pass through - * a specific tile, considering only civ-wide filters. - * Use [UnitMovementAlgorithms.canPassThrough] to check whether a specific unit can pass through - * a specific tile. - */ - fun canPassThroughTiles(otherCiv: CivilizationInfo): Boolean { - if (otherCiv == this) return true - if (otherCiv.isBarbarian()) return true - if (nation.isBarbarian() && gameInfo.turns >= gameInfo.difficultyObject.turnBarbariansCanEnterPlayerTiles) - return true - val diplomacyManager = diplomacy[otherCiv.civName] - if (diplomacyManager != null && (diplomacyManager.hasOpenBorders || diplomacyManager.diplomaticStatus == DiplomaticStatus.War)) - return true - // Players can always pass through city-state tiles - if (isHuman() && otherCiv.isCityState()) return true - return false - } - fun addNotification(text: String, location: Vector2, category:NotificationCategory, vararg notificationIcons: String) { addNotification(text, LocationAction(location), category, *notificationIcons) @@ -819,13 +777,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { } } - fun getResearchAgreementCost(): Int { - // https://forums.civfanatics.com/resources/research-agreements-bnw.25568/ - return ( - getEra().researchAgreementCost * gameInfo.speed.goldCostModifier - ).toInt() - } - fun updateProximity(otherCiv: CivilizationInfo, preCalculated: Proximity? = null): Proximity = cache.updateProximity(otherCiv, preCalculated) /** diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt index 7bd1d5f84b..ef9b52ea33 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt @@ -6,6 +6,8 @@ import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.NotificationCategory import com.unciv.logic.civilization.NotificationIcon import com.unciv.logic.civilization.PopupAlert +import com.unciv.logic.map.TileInfo +import com.unciv.logic.map.UnitMovementAlgorithms import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.stats.Stat import com.unciv.models.stats.Stats @@ -107,7 +109,7 @@ class DiplomacyFunctions(val civInfo:CivilizationInfo){ fun canSignResearchAgreementsWith(otherCiv: CivilizationInfo): Boolean { val diplomacyManager = civInfo.getDiplomacyManager(otherCiv) - val cost = civInfo.getResearchAgreementCost() + val cost = getResearchAgreementCost() return canSignResearchAgreement() && otherCiv.diplomacyFunctions.canSignResearchAgreement() && diplomacyManager.hasFlag(DiplomacyFlags.DeclarationOfFriendship) && !diplomacyManager.hasFlag(DiplomacyFlags.ResearchAgreement) @@ -115,5 +117,35 @@ class DiplomacyFunctions(val civInfo:CivilizationInfo){ && civInfo.gold >= cost && otherCiv.gold >= cost } + fun getResearchAgreementCost(): Int { + // https://forums.civfanatics.com/resources/research-agreements-bnw.25568/ + return ( + civInfo.getEra().researchAgreementCost * civInfo.gameInfo.speed.goldCostModifier + ).toInt() + } + + + /** + * @returns whether units of this civilization can pass through the tiles owned by [otherCiv], + * considering only civ-wide filters. + * Use [TileInfo.canCivPassThrough] to check whether units of a civilization can pass through + * a specific tile, considering only civ-wide filters. + * Use [UnitMovementAlgorithms.canPassThrough] to check whether a specific unit can pass through + * a specific tile. + */ + fun canPassThroughTiles(otherCiv: CivilizationInfo): Boolean { + if (otherCiv == civInfo) return true + if (otherCiv.isBarbarian()) return true + if (civInfo.isBarbarian() && civInfo.gameInfo.turns >= civInfo.gameInfo.difficultyObject.turnBarbariansCanEnterPlayerTiles) + return true + val diplomacyManager = civInfo.diplomacy[otherCiv.civName] + if (diplomacyManager != null && (diplomacyManager.hasOpenBorders || diplomacyManager.diplomaticStatus == DiplomaticStatus.War)) + return true + // Players can always pass through city-state tiles + if (civInfo.isHuman() && otherCiv.isCityState()) return true + return false + } + + } diff --git a/core/src/com/unciv/logic/civilization/transients/CivInfoTransientCache.kt b/core/src/com/unciv/logic/civilization/transients/CivInfoTransientCache.kt index 0e628ea253..cc3436f478 100644 --- a/core/src/com/unciv/logic/civilization/transients/CivInfoTransientCache.kt +++ b/core/src/com/unciv/logic/civilization/transients/CivInfoTransientCache.kt @@ -10,12 +10,35 @@ import com.unciv.logic.civilization.Proximity import com.unciv.logic.map.MapShape import com.unciv.logic.map.TileInfo import com.unciv.models.ruleset.tile.ResourceSupplyList +import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.unique.UniqueTarget import com.unciv.models.ruleset.unique.UniqueType /** CivInfo class was getting too crowded */ class CivInfoTransientCache(val civInfo: CivilizationInfo) { + @Transient + var lastEraResourceUsedForBuilding = java.util.HashMap() + + @Transient + val lastEraResourceUsedForUnit = java.util.HashMap() + + fun setTransients(){ + val ruleset = civInfo.gameInfo.ruleSet + for (resource in ruleset.tileResources.values.asSequence().filter { it.resourceType == ResourceType.Strategic }.map { it.name }) { + val applicableBuildings = ruleset.buildings.values.filter { it.requiresResource(resource) && civInfo.getEquivalentBuilding(it) == it } + val applicableUnits = ruleset.units.values.filter { it.requiresResource(resource) && civInfo.getEquivalentUnit(it) == it } + + val lastEraForBuilding = applicableBuildings.maxOfOrNull { ruleset.eras[ruleset.technologies[it.requiredTech]?.era()]?.eraNumber ?: 0 } + val lastEraForUnit = applicableUnits.maxOfOrNull { ruleset.eras[ruleset.technologies[it.requiredTech]?.era()]?.eraNumber ?: 0 } + + if (lastEraForBuilding != null) + lastEraResourceUsedForBuilding[resource] = lastEraForBuilding + if (lastEraForUnit != null) + lastEraResourceUsedForUnit[resource] = lastEraForUnit + } + } + fun updateSightAndResources() { updateViewableTiles() updateHasActiveEnemyMovementPenalty() diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 2694c3985b..2f7197d195 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -846,7 +846,7 @@ class MapUnit : IsPartOfGameInfoSerialization { if (hasUnique(UniqueType.ReligiousUnit) && getTile().getOwner() != null && !getTile().getOwner()!!.isCityState() - && !civInfo.canPassThroughTiles(getTile().getOwner()!!) + && !civInfo.diplomacyFunctions.canPassThroughTiles(getTile().getOwner()!!) ) { val lostReligiousStrength = getMatchingUniques(UniqueType.CanEnterForeignTilesButLosesReligiousStrength) @@ -874,8 +874,7 @@ class MapUnit : IsPartOfGameInfoSerialization { due = true // Hakkapeliitta movement boost - if (getTile().getUnits().count() > 1) - { + if (getTile().getUnits().count() > 1) { // For every double-stacked tile, check if our cohabitant can boost our speed for (unit in getTile().getUnits()) { @@ -893,11 +892,13 @@ class MapUnit : IsPartOfGameInfoSerialization { this.currentTile.getTilesInDistance(3).any { it.militaryUnit != null && it in civInfo.viewableTiles && it.militaryUnit!!.civInfo.isAtWarWith(civInfo) } - ) - action = null + ) action = null val tileOwner = getTile().getOwner() - if (tileOwner != null && !canEnterForeignTerrain && !civInfo.canPassThroughTiles(tileOwner) && !tileOwner.isCityState()) // if an enemy city expanded onto this tile while I was in it + if (tileOwner != null + && !canEnterForeignTerrain + && !civInfo.diplomacyFunctions.canPassThroughTiles(tileOwner) + && !tileOwner.isCityState()) // if an enemy city expanded onto this tile while I was in it movement.teleportToClosestMoveableTile() addMovementMemory() diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 93cf3e21a4..ca87bdd9e6 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -1066,7 +1066,7 @@ open class TileInfo : IsPartOfGameInfoSerialization { if (tileOwner == null || tileOwner == civInfo) return true if (isCityCenter() && civInfo.isAtWarWith(tileOwner) && !getCity()!!.hasJustBeenConquered) return false - if (!civInfo.canPassThroughTiles(tileOwner)) return false + if (!civInfo.diplomacyFunctions.canPassThroughTiles(tileOwner)) return false return true } diff --git a/core/src/com/unciv/logic/trade/TradeLogic.kt b/core/src/com/unciv/logic/trade/TradeLogic.kt index 84f5da3c92..db222be440 100644 --- a/core/src/com/unciv/logic/trade/TradeLogic.kt +++ b/core/src/com/unciv/logic/trade/TradeLogic.kt @@ -98,7 +98,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci city.getCenterTile().getUnits().toList().forEach { it.movement.teleportToClosestMoveableTile() } for (tile in city.getTiles()) { for (unit in tile.getUnits().toList()) { - if (!unit.civInfo.canPassThroughTiles(to) && !unit.canEnterForeignTerrain) + if (!unit.civInfo.diplomacyFunctions.canPassThroughTiles(to) && !unit.canEnterForeignTerrain) unit.movement.teleportToClosestMoveableTile() } } diff --git a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt index 68b44547b5..8e4b6455ba 100644 --- a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt +++ b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt @@ -24,9 +24,7 @@ import com.unciv.logic.trade.TradeType import com.unciv.models.ruleset.ModOptionsConstants import com.unciv.models.ruleset.Quest import com.unciv.models.ruleset.tile.ResourceType -import com.unciv.models.ruleset.unique.Unique import com.unciv.models.ruleset.unique.UniqueType -import com.unciv.models.translations.fillPlaceholders import com.unciv.models.translations.tr import com.unciv.ui.audio.MusicMood import com.unciv.ui.audio.MusicTrackChooserFlags @@ -289,10 +287,6 @@ class DiplomacyScreen( return diplomacyTable } - fun fillUniquePlaceholders(unique:Unique, vararg strings: String):String { - return unique.placeholderText.fillPlaceholders(*strings) + unique.conditionals.map { " <${it.text}>" } - .joinToString("") - } private fun getCityStateDiplomacyTable(otherCiv: CivilizationInfo): Table { val otherCivDiplomacyManager = otherCiv.getDiplomacyManager(viewingCiv) @@ -453,7 +447,7 @@ class DiplomacyScreen( } - if (isNotPlayersTurn() || otherCivDiplomacyManager.getInfluence() < 60 || !needsImprovements) + if (isNotPlayersTurn() || otherCivDiplomacyManager.getInfluence() < 60) improveTileButton.disable() return improveTileButton } @@ -749,7 +743,7 @@ class DiplomacyScreen( private fun getResearchAgreementButton(otherCiv: CivilizationInfo): TextButton { val researchAgreementButton = "Research Agreement".toTextButton() - val requiredGold = viewingCiv.getResearchAgreementCost() + val requiredGold = viewingCiv.diplomacyFunctions.getResearchAgreementCost() researchAgreementButton.onClick { val tradeTable = setTrade(otherCiv) val researchAgreement =