From 6dc471850eafe13dfcfa537c628a8e72f2b469a8 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Mon, 29 Jul 2019 16:31:21 +0300 Subject: [PATCH] Trades are no longer instantaneous, but consist of sending a trade request on the requestor's turn and accepting/denying it on the reciever's turn. This allows us to FINALLY enable trade between human players, enables PvP in hotseat multiplayer, and paves the way to real multiplayer! --- .../Translations/Diplomacy,Trade,Nations.json | 9 +-- .../jsons/Translations/Notifications.json | 36 +++++++--- android/build.gradle | 4 +- .../logic/automation/NextTurnAutomation.kt | 18 +++++ .../diplomacy/DiplomacyManager.kt | 1 + .../src/com/unciv/ui/trade/DiplomacyScreen.kt | 14 ++-- core/src/com/unciv/ui/trade/TradeTable.kt | 68 +++++++------------ .../com/unciv/ui/worldscreen/TradePopup.kt | 4 ++ .../com/unciv/ui/worldscreen/WorldScreen.kt | 2 +- 9 files changed, 88 insertions(+), 68 deletions(-) diff --git a/android/assets/jsons/Translations/Diplomacy,Trade,Nations.json b/android/assets/jsons/Translations/Diplomacy,Trade,Nations.json index c2e42c8cd0..8be7f0bed7 100644 --- a/android/assets/jsons/Translations/Diplomacy,Trade,Nations.json +++ b/android/assets/jsons/Translations/Diplomacy,Trade,Nations.json @@ -63,14 +63,7 @@ Portuguese:"[civName] declarou guerra contra nós!" German:"[civName] hat uns den Krieg erklärt!" } - - "[tradeOffer] from [otherCivName] has ended":{ - Italian:"Accordo di [tradeOffer] da [otherCivName] terminato" - Simplified_Chinese:"与[civName]的[tradeOffer]协议已经结束" - French:"[tradeOffer] de [otherCivName] a pris fin" - Portuguese:"[tradeOffer] de [otherCivName] terminou" - } - + "[leaderName] of [nation]":{ // e.g. Ramasses of Egypt, Napoleon of France Italian:"[leaderName] del popolo [nation]" German:"[leaderName] von [nation]" diff --git a/android/assets/jsons/Translations/Notifications.json b/android/assets/jsons/Translations/Notifications.json index 5044c21e31..5668720061 100644 --- a/android/assets/jsons/Translations/Notifications.json +++ b/android/assets/jsons/Translations/Notifications.json @@ -157,15 +157,6 @@ French:"[cityName] ne peut plus travailler sur [construction]" } - "One of our trades with [nation] has ended": { - Italian:"Un nostro accordo con [nation] è terminato." - French:"Un de nos échanges avec [nation] a pris fin" - }, - - "One of our trades with [nation] has been cut short": { - Italian:"Un nostro accordo con [nation] è stato interrotto." - French:"L'un de nos accord commercial avec [nation] s'est arrêté" - }, "[cityname] has expanded its borders!":{ Italian:"[cityname] ha espanso i suoi confini!" @@ -499,4 +490,31 @@ Italian:"[civName] ci ha dato [untiName] come regalo vicino a [cityName]!" French:"[civName] nous offre un(e) [untiName] prêt de [cityName]!" } + + // Trade + "[civName] has accepted your trade request":{ + } + + "[civName] has denied your trade request":{ + } + + + "[tradeOffer] from [otherCivName] has ended":{ + Italian:"Accordo di [tradeOffer] da [otherCivName] terminato" + Simplified_Chinese:"与[civName]的[tradeOffer]协议已经结束" + French:"[tradeOffer] de [otherCivName] a pris fin" + Portuguese:"[tradeOffer] de [otherCivName] terminou" + } + + + "One of our trades with [nation] has ended": { + Italian:"Un nostro accordo con [nation] è terminato." + French:"Un de nos échanges avec [nation] a pris fin" + }, + + "One of our trades with [nation] has been cut short": { + Italian:"Un nostro accordo con [nation] è stato interrotto." + French:"L'un de nos accord commercial avec [nation] s'est arrêté" + }, + } diff --git a/android/build.gradle b/android/build.gradle index 33719dc8e1..9011b690f9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,8 +21,8 @@ android { applicationId "com.unciv.app" minSdkVersion 14 targetSdkVersion 28 - versionCode 280 - versionName "2.18.6-patch3" + versionCode 281 + versionName "2.19.0" } // Had to add this crap for Travis to build, it wanted to sign the app diff --git a/core/src/com/unciv/logic/automation/NextTurnAutomation.kt b/core/src/com/unciv/logic/automation/NextTurnAutomation.kt index b608dc1768..6b343e474a 100644 --- a/core/src/com/unciv/logic/automation/NextTurnAutomation.kt +++ b/core/src/com/unciv/logic/automation/NextTurnAutomation.kt @@ -1,5 +1,6 @@ package com.unciv.logic.automation +import com.badlogic.gdx.graphics.Color import com.unciv.Constants import com.unciv.logic.city.CityInfo import com.unciv.logic.civilization.* @@ -20,6 +21,7 @@ class NextTurnAutomation{ /** Top-level AI turn tasklist */ fun automateCivMoves(civInfo: CivilizationInfo) { respondToDemands(civInfo) + respondToTradeRequests(civInfo) if(civInfo.isMajorCiv()) { offerPeaceTreaty(civInfo) @@ -40,6 +42,22 @@ class NextTurnAutomation{ civInfo.popupAlerts.clear() // AIs don't care about popups. } + private fun respondToTradeRequests(civInfo: CivilizationInfo) { + for(tradeRequest in civInfo.tradeRequests){ + val otherCiv = civInfo.gameInfo.getCivilization(tradeRequest.requestingCiv) + val tradeLogic = TradeLogic(civInfo, otherCiv) + tradeLogic.currentTrade.set(tradeRequest.trade) + if(TradeEvaluation().isTradeAcceptable(tradeLogic.currentTrade,civInfo,otherCiv)){ + tradeLogic.acceptTrade() + otherCiv.addNotification("[${civInfo.civName}] has accepted your trade request", Color.GOLD) + } + else{ + otherCiv.addNotification("[${civInfo.civName}] has denied your trade request", Color.GOLD) // todo translation + } + } + civInfo.tradeRequests.clear() + } + private fun respondToDemands(civInfo: CivilizationInfo) { for(popupAlert in civInfo.popupAlerts){ if(popupAlert.type==AlertType.CitySettledNearOtherCiv){ // we're called upon to make a decision diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt index dd2593b300..8a9db57b52 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt @@ -415,6 +415,7 @@ class DiplomacyManager() { RelationshipLevel.Ally -> addModifier(DiplomaticModifiers.DenouncedOurAllies,-15f) } } + otherCiv().addNotification("[${civInfo.civName}] has denounced us!", Color.RED) // todo translation } fun agreeNotToSettleNear(){ diff --git a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt index 3651071af2..fda04ebcca 100644 --- a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt +++ b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt @@ -53,7 +53,7 @@ class DiplomacyScreen:CameraStageBaseScreen() { leftSideTable.clear() val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization() for (civ in UnCivGame.Current.gameInfo.civilizations - .filterNot { it.isDefeated() || it.isPlayerCivilization() || it.isBarbarianCivilization() }) { + .filterNot { it.isDefeated() || it == currentPlayerCiv || it.isBarbarianCivilization() }) { if (!currentPlayerCiv.knows(civ)) continue val civIndicator = ImageGetter.getNationIndicator(civ.getNation(),100f) @@ -226,9 +226,15 @@ class DiplomacyScreen:CameraStageBaseScreen() { } diplomacyTable.add(demandsButton).row() - if(!otherCiv.isPlayerCivilization()) + if(!otherCiv.isPlayerCivilization()) { // human players make their own choices diplomacyTable.add(getRelationshipTable(otherCivDiplomacyManager)).row() + diplomacyTable.add(getDiplomacyModifiersTable(otherCivDiplomacyManager)).row() + } + return diplomacyTable + } + + private fun getDiplomacyModifiersTable(otherCivDiplomacyManager: DiplomacyManager): Table { val diplomacyModifiersTable = Table() for (modifier in otherCivDiplomacyManager.diplomaticModifiers) { var text = when (valueOf(modifier.key)) { @@ -256,9 +262,7 @@ class DiplomacyScreen:CameraStageBaseScreen() { val color = if (modifier.value < 0) Color.RED else Color.GREEN diplomacyModifiersTable.add(text.toLabel().setFontColor(color)).row() } - diplomacyTable.add(diplomacyModifiersTable).row() - - return diplomacyTable + return diplomacyModifiersTable } private fun getDemandsTable(currentPlayerCiv: CivilizationInfo, otherCiv: CivilizationInfo): Table { diff --git a/core/src/com/unciv/ui/trade/TradeTable.kt b/core/src/com/unciv/ui/trade/TradeTable.kt index 6c520440ed..8b3c3ea1d9 100644 --- a/core/src/com/unciv/ui/trade/TradeTable.kt +++ b/core/src/com/unciv/ui/trade/TradeTable.kt @@ -1,16 +1,15 @@ package com.unciv.ui.trade import com.badlogic.gdx.scenes.scene2d.Stage -import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.TextButton -import com.unciv.Constants import com.unciv.logic.civilization.CivilizationInfo -import com.unciv.logic.civilization.diplomacy.RelationshipLevel -import com.unciv.logic.trade.TradeEvaluation import com.unciv.logic.trade.TradeLogic +import com.unciv.logic.trade.TradeRequest import com.unciv.models.gamebasics.tr import com.unciv.ui.utils.CameraStageBaseScreen +import com.unciv.ui.utils.disable +import com.unciv.ui.utils.enable import com.unciv.ui.utils.onClick class TradeTable(val otherCivilization: CivilizationInfo, stage: Stage, onTradeComplete: () -> Unit): Table(CameraStageBaseScreen.skin){ @@ -18,57 +17,38 @@ class TradeTable(val otherCivilization: CivilizationInfo, stage: Stage, onTradeC var tradeLogic = TradeLogic(currentPlayerCiv,otherCivilization) var offerColumnsTable = OfferColumnsTable(tradeLogic, stage) { onChange() } var offerColumnsTableWrapper = Table() // This is so that after a trade has been traded, we can switch out the offersToDisplay to start anew - this is the easiest way - val tradeText = Label("", CameraStageBaseScreen.skin) val offerButton = TextButton("Offer trade".tr(), CameraStageBaseScreen.skin) - fun letsHearIt(){ - val relationshipLevel = otherCivilization.getDiplomacyManager(currentPlayerCiv).relationshipLevel() - if(relationshipLevel <= RelationshipLevel.Enemy) - tradeText.setText(otherCivilization.getTranslatedNation().hateLetsHearIt.random().tr()) - else tradeText.setText(otherCivilization.getTranslatedNation().neutralLetsHearIt.random().tr()) + fun isTradeOffered() = otherCivilization.tradeRequests.any{it.requestingCiv==currentPlayerCiv.civName} + + fun retractOffer(){ + otherCivilization.tradeRequests.removeAll { it.requestingCiv==currentPlayerCiv.civName } + offerButton.setText("Offer trade".tr()) } init{ - letsHearIt() offerColumnsTableWrapper.add(offerColumnsTable) add(offerColumnsTableWrapper).row() val lowerTable = Table().apply { defaults().pad(10f) } - lowerTable.add(tradeText).colspan(2).row() + val existingOffer = otherCivilization.tradeRequests.firstOrNull{it.requestingCiv==currentPlayerCiv.civName} + if(existingOffer!=null){ + tradeLogic.currentTrade.set(existingOffer.trade.reverse()) + offerColumnsTable.update() + } + + if(isTradeOffered()) offerButton.setText("Retract offer".tr()) // todo translation + else offerButton.setText("Offer trade".tr()) offerButton.onClick { - val relationshipLevel = otherCivilization.getDiplomacyManager(currentPlayerCiv).relationshipLevel() - if(offerButton.text.toString() == "Offer trade".tr()) { - if(tradeLogic.currentTrade.theirOffers.size==0 && tradeLogic.currentTrade.ourOffers.size==0){ - letsHearIt() - } - else if (TradeEvaluation().isTradeAcceptable(tradeLogic.currentTrade.reverse(),otherCivilization,currentPlayerCiv)){ - if(relationshipLevel<=RelationshipLevel.Enemy) - tradeText.setText(otherCivilization.getTranslatedNation().hateYes.random().tr()) - else tradeText.setText(otherCivilization.getTranslatedNation().neutralYes.random().tr()) - offerButton.setText("Accept".tr()) - } - else{ - if(relationshipLevel<=RelationshipLevel.Enemy) - tradeText.setText(otherCivilization.getTranslatedNation().hateNo.random().tr()) - tradeText.setText(otherCivilization.getTranslatedNation().neutralNo.random().tr()) - } + if(isTradeOffered()) { + retractOffer() + return@onClick } - else if(offerButton.text.toString() == "Accept".tr()){ - tradeLogic.acceptTrade() - if(tradeLogic.currentTrade.ourOffers.any { it.name== Constants.peaceTreaty }) - tradeText.setText(otherCivilization.getTranslatedNation().afterPeace) - else tradeText.setText("Pleasure doing business with you!".tr()) - tradeLogic = TradeLogic(currentPlayerCiv,otherCivilization) - offerColumnsTable = OfferColumnsTable(tradeLogic, stage) { onChange() } - offerColumnsTableWrapper.clear() - offerColumnsTableWrapper.add(offerColumnsTable) - onTradeComplete() - - offerButton.setText("Offer trade".tr()) - } + otherCivilization.tradeRequests.add(TradeRequest(currentPlayerCiv.civName,tradeLogic.currentTrade.reverse())) + offerButton.setText("Retract offer".tr()) } lowerTable.add(offerButton) @@ -81,8 +61,10 @@ class TradeTable(val otherCivilization: CivilizationInfo, stage: Stage, onTradeC private fun onChange(){ offerColumnsTable.update() - offerButton.setText("Offer trade".tr()) - letsHearIt() + retractOffer() + if(tradeLogic.currentTrade.theirOffers.size==0 && tradeLogic.currentTrade.ourOffers.size==0) + offerButton.disable() + else offerButton.enable() } } \ No newline at end of file diff --git a/core/src/com/unciv/ui/worldscreen/TradePopup.kt b/core/src/com/unciv/ui/worldscreen/TradePopup.kt index f8781ac196..67bddcc8a9 100644 --- a/core/src/com/unciv/ui/worldscreen/TradePopup.kt +++ b/core/src/com/unciv/ui/worldscreen/TradePopup.kt @@ -1,5 +1,6 @@ package com.unciv.ui.worldscreen +import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.ui.Table import com.unciv.Constants import com.unciv.logic.civilization.diplomacy.DiplomacyFlags @@ -55,6 +56,7 @@ class TradePopup(worldScreen: WorldScreen): PopupTable(worldScreen){ } open() } + requestingCiv.addNotification("[${currentPlayerCiv.civName}] has accepted your trade request", Color.GOLD) } addButton("Not this time.".tr()){ currentPlayerCiv.tradeRequests.remove(tradeRequest) @@ -67,6 +69,8 @@ class TradePopup(worldScreen: WorldScreen): PopupTable(worldScreen){ diplomacyManager.setFlag(DiplomacyFlags.DeclinedPeace,5) remove() + requestingCiv.addNotification("[${currentPlayerCiv.civName}] has denied your trade request", Color.GOLD) + worldScreen.shouldUpdate=true } addButton("How about something else...".tr()){ diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index acbff4d6a2..d03c9f6da3 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -186,7 +186,7 @@ class WorldScreen : CameraStageBaseScreen() { private fun updateDiplomacyButton(civInfo: CivilizationInfo) { diplomacyButtonWrapper.clear() if(civInfo.getKnownCivs() - .filterNot { it.isDefeated() || it.isPlayerCivilization() || it.isBarbarianCivilization() } + .filterNot { it.isDefeated() || it==currentPlayerCiv || it.isBarbarianCivilization() } .any()) { displayTutorials("OtherCivEncountered") val btn = TextButton("Diplomacy".tr(), skin)