Research agreements with gold offer (#9933)

* Research agreements can now be offered when both civilizations combined have enough gold.

* Moved a OfferColumnsTable constructor parameter.

* Fixed the ordering of an argument in TradeTable.

* Added an extra comment to OffersListScroll.

* Refactored research pact trade balancing when offered.

* Research agreement cost now uses the highest era from both participants.
This commit is contained in:
Oskar Niesen
2023-08-24 02:10:12 -05:00
committed by GitHub
parent 12e3cfc5b3
commit 592304ba2f
6 changed files with 57 additions and 22 deletions

View File

@ -796,7 +796,7 @@ object NextTurnAutomation {
// Default setting is 5, this will be changed according to different civ. // Default setting is 5, this will be changed according to different civ.
if ((1..10).random() > 5) continue if ((1..10).random() > 5) continue
val tradeLogic = TradeLogic(civInfo, otherCiv) val tradeLogic = TradeLogic(civInfo, otherCiv)
val cost = civInfo.diplomacyFunctions.getResearchAgreementCost() val cost = civInfo.diplomacyFunctions.getResearchAgreementCost(otherCiv)
tradeLogic.currentTrade.ourOffers.add(TradeOffer(Constants.researchAgreement, TradeType.Treaty, cost)) tradeLogic.currentTrade.ourOffers.add(TradeOffer(Constants.researchAgreement, TradeType.Treaty, cost))
tradeLogic.currentTrade.theirOffers.add(TradeOffer(Constants.researchAgreement, TradeType.Treaty, cost)) tradeLogic.currentTrade.theirOffers.add(TradeOffer(Constants.researchAgreement, TradeType.Treaty, cost))

View File

@ -11,6 +11,7 @@ import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import kotlin.math.max
class DiplomacyFunctions(val civInfo: Civilization){ class DiplomacyFunctions(val civInfo: Civilization){
@ -93,22 +94,26 @@ class DiplomacyFunctions(val civInfo: Civilization){
.none { civInfo.tech.canBeResearched(it.name) && !civInfo.tech.isResearched(it.name) }) return false .none { civInfo.tech.canBeResearched(it.name) && !civInfo.tech.isResearched(it.name) }) return false
return true return true
} }
fun canSignResearchAgreementsWith(otherCiv: Civilization): Boolean { fun canSignResearchAgreementNoCostWith (otherCiv: Civilization): Boolean {
val diplomacyManager = civInfo.getDiplomacyManager(otherCiv) val diplomacyManager = civInfo.getDiplomacyManager(otherCiv)
val cost = getResearchAgreementCost()
return canSignResearchAgreement() && otherCiv.diplomacyFunctions.canSignResearchAgreement() return canSignResearchAgreement() && otherCiv.diplomacyFunctions.canSignResearchAgreement()
&& diplomacyManager.hasFlag(DiplomacyFlags.DeclarationOfFriendship) && diplomacyManager.hasFlag(DiplomacyFlags.DeclarationOfFriendship)
&& !diplomacyManager.hasFlag(DiplomacyFlags.ResearchAgreement) && !diplomacyManager.hasFlag(DiplomacyFlags.ResearchAgreement)
&& !diplomacyManager.otherCivDiplomacy().hasFlag(DiplomacyFlags.ResearchAgreement) && !diplomacyManager.otherCivDiplomacy().hasFlag(DiplomacyFlags.ResearchAgreement)
&& civInfo.gold >= cost && otherCiv.gold >= cost
} }
fun getResearchAgreementCost(): Int { fun canSignResearchAgreementsWith(otherCiv: Civilization): Boolean {
val cost = getResearchAgreementCost(otherCiv)
return canSignResearchAgreementNoCostWith(otherCiv)
&& civInfo.gold >= cost && otherCiv.gold >= cost
}
fun getResearchAgreementCost(otherCiv: Civilization): Int {
// https://forums.civfanatics.com/resources/research-agreements-bnw.25568/ // https://forums.civfanatics.com/resources/research-agreements-bnw.25568/
return ( return ( max(civInfo.getEra().researchAgreementCost, otherCiv.getEra().researchAgreementCost)
civInfo.getEra().researchAgreementCost * civInfo.gameInfo.speed.goldCostModifier * civInfo.gameInfo.speed.goldCostModifier
).toInt() ).toInt()
} }
fun canSignDefensivePact(): Boolean { fun canSignDefensivePact(): Boolean {

View File

@ -32,8 +32,8 @@ class TradeLogic(val ourCivilization:Civilization, val otherCivilization: Civili
offers.add(TradeOffer(Constants.openBorders, TradeType.Agreement)) offers.add(TradeOffer(Constants.openBorders, TradeType.Agreement))
} }
if (civInfo.diplomacyFunctions.canSignResearchAgreementsWith(otherCivilization)) if (civInfo.diplomacyFunctions.canSignResearchAgreementNoCostWith(otherCivilization))
offers.add(TradeOffer(Constants.researchAgreement, TradeType.Treaty, civInfo.diplomacyFunctions.getResearchAgreementCost())) offers.add(TradeOffer(Constants.researchAgreement, TradeType.Treaty, civInfo.diplomacyFunctions.getResearchAgreementCost(otherCivilization)))
if (civInfo.diplomacyFunctions.canSignDefensivePactWith(otherCivilization)) if (civInfo.diplomacyFunctions.canSignDefensivePactWith(otherCivilization))
offers.add(TradeOffer(Constants.defensivePact, TradeType.Treaty)) offers.add(TradeOffer(Constants.defensivePact, TradeType.Treaty))

View File

@ -18,10 +18,12 @@ import com.unciv.ui.screens.basescreen.BaseScreen
class OfferColumnsTable( class OfferColumnsTable(
private val tradeLogic: TradeLogic, private val tradeLogic: TradeLogic,
private val screen: DiplomacyScreen, private val screen: DiplomacyScreen,
private val ourCiv: Civilization,
private val theirCiv: Civilization,
private val onChange: () -> Unit private val onChange: () -> Unit
): Table(BaseScreen.skin) { ): Table(BaseScreen.skin) {
private fun addOffer(offer: TradeOffer, offerList: TradeOffersList, correspondingOfferList: TradeOffersList) { fun addOffer(offer: TradeOffer, offerList: TradeOffersList, correspondingOfferList: TradeOffersList) {
offerList.add(offer.copy()) offerList.add(offer.copy())
if (offer.type == TradeType.Treaty) correspondingOfferList.add(offer.copy()) if (offer.type == TradeType.Treaty) correspondingOfferList.add(offer.copy())
onChange() onChange()
@ -104,10 +106,10 @@ class OfferColumnsTable(
.removeAll(Constants.tradable) .removeAll(Constants.tradable)
val theirUntradables = tradeLogic.otherCivilization.getCivResourcesWithOriginsForTrade() val theirUntradables = tradeLogic.otherCivilization.getCivResourcesWithOriginsForTrade()
.removeAll(Constants.tradable) .removeAll(Constants.tradable)
ourAvailableOffersTable.update(ourFilteredOffers, tradeLogic.theirAvailableOffers, ourUntradables) ourAvailableOffersTable.update(ourFilteredOffers, tradeLogic.theirAvailableOffers, ourUntradables, ourCiv, theirCiv)
ourOffersTable.update(tradeLogic.currentTrade.ourOffers, tradeLogic.theirAvailableOffers) ourOffersTable.update(tradeLogic.currentTrade.ourOffers, tradeLogic.theirAvailableOffers, ourCiv = ourCiv, theirCiv = theirCiv)
theirOffersTable.update(tradeLogic.currentTrade.theirOffers, tradeLogic.ourAvailableOffers) theirOffersTable.update(tradeLogic.currentTrade.theirOffers, tradeLogic.ourAvailableOffers, ourCiv = ourCiv, theirCiv = theirCiv)
theirAvailableOffersTable.update(theirFilteredOffers, tradeLogic.ourAvailableOffers, theirUntradables) theirAvailableOffersTable.update(theirFilteredOffers, tradeLogic.ourAvailableOffers, theirUntradables, ourCiv, theirCiv)
} }
private fun openGoldSelectionPopup(offer: TradeOffer, ourOffers: TradeOffersList, maxGold: Int) { private fun openGoldSelectionPopup(offer: TradeOffer, ourOffers: TradeOffersList, maxGold: Int) {

View File

@ -5,6 +5,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.civilization.Civilization
import com.unciv.logic.trade.TradeOffer import com.unciv.logic.trade.TradeOffer
import com.unciv.logic.trade.TradeOffersList import com.unciv.logic.trade.TradeOffersList
import com.unciv.logic.trade.TradeType import com.unciv.logic.trade.TradeType
@ -52,7 +53,9 @@ class OffersListScroll(
fun update( fun update(
offersToDisplay: TradeOffersList, offersToDisplay: TradeOffersList,
otherOffers: TradeOffersList, otherOffers: TradeOffersList,
untradableOffers: ResourceSupplyList = ResourceSupplyList.emptyList untradableOffers: ResourceSupplyList = ResourceSupplyList.emptyList,
ourCiv: Civilization,
theirCiv: Civilization
) { ) {
table.clear() table.clear()
expanderTabs.clear() expanderTabs.clear()
@ -109,7 +112,10 @@ class OffersListScroll(
else -> 1 else -> 1
} }
if (offer.isTradable() && offer.name != Constants.peaceTreaty) { // can't disable peace treaty! if (offer.isTradable() && offer.name != Constants.peaceTreaty // can't disable peace treaty!
&& (offer.name != Constants.researchAgreement // If we have a research agreement make sure the total gold of both Civs is higher than the total cost
// If both civs combined can pay for the research agreement, don't disable it. One can offer the other it's gold.
|| (ourCiv.gold + theirCiv.gold > ourCiv.diplomacyFunctions.getResearchAgreementCost(theirCiv) * 2))) {
// highlight unique suggestions // highlight unique suggestions
if (offerType in listOf(Luxury_Resource, Strategic_Resource) if (offerType in listOf(Luxury_Resource, Strategic_Resource)

View File

@ -1,9 +1,11 @@
package com.unciv.ui.screens.diplomacyscreen package com.unciv.ui.screens.diplomacyscreen
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.Constants
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.trade.TradeLogic import com.unciv.logic.trade.TradeLogic
import com.unciv.logic.trade.TradeRequest import com.unciv.logic.trade.TradeRequest
import com.unciv.logic.trade.TradeType
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.components.extensions.isEnabled import com.unciv.ui.components.extensions.isEnabled
@ -16,7 +18,7 @@ class TradeTable(
): Table(BaseScreen.skin) { ): Table(BaseScreen.skin) {
private val currentPlayerCiv = otherCivilization.gameInfo.getCurrentPlayerCivilization() private val currentPlayerCiv = otherCivilization.gameInfo.getCurrentPlayerCivilization()
internal val tradeLogic = TradeLogic(currentPlayerCiv, otherCivilization) internal val tradeLogic = TradeLogic(currentPlayerCiv, otherCivilization)
internal val offerColumnsTable = OfferColumnsTable(tradeLogic, diplomacyScreen) { onChange() } internal val offerColumnsTable = OfferColumnsTable(tradeLogic, diplomacyScreen , currentPlayerCiv, otherCivilization) { onChange() }
// This is so that after a trade has been traded, we can switch out the offersToDisplay to start anew - this is the easiest way // This is so that after a trade has been traded, we can switch out the offersToDisplay to start anew - this is the easiest way
private val offerColumnsTableWrapper = Table() private val offerColumnsTableWrapper = Table()
private val offerButton = "Offer trade".toTextButton() private val offerButton = "Offer trade".toTextButton()
@ -49,6 +51,26 @@ class TradeTable(
retractOffer() retractOffer()
return@onClick return@onClick
} }
// If there is a research agreement trade, make sure both civilizations should be able to pay for it.
// If not lets add an extra gold offer to satisfy this.
// There must be enough gold to add to the offer to satisfy this, otherwise the research agreement button would be disabled
if (tradeLogic.currentTrade.ourOffers.any { it.name == Constants.researchAgreement}) {
val researchCost = currentPlayerCiv.diplomacyFunctions.getResearchAgreementCost(otherCivilization)
val currentPlayerOfferedGold = tradeLogic.currentTrade.ourOffers.firstOrNull { it.type == TradeType.Gold }?.amount ?: 0
val otherCivOfferedGold = tradeLogic.currentTrade.theirOffers.firstOrNull { it.type == TradeType.Gold }?.amount ?: 0
val newCurrentPlayerGold = currentPlayerCiv.gold + otherCivOfferedGold - researchCost
val newOtherCivGold = otherCivilization.gold + currentPlayerOfferedGold - researchCost
// Check if we require more gold from them
if (newCurrentPlayerGold < 0) {
offerColumnsTable.addOffer( tradeLogic.theirAvailableOffers.first { it.type == TradeType.Gold }
.copy(amount = -newCurrentPlayerGold), tradeLogic.currentTrade.theirOffers, tradeLogic.currentTrade.ourOffers)
}
// Check if they require more gold from us
if (newOtherCivGold < 0) {
offerColumnsTable.addOffer( tradeLogic.ourAvailableOffers.first { it.type == TradeType.Gold }
.copy(amount = -newOtherCivGold), tradeLogic.currentTrade.ourOffers, tradeLogic.currentTrade.theirOffers)
}
}
otherCivilization.tradeRequests.add(TradeRequest(currentPlayerCiv.civName, tradeLogic.currentTrade.reverse())) otherCivilization.tradeRequests.add(TradeRequest(currentPlayerCiv.civName, tradeLogic.currentTrade.reverse()))
currentPlayerCiv.cache.updateCivResources() currentPlayerCiv.cache.updateCivResources()