Add prestige modifiers for battle outcomes (#8417)

This commit is contained in:
2026-07-10 13:51:48 -07:00
committed by GitHub
parent c2438e093f
commit bc17291691
11 changed files with 117 additions and 4 deletions
@@ -22,6 +22,8 @@ message PrestigeModifier {
SUPPRESSED_BEASTS = 1;
FESTIVAL = 2;
TUTORIAL_HANDICAP = 3;
BATTLE_VICTORY = 4;
BATTLE_DEFEAT = 5;
}
Type type = 1;
@@ -82,6 +82,8 @@ beastsEventChance 0.02 Double
riotEventChance 0.02 Double
minVigorForSuppressBeasts 50 Double
suppressBeastsPrestigeBonus 2 Int
winBattlePrestigeBonus 5 Int
loseBattlePrestigePenalty 2 Int
beastsDurationMonths 4 Int
beastsSupportDamage 3 Double
suppressBeastsSupportBonus 5 Double
1 actionVigorCost 15 Int
82 riotEventChance 0.02 Double
83 minVigorForSuppressBeasts 50 Double
84 suppressBeastsPrestigeBonus 2 Int
85 winBattlePrestigeBonus 5 Int
86 loseBattlePrestigePenalty 2 Int
87 beastsDurationMonths 4 Int
88 beastsSupportDamage 3 Double
89 suppressBeastsSupportBonus 5 Double
@@ -239,6 +239,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province/concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:action_result_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_battalion_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_faction_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_hero_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
"//src/main/scala/net/eagle0/eagle/model/action_result/types",
@@ -1483,6 +1484,8 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/settings:battle_agriculture_devastation_delta",
"//src/main/scala/net/eagle0/eagle/library/settings:battle_economy_devastation_delta",
"//src/main/scala/net/eagle0/eagle/library/settings:battle_infrastructure_devastation_delta",
"//src/main/scala/net/eagle0/eagle/library/settings:lose_battle_prestige_penalty",
"//src/main/scala/net/eagle0/eagle/library/settings:win_battle_prestige_bonus",
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//src/main/scala/net/eagle0/eagle/library/util/quest_fulfillment:quest_fulfillment_utils",
@@ -1493,6 +1496,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province/concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:action_result_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_battalion_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_faction_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_hero_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/types",
"//src/main/scala/net/eagle0/eagle/model/state:army",
@@ -1501,6 +1505,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/state:pending_conquest_info",
"//src/main/scala/net/eagle0/eagle/model/state:supplies",
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
"//src/main/scala/net/eagle0/eagle/model/state/faction:prestige_modifier",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/hero",
"//src/main/scala/net/eagle0/eagle/model/state/province",
@@ -7,7 +7,9 @@ import net.eagle0.eagle.library.actions.util.ShatteredArmyUtils
import net.eagle0.eagle.library.settings.{
BattleAgricultureDevastationDelta,
BattleEconomyDevastationDelta,
BattleInfrastructureDevastationDelta
BattleInfrastructureDevastationDelta,
LoseBattlePrestigePenalty,
WinBattlePrestigeBonus
}
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentUtils
@@ -15,6 +17,7 @@ import net.eagle0.eagle.library.util.EagleRequire.internalRequire
import net.eagle0.eagle.library.EagleInternalException
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedHeroC, StatAbsolute}
import net.eagle0.eagle.model.action_result.concrete.ChangedFactionC
import net.eagle0.eagle.model.action_result.types.ActionResultType.BattleEnded
import net.eagle0.eagle.model.action_result.ActionResultT
import net.eagle0.eagle.model.state.{AftermathClaimant, AftermathClaimantUnit, Army, CapturedHero}
@@ -27,6 +30,7 @@ import net.eagle0.eagle.model.state.{
TutorialPhase
}
import net.eagle0.eagle.model.state.battalion.{BattalionT, EventForBattalionBackstory, EventForBattalionBackstoryT}
import net.eagle0.eagle.model.state.faction.PrestigeModifier
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.hero.{EventForHeroBackstory, EventForHeroBackstoryT, HeroT}
import net.eagle0.eagle.model.state.province.ProvinceT
@@ -338,6 +342,23 @@ case class ResolveBattleAction(
battle: ShardokBattle,
battleResolution: BattleResolution
): Vector[ActionResultT] = {
val prestigeChangedFactions = battleResolution.resolvedPlayers.toVector.map { rsp =>
val (modifierType, value) =
if rsp.endGameCondition.isVictory then
(PrestigeModifier.Type.BattleVictory, WinBattlePrestigeBonus.intValue.toDouble)
else (PrestigeModifier.Type.BattleDefeat, -LoseBattlePrestigePenalty.intValue.toDouble)
ChangedFactionC(
factionId = rsp.eagleFid,
newPrestigeModifiers = Vector(
PrestigeModifier(
prestigeModifierType = modifierType,
value = value
)
)
)
}
val changedHeroes = battleResolution.resolvedPlayers
.flatMap(_.units)
.map { u =>
@@ -396,6 +417,7 @@ case class ResolveBattleAction(
battle = battle,
battleResolution = battleResolution
),
changedFactions = prestigeChangedFactions,
destroyedBattalionIds = destroyedBattalionIds(battleResolution)
)
@@ -1107,6 +1107,32 @@ scala_setting_library(
],
)
scala_setting_library(
name = "win_battle_prestige_bonus",
setting_name = "WinBattlePrestigeBonus",
setting_type = "Int",
setting_value = "5",
visibility = [
"//src/main/scala/net/eagle0/eagle:__subpackages__",
"//src/main/scala/net/eagle0/util:__subpackages__",
"//src/test/resources/net/eagle0/shardok/maps:__pkg__",
"//src/test/scala/net/eagle0/eagle:__subpackages__",
],
)
scala_setting_library(
name = "lose_battle_prestige_penalty",
setting_name = "LoseBattlePrestigePenalty",
setting_type = "Int",
setting_value = "2",
visibility = [
"//src/main/scala/net/eagle0/eagle:__subpackages__",
"//src/main/scala/net/eagle0/util:__subpackages__",
"//src/test/resources/net/eagle0/shardok/maps:__pkg__",
"//src/test/scala/net/eagle0/eagle:__subpackages__",
],
)
scala_setting_library(
name = "beasts_duration_months",
setting_name = "BeastsDurationMonths",
@@ -143,6 +143,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/settings:jailbreak_vigor_cost",
"//src/main/scala/net/eagle0/eagle/library/settings:jailbreak_wisdom_xp",
"//src/main/scala/net/eagle0/eagle/library/settings:llm_provider",
"//src/main/scala/net/eagle0/eagle/library/settings:lose_battle_prestige_penalty",
"//src/main/scala/net/eagle0/eagle/library/settings:loyalty_decrease_per_discordance",
"//src/main/scala/net/eagle0/eagle/library/settings:loyalty_discordance_threshold",
"//src/main/scala/net/eagle0/eagle/library/settings:loyalty_gain_from_feast",
@@ -321,6 +322,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/settings:vigor_gain_from_feast",
"//src/main/scala/net/eagle0/eagle/library/settings:vigor_to_constitution_xp_multiplier",
"//src/main/scala/net/eagle0/eagle/library/settings:warden_prisoner_recruitment_bias_per_round",
"//src/main/scala/net/eagle0/eagle/library/settings:win_battle_prestige_bonus",
"//src/main/scala/net/eagle0/eagle/library/settings:winter_supplies_loss",
"//src/main/scala/net/eagle0/eagle/library/settings:xp_for_stat_bump",
"//src/main/scala/net/eagle0/eagle/library/settings/base:double_setting",
@@ -68,6 +68,10 @@ object FactionConverter {
PrestigeModifierProto.Type.FESTIVAL
case PrestigeModifier.Type.TutorialHandicap =>
PrestigeModifierProto.Type.TUTORIAL_HANDICAP
case PrestigeModifier.Type.BattleVictory =>
PrestigeModifierProto.Type.BATTLE_VICTORY
case PrestigeModifier.Type.BattleDefeat =>
PrestigeModifierProto.Type.BATTLE_DEFEAT
},
value = prestigeModifier.value
)
@@ -194,6 +198,10 @@ object FactionConverter {
PrestigeModifier.Type.Festival
case PrestigeModifierProto.Type.TUTORIAL_HANDICAP =>
PrestigeModifier.Type.TutorialHandicap
case PrestigeModifierProto.Type.BATTLE_VICTORY =>
PrestigeModifier.Type.BattleVictory
case PrestigeModifierProto.Type.BATTLE_DEFEAT =>
PrestigeModifier.Type.BattleDefeat
case PrestigeModifierProto.Type.Unrecognized(value) =>
throw new IllegalArgumentException(
s"Unrecognized prestige modifier type: $value"
@@ -7,6 +7,8 @@ object PrestigeModifier {
case object SuppressedBeasts extends Type
case object Festival extends Type
case object TutorialHandicap extends Type
case object BattleVictory extends Type
case object BattleDefeat extends Type
}
}
@@ -532,6 +532,8 @@ private[service] object ChangedFactionTableReader {
case "SuppressedBeasts" => PrestigeModifier.Type.SuppressedBeasts
case "Festival" => PrestigeModifier.Type.Festival
case "TutorialHandicap" => PrestigeModifier.Type.TutorialHandicap
case "BattleVictory" => PrestigeModifier.Type.BattleVictory
case "BattleDefeat" => PrestigeModifier.Type.BattleDefeat
case other => throw new IllegalStateException(s"Unrecognized PrestigeModifier.Type: $other")
}
@@ -877,9 +877,12 @@ scala_test(
"//src/main/scala/net/eagle0/eagle/library/settings:battle_agriculture_devastation_delta",
"//src/main/scala/net/eagle0/eagle/library/settings:battle_economy_devastation_delta",
"//src/main/scala/net/eagle0/eagle/library/settings:battle_infrastructure_devastation_delta",
"//src/main/scala/net/eagle0/eagle/library/settings:lose_battle_prestige_penalty",
"//src/main/scala/net/eagle0/eagle/library/settings:win_battle_prestige_bonus",
"//src/main/scala/net/eagle0/eagle/model/action_result:action_result_trait",
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province",
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province/concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_faction_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_hero_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/types",
"//src/main/scala/net/eagle0/eagle/model/state:army",
@@ -894,6 +897,7 @@ scala_test(
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
"//src/main/scala/net/eagle0/eagle/model/state/date",
"//src/main/scala/net/eagle0/eagle/model/state/faction:faction_relationship",
"//src/main/scala/net/eagle0/eagle/model/state/faction:prestige_modifier",
"//src/main/scala/net/eagle0/eagle/model/state/faction/concrete",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/hero",
@@ -4,11 +4,13 @@ import net.eagle0.eagle.library.actions.applier.ActionResultApplierImpl
import net.eagle0.eagle.library.settings.{
BattleAgricultureDevastationDelta,
BattleEconomyDevastationDelta,
BattleInfrastructureDevastationDelta
BattleInfrastructureDevastationDelta,
LoseBattlePrestigePenalty,
WinBattlePrestigeBonus
}
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
import net.eagle0.eagle.model.action_result.changed_province.ChangedProvinceT
import net.eagle0.eagle.model.action_result.concrete.{ChangedHeroC, StatAbsolute}
import net.eagle0.eagle.model.action_result.concrete.{ChangedFactionC, ChangedHeroC, StatAbsolute}
import net.eagle0.eagle.model.action_result.generated_text_request.LlmRequestT
import net.eagle0.eagle.model.action_result.types.ActionResultType.{
BattleEnded,
@@ -34,8 +36,8 @@ import net.eagle0.eagle.model.state.{
}
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
import net.eagle0.eagle.model.state.date.Date
import net.eagle0.eagle.model.state.faction.{FactionRelationship, PrestigeModifier}
import net.eagle0.eagle.model.state.faction.concrete.FactionC
import net.eagle0.eagle.model.state.faction.FactionRelationship
import net.eagle0.eagle.model.state.faction.FactionRelationship.RelationshipLevel
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.hero.{EventForHeroBackstory, Gender, Profession}
@@ -549,6 +551,8 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
BattleEconomyDevastationDelta.setDoubleValue(15.0)
BattleAgricultureDevastationDelta.setDoubleValue(12.0)
BattleInfrastructureDevastationDelta.setDoubleValue(25.0)
WinBattlePrestigeBonus.setIntValue(7)
LoseBattlePrestigePenalty.setIntValue(3)
}
"BattleResolutionAction" should "generate a result with the changed heroes and battalions" in {
@@ -599,6 +603,40 @@ class ResolveBattleActionTest extends AnyFlatSpec with MockFactory with Matchers
.map(_.id)
}
it should "add prestige modifiers for battle winners and losers" in {
val action = ResolveBattleAction(
startingGameState,
Vector(
BattleResolution(
battle = shardokBattle,
resolvedPlayers = resolvedPlayersWithAttackerVictory
)
)
)
val results = action.resultsOfExecute()
inside(results.head.changedFactions.find(_.factionId == attackerFid)) {
case Some(cf: ChangedFactionC) =>
cf.newPrestigeModifiers shouldBe Vector(
PrestigeModifier(
prestigeModifierType = PrestigeModifier.Type.BattleVictory,
value = 7.0
)
)
}
inside(results.head.changedFactions.find(_.factionId == defenderFid)) {
case Some(cf: ChangedFactionC) =>
cf.newPrestigeModifiers shouldBe Vector(
PrestigeModifier(
prestigeModifierType = PrestigeModifier.Type.BattleDefeat,
value = -3.0
)
)
}
}
it should "remove the hostile incoming armies" in {
val action = ResolveBattleAction(
startingGameState,