Add protoless overloads to IncomingArmyUtils and FactionUtils (#4958)

- Add factionsAreMutuallyAllied and hostilityStatus to FactionUtils
- Add protoless overloads to IncomingArmyUtils:
  - hasRelevantIncomingArmy
  - relevantIncomingArmies
  - incomingArmiesAreMutuallyAllied
  - isHostileProvince
  - stats

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-01 15:03:54 -08:00
committed by GitHub
co-authored by Claude Opus 4.5
parent 1eb253d866
commit 4a7559c25d
3 changed files with 86 additions and 1 deletions
@@ -5,9 +5,12 @@ import net.eagle0.eagle.api.command.util.army_stats.ArmyStats
import net.eagle0.eagle.internal.army.{Army, MovingArmy}
import net.eagle0.eagle.internal.game_state.GameState
import net.eagle0.eagle.internal.province.Province
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
import net.eagle0.eagle.library.util.BattalionPower
import net.eagle0.eagle.model.proto_converters.BattalionConverter
import net.eagle0.eagle.model.state.{Army as ArmyC, MovingArmy as MovingArmyC}
import net.eagle0.eagle.model.state.game_state.GameState as GameStateC
import net.eagle0.eagle.model.state.province.ProvinceT
object IncomingArmyUtils {
def hasRelevantIncomingArmy(
@@ -112,4 +115,66 @@ object IncomingArmyUtils {
val battalions = army.units.flatMap(_.battalionId).map(gs.battalions)
battalions.map(BattalionPower.power).sum
}
// ==================== Protoless overloads ====================
/** Protoless overload */
def hasRelevantIncomingArmy(
province: ProvinceT,
factionId: FactionId,
currentRoundId: RoundId
): Boolean =
province.incomingArmies.exists(ia => ia.army.factionId == factionId && ia.arrivalRound == currentRoundId)
/** Protoless overload */
def relevantIncomingArmies(
province: ProvinceT,
factionId: FactionId,
currentRoundId: RoundId
): Vector[MovingArmyC] =
province.incomingArmies
.filter(ia => ia.army.factionId == factionId && ia.arrivalRound == currentRoundId)
/** Protoless overload */
def incomingArmiesAreMutuallyAllied(
province: ProvinceT,
gameState: GameStateC
): Boolean =
FactionUtils.factionsAreMutuallyAllied(
fids = province.incomingArmies
.filter(_.arrivalRound == gameState.currentRoundId)
.map(_.army.factionId) ++ province.hostileArmies
.flatMap(_.armies)
.map(_.army.factionId),
factions = gameState.factions.values.toVector
)
/** Protoless overload */
def isHostileProvince(
province: ProvinceT,
factionId: FactionId,
gameState: GameStateC
): Boolean =
province.rulingFactionId.exists(targetFid =>
FactionUtils.factionsAreHostile(targetFid, factionId, gameState.factions.values.toVector)
)
/** Protoless overload */
def stats(
toFid: FactionId,
army: ArmyC,
gs: GameStateC,
originProvinceId: ProvinceId
): ArmyStats = {
val battalions = army.units.flatMap(_.battalionId).map(gs.battalions)
val troopCount = battalions.map(_.size).sum
ArmyStats(
factionId = army.factionId,
heroCount = army.units.size,
troopCount = troopCount,
hostility = FactionUtils.hostilityStatus(toFid, army.factionId, gs.factions.values.toVector),
originProvinceId = originProvinceId
)
}
}
@@ -7,7 +7,11 @@ scala_library(
"//src/main/scala/net/eagle0/eagle:__subpackages__",
"//src/test/scala/net/eagle0/eagle:__subpackages__",
],
exports = [
"//src/main/protobuf/net/eagle0/common:hostility_scala_proto",
],
deps = [
"//src/main/protobuf/net/eagle0/common:hostility_scala_proto",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle/library/settings:min_support_for_taxes",
"//src/main/scala/net/eagle0/eagle/library/settings:prestige_per_supported_province",
@@ -1,5 +1,6 @@
package net.eagle0.eagle.library.util.faction_utils
import net.eagle0.common.hostility.Hostility
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
import net.eagle0.eagle.library.settings.{MinSupportForTaxes, PrestigePerSupportedProvince}
import net.eagle0.eagle.model.state.faction.{FactionRelationship, FactionT}
@@ -76,6 +77,21 @@ object FactionUtils {
): Boolean =
fid1 != fid2 && !hasTruceOrAlliance(fid1, fid2, factions)
def factionsAreMutuallyAllied(
fids: Vector[FactionId],
factions: Vector[FactionT]
): Boolean =
fids.forall(fid1 => fids.forall(fid2 => !factionsAreHostile(fid1, fid2, factions)))
def hostilityStatus(
fid1: FactionId,
fid2: FactionId,
factions: Vector[FactionT]
): Hostility =
if fid1 == fid2 then Hostility.SELF_HOSTILITY
else if factionsAreHostile(fid1, fid2, factions) then Hostility.ENEMY_HOSTILITY
else Hostility.ALLIED_HOSTILITY
private def incomingHids(province: ProvinceT): Vector[HeroId] =
province.incomingArmies
.map(_.army)