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!
This commit is contained in:
Yair Morgenstern 2019-07-29 16:31:21 +03:00
parent 2d24e62b0b
commit 6dc471850e
9 changed files with 88 additions and 68 deletions

View File

@ -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]"

View File

@ -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é"
},
}

View File

@ -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

View File

@ -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

View File

@ -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(){

View File

@ -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 {

View File

@ -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()
}
}

View File

@ -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()){

View File

@ -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)