mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-24 03:53:12 -04:00
Allow proposing peace between warring civs in trade window (#13595)
* intial peace proposals change * conditionally disable trades and trade evaluation * remove tooltips and trade validation * evaluate buying peace * avoid double signing and notification * update function comment * remove redundant code * cleanup * update reviewed comment * apply reviewed change * partially applied review * remove unneeded filter and disable trade for human players * fix disable trade if third civ is human player * simplified relationship comparison
This commit is contained in:
parent
d1cd968751
commit
b67c272f49
@ -329,8 +329,10 @@ Gold per turn =
|
||||
Cities =
|
||||
Technologies =
|
||||
Declarations of war =
|
||||
Peace Proposals =
|
||||
Introduction to [nation] =
|
||||
Declare war on [nation] =
|
||||
Make peace with [nation] =
|
||||
Luxury resources =
|
||||
Strategic resources =
|
||||
Stockpiled resources =
|
||||
|
@ -17,7 +17,6 @@ import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.MilestoneType
|
||||
import com.unciv.models.ruleset.Policy
|
||||
import com.unciv.models.ruleset.PolicyBranch
|
||||
import com.unciv.models.ruleset.Victory
|
||||
import com.unciv.models.ruleset.nation.PersonalityValue
|
||||
import com.unciv.models.ruleset.tech.Technology
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
|
@ -12,7 +12,6 @@ import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.components.extensions.toPercent
|
||||
import com.unciv.ui.screens.victoryscreen.RankingType
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.min
|
||||
@ -25,7 +24,7 @@ class TradeEvaluation {
|
||||
|
||||
// Edge case time! Guess what happens if you offer a peace agreement to the AI for all their cities except for the capital,
|
||||
// and then capture their capital THAT SAME TURN? It can agree, leading to the civilization getting instantly destroyed!
|
||||
// If a civ doen't has ever owned an original capital, which means it has not settle the first city yet,
|
||||
// If a civ has never owned an original capital, which means it has not settled the first city yet,
|
||||
// it shouldn't be forbidden to trade with other civs owing to cities.size == 0.
|
||||
if ((offerer.hasEverOwnedOriginalCapital && trade.ourOffers.count { it.type == TradeOfferType.City } == offerer.cities.size)
|
||||
|| (tradePartner.hasEverOwnedOriginalCapital && trade.theirOffers.count { it.type == TradeOfferType.City } == tradePartner.cities.size)) {
|
||||
@ -72,6 +71,7 @@ class TradeEvaluation {
|
||||
TradeOfferType.Technology -> true
|
||||
TradeOfferType.Introduction -> !tradePartner.knows(tradeOffer.name) // You can't introduce them to someone they already know!
|
||||
TradeOfferType.WarDeclaration -> offerer.getDiplomacyManager(tradeOffer.name)!!.canDeclareWar()
|
||||
TradeOfferType.PeaceProposal -> offerer.isAtWarWith(offerer.gameInfo.getCivilization(tradeOffer.name))
|
||||
TradeOfferType.City -> offerer.cities.any { it.id == tradeOffer.name }
|
||||
}
|
||||
}
|
||||
@ -207,6 +207,25 @@ class TradeEvaluation {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
TradeOfferType.PeaceProposal -> {
|
||||
val thirdCiv = civInfo.gameInfo.getCivilization(offer.name)
|
||||
|
||||
// We're buying peace if it's city state that is our ally
|
||||
if (thirdCiv.isCityState) {
|
||||
val allyCiv = thirdCiv.getAllyCiv()
|
||||
if (allyCiv != null && allyCiv == civInfo) {
|
||||
// TODO: More sophisticated formula needed, a reverse of [CityStateFunctions.influenceGainedByGift]
|
||||
val surplusInfluence = (thirdCiv.getDiplomacyManager(civInfo)!!.getInfluence() - 60).coerceAtLeast(0f)
|
||||
return (surplusInfluence * 15).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't like third civ why should we pay
|
||||
return if (civInfo.getDiplomacyManager(thirdCiv)!!.isRelationshipLevelLT(RelationshipLevel.Neutral)) {
|
||||
Int.MIN_VALUE // Maximum negative for deal to be rejected
|
||||
}
|
||||
else 0 // Accepts peace proposal with no value
|
||||
}
|
||||
TradeOfferType.City -> {
|
||||
val city = tradePartner.cities.firstOrNull { it.id == offer.name }
|
||||
?: throw Exception("Got an offer for city id "+offer.name+" which does't seem to exist for this civ!")
|
||||
@ -224,6 +243,51 @@ class TradeEvaluation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if peace proposal button in trade window should be enabled or disabled
|
||||
* Note that enabled peace proposal can still result in denied trade,
|
||||
* e.g. if no counter offer that is worth it can be made
|
||||
*
|
||||
* Certain peace proposal buttons in trade window are disabled when:
|
||||
* 1. Third civ is stronger, need to trade with them instead
|
||||
* 2. Third civ is city state allied to civ we or trade partner is at war with
|
||||
* 3. Third civ is human player, we don't know if they would agree to peace
|
||||
* 4. War count down hasn't expired yet, countdown depends on game speed
|
||||
*
|
||||
* @param thirdCiv Civilization for which peace proposal can be traded (mediated)
|
||||
* @param civInfo Civilization at war with thirdCiv which trades peace proposal
|
||||
*/
|
||||
fun isPeaceProposalEnabled(thirdCiv: Civilization, civInfo: Civilization): Boolean {
|
||||
val diploManager = civInfo.getDiplomacyManager(thirdCiv)!!
|
||||
val warCountDown = if (diploManager.hasFlag(DiplomacyFlags.DeclaredWar))
|
||||
diploManager.getFlag(DiplomacyFlags.DeclaredWar) else 0
|
||||
|
||||
// On standard speed 10 turns must pass before peace can be proposed
|
||||
if (warCountDown > 0) return false
|
||||
|
||||
// TODO: We don't know if other human player would agree to peace
|
||||
if (thirdCiv.isHuman()) return false
|
||||
|
||||
if (thirdCiv.isCityState) {
|
||||
val allyCiv = thirdCiv.getAllyCiv()
|
||||
if (allyCiv != null && civInfo.isAtWarWith(allyCiv)) {
|
||||
// City state is allied to civ with whom trade partner is at war with,
|
||||
// need to trade peace with that civ instead
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
val trade = Trade()
|
||||
val peaceOffer = TradeOffer(Constants.peaceTreaty, TradeOfferType.Treaty, duration = civInfo.gameInfo.speed.peaceDealDuration)
|
||||
trade.ourOffers.add(peaceOffer)
|
||||
trade.theirOffers.add(peaceOffer)
|
||||
|
||||
// If trade is acceptable to thirdCiv it's either loosing the war (result > 0) or it's equal to trade partner (result == 0),
|
||||
// so opinion of thirdCiv doesn't matter because it would agree to peace due to logic in evaluatePeaceCostForThem()
|
||||
// Otherwise we need to trade peace with thirdCiv, instead of trade partner who is loosing the war and would always agree to peace
|
||||
return TradeEvaluation().isTradeAcceptable(trade, thirdCiv, civInfo)
|
||||
}
|
||||
|
||||
private fun surroundedByOurCities(city: City, civInfo: Civilization): Int {
|
||||
val borderingCivs: Set<String> = getNeighbouringCivs(city)
|
||||
if (borderingCivs.contains(civInfo.civName))
|
||||
@ -253,6 +317,9 @@ class TradeEvaluation {
|
||||
return evaluateSellCost(offer, civInfo, tradePartner, trade)
|
||||
}
|
||||
|
||||
/**
|
||||
* How much our offer is worth to us in gold
|
||||
*/
|
||||
private fun evaluateSellCost(offer: TradeOffer, civInfo: Civilization, tradePartner: Civilization, trade: Trade): Int {
|
||||
when (offer.type) {
|
||||
TradeOfferType.Gold -> return offer.amount
|
||||
@ -320,14 +387,12 @@ class TradeEvaluation {
|
||||
}
|
||||
return totalCost
|
||||
}
|
||||
|
||||
TradeOfferType.Stockpiled_Resource -> {
|
||||
val resource = civInfo.gameInfo.ruleset.tileResources[offer.name] ?: return 0
|
||||
val lowestSellCost = resource.getMatchingUniques(UniqueType.AiWillSellAt, StateForConditionals(civInfo))
|
||||
.minOfOrNull { it.params[0].toInt() }
|
||||
return lowestSellCost ?: Int.MAX_VALUE
|
||||
}
|
||||
|
||||
TradeOfferType.Technology -> return sqrt(civInfo.gameInfo.ruleset.technologies[offer.name]!!.cost.toDouble()).toInt() * 20
|
||||
TradeOfferType.Introduction -> return introductionValue(civInfo.gameInfo.ruleset)
|
||||
TradeOfferType.WarDeclaration -> {
|
||||
@ -345,7 +410,11 @@ class TradeEvaluation {
|
||||
return (-25 * DeclareWarPlanEvaluator.evaluateDeclareWarPlan(civInfo, civToDeclareWarOn, null)).toInt().coerceAtLeast(0)
|
||||
}
|
||||
}
|
||||
|
||||
TradeOfferType.PeaceProposal -> {
|
||||
// We're evaluating peace cost for third civ to be paid by requesting civ (tradePartner) to us (civInfo)
|
||||
val thirdCiv = civInfo.gameInfo.getCivilization(offer.name)
|
||||
return evaluatePeaceCostForThem(civInfo, thirdCiv)
|
||||
}
|
||||
TradeOfferType.City -> {
|
||||
val city = civInfo.cities.firstOrNull { it.id == offer.name }
|
||||
?: throw Exception("Got an offer to sell city id " + offer.name + " which does't seem to exist for this civ!")
|
||||
|
@ -83,6 +83,18 @@ class TradeLogic(val ourCivilization: Civilization, val otherCivilization: Civil
|
||||
}
|
||||
}
|
||||
|
||||
if (!civInfo.isCityState && !otherCivilization.isCityState) {
|
||||
val thirdCivsAtWarTheyKnow = otherCivilization.getKnownCivs()
|
||||
.filter { it.isAtWarWith(civInfo) && !it.isDefeated()
|
||||
&& !it.gameInfo.ruleset.modOptions.hasUnique(UniqueType.DiplomaticRelationshipsCannotChange) }
|
||||
|
||||
for (thirdCiv in thirdCivsAtWarTheyKnow) {
|
||||
// Setting amount to 0 makes TradeOffer.isTradable() return false and also disables the button in trade window
|
||||
val amount = if (TradeEvaluation().isPeaceProposalEnabled(thirdCiv, civInfo)) 1 else 0
|
||||
offers.add(TradeOffer(thirdCiv.civName, TradeOfferType.PeaceProposal, amount, civInfo.gameInfo.speed))
|
||||
}
|
||||
}
|
||||
|
||||
return offers
|
||||
}
|
||||
|
||||
@ -156,6 +168,27 @@ class TradeLogic(val ourCivilization: Civilization, val otherCivilization: Civil
|
||||
|
||||
from.getDiplomacyManager(nameOfCivToDeclareWarOn)!!.declareWar(DeclareWarReason(warType, to))
|
||||
}
|
||||
TradeOfferType.PeaceProposal -> {
|
||||
// Convert PeaceProposal to peaceTreaty and apply to warring civs
|
||||
val trade = Trade()
|
||||
val peaceOffer = TradeOffer(Constants.peaceTreaty, TradeOfferType.Treaty, duration = offer.duration)
|
||||
trade.ourOffers.add(peaceOffer)
|
||||
trade.theirOffers.add(peaceOffer)
|
||||
|
||||
val thirdCiv = from.gameInfo.getCivilization(offer.name)
|
||||
val tradePartnerDiplo = from.getDiplomacyManager(thirdCiv)!!
|
||||
tradePartnerDiplo.apply {
|
||||
trades.add(trade)
|
||||
updateHasOpenBorders()
|
||||
}
|
||||
|
||||
thirdCiv.getDiplomacyManager(from)!!.apply {
|
||||
trades.add(trade)
|
||||
updateHasOpenBorders()
|
||||
}
|
||||
|
||||
tradePartnerDiplo.makePeace()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ data class TradeOffer(val name: String, val type: TradeOfferType, var amount: In
|
||||
) : this(name, type, amount, duration = -1) {
|
||||
duration = when {
|
||||
type.isImmediate -> -1 // -1 for offers that are immediate (e.g. gold transfer)
|
||||
name == Constants.peaceTreaty -> speed.peaceDealDuration
|
||||
name == Constants.peaceTreaty || type == TradeOfferType.PeaceProposal -> speed.peaceDealDuration
|
||||
else -> speed.dealDuration
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,7 @@ data class TradeOffer(val name: String, val type: TradeOfferType, var amount: In
|
||||
fun getOfferText(untradable: Int = 0): String {
|
||||
var offerText = when(type) {
|
||||
TradeOfferType.WarDeclaration -> "Declare war on [$name]"
|
||||
TradeOfferType.PeaceProposal -> "Make peace with [$name]"
|
||||
TradeOfferType.Introduction -> "Introduction to [$name]"
|
||||
TradeOfferType.City -> {
|
||||
val city =
|
||||
|
@ -18,6 +18,7 @@ enum class TradeOfferType(val numberType: TradeTypeNumberType, val isImmediate:
|
||||
Technology (TradeTypeNumberType.None, true),
|
||||
Introduction (TradeTypeNumberType.None, true),
|
||||
WarDeclaration (TradeTypeNumberType.None, true),
|
||||
PeaceProposal (TradeTypeNumberType.None, false),
|
||||
City (TradeTypeNumberType.None, true);
|
||||
|
||||
enum class TradeTypeNumberType { None, Simple, Gold }
|
||||
|
@ -63,6 +63,7 @@ class OffersListScroll(
|
||||
Stockpiled_Resource -> "Stockpiled resources"
|
||||
Technology -> "Technologies"
|
||||
WarDeclaration -> "Declarations of war"
|
||||
PeaceProposal -> "Peace Proposals"
|
||||
City -> "Cities"
|
||||
}
|
||||
val offersOfType = offersToDisplay.filter { it.type == offerType }
|
||||
@ -90,7 +91,7 @@ class OffersListScroll(
|
||||
val tradeIcon = when (offer.type) {
|
||||
Luxury_Resource, Strategic_Resource ->
|
||||
ImageGetter.getResourcePortrait(offer.name, 30f)
|
||||
WarDeclaration ->
|
||||
WarDeclaration, PeaceProposal ->
|
||||
ImageGetter.getNationPortrait(ourCiv.gameInfo.ruleset.nations[offer.name]!!, 30f)
|
||||
else -> null
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user