diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt index 30a9108c88..fa72452acd 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt @@ -18,6 +18,7 @@ import com.unciv.models.ruleset.unique.UniqueType import com.unciv.ui.components.extensions.toPercent import kotlin.math.ceil import kotlin.math.max +import kotlin.math.roundToInt import kotlin.math.sign enum class RelationshipLevel(val color: Color) { @@ -690,18 +691,24 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization { } /** - * Resolves adding gifts with negative gold values. + * Resolves the otherCiv giving gifts to this civ with negative gold values. * Prioritises reducing gifts given to the other civ before increasing our gift value. * Does not take the gold from either civ's stockpile * @param gold the amount of gold without inflation, can be negative + * @param isPureGift should be true if the gift was one-sided, or false if it wasn't */ - fun giftGold(gold: Int) { + fun giftGold(gold: Int, isPureGift: Boolean) { + val currentGold = if (isPureGift) + (gold * civInfo.gameInfo.ruleset.modOptions.constants.goldGiftMultiplier).roundToInt() + else + (gold * civInfo.gameInfo.ruleset.modOptions.constants.goldGiftTradeMultiplier).roundToInt() + val otherGold = otherCivDiplomacy().getGoldGifts() - if (otherGold > gold) { - otherCivDiplomacy().recieveGoldGifts(-gold) + if (otherGold > currentGold) { + otherCivDiplomacy().recieveGoldGifts(-currentGold) } else { otherCivDiplomacy().removeModifier(DiplomaticModifiers.GaveUsGifts) - recieveGoldGifts(gold - otherGold) + recieveGoldGifts(currentGold - otherGold) } } diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt index cd457e4491..84cd8a4d3e 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt @@ -294,22 +294,22 @@ object DiplomacyTurnManager { revertToZero(DiplomaticModifiers.LiberatedCity, 1 / 8f) if (hasModifier(DiplomaticModifiers.GaveUsGifts)) { val giftLoss = when { - relationshipLevel() == RelationshipLevel.Ally -> .5f - relationshipLevel() == RelationshipLevel.Friend -> 1f - relationshipLevel() == RelationshipLevel.Favorable -> 1.5f + relationshipLevel() == RelationshipLevel.Ally -> 1f + relationshipLevel() == RelationshipLevel.Friend -> 1.5f + relationshipLevel() == RelationshipLevel.Favorable -> 2f + relationshipLevel() == RelationshipLevel.Neutral -> 2.5f relationshipLevel() == RelationshipLevel.Competitor -> 5f relationshipLevel() == RelationshipLevel.Enemy -> 7.5f relationshipLevel() == RelationshipLevel.Unforgivable -> 10f - else -> 2f // Neutral - } + else -> 2.5f + } * civInfo.gameInfo.ruleset.modOptions.constants.goldGiftDegradationMultiplier // We should subtract a certain amount from this balanced each turn // Assuming neutral relations we will subtract the higher of either: - // 2% of the total amount or - // roughly 40 gold per turn (a value of ~.4 without inflation) + // 2.5% of the total amount or roughly 50 gold per turn (a value of ~.5 without inflation) // This ensures that the amount can be reduced to zero but scales with larger numbers val amountLost = (getModifier(DiplomaticModifiers.GaveUsGifts).absoluteValue * giftLoss / 100) .coerceAtLeast(giftLoss / 5) - revertToZero(DiplomaticModifiers.GaveUsGifts, amountLost) // Roughly worth 20 GPT without inflation + revertToZero(DiplomaticModifiers.GaveUsGifts, amountLost) } setFriendshipBasedModifier() diff --git a/core/src/com/unciv/logic/trade/TradeLogic.kt b/core/src/com/unciv/logic/trade/TradeLogic.kt index 823a140967..92ca769d49 100644 --- a/core/src/com/unciv/logic/trade/TradeLogic.kt +++ b/core/src/com/unciv/logic/trade/TradeLogic.kt @@ -159,9 +159,11 @@ class TradeLogic(val ourCivilization: Civilization, val otherCivilization: Civil val ourGoldValueOfTrade = TradeEvaluation().getTradeAcceptability(currentTrade, ourCivilization, otherCivilization, includeDiplomaticGifts = false) val theirGoldValueOfTrade = TradeEvaluation().getTradeAcceptability(currentTrade.reverse(), otherCivilization, ourCivilization, includeDiplomaticGifts = false) if (ourGoldValueOfTrade > theirGoldValueOfTrade) { - ourDiploManager.giftGold(ourGoldValueOfTrade - theirGoldValueOfTrade.coerceAtLeast(0)) + val isPureGift = currentTrade.ourOffers.isEmpty() + ourDiploManager.giftGold(ourGoldValueOfTrade - theirGoldValueOfTrade.coerceAtLeast(0), isPureGift) } else if (theirGoldValueOfTrade > ourGoldValueOfTrade) { - theirDiploManger.giftGold(theirGoldValueOfTrade - ourGoldValueOfTrade.coerceAtLeast(0)) + val isPureGift = currentTrade.theirOffers.isEmpty() + theirDiploManger.giftGold(theirGoldValueOfTrade - ourGoldValueOfTrade.coerceAtLeast(0), isPureGift) } } diff --git a/core/src/com/unciv/models/ModConstants.kt b/core/src/com/unciv/models/ModConstants.kt index 6433c76349..781e0b59da 100644 --- a/core/src/com/unciv/models/ModConstants.kt +++ b/core/src/com/unciv/models/ModConstants.kt @@ -52,6 +52,15 @@ class ModConstants { var cityWorkRange = 3 var cityExpandRange = 5 + // Modifies how much the gold value of a one-sided trade is applied to the gifts diplomatic modifier. + // Eg: One side offers a city, resource or gold for nothing in return. + var goldGiftMultiplier = 1f + // Modifies how much the gold value of a regular trade is applied to the gifts diplomatic modifier. + var goldGiftTradeMultiplier = .8f + // Modifies how quickly the GaveUsGifts dimplomacy modifier runs out. A higher value makes it run out quicker. + // Normally the gifts reduced by ~2.5% per turn depending on the diplomatic relations with the default value. + var goldGiftDegradationMultiplier = 1f + // Constants used to calculate Unit Upgrade gold Cost (can only be modded all-or-nothing) // This is a data class for one reason only: The equality implementation enables Gdx Json to omit it when default (otherwise only the individual fields are omitted) data class UnitUpgradeCost( diff --git a/docs/Modders/Mod-file-structure/5-Miscellaneous-JSON-files.md b/docs/Modders/Mod-file-structure/5-Miscellaneous-JSON-files.md index 8732e3e35d..d0d519af75 100644 --- a/docs/Modders/Mod-file-structure/5-Miscellaneous-JSON-files.md +++ b/docs/Modders/Mod-file-structure/5-Miscellaneous-JSON-files.md @@ -218,6 +218,9 @@ and city distance in another. In case of conflicts, there is no guarantee which | baseTurnsUntilRevolt | Int | 4 | [^Q] | | cityStateElectionTurns | Int | 15 | [^R] | | maxImprovementTechErasForward | Int | None | [^S] | +| goldGiftMultiplier | Float | 1 | [^T] | +| goldGiftTradeMultiplier | Float | 0.8 | [^U] | +| goldGiftDegradationMultiplier | Float | 1.0 | [^V] | Legend: @@ -257,6 +260,9 @@ Legend: - [^Q]: The number of turns before a revolt is spawned - [^R]: The number of turns between city-state elections - [^S]: If set, the Improvement picker will silently skip improvements whose tech requirement is more advanced than your current Era + this value. Example: With a 0, Trade posts will not show until the Medieval Era, with a 1 they will already show in the CLassical Era. +- [^T]: The multiplier of the gold value of a one-sided trade to be stored as gifts. +- [^U]: The multiplier of the gold value of a regular trade to be stored as gifts. Set to 0 to disable gold gifting in two-sided trades. +- [^U]: Modifies how quickly the GaveUsGifts dimplomacy modifier runs out. A higher value makes it run out quicker. Normally the gifts reduced by ~2.5% per turn depending on the diplomatic relations with the default value. #### UnitUpgradeCost diff --git a/tests/src/com/unciv/logic/civilization/diplomacy/GoldGiftingTests.kt b/tests/src/com/unciv/logic/civilization/diplomacy/GoldGiftingTests.kt index 8f97693791..dd1a6203c5 100644 --- a/tests/src/com/unciv/logic/civilization/diplomacy/GoldGiftingTests.kt +++ b/tests/src/com/unciv/logic/civilization/diplomacy/GoldGiftingTests.kt @@ -29,6 +29,9 @@ class GoldGiftingTests { a.diplomacyFunctions.makeCivilizationsMeet(b) aDiplomacy = a.getDiplomacyManager(b)!! bDiplomacy = b.getDiplomacyManager(a)!! + a.gameInfo.ruleset.modOptions.constants.goldGiftMultiplier = 1f + a.gameInfo.ruleset.modOptions.constants.goldGiftTradeMultiplier = 1f + a.gameInfo.ruleset.modOptions.constants.goldGiftDegradationMultiplier = 1f } @Test @@ -44,7 +47,7 @@ class GoldGiftingTests { fun `Gold Gift Test` () { assertEquals(0, aDiplomacy.getGoldGifts()) assertEquals(0, bDiplomacy.getGoldGifts()) - aDiplomacy.giftGold(10) + aDiplomacy.giftGold(10, true) assertTrue(aDiplomacy.getGoldGifts() > 0) assertEquals(0, bDiplomacy.getGoldGifts()) } @@ -72,7 +75,7 @@ class GoldGiftingTests { val gold2 = aDiplomacy.getGoldGifts() assertTrue(gold > gold2) assertTrue(gold2 >= gold * .9) // We shoulden't loose more than 10% of the value in one turn - assertTrue(gold2 >= 0) + assertTrue(gold2 >= 0) } @Test @@ -88,8 +91,8 @@ class GoldGiftingTests { @Test fun `Gifting gold reduces previous gifts taken` () { - aDiplomacy.giftGold(1000) - bDiplomacy.giftGold(500) + aDiplomacy.giftGold(1000, true) + bDiplomacy.giftGold(500, true) assertTrue(aDiplomacy.getGoldGifts() > 0) assertTrue(aDiplomacy.getGoldGifts() < 1000) assertTrue(bDiplomacy.getGoldGifts() == 0) @@ -134,5 +137,69 @@ class GoldGiftingTests { assertTrue(TradeEvaluation().getTradeAcceptability(tradeOffer2.currentTrade.reverse(), b,a,true) > 0) } + @Test + fun `Gold gifted uses pure gold multiplier constant`() { + a.addGold(1000) + a.gameInfo.ruleset.modOptions.constants.goldGiftMultiplier = .5f + a.gameInfo.ruleset.modOptions.constants.goldGiftTradeMultiplier = 0f + val tradeOffer = TradeLogic(a,b) + tradeOffer.currentTrade.ourOffers.add(tradeOffer.ourAvailableOffers.first { it.type == TradeOfferType.Gold }) + tradeOffer.acceptTrade() + assertEquals(500, bDiplomacy.getGoldGifts()) + } + + @Test + fun `Gold gifted uses gold multiplier constant`() { + a.addGold(1000) + b.addGold(500) + a.gameInfo.ruleset.modOptions.constants.goldGiftMultiplier = 0f + a.gameInfo.ruleset.modOptions.constants.goldGiftTradeMultiplier = .5f + val tradeOffer = TradeLogic(a,b) + tradeOffer.currentTrade.ourOffers.add(tradeOffer.ourAvailableOffers.first { it.type == TradeOfferType.Gold }) + tradeOffer.currentTrade.theirOffers.add(tradeOffer.theirAvailableOffers.first { it.type == TradeOfferType.Gold }) + tradeOffer.acceptTrade() + assertEquals(250, bDiplomacy.getGoldGifts()) + } + + @Test + fun `Gifted Gold Disapears using gold gift degradation multiplier constant` () { + val c = testGame.addCiv() + c.diplomacyFunctions.makeCivilizationsMeet(a) + val caDiplomacy = c.getDiplomacyManager(a)!! + a.gameInfo.ruleset.modOptions.constants.goldGiftDegradationMultiplier = 1f + aDiplomacy.recieveGoldGifts(100) + caDiplomacy.recieveGoldGifts(100) + assertTrue(aDiplomacy.getGoldGifts() > 0) + assertTrue(caDiplomacy.getGoldGifts() > 0) + + aDiplomacy.nextTurn() + a.gameInfo.ruleset.modOptions.constants.goldGiftDegradationMultiplier = 10f + caDiplomacy.nextTurn() + + assertTrue(caDiplomacy.getGoldGifts() < aDiplomacy.getGoldGifts()) + } + + @Test + fun `Gifted Gold doesn't disapear when gold gift degradation multiplier constant is set to 0` () { + a.gameInfo.ruleset.modOptions.constants.goldGiftDegradationMultiplier = 0f + aDiplomacy.recieveGoldGifts(1000) + assertTrue(aDiplomacy.getGoldGifts() > 0) + val gold = aDiplomacy.getGoldGifts() + aDiplomacy.nextTurn() + val gold2 = aDiplomacy.getGoldGifts() + assertTrue(gold == gold2) + } + + @Test + fun `Gifted Gold doesn't disapear when gold gift degradation multiplier constant is set to 0 with low gold` () { + a.gameInfo.ruleset.modOptions.constants.goldGiftDegradationMultiplier = 0f + aDiplomacy.recieveGoldGifts(10) + assertTrue(aDiplomacy.getGoldGifts() > 0) + val gold = aDiplomacy.getGoldGifts() + aDiplomacy.nextTurn() + val gold2 = aDiplomacy.getGoldGifts() + assertTrue(gold == gold2) + } + }