Add Warden prisoner interrogation

This commit is contained in:
2026-06-28 12:48:26 -07:00
parent f52fd34879
commit 9267ba7406
15 changed files with 266 additions and 15 deletions
@@ -22,6 +22,8 @@ namespace eagle {
public Toggle ReturnToggle;
public ToggleGroup decisionToggleGroup;
private Toggle _interrogateToggle;
public TMP_Dropdown moveToDropdown;
public HeroDetailsController heroDetails;
@@ -67,12 +69,29 @@ namespace eagle {
_availableCommand.ManagePrisonerCommand;
private ProvinceId ActingProvinceId => ManagePrisonersCommand.ActingProvinceId;
private Toggle InterrogateToggle {
get {
if (_interrogateToggle != null) return _interrogateToggle;
_interrogateToggle = Instantiate(ExecuteToggle, ExecuteToggle.transform.parent);
_interrogateToggle.name = "Interrogate Toggle";
_interrogateToggle.onValueChanged.RemoveAllListeners();
_interrogateToggle.group = decisionToggleGroup;
var label = _interrogateToggle.GetComponentInChildren<TMP_Text>();
label.text = "Interrogate";
return _interrogateToggle;
}
}
private void EnsureTouchTargets() {
TouchTargetUtils.EnsureMinimumTouchTargets(
ReleaseToggle,
MoveToggle,
ExileToggle,
ExecuteToggle,
InterrogateToggle,
ReturnToggle,
NextHeroButton);
TouchTargetUtils.EnsureMinimumTouchHeight(moveToDropdown);
@@ -128,6 +147,10 @@ namespace eagle {
return SelectedPrisonerToManage.AvailableOptions.First(
opt => opt.SealedValueCase ==
PrisonerManagementOption.SealedValueOneofCase.Exile);
else if (InterrogateToggle.isOn)
return SelectedPrisonerToManage.AvailableOptions.First(
opt => opt.SealedValueCase ==
PrisonerManagementOption.SealedValueOneofCase.Interrogate);
else if (ReturnToggle.isOn)
return SelectedPrisonerToManage.AvailableOptions.First(
opt => opt.SealedValueCase ==
@@ -194,6 +217,9 @@ namespace eagle {
bool hasExecute = options.Any(
opt => opt.SealedValueCase ==
PrisonerManagementOption.SealedValueOneofCase.Execute);
bool hasInterrogate = options.Any(
opt => opt.SealedValueCase ==
PrisonerManagementOption.SealedValueOneofCase.Interrogate);
bool hasReturn = options.Any(
opt => opt.SealedValueCase ==
PrisonerManagementOption.SealedValueOneofCase.Return);
@@ -202,11 +228,14 @@ namespace eagle {
ConfigureToggle(MoveToggle, hasMove);
ConfigureToggle(ExileToggle, hasExile);
ConfigureToggle(ExecuteToggle, hasExecute);
ConfigureToggle(InterrogateToggle, hasInterrogate);
ConfigureToggle(ReturnToggle, hasReturn);
// Select first available option (priority order: Release > Move > Exile > Execute >
// Return)
// Select first available option (priority order: Release > Interrogate > Move >
// Exile > Execute > Return)
if (hasRelease) ReleaseToggle.isOn = true;
else if (hasInterrogate)
InterrogateToggle.isOn = true;
else if (hasMove)
MoveToggle.isOn = true;
else if (hasExile)
@@ -88,6 +88,8 @@ namespace eagle {
case PrisonerManagementOption.SealedValueOneofCase.Exile: return "Exile";
case PrisonerManagementOption.SealedValueOneofCase.Release: return "Release";
case PrisonerManagementOption.SealedValueOneofCase.Return: return "Return";
case PrisonerManagementOption.SealedValueOneofCase.Interrogate:
return "Interrogate";
default: return "";
}
}
@@ -18,6 +18,7 @@ message PrisonerManagementOption {
PrisonerManagementOptionExile exile = 3;
PrisonerManagementOptionReturn return = 4;
PrisonerManagementOptionMove move = 5;
PrisonerManagementOptionInterrogate interrogate = 6;
}
}
@@ -41,3 +42,6 @@ message PrisonerManagementOptionMove {
int32 to_province_id = 1;
}
message PrisonerManagementOptionInterrogate {
}
@@ -10,6 +10,7 @@ import net.eagle0.eagle.model.state.command.available.AvailableCommand.{
}
import net.eagle0.eagle.model.state.command.common.PrisonerManagementOption
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.hero.Profession
import net.eagle0.eagle.model.state.unaffiliated_hero.{UnaffiliatedHeroT, UnaffiliatedHeroType}
object AvailableManagePrisonersCommandFactory extends ScalaAvailableCommandsFactory {
@@ -29,16 +30,26 @@ object AvailableManagePrisonersCommandFactory extends ScalaAvailableCommandsFact
private def optionsForPrisoner(
prisoner: UnaffiliatedHeroT,
moveToPids: Vector[ProvinceId],
hasWarden: Boolean,
gameState: GameState
): Vector[PrisonerManagementOption] = {
val factionWithThisPrisonerAsLeader = gameState.factions.values
.find(_.leaderIds.contains(prisoner.heroId))
val interrogateOptions = Option
.when(
hasWarden && factionWithThisPrisonerAsLeader
.map(_.id)
.orElse(prisoner.lastFactionId)
.exists(fid => gameState.provinces.values.exists(_.rulingFactionId.contains(fid)))
)(PrisonerManagementOption.Interrogate)
.toVector
factionWithThisPrisonerAsLeader match {
case Some(faction) =>
Vector(
PrisonerManagementOption.Execute
) ++ moveToPids
) ++ interrogateOptions ++ moveToPids
.map(pid => PrisonerManagementOption.Move(toProvinceId = pid))
.toVector ++ Option
.when(
@@ -54,7 +65,7 @@ object AvailableManagePrisonersCommandFactory extends ScalaAvailableCommandsFact
PrisonerManagementOption.Execute,
PrisonerManagementOption.Exile,
PrisonerManagementOption.Release
) ++ moveToPids
) ++ interrogateOptions ++ moveToPids
.map(pid => PrisonerManagementOption.Move(toProvinceId = pid))
.toVector
}
@@ -72,6 +83,10 @@ object AvailableManagePrisonersCommandFactory extends ScalaAvailableCommandsFact
.provinces(provinceId)
.unaffiliatedHeroes
.filter(_.unaffiliatedHeroType == UnaffiliatedHeroType.Prisoner)
val hasWarden = gameState
.provinces(provinceId)
.rulingFactionHeroIds
.exists(heroId => gameState.heroes(heroId).profession == Profession.Warden)
Option.when(prisoners.nonEmpty) {
ManagePrisonersAvailable(
@@ -90,6 +105,7 @@ object AvailableManagePrisonersCommandFactory extends ScalaAvailableCommandsFact
provinceId,
gameState
),
hasWarden = hasWarden,
gameState
)
)
@@ -463,6 +463,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
"//src/main/scala/net/eagle0/eagle/model/state/command/common",
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
"//src/main/scala/net/eagle0/eagle/model/state/hero:profession",
"//src/main/scala/net/eagle0/eagle/model/state/unaffiliated_hero",
],
)
@@ -87,6 +87,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/settings:truce_gold_cost",
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
"//src/main/scala/net/eagle0/eagle/library/util/quest_fulfillment:quest_fulfillment_utils",
"//src/main/scala/net/eagle0/eagle/library/util/view_filters:province_view_filter",
"//src/main/scala/net/eagle0/eagle/model/action_result:action_result_trait",
"//src/main/scala/net/eagle0/eagle/model/action_result:notification_trait",
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:action_result_concrete",
@@ -750,6 +751,7 @@ scala_library(
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
"//src/main/scala/net/eagle0/eagle/library/util/quest_fulfillment:quest_fulfillment_utils",
"//src/main/scala/net/eagle0/eagle/library/util/quest_fulfillment/prisoner_management:prisoner_management_quest_matchers",
"//src/main/scala/net/eagle0/eagle/library/util/view_filters:province_view_filter",
"//src/main/scala/net/eagle0/eagle/model/action_result:action_result_trait",
"//src/main/scala/net/eagle0/eagle/model/action_result:changed_battalion_trait",
"//src/main/scala/net/eagle0/eagle/model/action_result:changed_hero_trait",
@@ -758,14 +760,18 @@ 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/concrete:notification_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
"//src/main/scala/net/eagle0/eagle/model/action_result/types",
"//src/main/scala/net/eagle0/eagle/model/state/faction",
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
"//src/main/scala/net/eagle0/eagle/model/state/province",
"//src/main/scala/net/eagle0/eagle/model/state/province:deferred_change_trait",
"//src/main/scala/net/eagle0/eagle/model/state/quest",
"//src/main/scala/net/eagle0/eagle/model/state/unaffiliated_hero",
"//src/main/scala/net/eagle0/eagle/model/view/province:province_view",
],
)
@@ -16,6 +16,7 @@ import net.eagle0.eagle.library.actions.impl.common.{
import net.eagle0.eagle.library.settings.{AllianceGoldCost, BreakAllianceGoldCost, InviteGoldCost, TruceGoldCost}
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentChecker
import net.eagle0.eagle.library.util.view_filters.ProvinceViewFilter
import net.eagle0.eagle.library.EagleCommandException
import net.eagle0.eagle.library.EagleInternalException
import net.eagle0.eagle.model.state.{BattalionTypeId, CombatUnit as ModelCombatUnit, TributeAmount}
@@ -347,6 +348,8 @@ class CommandFactory extends TCommandFactory {
.getOrElse(throw new IllegalArgumentException(s"No prisoner found for hero ${sc.prisonerHeroId}")),
chosenOption = prisonerManagementOptionMap(sc.chosenOption),
allProvinces = allProvinces(gameState),
allFactions = allFactions(gameState),
provinceViewForInterrogation = province => ProvinceViewFilter.filteredProvinceView(province, gameState),
questFulfillmentChecker = questFulfillmentChecker(gameState),
gameId = gameState.gameId,
currentRoundId = gameState.currentRoundId
@@ -796,6 +799,7 @@ class CommandFactory extends TCommandFactory {
case PrisonerManagementOption.Release => CommandPrisonerManagementOption.Release
case PrisonerManagementOption.Execute => CommandPrisonerManagementOption.Execute
case PrisonerManagementOption.Exile => CommandPrisonerManagementOption.Exile
case PrisonerManagementOption.Interrogate => CommandPrisonerManagementOption.Interrogate
case PrisonerManagementOption.Move(toProvinceId) => CommandPrisonerManagementOption.Move(toProvinceId)
case PrisonerManagementOption.Return(toFactionId) => CommandPrisonerManagementOption.Return(toFactionId)
}
@@ -1,9 +1,12 @@
package net.eagle0.eagle.library.actions.impl.command
import scala.annotation.unused
import net.eagle0.eagle.{FactionId, GameId, HeroId, ProvinceId, RoundId}
import net.eagle0.eagle.library.actions.impl.command.ManagePrisonersCommand.PrisonerManagementOption.{
Execute,
Exile,
Interrogate,
Move,
Release,
Return
@@ -11,10 +14,11 @@ import net.eagle0.eagle.library.actions.impl.command.ManagePrisonersCommand.Pris
import net.eagle0.eagle.library.actions.impl.common.ProtolessSequentialResultsAction
import net.eagle0.eagle.library.util.quest_fulfillment.prisoner_management.PrisonerManagementQuestMatchers
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentChecker
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentUtils
import net.eagle0.eagle.library.util.EagleRequire.commandRequire
import net.eagle0.eagle.model.action_result.{ActionResultT, NotificationDetails, NotificationT}
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, NotificationC}
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC, NotificationC}
import net.eagle0.eagle.model.action_result.generated_text_request.LlmRequestT.{
PrisonerExecutedMessage,
PrisonerExiledMessage,
@@ -22,9 +26,16 @@ import net.eagle0.eagle.model.action_result.generated_text_request.LlmRequestT.{
PrisonerReturnedMessage
}
import net.eagle0.eagle.model.action_result.types.ActionResultType
import net.eagle0.eagle.model.action_result.types.ActionResultType.{PrisonerExecuted, PrisonerExiled, PrisonerReleased}
import net.eagle0.eagle.model.action_result.types.ActionResultType.{
PrisonerExecuted,
PrisonerExiled,
PrisonerReleased,
ReconSucceeded
}
import net.eagle0.eagle.model.state.faction.FactionT
import net.eagle0.eagle.model.state.province.DeferredChange.{PrisonerMoved, PrisonerReturned}
import net.eagle0.eagle.model.state.province.ProvinceT
import net.eagle0.eagle.model.state.quest.ReconProvincesQuest
import net.eagle0.eagle.model.state.unaffiliated_hero.{RecruitmentInfo, UnaffiliatedHeroT}
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.{
MovingPrisoner,
@@ -32,6 +43,7 @@ import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.{
ReturningPrisoner,
Traveler
}
import net.eagle0.eagle.model.view.province.ProvinceView
object ManagePrisonersCommand {
sealed trait PrisonerManagementOption
@@ -45,6 +57,8 @@ object ManagePrisonersCommand {
case class Move(toProvinceId: ProvinceId) extends PrisonerManagementOption
case object Execute extends PrisonerManagementOption
case object Interrogate extends PrisonerManagementOption
}
case class PrisonerToManage(
@@ -58,6 +72,8 @@ object ManagePrisonersCommand {
prisonerUnaffiliatedHero: UnaffiliatedHeroT,
selectedOption: PrisonerManagementOption,
allProvinces: Vector[ProvinceT],
allFactions: Vector[FactionT],
provinceViewForInterrogation: ProvinceT => ProvinceView,
questFulfillmentChecker: QuestFulfillmentChecker,
gameId: GameId,
currentRoundId: RoundId
@@ -278,6 +294,33 @@ object ManagePrisonersCommand {
isFailed = (quest, _ /*questHolderProvince*/ ) => releaseAllPrisonersQuestMatch(quest)
)
}
case Interrogate =>
val interrogatedFactionId = prisonerUnaffiliatedHero.lastFactionId
.orElse(allFactions.find(_.leaderIds.contains(prisonerUnaffiliatedHero.heroId)).map(_.id))
.getOrElse(
throw new IllegalArgumentException(
s"No faction found to interrogate prisoner ${prisonerUnaffiliatedHero.heroId} about"
)
)
val targetProvince = allProvinces
.find(_.rulingFactionId.contains(interrogatedFactionId))
.getOrElse(
throw new IllegalArgumentException(s"No province found for faction $interrogatedFactionId")
)
val factionProvinces = allProvinces.filter(_.rulingFactionId.contains(actingFactionId))
val baseResult = ActionResultC(
actionResultType = ReconSucceeded,
actingFactionId = Some(actingFactionId),
provinceId = Some(provinceId),
changedFactions = Vector(
ChangedFactionC(
factionId = actingFactionId,
updatedReconnedProvinces = Vector(provinceViewForInterrogation(targetProvince))
)
)
)
Vector(reconProvincesQuestIncrementer(factionProvinces)(baseResult))
case Execute =>
val llmRequestId =
s"prisoner_executed $currentRoundId ${prisonerUnaffiliatedHero.heroId}"
@@ -341,6 +384,16 @@ object ManagePrisonersCommand {
prisonerUnaffiliatedHero: UnaffiliatedHeroT,
chosenOption: PrisonerManagementOption,
allProvinces: Vector[ProvinceT],
allFactions: Vector[FactionT] = Vector.empty,
provinceViewForInterrogation: ProvinceT => ProvinceView = province =>
ProvinceView(
id = province.id,
name = province.name,
rulingFactionId = province.rulingFactionId,
fullInfo = None,
knownEvents = Vector.empty,
asOf = None
),
questFulfillmentChecker: QuestFulfillmentChecker,
gameId: GameId,
currentRoundId: RoundId
@@ -373,9 +426,29 @@ object ManagePrisonersCommand {
prisonerUnaffiliatedHero = prisonerUnaffiliatedHero,
selectedOption = chosenOption,
allProvinces = allProvinces,
allFactions = allFactions,
provinceViewForInterrogation = provinceViewForInterrogation,
questFulfillmentChecker = questFulfillmentChecker,
gameId = gameId,
currentRoundId = currentRoundId
)
}
private def reconProvincesQuestCheck(
uh: UnaffiliatedHeroT,
@unused province: ProvinceT
): Boolean =
uh.quest.exists {
case q: ReconProvincesQuest => q.componentsFulfilled < q.componentCount
case _ => false
}
private def reconProvincesQuestIncrementer(
factionProvinces: Vector[ProvinceT]
): ActionResultT => ActionResultT =
QuestFulfillmentUtils.withCountersIncremented(
provinces = factionProvinces,
incrementAmount = 1,
check = reconProvincesQuestCheck
)
}
@@ -25,6 +25,7 @@ import net.eagle0.eagle.api.command.util.prisoner_management_type.{
PrisonerManagementOption as PrisonerManagementOptionProto,
PrisonerManagementOptionExecute,
PrisonerManagementOptionExile,
PrisonerManagementOptionInterrogate,
PrisonerManagementOptionMove,
PrisonerManagementOptionRelease,
PrisonerManagementOptionReturn
@@ -881,6 +882,7 @@ object AvailableCommandConverter {
case PrisonerManagementOption.Release => PrisonerManagementOptionRelease()
case PrisonerManagementOption.Execute => PrisonerManagementOptionExecute()
case PrisonerManagementOption.Exile => PrisonerManagementOptionExile()
case PrisonerManagementOption.Interrogate => PrisonerManagementOptionInterrogate()
case PrisonerManagementOption.Move(toProvinceId) => PrisonerManagementOptionMove(toProvinceId = toProvinceId)
case PrisonerManagementOption.Return(toFactionId) => PrisonerManagementOptionReturn(toFactionId = toFactionId)
}
@@ -21,6 +21,7 @@ import net.eagle0.eagle.api.command.util.prisoner_management_type.{
PrisonerManagementOption as PrisonerManagementOptionProto,
PrisonerManagementOptionExecute,
PrisonerManagementOptionExile,
PrisonerManagementOptionInterrogate,
PrisonerManagementOptionMove,
PrisonerManagementOptionRelease,
PrisonerManagementOptionReturn
@@ -432,12 +433,13 @@ object SelectedCommandConverter {
private def prisonerManagementOptionFromProto(opt: PrisonerManagementOptionProto): PrisonerManagementOption =
opt match {
case _: PrisonerManagementOptionRelease => PrisonerManagementOption.Release
case _: PrisonerManagementOptionExecute => PrisonerManagementOption.Execute
case _: PrisonerManagementOptionExile => PrisonerManagementOption.Exile
case move: PrisonerManagementOptionMove => PrisonerManagementOption.Move(toProvinceId = move.toProvinceId)
case ret: PrisonerManagementOptionReturn => PrisonerManagementOption.Return(toFactionId = ret.toFactionId)
case PrisonerManagementOptionProto.Empty =>
case _: PrisonerManagementOptionRelease => PrisonerManagementOption.Release
case _: PrisonerManagementOptionExecute => PrisonerManagementOption.Execute
case _: PrisonerManagementOptionExile => PrisonerManagementOption.Exile
case _: PrisonerManagementOptionInterrogate => PrisonerManagementOption.Interrogate
case move: PrisonerManagementOptionMove => PrisonerManagementOption.Move(toProvinceId = move.toProvinceId)
case ret: PrisonerManagementOptionReturn => PrisonerManagementOption.Return(toFactionId = ret.toFactionId)
case PrisonerManagementOptionProto.Empty =>
throw new ProtoConversionException("PrisonerManagementOption has no value set")
}
@@ -33,6 +33,7 @@ enum PrisonerManagementOption:
case Release
case Execute
case Exile
case Interrogate
case Move(toProvinceId: ProvinceId)
case Return(toFactionId: FactionId)
@@ -8,6 +8,7 @@ import net.eagle0.eagle.model.state.command.common.PrisonerManagementOption
import net.eagle0.eagle.model.state.faction.concrete.FactionC
import net.eagle0.eagle.model.state.game_state.GameState
import net.eagle0.eagle.model.state.hero.concrete.HeroC
import net.eagle0.eagle.model.state.hero.Profession
import net.eagle0.eagle.model.state.province.concrete.ProvinceC
import net.eagle0.eagle.model.state.province.Neighbor
import net.eagle0.eagle.model.state.run_status.RunStatus
@@ -28,6 +29,7 @@ class AvailableManagePrisonerCommandsFactoryTest extends AnyFlatSpec with Before
private val actingProvinceId: ProvinceId = 5
private val enemyFactionLeaderHid: HeroId = 19
private val wardenHeroId: HeroId = 23
private val neighbor1ProvinceId: ProvinceId = 21
private val neighbor2ProvinceId: ProvinceId = 31
@@ -45,6 +47,7 @@ class AvailableManagePrisonerCommandsFactoryTest extends AnyFlatSpec with Before
heroes = Map(
17 -> HeroC(id = 17),
18 -> HeroC(id = 18),
wardenHeroId -> HeroC(id = wardenHeroId, profession = Profession.Warden),
enemyFactionLeaderHid -> HeroC(id = enemyFactionLeaderHid)
),
provinces = Map(
@@ -196,6 +199,62 @@ class AvailableManagePrisonerCommandsFactoryTest extends AnyFlatSpec with Before
}
}
it should "not include an Interrogate option without a Warden in the province" in {
val ac = getManagePrisonersCommand(baseGameState)
inside(ac.prisoners.find(_.prisoner.heroId == enemyFactionLeaderHid)) {
case Some(prisoner) =>
prisoner.availableOptions should not contain PrisonerManagementOption.Interrogate
}
}
it should "include an Interrogate option for a faction leader when a Warden is in the province" in {
val actingProvince = inside(baseGameState.provinces(actingProvinceId)) {
case province: ProvinceC => province
}
val gs = baseGameState.copy(
provinces = baseGameState.provinces.updated(
actingProvinceId,
actingProvince.copy(rulingFactionHeroIds = Vector(wardenHeroId))
)
)
val ac = getManagePrisonersCommand(gs)
inside(ac.prisoners.find(_.prisoner.heroId == enemyFactionLeaderHid)) {
case Some(prisoner) =>
prisoner.availableOptions should contain(PrisonerManagementOption.Interrogate)
}
}
it should "include an Interrogate option for a prisoner with a last faction when a Warden is in the province" in {
val actingProvince = inside(baseGameState.provinces(actingProvinceId)) {
case province: ProvinceC => province
}
val prisoner = inside(actingProvince.unaffiliatedHeroes.find(_.heroId == 17)) {
case Some(uh: UnaffiliatedHeroC) => uh
}
val gs = baseGameState.copy(
provinces = baseGameState.provinces.updated(
actingProvinceId,
actingProvince.copy(
rulingFactionHeroIds = Vector(wardenHeroId),
unaffiliatedHeroes = actingProvince.unaffiliatedHeroes.map {
case uh if uh.heroId == 17 => prisoner.copy(lastFactionId = Some(enemyFactionId))
case uh => uh
}
)
)
)
val ac = getManagePrisonersCommand(gs)
inside(ac.prisoners.find(_.prisoner.heroId == 17)) {
case Some(prisoner) =>
prisoner.availableOptions should contain(PrisonerManagementOption.Interrogate)
}
}
it should "return a Move option if there is an owned adjacent province" in {
val neighborProvince = inside(baseGameState.provinces(neighbor1ProvinceId)) {
case province: ProvinceC =>
@@ -526,14 +526,17 @@ scala_test(
"//src/main/scala/net/eagle0/eagle/model/action_result:changed_hero_trait",
"//src/main/scala/net/eagle0/eagle/model/action_result:notification_trait",
"//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:notification_concrete",
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
"//src/main/scala/net/eagle0/eagle/model/action_result/types",
"//src/main/scala/net/eagle0/eagle/model/state/faction/concrete",
"//src/main/scala/net/eagle0/eagle/model/state/province",
"//src/main/scala/net/eagle0/eagle/model/state/province/concrete",
"//src/main/scala/net/eagle0/eagle/model/state/quest",
"//src/main/scala/net/eagle0/eagle/model/state/unaffiliated_hero",
"//src/main/scala/net/eagle0/eagle/model/state/unaffiliated_hero/concrete",
"//src/main/scala/net/eagle0/eagle/model/view/province:province_view",
],
)
@@ -6,7 +6,7 @@ import net.eagle0.eagle.library.settings.{QuestFailedRecruitmentPenalty, QuestRe
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentChecker
import net.eagle0.eagle.library.EagleCommandException
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
import net.eagle0.eagle.model.action_result.concrete.NotificationC
import net.eagle0.eagle.model.action_result.concrete.{ChangedFactionC, NotificationC}
import net.eagle0.eagle.model.action_result.generated_text_request.LlmRequestT.PrisonerExecutedMessage
import net.eagle0.eagle.model.action_result.types.ActionResultType
import net.eagle0.eagle.model.action_result.types.ActionResultType.{
@@ -14,11 +14,13 @@ import net.eagle0.eagle.model.action_result.types.ActionResultType.{
PrisonerExiled,
PrisonerReleased,
QuestFailed,
QuestFulfilled
QuestFulfilled,
ReconSucceeded
}
import net.eagle0.eagle.model.action_result.NotificationDetails
import net.eagle0.eagle.model.action_result.NotificationT
import net.eagle0.eagle.model.state.date.Date
import net.eagle0.eagle.model.state.faction.concrete.FactionC
import net.eagle0.eagle.model.state.province.concrete.ProvinceC
import net.eagle0.eagle.model.state.province.DeferredChange.{PrisonerMoved, PrisonerReturned}
import net.eagle0.eagle.model.state.province.Neighbor
@@ -38,6 +40,7 @@ import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.{
ReturningPrisoner,
Traveler
}
import net.eagle0.eagle.model.view.province.ProvinceView
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatest.BeforeAndAfterEach
@@ -101,6 +104,21 @@ class ManagePrisonersCommandTest extends AnyFlatSpec with Matchers with BeforeAn
)
)
private val allFactions = Vector(
FactionC(id = actingFactionId, factionHeadId = 1, name = "Acting Faction", leaderIds = Vector(1)),
FactionC(id = enemyFactionId, factionHeadId = 97, name = "Enemy Faction", leaderIds = Vector(97))
)
private def provinceViewForInterrogation(province: net.eagle0.eagle.model.state.province.ProvinceT): ProvinceView =
ProvinceView(
id = province.id,
name = province.name,
rulingFactionId = province.rulingFactionId,
fullInfo = None,
knownEvents = Vector.empty,
asOf = None
)
private val heroIdDesiringExecute = 983
private val heroIdDesiringRelease = 984
private val heroIdDesiringExile = 985
@@ -169,6 +187,7 @@ class ManagePrisonersCommandTest extends AnyFlatSpec with Matchers with BeforeAn
heroId = 97,
availableOptions = Vector(
PrisonerManagementOption.Execute,
PrisonerManagementOption.Interrogate,
PrisonerManagementOption.Return(enemyFactionId),
PrisonerManagementOption.Move(toProvinceId = neighboringProvinceId)
)
@@ -228,7 +247,35 @@ class ManagePrisonersCommandTest extends AnyFlatSpec with Matchers with BeforeAn
currentRoundId = 17
)
ex.getMessage shouldBe "requirement failed: Selected management type Release was not among available options Vector(Execute, Return(98), Move(17))"
ex.getMessage shouldBe "requirement failed: Selected management type Release was not among available options Vector(Execute, Interrogate, Return(98), Move(17))"
}
"interrogate" should "reveal a province occupied by the prisoner's faction" in {
val ar = ManagePrisonersCommand
.make(
actingFactionId = actingFactionId,
actingProvinceId = actingProvinceId,
availablePrisoners = availablePrisoners,
prisonerUnaffiliatedHero = firstUH97,
chosenOption = PrisonerManagementOption.Interrogate,
allProvinces = allProvinces,
allFactions = allFactions,
provinceViewForInterrogation = provinceViewForInterrogation,
questFulfillmentChecker = questFulfillmentChecker,
gameId = 0xcafeface,
currentRoundId = 17
)
.results
.loneElement
ar.actionResultType shouldBe ReconSucceeded
ar.actingFactionId shouldBe Some(actingFactionId)
ar.provinceId shouldBe Some(actingProvinceId)
inside(ar.changedFactions.loneElement) {
case changedFaction: ChangedFactionC =>
changedFaction.factionId shouldBe actingFactionId
changedFaction.updatedReconnedProvinces.map(_.id) shouldBe Vector(enemyProvinceId)
}
}
"execute" should "set the basics" in {
@@ -21,6 +21,7 @@ import net.eagle0.eagle.api.command.util.prisoner_management_type.{
PrisonerManagementOption as PrisonerManagementOptionProto,
PrisonerManagementOptionExecute,
PrisonerManagementOptionExile,
PrisonerManagementOptionInterrogate,
PrisonerManagementOptionMove,
PrisonerManagementOptionRelease,
PrisonerManagementOptionReturn
@@ -525,6 +526,7 @@ class SelectedCommandConverterTest extends AnyFlatSpec with Matchers {
convert(PrisonerManagementOptionRelease()) shouldBe PrisonerManagementOption.Release
convert(PrisonerManagementOptionExecute()) shouldBe PrisonerManagementOption.Execute
convert(PrisonerManagementOptionExile()) shouldBe PrisonerManagementOption.Exile
convert(PrisonerManagementOptionInterrogate()) shouldBe PrisonerManagementOption.Interrogate
convert(PrisonerManagementOptionMove(toProvinceId = 42)) shouldBe PrisonerManagementOption.Move(toProvinceId = 42)
convert(PrisonerManagementOptionReturn(toFactionId = 7)) shouldBe PrisonerManagementOption.Return(toFactionId = 7)
}