Resolved #525 - Added Open Borders agreement

This commit is contained in:
Yair Morgenstern
2019-04-24 10:19:47 +03:00
parent d902db0af0
commit c81c81fe8f
11 changed files with 51 additions and 28 deletions

View File

@ -11,7 +11,6 @@ import com.unciv.logic.map.TileMap
import com.unciv.models.gamebasics.GameBasics import com.unciv.models.gamebasics.GameBasics
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.min
class GameParameters{ class GameParameters{
var difficulty="Prince" var difficulty="Prince"
@ -91,7 +90,7 @@ class GameStarter{
for(minimumDistanceBetweenStartingLocations in tileMap.tileMatrix.size/2 downTo 0){ for(minimumDistanceBetweenStartingLocations in tileMap.tileMatrix.size/2 downTo 0){
val freeTiles = landTilesInBigEnoughGroup val freeTiles = landTilesInBigEnoughGroup
.filter { vectorIsWithinNTilesOfEdge(it.position,min(3,minimumDistanceBetweenStartingLocations),tileMap)} .filter { vectorIsAtLeastNTilesAwayFromEdge(it.position,minimumDistanceBetweenStartingLocations,tileMap)}
.toMutableList() .toMutableList()
val startingLocations = ArrayList<TileInfo>() val startingLocations = ArrayList<TileInfo>()
@ -109,7 +108,7 @@ class GameStarter{
throw Exception("Didn't manage to get starting locations even with distance of 1?") 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 arrayXIndex = vector.x.toInt()-tileMap.leftX
val arrayYIndex = vector.y.toInt()-tileMap.bottomY val arrayYIndex = vector.y.toInt()-tileMap.bottomY

View File

@ -41,9 +41,9 @@ class NextTurnAutomation{
} }
private fun exchangeTechs(civInfo: CivilizationInfo) { private fun exchangeTechs(civInfo: CivilizationInfo) {
val otherCivList = civInfo.diplomacy.values.map { it.otherCiv() }. val otherCivList = civInfo.getKnownCivs()
filter { it.playerType == PlayerType.AI && !it.isBarbarianCivilization() }. .filter { it.playerType == PlayerType.AI && !it.isBarbarianCivilization() }
sortedBy { it.tech.techsResearched.size } .sortedBy { it.tech.techsResearched.size }
for (otherCiv in otherCivList) { for (otherCiv in otherCivList) {
val tradeLogic = TradeLogic(civInfo, otherCiv) val tradeLogic = TradeLogic(civInfo, otherCiv)
@ -141,7 +141,7 @@ class NextTurnAutomation{
} }
private fun exchangeLuxuries(civInfo: CivilizationInfo) { private fun exchangeLuxuries(civInfo: CivilizationInfo) {
val knownCivs = civInfo.diplomacy.values.map { it.otherCiv() } val knownCivs = civInfo.getKnownCivs()
// Player trades are... more complicated. // Player trades are... more complicated.
// When the AI offers a trade, it's not immediately accepted, // 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 // 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) 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) val trades = potentialLuxuryTrades(civInfo,otherCiv)
for(trade in trades){ for(trade in trades){
val tradeRequest = TradeRequest(civInfo.civName, trade.reverse()) val tradeRequest = TradeRequest(civInfo.civName, trade.reverse())
@ -185,7 +185,7 @@ class NextTurnAutomation{
val enemiesCiv = civInfo.diplomacy.filter{ it.value.diplomaticStatus == DiplomaticStatus.War } val enemiesCiv = civInfo.diplomacy.filter{ it.value.diplomaticStatus == DiplomaticStatus.War }
.map{ it.value.otherCiv() } .map{ it.value.otherCiv() }
.filterNot{ it == civInfo || it.isBarbarianCivilization() || it.cities.isEmpty() } .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) { for (enemy in enemiesCiv) {
val enemiesStrength = Automation().evaluteCombatStrength(enemy) val enemiesStrength = Automation().evaluteCombatStrength(enemy)
@ -228,8 +228,8 @@ class NextTurnAutomation{
if (!civInfo.isAtWar() && civInfo.happiness > 0 if (!civInfo.isAtWar() && civInfo.happiness > 0
&& ourMilitaryUnits >= civInfo.cities.size) { //evaluate war && ourMilitaryUnits >= civInfo.cities.size) { //evaluate war
val ourCombatStrength = Automation().evaluteCombatStrength(civInfo) val ourCombatStrength = Automation().evaluteCombatStrength(civInfo)
val enemyCivsByDistanceToOurs = civInfo.diplomacy.values.map { it.otherCiv() } val enemyCivsByDistanceToOurs = civInfo.getKnownCivs()
.filterNot { it == civInfo || it.cities.isEmpty() || !civInfo.diplomacy[it.civName]!!.canDeclareWar() } .filterNot { it == civInfo || it.cities.isEmpty() || !civInfo.getDiplomacyManager(it).canDeclareWar() }
.groupBy { getMinDistanceBetweenCities(civInfo, it) } .groupBy { getMinDistanceBetweenCities(civInfo, it) }
.toSortedMap() .toSortedMap()
@ -237,7 +237,7 @@ class NextTurnAutomation{
if (group.key > 7) break if (group.key > 7) break
for (otherCiv in group.value) { for (otherCiv in group.value) {
if (Automation().evaluteCombatStrength(otherCiv) * 2 < ourCombatStrength) { if (Automation().evaluteCombatStrength(otherCiv) * 2 < ourCombatStrength) {
civInfo.diplomacy[otherCiv.civName]!!.declareWar() civInfo.getDiplomacyManager(otherCiv).declareWar()
return return
} }
} }

View File

@ -94,7 +94,7 @@ class CivilizationInfo {
toReturn.goldenAges = goldenAges.clone() toReturn.goldenAges = goldenAges.clone()
toReturn.greatPeople = greatPeople.clone() toReturn.greatPeople = greatPeople.clone()
toReturn.victoryManager = victoryManager.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.cities = cities.map { it.clone() }
toReturn.exploredTiles.addAll(exploredTiles) toReturn.exploredTiles.addAll(exploredTiles)
toReturn.notifications.addAll(notifications) toReturn.notifications.addAll(notifications)
@ -119,6 +119,9 @@ class CivilizationInfo {
return translatedNation return translatedNation
} }
fun getDiplomacyManager(civInfo: CivilizationInfo) = diplomacy[civInfo.civName]!!
fun getKnownCivs() = diplomacy.values.map { it.otherCiv() }
fun getCapital()=cities.first { it.isCapital() } fun getCapital()=cities.first { it.isCapital() }
fun isPlayerCivilization() = playerType==PlayerType.Human fun isPlayerCivilization() = playerType==PlayerType.Human
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==this fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==this
@ -323,7 +326,7 @@ class CivilizationInfo {
if(otherCiv.isBarbarianCivilization() || isBarbarianCivilization()) return true if(otherCiv.isBarbarianCivilization() || isBarbarianCivilization()) return true
if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet
return false 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() } fun isAtWar() = diplomacy.values.any { it.diplomaticStatus== DiplomaticStatus.War && !it.otherCiv().isDefeated() }
@ -425,6 +428,7 @@ class CivilizationInfo {
fun canEnterTiles(otherCiv: CivilizationInfo): Boolean { fun canEnterTiles(otherCiv: CivilizationInfo): Boolean {
if(otherCiv==this) return true if(otherCiv==this) return true
if(isAtWarWith(otherCiv)) return true if(isAtWarWith(otherCiv)) return true
if(getDiplomacyManager(otherCiv).hasOpenBorders()) return true
return false return false
} }

View File

@ -45,6 +45,13 @@ class DiplomacyManager() {
return 0 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 canDeclareWar() = (turnsToPeaceTreaty()==0 && diplomaticStatus != DiplomaticStatus.War)
fun otherCiv() = civInfo.gameInfo.getCivilization(otherCivName) fun otherCiv() = civInfo.gameInfo.getCivilization(otherCivName)
@ -87,7 +94,7 @@ class DiplomacyManager() {
if (offer.type in listOf(TradeType.Luxury_Resource, TradeType.Strategic_Resource) if (offer.type in listOf(TradeType.Luxury_Resource, TradeType.Strategic_Resource)
&& offer.name in negativeCivResources){ && offer.name in negativeCivResources){
trades.remove(trade) trades.remove(trade)
val otherCivTrades = otherCiv().diplomacy[civInfo.civName]!!.trades val otherCivTrades = otherCiv().getDiplomacyManager(civInfo).trades
otherCivTrades.removeAll{ it.equals(trade.reverse()) } otherCivTrades.removeAll{ it.equals(trade.reverse()) }
civInfo.addNotification("One of our trades with [$otherCivName] has been cut short!".tr(),null, Color.GOLD) 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) 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 diplomaticStatus = DiplomaticStatus.War
val otherCiv = otherCiv() 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.addNotification("[${civInfo.civName}] has declared war on us!",null, Color.RED)
otherCiv.popupAlerts.add(PopupAlert(AlertType.WarDeclaration,civInfo.civName)) otherCiv.popupAlerts.add(PopupAlert(AlertType.WarDeclaration,civInfo.civName))
/// AI won't propose peace for 10 turns /// AI won't propose peace for 10 turns
flagsCountdown[DiplomacyFlags.DeclinedPeace]=10 flagsCountdown[DiplomacyFlags.DeclinedPeace]=10
otherCiv.diplomacy[civInfo.civName]!!.flagsCountdown[DiplomacyFlags.DeclinedPeace]=10 otherCiv.getDiplomacyManager(civInfo).flagsCountdown[DiplomacyFlags.DeclinedPeace]=10
} }
//endregion //endregion
} }

View File

@ -82,7 +82,7 @@ class TradeEvaluation{
val civToDeclareWarOn = civInfo.gameInfo.getCivilization(nameOfCivToDeclareWarOn) val civToDeclareWarOn = civInfo.gameInfo.getCivilization(nameOfCivToDeclareWarOn)
val threatToThem = Automation().threatAssessment(civInfo,civToDeclareWarOn) val threatToThem = Automation().threatAssessment(civInfo,civToDeclareWarOn)
if(civInfo.diplomacy[nameOfCivToDeclareWarOn]!!.diplomaticStatus== DiplomaticStatus.War){ if(civInfo.getDiplomacyManager(civToDeclareWarOn).diplomaticStatus== DiplomaticStatus.War){
when (threatToThem) { when (threatToThem) {
ThreatLevel.VeryLow -> return 0 ThreatLevel.VeryLow -> return 0
ThreatLevel.Low -> 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 val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food
return sumOfStats.toInt() * 100 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 val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food
return sumOfStats.toInt() * 100 return sumOfStats.toInt() * 100
} }
TradeType.Agreement -> {
if(offer.name=="Open Borders") return 100
throw Exception("Invalid agreement type!")
}
} }
} }

View File

@ -1,7 +1,6 @@
package com.unciv.logic.trade package com.unciv.logic.trade
import com.unciv.logic.civilization.CivilizationInfo 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.DiplomaticStatus
import com.unciv.models.gamebasics.tile.ResourceType import com.unciv.models.gamebasics.tile.ResourceType
import com.unciv.models.gamebasics.tr import com.unciv.models.gamebasics.tr
@ -17,6 +16,8 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
val offers = TradeOffersList() val offers = TradeOffersList()
if(civInfo.isAtWarWith(otherCivilization)) if(civInfo.isAtWarWith(otherCivilization))
offers.add(TradeOffer("Peace Treaty", TradeType.Treaty, 20)) 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 }) { for(entry in civInfo.getCivResources().filterNot { it.key.resourceType == ResourceType.Bonus }) {
val resourceTradeType = if(entry.key.resourceType== ResourceType.Luxury) TradeType.Luxury_Resource val resourceTradeType = if(entry.key.resourceType== ResourceType.Luxury) TradeType.Luxury_Resource
else TradeType.Strategic_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() }) for(city in civInfo.cities.filterNot { it.isCapital() })
offers.add(TradeOffer(city.name, TradeType.City, 0)) 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() } .filter { it != otherCivilization && !it.isBarbarianCivilization() && !it.isDefeated() }
val civsWeKnowAndTheyDont = otherCivsWeKnow val civsWeKnowAndTheyDont = otherCivsWeKnow
.filter { !otherCivilization.diplomacy.containsKey(it.civName) && !it.isDefeated() } .filter { !otherCivilization.diplomacy.containsKey(it.civName) && !it.isDefeated() }
@ -43,7 +44,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
val civsWeBothKnow = otherCivsWeKnow val civsWeBothKnow = otherCivsWeKnow
.filter { otherCivilization.diplomacy.containsKey(it.civName) } .filter { otherCivilization.diplomacy.containsKey(it.civName) }
val civsWeArentAtWarWith = civsWeBothKnow val civsWeArentAtWarWith = civsWeBothKnow
.filter { civInfo.diplomacy[it.civName]!!.diplomaticStatus== DiplomaticStatus.Peace } .filter { civInfo.getDiplomacyManager(it).diplomaticStatus== DiplomaticStatus.Peace }
for(thirdCiv in civsWeArentAtWarWith){ for(thirdCiv in civsWeArentAtWarWith){
offers.add(TradeOffer("Declare war on "+thirdCiv.civName,TradeType.WarDeclaration,0)) 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() { fun acceptTrade() {
ourCivilization.diplomacy[otherCivilization.civName]!!.trades.add(currentTrade) ourCivilization.getDiplomacyManager(otherCivilization).trades.add(currentTrade)
otherCivilization.diplomacy[ourCivilization.civName]!!.trades.add(currentTrade.reverse()) otherCivilization.getDiplomacyManager(ourCivilization).trades.add(currentTrade.reverse())
// instant transfers // instant transfers
fun transferTrade(to: CivilizationInfo, from: CivilizationInfo, trade: Trade) { 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.type== TradeType.Treaty){
if(offer.name=="Peace 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 }) for(unit in to.getCivUnits().filter { it.getTile().getOwner()==from })
unit.movementAlgs().teleportToClosestMoveableTile() unit.movementAlgs().teleportToClosestMoveableTile()
} }

View File

@ -3,7 +3,10 @@ package com.unciv.logic.trade
enum class TradeType{ enum class TradeType{
Gold, Gold,
Gold_Per_Turn, Gold_Per_Turn,
/** Treaties are shared by both sides - like peace treaty and defensive pact */
Treaty, Treaty,
/** Agreements are one-sided, like open borders */
Agreement,
Luxury_Resource, Luxury_Resource,
Strategic_Resource, Strategic_Resource,
Technology, Technology,

View File

@ -79,7 +79,7 @@ class DiplomacyScreen:CameraStageBaseScreen() {
diplomacyTable.add(tradeButton).row() diplomacyTable.add(tradeButton).row()
val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization() val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization()
val civDiplomacy = currentPlayerCiv.diplomacy[civ.civName]!! val civDiplomacy = currentPlayerCiv.getDiplomacyManager(civ)
if (!currentPlayerCiv.isAtWarWith(civ)) { if (!currentPlayerCiv.isAtWarWith(civ)) {
val declareWarButton = TextButton("Declare war".tr(), skin) val declareWarButton = TextButton("Declare war".tr(), skin)

View File

@ -31,6 +31,7 @@ class OffersListScroll(val onOfferClicked: (TradeOffer) -> Unit) : ScrollPane(nu
Technology -> "Technologies" Technology -> "Technologies"
WarDeclaration -> "Declarations of war" WarDeclaration -> "Declarations of war"
City -> "Cities" City -> "Cities"
Agreement -> "Agreements"
} }
val offersOfType = offersToDisplay.filter { it.type == offertype } val offersOfType = offersToDisplay.filter { it.type == offertype }
if (labelName!="" && offersOfType.any()) { if (labelName!="" && offersOfType.any()) {

View File

@ -59,10 +59,10 @@ class TradePopup(worldScreen: WorldScreen): PopupTable(worldScreen){
currentPlayerCiv.tradeRequests.remove(tradeRequest) currentPlayerCiv.tradeRequests.remove(tradeRequest)
if(trade.ourOffers.all { it.type==TradeType.Luxury_Resource } && trade.theirOffers.all { it.type==TradeType.Luxury_Resource }) 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" }) 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() remove()
worldScreen.shouldUpdate=true worldScreen.shouldUpdate=true

View File

@ -178,7 +178,7 @@ class WorldScreen : CameraStageBaseScreen() {
private fun updateDiplomacyButton(civInfo: CivilizationInfo) { private fun updateDiplomacyButton(civInfo: CivilizationInfo) {
diplomacyButtonWrapper.clear() diplomacyButtonWrapper.clear()
if(civInfo.diplomacy.values.map { it.otherCiv() } if(civInfo.getKnownCivs()
.filterNot { it.isDefeated() || it.isPlayerCivilization() || it.isBarbarianCivilization() } .filterNot { it.isDefeated() || it.isPlayerCivilization() || it.isBarbarianCivilization() }
.any()) { .any()) {
displayTutorials("OtherCivEncountered") displayTutorials("OtherCivEncountered")