From 7ee059b6652d1840e7cbb2ba3d66360c81640f21 Mon Sep 17 00:00:00 2001 From: Alexey Chernyshov Date: Fri, 10 Feb 2023 21:03:37 +0300 Subject: [PATCH 1/8] Expansion outlines for the destruction of kingdoms feature --- build/common.props | 2 +- changelog.txt | 5 ++ .../Costs/DiplomacyCostCalculator.cs | 4 +- .../WarPeace/KingdomPeaceAction.cs | 65 +++++++++++++++++-- src/Bannerlord.Diplomacy/Settings.cs | 24 ++++--- 5 files changed, 80 insertions(+), 20 deletions(-) diff --git a/build/common.props b/build/common.props index cd3ebe84..c4cf22aa 100644 --- a/build/common.props +++ b/build/common.props @@ -2,7 +2,7 @@ - 1.2.4 + 1.2.5 1.0.0 diff --git a/changelog.txt b/changelog.txt index 5b8c0c77..8b276fff 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,9 @@ --------------------------------------------------------------------------------------------------- +Version: 1.2.5 +Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0 +* Adjusted the calculation of war reparations to make it even more lenient. +* Added an option to disable the elimination of fiefless kingdoms. +--------------------------------------------------------------------------------------------------- Version: 1.2.4 Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0 * Fixed a crash after loading a save when attempting to fix any faulty factions. diff --git a/src/Bannerlord.Diplomacy/Costs/DiplomacyCostCalculator.cs b/src/Bannerlord.Diplomacy/Costs/DiplomacyCostCalculator.cs index 178c35fb..659730f9 100644 --- a/src/Bannerlord.Diplomacy/Costs/DiplomacyCostCalculator.cs +++ b/src/Bannerlord.Diplomacy/Costs/DiplomacyCostCalculator.cs @@ -73,7 +73,7 @@ private static KingdomWalletCost DetermineReparationsForMakingPeace(Kingdom king return new(giver, receiver, reparationsCost); //No reparations from kingdoms that are about to be destroyed - if (!(kingdomMakingPeace.Fiefs.Count > 0) && !FactionManager.GetEnemyKingdoms(kingdomMakingPeace).Any(k => k != otherKingdom && !k.IsEliminated)) + if (Settings.Instance!.EnableKingdomElimination && kingdomMakingPeace.Fiefs.Count == 0 && !FactionManager.GetEnemyKingdoms(kingdomMakingPeace).Any(k => k != otherKingdom && !k.IsEliminated)) return new(giver, receiver, reparationsCost); if (kingdomMakingPeace.IsRebelKingdomOf(otherKingdom) || (otherKingdom.IsRebelKingdomOf(kingdomMakingPeace) && kingdomMakingPeace.GetRebelFactions().Any(x => x.RebelKingdom == otherKingdom && x is not SecessionFaction))) return new(giver, receiver, reparationsCost); @@ -110,7 +110,7 @@ private static KingdomWalletCost DetermineReparationsForMakingPeace(Kingdom king static int CalculateReparations(Kingdom kingdomPayingReparations, Kingdom otherKingdom, float payingKingdomWarExhaustion, float otherKingdomWarExhaustion, bool? payerLostWar = null) { var lossReparations = (payerLostWar ?? WarExhaustionManager.Instance.GetWarResult(kingdomPayingReparations, otherKingdom) == WarExhaustionManager.WarResult.Loss) ? Settings.Instance!.DefeatedGoldCost : 0; - var warExhaustionReparations = otherKingdomWarExhaustion * ((payingKingdomWarExhaustion - otherKingdomWarExhaustion) / (WarExhaustionManager.IsCriticalWarExhaustion(otherKingdomWarExhaustion) ? 8f : 4f)); + var warExhaustionReparations = otherKingdomWarExhaustion * ((payingKingdomWarExhaustion - otherKingdomWarExhaustion) / (WarExhaustionManager.IsCriticalWarExhaustion(otherKingdomWarExhaustion) ? 20f : 10f)); return (int) ((lossReparations + warExhaustionReparations) * GetKingdomScalingFactorForReparations(kingdomPayingReparations)); } diff --git a/src/Bannerlord.Diplomacy/DiplomaticAction/WarPeace/KingdomPeaceAction.cs b/src/Bannerlord.Diplomacy/DiplomaticAction/WarPeace/KingdomPeaceAction.cs index 26db24e1..683cb642 100644 --- a/src/Bannerlord.Diplomacy/DiplomaticAction/WarPeace/KingdomPeaceAction.cs +++ b/src/Bannerlord.Diplomacy/DiplomaticAction/WarPeace/KingdomPeaceAction.cs @@ -51,7 +51,7 @@ internal sealed class KingdomPeaceAction private const string _noChoice = "{=13G0c8RE}Given how badly your kingdom has been ravaged by the war, you have no choice but to accept the peace."; - private static void ApplyPeaceInternal(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, bool skipPlayerPrompts, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, bool shouldBeDestroyed) + private static void ApplyPeaceInternal(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, bool skipPlayerPrompts, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed) { if (kingdomMakingPeace == Clan.PlayerClan.Kingdom && !skipPlayerPrompts) { @@ -67,7 +67,7 @@ private static void ApplyPeaceInternal(Kingdom kingdomMakingPeace, Kingdom other } } - private static void NotifyPlayerOfPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, bool shouldBeDestroyed) + private static void NotifyPlayerOfPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed) { GetNotificationInquiryTitleAndBody(kingdomMakingPeace, otherKingdom, isATie, diplomacyCost, dailyPeaceTributeToPay, fiefsToBeReturned, hasFiefsRemaining, shouldBeDestroyed, out var inquiryBody, out var inquiryTitle); @@ -82,7 +82,7 @@ private static void NotifyPlayerOfPeace(Kingdom kingdomMakingPeace, Kingdom othe null), true); } - private static void GetNotificationInquiryTitleAndBody(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, bool shouldBeDestroyed, out TextObject inquiryBody, out TextObject inquiryTitle) + private static void GetNotificationInquiryTitleAndBody(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed, out TextObject inquiryBody, out TextObject inquiryTitle) { var rebelIsMakingPeace = kingdomMakingPeace.IsRebelKingdomOf(otherKingdom); var originalIsMakingPeace = otherKingdom.IsRebelKingdomOf(kingdomMakingPeace); @@ -140,7 +140,7 @@ private static void GetNotificationInquiryTitleAndBody(Kingdom kingdomMakingPeac } } - private static void AcceptPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool shouldBeDestroyed) + private static void AcceptPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, EliminationStatus shouldBeDestroyed) { diplomacyCost.ApplyCost(); DoReturnFiefs(kingdomMakingPeace, otherKingdom, fiefsToBeReturned); @@ -174,7 +174,7 @@ private static void DoReturnFiefs(Kingdom kingdomMakingPeace, Kingdom otherKingd } } - private static void CreatePeaceInquiry(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, bool shouldBeDestroyed) + private static void CreatePeaceInquiry(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed) { #if v100 || v101 || v102 || v103 InformationManager.ShowInquiry(new InquiryData( @@ -200,7 +200,7 @@ private static void CreatePeaceInquiry(Kingdom kingdomMakingPeace, Kingdom other #endif } - private static string GetPeaceInquiryText(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, bool shouldBeDestroyed) + private static string GetPeaceInquiryText(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed) { var rebelIsMakingPeace = kingdomMakingPeace.IsRebelKingdomOf(otherKingdom); var originalIsMakingPeace = otherKingdom.IsRebelKingdomOf(kingdomMakingPeace); @@ -276,11 +276,22 @@ public static void ApplyPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, var isATie = WarExhaustionManager.Instance.GetWarResult(kingdomMakingPeace, otherKingdom) == WarExhaustionManager.WarResult.Tie; var fiefsToBeReturned = GetFiefsToBeReturned(kingdomMakingPeace, otherKingdom); var hasFiefsRemaining = kingdomMakingPeace.Fiefs.Count > 0; - var shouldBeDestroyed = !hasFiefsRemaining && !FactionManager.GetEnemyKingdoms(kingdomMakingPeace).Any(k => k != otherKingdom && !k.IsEliminated) && (!otherKingdom.IsRebelKingdomOf(kingdomMakingPeace) || !otherKingdom.Fiefs.Any()); + var shouldBeDestroyed = ShouldKingdomsBeDestroyed(kingdomMakingPeace, otherKingdom, hasFiefsRemaining); ApplyPeaceInternal(kingdomMakingPeace, otherKingdom, isATie, skipPlayerPrompts, diplomacyCost, dailyPeaceTributeToPay, fiefsToBeReturned, hasFiefsRemaining, shouldBeDestroyed); } + public static EliminationStatus ShouldKingdomsBeDestroyed(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool? hasFiefsRemaining = null) + { + return (true, true); + /* + return + Settings.Instance!.EnableKingdomElimination && !(hasFiefsRemaining ?? kingdomMakingPeace.Fiefs.Count > 0) + && !FactionManager.GetEnemyKingdoms(kingdomMakingPeace).Any(k => k != otherKingdom && !k.IsEliminated) + && (!otherKingdom.IsRebelKingdomOf(kingdomMakingPeace) || !otherKingdom.Fiefs.Any()); + */ + } + private static List GetFiefsToBeReturned(Kingdom kingdomMakingPeace, Kingdom otherKingdom) { if (!ShouldAnyFiefsBeReturned(kingdomMakingPeace, otherKingdom, out var warResultForOtherKingdom)) @@ -340,4 +351,44 @@ private static List GetFiefsSuitableToBeReturnedInternal(Kingdom kingdomMa return kingdomMakingPeace.Fiefs.Where(f => settlementsCaptured.Contains(f.Settlement)).ToList(); } } + + internal struct EliminationStatus + { + public bool DestroyKingdomMakingPeace; + public bool DestroyOtherKingdom; + + public EliminationStatus(bool destroyKingdomMakingPeace, bool destroyOtherKingdom) + { + DestroyKingdomMakingPeace = destroyKingdomMakingPeace; + DestroyOtherKingdom = destroyOtherKingdom; + } + + public override bool Equals(object? obj) + { + return obj is EliminationStatus other && + DestroyKingdomMakingPeace == other.DestroyKingdomMakingPeace && + DestroyOtherKingdom == other.DestroyOtherKingdom; + } + + public override int GetHashCode() + { + return HashCode.Combine(DestroyKingdomMakingPeace, DestroyOtherKingdom); + } + + public void Deconstruct(out bool destroyKingdomMakingPeace, out bool destroyOtherKingdom) + { + destroyKingdomMakingPeace = DestroyKingdomMakingPeace; + destroyOtherKingdom = DestroyOtherKingdom; + } + + public static implicit operator (bool DestroyKingdomMakingPeace, bool DestroyOtherKingdom)(EliminationStatus value) + { + return (value.DestroyKingdomMakingPeace, value.DestroyOtherKingdom); + } + + public static implicit operator EliminationStatus((bool DestroyKingdomMakingPeace, bool DestroyOtherKingdom) value) + { + return new EliminationStatus(value.DestroyKingdomMakingPeace, value.DestroyOtherKingdom); + } + } } \ No newline at end of file diff --git a/src/Bannerlord.Diplomacy/Settings.cs b/src/Bannerlord.Diplomacy/Settings.cs index 6c4b9205..02384f2c 100644 --- a/src/Bannerlord.Diplomacy/Settings.cs +++ b/src/Bannerlord.Diplomacy/Settings.cs @@ -38,34 +38,38 @@ class Settings : AttributeGlobalSettings [SettingPropertyGroup(HeadingKingdomDiplomacy)] public bool EnableFiefFirstRight { get; set; } = true; - [SettingPropertyInteger("{=ZRlNvsev}Minimum War Duration in Days", 0, 500, Order = 1, RequireRestart = false, HintText = "{=vuFT5ns8}The minimum duration (in days) that a war can last before proposing peace. Default value is 21 (quarter of a standard game year).")] + [SettingPropertyBool("{=}Enable Fiefless Kingdom Elimination", Order = 10, RequireRestart = false, HintText = "{=}If enabled, kingdoms without any fiefs are destroyed when they are sign peace treaty ending the last ongoing war they participate in. Default value is enabled.")] + [SettingPropertyGroup(HeadingKingdomDiplomacy)] + public bool EnableKingdomElimination { get; set; } = true; + + [SettingPropertyInteger("{=ZRlNvsev}Minimum War Duration in Days", 0, 500, Order = 20, RequireRestart = false, HintText = "{=vuFT5ns8}The minimum duration (in days) that a war can last before proposing peace. Default value is 21 (quarter of a standard game year).")] [SettingPropertyGroup(HeadingKingdomDiplomacy)] public int MinimumWarDurationInDays { get; set; } = 21; - [SettingPropertyInteger("{=4MzQHMVj}Declare War Cooldown in Days", 0, 500, Order = 2, RequireRestart = false, HintText = "{=q2duqN8d}The minimum duration (in days) before re-declaring war on the same kingdom after making peace. Default value is 21 (quarter of a standard game year).")] + [SettingPropertyInteger("{=4MzQHMVj}Declare War Cooldown in Days", 0, 500, Order = 21, RequireRestart = false, HintText = "{=q2duqN8d}The minimum duration (in days) before re-declaring war on the same kingdom after making peace. Default value is 21 (quarter of a standard game year).")] [SettingPropertyGroup(HeadingKingdomDiplomacy)] public int DeclareWarCooldownInDays { get; set; } = 21; - [SettingPropertyBool("{=2XC8QHkl}Enable Alliances", Order = 3, RequireRestart = false, HintText = "{=5YJBZx28}If disabled, this disables the ability to form alliances for both player and AI kingdoms. Default value is enabled.")] + [SettingPropertyBool("{=2XC8QHkl}Enable Alliances", Order = 30, RequireRestart = false, HintText = "{=5YJBZx28}If disabled, this disables the ability to form alliances for both player and AI kingdoms. Default value is enabled.")] [SettingPropertyGroup(HeadingKingdomDiplomacy)] public bool EnableAlliances { get; set; } = true; - [SettingPropertyInteger("{=H6XMjwpF}Minimum Alliance Duration in Days", 0, 500, Order = 4, RequireRestart = false, HintText = "{=RrsWhIWi}The minimum duration (in days) that an alliance will last before it can be broken. Default value is 42 (half of a standard game year).")] + [SettingPropertyInteger("{=5a829TiT}Alliance Tendency", -100, 100, Order = 31, RequireRestart = false, HintText = "{=7nSjs8UL}Score modifier affecting the tendency of kingdoms to form alliances. Increasing the modifier makes alliances more desirable to AI kingdoms. Default value is 0.")] + [SettingPropertyGroup(HeadingKingdomDiplomacy)] + public int AllianceTendency { get; set; } = 0; + + [SettingPropertyInteger("{=H6XMjwpF}Minimum Alliance Duration in Days", 0, 500, Order = 32, RequireRestart = false, HintText = "{=RrsWhIWi}The minimum duration (in days) that an alliance will last before it can be broken. Default value is 42 (half of a standard game year).")] [SettingPropertyGroup(HeadingKingdomDiplomacy)] public int MinimumAllianceDuration { get; set; } = 42; - [SettingPropertyInteger("{=V35hUfcc}Non-Aggression Pact Duration in Days", 0, 1000, Order = 5, RequireRestart = false, HintText = "{=KXLGZEPh}The duration (in days) that a non-aggression pact will last. Default value is 84 (one standard game year).")] + [SettingPropertyInteger("{=V35hUfcc}Non-Aggression Pact Duration in Days", 0, 1000, Order = 50, RequireRestart = false, HintText = "{=KXLGZEPh}The duration (in days) that a non-aggression pact will last. Default value is 84 (one standard game year).")] [SettingPropertyGroup(HeadingKingdomDiplomacy)] public int NonAggressionPactDuration { get; set; } = 84; - [SettingPropertyInteger("{=G8BhBnRG}Non-Aggression Pact Tendency", -100, 100, Order = 6, RequireRestart = false, HintText = "{=907ER5u9}Score modifier affecting the tendency of kingdoms to form non-aggression pacts. Increasing the modifier makes non-aggression pacts more desirable to AI kingdoms. Default value is 0.")] + [SettingPropertyInteger("{=G8BhBnRG}Non-Aggression Pact Tendency", -100, 100, Order = 51, RequireRestart = false, HintText = "{=907ER5u9}Score modifier affecting the tendency of kingdoms to form non-aggression pacts. Increasing the modifier makes non-aggression pacts more desirable to AI kingdoms. Default value is 0.")] [SettingPropertyGroup(HeadingKingdomDiplomacy)] public int NonAggressionPactTendency { get; set; } = 0; - [SettingPropertyInteger("{=5a829TiT}Alliance Tendency", -100, 100, Order = 7, RequireRestart = false, HintText = "{=7nSjs8UL}Score modifier affecting the tendency of kingdoms to form alliances. Increasing the modifier makes alliances more desirable to AI kingdoms. Default value is 0.")] - [SettingPropertyGroup(HeadingKingdomDiplomacy)] - public int AllianceTendency { get; set; } = 0; - [SettingPropertyBool(displayName: "{=6m1SspFW}Enable Player Kingdom Diplomacy Control", Order = 999, RequireRestart = false, HintText = "{=N5EouSSj}Gives the player full control over declaring war and making peace in the kingdom they belong to, even if they are just a vassal and not the leader of the kingdom. Default value is disabled.")] [SettingPropertyGroup(HeadingKingdomDiplomacy)] public bool PlayerDiplomacyControl { get; set; } = false; From 6ee5fef505d65fc676902e9b9d23b67cc200d7b5 Mon Sep 17 00:00:00 2001 From: Alexey Chernyshov <65975574+artifixer@users.noreply.github.com> Date: Fri, 10 Feb 2023 22:45:48 +0300 Subject: [PATCH 2/8] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index e8de31fd..9163e0e3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright © 2020-2022 Diplomacy Team +Copyright © 2020-2023 Diplomacy Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 8a94c55ecba0dd46c0088d8c7d6d4fcf18935c7b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 Feb 2023 00:28:46 +0000 Subject: [PATCH 3/8] Automated dotnet-format update --- .../Events/WarExhaustionInitializedEvent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bannerlord.Diplomacy/Events/WarExhaustionInitializedEvent.cs b/src/Bannerlord.Diplomacy/Events/WarExhaustionInitializedEvent.cs index d63089c5..addd0d0a 100644 --- a/src/Bannerlord.Diplomacy/Events/WarExhaustionInitializedEvent.cs +++ b/src/Bannerlord.Diplomacy/Events/WarExhaustionInitializedEvent.cs @@ -13,4 +13,4 @@ public WarExhaustionInitializedEvent(Kingdom kingdom, Kingdom otherKingdom) public Kingdom Kingdom { get; } public Kingdom OtherKingdom { get; } } -} +} \ No newline at end of file From dca1cb9c984782bc1542a87dea68273ff75cf4da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:27:46 +0000 Subject: [PATCH 4/8] Bump Bannerlord.BuildResources from 1.0.1.89 to 1.0.1.92 Bumps [Bannerlord.BuildResources](https://github.com/BUTR/Bannerlord.BuildResources) from 1.0.1.89 to 1.0.1.92. - [Release notes](https://github.com/BUTR/Bannerlord.BuildResources/releases) - [Commits](https://github.com/BUTR/Bannerlord.BuildResources/commits) --- updated-dependencies: - dependency-name: Bannerlord.BuildResources dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build/common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/common.props b/build/common.props index cd3ebe84..2013b8da 100644 --- a/build/common.props +++ b/build/common.props @@ -9,7 +9,7 @@ - 1.0.1.89 + 1.0.1.92 2.2.2 From a07555bc12d7fe5d7df6fec7a8dd4b1823752e1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:13:58 +0000 Subject: [PATCH 5/8] Bump Harmony.Extensions from 3.2.0.73 to 3.2.0.75 Bumps [Harmony.Extensions](https://github.com/BUTR/Harmony.Extensions) from 3.2.0.73 to 3.2.0.75. - [Release notes](https://github.com/BUTR/Harmony.Extensions/releases) - [Commits](https://github.com/BUTR/Harmony.Extensions/commits) --- updated-dependencies: - dependency-name: Harmony.Extensions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build/common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/common.props b/build/common.props index cd3ebe84..d735ec3b 100644 --- a/build/common.props +++ b/build/common.props @@ -25,7 +25,7 @@ 1.0.1.44 - 3.2.0.73 + 3.2.0.75 net472 From 98d4d9a3e2aeade5461dc9720bc941ec546f5806 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:14:05 +0000 Subject: [PATCH 6/8] Bump Bannerlord.BUTR.Shared from 3.0.0.130 to 3.0.0.134 Bumps [Bannerlord.BUTR.Shared](https://github.com/BUTR/Bannerlord.BUTR.Shared) from 3.0.0.130 to 3.0.0.134. - [Release notes](https://github.com/BUTR/Bannerlord.BUTR.Shared/releases) - [Commits](https://github.com/BUTR/Bannerlord.BUTR.Shared/commits) --- updated-dependencies: - dependency-name: Bannerlord.BUTR.Shared dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build/common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/common.props b/build/common.props index cd3ebe84..c96d5a95 100644 --- a/build/common.props +++ b/build/common.props @@ -19,7 +19,7 @@ 2.6.0 - 3.0.0.130 + 3.0.0.134 5.0.198 From a6ab738efda1f24d9979e1f3863f660df36fc73b Mon Sep 17 00:00:00 2001 From: Alexey Chernyshov Date: Sun, 26 Feb 2023 21:59:44 +0300 Subject: [PATCH 7/8] Fixes and balancing --- changelog.txt | 11 ++ .../Actions/GiveGoldToClanAction.cs | 6 +- .../Actions/GiveGoldToKingdomAction.cs | 140 +++++++++++++----- ...ction.cs => SoftlyDestroyKingdomAction.cs} | 2 +- .../CampaignBehaviors/CivilWarBehavior.cs | 41 +---- src/Bannerlord.Diplomacy/Cheats.cs | 5 +- .../CivilWar/Actions/StartRebellionAction.cs | 2 +- .../CivilWar/Factions/AbdicationFaction.cs | 2 + .../CivilWar/Factions/RebelFaction.cs | 2 + .../CivilWar/Factions/SecessionFaction.cs | 2 + .../CivilWar/RebelFactionManager.cs | 14 +- .../Costs/DiplomacyCostCalculator.cs | 20 +-- .../Costs/KingdomWalletCost.cs | 2 +- .../WarPeace/KingdomPeaceAction.cs | 96 ++++++------ .../Extensions/KingdomExtensions.cs | 21 +++ .../Helpers/TributeHelper.cs | 32 ++-- .../PatchTools/PatchManager.cs | 1 + .../Patches/MakePeaceKingdomDecisionPatch.cs | 30 ++++ src/Bannerlord.Diplomacy/Settings.cs | 18 ++- .../ViewModel/DetailWarVM.cs | 14 +- .../WarExhaustionMapIndicatorItemVM.cs | 8 +- .../ViewModelMixin/KingdomTruceItemVmMixin.cs | 4 +- .../ViewModelMixin/KingdomWarItemVMMixin.cs | 4 +- .../WarExhaustionManager.EventHandling.cs | 96 ++++++------ .../WarExhaustion/WarExhaustionManager.cs | 26 +++- .../Languages/std_module_strings_xml.xml | 21 +-- .../Languages/war_exhaustion_strings.xml | 8 +- 27 files changed, 390 insertions(+), 238 deletions(-) rename src/Bannerlord.Diplomacy/Actions/{DestroyKingdomSoftlyAction.cs => SoftlyDestroyKingdomAction.cs} (94%) create mode 100644 src/Bannerlord.Diplomacy/Patches/MakePeaceKingdomDecisionPatch.cs diff --git a/changelog.txt b/changelog.txt index 8b276fff..f6057eca 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,8 +1,19 @@ --------------------------------------------------------------------------------------------------- Version: 1.2.5 Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0 +* Fixed a crash on making peace when War Exhaustion is disabled. +* Fixed a crash after loading a save when attempting to fix any faulty factions (again). +* Fixed a bug due to which kingdoms were sometimes not destroyed when they should have been. +* Fixed a bug due to which War Exhaustion breakdowns were sometimes showing wrong values (unaffected by rates). +* Making peace should now use all of the mod's enabled mechanics, no matter what interface option was used for it. * Adjusted the calculation of war reparations to make it even more lenient. +* Adjusted the way war reparations are gathered by the paying kingdom to make it less stressful. +* Adjusted the way war reparations are distributed among the winning faction. +* Adjusted the calculation and default values for some of the War Exhaustion entries. +* Adjusted War Exhaustion rates calculation. +* Adjusted descriptions of some settings for clarity. * Added an option to disable the elimination of fiefless kingdoms. +* Added an option to disable fief repatriation after losing a war. --------------------------------------------------------------------------------------------------- Version: 1.2.4 Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0 diff --git a/src/Bannerlord.Diplomacy/Actions/GiveGoldToClanAction.cs b/src/Bannerlord.Diplomacy/Actions/GiveGoldToClanAction.cs index dbc25b2b..7b3f1545 100644 --- a/src/Bannerlord.Diplomacy/Actions/GiveGoldToClanAction.cs +++ b/src/Bannerlord.Diplomacy/Actions/GiveGoldToClanAction.cs @@ -1,5 +1,7 @@ using Helpers; +using System.Linq; + using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Actions; using TaleWorlds.Library; @@ -22,7 +24,7 @@ private static void ApplyInternal(Hero? giverHero, Clan? clan, int goldAmount) private static void GiveGoldToClan(int gold, Clan clan) { - foreach ((var recipientHero, var amount) in MBMath.DistributeShares(gold, clan.Lords, CalculateShare)) + foreach ((var recipientHero, var amount) in MBMath.DistributeShares(gold, clan.Lords.Where(l => l.IsAlive && l.IsCommander), CalculateShare)) { GiveGoldAction.ApplyBetweenCharacters(null, recipientHero, amount); } @@ -30,7 +32,7 @@ private static void GiveGoldToClan(int gold, Clan clan) private static int CalculateShare(Hero member) { - return HeroHelper.CalculateTotalStrength(member) + (member == member.Clan?.Leader ? 100 : 10); + return HeroHelper.CalculateTotalStrength(member) + (member == member.Clan?.Leader ? 500 : 10); } public static void ApplyFromHeroToClan(Hero giverHero, Clan clan, int amount) diff --git a/src/Bannerlord.Diplomacy/Actions/GiveGoldToKingdomAction.cs b/src/Bannerlord.Diplomacy/Actions/GiveGoldToKingdomAction.cs index 07224b01..9a0017dc 100644 --- a/src/Bannerlord.Diplomacy/Actions/GiveGoldToKingdomAction.cs +++ b/src/Bannerlord.Diplomacy/Actions/GiveGoldToKingdomAction.cs @@ -1,11 +1,21 @@ -using TaleWorlds.CampaignSystem; +using System; +using System.Linq; + +using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Actions; +using TaleWorlds.CampaignSystem.Settlements; using TaleWorlds.Library; namespace Diplomacy.Actions { public static class GiveGoldToKingdomAction { + private const int MinRequiredBudgetWalletSize = 2000000; + private const int MaxRequiredClanGold = 50000; + private const int MinRequiredShortfall = 100000; + private const int GoldPerProsperity = 1000; + private const int MaxRevenuePerMercenaryTier = 10000; + private static void ApplyInternal(Kingdom? giverKingdom, Kingdom? receiverKingdom, int amount, WalletType giverWallet, WalletType receiverWallet) { if (amount == 0) @@ -13,37 +23,99 @@ private static void ApplyInternal(Kingdom? giverKingdom, Kingdom? receiverKingdo if (giverKingdom != null) { - switch (giverWallet) - { - case WalletType.TributeWallet: - giverKingdom.TributeWallet -= amount; - break; - case WalletType.BudgetWallet: - //Leaders should benefit from those payments too - var leaderAmount = amount < 0 ? amount / 3 : 0; - if (leaderAmount > 0) GiveGoldAction.ApplyBetweenCharacters(null, giverKingdom.Leader, leaderAmount); - giverKingdom.KingdomBudgetWallet -= (amount - leaderAmount); - break; - default: - break; - } + GetMoneyFromGiver(giverKingdom, amount, giverWallet); } if (receiverKingdom != null) { - switch (receiverWallet) - { - case WalletType.TributeWallet: - receiverKingdom.TributeWallet += amount; - break; - case WalletType.BudgetWallet: - //Leaders should benefit from those payments too - var leaderAmount = amount > 0 ? amount / 3 : 0; - if (leaderAmount > 0) GiveGoldAction.ApplyBetweenCharacters(null, receiverKingdom.Leader, leaderAmount); - receiverKingdom.KingdomBudgetWallet += (amount - leaderAmount); - break; - default: - break; - } + GiveMoneyToReceiver(receiverKingdom, amount, receiverWallet); + } + } + + private static void GetMoneyFromGiver(Kingdom giverKingdom, int amount, WalletType giverWallet) + { + switch (giverWallet) + { + case WalletType.TributeWallet: + giverKingdom.TributeWallet -= amount; + return; + case WalletType.BudgetWallet: + giverKingdom.KingdomBudgetWallet -= amount; + return; + case WalletType.ReparationsWallet: + //Cover from the kingdom budget + var availableBudgetAmount = Math.Max(giverKingdom.KingdomBudgetWallet - MinRequiredBudgetWalletSize, 0); + if (availableBudgetAmount > 0) + { + var budgetCoveredAmount = Math.Min(availableBudgetAmount, amount); + giverKingdom.KingdomBudgetWallet -= budgetCoveredAmount; + amount -= budgetCoveredAmount; + } + if (amount <= 0) + return; + + //Cover from the kingdom prosperity + var tolerableAmount = giverKingdom.Clans.Where(c => !c.IsUnderMercenaryService && !c.IsEliminated).Sum(c => c.Gold - Math.Min(c.Gold / 4, MaxRequiredClanGold)); + if (tolerableAmount < amount) + { + var amountToCover = amount - tolerableAmount; + if (amountToCover >= MinRequiredShortfall) + { + foreach ((var settlement, var amountToCoverBySettlement) in MBMath.DistributeShares(amountToCover, giverKingdom.Settlements.Where(s => s.IsCastle || s.IsTown), CalculateSettlementShare)) + { + settlement.Prosperity -= amountToCoverBySettlement / GoldPerProsperity; + } + amount = tolerableAmount; + } + } + + giverKingdom.TributeWallet -= amount; + return; + default: + return; + } + } + + private static void GiveMoneyToReceiver(Kingdom receiverKingdom, int amount, WalletType receiverWallet) + { + switch (receiverWallet) + { + case WalletType.TributeWallet: + receiverKingdom.TributeWallet += amount; + return; + case WalletType.BudgetWallet: + receiverKingdom.KingdomBudgetWallet += amount; + return; + case WalletType.ReparationsWallet: + //Leaders should benefit from those payments too + var leaderAmount = amount / 3; + if (leaderAmount > 0) GiveGoldAction.ApplyBetweenCharacters(null, receiverKingdom.Leader, leaderAmount); + //Mercenaries + var mercenaryAmount = amount / 6; + int mercenaryAmountFact = 0; + foreach ((var recipientMercClan, var amountForMercClan) in MBMath.DistributeShares(mercenaryAmount, receiverKingdom.Clans.Where(c => c.IsUnderMercenaryService && !c.IsEliminated), CalculateMercenaryShare)) + { + var amountForMercClanFact = Math.Min(amountForMercClan, recipientMercClan.Tier * MaxRevenuePerMercenaryTier); + GiveGoldAction.ApplyBetweenCharacters(null, recipientMercClan.Leader, amountForMercClanFact); + mercenaryAmountFact += amountForMercClanFact; + } + //Reassess + amount = amount - leaderAmount - mercenaryAmountFact; + //Player + var playerClan = Clan.PlayerClan; + if (playerClan.Kingdom == receiverKingdom && !playerClan.IsUnderMercenaryService && playerClan.Leader != receiverKingdom.Leader) + { + var playerAmount = MBMath.DistributeShares(amount, receiverKingdom.Clans.Where(c => !c.IsUnderMercenaryService && !c.IsEliminated), CalculateShare).FirstOrDefault(x => x.Item1 == playerClan); + if (playerAmount != default && playerAmount.Item2 > 0) + { + GiveGoldAction.ApplyBetweenCharacters(null, playerClan.Leader, playerAmount.Item2); + amount -= playerAmount.Item2; + } + } + + receiverKingdom.KingdomBudgetWallet += amount; + return; + default: + return; } } @@ -61,16 +133,15 @@ private static void ApplyInternal(Hero? giverHero, Kingdom? kingdom, int goldAmo private static void GiveGoldToKingdom(int gold, Kingdom kingdom) { - foreach ((var recipientClan, var amount) in MBMath.DistributeShares(gold, kingdom.Clans, CalculateShare)) + foreach ((var recipientClan, var amount) in MBMath.DistributeShares(gold, kingdom.Clans.Where(c => !c.IsUnderMercenaryService && !c.IsEliminated), CalculateShare)) { GiveGoldToClanAction.ApplyToClan(recipientClan, amount); } } - private static int CalculateShare(Clan clan) - { - return (int) clan.TotalStrength + (clan == clan.Kingdom?.Leader?.Clan ? 1000 : 10); - } + private static int CalculateShare(Clan clan) => Math.Max(clan.Tier / 2, 1) + (clan == clan.Kingdom?.Leader?.Clan ? 1 : 0); + private static int CalculateMercenaryShare(Clan clan) => Math.Max((int) clan.Influence, 1); + private static int CalculateSettlementShare(Settlement settlement) => Math.Max((int) settlement.Prosperity, 1); public static void ApplyFromHeroToKingdom(Hero giverHero, Kingdom kingdom, int amount) { @@ -88,6 +159,7 @@ public enum WalletType : byte MercenaryWallet = 1, TributeWallet = 2, BudgetWallet = 3, + ReparationsWallet = 4 } } } \ No newline at end of file diff --git a/src/Bannerlord.Diplomacy/Actions/DestroyKingdomSoftlyAction.cs b/src/Bannerlord.Diplomacy/Actions/SoftlyDestroyKingdomAction.cs similarity index 94% rename from src/Bannerlord.Diplomacy/Actions/DestroyKingdomSoftlyAction.cs rename to src/Bannerlord.Diplomacy/Actions/SoftlyDestroyKingdomAction.cs index e28c30f1..d06cf3d4 100644 --- a/src/Bannerlord.Diplomacy/Actions/DestroyKingdomSoftlyAction.cs +++ b/src/Bannerlord.Diplomacy/Actions/SoftlyDestroyKingdomAction.cs @@ -5,7 +5,7 @@ namespace Diplomacy.Actions { - public static class DestroyKingdomSoftlyAction + public static class SoftlyDestroyKingdomAction { public static void Apply(Kingdom kingdomToDestroy) { diff --git a/src/Bannerlord.Diplomacy/CampaignBehaviors/CivilWarBehavior.cs b/src/Bannerlord.Diplomacy/CampaignBehaviors/CivilWarBehavior.cs index 24db806b..c3dc9fdb 100644 --- a/src/Bannerlord.Diplomacy/CampaignBehaviors/CivilWarBehavior.cs +++ b/src/Bannerlord.Diplomacy/CampaignBehaviors/CivilWarBehavior.cs @@ -92,6 +92,7 @@ private void ResolveCivilWar(IFaction factionMakingPeace, IFaction otherFaction) private void ResolveCivilWar(IFaction factionMakingPeace, IFaction otherFaction, MakePeaceAction.MakePeaceDetail makePeaceDetail) #endif { + //Need to check if this runs before or after WarExhaustionBehavior if (factionMakingPeace is Kingdom kingdomMakingPeace && otherFaction is Kingdom otherKingdom) { var kingdomMakingPeaceIsRebel = kingdomMakingPeace.IsRebelKingdomOf(otherKingdom); @@ -100,43 +101,8 @@ private void ResolveCivilWar(IFaction factionMakingPeace, IFaction otherFaction, var rebelKingdom = kingdomMakingPeaceIsRebel ? kingdomMakingPeace : otherKingdom; var parentKingdom = kingdomMakingPeaceIsRebel ? otherKingdom : kingdomMakingPeace; var rebelFaction = RebelFactionManager.GetRebelFaction(parentKingdom).First(x => x.RebelKingdom == rebelKingdom); - - if (!Settings.Instance!.EnableWarExhaustion) - { - ResolveLoss(factionMakingPeace, rebelKingdom, rebelFaction); - } - else - { - var warResult = WarExhaustionManager.Instance.GetWarResult(kingdomMakingPeace, otherKingdom); - switch (warResult) - { - case WarExhaustionManager.WarResult.Tie when factionMakingPeace.Fiefs.Any(): - { - var peaceBarterable = new PeaceBarterable(kingdomMakingPeace.Leader, kingdomMakingPeace, otherKingdom, CampaignTime.Years(1f)); - var valueForOtherKingdom = -peaceBarterable.GetValueForFaction(otherKingdom); - foreach (Clan clan in otherKingdom.Clans) - { - var valueForClan = -peaceBarterable.GetValueForFaction(clan); - if (valueForClan > valueForOtherKingdom) - valueForOtherKingdom = valueForClan; - } - if (valueForOtherKingdom > -5000 && valueForOtherKingdom < 5000) - valueForOtherKingdom = 0; - - if (valueForOtherKingdom < 0) - ResolveLoss(otherKingdom, rebelKingdom, rebelFaction); - else - ResolveLoss(factionMakingPeace, rebelKingdom, rebelFaction); - break; - } - case >= WarExhaustionManager.WarResult.PyrrhicVictory: - ResolveLoss(otherKingdom, rebelKingdom, rebelFaction); - break; - default: - ResolveLoss(factionMakingPeace, rebelKingdom, rebelFaction); - break; - } - } + var loserKingdom = RebelFactionManager.GetCivilWarLoser(kingdomMakingPeace, otherKingdom); + ResolveLoss(loserKingdom, rebelKingdom, rebelFaction); } } @@ -148,6 +114,7 @@ static void ResolveLoss(IFaction loser, Kingdom rebelKingdom, RebelFaction rebel rebelFaction.EnforceSuccess(); } } + private void RemoveClanFromRebelFaction(Clan clan, Kingdom oldKingdom, Kingdom newKingdom) { var rebelFactions = RebelFactionManager.GetRebelFaction(oldKingdom).ToList(); diff --git a/src/Bannerlord.Diplomacy/Cheats.cs b/src/Bannerlord.Diplomacy/Cheats.cs index 57a7e2aa..66b79e13 100644 --- a/src/Bannerlord.Diplomacy/Cheats.cs +++ b/src/Bannerlord.Diplomacy/Cheats.cs @@ -166,6 +166,9 @@ private static string SetWarExhaustion(List strings) if (!CampaignCheats.CheckCheatUsage(ref CampaignCheats.ErrorType)) return CampaignCheats.ErrorType; + if (!Settings.Instance!.EnableWarExhaustion) + return "War exhaustion is disabled!"; + var isNumeric = int.TryParse(strings[2], out var targetWarExhaustion); if (!CampaignCheats.CheckParameters(strings, 3) || CampaignCheats.CheckHelp(strings) || !isNumeric) @@ -199,7 +202,7 @@ private static string SetWarExhaustion(List strings) if (kingdom2 is null) return "2nd kingdom ID not found: " + b2; - WarExhaustionManager.Instance.AddDivineWarExhaustion(kingdom1, kingdom2, targetWarExhaustion); + WarExhaustionManager.Instance!.AddDivineWarExhaustion(kingdom1, kingdom2, targetWarExhaustion); return "done!"; } diff --git a/src/Bannerlord.Diplomacy/CivilWar/Actions/StartRebellionAction.cs b/src/Bannerlord.Diplomacy/CivilWar/Actions/StartRebellionAction.cs index 57d56691..c7bbeefa 100644 --- a/src/Bannerlord.Diplomacy/CivilWar/Actions/StartRebellionAction.cs +++ b/src/Bannerlord.Diplomacy/CivilWar/Actions/StartRebellionAction.cs @@ -51,7 +51,7 @@ public static void Apply(RebelFaction rebelFaction) ChangeKingdomBannerAction.Apply(rebelFaction.RebelKingdom!, true); - foreach (var clan in rebelFaction.Clans) + foreach (var clan in rebelFaction.Clans.ToList()) { if (clan.IsEliminated) { diff --git a/src/Bannerlord.Diplomacy/CivilWar/Factions/AbdicationFaction.cs b/src/Bannerlord.Diplomacy/CivilWar/Factions/AbdicationFaction.cs index d11facf5..1a065504 100644 --- a/src/Bannerlord.Diplomacy/CivilWar/Factions/AbdicationFaction.cs +++ b/src/Bannerlord.Diplomacy/CivilWar/Factions/AbdicationFaction.cs @@ -25,6 +25,8 @@ public AbdicationFaction(Clan sponsorClan) : base(sponsorClan) { } public override float MemberInfluenceOnFailure => -100f; + public override bool ConsolidateOnSuccess => true; + protected override void ApplyDemand() { var strVars = new Dictionary diff --git a/src/Bannerlord.Diplomacy/CivilWar/Factions/RebelFaction.cs b/src/Bannerlord.Diplomacy/CivilWar/Factions/RebelFaction.cs index 30014dd7..d83ad47a 100644 --- a/src/Bannerlord.Diplomacy/CivilWar/Factions/RebelFaction.cs +++ b/src/Bannerlord.Diplomacy/CivilWar/Factions/RebelFaction.cs @@ -49,6 +49,8 @@ public abstract class RebelFaction public abstract RebelDemandType RebelDemandType { get; } + public abstract bool ConsolidateOnSuccess { get; } + public float FactionStrength { get { return _participatingClans.Select(c => c.TotalStrength).Sum(); } diff --git a/src/Bannerlord.Diplomacy/CivilWar/Factions/SecessionFaction.cs b/src/Bannerlord.Diplomacy/CivilWar/Factions/SecessionFaction.cs index b42e5f92..6e226cfe 100644 --- a/src/Bannerlord.Diplomacy/CivilWar/Factions/SecessionFaction.cs +++ b/src/Bannerlord.Diplomacy/CivilWar/Factions/SecessionFaction.cs @@ -23,6 +23,8 @@ public SecessionFaction(Clan sponsorClan) : base(sponsorClan) { } public override float MemberInfluenceOnFailure => -100f; + public override bool ConsolidateOnSuccess => false; + protected override void ApplyDemand() { var kingdomName = FactionNameGenerator.GenerateKingdomName(this); diff --git a/src/Bannerlord.Diplomacy/CivilWar/RebelFactionManager.cs b/src/Bannerlord.Diplomacy/CivilWar/RebelFactionManager.cs index dd32f4d9..d7b782c4 100644 --- a/src/Bannerlord.Diplomacy/CivilWar/RebelFactionManager.cs +++ b/src/Bannerlord.Diplomacy/CivilWar/RebelFactionManager.cs @@ -1,5 +1,7 @@ using Diplomacy.CivilWar.Actions; using Diplomacy.CivilWar.Factions; +using Diplomacy.Helpers; +using Diplomacy.WarExhaustion; using JetBrains.Annotations; @@ -103,6 +105,16 @@ public static IEnumerable GetRebelFaction(Kingdom kingdom) return AllRebelFactions.Values.SelectMany(x => x).FirstOrDefault(rf => rebelKingdom == rf.RebelKingdom); } + public static Kingdom GetCivilWarLoser(Kingdom kingdomMakingPeace, Kingdom otherKingdom) + { + return (WarExhaustionManager.Instance?.GetWarResult(kingdomMakingPeace, otherKingdom) ?? WarExhaustionManager.WarResult.None) switch + { + WarExhaustionManager.WarResult.Tie when kingdomMakingPeace.Fiefs.Any() => TributeHelper.GetBaseValueForTrubute(kingdomMakingPeace, otherKingdom) < 0 ? otherKingdom : kingdomMakingPeace, + >= WarExhaustionManager.WarResult.PyrrhicVictory => otherKingdom, + _ => kingdomMakingPeace, + }; + } + internal void OnAfterSaveLoaded() { //Remove factions of dead kingdoms @@ -121,7 +133,7 @@ internal void OnAfterSaveLoaded() DestroyRebelFaction(faction); continue; } - foreach (var clan in faction.Clans) + foreach (var clan in faction.Clans.ToList()) { //Clear rest of the factions from dead clans if (clan.IsEliminated) faction.RemoveClan(clan); diff --git a/src/Bannerlord.Diplomacy/Costs/DiplomacyCostCalculator.cs b/src/Bannerlord.Diplomacy/Costs/DiplomacyCostCalculator.cs index 659730f9..eb3a8bf4 100644 --- a/src/Bannerlord.Diplomacy/Costs/DiplomacyCostCalculator.cs +++ b/src/Bannerlord.Diplomacy/Costs/DiplomacyCostCalculator.cs @@ -1,4 +1,5 @@ using Diplomacy.CivilWar.Factions; +using Diplomacy.DiplomaticAction.WarPeace; using Diplomacy.Extensions; using Diplomacy.WarExhaustion; @@ -42,7 +43,7 @@ internal static InfluenceCost DetermineInfluenceCostForMakingPeace(Kingdom kingd return new InfluenceCost(clanPayingInfluence, 0f); //Making peace is free for critically exhausted factions that not yet lost - if (Settings.Instance!.EnableWarExhaustion && WarExhaustionManager.Instance.HasCriticalWarExhaustion(kingdomMakingPeace, otherKingdom, checkMaxWarExhaustion: true)) + if (Settings.Instance!.EnableWarExhaustion && WarExhaustionManager.Instance!.HasCriticalWarExhaustion(kingdomMakingPeace, otherKingdom, checkMaxWarExhaustion: true)) return new InfluenceCost(clanPayingInfluence, 0f); if (!Settings.Instance!.ScalingInfluenceCosts) @@ -72,14 +73,15 @@ private static KingdomWalletCost DetermineReparationsForMakingPeace(Kingdom king if (!Settings.Instance!.EnableWarExhaustion) return new(giver, receiver, reparationsCost); - //No reparations from kingdoms that are about to be destroyed - if (Settings.Instance!.EnableKingdomElimination && kingdomMakingPeace.Fiefs.Count == 0 && !FactionManager.GetEnemyKingdoms(kingdomMakingPeace).Any(k => k != otherKingdom && !k.IsEliminated)) + //No reparations between kingdoms that are about to be destroyed + var shouldBeDestroyed = KingdomPeaceAction.ShouldKingdomsBeDestroyed(kingdomMakingPeace, otherKingdom); + if (shouldBeDestroyed.KingdomMakingPeace || shouldBeDestroyed.OtherKingdom) return new(giver, receiver, reparationsCost); if (kingdomMakingPeace.IsRebelKingdomOf(otherKingdom) || (otherKingdom.IsRebelKingdomOf(kingdomMakingPeace) && kingdomMakingPeace.GetRebelFactions().Any(x => x.RebelKingdom == otherKingdom && x is not SecessionFaction))) return new(giver, receiver, reparationsCost); - var kingdomMakingPeaceWarExhaustion = WarExhaustionManager.Instance.GetWarExhaustion(kingdomMakingPeace, otherKingdom); - var otherKingdomWarExhaustion = WarExhaustionManager.Instance.GetWarExhaustion(otherKingdom, kingdomMakingPeace); + var kingdomMakingPeaceWarExhaustion = WarExhaustionManager.Instance!.GetWarExhaustion(kingdomMakingPeace, otherKingdom); + var otherKingdomWarExhaustion = WarExhaustionManager.Instance!.GetWarExhaustion(otherKingdom, kingdomMakingPeace); if (kingdomMakingPeaceWarExhaustion > otherKingdomWarExhaustion && WarExhaustionManager.IsCriticalWarExhaustion(kingdomMakingPeaceWarExhaustion)) { @@ -91,14 +93,14 @@ private static KingdomWalletCost DetermineReparationsForMakingPeace(Kingdom king } else if (kingdomMakingPeaceWarExhaustion == otherKingdomWarExhaustion) { - var otherKingdomLost = WarExhaustionManager.Instance.GetWarResult(otherKingdom, kingdomMakingPeace) == WarExhaustionManager.WarResult.Loss; + var otherKingdomLost = WarExhaustionManager.Instance!.GetWarResult(otherKingdom, kingdomMakingPeace) == WarExhaustionManager.WarResult.Loss; if (otherKingdomLost) { GetReparationsFromOtherKingdom(kingdomMakingPeace, otherKingdom, out giver, out receiver, out reparationsCost, kingdomMakingPeaceWarExhaustion, otherKingdomWarExhaustion, otherKingdomLost); } else { - var kingdomMakingPeaceLost = WarExhaustionManager.Instance.GetWarResult(kingdomMakingPeace, otherKingdom) == WarExhaustionManager.WarResult.Loss; + var kingdomMakingPeaceLost = WarExhaustionManager.Instance!.GetWarResult(kingdomMakingPeace, otherKingdom) == WarExhaustionManager.WarResult.Loss; if (kingdomMakingPeaceLost) reparationsCost = CalculateReparations(kingdomMakingPeace, otherKingdom, kingdomMakingPeaceWarExhaustion, otherKingdomWarExhaustion, kingdomMakingPeaceLost); } @@ -109,8 +111,8 @@ private static KingdomWalletCost DetermineReparationsForMakingPeace(Kingdom king static int CalculateReparations(Kingdom kingdomPayingReparations, Kingdom otherKingdom, float payingKingdomWarExhaustion, float otherKingdomWarExhaustion, bool? payerLostWar = null) { - var lossReparations = (payerLostWar ?? WarExhaustionManager.Instance.GetWarResult(kingdomPayingReparations, otherKingdom) == WarExhaustionManager.WarResult.Loss) ? Settings.Instance!.DefeatedGoldCost : 0; - var warExhaustionReparations = otherKingdomWarExhaustion * ((payingKingdomWarExhaustion - otherKingdomWarExhaustion) / (WarExhaustionManager.IsCriticalWarExhaustion(otherKingdomWarExhaustion) ? 20f : 10f)); + var lossReparations = (payerLostWar ?? WarExhaustionManager.Instance!.GetWarResult(kingdomPayingReparations, otherKingdom) == WarExhaustionManager.WarResult.Loss) ? Settings.Instance!.DefeatedGoldCost : 0; + var warExhaustionReparations = otherKingdomWarExhaustion * ((payingKingdomWarExhaustion - otherKingdomWarExhaustion) / (WarExhaustionManager.IsCriticalWarExhaustion(otherKingdomWarExhaustion) ? 10f : 5f)); return (int) ((lossReparations + warExhaustionReparations) * GetKingdomScalingFactorForReparations(kingdomPayingReparations)); } diff --git a/src/Bannerlord.Diplomacy/Costs/KingdomWalletCost.cs b/src/Bannerlord.Diplomacy/Costs/KingdomWalletCost.cs index 6af56fc7..4f785604 100644 --- a/src/Bannerlord.Diplomacy/Costs/KingdomWalletCost.cs +++ b/src/Bannerlord.Diplomacy/Costs/KingdomWalletCost.cs @@ -17,7 +17,7 @@ public sealed class KingdomWalletCost : AbstractDiplomacyCost public Kingdom? ReceivingKingdom => _receiver; public WalletType ReceiverWallet => _receiverWallet; - public KingdomWalletCost(Kingdom? payer, Kingdom? receiver, float value, WalletType payerWallet = WalletType.TributeWallet, WalletType receiverWallet = WalletType.BudgetWallet) : base(value) + public KingdomWalletCost(Kingdom? payer, Kingdom? receiver, float value, WalletType payerWallet = WalletType.ReparationsWallet, WalletType receiverWallet = WalletType.ReparationsWallet) : base(value) { _payer = payer; _payerWallet = payerWallet; diff --git a/src/Bannerlord.Diplomacy/DiplomaticAction/WarPeace/KingdomPeaceAction.cs b/src/Bannerlord.Diplomacy/DiplomaticAction/WarPeace/KingdomPeaceAction.cs index 683cb642..7b3ded98 100644 --- a/src/Bannerlord.Diplomacy/DiplomaticAction/WarPeace/KingdomPeaceAction.cs +++ b/src/Bannerlord.Diplomacy/DiplomaticAction/WarPeace/KingdomPeaceAction.cs @@ -1,6 +1,7 @@ using Bannerlord.ButterLib.Common.Helpers; using Diplomacy.Actions; +using Diplomacy.CivilWar; using Diplomacy.CivilWar.Factions; using Diplomacy.Costs; using Diplomacy.Events; @@ -17,6 +18,7 @@ using TaleWorlds.CampaignSystem; using TaleWorlds.CampaignSystem.Actions; +using TaleWorlds.CampaignSystem.GameState; using TaleWorlds.CampaignSystem.Settlements; using TaleWorlds.Core; using TaleWorlds.Library; @@ -51,7 +53,7 @@ internal sealed class KingdomPeaceAction private const string _noChoice = "{=13G0c8RE}Given how badly your kingdom has been ravaged by the war, you have no choice but to accept the peace."; - private static void ApplyPeaceInternal(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, bool skipPlayerPrompts, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed) + private static void ApplyPeaceInternal(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, bool skipPlayerPrompts, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationOnPeace shouldBeDestroyed) { if (kingdomMakingPeace == Clan.PlayerClan.Kingdom && !skipPlayerPrompts) { @@ -67,7 +69,7 @@ private static void ApplyPeaceInternal(Kingdom kingdomMakingPeace, Kingdom other } } - private static void NotifyPlayerOfPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed) + private static void NotifyPlayerOfPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationOnPeace shouldBeDestroyed) { GetNotificationInquiryTitleAndBody(kingdomMakingPeace, otherKingdom, isATie, diplomacyCost, dailyPeaceTributeToPay, fiefsToBeReturned, hasFiefsRemaining, shouldBeDestroyed, out var inquiryBody, out var inquiryTitle); @@ -82,7 +84,7 @@ private static void NotifyPlayerOfPeace(Kingdom kingdomMakingPeace, Kingdom othe null), true); } - private static void GetNotificationInquiryTitleAndBody(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed, out TextObject inquiryBody, out TextObject inquiryTitle) + private static void GetNotificationInquiryTitleAndBody(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationOnPeace shouldBeDestroyed, out TextObject inquiryBody, out TextObject inquiryTitle) { var rebelIsMakingPeace = kingdomMakingPeace.IsRebelKingdomOf(otherKingdom); var originalIsMakingPeace = otherKingdom.IsRebelKingdomOf(kingdomMakingPeace); @@ -94,7 +96,7 @@ private static void GetNotificationInquiryTitleAndBody(Kingdom kingdomMakingPeac {"ORGANIZATIONAL_EXPENSES", diplomacyCost.GoldCost.Value}, {"REBEL_KINGDOM", rebelIsMakingPeace? kingdomMakingPeace.Name : otherKingdom.Name}, {"ORIGINAL_KINGDOM", rebelIsMakingPeace? otherKingdom.Name : kingdomMakingPeace.Name}, - {"TO_BE_DESTROYED", shouldBeDestroyed ? 1 : 0}, + {"TO_BE_DESTROYED", shouldBeDestroyed.KingdomMakingPeace ? 1 : 0}, {"NEW_LINE", Environment.NewLine} }; @@ -113,7 +115,7 @@ private static void GetNotificationInquiryTitleAndBody(Kingdom kingdomMakingPeac {"INFLUENCE", diplomacyCost.InfluenceCost.Value}, {"ENEMY_KINGDOM", otherKingdom.Name}, {"ADD_TRIBUTE", dailyPeaceTributeToPay != 0 ? 1 : 0}, - {"TO_BE_DESTROYED", shouldBeDestroyed ? 1 : 0}, + {"TO_BE_DESTROYED", shouldBeDestroyed.KingdomMakingPeace ? 1 : 0}, {"IS_REBELLION", rebelIsMakingPeace ? 1 : 0}, {"NEW_LINE", Environment.NewLine} }; @@ -140,12 +142,13 @@ private static void GetNotificationInquiryTitleAndBody(Kingdom kingdomMakingPeac } } - private static void AcceptPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, EliminationStatus shouldBeDestroyed) + private static void AcceptPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, EliminationOnPeace shouldBeDestroyed) { diplomacyCost.ApplyCost(); DoReturnFiefs(kingdomMakingPeace, otherKingdom, fiefsToBeReturned); MakePeaceAction.Apply(kingdomMakingPeace, otherKingdom, dailyPeaceTributeToPay); - if (shouldBeDestroyed) DestroyKingdomSoftlyAction.Apply(kingdomMakingPeace); + if (shouldBeDestroyed.KingdomMakingPeace) SoftlyDestroyKingdomAction.Apply(kingdomMakingPeace); + if (shouldBeDestroyed.OtherKingdom) SoftlyDestroyKingdomAction.Apply(otherKingdom); DoLogging(kingdomMakingPeace, otherKingdom, diplomacyCost, dailyPeaceTributeToPay); } @@ -174,7 +177,7 @@ private static void DoReturnFiefs(Kingdom kingdomMakingPeace, Kingdom otherKingd } } - private static void CreatePeaceInquiry(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed) + private static void CreatePeaceInquiry(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationOnPeace shouldBeDestroyed) { #if v100 || v101 || v102 || v103 InformationManager.ShowInquiry(new InquiryData( @@ -200,7 +203,7 @@ private static void CreatePeaceInquiry(Kingdom kingdomMakingPeace, Kingdom other #endif } - private static string GetPeaceInquiryText(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationStatus shouldBeDestroyed) + private static string GetPeaceInquiryText(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool isATie, HybridCost diplomacyCost, int dailyPeaceTributeToPay, List fiefsToBeReturned, bool hasFiefsRemaining, EliminationOnPeace shouldBeDestroyed) { var rebelIsMakingPeace = kingdomMakingPeace.IsRebelKingdomOf(otherKingdom); var originalIsMakingPeace = otherKingdom.IsRebelKingdomOf(kingdomMakingPeace); @@ -211,7 +214,7 @@ private static string GetPeaceInquiryText(Kingdom kingdomMakingPeace, Kingdom ot { {"ORGANIZATIONAL_EXPENSES", diplomacyCost.GoldCost.Value}, {"REBEL_KINGDOM", rebelIsMakingPeace? kingdomMakingPeace.Name : otherKingdom.Name}, - {"TO_BE_DESTROYED", shouldBeDestroyed ? 1 : 0}, + {"TO_BE_DESTROYED", shouldBeDestroyed.KingdomMakingPeace ? 1 : 0}, {"NEW_LINE", Environment.NewLine} }; @@ -231,7 +234,7 @@ private static string GetPeaceInquiryText(Kingdom kingdomMakingPeace, Kingdom ot {"GOLD_ICON", StringConstants.GoldIcon}, {"PLAYER_KINGDOM", otherKingdom.Name}, {"ADD_TRIBUTE", dailyPeaceTributeToPay != 0 ? 1 : 0}, - {"TO_BE_DESTROYED", shouldBeDestroyed ? 1 : 0}, + {"TO_BE_DESTROYED", shouldBeDestroyed.KingdomMakingPeace ? 1 : 0}, {"IS_REBELLION", rebelIsMakingPeace ? 1 : 0}, {"NEW_LINE", Environment.NewLine} }; @@ -250,7 +253,7 @@ private static string GetPeaceInquiryText(Kingdom kingdomMakingPeace, Kingdom ot #if v100 || v101 || v102 || v103 private static bool IsDeclineAvailable(Kingdom kingdomMakingPeace, Kingdom otherKingdom) { - var warResultForOtherKingdom = WarExhaustionManager.Instance.GetWarResult(otherKingdom, kingdomMakingPeace); + var warResultForOtherKingdom = WarExhaustionManager.Instance?.GetWarResult(otherKingdom, kingdomMakingPeace) ?? WarExhaustionManager.WarResult.None; return warResultForOtherKingdom switch { > WarExhaustionManager.WarResult.None and <= WarExhaustionManager.WarResult.PyrrhicVictory => false, @@ -260,7 +263,7 @@ private static bool IsDeclineAvailable(Kingdom kingdomMakingPeace, Kingdom other #else private static (bool, string) IsDeclineAvailable(Kingdom kingdomMakingPeace, Kingdom otherKingdom) { - var warResultForOtherKingdom = WarExhaustionManager.Instance.GetWarResult(otherKingdom, kingdomMakingPeace); + var warResultForOtherKingdom = WarExhaustionManager.Instance?.GetWarResult(otherKingdom, kingdomMakingPeace) ?? WarExhaustionManager.WarResult.None; return warResultForOtherKingdom switch { > WarExhaustionManager.WarResult.None and <= WarExhaustionManager.WarResult.PyrrhicVictory => (false, new TextObject(_noChoice).ToString()), @@ -269,29 +272,34 @@ private static (bool, string) IsDeclineAvailable(Kingdom kingdomMakingPeace, Kin } #endif - public static void ApplyPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool forcePlayerCharacterCosts = false, bool skipPlayerPrompts = false) + public static void ApplyPeace(Kingdom kingdomMakingPeace, Kingdom otherKingdom, int? dailyTribute = null, bool forcePlayerCharacterCosts = false, bool skipPlayerPrompts = false) { var diplomacyCost = DiplomacyCostCalculator.DetermineCostForMakingPeace(kingdomMakingPeace, otherKingdom, forcePlayerCharacterCosts); - var dailyPeaceTributeToPay = TributeHelper.GetDailyTribute(kingdomMakingPeace, otherKingdom); - var isATie = WarExhaustionManager.Instance.GetWarResult(kingdomMakingPeace, otherKingdom) == WarExhaustionManager.WarResult.Tie; + var dailyPeaceTributeToPay = dailyTribute ?? TributeHelper.GetDailyTribute(kingdomMakingPeace, otherKingdom); + var isATie = (WarExhaustionManager.Instance?.GetWarResult(kingdomMakingPeace, otherKingdom) ?? WarExhaustionManager.WarResult.None) == WarExhaustionManager.WarResult.Tie; var fiefsToBeReturned = GetFiefsToBeReturned(kingdomMakingPeace, otherKingdom); var hasFiefsRemaining = kingdomMakingPeace.Fiefs.Count > 0; - var shouldBeDestroyed = ShouldKingdomsBeDestroyed(kingdomMakingPeace, otherKingdom, hasFiefsRemaining); + var shouldBeDestroyed = ShouldKingdomsBeDestroyed(kingdomMakingPeace, otherKingdom); + + if (kingdomMakingPeace == Clan.PlayerClan.Kingdom && shouldBeDestroyed.KingdomMakingPeace && Game.Current.GameStateManager.ActiveState is KingdomState) + { + Game.Current.GameStateManager.PopState(); + } ApplyPeaceInternal(kingdomMakingPeace, otherKingdom, isATie, skipPlayerPrompts, diplomacyCost, dailyPeaceTributeToPay, fiefsToBeReturned, hasFiefsRemaining, shouldBeDestroyed); } - public static EliminationStatus ShouldKingdomsBeDestroyed(Kingdom kingdomMakingPeace, Kingdom otherKingdom, bool? hasFiefsRemaining = null) + public static EliminationOnPeace ShouldKingdomsBeDestroyed(Kingdom kingdomMakingPeace, Kingdom otherKingdom) { - return (true, true); - /* - return - Settings.Instance!.EnableKingdomElimination && !(hasFiefsRemaining ?? kingdomMakingPeace.Fiefs.Count > 0) - && !FactionManager.GetEnemyKingdoms(kingdomMakingPeace).Any(k => k != otherKingdom && !k.IsEliminated) - && (!otherKingdom.IsRebelKingdomOf(kingdomMakingPeace) || !otherKingdom.Fiefs.Any()); - */ + var loserKingdom = RebelFactionManager.GetCivilWarLoser(kingdomMakingPeace, otherKingdom); + return (ShouldKingdomBeDestroyed(kingdomMakingPeace, otherKingdom, loserKingdom), ShouldKingdomBeDestroyed(otherKingdom, kingdomMakingPeace, loserKingdom)); } + private static bool ShouldKingdomBeDestroyed(Kingdom kingdomInQuestion, Kingdom otherKingdom, Kingdom loserKingdom) => + Settings.Instance!.EnableKingdomElimination && kingdomInQuestion.Fiefs.Count <= 0 + && !FactionManager.GetEnemyKingdoms(kingdomInQuestion).Any(k => k != otherKingdom && !k.IsEliminated) + && (!kingdomInQuestion.WillBeConsolidatedWith(otherKingdom, loserKingdom) || otherKingdom.Fiefs.Count <= 0); + private static List GetFiefsToBeReturned(Kingdom kingdomMakingPeace, Kingdom otherKingdom) { if (!ShouldAnyFiefsBeReturned(kingdomMakingPeace, otherKingdom, out var warResultForOtherKingdom)) @@ -325,7 +333,7 @@ public static List GetFiefsSuitableToBeReturned(Kingdom kingdomMakingPeace private static bool ShouldAnyFiefsBeReturned(Kingdom kingdomMakingPeace, Kingdom otherKingdom, out WarExhaustionManager.WarResult warResultForOtherKingdom) { warResultForOtherKingdom = WarExhaustionManager.WarResult.None; - if (!Settings.Instance!.EnableWarExhaustion || !WarExhaustionManager.Instance.HasMaxWarExhaustion(kingdomMakingPeace, otherKingdom)) + if (!Settings.Instance!.EnableWarExhaustion || !Settings.Instance!.EnableFiefRepatriation || !WarExhaustionManager.Instance!.HasMaxWarExhaustion(kingdomMakingPeace, otherKingdom)) return false; if (!kingdomMakingPeace.Fiefs.Any()) @@ -334,7 +342,7 @@ private static bool ShouldAnyFiefsBeReturned(Kingdom kingdomMakingPeace, Kingdom if (kingdomMakingPeace.IsRebelKingdomOf(otherKingdom) || (otherKingdom.IsRebelKingdomOf(kingdomMakingPeace) && kingdomMakingPeace.GetRebelFactions().Any(x => x.RebelKingdom == otherKingdom && x is not SecessionFaction))) return false; - warResultForOtherKingdom = WarExhaustionManager.Instance.GetWarResult(otherKingdom, kingdomMakingPeace); + warResultForOtherKingdom = WarExhaustionManager.Instance!.GetWarResult(otherKingdom, kingdomMakingPeace); if (warResultForOtherKingdom <= WarExhaustionManager.WarResult.PyrrhicVictory) return false; @@ -343,7 +351,7 @@ private static bool ShouldAnyFiefsBeReturned(Kingdom kingdomMakingPeace, Kingdom private static List GetFiefsSuitableToBeReturnedInternal(Kingdom kingdomMakingPeace, Kingdom otherKingdom) { - var eventRecords = WarExhaustionManager.Instance.GetWarExhaustionEventRecords(kingdomMakingPeace, otherKingdom, out var kingdoms); + var eventRecords = WarExhaustionManager.Instance!.GetWarExhaustionEventRecords(kingdomMakingPeace, otherKingdom, out var kingdoms); if (kingdoms is null || eventRecords.IsEmpty()) return new(); @@ -352,43 +360,43 @@ private static List GetFiefsSuitableToBeReturnedInternal(Kingdom kingdomMa } } - internal struct EliminationStatus + public struct EliminationOnPeace { - public bool DestroyKingdomMakingPeace; - public bool DestroyOtherKingdom; + public bool KingdomMakingPeace; + public bool OtherKingdom; - public EliminationStatus(bool destroyKingdomMakingPeace, bool destroyOtherKingdom) + public EliminationOnPeace(bool destroyKingdomMakingPeace, bool destroyOtherKingdom) { - DestroyKingdomMakingPeace = destroyKingdomMakingPeace; - DestroyOtherKingdom = destroyOtherKingdom; + KingdomMakingPeace = destroyKingdomMakingPeace; + OtherKingdom = destroyOtherKingdom; } public override bool Equals(object? obj) { - return obj is EliminationStatus other && - DestroyKingdomMakingPeace == other.DestroyKingdomMakingPeace && - DestroyOtherKingdom == other.DestroyOtherKingdom; + return obj is EliminationOnPeace other && + KingdomMakingPeace == other.KingdomMakingPeace && + OtherKingdom == other.OtherKingdom; } public override int GetHashCode() { - return HashCode.Combine(DestroyKingdomMakingPeace, DestroyOtherKingdom); + return HashCode.Combine(KingdomMakingPeace, OtherKingdom); } public void Deconstruct(out bool destroyKingdomMakingPeace, out bool destroyOtherKingdom) { - destroyKingdomMakingPeace = DestroyKingdomMakingPeace; - destroyOtherKingdom = DestroyOtherKingdom; + destroyKingdomMakingPeace = KingdomMakingPeace; + destroyOtherKingdom = OtherKingdom; } - public static implicit operator (bool DestroyKingdomMakingPeace, bool DestroyOtherKingdom)(EliminationStatus value) + public static implicit operator (bool DestroyKingdomMakingPeace, bool DestroyOtherKingdom)(EliminationOnPeace value) { - return (value.DestroyKingdomMakingPeace, value.DestroyOtherKingdom); + return (value.KingdomMakingPeace, value.OtherKingdom); } - public static implicit operator EliminationStatus((bool DestroyKingdomMakingPeace, bool DestroyOtherKingdom) value) + public static implicit operator EliminationOnPeace((bool DestroyKingdomMakingPeace, bool DestroyOtherKingdom) value) { - return new EliminationStatus(value.DestroyKingdomMakingPeace, value.DestroyOtherKingdom); + return new EliminationOnPeace(value.DestroyKingdomMakingPeace, value.DestroyOtherKingdom); } } } \ No newline at end of file diff --git a/src/Bannerlord.Diplomacy/Extensions/KingdomExtensions.cs b/src/Bannerlord.Diplomacy/Extensions/KingdomExtensions.cs index a4765fd3..2ece5459 100644 --- a/src/Bannerlord.Diplomacy/Extensions/KingdomExtensions.cs +++ b/src/Bannerlord.Diplomacy/Extensions/KingdomExtensions.cs @@ -83,6 +83,27 @@ public static bool IsRebelKingdomOf(this Kingdom kingdom, Kingdom parentKingdom) public static bool HasRebellion(this Kingdom kingdom) => GetRebelFactions(kingdom).Any(x => x.AtWar); + public static bool WillBeConsolidatedWith(this Kingdom kingdom, Kingdom otherKingdom, Kingdom losingKingdom) + { + if (kingdom.IsRebelKingdomOf(otherKingdom)) + { + return ShouldConsolidate(kingdom, otherKingdom, losingKingdom); + } + else if (otherKingdom.IsRebelKingdomOf(kingdom)) + { + return ShouldConsolidate(otherKingdom, kingdom, losingKingdom); + } + else + { + return false; + } + + static bool ShouldConsolidate(Kingdom rebelKingdom, Kingdom parentKingdom, Kingdom losingKingdom) + { + return rebelKingdom == losingKingdom || parentKingdom.GetRebelFactions().Any(x => x.RebelKingdom == rebelKingdom && x.ConsolidateOnSuccess); + } + } + public static IEnumerable GetRebelFactions(this Kingdom kingdom) => RebelFactionManager.GetRebelFaction(kingdom); } } \ No newline at end of file diff --git a/src/Bannerlord.Diplomacy/Helpers/TributeHelper.cs b/src/Bannerlord.Diplomacy/Helpers/TributeHelper.cs index 33d177a6..8d49a29c 100644 --- a/src/Bannerlord.Diplomacy/Helpers/TributeHelper.cs +++ b/src/Bannerlord.Diplomacy/Helpers/TributeHelper.cs @@ -18,7 +18,7 @@ internal static int GetDailyTribute(Kingdom kingdomInQuestion, Kingdom otherKing if (!atWar) { var stance = kingdomInQuestion.GetStanceWith(otherKingdom); - return stance.GetDailyTributePaid(kingdomInQuestion); + return stance?.GetDailyTributePaid(kingdomInQuestion) ?? 0; } if (kingdomInQuestion == Clan.PlayerClan.Kingdom) @@ -33,20 +33,11 @@ internal static int GetDailyTribute(Kingdom kingdomInQuestion, Kingdom otherKing if (kingdomInQuestion.IsRebelKingdomOf(otherKingdom) || otherKingdom.IsRebelKingdomOf(kingdomInQuestion)) return 0; - var peaceBarterable = new PeaceBarterable(kingdomInQuestion.Leader, kingdomInQuestion, otherKingdom, CampaignTime.Years(1f)); - var valueForOtherKingdom = -peaceBarterable.GetValueForFaction(otherKingdom); - foreach (Clan clan in otherKingdom.Clans) - { - var valueForClan = -peaceBarterable.GetValueForFaction(clan); - if (valueForClan > valueForOtherKingdom) - valueForOtherKingdom = valueForClan; - } - if (valueForOtherKingdom > -5000 && valueForOtherKingdom < 5000) - valueForOtherKingdom = 0; + int valueForOtherKingdom = GetBaseValueForTrubute(kingdomInQuestion, otherKingdom); if (Settings.Instance!.EnableWarExhaustion) { - var warResult = Instance.GetWarResult(kingdomInQuestion, otherKingdom); + var warResult = Instance!.GetWarResult(kingdomInQuestion, otherKingdom); var warResultForOtherKingdom = Instance.GetWarResult(otherKingdom, kingdomInQuestion); //Neither kingdom should get any tribute when there's a tie or PyrrhicVictory if (Min(warResult, warResultForOtherKingdom) != WarResult.None && Max(warResult, warResultForOtherKingdom) <= WarResult.PyrrhicVictory) @@ -58,7 +49,7 @@ internal static int GetDailyTribute(Kingdom kingdomInQuestion, Kingdom otherKing { valueForOtherKingdom = 0; } - //...and winning kingdom should not pay a tribute to the looser + //...and winning kingdom should not pay a tribute to the loser if (valueForOtherKingdom > 0 && warResult > WarResult.PyrrhicVictory && warResultForOtherKingdom == WarResult.Loss) { valueForOtherKingdom = 0; @@ -67,5 +58,20 @@ internal static int GetDailyTribute(Kingdom kingdomInQuestion, Kingdom otherKing var dailyPeaceTributeToPay = Campaign.Current.Models.DiplomacyModel.GetDailyTributeForValue(valueForOtherKingdom); return 10 * (dailyPeaceTributeToPay / 10); } + + internal static int GetBaseValueForTrubute(Kingdom kingdomInQuestion, Kingdom otherKingdom) + { + var peaceBarterable = new PeaceBarterable(kingdomInQuestion.Leader, kingdomInQuestion, otherKingdom, CampaignTime.Years(1f)); + var valueForOtherKingdom = -peaceBarterable.GetValueForFaction(otherKingdom); + foreach (Clan clan in otherKingdom.Clans) + { + var valueForClan = -peaceBarterable.GetValueForFaction(clan); + if (valueForClan > valueForOtherKingdom) + valueForOtherKingdom = valueForClan; + } + if (valueForOtherKingdom > -5000 && valueForOtherKingdom < 5000) + valueForOtherKingdom = 0; + return valueForOtherKingdom; + } } } \ No newline at end of file diff --git a/src/Bannerlord.Diplomacy/PatchTools/PatchManager.cs b/src/Bannerlord.Diplomacy/PatchTools/PatchManager.cs index 7a71fc7a..aae3ca14 100644 --- a/src/Bannerlord.Diplomacy/PatchTools/PatchManager.cs +++ b/src/Bannerlord.Diplomacy/PatchTools/PatchManager.cs @@ -95,6 +95,7 @@ private PatchManager(string harmonyId, bool useMainPatches = true) new DefaultEncyclopediaFactionPagePatch(), new KingdomManagementVMPatch(), new MBBannerEditorGauntletScreenPatch(), + new MakePeaceKingdomDecisionPatch() }; // REGISTER ALL ACTIVE HARMONY PATCH CLASSES TO USE OnGameStart HERE: diff --git a/src/Bannerlord.Diplomacy/Patches/MakePeaceKingdomDecisionPatch.cs b/src/Bannerlord.Diplomacy/Patches/MakePeaceKingdomDecisionPatch.cs new file mode 100644 index 00000000..aaeeffc2 --- /dev/null +++ b/src/Bannerlord.Diplomacy/Patches/MakePeaceKingdomDecisionPatch.cs @@ -0,0 +1,30 @@ +using Diplomacy.DiplomaticAction.WarPeace; +using Diplomacy.PatchTools; + +using System.Collections.Generic; + +using TaleWorlds.CampaignSystem; +using TaleWorlds.CampaignSystem.Election; + +namespace Diplomacy.Patches +{ + internal sealed class MakePeaceKingdomDecisionPatch : PatchClass + { + protected override IEnumerable Prepare() => new Patch[] + { + new Prefix(nameof(ApplyChosenOutcomePrefix), "ApplyChosenOutcome") + }; + + private static bool ApplyChosenOutcomePrefix(DecisionOutcome chosenOutcome, MakePeaceKingdomDecision __instance, bool ____applyResults) + { + if (__instance.FactionToMakePeaceWith is not Kingdom kingdomToMakePeaceWith) + return true; + + if (!____applyResults || !((MakePeaceKingdomDecision.MakePeaceDecisionOutcome) chosenOutcome).ShouldPeaceBeDeclared) + return false; + + KingdomPeaceAction.ApplyPeace(__instance.Kingdom, kingdomToMakePeaceWith, dailyTribute: __instance.DailyTributeToBePaid, skipPlayerPrompts: true); + return false; + } + } +} \ No newline at end of file diff --git a/src/Bannerlord.Diplomacy/Settings.cs b/src/Bannerlord.Diplomacy/Settings.cs index 02384f2c..b6f494c0 100644 --- a/src/Bannerlord.Diplomacy/Settings.cs +++ b/src/Bannerlord.Diplomacy/Settings.cs @@ -38,7 +38,7 @@ class Settings : AttributeGlobalSettings [SettingPropertyGroup(HeadingKingdomDiplomacy)] public bool EnableFiefFirstRight { get; set; } = true; - [SettingPropertyBool("{=}Enable Fiefless Kingdom Elimination", Order = 10, RequireRestart = false, HintText = "{=}If enabled, kingdoms without any fiefs are destroyed when they are sign peace treaty ending the last ongoing war they participate in. Default value is enabled.")] + [SettingPropertyBool("{=8VKC3jtN}Enable Fiefless Kingdom Elimination", Order = 10, RequireRestart = false, HintText = "{=TlymwwPZ}If enabled, kingdoms without any fiefs are destroyed when they sign a peace treaty ending the last ongoing war they participate in. Default value is enabled.")] [SettingPropertyGroup(HeadingKingdomDiplomacy)] public bool EnableKingdomElimination { get; set; } = true; @@ -106,6 +106,10 @@ class Settings : AttributeGlobalSettings [SettingPropertyGroup(HeadingWarExhaustion)] public bool IndividualWarExhaustionRates { get; set; } = true; + [SettingPropertyBool("{=gW3eVr5E}Enable Fief Repatriation", Order = 3, RequireRestart = false, HintText = "{=KEi0UykN}If enabled, kingdoms may have to return some of their conquered fiefs back to the original owner if they lose the war substantially. Default value is enabled.")] + [SettingPropertyGroup(HeadingWarExhaustion)] + public bool EnableFiefRepatriation { get; set; } = true; + [SettingPropertyFloatingInteger("{=8TFQWL55}War Exhaustion Per Day", 0f, 5f, "0.00\\%", Order = 10, RequireRestart = false, HintText = "{=lgza5wDq}The amount of war exhaustion added per day a war is ongoing. Not affected by war exhaustion rate. Default value is 0.25%.")] [SettingPropertyGroup(HeadingWarExhaustion)] public float WarExhaustionPerDay { get; set; } = 0.25f; @@ -118,13 +122,13 @@ class Settings : AttributeGlobalSettings [SettingPropertyGroup(HeadingWarExhaustion)] public float WarExhaustionPerCasualty { get; set; } = 0.02f; - [SettingPropertyFloatingInteger("{=kr5zAufg}War Exhaustion Per Caravan Raid", 0f, 50f, "0.00\\%", Order = 25, RequireRestart = false, HintText = "{=PinVMCUE}The amount of war exhaustion added when a faction's caravan is raided. Default value is 3.0%.")] + [SettingPropertyFloatingInteger("{=kr5zAufg}War Exhaustion Per Caravan Raid", 0f, 50f, "0.00\\%", Order = 25, RequireRestart = false, HintText = "{=PinVMCUE}The amount of war exhaustion added when a faction's caravan is raided. Default value is 2.0%.")] [SettingPropertyGroup(HeadingWarExhaustion)] - public float WarExhaustionPerCaravanRaid { get; set; } = 3f; + public float WarExhaustionPerCaravanRaid { get; set; } = 2f; - [SettingPropertyFloatingInteger("{=qFJ23KxQ}War Exhaustion Per Hero Imprisoned", 0f, 50f, "0.00\\%", Order = 30, RequireRestart = false, HintText = "{=wctCn9uO}The base amount of war exhaustion added when a faction's noble hero is imprisoned. Affected by the hero significance for the faction. Potentially subject to diminishing returns. Default value is 2.0%.")] + [SettingPropertyFloatingInteger("{=qFJ23KxQ}War Exhaustion Per Hero Imprisoned", 0f, 50f, "0.00\\%", Order = 30, RequireRestart = false, HintText = "{=wctCn9uO}The base amount of war exhaustion added when a faction's noble hero is imprisoned. Affected by the hero significance for the faction. Potentially subject to diminishing returns. Default value is 1.0%.")] [SettingPropertyGroup(HeadingWarExhaustion)] - public float WarExhaustionPerImprisonment { get; set; } = 2f; + public float WarExhaustionPerImprisonment { get; set; } = 1f; [SettingPropertyFloatingInteger("{=4vTzbsXD}War Exhaustion Per Hero Perished", 0f, 50f, "0.00\\%", Order = 35, RequireRestart = false, HintText = "{=w80tUaVd}The base amount of war exhaustion added when a faction's noble hero is killed. Affected by the hero significance for the faction. Potentially subject to diminishing returns when multiple heroes of the same clan are killed. Default value is 5.0%.")] [SettingPropertyGroup(HeadingWarExhaustion)] @@ -138,7 +142,7 @@ class Settings : AttributeGlobalSettings [SettingPropertyGroup(HeadingWarExhaustion)] public float WarExhaustionPerSiege { get; set; } = 10f; - [SettingPropertyFloatingInteger("{=JmUPtZdw}War Exhaustion When Occupied", 0f, 50f, "0.00\\%", Order = 50, RequireRestart = false, HintText = "{=541jGrpb}The amount of war exhaustion added when a faction's lost all fiefs. Not affected by war exhaustion rate. Potentially subject to diminishing returns. Default value is 15.0%.")] + [SettingPropertyFloatingInteger("{=JmUPtZdw}War Exhaustion When Occupied", 0f, 50f, "0.00\\%", Order = 50, RequireRestart = false, HintText = "{=541jGrpb}The amount of war exhaustion added when a faction loses all of its fiefs. Not affected by war exhaustion rate. Potentially subject to diminishing returns. Default value is 15.0%.")] [SettingPropertyGroup(HeadingWarExhaustion)] public float WarExhaustionWhenOccupied { get; set; } = 15f; @@ -189,7 +193,7 @@ public bool EnableWarExhaustionCampaignMapWidget [SettingPropertyGroup(HeadingGoldCosts)] public float ScalingWarReparationsGoldCostMultiplier { get; set; } = 50; - [SettingPropertyInteger(displayName: "{=Cr6a5Jap}Defeated War Reparations Gold Cost", 0, 10000, Order = 12, RequireRestart = false, HintText = "{=NH4GNKva}The cost in gold for losing a war of attrition against another kingdom, measured in millions. The default value is 200.")] + [SettingPropertyInteger(displayName: "{=Cr6a5Jap}Defeated War Reparations Gold Cost", 0, 10000, Order = 12, RequireRestart = false, HintText = "{=NH4GNKva}The base cost in gold for losing a war of attrition against another kingdom. Affected by scaling and with scaling disabled will be multiplied by a thousand. The default value is 200.")] [SettingPropertyGroup(HeadingGoldCosts)] public int DefeatedGoldCost { get; set; } = 200; diff --git a/src/Bannerlord.Diplomacy/ViewModel/DetailWarVM.cs b/src/Bannerlord.Diplomacy/ViewModel/DetailWarVM.cs index 485e547f..ab06a31c 100644 --- a/src/Bannerlord.Diplomacy/ViewModel/DetailWarVM.cs +++ b/src/Bannerlord.Diplomacy/ViewModel/DetailWarVM.cs @@ -142,8 +142,8 @@ public DetailWarVM(Kingdom opposingKingdom, Action onFinalize) OpponentKingdom = new DiplomacyFactionRelationshipVM(_opposingKingdom); Kingdom = new DiplomacyFactionRelationshipVM(Clan.PlayerClan.Kingdom); HelpHint = new HintViewModel(GameTexts.FindText("str_wardetail_help")); - WarExhaustionRate = Settings.Instance!.IndividualWarExhaustionRates ? $"{Instance.GetWarExhaustionRate(Clan.PlayerClan.Kingdom, _opposingKingdom):0%} / {Instance.GetWarExhaustionRate(_opposingKingdom, Clan.PlayerClan.Kingdom):0%}" - : $"{Instance.GetWarExhaustionRate(Clan.PlayerClan.Kingdom, _opposingKingdom):0%}"; + WarExhaustionRate = Settings.Instance!.IndividualWarExhaustionRates ? $"{Instance!.GetWarExhaustionRate(Clan.PlayerClan.Kingdom, _opposingKingdom):0%} / {Instance.GetWarExhaustionRate(_opposingKingdom, Clan.PlayerClan.Kingdom):0%}" + : $"{Instance!.GetWarExhaustionRate(Clan.PlayerClan.Kingdom, _opposingKingdom):0%}"; RateHelpHint = new HintViewModel(GameTexts.FindText("str_warexhaustionrate_help")); StatsLabel = GameTexts.FindText("str_stat").ToString(); WarReportLabel = new TextObject("{=mCue7aFc}War Report").ToString(); @@ -163,16 +163,16 @@ public override void RefreshValues() Faction1Visual = new ImageIdentifierVM(BannerCode.CreateFrom(playerKingdom.Banner), true); Faction2Visual = new ImageIdentifierVM(BannerCode.CreateFrom(_opposingKingdom.Banner), true); - PlayerWarExhaustion = $"{Instance.GetWarExhaustion(Clan.PlayerClan.Kingdom, _opposingKingdom):F1}%"; - OpponentWarExhaustion = $"{Instance.GetWarExhaustion(_opposingKingdom, Clan.PlayerClan.Kingdom):F1}%"; + PlayerWarExhaustion = $"{Instance!.GetWarExhaustion(Clan.PlayerClan.Kingdom, _opposingKingdom):F1}%"; + OpponentWarExhaustion = $"{Instance!.GetWarExhaustion(_opposingKingdom, Clan.PlayerClan.Kingdom):F1}%"; - PlayerWarExhaustionIsCritical = Instance.HasCriticalWarExhaustion(Clan.PlayerClan.Kingdom, _opposingKingdom); - OpponentWarExhaustionIsCritical = Instance.HasCriticalWarExhaustion(_opposingKingdom, Clan.PlayerClan.Kingdom); + PlayerWarExhaustionIsCritical = Instance!.HasCriticalWarExhaustion(Clan.PlayerClan.Kingdom, _opposingKingdom); + OpponentWarExhaustionIsCritical = Instance!.HasCriticalWarExhaustion(_opposingKingdom, Clan.PlayerClan.Kingdom); PlayerCriticaExhaustionlHint = GetCriticaExhaustionlHint(PlayerWarExhaustionIsCritical, Clan.PlayerClan.Kingdom, _opposingKingdom); OpponentCriticaExhaustionlHint = GetCriticaExhaustionlHint(OpponentWarExhaustionIsCritical, _opposingKingdom, Clan.PlayerClan.Kingdom); - var currentQuestState = Instance.GetWarExhaustionQuestState(Clan.PlayerClan.Kingdom, _opposingKingdom); + var currentQuestState = Instance!.GetWarExhaustionQuestState(Clan.PlayerClan.Kingdom, _opposingKingdom); int questStateWarningHeight; switch (currentQuestState) { diff --git a/src/Bannerlord.Diplomacy/ViewModel/WarExhaustionMapIndicatorItemVM.cs b/src/Bannerlord.Diplomacy/ViewModel/WarExhaustionMapIndicatorItemVM.cs index 5bb259c9..b3f92079 100644 --- a/src/Bannerlord.Diplomacy/ViewModel/WarExhaustionMapIndicatorItemVM.cs +++ b/src/Bannerlord.Diplomacy/ViewModel/WarExhaustionMapIndicatorItemVM.cs @@ -55,10 +55,10 @@ public override void RefreshValues() public void UpdateWarExhaustion() { - PlayerWarExhaustion = (int) WarExhaustionManager.Instance.GetWarExhaustion(Clan.PlayerClan.Kingdom, _opposingKingdom); - OpponentWarExhaustion = (int) WarExhaustionManager.Instance.GetWarExhaustion(_opposingKingdom, Clan.PlayerClan.Kingdom); - IsCriticalFaction1 = WarExhaustionManager.Instance.HasCriticalWarExhaustion(Clan.PlayerClan.Kingdom, _opposingKingdom); - IsCriticalFaction2 = WarExhaustionManager.Instance.HasCriticalWarExhaustion(_opposingKingdom, Clan.PlayerClan.Kingdom); + PlayerWarExhaustion = (int) WarExhaustionManager.Instance!.GetWarExhaustion(Clan.PlayerClan.Kingdom, _opposingKingdom); + OpponentWarExhaustion = (int) WarExhaustionManager.Instance!.GetWarExhaustion(_opposingKingdom, Clan.PlayerClan.Kingdom); + IsCriticalFaction1 = WarExhaustionManager.Instance!.HasCriticalWarExhaustion(Clan.PlayerClan.Kingdom, _opposingKingdom); + IsCriticalFaction2 = WarExhaustionManager.Instance!.HasCriticalWarExhaustion(_opposingKingdom, Clan.PlayerClan.Kingdom); } [UsedImplicitly] diff --git a/src/Bannerlord.Diplomacy/ViewModelMixin/KingdomTruceItemVmMixin.cs b/src/Bannerlord.Diplomacy/ViewModelMixin/KingdomTruceItemVmMixin.cs index 735b4632..a5ee4f87 100644 --- a/src/Bannerlord.Diplomacy/ViewModelMixin/KingdomTruceItemVmMixin.cs +++ b/src/Bannerlord.Diplomacy/ViewModelMixin/KingdomTruceItemVmMixin.cs @@ -164,8 +164,8 @@ public override void OnRefresh() ViewModel!.Stats.RemoveAt(1); ViewModel!.Stats.Insert(1, new KingdomWarComparableStatVM( - (int) WarExhaustionManager.Instance.GetWarExhaustion(_faction1, _faction2), - (int) WarExhaustionManager.Instance.GetWarExhaustion(_faction2, _faction1), + (int) WarExhaustionManager.Instance!.GetWarExhaustion(_faction1, _faction2), + (int) WarExhaustionManager.Instance!.GetWarExhaustion(_faction2, _faction1), _TWarExhaustion, Color.FromUint(_faction1.Color).ToString(), Color.FromUint(_faction2.Color).ToString(), diff --git a/src/Bannerlord.Diplomacy/ViewModelMixin/KingdomWarItemVMMixin.cs b/src/Bannerlord.Diplomacy/ViewModelMixin/KingdomWarItemVMMixin.cs index f3ae5224..d1d84687 100644 --- a/src/Bannerlord.Diplomacy/ViewModelMixin/KingdomWarItemVMMixin.cs +++ b/src/Bannerlord.Diplomacy/ViewModelMixin/KingdomWarItemVMMixin.cs @@ -114,8 +114,8 @@ public override void OnRefresh() ViewModel!.Stats.RemoveAt(1); ViewModel!.Stats.Insert(1, new KingdomWarComparableStatVM( - (int) WarExhaustionManager.Instance.GetWarExhaustion(_faction1, _faction2), - (int) WarExhaustionManager.Instance.GetWarExhaustion(_faction2, _faction1), + (int) WarExhaustionManager.Instance!.GetWarExhaustion(_faction1, _faction2), + (int) WarExhaustionManager.Instance!.GetWarExhaustion(_faction2, _faction1), _TWarExhaustion, Color.FromUint(_faction1.Color).ToString(), Color.FromUint(_faction2.Color).ToString(), diff --git a/src/Bannerlord.Diplomacy/WarExhaustion/WarExhaustionManager.EventHandling.cs b/src/Bannerlord.Diplomacy/WarExhaustion/WarExhaustionManager.EventHandling.cs index ea2cf5f4..525e2abd 100644 --- a/src/Bannerlord.Diplomacy/WarExhaustion/WarExhaustionManager.EventHandling.cs +++ b/src/Bannerlord.Diplomacy/WarExhaustion/WarExhaustionManager.EventHandling.cs @@ -104,17 +104,11 @@ private void AddWarExhaustion(Kingdoms kingdoms, WarExhaustionType warExhaustion var key = kingdoms.Key; if (key is null) return; - var weightedWarExhaustionDelta = warExhaustionType switch - { - WarExhaustionType.Daily => warExhaustionToAdd, - WarExhaustionType.Occupied => warExhaustionToAdd, - WarExhaustionType.Divine => warExhaustionToAdd, - _ => GetWeightedWarExhaustionDelta(kingdoms, warExhaustionToAdd, key), - }; + if (_warExhaustionScores.TryGetValue(key, out var currentValue)) - _warExhaustionScores[key] = currentValue + weightedWarExhaustionDelta; + _warExhaustionScores[key] = currentValue + warExhaustionToAdd; else - _warExhaustionScores[key] = weightedWarExhaustionDelta; + _warExhaustionScores[key] = warExhaustionToAdd; //Divine intervention can override the victor var newValue = _warExhaustionScores[key]; @@ -134,7 +128,7 @@ private void AddWarExhaustion(Kingdoms kingdoms, WarExhaustionType warExhaustion if (Settings.Instance!.EnableWarExhaustionDebugMessages && (kingdoms.Kingdom1 == Hero.MainHero.MapFaction || kingdoms.Kingdom2 == Hero.MainHero.MapFaction)) { var information = - $"Added {weightedWarExhaustionDelta} {Enum.GetName(typeof(WarExhaustionType), warExhaustionType)} war exhaustion to {kingdoms.Kingdom1.Name}'s war with {kingdoms.Kingdom2.Name}"; + $"Added {warExhaustionToAdd} {Enum.GetName(typeof(WarExhaustionType), warExhaustionType)} war exhaustion to {kingdoms.Kingdom1.Name}'s war with {kingdoms.Kingdom2.Name}"; InformationManager.DisplayMessage(new InformationMessage(information, Color.FromUint(4282569842U))); } @@ -179,16 +173,6 @@ static void InvokeIfApplicableForValue(Kingdom kingdom1, Kingdom kingdom2, WarEx } } - private WarExhaustionRecord GetWeightedWarExhaustionDelta(Kingdoms kingdoms, WarExhaustionRecord warExhaustionToAdd, string key) - { - if (!_warExhaustionRates.TryGetValue(key, out var warExhaustionRate)) - { - RegisterWarExhaustionMultiplier(kingdoms); - warExhaustionRate = _warExhaustionRates[key]; - } - return warExhaustionRate * warExhaustionToAdd; - } - private WarExhaustionRecord GetDailyWarExhaustionDelta(Kingdoms kingdoms, CampaignTime warStartDate, out DailyRecord dailyRecord) { if (!_warExhaustionEventRecords.TryGetValue(kingdoms.Key!, out var currentRecords)) @@ -220,7 +204,7 @@ private static void GetWarExhaustionPerDay(out float warExhaustionPerDay, out fl warExhaustionPerDayOccupied = Math.Max(warExhaustionPerDay * Settings.Instance!.FieflessWarExhaustionMultiplier, 1f); } - private static WarExhaustionRecord GetCasualtyWarExhaustionDelta(Kingdoms kingdoms, MapEvent mapEvent, out BattleCasualtyRecord battleCasualtyRecord) + private WarExhaustionRecord GetCasualtyWarExhaustionDelta(Kingdoms kingdoms, MapEvent mapEvent, out BattleCasualtyRecord battleCasualtyRecord) { int attackerSideCasualties = mapEvent.AttackerSide.Casualties; int defenderSideCasualties = mapEvent.DefenderSide.Casualties; @@ -234,15 +218,16 @@ private static WarExhaustionRecord GetCasualtyWarExhaustionDelta(Kingdoms kingdo var eventRelatedSettlement = mapEvent.MapEventSettlement; var hasActiveQuest = !IsValidQuestState(kingdoms.Kingdom1, kingdoms.Kingdom2); + var rates = GetWarExhaustionRates(kingdoms); if (kingdoms.ReversedKeyOrder) { - battleCasualtyRecord = new(CampaignTime.Now, defenderSideCasualties, defenderSideWarExhaustion, attackerSideCasualties, attackerSideWarExhaustion, defenderSidePartyName, attackerSidePartyName, eventRelatedSettlement); - return new(defenderSideWarExhaustion, attackerSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + battleCasualtyRecord = new(CampaignTime.Now, defenderSideCasualties, defenderSideWarExhaustion * rates.Faction1Value, attackerSideCasualties, attackerSideWarExhaustion * rates.Faction2Value, defenderSidePartyName, attackerSidePartyName, eventRelatedSettlement); + return new(defenderSideWarExhaustion * rates.Faction1Value, attackerSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } else { - battleCasualtyRecord = new(CampaignTime.Now, attackerSideCasualties, attackerSideWarExhaustion, defenderSideCasualties, defenderSideWarExhaustion, attackerSidePartyName, defenderSidePartyName, eventRelatedSettlement); - return new(attackerSideWarExhaustion, defenderSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + battleCasualtyRecord = new(CampaignTime.Now, attackerSideCasualties, attackerSideWarExhaustion * rates.Faction1Value, defenderSideCasualties, defenderSideWarExhaustion * rates.Faction2Value, attackerSidePartyName, defenderSidePartyName, eventRelatedSettlement); + return new(attackerSideWarExhaustion * rates.Faction1Value, defenderSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } } @@ -255,15 +240,17 @@ private WarExhaustionRecord GetCasualtyWarExhaustionDelta(Kingdoms kingdoms, int kingdom1PartyName ??= kingdoms.Kingdom1.Name; kingdom2PartyName ??= kingdoms.Kingdom2.Name; var hasActiveQuest = !IsValidQuestState(kingdoms.Kingdom1, kingdoms.Kingdom2); + + var rates = GetWarExhaustionRates(kingdoms); if (kingdoms.ReversedKeyOrder) { - battleCasualtyRecord = new(CampaignTime.Now, kingdom2Casualties, kingdom2WarExhaustion, kingdom1Casualties, kingdom1WarExhaustion, kingdom2PartyName, kingdom1PartyName, eventRelatedSettlement: null); - return new(kingdom2WarExhaustion, kingdom1WarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + battleCasualtyRecord = new(CampaignTime.Now, kingdom2Casualties, kingdom2WarExhaustion * rates.Faction1Value, kingdom1Casualties, kingdom1WarExhaustion * rates.Faction2Value, kingdom2PartyName, kingdom1PartyName, eventRelatedSettlement: null); + return new(kingdom2WarExhaustion * rates.Faction1Value, kingdom1WarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } else { - battleCasualtyRecord = new(CampaignTime.Now, kingdom1Casualties, kingdom1WarExhaustion, kingdom2Casualties, kingdom2WarExhaustion, kingdom1PartyName, kingdom2PartyName, eventRelatedSettlement: null); - return new(kingdom1WarExhaustion, kingdom2WarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + battleCasualtyRecord = new(CampaignTime.Now, kingdom1Casualties, kingdom1WarExhaustion * rates.Faction1Value, kingdom2Casualties, kingdom2WarExhaustion * rates.Faction2Value, kingdom1PartyName, kingdom2PartyName, eventRelatedSettlement: null); + return new(kingdom1WarExhaustion * rates.Faction1Value, kingdom2WarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } } @@ -273,15 +260,16 @@ private WarExhaustionRecord GetRaidWarExhaustionDelta(Kingdoms kingdoms, Village float defenderSideWarExhaustion = Settings.Instance!.WarExhaustionPerRaid * GetDiminishingReturnsFactor(kingdoms, raidedVillage, out var yieldsDiminishingReturns); var hasActiveQuest = !IsValidQuestState(kingdoms.Kingdom1, kingdoms.Kingdom2); + var rates = GetWarExhaustionRates(kingdoms); if (kingdoms.ReversedKeyOrder) { - raidRecord = new(CampaignTime.Now, raidedVillage, 1, defenderSideWarExhaustion, 0, attackerSideWarExhaustion, raidingPartyName, yieldsDiminishingReturns); - return new(defenderSideWarExhaustion, attackerSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + raidRecord = new(CampaignTime.Now, raidedVillage, 1, defenderSideWarExhaustion * rates.Faction1Value, 0, attackerSideWarExhaustion * rates.Faction2Value, raidingPartyName, yieldsDiminishingReturns); + return new(defenderSideWarExhaustion * rates.Faction1Value, attackerSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } else { - raidRecord = new(CampaignTime.Now, raidedVillage, 0, attackerSideWarExhaustion, 1, defenderSideWarExhaustion, raidingPartyName, yieldsDiminishingReturns); - return new(attackerSideWarExhaustion, defenderSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + raidRecord = new(CampaignTime.Now, raidedVillage, 0, attackerSideWarExhaustion * rates.Faction1Value, 1, defenderSideWarExhaustion * rates.Faction2Value, raidingPartyName, yieldsDiminishingReturns); + return new(attackerSideWarExhaustion * rates.Faction1Value, defenderSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } } @@ -296,15 +284,16 @@ private WarExhaustionRecord GetSiegeWarExhaustionDelta(Kingdoms kingdoms, MapEve TextObject attackerSidePartyName = mapEvent.AttackerSide.LeaderParty?.Name ?? kingdoms.Kingdom1.Name; TextObject defenderSidePartyName = mapEvent.DefenderSide.LeaderParty?.Name ?? new("{=fnPa5bas}garrisoned troops"); + var rates = GetWarExhaustionRates(kingdoms); if (kingdoms.ReversedKeyOrder) { - siegeRecord = new(CampaignTime.Now, eventRelatedSettlement, 1, defenderSideWarExhaustion, 0, attackerSideWarExhaustion, defenderSidePartyName, attackerSidePartyName, yieldsDiminishingReturns); - return new(defenderSideWarExhaustion, attackerSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + siegeRecord = new(CampaignTime.Now, eventRelatedSettlement, 1, defenderSideWarExhaustion * rates.Faction1Value, 0, attackerSideWarExhaustion * rates.Faction2Value, defenderSidePartyName, attackerSidePartyName, yieldsDiminishingReturns); + return new(defenderSideWarExhaustion * rates.Faction1Value, attackerSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } else { - siegeRecord = new(CampaignTime.Now, eventRelatedSettlement, 0, attackerSideWarExhaustion, 1, defenderSideWarExhaustion, attackerSidePartyName, defenderSidePartyName, yieldsDiminishingReturns); - return new(attackerSideWarExhaustion, defenderSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + siegeRecord = new(CampaignTime.Now, eventRelatedSettlement, 0, attackerSideWarExhaustion * rates.Faction1Value, 1, defenderSideWarExhaustion * rates.Faction2Value, attackerSidePartyName, defenderSidePartyName, yieldsDiminishingReturns); + return new(attackerSideWarExhaustion * rates.Faction1Value, defenderSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } } @@ -315,15 +304,16 @@ private WarExhaustionRecord GetHeroImprisonedWarExhaustionDelta(Kingdoms kingdom float defenderSideWarExhaustion = Settings.Instance!.WarExhaustionPerImprisonment * importanceFactor * GetDiminishingReturnsFactor(kingdoms, hero, out var yieldsDiminishingReturns); var hasActiveQuest = !IsValidQuestState(kingdoms.Kingdom1, kingdoms.Kingdom2); + var rates = GetWarExhaustionRates(kingdoms); if (kingdoms.ReversedKeyOrder) { - heroImprisonedRecord = new(CampaignTime.Now, hero, 1, defenderSideWarExhaustion, 0, attackerSideWarExhaustion, otherPartyName, yieldsDiminishingReturns); - return new(defenderSideWarExhaustion, attackerSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + heroImprisonedRecord = new(CampaignTime.Now, hero, 1, defenderSideWarExhaustion * rates.Faction1Value, 0, attackerSideWarExhaustion * rates.Faction2Value, otherPartyName, yieldsDiminishingReturns); + return new(defenderSideWarExhaustion * rates.Faction1Value, attackerSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } else { - heroImprisonedRecord = new(CampaignTime.Now, hero, 0, attackerSideWarExhaustion, 1, defenderSideWarExhaustion, otherPartyName, yieldsDiminishingReturns); - return new(attackerSideWarExhaustion, defenderSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + heroImprisonedRecord = new(CampaignTime.Now, hero, 0, attackerSideWarExhaustion * rates.Faction1Value, 1, defenderSideWarExhaustion * rates.Faction2Value, otherPartyName, yieldsDiminishingReturns); + return new(attackerSideWarExhaustion * rates.Faction1Value, defenderSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } } @@ -334,15 +324,16 @@ private WarExhaustionRecord GetHeroPerishedWarExhaustionDelta(Kingdoms kingdoms, float defenderSideWarExhaustion = Settings.Instance!.WarExhaustionPerDeath * importanceFactor * GetDiminishingReturnsFactor(kingdoms, hero, out var yieldsDiminishingReturns); var hasActiveQuest = !IsValidQuestState(kingdoms.Kingdom1, kingdoms.Kingdom2); + var rates = GetWarExhaustionRates(kingdoms); if (kingdoms.ReversedKeyOrder) { - heroPerishedRecord = new(CampaignTime.Now, hero, 1, defenderSideWarExhaustion, 0, attackerSideWarExhaustion, otherPartyName, deathDetail, yieldsDiminishingReturns); - return new(defenderSideWarExhaustion, attackerSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + heroPerishedRecord = new(CampaignTime.Now, hero, 1, defenderSideWarExhaustion * rates.Faction1Value, 0, attackerSideWarExhaustion * rates.Faction2Value, otherPartyName, deathDetail, yieldsDiminishingReturns); + return new(defenderSideWarExhaustion * rates.Faction1Value, attackerSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } else { - heroPerishedRecord = new(CampaignTime.Now, hero, 0, attackerSideWarExhaustion, 1, defenderSideWarExhaustion, otherPartyName, deathDetail, yieldsDiminishingReturns); - return new(attackerSideWarExhaustion, defenderSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + heroPerishedRecord = new(CampaignTime.Now, hero, 0, attackerSideWarExhaustion * rates.Faction1Value, 1, defenderSideWarExhaustion * rates.Faction2Value, otherPartyName, deathDetail, yieldsDiminishingReturns); + return new(attackerSideWarExhaustion * rates.Faction1Value, defenderSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } } @@ -355,15 +346,16 @@ private WarExhaustionRecord GetCaravanRaidWarExhaustionDelta(Kingdoms kingdoms, TextObject winningSidePartyName = winningSide.LeaderParty?.Name ?? kingdoms.Kingdom1.Name; TextObject loosingSidePartyName = cp.Name ?? new("{=rwFeQWky}A caravan of {KINGDOM_NAME}", new() { ["KINGDOM_NAME"] = kingdoms.Kingdom2.Name }); + var rates = GetWarExhaustionRates(kingdoms); if (kingdoms.ReversedKeyOrder) { - caravanRaidRecord = new(CampaignTime.Now, 1, loosingSideWarExhaustion, 0, winningSideWarExhaustion, loosingSidePartyName, winningSidePartyName); - return new(loosingSideWarExhaustion, winningSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + caravanRaidRecord = new(CampaignTime.Now, 1, loosingSideWarExhaustion * rates.Faction1Value, 0, winningSideWarExhaustion * rates.Faction2Value, loosingSidePartyName, winningSidePartyName); + return new(loosingSideWarExhaustion * rates.Faction1Value, winningSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } else { - caravanRaidRecord = new(CampaignTime.Now, 0, winningSideWarExhaustion, 1, loosingSideWarExhaustion, winningSidePartyName, loosingSidePartyName); - return new(winningSideWarExhaustion, loosingSideWarExhaustion, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); + caravanRaidRecord = new(CampaignTime.Now, 0, winningSideWarExhaustion * rates.Faction1Value, 1, loosingSideWarExhaustion * rates.Faction2Value, winningSidePartyName, loosingSidePartyName); + return new(winningSideWarExhaustion * rates.Faction1Value, loosingSideWarExhaustion * rates.Faction2Value, hasActiveQuest: hasActiveQuest, considerRangeLimits: false); } } @@ -454,9 +446,9 @@ private ImportanceEnum GetHeroImportanceForFaction(Hero hero, IFaction faction) if (hero.IsFactionLeader) return ImportanceEnum.MatterOfLifeAndDeath; if (faction.Leader.Clan is Clan factionLeaderClan && factionLeaderClan == clan) - return ImportanceEnum.ExtremelyImportant; + return hero.IsNoncombatant ? ImportanceEnum.Important : ImportanceEnum.ExtremelyImportant; - int inportance = Math.Min(clan.Tier, 6) - (clan.IsUnderMercenaryService ? 5 : 3) + (hero.IsCommander ? 1 : 0) + (clan.Leader == hero ? 3 : 0) - (hero.IsLord ? 0 : 3); + int inportance = Math.Min(clan.Tier, 6) - (clan.IsUnderMercenaryService ? 5 : 3) + (hero.IsCommander ? 1 : 0) + (clan.Leader == hero ? 3 : 0) - (hero.IsLord ? 0 : 3) - (hero.IsNoncombatant ? 3 : 0); return (ImportanceEnum) MBMath.ClampInt(inportance, 1, (int) ImportanceEnum.ExtremelyImportant); } @@ -497,7 +489,7 @@ private void TryInitWarExhaustion(out Dictionary wa var enemyKingdomLastOccupationDate = GetLastOccupationDate(enemyKingdom, out var enemyKingdomLastOccupatorFaction); var stance = kingdom.GetStanceWith(enemyKingdom); CampaignTime warStartDate = stance.WarStartDate; - GetWarExhaustionMultiplier(kingdoms, out var multiplier1, out var multiplier2); + CalculateWarExhaustionMultiplier(kingdoms, out var multiplier1, out var multiplier2); var eventRecs = new List(); var hasActiveQuest = !IsValidQuestState(kingdom, enemyKingdom); diff --git a/src/Bannerlord.Diplomacy/WarExhaustion/WarExhaustionManager.cs b/src/Bannerlord.Diplomacy/WarExhaustion/WarExhaustionManager.cs index 447a563e..c98cd496 100644 --- a/src/Bannerlord.Diplomacy/WarExhaustion/WarExhaustionManager.cs +++ b/src/Bannerlord.Diplomacy/WarExhaustion/WarExhaustionManager.cs @@ -35,13 +35,13 @@ internal record Kingdoms [SaveableField(3)] private Dictionary> _warExhaustionEventRecords; - public static WarExhaustionManager Instance { get; private set; } = default!; + public static WarExhaustionManager? Instance { get; private set; } = default; internal const float MaxWarExhaustion = 100f; internal const float MinWarExhaustion = 0f; internal const float CriticalThresholdWarExhaustion = 0.75f; - private const float BaseKingdomStrengthForExhaustionRate = 2000f; + private const float BaseKingdomStrengthForExhaustionRate = 4000f; private const float MinExhaustionRate = 0.25f; private const float MaxExhaustionRate = 1.0f; @@ -69,6 +69,16 @@ public float GetWarExhaustionRate(Kingdom kingdom1, Kingdom kingdom2) : 1f; } + private WarExhaustionRecord GetWarExhaustionRates(Kingdoms kingdoms) + { + if (!_warExhaustionRates.TryGetValue(kingdoms.Key!, out var warExhaustionRate)) + { + RegisterWarExhaustionMultiplier(kingdoms); + warExhaustionRate = _warExhaustionRates[kingdoms.Key!]; + } + return warExhaustionRate; + } + public ActiveQuestState GetWarExhaustionQuestState(Kingdom kingdom1, Kingdom kingdom2) { var key = CreateKey(kingdom1, kingdom2, out _); @@ -102,17 +112,17 @@ internal void RegisterWarExhaustionMultiplier(Kingdoms kingdoms) var key = kingdoms.Key; if (key is not null) { - GetWarExhaustionMultiplier(kingdoms, out var multiplier1, out var multiplier2); + CalculateWarExhaustionMultiplier(kingdoms, out var multiplier1, out var multiplier2); _warExhaustionRates[key] = new(multiplier1, multiplier2, considerRangeLimits: false); } } - private static void GetWarExhaustionMultiplier(Kingdoms kingdoms, out float multiplier1, out float multiplier2) + private static void CalculateWarExhaustionMultiplier(Kingdoms kingdoms, out float multiplier1, out float multiplier2) { if (Settings.Instance!.IndividualWarExhaustionRates) { - var kingdom1multiplier = MBMath.ClampFloat(BaseKingdomStrengthForExhaustionRate / kingdoms.Kingdom1.TotalStrength, MinExhaustionRate, MaxExhaustionRate); - var kingdom2multiplier = MBMath.ClampFloat(BaseKingdomStrengthForExhaustionRate / kingdoms.Kingdom2.TotalStrength, MinExhaustionRate, MaxExhaustionRate); + var kingdom1multiplier = MBMath.ClampFloat(CalculateMultiplier(kingdoms.Kingdom1.TotalStrength), MinExhaustionRate, MaxExhaustionRate); + var kingdom2multiplier = MBMath.ClampFloat(CalculateMultiplier(kingdoms.Kingdom2.TotalStrength), MinExhaustionRate, MaxExhaustionRate); if (kingdoms.ReversedKeyOrder) { multiplier1 = kingdom2multiplier; @@ -127,9 +137,11 @@ private static void GetWarExhaustionMultiplier(Kingdoms kingdoms, out float mult else { var average = (kingdoms.Kingdom1.TotalStrength + kingdoms.Kingdom2.TotalStrength) / 2; - multiplier1 = MBMath.ClampFloat(BaseKingdomStrengthForExhaustionRate / average, MinExhaustionRate, MaxExhaustionRate); + multiplier1 = MBMath.ClampFloat(CalculateMultiplier(average), MinExhaustionRate, MaxExhaustionRate); multiplier2 = multiplier1; } + + static float CalculateMultiplier(float strength) => 1 - 0.25f * (Math.Max(strength / BaseKingdomStrengthForExhaustionRate, 1) - 1); } public List GetWarExhaustionEventRecords(Kingdom kingdom1, Kingdom kingdom2, out Kingdoms? kingdoms) diff --git a/src/Bannerlord.Diplomacy/_Module/ModuleData/Languages/std_module_strings_xml.xml b/src/Bannerlord.Diplomacy/_Module/ModuleData/Languages/std_module_strings_xml.xml index fd3477ca..5277395e 100644 --- a/src/Bannerlord.Diplomacy/_Module/ModuleData/Languages/std_module_strings_xml.xml +++ b/src/Bannerlord.Diplomacy/_Module/ModuleData/Languages/std_module_strings_xml.xml @@ -160,22 +160,26 @@ --> + + + + - - - - + + + + - - + + @@ -187,7 +191,7 @@ - + @@ -213,9 +217,6 @@ - - - diff --git a/src/Bannerlord.Diplomacy/_Module/ModuleData/Languages/war_exhaustion_strings.xml b/src/Bannerlord.Diplomacy/_Module/ModuleData/Languages/war_exhaustion_strings.xml index f5569755..fe401179 100644 --- a/src/Bannerlord.Diplomacy/_Module/ModuleData/Languages/war_exhaustion_strings.xml +++ b/src/Bannerlord.Diplomacy/_Module/ModuleData/Languages/war_exhaustion_strings.xml @@ -87,6 +87,8 @@ + + @@ -94,9 +96,9 @@ - + - + @@ -104,7 +106,7 @@ - + From 7ca5cb9d4c1c1689272d4ee15eb593b6c598754f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 26 Feb 2023 19:05:54 +0000 Subject: [PATCH 8/8] Automated dotnet-format update --- .../Patches/MakePeaceKingdomDecisionPatch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bannerlord.Diplomacy/Patches/MakePeaceKingdomDecisionPatch.cs b/src/Bannerlord.Diplomacy/Patches/MakePeaceKingdomDecisionPatch.cs index aaeeffc2..cdba2049 100644 --- a/src/Bannerlord.Diplomacy/Patches/MakePeaceKingdomDecisionPatch.cs +++ b/src/Bannerlord.Diplomacy/Patches/MakePeaceKingdomDecisionPatch.cs @@ -19,7 +19,7 @@ private static bool ApplyChosenOutcomePrefix(DecisionOutcome chosenOutcome, Make { if (__instance.FactionToMakePeaceWith is not Kingdom kingdomToMakePeaceWith) return true; - + if (!____applyResults || !((MakePeaceKingdomDecision.MakePeaceDecisionOutcome) chosenOutcome).ShouldPeaceBeDeclared) return false;