From c81c81fe8f0ecb40058b202d0748b3555172b4bf Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Wed, 24 Apr 2019 10:19:47 +0300 Subject: [PATCH] Resolved #525 - Added Open Borders agreement --- core/src/com/unciv/GameStarter.kt | 5 ++--- .../logic/automation/NextTurnAutomation.kt | 18 +++++++++--------- .../logic/civilization/CivilizationInfo.kt | 8 ++++++-- .../civilization/diplomacy/DiplomacyManager.kt | 13 ++++++++++--- .../com/unciv/logic/trade/TradeEvaluation.kt | 10 +++++++++- core/src/com/unciv/logic/trade/TradeLogic.kt | 13 +++++++------ core/src/com/unciv/logic/trade/TradeType.kt | 3 +++ core/src/com/unciv/ui/trade/DiplomacyScreen.kt | 2 +- .../src/com/unciv/ui/trade/OffersListScroll.kt | 1 + .../src/com/unciv/ui/worldscreen/TradePopup.kt | 4 ++-- .../com/unciv/ui/worldscreen/WorldScreen.kt | 2 +- 11 files changed, 51 insertions(+), 28 deletions(-) diff --git a/core/src/com/unciv/GameStarter.kt b/core/src/com/unciv/GameStarter.kt index c5ccab8fd5..a07117c383 100644 --- a/core/src/com/unciv/GameStarter.kt +++ b/core/src/com/unciv/GameStarter.kt @@ -11,7 +11,6 @@ import com.unciv.logic.map.TileMap import com.unciv.models.gamebasics.GameBasics import java.util.* import kotlin.collections.ArrayList -import kotlin.math.min class GameParameters{ var difficulty="Prince" @@ -91,7 +90,7 @@ class GameStarter{ for(minimumDistanceBetweenStartingLocations in tileMap.tileMatrix.size/2 downTo 0){ val freeTiles = landTilesInBigEnoughGroup - .filter { vectorIsWithinNTilesOfEdge(it.position,min(3,minimumDistanceBetweenStartingLocations),tileMap)} + .filter { vectorIsAtLeastNTilesAwayFromEdge(it.position,minimumDistanceBetweenStartingLocations,tileMap)} .toMutableList() val startingLocations = ArrayList() @@ -109,7 +108,7 @@ class GameStarter{ throw Exception("Didn't manage to get starting locations even with distance of 1?") } - fun vectorIsWithinNTilesOfEdge(vector: Vector2,n:Int, tileMap: TileMap): Boolean { + fun vectorIsAtLeastNTilesAwayFromEdge(vector: Vector2, n:Int, tileMap: TileMap): Boolean { val arrayXIndex = vector.x.toInt()-tileMap.leftX val arrayYIndex = vector.y.toInt()-tileMap.bottomY diff --git a/core/src/com/unciv/logic/automation/NextTurnAutomation.kt b/core/src/com/unciv/logic/automation/NextTurnAutomation.kt index b6b39765d2..a40a69e186 100644 --- a/core/src/com/unciv/logic/automation/NextTurnAutomation.kt +++ b/core/src/com/unciv/logic/automation/NextTurnAutomation.kt @@ -41,9 +41,9 @@ class NextTurnAutomation{ } private fun exchangeTechs(civInfo: CivilizationInfo) { - val otherCivList = civInfo.diplomacy.values.map { it.otherCiv() }. - filter { it.playerType == PlayerType.AI && !it.isBarbarianCivilization() }. - sortedBy { it.tech.techsResearched.size } + val otherCivList = civInfo.getKnownCivs() + .filter { it.playerType == PlayerType.AI && !it.isBarbarianCivilization() } + .sortedBy { it.tech.techsResearched.size } for (otherCiv in otherCivList) { val tradeLogic = TradeLogic(civInfo, otherCiv) @@ -141,7 +141,7 @@ class NextTurnAutomation{ } private fun exchangeLuxuries(civInfo: CivilizationInfo) { - val knownCivs = civInfo.diplomacy.values.map { it.otherCiv() } + val knownCivs = civInfo.getKnownCivs() // Player trades are... more complicated. // When the AI offers a trade, it's not immediately accepted, @@ -151,7 +151,7 @@ class NextTurnAutomation{ // B. have a way for the AI to keep track of the "pending offers" - see DiplomacyManager.resourcesFromTrade for (otherCiv in knownCivs.filter { it.isPlayerCivilization() && !it.isAtWarWith(civInfo) - && !civInfo.diplomacy[it.civName]!!.flagsCountdown.containsKey(DiplomacyFlags.DeclinedLuxExchange)}) { + && !civInfo.getDiplomacyManager(it).flagsCountdown.containsKey(DiplomacyFlags.DeclinedLuxExchange)}) { val trades = potentialLuxuryTrades(civInfo,otherCiv) for(trade in trades){ val tradeRequest = TradeRequest(civInfo.civName, trade.reverse()) @@ -185,7 +185,7 @@ class NextTurnAutomation{ val enemiesCiv = civInfo.diplomacy.filter{ it.value.diplomaticStatus == DiplomaticStatus.War } .map{ it.value.otherCiv() } .filterNot{ it == civInfo || it.isBarbarianCivilization() || it.cities.isEmpty() } - .filter { !civInfo.diplomacy[it.civName]!!.flagsCountdown.containsKey(DiplomacyFlags.DeclinedPeace) } + .filter { !civInfo.getDiplomacyManager(it).flagsCountdown.containsKey(DiplomacyFlags.DeclinedPeace) } for (enemy in enemiesCiv) { val enemiesStrength = Automation().evaluteCombatStrength(enemy) @@ -228,8 +228,8 @@ class NextTurnAutomation{ if (!civInfo.isAtWar() && civInfo.happiness > 0 && ourMilitaryUnits >= civInfo.cities.size) { //evaluate war val ourCombatStrength = Automation().evaluteCombatStrength(civInfo) - val enemyCivsByDistanceToOurs = civInfo.diplomacy.values.map { it.otherCiv() } - .filterNot { it == civInfo || it.cities.isEmpty() || !civInfo.diplomacy[it.civName]!!.canDeclareWar() } + val enemyCivsByDistanceToOurs = civInfo.getKnownCivs() + .filterNot { it == civInfo || it.cities.isEmpty() || !civInfo.getDiplomacyManager(it).canDeclareWar() } .groupBy { getMinDistanceBetweenCities(civInfo, it) } .toSortedMap() @@ -237,7 +237,7 @@ class NextTurnAutomation{ if (group.key > 7) break for (otherCiv in group.value) { if (Automation().evaluteCombatStrength(otherCiv) * 2 < ourCombatStrength) { - civInfo.diplomacy[otherCiv.civName]!!.declareWar() + civInfo.getDiplomacyManager(otherCiv).declareWar() return } } diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 7261d971ad..b3e2343242 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -94,7 +94,7 @@ class CivilizationInfo { toReturn.goldenAges = goldenAges.clone() toReturn.greatPeople = greatPeople.clone() toReturn.victoryManager = victoryManager.clone() - toReturn.diplomacy.putAll(diplomacy.values.map { it.clone() }.associateBy { it.otherCivName }) + toReturn.diplomacy.putAll(diplomacy) toReturn.cities = cities.map { it.clone() } toReturn.exploredTiles.addAll(exploredTiles) toReturn.notifications.addAll(notifications) @@ -119,6 +119,9 @@ class CivilizationInfo { return translatedNation } + fun getDiplomacyManager(civInfo: CivilizationInfo) = diplomacy[civInfo.civName]!! + fun getKnownCivs() = diplomacy.values.map { it.otherCiv() } + fun getCapital()=cities.first { it.isCapital() } fun isPlayerCivilization() = playerType==PlayerType.Human fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==this @@ -323,7 +326,7 @@ class CivilizationInfo { if(otherCiv.isBarbarianCivilization() || isBarbarianCivilization()) return true if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet return false - return diplomacy[otherCiv.civName]!!.diplomaticStatus == DiplomaticStatus.War + return getDiplomacyManager(otherCiv).diplomaticStatus == DiplomaticStatus.War } fun isAtWar() = diplomacy.values.any { it.diplomaticStatus== DiplomaticStatus.War && !it.otherCiv().isDefeated() } @@ -425,6 +428,7 @@ class CivilizationInfo { fun canEnterTiles(otherCiv: CivilizationInfo): Boolean { if(otherCiv==this) return true if(isAtWarWith(otherCiv)) return true + if(getDiplomacyManager(otherCiv).hasOpenBorders()) return true return false } diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt index 6ab9c9691a..3ee5db6f56 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt @@ -45,6 +45,13 @@ class DiplomacyManager() { return 0 } + fun hasOpenBorders(): Boolean { + for(trade in trades) + for(offer in trade.theirOffers) + if(offer.name=="Open Borders" && offer.duration > 0) return true + return false + } + fun canDeclareWar() = (turnsToPeaceTreaty()==0 && diplomaticStatus != DiplomaticStatus.War) fun otherCiv() = civInfo.gameInfo.getCivilization(otherCivName) @@ -87,7 +94,7 @@ class DiplomacyManager() { if (offer.type in listOf(TradeType.Luxury_Resource, TradeType.Strategic_Resource) && offer.name in negativeCivResources){ trades.remove(trade) - val otherCivTrades = otherCiv().diplomacy[civInfo.civName]!!.trades + val otherCivTrades = otherCiv().getDiplomacyManager(civInfo).trades otherCivTrades.removeAll{ it.equals(trade.reverse()) } civInfo.addNotification("One of our trades with [$otherCivName] has been cut short!".tr(),null, Color.GOLD) otherCiv().addNotification("One of our trades with [${civInfo.civName}] has been cut short!".tr(),null, Color.GOLD) @@ -119,13 +126,13 @@ class DiplomacyManager() { diplomaticStatus = DiplomaticStatus.War val otherCiv = otherCiv() - otherCiv.diplomacy[civInfo.civName]!!.diplomaticStatus = DiplomaticStatus.War + otherCiv.getDiplomacyManager(civInfo).diplomaticStatus = DiplomaticStatus.War otherCiv.addNotification("[${civInfo.civName}] has declared war on us!",null, Color.RED) otherCiv.popupAlerts.add(PopupAlert(AlertType.WarDeclaration,civInfo.civName)) /// AI won't propose peace for 10 turns flagsCountdown[DiplomacyFlags.DeclinedPeace]=10 - otherCiv.diplomacy[civInfo.civName]!!.flagsCountdown[DiplomacyFlags.DeclinedPeace]=10 + otherCiv.getDiplomacyManager(civInfo).flagsCountdown[DiplomacyFlags.DeclinedPeace]=10 } //endregion } \ No newline at end of file diff --git a/core/src/com/unciv/logic/trade/TradeEvaluation.kt b/core/src/com/unciv/logic/trade/TradeEvaluation.kt index e84f47024b..840da90e3a 100644 --- a/core/src/com/unciv/logic/trade/TradeEvaluation.kt +++ b/core/src/com/unciv/logic/trade/TradeEvaluation.kt @@ -82,7 +82,7 @@ class TradeEvaluation{ val civToDeclareWarOn = civInfo.gameInfo.getCivilization(nameOfCivToDeclareWarOn) val threatToThem = Automation().threatAssessment(civInfo,civToDeclareWarOn) - if(civInfo.diplomacy[nameOfCivToDeclareWarOn]!!.diplomaticStatus== DiplomaticStatus.War){ + if(civInfo.getDiplomacyManager(civToDeclareWarOn).diplomaticStatus== DiplomaticStatus.War){ when (threatToThem) { ThreatLevel.VeryLow -> return 0 ThreatLevel.Low -> return 0 @@ -101,6 +101,10 @@ class TradeEvaluation{ val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food return sumOfStats.toInt() * 100 } + TradeType.Agreement -> { + if(offer.name=="Open Borders") return 100 + throw Exception("Invalid agreement type!") + } } } @@ -141,6 +145,10 @@ class TradeEvaluation{ val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food return sumOfStats.toInt() * 100 } + TradeType.Agreement -> { + if(offer.name=="Open Borders") return 100 + throw Exception("Invalid agreement type!") + } } } diff --git a/core/src/com/unciv/logic/trade/TradeLogic.kt b/core/src/com/unciv/logic/trade/TradeLogic.kt index 59d03a8b36..605ce545a1 100644 --- a/core/src/com/unciv/logic/trade/TradeLogic.kt +++ b/core/src/com/unciv/logic/trade/TradeLogic.kt @@ -1,7 +1,6 @@ package com.unciv.logic.trade import com.unciv.logic.civilization.CivilizationInfo -import com.unciv.logic.civilization.diplomacy.DiplomacyFlags import com.unciv.logic.civilization.diplomacy.DiplomaticStatus import com.unciv.models.gamebasics.tile.ResourceType import com.unciv.models.gamebasics.tr @@ -17,6 +16,8 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci val offers = TradeOffersList() if(civInfo.isAtWarWith(otherCivilization)) offers.add(TradeOffer("Peace Treaty", TradeType.Treaty, 20)) + if(!otherCivilization.getDiplomacyManager(civInfo).hasOpenBorders()) + offers.add(TradeOffer("Open Borders", TradeType.Agreement, 30)) for(entry in civInfo.getCivResources().filterNot { it.key.resourceType == ResourceType.Bonus }) { val resourceTradeType = if(entry.key.resourceType== ResourceType.Luxury) TradeType.Luxury_Resource else TradeType.Strategic_Resource @@ -32,7 +33,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci for(city in civInfo.cities.filterNot { it.isCapital() }) offers.add(TradeOffer(city.name, TradeType.City, 0)) - val otherCivsWeKnow = civInfo.diplomacy.values.map { it.otherCiv() } + val otherCivsWeKnow = civInfo.getKnownCivs() .filter { it != otherCivilization && !it.isBarbarianCivilization() && !it.isDefeated() } val civsWeKnowAndTheyDont = otherCivsWeKnow .filter { !otherCivilization.diplomacy.containsKey(it.civName) && !it.isDefeated() } @@ -43,7 +44,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci val civsWeBothKnow = otherCivsWeKnow .filter { otherCivilization.diplomacy.containsKey(it.civName) } val civsWeArentAtWarWith = civsWeBothKnow - .filter { civInfo.diplomacy[it.civName]!!.diplomaticStatus== DiplomaticStatus.Peace } + .filter { civInfo.getDiplomacyManager(it).diplomaticStatus== DiplomaticStatus.Peace } for(thirdCiv in civsWeArentAtWarWith){ offers.add(TradeOffer("Declare war on "+thirdCiv.civName,TradeType.WarDeclaration,0)) } @@ -52,8 +53,8 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci } fun acceptTrade() { - ourCivilization.diplomacy[otherCivilization.civName]!!.trades.add(currentTrade) - otherCivilization.diplomacy[ourCivilization.civName]!!.trades.add(currentTrade.reverse()) + ourCivilization.getDiplomacyManager(otherCivilization).trades.add(currentTrade) + otherCivilization.getDiplomacyManager(ourCivilization).trades.add(currentTrade.reverse()) // instant transfers fun transferTrade(to: CivilizationInfo, from: CivilizationInfo, trade: Trade) { @@ -74,7 +75,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci } if(offer.type== TradeType.Treaty){ if(offer.name=="Peace Treaty"){ - to.diplomacy[from.civName]!!.diplomaticStatus= DiplomaticStatus.Peace + to.getDiplomacyManager(from).diplomaticStatus= DiplomaticStatus.Peace for(unit in to.getCivUnits().filter { it.getTile().getOwner()==from }) unit.movementAlgs().teleportToClosestMoveableTile() } diff --git a/core/src/com/unciv/logic/trade/TradeType.kt b/core/src/com/unciv/logic/trade/TradeType.kt index 185ecbed3d..41c336d843 100644 --- a/core/src/com/unciv/logic/trade/TradeType.kt +++ b/core/src/com/unciv/logic/trade/TradeType.kt @@ -3,7 +3,10 @@ package com.unciv.logic.trade enum class TradeType{ Gold, Gold_Per_Turn, + /** Treaties are shared by both sides - like peace treaty and defensive pact */ Treaty, + /** Agreements are one-sided, like open borders */ + Agreement, Luxury_Resource, Strategic_Resource, Technology, diff --git a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt index fdbbacab18..4f7642b43d 100644 --- a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt +++ b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt @@ -79,7 +79,7 @@ class DiplomacyScreen:CameraStageBaseScreen() { diplomacyTable.add(tradeButton).row() val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization() - val civDiplomacy = currentPlayerCiv.diplomacy[civ.civName]!! + val civDiplomacy = currentPlayerCiv.getDiplomacyManager(civ) if (!currentPlayerCiv.isAtWarWith(civ)) { val declareWarButton = TextButton("Declare war".tr(), skin) diff --git a/core/src/com/unciv/ui/trade/OffersListScroll.kt b/core/src/com/unciv/ui/trade/OffersListScroll.kt index d6729a2048..c9555b85ba 100644 --- a/core/src/com/unciv/ui/trade/OffersListScroll.kt +++ b/core/src/com/unciv/ui/trade/OffersListScroll.kt @@ -31,6 +31,7 @@ class OffersListScroll(val onOfferClicked: (TradeOffer) -> Unit) : ScrollPane(nu Technology -> "Technologies" WarDeclaration -> "Declarations of war" City -> "Cities" + Agreement -> "Agreements" } val offersOfType = offersToDisplay.filter { it.type == offertype } if (labelName!="" && offersOfType.any()) { diff --git a/core/src/com/unciv/ui/worldscreen/TradePopup.kt b/core/src/com/unciv/ui/worldscreen/TradePopup.kt index 56f3ce160f..1caf2d1e86 100644 --- a/core/src/com/unciv/ui/worldscreen/TradePopup.kt +++ b/core/src/com/unciv/ui/worldscreen/TradePopup.kt @@ -59,10 +59,10 @@ class TradePopup(worldScreen: WorldScreen): PopupTable(worldScreen){ currentPlayerCiv.tradeRequests.remove(tradeRequest) if(trade.ourOffers.all { it.type==TradeType.Luxury_Resource } && trade.theirOffers.all { it.type==TradeType.Luxury_Resource }) - requestingCiv.diplomacy[currentPlayerCiv.civName]!!.flagsCountdown[DiplomacyFlags.DeclinedLuxExchange]=20 // offer again in 20 turns + requestingCiv.getDiplomacyManager(currentPlayerCiv).flagsCountdown[DiplomacyFlags.DeclinedLuxExchange]=20 // offer again in 20 turns if(trade.ourOffers.any{ it.type==TradeType.Treaty && it.name=="Peace Treaty" }) - requestingCiv.diplomacy[currentPlayerCiv.civName]!!.flagsCountdown[DiplomacyFlags.DeclinedPeace]=5 // offer again in 20 turns + requestingCiv.getDiplomacyManager(currentPlayerCiv).flagsCountdown[DiplomacyFlags.DeclinedPeace]=5 // offer again in 20 turns remove() worldScreen.shouldUpdate=true diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index 061fcebd05..216ee1992a 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -178,7 +178,7 @@ class WorldScreen : CameraStageBaseScreen() { private fun updateDiplomacyButton(civInfo: CivilizationInfo) { diplomacyButtonWrapper.clear() - if(civInfo.diplomacy.values.map { it.otherCiv() } + if(civInfo.getKnownCivs() .filterNot { it.isDefeated() || it.isPlayerCivilization() || it.isBarbarianCivilization() } .any()) { displayTutorials("OtherCivEncountered")