From e56d53dcd93e54adfd2f513adf0e6d942ff25a0c Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Wed, 18 Jan 2023 18:11:18 +0200 Subject: [PATCH] chore: Moved diplomacy functions to DiplomacyFunctions --- core/src/com/unciv/MainMenuScreen.kt | 2 +- .../civilization/NextTurnAutomation.kt | 8 +- .../managers/CityInfoConquestFunctions.kt | 6 +- .../logic/civilization/CivilizationInfo.kt | 142 +++--------------- .../diplomacy/CityStateFunctions.kt | 2 +- .../diplomacy/DiplomacyFunctions.kt | 119 +++++++++++++++ .../diplomacy/DiplomacyManager.kt | 2 +- .../transients/CivInfoTransientCache.kt | 2 +- .../unciv/logic/map/UnitMovementAlgorithms.kt | 16 +- core/src/com/unciv/logic/trade/TradeLogic.kt | 2 +- core/src/com/unciv/models/ruleset/Ruleset.kt | 2 - .../com/unciv/models/ruleset/unique/Unique.kt | 2 +- .../unciv/ui/multiplayer/MultiplayerScreen.kt | 3 +- core/src/com/unciv/ui/options/GameplayTab.kt | 9 +- .../GlobalPoliticsOverviewTable.kt | 6 +- .../com/unciv/ui/pickerscreens/PickerPane.kt | 2 +- .../ui/pickerscreens/PolicyPickerScreen.kt | 6 +- .../src/com/unciv/ui/trade/DiplomacyScreen.kt | 6 +- .../unciv/ui/worldscreen/unit/UnitActions.kt | 4 +- .../diplomacy/DiplomacyManagerTests.kt | 2 +- .../com/unciv/testing/SerializationTests.kt | 2 +- .../com/unciv/uniques/GlobalUniquesTests.kt | 2 +- 22 files changed, 184 insertions(+), 163 deletions(-) create mode 100644 core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt diff --git a/core/src/com/unciv/MainMenuScreen.kt b/core/src/com/unciv/MainMenuScreen.kt index 7b776f7d39..8101b7069e 100644 --- a/core/src/com/unciv/MainMenuScreen.kt +++ b/core/src/com/unciv/MainMenuScreen.kt @@ -167,7 +167,7 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize { } val multiplayerTable = getMenuButton("Multiplayer", "OtherIcons/Multiplayer", 'm') - { game.pushScreen(MultiplayerScreen(this)) } + { game.pushScreen(MultiplayerScreen()) } column2.add(multiplayerTable).row() val mapEditorScreenTable = getMenuButton("Map editor", "OtherIcons/MapEditor", 'e') diff --git a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt index 237479b052..dac02ad49d 100644 --- a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt +++ b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt @@ -683,12 +683,12 @@ object NextTurnAutomation { } private fun offerResearchAgreement(civInfo: CivilizationInfo) { - if (!civInfo.canSignResearchAgreement()) return // don't waste your time + if (!civInfo.diplomacyFunctions.canSignResearchAgreement()) return // don't waste your time val canSignResearchAgreementCiv = civInfo.getKnownCivs() .asSequence() .filter { - civInfo.canSignResearchAgreementsWith(it) + civInfo.diplomacyFunctions.canSignResearchAgreementsWith(it) && !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedResearchAgreement) } .sortedByDescending { it.stats.statsForNextTurn.science } @@ -752,12 +752,12 @@ object NextTurnAutomation { val theirCity = closestCities.city2 if (civInfo.units.getCivUnits().filter { it.isMilitary() }.none { - val damageRecievedWhenAttacking = + val damageReceivedWhenAttacking = BattleDamage.calculateDamageToAttacker( MapUnitCombatant(it), CityCombatant(theirCity) ) - damageRecievedWhenAttacking < 100 + damageReceivedWhenAttacking < 100 }) return 0 // You don't have any units that can attack this city without dying, don't declare war. diff --git a/core/src/com/unciv/logic/city/managers/CityInfoConquestFunctions.kt b/core/src/com/unciv/logic/city/managers/CityInfoConquestFunctions.kt index 8d2d202e1b..37f753a3f9 100644 --- a/core/src/com/unciv/logic/city/managers/CityInfoConquestFunctions.kt +++ b/core/src/com/unciv/logic/city/managers/CityInfoConquestFunctions.kt @@ -174,7 +174,7 @@ class CityInfoConquestFunctions(val city: CityInfo){ // How can you conquer a city but not know the civ you conquered it from?! // I don't know either, but some of our players have managed this, and crashed their game! if (!conqueringCiv.knows(oldCiv)) - conqueringCiv.makeCivilizationsMeet(oldCiv) + conqueringCiv.diplomacyFunctions.makeCivilizationsMeet(oldCiv) oldCiv.getDiplomacyManager(conqueringCiv) .addModifier(DiplomaticModifiers.CapturedOurCities, -aggroGenerated) @@ -246,7 +246,7 @@ class CityInfoConquestFunctions(val city: CityInfo){ // In order to get "plus points" in Diplomacy, you have to establish diplomatic relations if you haven't yet if (!conqueringCiv.knows(foundingCiv)) - conqueringCiv.makeCivilizationsMeet(foundingCiv) + conqueringCiv.diplomacyFunctions.makeCivilizationsMeet(foundingCiv) if (foundingCiv.isMajorCiv()) { foundingCiv.getDiplomacyManager(conqueringCiv) @@ -299,7 +299,7 @@ class CityInfoConquestFunctions(val city: CityInfo){ removeBuildingsOnMoveToCiv(oldCiv) // Place palace for newCiv if this is the only city they have - // This needs to happen _before_ free buildings are added, as somtimes these should + // This needs to happen _before_ free buildings are added, as sometimes these should // only be placed in the capital, and then there needs to be a capital. if (newCivInfo.cities.size == 1) { newCivInfo.moveCapitalTo(this) diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 8d36ee7766..abd0d4c9ab 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.DiplomacyFlags import com.unciv.logic.civilization.diplomacy.DiplomacyManager +import com.unciv.logic.civilization.diplomacy.DiplomacyFunctions import com.unciv.logic.civilization.diplomacy.DiplomaticStatus import com.unciv.logic.civilization.managers.EspionageManager import com.unciv.logic.civilization.managers.GoldenAgeManager @@ -48,10 +48,8 @@ import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.getMatchingUniques import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.stats.Stat -import com.unciv.models.stats.Stats import com.unciv.models.translations.tr import com.unciv.ui.utils.extensions.toPercent -import com.unciv.ui.utils.extensions.withItem import com.unciv.ui.victoryscreen.RankingType import java.util.* import kotlin.math.max @@ -91,6 +89,9 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { @Transient val units = UnitManager(this) + @Transient + var diplomacyFunctions = DiplomacyFunctions(this) + @Transient var viewableTiles = setOf() @@ -328,39 +329,25 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { fun knows(otherCivName: String) = diplomacy.containsKey(otherCivName) fun knows(otherCiv: CivilizationInfo) = knows(otherCiv.civName) - /** A sorted Sequence of all other civs we know (excluding barbarians and spectators) */ - fun getKnownCivsSorted(includeCityStates: Boolean = true, includeDefeated: Boolean = false) = - gameInfo.civilizations.asSequence() - .filterNot { - it == this || - it.isBarbarian() || it.isSpectator() || - !this.knows(it) || - (!includeDefeated && it.isDefeated()) || - (!includeCityStates && it.isCityState()) - } - .sortedWith( - compareByDescending { it.isMajorCiv() } - .thenBy (UncivGame.Current.settings.getCollatorFromLocale()) { it.civName.tr() } - ) fun getCapital() = cities.firstOrNull { it.isCapital() } fun isHuman() = playerType == PlayerType.Human fun isAI() = playerType == PlayerType.AI - fun isOneCityChallenger() = ( - playerType == PlayerType.Human && - gameInfo.gameParameters.oneCityChallenge) + fun isOneCityChallenger() = playerType == PlayerType.Human && gameInfo.gameParameters.oneCityChallenge fun isCurrentPlayer() = gameInfo.currentPlayerCiv == this + fun isMajorCiv() = nation.isMajorCiv() + fun isMinorCiv() = nation.isCityState() || nation.isBarbarian() + fun isCityState(): Boolean = nation.isCityState() fun isBarbarian() = nation.isBarbarian() fun isSpectator() = nation.isSpectator() - fun isCityState(): Boolean = nation.isCityState() + fun isAlive(): Boolean = !isDefeated() + @delegate:Transient val cityStateType: CityStateType by lazy { gameInfo.ruleSet.cityStateTypes[nation.cityStateType!!]!! } var cityStatePersonality: CityStatePersonality = CityStatePersonality.Neutral var cityStateResource: String? = null var cityStateUniqueUnit: String? = null // Unique unit for militaristic city state. Might still be null if there are no appropriate units - fun isMajorCiv() = nation.isMajorCiv() - fun isMinorCiv() = nation.isCityState() || nation.isBarbarian() - fun isAlive(): Boolean = !isDefeated() + fun hasMetCivTerritory(otherCiv: CivilizationInfo): Boolean = otherCiv.getCivTerritory().any { hasExplored(it) } fun getCompletedPolicyBranchesCount(): Int = policies.adoptedPolicies.count { Policy.isBranchCompleteByName(it) } @@ -400,7 +387,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { fun getHappiness() = stats.happiness - fun getCivResources(): ResourceSupplyList = summarizedCivResources // Preserves some origins for resources so we can separate them for trades @@ -509,6 +495,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { ?: throw UncivShowableException("Unit $baseUnitName doesn't seem to exist!") return getEquivalentUnit(baseUnit) } + fun getEquivalentUnit(baseUnit: BaseUnit): BaseUnit { if (baseUnit.replaces != null) return getEquivalentUnit(baseUnit.replaces!!) // Equivalent of unique unit is the equivalent of the replaced unit @@ -519,56 +506,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { return baseUnit } - fun makeCivilizationsMeet(otherCiv: CivilizationInfo, warOnContact: Boolean = false) { - meetCiv(otherCiv, warOnContact) - otherCiv.meetCiv(this, warOnContact) - } - - private fun meetCiv(otherCiv: CivilizationInfo, warOnContact: Boolean = false) { - diplomacy[otherCiv.civName] = DiplomacyManager(this, otherCiv.civName) - .apply { diplomaticStatus = DiplomaticStatus.Peace } - - if (!otherCiv.isSpectator()) - otherCiv.popupAlerts.add(PopupAlert(AlertType.FirstContact, civName)) - - if (isCurrentPlayer()) - UncivGame.Current.settings.addCompletedTutorialTask("Meet another civilization") - - if (!(isCityState() && otherCiv.isMajorCiv())) return - if (warOnContact || otherCiv.isMinorCivAggressor()) return // No gift if they are bad people, or we are just about to be at war - - val cityStateLocation = if (cities.isEmpty()) null else getCapital()!!.location - - val giftAmount = Stats(gold = 15f) - val faithAmount = Stats(faith = 4f) - // Later, religious city-states will also gift gold, making this the better implementation - // For now, it might be overkill though. - var meetString = "[${civName}] has given us [${giftAmount}] as a token of goodwill for meeting us" - val religionMeetString = "[${civName}] has also given us [${faithAmount}]" - if (diplomacy.filter { it.value.otherCiv().isMajorCiv() }.size == 1) { - giftAmount.timesInPlace(2f) - meetString = "[${civName}] has given us [${giftAmount}] as we are the first major civ to meet them" - } - if (cityStateLocation != null) - otherCiv.addNotification(meetString, cityStateLocation, NotificationCategory.Diplomacy, NotificationIcon.Gold) - else - otherCiv.addNotification(meetString, NotificationCategory.Diplomacy, NotificationIcon.Gold) - - if (otherCiv.isCityState() && otherCiv.cityStateFunctions.canProvideStat(Stat.Faith)){ - otherCiv.addNotification(religionMeetString, NotificationCategory.Diplomacy, NotificationIcon.Faith) - - for ((key, value) in faithAmount) - otherCiv.addStat(key, value.toInt()) - } - for ((key, value) in giftAmount) - otherCiv.addStat(key, value.toInt()) - - if (cities.isNotEmpty()) - otherCiv.exploredTiles = otherCiv.exploredTiles.withItem(getCapital()!!.location) - - questManager.justMet(otherCiv) // Include them in war with major pseudo-quest - } - override fun toString(): String = civName // for debug /** @@ -587,31 +524,10 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { fun getEraNumber(): Int = getEra().eraNumber - fun isAtWarWith(otherCiv: CivilizationInfo): Boolean { - return when { - otherCiv == this -> false - otherCiv.isBarbarian() || isBarbarian() -> true - else -> { - val diplomacyManager = diplomacy[otherCiv.civName] - ?: return false // not encountered yet - return diplomacyManager.diplomaticStatus == DiplomaticStatus.War - } - } - } + fun isAtWarWith(otherCiv: CivilizationInfo) = diplomacyFunctions.isAtWarWith(otherCiv) fun isAtWar() = diplomacy.values.any { it.diplomaticStatus == DiplomaticStatus.War && !it.otherCiv().isDefeated() } - fun getEnemyMovementPenalty(enemyUnit: MapUnit): Float { - if (enemyMovementPenaltyUniques != null && enemyMovementPenaltyUniques!!.any()) { - return enemyMovementPenaltyUniques!!.sumOf { - if (it.type!! == UniqueType.EnemyLandUnitsSpendExtraMovement - && enemyUnit.matchesFilter(it.params[0])) - it.params[1].toInt() - else 0 - }.toFloat() - } - return 0f // should not reach this point - } /** * Returns a civilization caption suitable for greetings including player type info: @@ -623,34 +539,13 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { val online = gameInfo.gameParameters.isOnlineMultiplayer return nation.getLeaderDisplayName().tr() + when { - !online && !severalHumans -> - "" // offline single player will know everybody else is AI - playerType == PlayerType.AI -> - " (" + "AI".tr() + ")" - online -> - " (" + "Human".tr() + " - " + "Multiplayer".tr() + ")" - else -> - " (" + "Human".tr() + " - " + "Hotseat".tr() + ")" + !online && !severalHumans -> "" // offline single player will know everybody else is AI + playerType == PlayerType.AI -> " (${"AI".tr()})" + online -> " (${"Human".tr()} - ${"Multiplayer".tr()})" + else -> " (${"Human".tr()} - ${"Hotseat".tr()})" } } - fun canSignResearchAgreement(): Boolean { - if (!isMajorCiv()) return false - if (!hasUnique(UniqueType.EnablesResearchAgreements)) return false - if (gameInfo.ruleSet.technologies.values - .none { tech.canBeResearched(it.name) && !tech.isResearched(it.name) }) return false - return true - } - - fun canSignResearchAgreementsWith(otherCiv: CivilizationInfo): Boolean { - val diplomacyManager = getDiplomacyManager(otherCiv) - val cost = getResearchAgreementCost() - return canSignResearchAgreement() && otherCiv.canSignResearchAgreement() - && diplomacyManager.hasFlag(DiplomacyFlags.DeclarationOfFriendship) - && !diplomacyManager.hasFlag(DiplomacyFlags.ResearchAgreement) - && !diplomacyManager.otherCivDiplomacy().hasFlag(DiplomacyFlags.ResearchAgreement) - && gold >= cost && otherCiv.gold >= cost - } fun getStatForRanking(category: RankingType): Int { return if (isDefeated()) 0 @@ -687,9 +582,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization { return sum } - fun hasTechOrPolicy(techOrPolicyName: String) = - tech.isResearched(techOrPolicyName) || policies.isAdopted(techOrPolicyName) - fun isMinorCivAggressor() = numMinorCivsAttacked >= 2 fun isMinorCivWarmonger() = numMinorCivsAttacked >= 4 diff --git a/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt b/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt index f691576f83..3b48004e88 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt @@ -257,7 +257,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) { civInfo.getDiplomacyManager(newEnemy).declareWar() else if (!civInfo.knows(newEnemy)) { // We have to meet first - civInfo.makeCivilizationsMeet(newEnemy, warOnContact = true) + civInfo.diplomacyFunctions.makeCivilizationsMeet(newEnemy, warOnContact = true) civInfo.getDiplomacyManager(newEnemy).declareWar() } } diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt new file mode 100644 index 0000000000..7bd1d5f84b --- /dev/null +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt @@ -0,0 +1,119 @@ +package com.unciv.logic.civilization.diplomacy + +import com.unciv.UncivGame +import com.unciv.logic.civilization.AlertType +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.models.ruleset.unique.UniqueType +import com.unciv.models.stats.Stat +import com.unciv.models.stats.Stats +import com.unciv.models.translations.tr +import com.unciv.ui.utils.extensions.withItem + +class DiplomacyFunctions(val civInfo:CivilizationInfo){ + + /** A sorted Sequence of all other civs we know (excluding barbarians and spectators) */ + fun getKnownCivsSorted(includeCityStates: Boolean = true, includeDefeated: Boolean = false) = + civInfo.gameInfo.civilizations.asSequence() + .filterNot { + it == civInfo || + it.isBarbarian() || + it.isSpectator() || + !civInfo.knows(it) || + !includeDefeated && it.isDefeated() || + !includeCityStates && it.isCityState() + } + .sortedWith( + compareByDescending { it.isMajorCiv() } + .thenBy (UncivGame.Current.settings.getCollatorFromLocale()) { it.civName.tr() } + ) + + + fun makeCivilizationsMeet(otherCiv: CivilizationInfo, warOnContact: Boolean = false) { + meetCiv(otherCiv, warOnContact) + otherCiv.diplomacyFunctions.meetCiv(civInfo, warOnContact) + } + + fun meetCiv(otherCiv: CivilizationInfo, warOnContact: Boolean = false) { + civInfo.diplomacy[otherCiv.civName] = DiplomacyManager(civInfo, otherCiv.civName) + .apply { diplomaticStatus = DiplomaticStatus.Peace } + + if (!otherCiv.isSpectator()) + otherCiv.popupAlerts.add(PopupAlert(AlertType.FirstContact, civInfo.civName)) + + if (civInfo.isCurrentPlayer()) + UncivGame.Current.settings.addCompletedTutorialTask("Meet another civilization") + + + if (civInfo.isCityState() && otherCiv.isMajorCiv()) { + if (warOnContact || otherCiv.isMinorCivAggressor()) return // No gift if they are bad people, or we are just about to be at war + + val cityStateLocation = if (civInfo.cities.isEmpty()) null else civInfo.getCapital()!!.location + + val giftAmount = Stats(gold = 15f) + val faithAmount = Stats(faith = 4f) + // Later, religious city-states will also gift gold, making this the better implementation + // For now, it might be overkill though. + var meetString = "[${civInfo.civName}] has given us [${giftAmount}] as a token of goodwill for meeting us" + val religionMeetString = "[${civInfo.civName}] has also given us [${faithAmount}]" + if (civInfo.diplomacy.filter { it.value.otherCiv().isMajorCiv() }.size == 1) { + giftAmount.timesInPlace(2f) + meetString = "[${civInfo.civName}] has given us [${giftAmount}] as we are the first major civ to meet them" + } + if (cityStateLocation != null) + otherCiv.addNotification(meetString, cityStateLocation, NotificationCategory.Diplomacy, NotificationIcon.Gold) + else + otherCiv.addNotification(meetString, NotificationCategory.Diplomacy, NotificationIcon.Gold) + + if (otherCiv.isCityState() && otherCiv.cityStateFunctions.canProvideStat(Stat.Faith)){ + otherCiv.addNotification(religionMeetString, NotificationCategory.Diplomacy, NotificationIcon.Faith) + + for ((key, value) in faithAmount) + otherCiv.addStat(key, value.toInt()) + } + for ((key, value) in giftAmount) + otherCiv.addStat(key, value.toInt()) + + if (civInfo.cities.isNotEmpty()) + otherCiv.exploredTiles = otherCiv.exploredTiles.withItem(civInfo.getCapital()!!.location) + + civInfo.questManager.justMet(otherCiv) // Include them in war with major pseudo-quest + } + } + + + fun isAtWarWith(otherCiv: CivilizationInfo): Boolean { + return when { + otherCiv == civInfo -> false + otherCiv.isBarbarian() || civInfo.isBarbarian() -> true + else -> { + val diplomacyManager = civInfo.diplomacy[otherCiv.civName] + ?: return false // not encountered yet + return diplomacyManager.diplomaticStatus == DiplomaticStatus.War + } + } + } + + + fun canSignResearchAgreement(): Boolean { + if (!civInfo.isMajorCiv()) return false + if (!civInfo.hasUnique(UniqueType.EnablesResearchAgreements)) return false + if (civInfo.gameInfo.ruleSet.technologies.values + .none { civInfo.tech.canBeResearched(it.name) && !civInfo.tech.isResearched(it.name) }) return false + return true + } + + fun canSignResearchAgreementsWith(otherCiv: CivilizationInfo): Boolean { + val diplomacyManager = civInfo.getDiplomacyManager(otherCiv) + val cost = civInfo.getResearchAgreementCost() + return canSignResearchAgreement() && otherCiv.diplomacyFunctions.canSignResearchAgreement() + && diplomacyManager.hasFlag(DiplomacyFlags.DeclarationOfFriendship) + && !diplomacyManager.hasFlag(DiplomacyFlags.ResearchAgreement) + && !diplomacyManager.otherCivDiplomacy().hasFlag(DiplomacyFlags.ResearchAgreement) + && civInfo.gold >= cost && otherCiv.gold >= cost + } + + +} diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt index a04ee9e7c9..ecdeebdc42 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt @@ -665,7 +665,7 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization { thirdCiv.getDiplomacyManager(civAtWarWith).declareWar(true) else if (!thirdCiv.knows(civAtWarWith)) { // Our city state ally has not met them yet, so they have to meet first - thirdCiv.makeCivilizationsMeet(civAtWarWith, warOnContact = true) + thirdCiv.diplomacyFunctions.makeCivilizationsMeet(civAtWarWith, warOnContact = true) thirdCiv.getDiplomacyManager(civAtWarWith).declareWar(true) } } diff --git a/core/src/com/unciv/logic/civilization/transients/CivInfoTransientCache.kt b/core/src/com/unciv/logic/civilization/transients/CivInfoTransientCache.kt index f3f11146e5..0e628ea253 100644 --- a/core/src/com/unciv/logic/civilization/transients/CivInfoTransientCache.kt +++ b/core/src/com/unciv/logic/civilization/transients/CivInfoTransientCache.kt @@ -50,7 +50,7 @@ class CivInfoTransientCache(val civInfo: CivilizationInfo) { for (entry in viewedCivs) { val metCiv = entry.key if (metCiv == civInfo || metCiv.isBarbarian() || civInfo.diplomacy.containsKey(metCiv.civName)) continue - civInfo.makeCivilizationsMeet(metCiv) + civInfo.diplomacyFunctions.makeCivilizationsMeet(metCiv) civInfo.addNotification("We have encountered [${metCiv.civName}]!", entry.value.position, NotificationCategory.Diplomacy, metCiv.civName, diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 72ce527394..4d453f2c6c 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -2,8 +2,8 @@ package com.unciv.logic.map import com.badlogic.gdx.math.Vector2 import com.unciv.Constants -import com.unciv.logic.map.HexMath.getDistance import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.logic.map.HexMath.getDistance import com.unciv.models.helpers.UnitMovementMemoryType import com.unciv.models.ruleset.unique.UniqueType @@ -11,6 +11,18 @@ class UnitMovementAlgorithms(val unit: MapUnit) { private val pathfindingCache = PathfindingCache(unit) + fun getEnemyMovementPenalty(civInfo:CivilizationInfo, enemyUnit: MapUnit): Float { + if (civInfo.enemyMovementPenaltyUniques != null && civInfo.enemyMovementPenaltyUniques!!.any()) { + return civInfo.enemyMovementPenaltyUniques!!.sumOf { + if (it.type!! == UniqueType.EnemyLandUnitsSpendExtraMovement + && enemyUnit.matchesFilter(it.params[0])) + it.params[1].toInt() + else 0 + }.toFloat() + } + return 0f // should not reach this point + } + // This function is called ALL THE TIME and should be as time-optimal as possible! private fun getMovementCostBetweenAdjacentTiles( from: TileInfo, @@ -36,7 +48,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) { toOwner != null && toOwner.hasActiveEnemyMovementPenalty && civInfo.isAtWarWith(toOwner) - ) toOwner.getEnemyMovementPenalty(unit) else 0f + ) getEnemyMovementPenalty(toOwner, unit) else 0f if (from.getUnpillagedRoad() == RoadStatus.Railroad && to.getUnpillagedRoad() == RoadStatus.Railroad) return RoadStatus.Railroad.movement + extraCost diff --git a/core/src/com/unciv/logic/trade/TradeLogic.kt b/core/src/com/unciv/logic/trade/TradeLogic.kt index 201be00109..84f5da3c92 100644 --- a/core/src/com/unciv/logic/trade/TradeLogic.kt +++ b/core/src/com/unciv/logic/trade/TradeLogic.kt @@ -120,7 +120,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci } } if (offer.type == TradeType.Introduction) - to.makeCivilizationsMeet(to.gameInfo.getCivilization(offer.name)) + to.diplomacyFunctions.makeCivilizationsMeet(to.gameInfo.getCivilization(offer.name)) if (offer.type == TradeType.WarDeclaration) { val nameOfCivToDeclareWarOn = offer.name from.getDiplomacyManager(nameOfCivToDeclareWarOn).declareWar() diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index 34c5588d0b..26c620371a 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -232,8 +232,6 @@ class Ruleset { allRulesetObjects() + sequenceOf(modOptions) fun load(folderHandle: FileHandle) { - val gameBasicsStartTime = System.currentTimeMillis() - val modOptionsFile = folderHandle.child("ModOptions.json") if (modOptionsFile.exists()) { try { diff --git a/core/src/com/unciv/models/ruleset/unique/Unique.kt b/core/src/com/unciv/models/ruleset/unique/Unique.kt index a467600279..4d4781b9e3 100644 --- a/core/src/com/unciv/models/ruleset/unique/Unique.kt +++ b/core/src/com/unciv/models/ruleset/unique/Unique.kt @@ -271,7 +271,7 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s UniqueType.ConditionalFirstCivToResearch -> sourceObjectType == UniqueTarget.Tech && state.civInfo != null && state.civInfo.gameInfo.civilizations.none { - it != state.civInfo && it.isMajorCiv() && it.hasTechOrPolicy(sourceObjectName!!) + it != state.civInfo && it.isMajorCiv() && (it.tech.isResearched(sourceObjectName!!) || it.policies.isAdopted(sourceObjectName)) } else -> false diff --git a/core/src/com/unciv/ui/multiplayer/MultiplayerScreen.kt b/core/src/com/unciv/ui/multiplayer/MultiplayerScreen.kt index 0c3f4414e9..4eb9a3b9fa 100644 --- a/core/src/com/unciv/ui/multiplayer/MultiplayerScreen.kt +++ b/core/src/com/unciv/ui/multiplayer/MultiplayerScreen.kt @@ -10,14 +10,13 @@ import com.unciv.models.translations.tr import com.unciv.ui.pickerscreens.PickerScreen import com.unciv.ui.popup.Popup import com.unciv.ui.popup.ToastPopup -import com.unciv.ui.utils.BaseScreen import com.unciv.ui.utils.extensions.disable import com.unciv.ui.utils.extensions.enable import com.unciv.ui.utils.extensions.onClick import com.unciv.ui.utils.extensions.toTextButton import com.unciv.ui.utils.AutoScrollPane as ScrollPane -class MultiplayerScreen(previousScreen: BaseScreen) : PickerScreen() { +class MultiplayerScreen : PickerScreen() { private var selectedGame: OnlineMultiplayerGame? = null private val editButtonText = "Game settings" diff --git a/core/src/com/unciv/ui/options/GameplayTab.kt b/core/src/com/unciv/ui/options/GameplayTab.kt index 69a6c733c8..12dc62ddc7 100644 --- a/core/src/com/unciv/ui/options/GameplayTab.kt +++ b/core/src/com/unciv/ui/options/GameplayTab.kt @@ -7,7 +7,6 @@ import com.unciv.models.metadata.GameSettings import com.unciv.ui.utils.BaseScreen import com.unciv.ui.utils.UncivSlider import com.unciv.ui.utils.extensions.toLabel -import com.unciv.ui.worldscreen.WorldScreen fun gameplayTab( optionsPopup: OptionsPopup @@ -45,10 +44,14 @@ fun gameplayTab( optionsPopup.addCheckbox(this, "Order trade offers by amount", settings.orderTradeOffersByAmount) { settings.orderTradeOffersByAmount = it } optionsPopup.addCheckbox(this, "Ask for confirmation when pressing next turn", settings.confirmNextTurn) { settings.confirmNextTurn = it } - addNotificationLogMaxTurnsSlider(this, settings, UncivGame.Current.worldScreen, optionsPopup.selectBoxMinWidth) + addNotificationLogMaxTurnsSlider(this, settings, optionsPopup.selectBoxMinWidth) } -private fun addNotificationLogMaxTurnsSlider(table: Table, settings: GameSettings, screen: BaseScreen?, selectBoxMinWidth: Float) { +private fun addNotificationLogMaxTurnsSlider( + table: Table, + settings: GameSettings, + selectBoxMinWidth: Float +) { table.add("Notifications log max turns".toLabel()).left().fillX() val minimapSlider = UncivSlider( diff --git a/core/src/com/unciv/ui/overviewscreen/GlobalPoliticsOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/GlobalPoliticsOverviewTable.kt index d68a3f20d8..9912baa591 100644 --- a/core/src/com/unciv/ui/overviewscreen/GlobalPoliticsOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/GlobalPoliticsOverviewTable.kt @@ -9,11 +9,11 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.badlogic.gdx.utils.Align import com.unciv.Constants import com.unciv.UncivGame -import com.unciv.logic.map.HexMath import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.diplomacy.DiplomacyFlags import com.unciv.logic.civilization.diplomacy.DiplomaticStatus import com.unciv.logic.civilization.diplomacy.RelationshipLevel +import com.unciv.logic.map.HexMath import com.unciv.models.ruleset.Policy.PolicyBranchType import com.unciv.ui.images.ImageGetter import com.unciv.ui.trade.DiplomacyScreen @@ -262,8 +262,8 @@ class GlobalPoliticsOverviewTable ( !it.isSpectator() && !it.isBarbarian() && (persistableData.includeCityStates || !it.isCityState()) } undefeatedCivs = sequenceOf(viewingPlayer) + - viewingPlayer.getKnownCivsSorted(persistableData.includeCityStates) - defeatedCivs = viewingPlayer.getKnownCivsSorted(persistableData.includeCityStates, true) + viewingPlayer.diplomacyFunctions.getKnownCivsSorted(persistableData.includeCityStates) + defeatedCivs = viewingPlayer.diplomacyFunctions.getKnownCivsSorted(persistableData.includeCityStates, true) .filter { it.isDefeated() } clear() diff --git a/core/src/com/unciv/ui/pickerscreens/PickerPane.kt b/core/src/com/unciv/ui/pickerscreens/PickerPane.kt index 6b2d5ab7db..87a517eb9a 100644 --- a/core/src/com/unciv/ui/pickerscreens/PickerPane.kt +++ b/core/src/com/unciv/ui/pickerscreens/PickerPane.kt @@ -83,7 +83,7 @@ class PickerPane( /** Return a button for picker screens that display a list of big buttons with icons and labels. */ fun getPickerOptionButton(icon: Actor, label: String): Button { return IconTextButton(label, icon).apply { - iconCell!!.size(pickerOptionIconSize).pad(10f) + iconCell.size(pickerOptionIconSize).pad(10f) labelCell.pad(10f) } } diff --git a/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt index 4a0c4f987f..32e3b300e9 100644 --- a/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt @@ -211,7 +211,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo topTable.row() val branches = viewingCiv.gameInfo.ruleSet.policyBranches - var rowChangeCount = Int.MAX_VALUE + val rowChangeCount: Int // estimate how many branch boxes fit using average size (including pad) // TODO If we'd want to use scene2d correctly, this is supposed to happen inside an overridden layout() method @@ -220,9 +220,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo if (numBranchesY > 1.5f) { val numRows = if (numBranchesY < 2.9f) 2 else (numBranchesY + 0.1f).toInt() rowChangeCount = (branches.size + numRows - 1) / numRows - } else { - rowChangeCount = branches.size - } + } else rowChangeCount = branches.size // Actually create and distribute the policy branches diff --git a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt index 0dfca06699..68b44547b5 100644 --- a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt +++ b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt @@ -8,7 +8,6 @@ import com.badlogic.gdx.utils.Align import com.unciv.Constants import com.unciv.UncivGame import com.unciv.logic.civilization.AlertType -import com.unciv.logic.civilization.managers.AssignedQuest import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.PopupAlert import com.unciv.logic.civilization.diplomacy.DiplomacyFlags @@ -17,6 +16,7 @@ import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers.* import com.unciv.logic.civilization.diplomacy.DiplomaticStatus import com.unciv.logic.civilization.diplomacy.RelationshipLevel +import com.unciv.logic.civilization.managers.AssignedQuest import com.unciv.logic.trade.Trade import com.unciv.logic.trade.TradeLogic import com.unciv.logic.trade.TradeOffer @@ -113,7 +113,7 @@ class DiplomacyScreen( var selectCivY = 0f - for (civ in viewingCiv.getKnownCivsSorted()) { + for (civ in viewingCiv.diplomacyFunctions.getKnownCivsSorted()) { if (civ == selectCiv) { selectCivY = leftSideTable.prefHeight } @@ -665,7 +665,7 @@ class DiplomacyScreen( diplomacyTable.add(getDeclareFriendshipButton(otherCiv)).row() - if (viewingCiv.canSignResearchAgreementsWith(otherCiv)) + if (viewingCiv.diplomacyFunctions.canSignResearchAgreementsWith(otherCiv)) diplomacyTable.add(getResearchAgreementButton(otherCiv)).row() if (!diplomacyManager.hasFlag(DiplomacyFlags.Denunciation) diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index acbfa653af..66953bdc8c 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -884,7 +884,7 @@ object UnitActions { val otherCiv = tile.getOwner() if (otherCiv != null) { // decrease relations for -10 pt/tile - if (!otherCiv.knows(unit.civInfo)) otherCiv.makeCivilizationsMeet(unit.civInfo) + if (!otherCiv.knows(unit.civInfo)) otherCiv.diplomacyFunctions.makeCivilizationsMeet(unit.civInfo) otherCiv.getDiplomacyManager(unit.civInfo).addModifier(DiplomaticModifiers.StealingTerritory, -10f) civsToNotify.add(otherCiv) } @@ -989,7 +989,7 @@ object UnitActions { .addModifier(DiplomaticModifiers.GaveUsUnits, 5f) if (recipient.isCityState() && unit.isGreatPerson()) - unit.destroy() // City states dont get GPs + unit.destroy() // City states don't get GPs else unit.gift(recipient) UncivGame.Current.worldScreen!!.shouldUpdate = true diff --git a/tests/src/com/unciv/logic/civilization/diplomacy/DiplomacyManagerTests.kt b/tests/src/com/unciv/logic/civilization/diplomacy/DiplomacyManagerTests.kt index 60a68ae76c..316e701054 100644 --- a/tests/src/com/unciv/logic/civilization/diplomacy/DiplomacyManagerTests.kt +++ b/tests/src/com/unciv/logic/civilization/diplomacy/DiplomacyManagerTests.kt @@ -25,7 +25,7 @@ class DiplomacyManagerTests { private fun meetByName(civilization: String, civilizationToMeet: String) { civilizations.getValue(civilization) - .makeCivilizationsMeet(civilizations.getValue(civilizationToMeet)) + .diplomacyFunctions.makeCivilizationsMeet(civilizations.getValue(civilizationToMeet)) } @Before diff --git a/tests/src/com/unciv/testing/SerializationTests.kt b/tests/src/com/unciv/testing/SerializationTests.kt index ebecf8c721..42efbd482c 100644 --- a/tests/src/com/unciv/testing/SerializationTests.kt +++ b/tests/src/com/unciv/testing/SerializationTests.kt @@ -80,7 +80,7 @@ class SerializationTests { // Ensure some diplomacy objects are instantiated val otherCiv = game.getCivilization("Greece") - civ.makeCivilizationsMeet(otherCiv) + civ.diplomacyFunctions.makeCivilizationsMeet(otherCiv) } @Test diff --git a/tests/src/com/unciv/uniques/GlobalUniquesTests.kt b/tests/src/com/unciv/uniques/GlobalUniquesTests.kt index f6eb11286b..121dec2c83 100644 --- a/tests/src/com/unciv/uniques/GlobalUniquesTests.kt +++ b/tests/src/com/unciv/uniques/GlobalUniquesTests.kt @@ -383,7 +383,7 @@ class GlobalUniquesTests { val cityStateTile = game.getTile(Vector2(0f, 1f)) @Suppress("UNUSED_VARIABLE") val cityStateCity = game.addCity(cityState, cityStateTile, true) - civInfo.makeCivilizationsMeet(cityState) + civInfo.diplomacyFunctions.makeCivilizationsMeet(cityState) cityState.getDiplomacyManager(civInfo).addInfluence(100f) city.cityStats.update()