mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 04:25:42 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d8f05ef99 | ||
|
|
846d0e85aa |
@@ -9,16 +9,15 @@ This document analyzes all actions and commands in `src/main/scala/net/eagle0/ea
|
||||
|
||||
## Summary
|
||||
|
||||
Based on BUILD.bazel dependency analysis (2025-09-16, updated 2025-09-17):
|
||||
Based on BUILD.bazel dependency analysis (2025-09-16):
|
||||
- **Total Commands Analyzed:** 41
|
||||
- **Commands Fully Migrated (No Protobuf):** 41 (100%) ✅
|
||||
- **Commands Still Using Protobuf:** 0 (0%) ✅
|
||||
- **Total Actions Analyzed:** 48
|
||||
- **Actions Fully Migrated (No Protobuf):** 5 (10.4%)
|
||||
- **Actions Partially Migrated:** 19 (39.6%)
|
||||
- **Actions Fully Migrated (No Protobuf):** 4 (8.3%)
|
||||
- **Actions Partially Migrated:** 20 (41.7%)
|
||||
- **Actions Still Using Protobuf:** 24 (50%)
|
||||
- **Base Classes:** 8 protoless variants available, 6 still use protobuf
|
||||
- **Shared Components:** `ResolvedEagleUnit` migrated to use `Option[BattalionT]` for proper null handling
|
||||
|
||||
## Conversion Insights
|
||||
|
||||
@@ -94,7 +93,6 @@ These actions have been successfully migrated to use Scala models only:
|
||||
| File | Base Class | Notes |
|
||||
|------|------------|-------|
|
||||
| HeroBackstoryUpdateAction.scala | ProtolessSequentialResultsAction | Processes hero backstory updates with LLM integration |
|
||||
| ProvinceConqueredAction.scala | ProtolessSimpleAction | Uses component-based design (gameId, currentRoundId, currentDate, Scala models) |
|
||||
| ProvinceHeldAction.scala | ProtolessSimpleAction | Uses specific components (gameId, currentRoundId, defendingProvince, etc.) instead of full GameState |
|
||||
| UnaffiliatedHeroAppearedAction.scala | ProtolessSimpleAction | Handles unaffiliated hero appearance with name generation |
|
||||
| WithdrawnArmiesReturnHomeAction.scala | ProtolessSequentialResultsAction | Manages army withdrawal and return mechanics |
|
||||
@@ -117,7 +115,7 @@ These actions use protoless base classes but still have some protobuf dependenci
|
||||
| FreeForAllDrawAction.scala | ProtolessSimpleAction | Depends on multiple protobuf targets |
|
||||
| FriendlyMoveAction.scala | ProtolessSimpleAction | Depends on multiple protobuf targets |
|
||||
| PerformUncontestedConquestAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
|
||||
| ProvinceConqueredAction.scala | ProtolessSimpleAction | **CONVERTED** - Uses specific components (gameId, currentRoundId, currentDate, Scala models) |
|
||||
| ProvinceConqueredAction.scala | ProtolessSimpleAction | Depends on `common_unit_scala_proto` |
|
||||
| SafePassageArmiesProceedAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
|
||||
| ShipmentArrivedAction.scala | ProtolessSimpleAction | Depends on multiple protobuf targets |
|
||||
| TruceTurnBackPhaseAction.scala | ProtolessSequentialResultsAction | Depends on multiple protobuf targets |
|
||||
@@ -248,30 +246,21 @@ Based on the BUILD.bazel dependency analysis, here are the key findings and reco
|
||||
### 🎯 Action Migration Progress
|
||||
|
||||
**Migration Statistics:**
|
||||
- 5/48 Actions fully migrated (10.4%)
|
||||
- 4/48 Actions fully migrated (8.3%)
|
||||
- 20/48 Actions using protoless base classes but with protobuf dependencies (41.7%)
|
||||
- 24/48 Actions still fully on protobuf (50%)
|
||||
|
||||
**Successfully Migrated Actions:**
|
||||
1. **HeroBackstoryUpdateAction** - LLM integration for hero backstories
|
||||
2. **ProvinceConqueredAction** - Component-based design with prisoner handling and province conquest
|
||||
3. **ProvinceHeldAction** - Component-based design pattern (gameId, currentRoundId, specific models)
|
||||
4. **UnaffiliatedHeroAppearedAction** - Hero appearance with name generation
|
||||
5. **WithdrawnArmiesReturnHomeAction** - Army withdrawal mechanics
|
||||
|
||||
**Recent Migration Updates (2025-09-17):**
|
||||
- **ResolvedEagleUnit** - Changed `battalion: BattalionT` to `battalion: Option[BattalionT]`
|
||||
- Properly handles units without battalions (battalion ID -1)
|
||||
- Updated `ShardokInterfaceGrpcClient` to check for `defaultBattalionId` and use `None`
|
||||
- Updated `ResolveBattleAction`, `ProvinceConqueredAction`, `RequestBattlesAction`
|
||||
- All tests updated to handle optional battalions
|
||||
2. **ProvinceHeldAction** - Component-based design pattern (gameId, currentRoundId, specific models)
|
||||
3. **UnaffiliatedHeroAppearedAction** - Hero appearance with name generation
|
||||
4. **WithdrawnArmiesReturnHomeAction** - Army withdrawal mechanics
|
||||
|
||||
**Key Migration Patterns:**
|
||||
- ✅ Use specific components instead of full GameState (see ProvinceHeldAction, ProvinceConqueredAction)
|
||||
- ✅ Use specific components instead of full GameState (see ProvinceHeldAction)
|
||||
- ✅ Convert protobuf models to Scala models at Action boundaries
|
||||
- ✅ Update BUILD.bazel to remove protobuf dependencies
|
||||
- ✅ Update all call sites and tests
|
||||
- ✅ Use `Option[T]` for optional fields instead of special sentinel values (e.g., battalion ID -1)
|
||||
|
||||
**Next Migration Candidates (Simple Actions with Protoless Base):**
|
||||
1. **FreeForAllDrawAction** - Already uses ProtolessSimpleAction
|
||||
@@ -301,5 +290,4 @@ Based on the BUILD.bazel dependency analysis, here are the key findings and reco
|
||||
|
||||
---
|
||||
|
||||
*Updated on 2025-09-17 - Analysis based on BUILD.bazel dependencies and code review*
|
||||
*Latest update: ResolvedEagleUnit migrated to use Option[BattalionT] for proper battalion handling*
|
||||
*Updated on 2025-09-16 - Analysis based on BUILD.bazel dependencies and code review*
|
||||
+6
-17
@@ -48,7 +48,6 @@ namespace eagle {
|
||||
private PlayerId _playerId;
|
||||
private int _recommendedFood;
|
||||
private int _recommendedGold;
|
||||
private List<AvailableDestinationProvince> _sortedAvailableDestinations;
|
||||
|
||||
public override AvailableCommand.SealedValueOneofCase CommandType =>
|
||||
AvailableCommand.SealedValueOneofCase.MarchCommand;
|
||||
@@ -121,13 +120,9 @@ namespace eagle {
|
||||
DestinationProvince is { RulingFactionId : not null } &&
|
||||
DestinationProvince.RulingFactionId.Value != _playerId;
|
||||
|
||||
private bool RequiresRiverCrossing => SelectedDestinationProvinceIndex is {}
|
||||
idx && _sortedAvailableDestinations != null && idx < _sortedAvailableDestinations.Count &&
|
||||
_sortedAvailableDestinations[idx].RequiresRiverCrossing;
|
||||
|
||||
public override bool WarnOnCommitButton =>
|
||||
WouldAbandonProvince || NotEnoughFood || MarchingOnTrucePartner ||
|
||||
BadlyConstructedUnits || NoBattalionsIntoEnemy || RequiresRiverCrossing;
|
||||
public override bool WarnOnCommitButton => WouldAbandonProvince || NotEnoughFood ||
|
||||
MarchingOnTrucePartner ||
|
||||
BadlyConstructedUnits || NoBattalionsIntoEnemy;
|
||||
public override string CommitWarningText {
|
||||
get {
|
||||
if (!(WarnOnCommitButton)) return null;
|
||||
@@ -152,9 +147,6 @@ namespace eagle {
|
||||
txt += "Some of your heroes are leading battalion types that would restrict their abilities\n\n";
|
||||
}
|
||||
}
|
||||
if (RequiresRiverCrossing) {
|
||||
txt += $"Assaulting {DestinationProvince.Name} may require crossing a river\n\n";
|
||||
}
|
||||
|
||||
return txt;
|
||||
}
|
||||
@@ -218,12 +210,9 @@ namespace eagle {
|
||||
}
|
||||
|
||||
private void SetUpDestinationProvinceDropdown() {
|
||||
_sortedAvailableDestinations =
|
||||
SelectedOneProvinceCommand.AvailableDestinationProvinces
|
||||
.OrderBy(adp => _model.Provinces[adp.ProvinceId].Name)
|
||||
.ToList();
|
||||
_sortedDestinationProvinceIds =
|
||||
_sortedAvailableDestinations.Select(adp => adp.ProvinceId).ToList();
|
||||
_sortedDestinationProvinceIds = SelectedOneProvinceCommand.AvailableDestinationProvinces
|
||||
.OrderBy(pid => _model.Provinces[pid].Name)
|
||||
.ToList();
|
||||
var destinationProvinceNames = _sortedDestinationProvinceIds
|
||||
.Select(id => GUIUtils.DarkColoredProvinceName(
|
||||
_model.Provinces[id],
|
||||
|
||||
+1
-10
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using Net.Eagle0.Eagle.Api;
|
||||
using Net.Eagle0.Eagle.Views;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace eagle {
|
||||
@@ -23,9 +21,6 @@ namespace eagle {
|
||||
|
||||
private TradeAvailableCommand TradeCommand => _availableCommand.TradeCommand;
|
||||
private ProvinceId ActingProvinceId => TradeCommand.ActingProvinceId;
|
||||
private ProvinceView ActingProvince => _model.Provinces[ActingProvinceId];
|
||||
private int ProvinceGoldCap => ActingProvince.FullInfo.GoldCap;
|
||||
private int ProvinceFoodCap => ActingProvince.FullInfo.FoodCap;
|
||||
|
||||
private int MaxFood =>
|
||||
TradeCommand.FoodAvailable +
|
||||
@@ -53,8 +48,7 @@ namespace eagle {
|
||||
SetTextFields();
|
||||
}
|
||||
|
||||
public override bool EnableCommitButton =>
|
||||
!Mathf.Approximately(slider.value, TradeCommand.FoodAvailable);
|
||||
public override bool EnableCommitButton => slider.value != TradeCommand.FoodAvailable;
|
||||
public override string DisabledCommitButtonReason => "No change";
|
||||
|
||||
private bool IsBuy => FoodAmount > TradeCommand.FoodAvailable;
|
||||
@@ -74,10 +68,7 @@ namespace eagle {
|
||||
|
||||
public void SetTextFields() {
|
||||
goldText.text = GoldAmount.ToString();
|
||||
goldText.color = GoldAmount > ProvinceGoldCap ? Color.red : Color.black;
|
||||
|
||||
foodText.text = FoodAmount.ToString();
|
||||
foodText.color = FoodAmount > ProvinceFoodCap ? Color.red : Color.black;
|
||||
|
||||
description.text = IsBuy ? $"Buying {NetFood} food for {-NetGold} gold"
|
||||
: $"Selling {-NetFood} food for {NetGold} gold";
|
||||
|
||||
@@ -244,11 +244,6 @@ message ManagePrisonersAvailableCommand {
|
||||
repeated PrisonerToManage prisoners = 2;
|
||||
}
|
||||
|
||||
message AvailableDestinationProvince {
|
||||
int32 province_id = 1;
|
||||
bool requires_river_crossing = 2;
|
||||
}
|
||||
|
||||
message MarchCommandFromOneProvince {
|
||||
map<int32, .net.eagle0.eagle.api.command.util.SuitableBattalions> suitable_battalions_for_heroes = 1;
|
||||
|
||||
@@ -259,7 +254,7 @@ message MarchCommandFromOneProvince {
|
||||
|
||||
int32 food_available = 6;
|
||||
|
||||
repeated AvailableDestinationProvince available_destination_provinces = 7;
|
||||
repeated int32 available_destination_provinces = 7;
|
||||
repeated int32 available_hero_ids = 8;
|
||||
repeated .net.eagle0.eagle.api.command.util.BattalionWithFoodCost available_battalions = 10;
|
||||
|
||||
|
||||
@@ -35,7 +35,9 @@ object FixLeaderAloneCommandSelector {
|
||||
)
|
||||
) { opmc => fr =>
|
||||
opmc.availableDestinationProvinces
|
||||
.map(dest => gameState.provinces(dest.provinceId))
|
||||
.map(
|
||||
gameState.provinces
|
||||
)
|
||||
.filter(_.rulingFactionId.contains(actingFactionId))
|
||||
.filter(_.rulingFactionHeroIds.size == 1)
|
||||
.filter(dest =>
|
||||
|
||||
@@ -27,7 +27,7 @@ object MarchTowardProvinceCommandChooser {
|
||||
): Option[CommandSelection] = {
|
||||
val bestCommand =
|
||||
opmc.availableDestinationProvinces
|
||||
.map(dest => (opmc, gs.provinces(dest.provinceId)))
|
||||
.map(dest => (opmc, gs.provinces(dest)))
|
||||
.flatMap { case (opmc, p) =>
|
||||
ProvinceDistances
|
||||
.distanceThroughFriendliesOption(
|
||||
|
||||
@@ -1063,7 +1063,7 @@ object MidGameAIClient {
|
||||
.intersect(factionLeaders)
|
||||
.maxBy(hid => LegacyHeroUtils.seniorityOrder(faction, hid))
|
||||
val validDestinations = opmc.availableDestinationProvinces
|
||||
.map(dest => gameState.provinces(dest.provinceId))
|
||||
.map(gameState.provinces)
|
||||
.filter(
|
||||
_.rulingFactionId.contains(actingFactionId)
|
||||
) // only send to a province we own
|
||||
|
||||
@@ -62,7 +62,7 @@ object MoveLeaderToBetterProvinceCommandChooser {
|
||||
ProvinceDistances
|
||||
.closestProvinceThroughFriendliesOption(
|
||||
desiredPid,
|
||||
mcfop.availableDestinationProvinces.map(_.provinceId).toVector,
|
||||
mcfop.availableDestinationProvinces.toVector,
|
||||
actingFactionId,
|
||||
gs
|
||||
)
|
||||
|
||||
@@ -48,7 +48,7 @@ object SeekMoreLeadersCommandChooser {
|
||||
.get
|
||||
val bestDestination =
|
||||
opmc.availableDestinationProvinces
|
||||
.map(dest => (opmc, gameState.provinces(dest.provinceId)))
|
||||
.map(dest => (opmc, gameState.provinces(dest)))
|
||||
.flatMap { case (opmc, p) =>
|
||||
ProvinceDistances
|
||||
.distanceThroughFriendliesOption(
|
||||
|
||||
@@ -68,19 +68,17 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types/base:action_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/shardok_battle:shardok_battle_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/shardok_battle",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_resolution",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_update",
|
||||
],
|
||||
@@ -217,18 +215,8 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types/base:action_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:action_result_proto_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_type_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -29,12 +29,11 @@ import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.library.util.hero_generator.HeroGenerator
|
||||
import net.eagle0.eagle.library.util.validations.RuntimeValidator
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.battalion.BattalionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.proto_converters.shardok_battle.ShardokBattleConverter
|
||||
import net.eagle0.eagle.shardok_interface.{BattleResolution, BattleUpdate}
|
||||
import net.eagle0.eagle.views.action_result_view.ActionResultView
|
||||
import net.eagle0.eagle.{FactionId, GameId, ProvinceId}
|
||||
@@ -232,25 +231,9 @@ case class EngineImpl(
|
||||
results = actionResultProtoApplier.applyActionResults(
|
||||
startingState = currentState,
|
||||
results = ResolveBattleAction(
|
||||
gameId = gameId,
|
||||
currentRoundId = currentState.currentRoundId,
|
||||
currentDate = DateConverter.fromProto(currentState.currentDate),
|
||||
heroes =
|
||||
currentState.heroes.view.mapValues(HeroConverter.fromProto).toMap,
|
||||
battalions = currentState.battalions.view
|
||||
.mapValues(BattalionConverter.fromProto)
|
||||
.toMap,
|
||||
provinces = currentState.provinces.view
|
||||
.mapValues(ProvinceConverter.fromProto)
|
||||
.toMap,
|
||||
factions = currentState.factions.view
|
||||
.mapValues(FactionConverter.fromProto)
|
||||
.toMap,
|
||||
outstandingBattles = currentState.outstandingBattles.toVector.map(
|
||||
ShardokBattleConverter.fromProto
|
||||
),
|
||||
startingGameState = currentState,
|
||||
resolvedBattles = Vector(battleResolution)
|
||||
).results.map(_.asInstanceOf[ActionResult])
|
||||
).execute(actionResultProtoApplier).map(_.actionResult)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -17,12 +17,7 @@ import net.eagle0.eagle.library.util.EagleRequire.internalValidated
|
||||
import net.eagle0.eagle.library.util.hero_generator.HeroGenerator
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.proto_converters.ActionResultProtoConverter
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionTypeConverter
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
@@ -197,27 +192,7 @@ object RoundPhaseAdvancer {
|
||||
case UNCONTESTED_CONQUEST =>
|
||||
actionResultProtoApplier.applyActionResults(
|
||||
currentState,
|
||||
PerformUncontestedConquestAction(
|
||||
gameId = currentState.gameId,
|
||||
currentRoundId = currentState.currentRoundId,
|
||||
currentDate = DateConverter.fromProto(currentState.currentDate),
|
||||
provinces = currentState.provinces.values
|
||||
.map(ProvinceConverter.fromProto)
|
||||
.map(p => p.id -> p)
|
||||
.toMap,
|
||||
factions = currentState.factions.values
|
||||
.map(FactionConverter.fromProto)
|
||||
.map(f => f.id -> f)
|
||||
.toMap,
|
||||
heroes = currentState.heroes.values
|
||||
.map(HeroConverter.fromProto)
|
||||
.map(h => h.id -> h)
|
||||
.toMap,
|
||||
battalions = currentState.battalions.values
|
||||
.map(BattalionConverter.fromProto)
|
||||
.map(b => (b.id, b))
|
||||
.toMap
|
||||
).results
|
||||
PerformUncontestedConquestAction(currentState).results
|
||||
.map(ActionResultProtoConverter.toProto)
|
||||
)
|
||||
|
||||
@@ -265,33 +240,8 @@ object RoundPhaseAdvancer {
|
||||
TruceTurnBackPhaseAction(currentState).execute(actionResultProtoApplier)
|
||||
|
||||
case BATTLE_REQUEST =>
|
||||
val requestBattlesAction = RequestBattlesAction(
|
||||
gameId = currentState.gameId,
|
||||
currentRoundId = currentState.currentRoundId,
|
||||
currentDate = DateConverter.fromProto(currentState.currentDate),
|
||||
battleCounter = currentState.battleCounter,
|
||||
heroes =
|
||||
currentState.heroes.view.mapValues(HeroConverter.fromProto).toMap,
|
||||
battalions = currentState.battalions.view
|
||||
.mapValues(BattalionConverter.fromProto)
|
||||
.toMap,
|
||||
provinces = currentState.provinces.view
|
||||
.mapValues(ProvinceConverter.fromProto)
|
||||
.toMap,
|
||||
factions = currentState.factions.view
|
||||
.mapValues(FactionConverter.fromProto)
|
||||
.toMap,
|
||||
battalionTypes = currentState.battalionTypes.map { bt =>
|
||||
val converted = BattalionTypeConverter.fromProto(bt)
|
||||
converted.typeId -> converted
|
||||
}.toMap
|
||||
)
|
||||
val requestResults = actionResultProtoApplier.applyActionResults(
|
||||
currentState,
|
||||
requestBattlesAction.results.map(
|
||||
ActionResultProtoConverter.toProto(_)
|
||||
)
|
||||
)
|
||||
val requestResults =
|
||||
RequestBattlesAction(currentState).execute(actionResultProtoApplier)
|
||||
requestResults ++ EndBattleRequestPhaseAction(
|
||||
requestResults.lastOption.map(_.gameState).getOrElse(currentState)
|
||||
).execute(actionResultProtoApplier)
|
||||
|
||||
+2
-21
@@ -1,7 +1,6 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableDestinationProvince,
|
||||
MarchAvailableCommand,
|
||||
MarchCommandFromOneProvince,
|
||||
RecommendedBattleConfig
|
||||
@@ -21,13 +20,11 @@ import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.{
|
||||
BattalionSuitability,
|
||||
LegacyBattalionUtils,
|
||||
ShardokMapInfo
|
||||
LegacyBattalionUtils
|
||||
}
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId}
|
||||
|
||||
object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
||||
|
||||
private def oneOriginProvinceCommand(
|
||||
gameState: GameState,
|
||||
originProvince: Province
|
||||
@@ -36,23 +33,7 @@ object AvailableMarchCommandFactory extends AvailableCommandsFactoryForType {
|
||||
.map(_.provinceId)
|
||||
.flatMap(gameState.provinces.get)
|
||||
.filterNot(LegacyProvinceUtils.hasBlizzard)
|
||||
.map { destinationProvince =>
|
||||
val startingPositionIndex = destinationProvince.neighbors
|
||||
.find(n => n.provinceId == originProvince.id)
|
||||
.get
|
||||
.startingPositionIndex
|
||||
|
||||
val mapInfo = ShardokMapInfo.shardokMapInfos(destinationProvince.id)
|
||||
val destinationCastleCount = mapInfo.castleCount
|
||||
val requiresRiverCrossing = mapInfo.waterCrossingRequirementsByPosition(
|
||||
startingPositionIndex
|
||||
) >= 10
|
||||
|
||||
AvailableDestinationProvince(
|
||||
provinceId = destinationProvince.id,
|
||||
requiresRiverCrossing = requiresRiverCrossing
|
||||
)
|
||||
}
|
||||
.map(_.id)
|
||||
.toVector
|
||||
|
||||
val availableHeroes = originProvince.rulingFactionHeroIds
|
||||
|
||||
@@ -446,7 +446,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:min_vigor_for_march",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_suitability",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:legacy_battalion_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:shardok_map_info",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:combat_unit_selector",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/command_choice_helpers:march_supplies_helpers",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:legacy_hero_utils",
|
||||
|
||||
@@ -341,7 +341,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action/diplomacy_helpers:ransom_resolution_helpers",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action/diplomacy_helpers:truce_resolution_helpers",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:random_state_trait_sequencer",
|
||||
"//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/ransom_validity",
|
||||
@@ -357,8 +356,8 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:end_diplomacy_resolution_phase_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:ransom_invalidated_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:notification_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
@@ -725,7 +724,7 @@ scala_library(
|
||||
"//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/proto_converters:action_result_proto_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
@@ -752,6 +751,8 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_simple_action",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_simple_action",
|
||||
@@ -766,13 +767,9 @@ scala_library(
|
||||
"//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:free_for_all_draw_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:army_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_eagle_unit",
|
||||
],
|
||||
)
|
||||
@@ -1066,13 +1063,17 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_sequential_results_action",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:faction_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:province_conquered_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action_with_resulting_state",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_sequential_results_action",
|
||||
"//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/faction_utils:legacy_faction_utils",
|
||||
"//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",
|
||||
@@ -1086,16 +1087,10 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:end_uncontested_conquest_phase_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:sent_supplies_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:withdrawal_for_truce_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:army_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//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",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1428,6 +1423,10 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_simple_action",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:action_result_notification_details_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library:eagle_internal_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
@@ -1435,7 +1434,9 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/llm_request_generators/captured_hero_helpers:captured_hero_plea_generator",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:freed_prisoner_loyalty",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:province_distances",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:legacy_hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/unaffiliated_hero:legacy_unaffiliated_hero_utils",
|
||||
"//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_faction_trait",
|
||||
@@ -1450,23 +1451,18 @@ scala_library(
|
||||
"//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:province_conquered_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:unaffiliated_hero_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:captured_hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//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/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:event_for_hero_backstory_trait",
|
||||
"//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:orders",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/quest",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unaffiliated_hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:eagle_unit",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_eagle_unit",
|
||||
],
|
||||
)
|
||||
@@ -1501,9 +1497,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:province_held_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:captured_hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
@@ -1520,37 +1513,32 @@ scala_library(
|
||||
exports = [
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action_with_resulting_state",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_sequential_results_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:deterministic_sequential_results_action",
|
||||
],
|
||||
deps = [
|
||||
":province_conquered_action",
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:faction_relationship_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_sequential_results_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:deterministic_sequential_results_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:legacy_battalion_utils",
|
||||
"//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_faction_trait",
|
||||
"//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",
|
||||
"//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_hero_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:sent_supplies_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:start_battle_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:action_result_proto_converter",
|
||||
"//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/shardok_battle",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:eagle_unit",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_eagle_unit",
|
||||
],
|
||||
)
|
||||
@@ -1591,20 +1579,30 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action_with_resulting_state",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:deterministic_single_result_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_sequential_results_action",
|
||||
],
|
||||
deps = [
|
||||
":free_for_all_draw_action",
|
||||
":province_conquered_action",
|
||||
":province_held_action",
|
||||
":won_free_for_all_action",
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/common:shardok_internal_interface_scala_grpc",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:round_phase_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library:eagle_internal_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action_with_resulting_state",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:deterministic_single_result_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_sequential_results_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/util:shattered_army_utils",
|
||||
"//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/util:eagle_require",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_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:legacy_battalion_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",
|
||||
@@ -1615,14 +1613,16 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_battalion_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/proto_converters:action_result_proto_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:army_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:changed_province_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//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/shardok_battle",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/quest",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_resolution",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_eagle_unit",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_shardok_player",
|
||||
@@ -1897,6 +1897,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_simple_action",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_simple_action",
|
||||
@@ -1911,14 +1912,9 @@ scala_library(
|
||||
"//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:won_free_for_all_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:army_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_eagle_unit",
|
||||
],
|
||||
)
|
||||
|
||||
+180
-310
@@ -11,10 +11,7 @@ import net.eagle0.eagle.library.actions.impl.action.diplomacy_helpers.{
|
||||
RansomResolutionHelpers,
|
||||
TruceResolutionHelpers
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.common.{
|
||||
ProtolessRandomSequentialResultsAction,
|
||||
RandomStateTSequencer
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessRandomSequentialResultsAction
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentChecker
|
||||
import net.eagle0.eagle.library.util.ransom_validity.RansomValidity
|
||||
@@ -26,11 +23,9 @@ import net.eagle0.eagle.model.action_result.types.{
|
||||
EndDiplomacyResolutionPhaseResultType,
|
||||
RansomInvalidatedResultType
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
BattalionConverter,
|
||||
NotificationConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.{ActionResultT, NotificationT}
|
||||
import net.eagle0.eagle.model.proto_converters.NotificationConverter
|
||||
import net.eagle0.eagle.model.proto_converters.battalion.BattalionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||
@@ -61,100 +56,79 @@ case class EndDiplomacyResolutionPhaseAction(
|
||||
actionResultTApplier: ActionResultTApplier
|
||||
) extends ProtolessRandomSequentialResultsAction {
|
||||
|
||||
private lazy val provinceTs: Vector[ProvinceT] =
|
||||
gameState.provinces.values.map(ProvinceConverter.fromProto).toVector
|
||||
|
||||
private lazy val factionTs: Vector[FactionT] =
|
||||
gameState.factions.values.map(FactionConverter.fromProto).toVector
|
||||
|
||||
private lazy val heroTs: Vector[HeroT] =
|
||||
gameState.heroes.values.map(HeroConverter.fromProto).toVector
|
||||
|
||||
private lazy val battalionTs: Vector[BattalionT] =
|
||||
gameState.battalions.values.map(BattalionConverter.fromProto).toVector
|
||||
|
||||
private lazy val currentDateT: Date =
|
||||
DateConverter.fromProto(gameState.currentDate)
|
||||
|
||||
private lazy val deferredNotificationTs: Vector[NotificationT] =
|
||||
gameState.deferredNotifications
|
||||
.map(n => NotificationConverter.fromProto(n, deferred = true))
|
||||
.toVector
|
||||
|
||||
override def randomResults(
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
import EndDiplomacyResolutionPhaseActionHelpers.*
|
||||
RandomStateTSequencer(gameState, actionResultTApplier, functionalRandom)
|
||||
.withActionResults(invalidationResultsForState)
|
||||
.withActionResults(ransomResolutionsForState)
|
||||
.withRandomActionResults(truceResolutionsForState)
|
||||
.withRandomActionResults(allianceResolutionsForState)
|
||||
.withRandomActionResults(breakAllianceResolutionsForState)
|
||||
.withRandomActionResults(inviteResolutionsForState)
|
||||
.withRandomActionResults { (currentGameState, fr) =>
|
||||
RandomState(invalidationResults, functionalRandom)
|
||||
.map(_ ++ ransomResolutions)
|
||||
.continue((acc, fr) => truceResolutions(fr).map(acc ++ _))
|
||||
.continue((acc, fr) => allianceResolutions(fr).map(acc ++ _))
|
||||
.continue((acc, fr) => breakAllianceResolutions(fr).map(acc ++ _))
|
||||
.continue((acc, fr) => inviteResolutions(fr).map(acc ++ _))
|
||||
.continue((acc, fr) =>
|
||||
CheckForFactionChangesAction(
|
||||
gameId = currentGameState.gameId,
|
||||
factions = currentGameState.factions.values.toVector.map(
|
||||
FactionConverter.fromProto
|
||||
),
|
||||
provinces = currentGameState.provinces.values.toVector.map(
|
||||
gameId = gameState.gameId,
|
||||
factions =
|
||||
gameState.factions.values.toVector.map(FactionConverter.fromProto),
|
||||
provinces = gameState.provinces.values.toVector.map(
|
||||
ProvinceConverter.fromProto
|
||||
),
|
||||
heroes = currentGameState.heroes.values.toVector.map(
|
||||
HeroConverter.fromProto
|
||||
),
|
||||
killedHeroIds = currentGameState.killedHeroes.keys.toVector
|
||||
).randomResults(fr)
|
||||
}
|
||||
.withActionResults { currentGameState =>
|
||||
HeroBackstoryUpdateAction(
|
||||
gameId = currentGameState.gameId,
|
||||
roundId = currentGameState.currentRoundId,
|
||||
heroes = currentGameState.heroes.values
|
||||
.map(HeroConverter.fromProto)
|
||||
.toVector,
|
||||
visibleToFactionIds = toFid =>
|
||||
FactionUtils.alliedFactions(
|
||||
toFid,
|
||||
currentGameState.factions.values
|
||||
.map(FactionConverter.fromProto)
|
||||
.toVector
|
||||
),
|
||||
heroInProvinceOwnedBy = heroId => {
|
||||
val provinces = currentGameState.provinces.values
|
||||
.map(ProvinceConverter.fromProto)
|
||||
.toVector
|
||||
provinces
|
||||
heroes =
|
||||
gameState.heroes.values.toVector.map(HeroConverter.fromProto),
|
||||
killedHeroIds = gameState.killedHeroes.keys.toVector
|
||||
).randomResults(fr).map(acc ++ _)
|
||||
)
|
||||
.map(
|
||||
_ ++ HeroBackstoryUpdateAction(
|
||||
gameId = gameState.gameId,
|
||||
roundId = gameState.currentRoundId,
|
||||
heroes = heroTs,
|
||||
visibleToFactionIds =
|
||||
toFid => FactionUtils.alliedFactions(toFid, factionTs),
|
||||
heroInProvinceOwnedBy = heroId =>
|
||||
provinceTs
|
||||
.find(province =>
|
||||
province.rulingFactionHeroIds.contains(heroId) ||
|
||||
province.unaffiliatedHeroes.exists(_.heroId == heroId)
|
||||
province.rulingFactionHeroIds.contains(
|
||||
heroId
|
||||
) || province.unaffiliatedHeroes.exists(_.heroId == heroId)
|
||||
)
|
||||
.flatMap(_.rulingFactionId)
|
||||
}
|
||||
).results
|
||||
}
|
||||
.withActionResult(endPhaseResultForState)
|
||||
.actionResults
|
||||
)
|
||||
.map(_ :+ endPhaseResult)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Companion object for EndDiplomacyResolutionPhaseAction containing helper
|
||||
* methods.
|
||||
*
|
||||
* IMPORTANT: This is intentionally named differently from the case class to
|
||||
* avoid Scala naming conflicts that prevent the class from being imported in
|
||||
* other packages. When a case class and its companion object share the same
|
||||
* name, the compiler can become confused about which one is being referenced
|
||||
* during imports.
|
||||
*
|
||||
* All helper methods take explicit parameters for game state components to
|
||||
* ensure they always operate on the current state as provided by
|
||||
* RandomStateTSequencer, preventing use of stale data from the initial game
|
||||
* state.
|
||||
*/
|
||||
private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
|
||||
// Methods that use the updated game state from the sequencer
|
||||
def invalidationResultsForState(
|
||||
currentGameState: GameState
|
||||
): Vector[ActionResultT] = {
|
||||
val factions =
|
||||
currentGameState.factions.values.map(FactionConverter.fromProto).toVector
|
||||
val provinces = currentGameState.provinces.values
|
||||
.map(ProvinceConverter.fromProto)
|
||||
.toVector
|
||||
|
||||
// Check that unresolved offers ought to be invalidated and invalidate them
|
||||
private def invalidationResults: Vector[ActionResultT] =
|
||||
for {
|
||||
faction <- factions
|
||||
faction <- factionTs
|
||||
diploOffer <- faction.incomingDiplomacyOffers.filter(
|
||||
_.status == Unresolved
|
||||
)
|
||||
} yield {
|
||||
diploOffer match {
|
||||
case ro: RansomOffer
|
||||
if !RansomValidity.isStillValid(diploOffer, provinces) =>
|
||||
if !RansomValidity.isStillValid(diploOffer, provinceTs) =>
|
||||
ActionResultC(
|
||||
actionResultType = RansomInvalidatedResultType,
|
||||
changedFactions = Vector(
|
||||
@@ -173,178 +147,18 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def endPhaseResultForState(
|
||||
currentGameState: GameState
|
||||
): ActionResultT = {
|
||||
val deferredNotifications = currentGameState.deferredNotifications
|
||||
.map(n => NotificationConverter.fromProto(n, deferred = true))
|
||||
.toVector
|
||||
|
||||
ActionResultC(
|
||||
actionResultType = EndDiplomacyResolutionPhaseResultType,
|
||||
newRoundPhase = Some(ReconResolution),
|
||||
removedNotifications = deferredNotifications,
|
||||
newNotifications = deferredNotifications.map(_.withDeferred(false))
|
||||
)
|
||||
}
|
||||
|
||||
private def factionTs(gs: GameState): Vector[FactionT] =
|
||||
gs.factions.values.map(FactionConverter.fromProto).toVector
|
||||
|
||||
private def resolutionsForType[A](
|
||||
getter: FactionT => Vector[A],
|
||||
resolver: A => Vector[ActionResultT]
|
||||
)(factions: Vector[FactionT]): Vector[ActionResultT] =
|
||||
(for {
|
||||
faction <- factions
|
||||
outgoingDiplo <- getter(faction)
|
||||
} yield resolver(outgoingDiplo)).flatten
|
||||
|
||||
private def randomResolutionsForType[A](
|
||||
getter: FactionT => Vector[A],
|
||||
resolver: (A, FunctionalRandom) => RandomState[Vector[ActionResultT]],
|
||||
functionalRandom: FunctionalRandom
|
||||
)(factions: Vector[FactionT]): RandomState[Vector[ActionResultT]] =
|
||||
functionalRandom.nextFlatMap(factions) { case (faction, fr) =>
|
||||
fr.nextMap(getter(faction)) { case (outgoingDiplo, innerFr) =>
|
||||
resolver(outgoingDiplo, innerFr)
|
||||
}.map(_.flatten)
|
||||
}
|
||||
|
||||
def truceResolutionsForState(
|
||||
currentGameState: GameState,
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
val factions = factionTs(currentGameState)
|
||||
randomResolutionsForType(
|
||||
_.incomingDiplomacyOffers.collect { case to: TruceOffer => to },
|
||||
(to: TruceOffer, fr: FunctionalRandom) => {
|
||||
val provinces = currentGameState.provinces.values
|
||||
.map(ProvinceConverter.fromProto)
|
||||
.toVector
|
||||
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
||||
resultsForTruceOffer(
|
||||
to,
|
||||
fr,
|
||||
provinces,
|
||||
factions,
|
||||
currentDate,
|
||||
currentGameState
|
||||
)
|
||||
},
|
||||
functionalRandom
|
||||
)(factions)
|
||||
}
|
||||
|
||||
def allianceResolutionsForState(
|
||||
currentGameState: GameState,
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
val factions = factionTs(currentGameState)
|
||||
randomResolutionsForType(
|
||||
_.incomingDiplomacyOffers.collect { case ao: AllianceOffer => ao },
|
||||
(ao: AllianceOffer, fr: FunctionalRandom) => {
|
||||
val provinces = currentGameState.provinces.values
|
||||
.map(ProvinceConverter.fromProto)
|
||||
.toVector
|
||||
val heroes =
|
||||
currentGameState.heroes.values.map(HeroConverter.fromProto).toVector
|
||||
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
||||
resultsForAllianceOffer(
|
||||
ao,
|
||||
fr,
|
||||
provinces,
|
||||
factions,
|
||||
heroes,
|
||||
currentDate
|
||||
)
|
||||
},
|
||||
functionalRandom
|
||||
)(factions)
|
||||
}
|
||||
|
||||
def breakAllianceResolutionsForState(
|
||||
currentGameState: GameState,
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
val factions = factionTs(currentGameState)
|
||||
randomResolutionsForType(
|
||||
_.incomingDiplomacyOffers.collect { case ba: BreakAlliance => ba },
|
||||
(ba: BreakAlliance, fr: FunctionalRandom) => {
|
||||
val provinces = currentGameState.provinces.values
|
||||
.map(ProvinceConverter.fromProto)
|
||||
.toVector
|
||||
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
||||
resultsForBreakAlliance(ba, fr, provinces, factions, currentDate)
|
||||
},
|
||||
functionalRandom
|
||||
)(factions)
|
||||
}
|
||||
|
||||
def inviteResolutionsForState(
|
||||
currentGameState: GameState,
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
val factions = factionTs(currentGameState)
|
||||
randomResolutionsForType(
|
||||
_.incomingDiplomacyOffers.collect { case id: Invitation => id },
|
||||
(id: Invitation, fr: FunctionalRandom) => {
|
||||
val provinces = currentGameState.provinces.values
|
||||
.map(ProvinceConverter.fromProto)
|
||||
.toVector
|
||||
val battalions = currentGameState.battalions.values
|
||||
.map(BattalionConverter.fromProto)
|
||||
.toVector
|
||||
val currentDate = DateConverter.fromProto(currentGameState.currentDate)
|
||||
resultsForInvitation(
|
||||
id,
|
||||
fr,
|
||||
provinces,
|
||||
factions,
|
||||
battalions,
|
||||
currentDate
|
||||
)
|
||||
},
|
||||
functionalRandom
|
||||
)(factions)
|
||||
}
|
||||
|
||||
def ransomResolutionsForState(
|
||||
currentGameState: GameState
|
||||
): Vector[ActionResultT] = {
|
||||
val factions = factionTs(currentGameState)
|
||||
resolutionsForType(
|
||||
_.incomingDiplomacyOffers.collect { case ro: RansomOffer => ro },
|
||||
(ro: RansomOffer) => {
|
||||
val provinces = currentGameState.provinces.values
|
||||
.map(ProvinceConverter.fromProto)
|
||||
.toVector
|
||||
resultsForRansomOffer(
|
||||
ro,
|
||||
DateConverter.fromProto(currentGameState.currentDate),
|
||||
provinces,
|
||||
currentGameState
|
||||
)
|
||||
}
|
||||
)(factions)
|
||||
}
|
||||
|
||||
private def resultsForRansomOffer(
|
||||
ransomOffer: RansomOffer,
|
||||
currentDate: Date,
|
||||
provinces: Vector[ProvinceT],
|
||||
gameState: GameState
|
||||
): Vector[ActionResultT] = ransomOffer.status match {
|
||||
ransomOffer: RansomOffer
|
||||
): Vector[ActionResultT] = (ransomOffer.status match {
|
||||
case Accepted =>
|
||||
RansomResolutionHelpers.acceptedRansomResults(
|
||||
ransomOffer,
|
||||
currentDate,
|
||||
allProvinces = provinces,
|
||||
DateConverter.fromProto(gameState.currentDate),
|
||||
allProvinces = provinceTs,
|
||||
questFulfillmentChecker = new QuestFulfillmentChecker(
|
||||
gameId = gameState.gameId,
|
||||
currentDate = currentDate,
|
||||
currentDate = currentDateT,
|
||||
currentRoundId = gameState.currentRoundId,
|
||||
previousBackstoryTextIdLookup =
|
||||
hid => gameState.heroes(hid).backstoryVersions.toVector.last.textId
|
||||
@@ -366,24 +180,20 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
Vector(
|
||||
RansomResolutionHelpers.invalidatedRansomResult(ransomOffer)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
private def resultsForTruceOffer(
|
||||
truceOffer: TruceOffer,
|
||||
functionalRandom: FunctionalRandom,
|
||||
provinces: Vector[ProvinceT],
|
||||
factions: Vector[FactionT],
|
||||
currentDate: Date,
|
||||
gameState: GameState
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] =
|
||||
truceOffer.status match {
|
||||
(truceOffer.status match {
|
||||
case Accepted =>
|
||||
TruceResolutionHelpers
|
||||
.acceptedTruceResult(
|
||||
truceOffer = truceOffer,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
currentDate = currentDate,
|
||||
provinces = provinceTs,
|
||||
factions = factionTs,
|
||||
currentDate = currentDateT,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.map(Vector(_))
|
||||
@@ -391,8 +201,8 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
TruceResolutionHelpers
|
||||
.rejectedTruceResults(
|
||||
truceOffer = truceOffer,
|
||||
provinces = provinces,
|
||||
currentDate = currentDate,
|
||||
provinces = provinceTs,
|
||||
currentDate = currentDateT,
|
||||
eagleGameId = gameState.gameId,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
@@ -400,9 +210,9 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
RandomState(
|
||||
TruceResolutionHelpers.imprisonedAmbassadorResults(
|
||||
truceOffer = truceOffer,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
currentDate = currentDate,
|
||||
provinces = provinceTs,
|
||||
factions = factionTs,
|
||||
currentDate = currentDateT,
|
||||
eagleGameId = gameState.gameId
|
||||
),
|
||||
functionalRandom
|
||||
@@ -411,8 +221,8 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
TruceResolutionHelpers
|
||||
.invalidatedTruceResults(
|
||||
truceOffer = truceOffer,
|
||||
provinces = provinces,
|
||||
currentDate = currentDate,
|
||||
provinces = provinceTs,
|
||||
currentDate = currentDateT,
|
||||
eagleGameId = gameState.gameId,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
@@ -420,33 +230,29 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
throw new EagleInternalException(
|
||||
"UNRESOLVED truce offer at the end of the round"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
private def resultsForAllianceOffer(
|
||||
allianceOffer: AllianceOffer,
|
||||
functionalRandom: FunctionalRandom,
|
||||
provinces: Vector[ProvinceT],
|
||||
factions: Vector[FactionT],
|
||||
heroes: Vector[HeroT],
|
||||
currentDate: Date
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
allianceOffer.status match {
|
||||
case Accepted =>
|
||||
AllianceResolutionHelpers
|
||||
.acceptedAllianceResult(
|
||||
allianceOffer = allianceOffer,
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
heroes = heroes,
|
||||
factions = factions,
|
||||
currentDate = currentDateT,
|
||||
provinces = provinceTs,
|
||||
heroes = heroTs,
|
||||
factions = factionTs,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
case Rejected =>
|
||||
AllianceResolutionHelpers
|
||||
.rejectedAllianceResult(
|
||||
allianceOffer = allianceOffer,
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
currentDate = currentDateT,
|
||||
provinces = provinceTs,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.map(Vector(_))
|
||||
@@ -455,9 +261,9 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
AllianceResolutionHelpers
|
||||
.imprisonedAmbassadorResult(
|
||||
allianceOffer = allianceOffer,
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
factions = factions
|
||||
currentDate = currentDateT,
|
||||
provinces = provinceTs,
|
||||
factions = factionTs
|
||||
),
|
||||
functionalRandom
|
||||
).map(Vector(_))
|
||||
@@ -465,7 +271,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
AllianceResolutionHelpers
|
||||
.invalidatedAllianceResults(
|
||||
allianceOffer = allianceOffer,
|
||||
provinces = provinces,
|
||||
provinces = provinceTs,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.map(Vector(_))
|
||||
@@ -478,19 +284,16 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
|
||||
private def resultsForBreakAlliance(
|
||||
breakAllianceOffer: BreakAlliance,
|
||||
functionalRandom: FunctionalRandom,
|
||||
provinces: Vector[ProvinceT],
|
||||
factions: Vector[FactionT],
|
||||
currentDate: Date
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] = {
|
||||
breakAllianceOffer.status match {
|
||||
case Accepted =>
|
||||
BreakAllianceResolutionHelpers
|
||||
.acceptedResult(
|
||||
breakAllianceOffer = breakAllianceOffer,
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
currentDate = currentDateT,
|
||||
provinces = provinceTs,
|
||||
factions = factionTs,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.map(Vector(_))
|
||||
@@ -507,9 +310,9 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
BreakAllianceResolutionHelpers
|
||||
.imprisonedAmbassadorResult(
|
||||
breakAllianceOffer = breakAllianceOffer,
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
factions = factions
|
||||
currentDate = currentDateT,
|
||||
provinces = provinceTs,
|
||||
factions = factionTs
|
||||
),
|
||||
functionalRandom
|
||||
).map(Vector(_))
|
||||
@@ -521,7 +324,7 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
BreakAllianceResolutionHelpers
|
||||
.invalidatedBreakAllianceResult(
|
||||
offer = breakAllianceOffer,
|
||||
provinces = provinces,
|
||||
provinces = provinceTs,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.map(Vector(_))
|
||||
@@ -530,27 +333,23 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
|
||||
private def resultsForInvitation(
|
||||
invitation: Invitation,
|
||||
functionalRandom: FunctionalRandom,
|
||||
provinces: Vector[ProvinceT],
|
||||
factions: Vector[FactionT],
|
||||
battalions: Vector[BattalionT],
|
||||
currentDate: Date
|
||||
): RandomState[Vector[ActionResultT]] = invitation.status match {
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] = (invitation.status match {
|
||||
case Accepted =>
|
||||
InvitationResolutionHelpers.acceptedInvitationResults(
|
||||
acceptedInvitation = invitation,
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
battalions = battalions,
|
||||
currentDate = currentDateT,
|
||||
provinces = provinceTs,
|
||||
factions = factionTs,
|
||||
battalions = battalionTs,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
case Rejected =>
|
||||
InvitationResolutionHelpers
|
||||
.rejectedInvitationResult(
|
||||
rejectedInvitation = invitation,
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
currentDate = currentDateT,
|
||||
provinces = provinceTs,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.map(Vector(_))
|
||||
@@ -559,9 +358,9 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
Vector(
|
||||
InvitationResolutionHelpers.imprisonedAmbassadorResult(
|
||||
invitation = invitation,
|
||||
currentDate = currentDate,
|
||||
provinces = provinces,
|
||||
factions = factions
|
||||
currentDate = currentDateT,
|
||||
provinces = provinceTs,
|
||||
factions = factionTs
|
||||
)
|
||||
),
|
||||
functionalRandom
|
||||
@@ -574,9 +373,80 @@ private object EndDiplomacyResolutionPhaseActionHelpers {
|
||||
InvitationResolutionHelpers
|
||||
.invalidatedInvitationResult(
|
||||
invalidatedInvitation = invitation,
|
||||
provinces = provinces,
|
||||
provinces = provinceTs,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
.map(Vector(_))
|
||||
}
|
||||
})
|
||||
|
||||
// Remove any outstanding truce/invite offers; we may have been unable to accept because
|
||||
// of lack of provinces, for instance.
|
||||
private def endPhaseResult: ActionResultT = ActionResultC(
|
||||
actionResultType = EndDiplomacyResolutionPhaseResultType,
|
||||
newRoundPhase = Some(ReconResolution),
|
||||
removedNotifications = deferredNotificationTs,
|
||||
newNotifications = deferredNotificationTs.map(_.withDeferred(false))
|
||||
)
|
||||
|
||||
private def resolutionsForType[A](
|
||||
getter: FactionT => Vector[A],
|
||||
resolver: A => Vector[ActionResultT]
|
||||
): Vector[ActionResultT] =
|
||||
(for {
|
||||
faction <- factionTs
|
||||
outgoingDiplo <- getter(faction)
|
||||
} yield resolver(outgoingDiplo)).flatten
|
||||
|
||||
private def randomResolutionsForType[A](
|
||||
getter: FactionT => Vector[A],
|
||||
resolver: (A, FunctionalRandom) => RandomState[Vector[ActionResultT]],
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] =
|
||||
functionalRandom.nextFlatMap(factionTs) { case (faction, fr) =>
|
||||
fr.nextMap(getter(faction)) { case (outgoingDiplo, innerFr) =>
|
||||
resolver(outgoingDiplo, innerFr)
|
||||
}.map(_.flatten)
|
||||
}
|
||||
|
||||
private def truceResolutions(
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] =
|
||||
randomResolutionsForType(
|
||||
_.incomingDiplomacyOffers.collect { case to: TruceOffer => to },
|
||||
resultsForTruceOffer,
|
||||
functionalRandom
|
||||
)
|
||||
|
||||
private def allianceResolutions(
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] =
|
||||
randomResolutionsForType(
|
||||
_.incomingDiplomacyOffers.collect { case ao: AllianceOffer => ao },
|
||||
resultsForAllianceOffer,
|
||||
functionalRandom
|
||||
)
|
||||
|
||||
private def breakAllianceResolutions(
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] =
|
||||
randomResolutionsForType(
|
||||
_.incomingDiplomacyOffers.collect { case ba: BreakAlliance => ba },
|
||||
resultsForBreakAlliance,
|
||||
functionalRandom
|
||||
)
|
||||
|
||||
private def inviteResolutions(
|
||||
functionalRandom: FunctionalRandom
|
||||
): RandomState[Vector[ActionResultT]] =
|
||||
randomResolutionsForType(
|
||||
getter = _.incomingDiplomacyOffers.collect { case id: Invitation => id },
|
||||
resolver = resultsForInvitation,
|
||||
functionalRandom = functionalRandom
|
||||
)
|
||||
|
||||
private def ransomResolutions: Vector[ActionResultT] =
|
||||
resolutionsForType(
|
||||
_.incomingDiplomacyOffers.collect { case ro: RansomOffer => ro },
|
||||
resultsForRansomOffer
|
||||
)
|
||||
}
|
||||
|
||||
+2
-4
@@ -13,10 +13,8 @@ import net.eagle0.eagle.library.actions.impl.common.{
|
||||
}
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
ActionResultProtoConverter,
|
||||
BattalionConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.ActionResultProtoConverter
|
||||
import net.eagle0.eagle.model.proto_converters.battalion.BattalionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
|
||||
+22
-15
@@ -1,22 +1,28 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.internal.army.MovingArmy as MovingArmyProto
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSimpleAction
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.concrete.ActionResultC
|
||||
import net.eagle0.eagle.model.action_result.types.FreeForAllDrawResultType
|
||||
import net.eagle0.eagle.model.proto_converters.ArmyConverter
|
||||
import net.eagle0.eagle.model.state.MovingArmy
|
||||
import net.eagle0.eagle.shardok_interface.ResolvedEagleUnit
|
||||
import net.eagle0.eagle.ProvinceId
|
||||
|
||||
case class FreeForAllDrawAction(
|
||||
defenderProvince: ProvinceId,
|
||||
armiesFromPlayers: Vector[MovingArmy],
|
||||
startingState: GameState,
|
||||
battle: ShardokBattle,
|
||||
remainingUnits: Seq[ResolvedEagleUnit]
|
||||
) extends ProtolessSimpleAction {
|
||||
|
||||
private val changedProvincesFromRemainingUnits: Vector[ChangedProvinceC] = {
|
||||
val mas = armiesFromPlayers.map(reversedMovingArmy)
|
||||
val mas = for {
|
||||
p <- battle.players
|
||||
ma <- p.getArmyGroup.armies
|
||||
} yield reversedMovingArmy(ma)
|
||||
|
||||
mas
|
||||
.groupBy(_.destinationProvinceId)
|
||||
@@ -32,21 +38,22 @@ case class FreeForAllDrawAction(
|
||||
override def immediateExecute: ActionResultT = {
|
||||
ActionResultC(
|
||||
actionResultType = FreeForAllDrawResultType,
|
||||
provinceId = Some(defenderProvince),
|
||||
provinceId = Some(battle.defenderProvince),
|
||||
changedProvinces = changedProvincesFromRemainingUnits
|
||||
)
|
||||
}
|
||||
|
||||
private def reversedMovingArmy(ma: MovingArmy): MovingArmy =
|
||||
ma.copy(
|
||||
originProvinceId = ma.destinationProvinceId,
|
||||
destinationProvinceId = ma.destinationProvinceId,
|
||||
arrivalRound = ma.arrivalRound + 1,
|
||||
army = ma.army.copy(
|
||||
units = ma.army.units.filter(cu =>
|
||||
remainingUnits.exists(_.hero.id == cu.heroId)
|
||||
),
|
||||
fleeProvinceId = None
|
||||
private def reversedMovingArmy(ma: MovingArmyProto): MovingArmy =
|
||||
ArmyConverter.fromProto(
|
||||
ma.update(
|
||||
_.originProvince := ma.destinationProvince,
|
||||
_.destinationProvince := ma.destinationProvince,
|
||||
_.arrivalRound.modify(_ + 1),
|
||||
_.army.units
|
||||
.modify(
|
||||
_.filter(cu => remainingUnits.exists(_.hero.id == cu.heroId))
|
||||
),
|
||||
_.army.optionalFleeProvinceId := None
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+50
-128
@@ -1,8 +1,11 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.internal.army.HostileArmyGroup as HostileArmyGroupProto
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province as ProvinceProto
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSequentialResultsAction
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.concrete.ActionResultC
|
||||
@@ -11,49 +14,31 @@ import net.eagle0.eagle.model.action_result.types.{
|
||||
SentSuppliesResultType,
|
||||
WithdrawalForTruceResultType
|
||||
}
|
||||
import net.eagle0.eagle.model.state.{HostileArmyGroup, RoundPhase}
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.date.Date as DateT
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.{
|
||||
BattalionId,
|
||||
FactionId,
|
||||
GameId,
|
||||
HeroId,
|
||||
ProvinceId,
|
||||
RoundId
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.ArmyConverter
|
||||
import net.eagle0.eagle.model.state.RoundPhase
|
||||
|
||||
case class PerformUncontestedConquestAction(
|
||||
gameId: GameId,
|
||||
currentRoundId: RoundId,
|
||||
currentDate: DateT,
|
||||
provinces: Map[ProvinceId, ProvinceT],
|
||||
factions: Map[FactionId, FactionT],
|
||||
heroes: Map[HeroId, HeroT],
|
||||
battalions: Map[BattalionId, BattalionT]
|
||||
) extends ProtolessSequentialResultsAction {
|
||||
case class PerformUncontestedConquestAction(gameState: GameState)
|
||||
extends ProtolessSequentialResultsAction {
|
||||
|
||||
private val endPhaseAction: ActionResultT = ActionResultC(
|
||||
actionResultType = EndUncontestedConquestPhaseResultType,
|
||||
newRoundPhase = Some(RoundPhase.AttackDecision)
|
||||
)
|
||||
|
||||
override def results: Vector[ActionResultT] =
|
||||
provinces.values
|
||||
override def results: Vector[ActionResultT] = {
|
||||
gameState.provinces.values
|
||||
.flatMap(conquestOrBounceResult)
|
||||
.toVector :+ endPhaseAction
|
||||
}
|
||||
|
||||
private def conquestOrBounceResult(
|
||||
p: ProvinceT
|
||||
): Vector[ActionResultT] =
|
||||
p: ProvinceProto
|
||||
): Vector[ActionResultT] = {
|
||||
if p.rulingFactionId.nonEmpty then Vector.empty
|
||||
else
|
||||
p.hostileArmies match {
|
||||
case items if items.isEmpty => Vector()
|
||||
case Seq(hostileArmy: HostileArmyGroup) =>
|
||||
case items if items.isEmpty => Vector()
|
||||
case Seq(hostileArmy: HostileArmyGroupProto) =>
|
||||
Vector(
|
||||
ActionResultC(
|
||||
actionResultType = SentSuppliesResultType,
|
||||
@@ -62,118 +47,55 @@ case class PerformUncontestedConquestAction(
|
||||
provinceId = p.id,
|
||||
removedHostileArmyFactionIds = Vector(hostileArmy.factionId),
|
||||
goldDelta = Some(
|
||||
hostileArmy.armies.map(_.supplies.gold).sum
|
||||
hostileArmy.armies.flatMap(_.supplies).map(_.gold).sum
|
||||
),
|
||||
foodDelta = Some(hostileArmy.armies.map(_.supplies.food).sum)
|
||||
foodDelta =
|
||||
Some(hostileArmy.armies.flatMap(_.supplies).map(_.food).sum)
|
||||
)
|
||||
)
|
||||
),
|
||||
ProvinceConqueredAction
|
||||
.uncontestedConquest(
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId,
|
||||
currentDate = currentDate,
|
||||
conqueredProvince = p,
|
||||
attackers = hostileArmy.armies
|
||||
.flatMap(_.army.units)
|
||||
.toVector,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces
|
||||
startingState = gameState,
|
||||
conqueredProvince = gameState.provinces(p.id),
|
||||
attackers =
|
||||
hostileArmy.armies.flatMap(_.army).flatMap(_.units).toVector
|
||||
)
|
||||
.immediateExecute
|
||||
)
|
||||
|
||||
case multipleArmies: Seq[HostileArmyGroup] =>
|
||||
case multipleArmies: Seq[HostileArmyGroupProto] =>
|
||||
val fids = multipleArmies.map(_.factionId).toVector
|
||||
if areMutuallyAllied(fids) then
|
||||
// Allies attacking together - treat as single army
|
||||
Vector(
|
||||
ActionResultC(
|
||||
actionResultType = SentSuppliesResultType,
|
||||
changedProvinces = Vector(
|
||||
ChangedProvinceC(
|
||||
provinceId = p.id,
|
||||
removedHostileArmyFactionIds = fids,
|
||||
goldDelta = Some(
|
||||
multipleArmies.flatMap(_.armies.map(_.supplies.gold)).sum
|
||||
),
|
||||
foodDelta = Some(
|
||||
multipleArmies.flatMap(_.armies.map(_.supplies.food)).sum
|
||||
internalRequire(
|
||||
LegacyFactionUtils.factionsAreMutuallyAllied(
|
||||
fids = fids,
|
||||
gs = gameState
|
||||
),
|
||||
s"Attacking armies $fids are not mutually allied"
|
||||
)
|
||||
Vector(
|
||||
ActionResultC(
|
||||
actionResultType = WithdrawalForTruceResultType,
|
||||
changedProvinces = (ChangedProvinceC(
|
||||
provinceId = p.id,
|
||||
removedHostileArmyFactionIds = fids
|
||||
) +: (for {
|
||||
armyGroup <- multipleArmies
|
||||
army <- armyGroup.armies
|
||||
} yield ChangedProvinceC(
|
||||
provinceId = army.originProvince,
|
||||
newIncomingArmies = Vector(
|
||||
ArmyConverter.fromProto(
|
||||
army.update(
|
||||
_.originProvince := p.id,
|
||||
_.arrivalRound := gameState.currentRoundId + 1,
|
||||
_.army.optionalFleeProvinceId := None
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
ProvinceConqueredAction
|
||||
.uncontestedConquest(
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId,
|
||||
currentDate = currentDate,
|
||||
conqueredProvince = p,
|
||||
attackers = multipleArmies
|
||||
.flatMap(_.armies)
|
||||
.flatMap(_.army.units)
|
||||
.toVector,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces
|
||||
)
|
||||
.immediateExecute
|
||||
))).toVector
|
||||
)
|
||||
else if areMutuallyTruced(fids) then
|
||||
// Truce factions attacking together - bounce them back
|
||||
Vector(
|
||||
ActionResultC(
|
||||
actionResultType = WithdrawalForTruceResultType,
|
||||
changedProvinces = (ChangedProvinceC(
|
||||
provinceId = p.id,
|
||||
removedHostileArmyFactionIds = fids
|
||||
) +: (for {
|
||||
armyGroup <- multipleArmies
|
||||
army <- armyGroup.armies
|
||||
} yield ChangedProvinceC(
|
||||
provinceId = army.originProvinceId,
|
||||
newIncomingArmies = Vector(
|
||||
army.copy(
|
||||
originProvinceId = p.id,
|
||||
arrivalRound = currentRoundId + 1,
|
||||
army = army.army.copy(fleeProvinceId = None)
|
||||
)
|
||||
)
|
||||
))).toVector
|
||||
)
|
||||
)
|
||||
else
|
||||
// Hostile factions attacking together - not allowed
|
||||
internalRequire(
|
||||
false,
|
||||
s"Attacking armies $fids are not mutually allied"
|
||||
)
|
||||
Vector.empty // This won't be reached due to internalRequire
|
||||
end if
|
||||
)
|
||||
}
|
||||
|
||||
private def areMutuallyAllied(fids: Vector[FactionId]): Boolean =
|
||||
fids.forall { fid1 =>
|
||||
fids.forall { fid2 =>
|
||||
fid1 == fid2 || FactionUtils.hasAlliance(
|
||||
fid1,
|
||||
fid2,
|
||||
factions.values.toVector
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def areMutuallyTruced(fids: Vector[FactionId]): Boolean =
|
||||
fids.forall { fid1 =>
|
||||
fids.forall { fid2 =>
|
||||
fid1 == fid2 || FactionUtils.hasTruce(
|
||||
fid1,
|
||||
fid2,
|
||||
factions.values.toVector
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+77
-93
@@ -1,17 +1,29 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.model.state.CombatUnit
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.UnaffiliatedHeroType.{
|
||||
Outlaw,
|
||||
Prisoner
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.common.diplomacy_offer.{
|
||||
DiplomacyOffer,
|
||||
PrisonerToBeRansomed,
|
||||
RansomOfferDetails
|
||||
}
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.RecruitmentInfo
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.RansomOffer
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.Invalidated
|
||||
import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.{
|
||||
UNAFFILIATED_HERO_OUTLAW,
|
||||
UNAFFILIATED_HERO_PRISONER
|
||||
}
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSimpleAction
|
||||
import net.eagle0.eagle.library.actions.generated_text_request_generators.captured_hero_helpers.CapturedHeroPleaGenerator
|
||||
import net.eagle0.eagle.library.settings.FreedPrisonerLoyalty
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.library.util.ProvinceDistances
|
||||
import net.eagle0.eagle.library.util.hero.HeroUtils
|
||||
import net.eagle0.eagle.library.util.unaffiliated_hero.LegacyUnaffiliatedHeroUtils
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.concrete.{
|
||||
ActionResultC,
|
||||
@@ -22,92 +34,62 @@ import net.eagle0.eagle.model.action_result.concrete.{
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.types.ProvinceConqueredResultType
|
||||
import net.eagle0.eagle.model.action_result.{ActionResultT, NotificationDetails}
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.RansomOffer
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.Invalidated
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.model.state.date.Date as DateT
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.faction.FactionRelationship.RelationshipLevel
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.model.proto_converters.UnaffiliatedHeroConverter
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.diplomacy_offer.DiplomacyOfferConverter
|
||||
import net.eagle0.eagle.model.state.CapturedHero
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.Invalidated
|
||||
import net.eagle0.eagle.model.state.hero.ReleasedByProvinceCaptureBackstoryEvent
|
||||
import net.eagle0.eagle.model.state.province.ProvinceOrderType.Entrust
|
||||
import net.eagle0.eagle.shardok_interface.ResolvedEagleUnit
|
||||
import net.eagle0.eagle.{
|
||||
BattalionId,
|
||||
ClientTextId,
|
||||
FactionId,
|
||||
GameId,
|
||||
HeroId,
|
||||
ProvinceId,
|
||||
RoundId
|
||||
ProvinceId
|
||||
}
|
||||
|
||||
object ProvinceConqueredAction {
|
||||
def uncontestedConquest(
|
||||
gameId: GameId,
|
||||
currentRoundId: RoundId,
|
||||
currentDate: DateT,
|
||||
conqueredProvince: ProvinceT,
|
||||
attackers: Vector[CombatUnit],
|
||||
factions: Map[FactionId, FactionT],
|
||||
heroes: Map[HeroId, HeroT],
|
||||
battalions: Map[BattalionId, BattalionT],
|
||||
provinces: Map[ProvinceId, ProvinceT]
|
||||
): ProvinceConqueredAction = {
|
||||
val resolvedAttackers = attackers.map { unit =>
|
||||
ResolvedEagleUnit(
|
||||
hero = heroes(unit.heroId),
|
||||
battalion = unit.battalionId.map(battalions),
|
||||
status = UnitStatus.Normal
|
||||
)
|
||||
}
|
||||
startingState: GameState,
|
||||
conqueredProvince: Province,
|
||||
attackers: Vector[CombatUnit]
|
||||
): ProvinceConqueredAction =
|
||||
ProvinceConqueredAction(
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId,
|
||||
currentDate = currentDate,
|
||||
startingState = startingState,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = resolvedAttackers,
|
||||
notFledConquerors =
|
||||
attackers.map(u => ResolvedEagleUnit(u, startingState)),
|
||||
notFledDefenders = Vector.empty,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = None,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces
|
||||
defendingFactionId = None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Changes the conquered province to belong to the conquering player, sets up the invading armies and battalions there,
|
||||
// and imprisons any of the former ruling heroes.
|
||||
case class ProvinceConqueredAction(
|
||||
gameId: GameId,
|
||||
currentRoundId: RoundId,
|
||||
currentDate: DateT,
|
||||
conqueredProvince: ProvinceT,
|
||||
startingState: GameState,
|
||||
conqueredProvince: Province,
|
||||
notFledConquerors: Vector[ResolvedEagleUnit],
|
||||
notFledDefenders: Vector[ResolvedEagleUnit],
|
||||
destroyedBattalions: Vector[BattalionId],
|
||||
defendingFactionId: Option[FactionId],
|
||||
factions: Map[FactionId, FactionT],
|
||||
heroes: Map[HeroId, HeroT],
|
||||
battalions: Map[BattalionId, BattalionT],
|
||||
provinces: Map[ProvinceId, ProvinceT]
|
||||
defendingFactionId: Option[FactionId]
|
||||
) extends ProtolessSimpleAction {
|
||||
private def invalidatedRansomFactions: Vector[ChangedFactionC] = {
|
||||
conqueredProvince.rulingFactionId
|
||||
.map { rfid =>
|
||||
val incomingRansomOffers = factions
|
||||
.get(rfid)
|
||||
.map(_.incomingDiplomacyOffers)
|
||||
.getOrElse(Vector.empty)
|
||||
.collect {
|
||||
case offer: RansomOffer
|
||||
if offer.prisonerToBeRansomed.provinceIdForPrisoner == conqueredProvince.id =>
|
||||
offer
|
||||
}
|
||||
val incomingRansomOffers =
|
||||
startingState
|
||||
.factions(rfid)
|
||||
.incomingDiplomacyOffers
|
||||
.map(DiplomacyOfferConverter.fromProto)
|
||||
.collect {
|
||||
case ransomOffer: RansomOffer
|
||||
if ransomOffer.prisonerToBeRansomed.provinceIdForPrisoner == conqueredProvince.id =>
|
||||
ransomOffer
|
||||
}
|
||||
|
||||
if incomingRansomOffers.nonEmpty then {
|
||||
Vector(
|
||||
@@ -128,15 +110,15 @@ case class ProvinceConqueredAction(
|
||||
}
|
||||
|
||||
override def immediateExecute: ActionResultT = {
|
||||
val attackingPlayerId = notFledConquerors.head.hero.factionId.get
|
||||
val attackingPlayerId = notFledConquerors.head.hero.getFactionId
|
||||
val seniorConquerorHeroId = notFledConquerors
|
||||
.map(_.hero)
|
||||
.min(HeroUtils.sortOrdering(factions(attackingPlayerId).leaderIds))
|
||||
.id
|
||||
.map(_.hero.id)
|
||||
.sortWith(LegacyHeroUtils.sortOrderer(startingState))
|
||||
.min
|
||||
|
||||
CapturedHeroPleaGenerator.withPleasForFirstNewCapturedHero(
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId,
|
||||
gameId = startingState.gameId,
|
||||
currentRoundId = startingState.currentRoundId,
|
||||
capturingHeroId = seniorConquerorHeroId,
|
||||
capturingFactionId = attackingPlayerId,
|
||||
actionResult = afterUpdatingUnaffiliatedHeroes(
|
||||
@@ -146,7 +128,7 @@ case class ProvinceConqueredAction(
|
||||
actingFactionId = Some(attackingPlayerId),
|
||||
provinceId = Some(conqueredProvince.id),
|
||||
destroyedBattalionIds = destroyedBattalionIds.toVector,
|
||||
changedFactions = factions.keys
|
||||
changedFactions = startingState.factions.keys
|
||||
.map(fid =>
|
||||
ChangedFactionC(
|
||||
factionId = fid,
|
||||
@@ -156,7 +138,7 @@ case class ProvinceConqueredAction(
|
||||
.toVector ++ invalidatedRansomFactions ++ Option
|
||||
.when(
|
||||
conqueredProvince.rulingFactionId
|
||||
.flatMap(factions.get)
|
||||
.map(startingState.factions)
|
||||
.flatMap(_.focusProvinceId)
|
||||
.contains(conqueredProvince.id)
|
||||
) {
|
||||
@@ -186,7 +168,8 @@ case class ProvinceConqueredAction(
|
||||
CapturedHero(
|
||||
heroId = hid,
|
||||
recruitmentAttempted = false,
|
||||
previousFactionId = heroes(hid).factionId.get
|
||||
previousFactionId =
|
||||
startingState.heroes(hid).factionId.get
|
||||
)
|
||||
)
|
||||
.toVector,
|
||||
@@ -203,7 +186,7 @@ case class ProvinceConqueredAction(
|
||||
provinceId = conqueredProvince.id,
|
||||
conqueringFactionId = attackingPlayerId,
|
||||
otherAttackingFactionIds = notFledConquerors
|
||||
.map(_.hero.factionId.get)
|
||||
.map(_.hero.getFactionId)
|
||||
.distinct
|
||||
.filterNot(_ == attackingPlayerId)
|
||||
.toVector,
|
||||
@@ -234,7 +217,7 @@ case class ProvinceConqueredAction(
|
||||
private def destroyedBattalionIds: Vector[BattalionId] =
|
||||
notFledDefenders
|
||||
.flatMap(_.optionalBattalionId)
|
||||
.filter(battalions.contains)
|
||||
.filter(startingState.battalions.contains)
|
||||
|
||||
private def afterFreeingAlliedPrisonersFromOneFaction(
|
||||
actionResult: ActionResultT,
|
||||
@@ -242,18 +225,18 @@ case class ProvinceConqueredAction(
|
||||
): ActionResultT = {
|
||||
val prisonersFromFaction =
|
||||
conqueredProvince.unaffiliatedHeroes
|
||||
.filter(_.unaffiliatedHeroType == Prisoner)
|
||||
.filter(_.lastFactionId.contains(alliedFid))
|
||||
.filter(_.`type` == UNAFFILIATED_HERO_PRISONER)
|
||||
.filter(_.lastFaction.contains(alliedFid))
|
||||
|
||||
ProvinceDistances
|
||||
.closestProvinceOption(
|
||||
to = conqueredProvince.id,
|
||||
candidates = provinces.values
|
||||
.filter(_.rulingFactionId.contains(alliedFid))
|
||||
candidates = LegacyFactionUtils
|
||||
.provinces(alliedFid, startingState)
|
||||
.map(_.id)
|
||||
.toVector,
|
||||
eligibleNeighbors =
|
||||
pid => provinces(pid).neighbors.map(_.provinceId).toVector
|
||||
eligibleNeighbors = pid =>
|
||||
startingState.provinces(pid).neighbors.toVector.map(_.provinceId)
|
||||
)
|
||||
.map(_.provinceId)
|
||||
.map { targetPid =>
|
||||
@@ -281,7 +264,7 @@ case class ProvinceConqueredAction(
|
||||
loyaltyChange = StatAbsolute(FreedPrisonerLoyalty.doubleValue),
|
||||
newEventsForHeroBackstory = Vector(
|
||||
ReleasedByProvinceCaptureBackstoryEvent(
|
||||
date = currentDate,
|
||||
date = DateConverter.fromProto(startingState.currentDate),
|
||||
capturingFactionId = actionResult.actingFactionId.get,
|
||||
releasedInProvinceId = conqueredProvince.id
|
||||
)
|
||||
@@ -298,7 +281,9 @@ case class ProvinceConqueredAction(
|
||||
case (headCp: ChangedProvinceC) +: tail =>
|
||||
headCp.withChangedUnaffiliatedHeroes(prisonersFromFaction.map {
|
||||
uh =>
|
||||
uh.copy(unaffiliatedHeroType = Outlaw)
|
||||
UnaffiliatedHeroConverter.fromProto(
|
||||
uh.withType(UNAFFILIATED_HERO_OUTLAW)
|
||||
)
|
||||
}.toVector) +: tail
|
||||
case _ =>
|
||||
throw new EagleInternalException(
|
||||
@@ -314,7 +299,7 @@ case class ProvinceConqueredAction(
|
||||
loyaltyChange = StatAbsolute(FreedPrisonerLoyalty.doubleValue),
|
||||
newEventsForHeroBackstory = Vector(
|
||||
ReleasedByProvinceCaptureBackstoryEvent(
|
||||
date = currentDate,
|
||||
date = DateConverter.fromProto(startingState.currentDate),
|
||||
capturingFactionId = actionResult.actingFactionId.get,
|
||||
releasedInProvinceId = conqueredProvince.id
|
||||
)
|
||||
@@ -328,10 +313,10 @@ case class ProvinceConqueredAction(
|
||||
private def afterFreeingAllAlliedPrisoners(
|
||||
actionResult: ActionResultT
|
||||
): ActionResultT =
|
||||
factions(actionResult.actingFactionId.get).factionRelationships
|
||||
.filter(
|
||||
_.relationshipLevel == RelationshipLevel.Ally
|
||||
)
|
||||
startingState
|
||||
.factions(actionResult.actingFactionId.get)
|
||||
.factionRelationships
|
||||
.filter(_.relationshipLevel == FactionRelationship.RelationshipLevel.ALLY)
|
||||
.map(_.targetFactionId)
|
||||
.foldLeft(actionResult) { case (acc, alliedFid) =>
|
||||
afterFreeingAlliedPrisonersFromOneFaction(acc, alliedFid)
|
||||
@@ -342,8 +327,8 @@ case class ProvinceConqueredAction(
|
||||
): ActionResultT = {
|
||||
val afterFreeingPrisoners =
|
||||
conqueredProvince.unaffiliatedHeroes
|
||||
.filter(_.unaffiliatedHeroType == Prisoner)
|
||||
.filter(_.lastFactionId.contains(actionResult.actingFactionId.get))
|
||||
.filter(_.`type` == UNAFFILIATED_HERO_PRISONER)
|
||||
.filter(_.lastFaction.contains(actionResult.actingFactionId.get))
|
||||
|
||||
afterFreeingAllAlliedPrisoners(
|
||||
actionResult
|
||||
@@ -371,7 +356,7 @@ case class ProvinceConqueredAction(
|
||||
loyaltyChange = StatAbsolute(FreedPrisonerLoyalty.doubleValue),
|
||||
newEventsForHeroBackstory = Vector(
|
||||
ReleasedByProvinceCaptureBackstoryEvent(
|
||||
date = currentDate,
|
||||
date = DateConverter.fromProto(startingState.currentDate),
|
||||
capturingFactionId = actionResult.actingFactionId.get,
|
||||
releasedInProvinceId = conqueredProvince.id
|
||||
)
|
||||
@@ -395,9 +380,8 @@ case class ProvinceConqueredAction(
|
||||
|
||||
val changedUHs =
|
||||
remainingUHs
|
||||
.map(uh =>
|
||||
uh.copy(recruitmentInfo = RecruitmentInfo.NotDivined)
|
||||
) // Clear recruitment info
|
||||
.map(uh => LegacyUnaffiliatedHeroUtils.withQuestCleared(uh))
|
||||
.map(UnaffiliatedHeroConverter.fromProto)
|
||||
|
||||
actionResult.copy(
|
||||
changedProvinces = headCp.copy(
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.eagle0.eagle.model.action_result.concrete.{
|
||||
import net.eagle0.eagle.model.action_result.types.ProvinceHeldResultType
|
||||
import net.eagle0.eagle.model.action_result.{ActionResultT, NotificationDetails}
|
||||
import net.eagle0.eagle.model.state.CapturedHero
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.{BattalionId, FactionId, GameId, HeroId, RoundId}
|
||||
|
||||
|
||||
+115
-152
@@ -1,73 +1,57 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSequentialResultsAction
|
||||
import net.eagle0.eagle.library.util.BattalionUtils
|
||||
import net.eagle0.common.victory_condition.VictoryCondition.{
|
||||
VICTORY_CONDITION_HOLDS_CRITICAL_TILES,
|
||||
VICTORY_CONDITION_LAST_PLAYER_STANDING,
|
||||
VICTORY_CONDITION_WIN_AFTER_MAX_ROUNDS
|
||||
}
|
||||
import net.eagle0.eagle.common.action_result_type.ActionResultType.{
|
||||
SENT_SUPPLIES,
|
||||
START_BATTLE
|
||||
}
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.army.{Army, HostileArmyGroup, MovingArmy}
|
||||
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle.BattleType
|
||||
import net.eagle0.eagle.internal.shardok_battle.{ShardokBattle, ShardokPlayer}
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
||||
import net.eagle0.eagle.library.actions.impl.common.DeterministicSequentialResultsAction
|
||||
import net.eagle0.eagle.library.util.LegacyBattalionUtils
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.state.HostileArmyGroupStatus.Attacking
|
||||
import net.eagle0.eagle.model.state.{
|
||||
Army,
|
||||
BattalionType,
|
||||
BattalionTypeId,
|
||||
HostileArmyGroup,
|
||||
MovingArmy,
|
||||
Supplies
|
||||
}
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.shardok_interface.ResolvedEagleUnit
|
||||
import net.eagle0.eagle.{
|
||||
BattalionId,
|
||||
FactionId,
|
||||
GameId,
|
||||
HeroId,
|
||||
ProvinceId,
|
||||
RoundId
|
||||
}
|
||||
import net.eagle0.eagle.model.action_result.concrete.ActionResultC
|
||||
import net.eagle0.eagle.model.action_result.types.{
|
||||
SentSuppliesResultType,
|
||||
StartBattleResultType
|
||||
}
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.shardok_battle.{
|
||||
BattleType,
|
||||
ShardokBattle,
|
||||
ShardokPlayer,
|
||||
VictoryCondition
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.ActionResultProtoConverter
|
||||
import net.eagle0.eagle.shardok_interface.{EagleUnit, ResolvedEagleUnit}
|
||||
import net.eagle0.eagle.{GameId, ProvinceId, RoundId}
|
||||
|
||||
import scala.util.hashing.MurmurHash3
|
||||
|
||||
case class RequestBattlesAction(
|
||||
gameId: GameId,
|
||||
currentRoundId: RoundId,
|
||||
currentDate: net.eagle0.eagle.model.state.date.Date,
|
||||
battleCounter: Int,
|
||||
heroes: Map[HeroId, HeroT],
|
||||
battalions: Map[BattalionId, BattalionT],
|
||||
provinces: Map[ProvinceId, ProvinceT],
|
||||
factions: Map[FactionId, FactionT],
|
||||
battalionTypes: Map[BattalionTypeId, BattalionType]
|
||||
) extends ProtolessSequentialResultsAction {
|
||||
override def results: Vector[ActionResultT] =
|
||||
provincesWithAttackingArmies.zipWithIndex
|
||||
.flatMap { case (pwaas, idx) => handleOneProvince(pwaas, idx) }
|
||||
case class RequestBattlesAction(startingGameState: GameState)
|
||||
extends DeterministicSequentialResultsAction(startingGameState) {
|
||||
val ExpandUnit: CombatUnit => EagleUnit =
|
||||
EagleUnit.ExpandUnit(_, startingGameState)
|
||||
|
||||
private def handleOneProvince(
|
||||
override def results(
|
||||
actionResultProtoApplier: ActionResultProtoApplier
|
||||
): Vector[ActionResult] =
|
||||
provincesWithAttackingArmies.zipWithIndex
|
||||
.flatMap { case (pwaas, idx) =>
|
||||
handleOneProvince(startingGameState, pwaas, idx)
|
||||
}
|
||||
|
||||
def handleOneProvince(
|
||||
gameState: GameState,
|
||||
provinceWithAttackingArmies: ProvinceWithAttackingArmies,
|
||||
index: Int
|
||||
): Vector[ActionResultT] = {
|
||||
val defendingArmy = provinces(
|
||||
provinceWithAttackingArmies.provinceId
|
||||
).defendingArmy
|
||||
): Vector[ActionResult] = {
|
||||
val defendingArmy = gameState
|
||||
.provinces(provinceWithAttackingArmies.provinceId)
|
||||
.defendingArmy
|
||||
|
||||
internalRequire(
|
||||
provinceWithAttackingArmies.armies
|
||||
.forall(_.status == Attacking),
|
||||
.forall(_.status.asMessage.sealedValue.isAttacking),
|
||||
"all armies must be attacking in the battle request phase"
|
||||
)
|
||||
|
||||
@@ -76,14 +60,15 @@ case class RequestBattlesAction(
|
||||
|
||||
if participantCount == 1 then {
|
||||
// it's a single-army attack with no defending army
|
||||
uncontestedAttackResults(provinceWithAttackingArmies)
|
||||
uncontestedAttackResults(provinceWithAttackingArmies, gameState)
|
||||
} else {
|
||||
Vector(
|
||||
CommenceImminentBattle(
|
||||
eagleGameId = gameId,
|
||||
battleIndex = battleCounter + index + 1,
|
||||
roundId = currentRoundId,
|
||||
battleProvince = provinces(provinceWithAttackingArmies.provinceId),
|
||||
eagleGameId = gameState.gameId,
|
||||
battleIndex = gameState.battleCounter + index + 1,
|
||||
roundId = gameState.currentRoundId,
|
||||
battleProvince =
|
||||
gameState.provinces(provinceWithAttackingArmies.provinceId),
|
||||
attackingArmies = provinceWithAttackingArmies.armies,
|
||||
defendingArmy = defendingArmy
|
||||
)
|
||||
@@ -95,48 +80,48 @@ case class RequestBattlesAction(
|
||||
eagleGameId: GameId,
|
||||
battleIndex: Int,
|
||||
roundId: RoundId,
|
||||
battleProvince: ProvinceT,
|
||||
battleProvince: Province,
|
||||
attackingArmies: Vector[HostileArmyGroup],
|
||||
defendingArmy: Option[Army]
|
||||
): ActionResultT =
|
||||
ActionResultC(
|
||||
actionResultType = StartBattleResultType,
|
||||
): ActionResult =
|
||||
ActionResult(
|
||||
`type` = START_BATTLE,
|
||||
changedProvinces = Vector(
|
||||
ChangedProvinceC(
|
||||
provinceId = battleProvince.id,
|
||||
removedHostileArmyFactionIds = attackingArmies.map(_.factionId),
|
||||
ChangedProvince(
|
||||
id = battleProvince.id,
|
||||
removedHostileArmyFactionIds =
|
||||
attackingArmies.map(_.factionId).toVector,
|
||||
foodDelta = Some(-defenderFood(battleProvince))
|
||||
)
|
||||
),
|
||||
provinceId = Some(battleProvince.id),
|
||||
province = Some(battleProvince.id),
|
||||
newBattle = Some(
|
||||
ShardokBattle(
|
||||
shardokGameId =
|
||||
s"${eagleGameId.toHexString}_${battleIndex.toHexString}_${battleHash(battleProvince.id, attackingArmies, defendingArmy, roundId).toHexString}",
|
||||
battleIndex = battleIndex,
|
||||
battleType = BattleType.AssaultProvince,
|
||||
battleType = BattleType.BATTLE_TYPE_ASSAULT_PROVINCE,
|
||||
players = shardokPlayers(
|
||||
attackingArmies,
|
||||
defendingArmy,
|
||||
defenderFood(battleProvince),
|
||||
battleProvince
|
||||
defenderFood(battleProvince)
|
||||
),
|
||||
hexMapName = battleProvince.hexMapName,
|
||||
eagleGameId = eagleGameId,
|
||||
defenderProvince = battleProvince.id,
|
||||
roundStarted = currentRoundId
|
||||
roundStarted = startingGameState.currentRoundId
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def defenderFood(province: ProvinceT): Int = {
|
||||
def defenderFood(province: Province): Int = {
|
||||
Math.min(
|
||||
BattalionUtils.monthlyConsumedFood(
|
||||
LegacyBattalionUtils.monthlyConsumedFood(
|
||||
battalions = province.defendingArmy.toVector
|
||||
.flatMap(_.units)
|
||||
.flatMap(_.battalionId)
|
||||
.map(battalions),
|
||||
bts = battalionTypes.values.toVector
|
||||
.map(startingGameState.battalions),
|
||||
bts = startingGameState.battalionTypes.toVector
|
||||
),
|
||||
province.food
|
||||
)
|
||||
@@ -149,28 +134,18 @@ case class RequestBattlesAction(
|
||||
roundId: RoundId
|
||||
): Int =
|
||||
MurmurHash3.arrayHash(
|
||||
attackingArmies
|
||||
.flatMap(group =>
|
||||
group.armies.map(army =>
|
||||
s"${group.factionId}:${army.id}:${army.originProvinceId}->${army.destinationProvinceId}:r${army.arrivalRound}"
|
||||
)
|
||||
)
|
||||
.sorted
|
||||
.toArray ++
|
||||
attackingArmies.map(_.toProtoString).toArray ++
|
||||
Array(
|
||||
defendingArmy
|
||||
.map(army => s"defender:${army.factionId}")
|
||||
.getOrElse("no defender"),
|
||||
s"province:$battleProvinceId",
|
||||
s"round:$roundId"
|
||||
defendingArmy.map(_.toProtoString).getOrElse("no defender"),
|
||||
battleProvinceId.toString,
|
||||
roundId.toString
|
||||
)
|
||||
)
|
||||
|
||||
private def shardokPlayers(
|
||||
attackingArmies: Vector[HostileArmyGroup],
|
||||
defendingArmy: Option[Army],
|
||||
defenderFood: Int,
|
||||
battleProvince: ProvinceT
|
||||
defenderFood: Int
|
||||
): Vector[ShardokPlayer] =
|
||||
attackingArmies.map { aa =>
|
||||
ShardokPlayer(
|
||||
@@ -179,8 +154,8 @@ case class RequestBattlesAction(
|
||||
armyGroup = Some(aa),
|
||||
food = attackerFood(aa),
|
||||
victoryConditions = Vector(
|
||||
VictoryCondition.HoldsCriticalTiles,
|
||||
VictoryCondition.LastPlayerStanding
|
||||
VICTORY_CONDITION_HOLDS_CRITICAL_TILES,
|
||||
VICTORY_CONDITION_LAST_PLAYER_STANDING
|
||||
)
|
||||
)
|
||||
} ++ defendingArmy.map { da =>
|
||||
@@ -191,109 +166,97 @@ case class RequestBattlesAction(
|
||||
HostileArmyGroup(
|
||||
factionId = da.factionId,
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 0, // placeholder
|
||||
army = da,
|
||||
arrivalRound = currentRoundId,
|
||||
destinationProvinceId = battleProvince.id,
|
||||
originProvinceId = battleProvince.id,
|
||||
supplies = Supplies(0, 0),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
)
|
||||
),
|
||||
status = Attacking
|
||||
MovingArmy(army = Some(da))
|
||||
)
|
||||
)
|
||||
),
|
||||
food = defenderFood,
|
||||
victoryConditions = Vector(
|
||||
VictoryCondition.LastPlayerStanding,
|
||||
VictoryCondition.WinAfterMaxRounds
|
||||
VICTORY_CONDITION_LAST_PLAYER_STANDING,
|
||||
VICTORY_CONDITION_WIN_AFTER_MAX_ROUNDS
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def attackerFood(armyGroup: HostileArmyGroup): Int = {
|
||||
Math.min(
|
||||
BattalionUtils.monthlyConsumedFood(
|
||||
LegacyBattalionUtils.monthlyConsumedFood(
|
||||
battalions = armyGroup.armies
|
||||
.flatMap(_.army.units)
|
||||
.flatMap(_.getArmy.units)
|
||||
.flatMap(_.battalionId)
|
||||
.map(battalions),
|
||||
bts = battalionTypes.values.toVector
|
||||
.map(startingGameState.battalions),
|
||||
bts = startingGameState.battalionTypes.toVector
|
||||
),
|
||||
armyGroup.armies.map(_.supplies.food).sum
|
||||
armyGroup.armies.map(_.supplies.map(_.food).getOrElse(0)).sum
|
||||
)
|
||||
}
|
||||
|
||||
private def uncontestedAttackResults(
|
||||
provinceWithAttackingArmies: ProvinceWithAttackingArmies
|
||||
): Vector[ActionResultT] = {
|
||||
provinceWithAttackingArmies: ProvinceWithAttackingArmies,
|
||||
gameState: GameState
|
||||
): Vector[ActionResult] = {
|
||||
Vector(
|
||||
ActionResultC(
|
||||
actionResultType = SentSuppliesResultType,
|
||||
ActionResult(
|
||||
`type` = SENT_SUPPLIES,
|
||||
changedProvinces = Vector(
|
||||
ChangedProvinceC(
|
||||
provinceId = provinceWithAttackingArmies.provinceId,
|
||||
ChangedProvince(
|
||||
id = provinceWithAttackingArmies.provinceId,
|
||||
removedIncomingArmyIds = provinceWithAttackingArmies.armies
|
||||
.flatMap(_.armies)
|
||||
.map(_.id),
|
||||
.map(_.id)
|
||||
.toVector,
|
||||
goldDelta = Some(
|
||||
provinceWithAttackingArmies.armies
|
||||
.flatMap(_.armies)
|
||||
.map(_.supplies.gold)
|
||||
.flatMap(_.supplies)
|
||||
.map(_.gold)
|
||||
.sum
|
||||
).filterNot(_ == 0),
|
||||
foodDelta = Some(
|
||||
provinceWithAttackingArmies.armies
|
||||
.flatMap(_.armies)
|
||||
.map(_.supplies.food)
|
||||
.flatMap(_.supplies)
|
||||
.map(_.food)
|
||||
.sum
|
||||
).filterNot(_ == 0)
|
||||
)
|
||||
)
|
||||
),
|
||||
ProvinceConqueredAction(
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId,
|
||||
currentDate = currentDate,
|
||||
conqueredProvince = provinces(provinceWithAttackingArmies.provinceId),
|
||||
notFledConquerors = provinceWithAttackingArmies.armies
|
||||
.flatMap(_.armies)
|
||||
.flatMap(_.army.units)
|
||||
.map(u =>
|
||||
ResolvedEagleUnit(
|
||||
hero = heroes(u.heroId),
|
||||
battalion = u.battalionId.map(battalions),
|
||||
status = UnitStatus.Normal
|
||||
)
|
||||
),
|
||||
notFledDefenders = Vector.empty,
|
||||
destroyedBattalions =
|
||||
provinces(provinceWithAttackingArmies.provinceId).battalionIds,
|
||||
defendingFactionId =
|
||||
provinces(provinceWithAttackingArmies.provinceId).rulingFactionId,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces
|
||||
).immediateExecute
|
||||
ActionResultProtoConverter.toProto(
|
||||
ProvinceConqueredAction(
|
||||
startingState = gameState,
|
||||
conqueredProvince =
|
||||
gameState.provinces(provinceWithAttackingArmies.provinceId),
|
||||
notFledConquerors = provinceWithAttackingArmies.armies
|
||||
.flatMap(_.armies)
|
||||
.flatMap(_.getArmy.units)
|
||||
.map(u => ResolvedEagleUnit(u, gameState)),
|
||||
notFledDefenders = Vector.empty,
|
||||
destroyedBattalions = gameState
|
||||
.provinces(provinceWithAttackingArmies.provinceId)
|
||||
.battalionIds
|
||||
.toVector,
|
||||
defendingFactionId = gameState
|
||||
.provinces(provinceWithAttackingArmies.provinceId)
|
||||
.rulingFactionId
|
||||
).immediateExecute
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private def provincesWithAttackingArmies
|
||||
: Vector[ProvinceWithAttackingArmies] =
|
||||
provinces.values
|
||||
startingGameState.provinces.values
|
||||
.filter(_.hostileArmies.nonEmpty)
|
||||
.map(p =>
|
||||
ProvinceWithAttackingArmies(
|
||||
provinceId = p.id,
|
||||
armies = p.hostileArmies
|
||||
armies = p.hostileArmies.toVector
|
||||
)
|
||||
)
|
||||
.toVector
|
||||
|
||||
private case class ProvinceWithAttackingArmies(
|
||||
case class ProvinceWithAttackingArmies(
|
||||
provinceId: ProvinceId,
|
||||
armies: Vector[HostileArmyGroup]
|
||||
)
|
||||
|
||||
+647
-81
@@ -1,81 +1,372 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.common.common_unit.UnitStatus as CommonUnitStatus
|
||||
import net.eagle0.common.common_unit.UnitStatus.{
|
||||
CAPTURED_UNIT,
|
||||
FLED_UNIT,
|
||||
NEVER_ENTERED_UNIT,
|
||||
NORMAL_UNIT,
|
||||
OUTLAWED_UNIT,
|
||||
RETREATED_UNIT,
|
||||
UNKNOWN_UNIT,
|
||||
Unrecognized
|
||||
}
|
||||
import net.eagle0.eagle.common.action_result_type.ActionResultType.BATTLE_ENDED
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.common.recruitment_info.RecruitmentInfo
|
||||
import net.eagle0.eagle.common.recruitment_info.RecruitmentStatus.RECRUITMENT_STATUS_OUTLAW
|
||||
import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
||||
import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.UNAFFILIATED_HERO_OUTLAW
|
||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.army.{Army, MovingArmy}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.battle_revelation.BattleRevelation
|
||||
import net.eagle0.eagle.internal.battle_revelation.BattleRevelation.RevelationType.DID_BATTLE
|
||||
import net.eagle0.eagle.internal.changed_hero.ChangedHero
|
||||
import net.eagle0.eagle.internal.changed_hero.ChangedHero.Vigor
|
||||
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
||||
import net.eagle0.eagle.internal.event_for_hero_backstory.{
|
||||
EventForHeroBackstory,
|
||||
FoughtInBattleBackstoryEvent
|
||||
}
|
||||
import net.eagle0.eagle.internal.event_for_hero_backstory.FoughtInBattleBackstoryEvent.UnitStatus
|
||||
import net.eagle0.eagle.internal.event_for_hero_backstory.FoughtInBattleBackstoryEvent.UnitStatus.{
|
||||
UNIT_STATUS_CAPTURED,
|
||||
UNIT_STATUS_FLED,
|
||||
UNIT_STATUS_NEVER_ENTERED,
|
||||
UNIT_STATUS_NORMAL,
|
||||
UNIT_STATUS_OUTLAWED,
|
||||
UNIT_STATUS_RETREATED,
|
||||
UNIT_STATUS_UNKNOWN
|
||||
}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle.BattleType
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle.BattleType.BATTLE_TYPE_ASSAULT_PROVINCE
|
||||
import net.eagle0.eagle.internal.shardok_battle.{ShardokBattle, ShardokPlayer}
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSequentialResultsAction
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplier
|
||||
import net.eagle0.eagle.library.actions.impl.common.{
|
||||
Action,
|
||||
ActionWithResultingState
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.util.ShatteredArmyUtils
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
BattleAgricultureDevastationDelta,
|
||||
BattleEconomyDevastationDelta,
|
||||
BattleInfrastructureDevastationDelta
|
||||
}
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.library.util.quest_fulfillment.QuestFulfillmentUtils
|
||||
import net.eagle0.eagle.library.util.view_filters.LegacyBattalionViewFilter
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
ActionResultProtoConverter,
|
||||
ArmyConverter,
|
||||
ChangedProvinceConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.model.state.shardok_battle.{BattleType, ShardokBattle}
|
||||
import net.eagle0.eagle.shardok_interface.BattleResolution
|
||||
import net.eagle0.eagle.{
|
||||
BattalionId,
|
||||
FactionId,
|
||||
GameId,
|
||||
HeroId,
|
||||
ProvinceId,
|
||||
RoundId
|
||||
}
|
||||
import net.eagle0.eagle.library.actions.impl.action.{
|
||||
ProvinceConqueredAction,
|
||||
ProvinceHeldAction
|
||||
import net.eagle0.eagle.model.state.quest.QuestT
|
||||
import net.eagle0.eagle.model.state.quest.concrete.DefeatFactionQuest
|
||||
import net.eagle0.eagle.shardok_interface.{
|
||||
BattleResolution,
|
||||
ResolvedEagleUnit,
|
||||
ResolvedShardokPlayer
|
||||
}
|
||||
import net.eagle0.eagle.{BattalionId, FactionId, HeroId, ProvinceId, RoundId}
|
||||
|
||||
case class ResolveBattleAction(
|
||||
gameId: GameId,
|
||||
currentRoundId: RoundId,
|
||||
currentDate: Date,
|
||||
heroes: Map[HeroId, HeroT],
|
||||
battalions: Map[BattalionId, BattalionT],
|
||||
provinces: Map[ProvinceId, ProvinceT],
|
||||
factions: Map[FactionId, FactionT],
|
||||
outstandingBattles: Vector[ShardokBattle],
|
||||
startingGameState: GameState,
|
||||
resolvedBattles: Vector[BattleResolution]
|
||||
) extends ProtolessSequentialResultsAction {
|
||||
) extends Action {
|
||||
def fleeProvince(hero: Hero, battle: ShardokBattle): Option[ProvinceId] =
|
||||
battle.players
|
||||
.find(_.eagleFid == hero.getFactionId)
|
||||
.map(sp =>
|
||||
sp.getArmyGroup.armies
|
||||
.map(_.getArmy)
|
||||
.find(_.units.exists(_.heroId == hero.id))
|
||||
.get
|
||||
)
|
||||
.flatMap(_.fleeProvinceId)
|
||||
|
||||
override def results: Vector[ActionResultT] = {
|
||||
override def execute(
|
||||
actionResultProtoApplier: ActionResultProtoApplier
|
||||
): Vector[ActionWithResultingState] =
|
||||
for {
|
||||
rb <- resolvedBattles
|
||||
sb <- outstandingBattles
|
||||
.find(_.shardokGameId == rb.battle.shardokGameId)
|
||||
.toVector
|
||||
res <- resolveOneBattleProtoless(sb, rb)
|
||||
sb <- outstandingBattle(
|
||||
startingGameState.outstandingBattles.toVector,
|
||||
rb
|
||||
).toVector
|
||||
res <- resolveOneBattle(
|
||||
startingState = startingGameState,
|
||||
actionResultProtoApplier = actionResultProtoApplier,
|
||||
battle = sb,
|
||||
battleResolution = rb
|
||||
)
|
||||
} yield res
|
||||
}
|
||||
|
||||
def fleeProvince(hero: HeroT, battle: ShardokBattle): Option[ProvinceId] =
|
||||
battle.players
|
||||
.find(_.eagleFid == hero.factionId.get)
|
||||
.flatMap(_.armyGroup)
|
||||
.flatMap(_.armies.find(_.army.units.exists(_.heroId == hero.id)))
|
||||
.flatMap(_.army.fleeProvinceId)
|
||||
private def newOutlaws(
|
||||
battleResolution: BattleResolution
|
||||
): Vector[UnaffiliatedHero] =
|
||||
(for {
|
||||
rsp <- battleResolution.resolvedPlayers
|
||||
outlaw <- rsp.units.filter(_.status == OUTLAWED_UNIT)
|
||||
} yield UnaffiliatedHero(
|
||||
heroId = outlaw.hero.id,
|
||||
`type` = UNAFFILIATED_HERO_OUTLAW,
|
||||
roundsInType = 0,
|
||||
recruitmentAttempted = false,
|
||||
lastFaction = Some(rsp.eagleFid),
|
||||
recruitmentInfo = Some(
|
||||
RecruitmentInfo(
|
||||
status = RECRUITMENT_STATUS_OUTLAW
|
||||
)
|
||||
)
|
||||
)).toVector
|
||||
|
||||
private def resolveOneBattleProtoless(
|
||||
private def defenderCP(
|
||||
battle: ShardokBattle,
|
||||
battleResolution: BattleResolution
|
||||
): Vector[ActionResultT] = {
|
||||
): ChangedProvince =
|
||||
ChangedProvince(
|
||||
id = battle.defenderProvince,
|
||||
newUnaffiliatedHeroes = newOutlaws(battleResolution),
|
||||
removedHostileArmyFactionIds =
|
||||
battle.players.filterNot(_.isDefender).map(_.eagleFid),
|
||||
removedRulingPlayerHeroIds = (for {
|
||||
rsp <- battleResolution.resolvedPlayers
|
||||
if startingGameState
|
||||
.provinces(battle.defenderProvince)
|
||||
.rulingFactionId
|
||||
.contains(rsp.eagleFid)
|
||||
rse <- rsp.units
|
||||
in <- battle.players
|
||||
.find(sp => rse.hero.factionId.contains(sp.eagleFid))
|
||||
if unitReturned(
|
||||
hid = rse.hero.id,
|
||||
in = in,
|
||||
out = rsp,
|
||||
battleType = battle.battleType
|
||||
)
|
||||
} yield rse.hero.id).toVector,
|
||||
removedBattalionIds = (for {
|
||||
rsp <- battleResolution.resolvedPlayers
|
||||
if startingGameState
|
||||
.provinces(battle.defenderProvince)
|
||||
.rulingFactionId
|
||||
.contains(rsp.eagleFid)
|
||||
rse <- rsp.units
|
||||
in <- battle.players
|
||||
.find(sp => rse.hero.factionId.contains(sp.eagleFid))
|
||||
if unitReturned(
|
||||
hid = rse.hero.id,
|
||||
in = in,
|
||||
out = rsp,
|
||||
battleType = battle.battleType
|
||||
)
|
||||
} yield rse.battalion.id).toVector,
|
||||
goldDelta = Some(
|
||||
battle.players
|
||||
.filterNot(_.isDefender)
|
||||
.flatMap(_.getArmyGroup.armies)
|
||||
.map(_.getSupplies.gold)
|
||||
.sum
|
||||
),
|
||||
foodDelta = Some(
|
||||
battle.players
|
||||
.filterNot(_.isDefender)
|
||||
.flatMap(_.getArmyGroup.armies)
|
||||
.map(_.getSupplies.food)
|
||||
.sum
|
||||
),
|
||||
economyDevastationDelta = Some(BattleEconomyDevastationDelta.doubleValue),
|
||||
agricultureDevastationDelta =
|
||||
Some(BattleAgricultureDevastationDelta.doubleValue),
|
||||
infrastructureDevastationDelta =
|
||||
Some(BattleInfrastructureDevastationDelta.doubleValue),
|
||||
addedBattleRevelations =
|
||||
battle.players.filterNot(_.isDefender).map { sp =>
|
||||
BattleRevelation(
|
||||
provinceId = battle.defenderProvince,
|
||||
revealedToFactionId = sp.eagleFid,
|
||||
revelationType = DID_BATTLE
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
private def defeatLoserQuestCompletionCheck(
|
||||
losingFids: Vector[FactionId]
|
||||
): (QuestT, ProvinceT) => Boolean =
|
||||
(q, _: ProvinceT) =>
|
||||
q match {
|
||||
case DefeatFactionQuest(targetFactionId) =>
|
||||
losingFids.contains(targetFactionId)
|
||||
case _ => false
|
||||
}
|
||||
|
||||
private def winnersFulfilledQuestActionResults(
|
||||
startingState: GameState,
|
||||
battleResolution: BattleResolution
|
||||
): Vector[ActionResultT] =
|
||||
battleResolution.resolvedPlayers.toVector.partition(
|
||||
_.endGameCondition.condition.isVictory
|
||||
) match {
|
||||
case (winners, losers) =>
|
||||
winners
|
||||
.flatMap(rsp =>
|
||||
LegacyFactionUtils.provinces(rsp.eagleFid, startingState)
|
||||
)
|
||||
.flatMap(p =>
|
||||
QuestFulfillmentUtils.fulfilledQuestResults(
|
||||
province = ProvinceConverter.fromProto(p),
|
||||
isFulfilled =
|
||||
defeatLoserQuestCompletionCheck(losers.map(_.eagleFid)),
|
||||
gameId = startingState.gameId,
|
||||
currentDate = DateConverter.fromProto(startingState.currentDate),
|
||||
currentRoundId = startingState.currentRoundId,
|
||||
previousBackstoryTextIdLookup = hid => {
|
||||
startingState.heroes(hid).backstoryVersions.last.textId
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private def verifyHidsOutMatchHidsIn(
|
||||
battle: ShardokBattle,
|
||||
battleResolution: BattleResolution
|
||||
): Unit = {
|
||||
battle.players
|
||||
.sortBy(_.eagleFid)
|
||||
.zip(battleResolution.resolvedPlayers.sortBy(_.eagleFid))
|
||||
.foreach { case (setup, resolved) =>
|
||||
internalRequire(
|
||||
resolved.eagleFid == setup.eagleFid,
|
||||
s"FactionIds do not match for all players in ${battle.players}, ${battleResolution.resolvedPlayers}"
|
||||
)
|
||||
|
||||
val sentHids =
|
||||
setup.getArmyGroup.armies
|
||||
.flatMap(_.getArmy.units)
|
||||
.map(_.heroId)
|
||||
.toSet
|
||||
val returnedHids = resolved.units.map(_.hero.id).toSet
|
||||
|
||||
internalRequire(
|
||||
sentHids == returnedHids,
|
||||
s"Sent $sentHids into battle but got $returnedHids back"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def battleEndedARs(
|
||||
startingState: GameState,
|
||||
battle: ShardokBattle,
|
||||
battleResolution: BattleResolution
|
||||
): Vector[ActionResult] = {
|
||||
val changedHeroes = battleResolution.resolvedPlayers
|
||||
.flatMap(_.units)
|
||||
.map { u =>
|
||||
val isAttacker = !battle.players
|
||||
.find(_.eagleFid == u.hero.factionId.get)
|
||||
.get
|
||||
.isDefender
|
||||
|
||||
changedHeroWithNewBackstoryRequest(
|
||||
newHero = u.hero,
|
||||
newBattalion = Option.when(
|
||||
u.battalion != Battalion.defaultInstance && u.battalion.id != -1
|
||||
) {
|
||||
u.battalion
|
||||
},
|
||||
status = u.status match {
|
||||
case UNKNOWN_UNIT => UNIT_STATUS_UNKNOWN
|
||||
case NORMAL_UNIT => UNIT_STATUS_NORMAL
|
||||
case CAPTURED_UNIT => UNIT_STATUS_CAPTURED
|
||||
case FLED_UNIT => UNIT_STATUS_FLED
|
||||
case RETREATED_UNIT => UNIT_STATUS_RETREATED
|
||||
case NEVER_ENTERED_UNIT => UNIT_STATUS_NEVER_ENTERED
|
||||
case OUTLAWED_UNIT => UNIT_STATUS_OUTLAWED
|
||||
case CommonUnitStatus.Unrecognized(value) =>
|
||||
UnitStatus.Unrecognized(value)
|
||||
},
|
||||
provinceId = battle.defenderProvince,
|
||||
enemyFactionIds = battle.players
|
||||
.filter(_.isDefender == isAttacker)
|
||||
.map(_.eagleFid)
|
||||
.toVector,
|
||||
isVictorious = battleResolution.resolvedPlayers
|
||||
.find(_.eagleFid == u.hero.factionId.get)
|
||||
.get
|
||||
.endGameCondition
|
||||
.condition
|
||||
.isVictory,
|
||||
isAttacker = isAttacker,
|
||||
startingState = startingState
|
||||
)
|
||||
}
|
||||
.toVector
|
||||
|
||||
ActionResult(
|
||||
`type` = BATTLE_ENDED,
|
||||
changedHeroes = changedHeroes,
|
||||
changedBattalions = battleResolution.resolvedPlayers
|
||||
.flatMap(_.units)
|
||||
.map(_.battalion)
|
||||
.filterNot(_.id == 0)
|
||||
.filterNot(_.size == 0)
|
||||
.toVector,
|
||||
changedProvinces = changedProvincesFromWithdrawnUnits(
|
||||
batt = battle,
|
||||
br = battleResolution,
|
||||
destroyedBattalionIds = destroyedBattalionIds(battleResolution)
|
||||
) :+ defenderCP(
|
||||
battle = battle,
|
||||
battleResolution = battleResolution
|
||||
),
|
||||
destroyedBattalionIds = destroyedBattalionIds(battleResolution),
|
||||
resolvedBattle = Some(battle.shardokGameId)
|
||||
) +: winnersFulfilledQuestActionResults(
|
||||
startingState = startingState,
|
||||
battleResolution = battleResolution
|
||||
).map(ActionResultProtoConverter.toProto)
|
||||
}
|
||||
|
||||
private def resolveOneBattle(
|
||||
startingState: GameState,
|
||||
actionResultProtoApplier: ActionResultProtoApplier,
|
||||
battle: ShardokBattle,
|
||||
battleResolution: BattleResolution
|
||||
): Vector[ActionWithResultingState] = {
|
||||
internalRequire(
|
||||
battle == battleResolution.battle,
|
||||
s"Battles do not match! Sent $battle but resolved ${battleResolution.battle}"
|
||||
)
|
||||
|
||||
verifyHidsOutMatchHidsIn(battle, battleResolution)
|
||||
|
||||
val (winningResolvedPlayers, losingResolvedPlayers) =
|
||||
battleResolution.resolvedPlayers.toVector.partition(
|
||||
_.endGameCondition.condition.isVictory
|
||||
)
|
||||
|
||||
val battleEndedResults = actionResultProtoApplier.applyActionResults(
|
||||
startingState,
|
||||
battleEndedARs(startingState, battle, battleResolution)
|
||||
)
|
||||
|
||||
val winningShardokPlayers = winningResolvedPlayers
|
||||
.map(_.eagleFid)
|
||||
.flatMap(fid => battle.players.find(_.eagleFid == fid))
|
||||
|
||||
val finalResult = battle.battleType match {
|
||||
case BattleType.Unknown =>
|
||||
case BattleType.BATTLE_TYPE_UNKNOWN =>
|
||||
throw new EagleInternalException("Unknown battle type")
|
||||
case BattleType.AssaultProvince =>
|
||||
case BattleType.BATTLE_TYPE_ASSAULT_PROVINCE =>
|
||||
internalRequire(
|
||||
winningShardokPlayers.size == 1,
|
||||
s"Assault province battle type must have exactly one victor"
|
||||
@@ -83,57 +374,121 @@ case class ResolveBattleAction(
|
||||
|
||||
val wsp = winningShardokPlayers.head
|
||||
if wsp.isDefender then
|
||||
val gs = battleEndedResults.last.gameState
|
||||
val defendingProv = gs.provinces(battle.defenderProvince)
|
||||
val capturedUnits = battleResolution.resolvedPlayers.toVector
|
||||
.filterNot(_.eagleFid == wsp.eagleFid)
|
||||
.flatMap { losingPlayer =>
|
||||
losingPlayer.units
|
||||
.filterNot(u =>
|
||||
unitReturned(
|
||||
hid = u.hero.id,
|
||||
in = battle.players
|
||||
.find(_.eagleFid == losingPlayer.eagleFid)
|
||||
.get,
|
||||
out = losingPlayer,
|
||||
battleType = BATTLE_TYPE_ASSAULT_PROVINCE
|
||||
)
|
||||
)
|
||||
.filterNot(_.status.isOutlawedUnit)
|
||||
}
|
||||
|
||||
ProvinceHeldAction(
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId,
|
||||
defendingProvince = provinces(battle.defenderProvince),
|
||||
gameId = gs.gameId,
|
||||
currentRoundId = gs.currentRoundId,
|
||||
defendingProvince = ProvinceConverter.fromProto(defendingProv),
|
||||
attackingFactionIds = losingResolvedPlayers.map(_.eagleFid),
|
||||
capturedHeroIds =
|
||||
Vector.empty, // TODO: implement captured units logic
|
||||
destroyedBattalionIds = destroyedBattalionIds(battleResolution),
|
||||
heroFactions = Map.empty // TODO: implement hero factions
|
||||
).immediateExecute
|
||||
capturedHeroIds = capturedUnits.map(_.hero.id),
|
||||
destroyedBattalionIds = capturedUnits
|
||||
.flatMap(_.optionalBattalionId)
|
||||
.filter(gs.battalions.contains),
|
||||
heroFactions = capturedUnits
|
||||
.map(u => u.hero.id -> gs.heroes(u.hero.id).factionId.get)
|
||||
.toMap
|
||||
)
|
||||
else
|
||||
ProvinceConqueredAction(
|
||||
gameId = gameId,
|
||||
currentRoundId = currentRoundId,
|
||||
currentDate = currentDate,
|
||||
conqueredProvince = provinces(battle.defenderProvince),
|
||||
notFledConquerors = Vector.empty, // TODO: implement
|
||||
notFledDefenders = Vector.empty, // TODO: implement
|
||||
startingState = battleEndedResults.last.gameState,
|
||||
conqueredProvince = battleEndedResults.last.gameState
|
||||
.provinces(battle.defenderProvince),
|
||||
notFledConquerors = winningResolvedPlayers.head.units
|
||||
.filterNot(u =>
|
||||
unitReturned(
|
||||
hid = u.hero.id,
|
||||
in = wsp,
|
||||
out = winningResolvedPlayers.head,
|
||||
battleType = BATTLE_TYPE_ASSAULT_PROVINCE
|
||||
)
|
||||
)
|
||||
.filterNot(_.status == OUTLAWED_UNIT)
|
||||
.toVector,
|
||||
notFledDefenders = (for {
|
||||
sp <- battle.players.filter(_.isDefender)
|
||||
rsp = battleResolution.resolvedPlayers
|
||||
.find(_.eagleFid == sp.eagleFid)
|
||||
.get
|
||||
u <- rsp.units.filterNot(u =>
|
||||
unitReturned(
|
||||
hid = u.hero.id,
|
||||
in = sp,
|
||||
out = rsp,
|
||||
battleType = BATTLE_TYPE_ASSAULT_PROVINCE
|
||||
)
|
||||
)
|
||||
} yield u).toVector,
|
||||
destroyedBattalions = destroyedBattalionIds(battleResolution),
|
||||
defendingFactionId =
|
||||
battle.players.find(_.isDefender).map(_.eagleFid),
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces
|
||||
).immediateExecute
|
||||
battle.players.find(_.isDefender).map(_.eagleFid)
|
||||
)
|
||||
end if
|
||||
case BattleType.FreeForAll =>
|
||||
case BattleType.BATTLE_TYPE_FREE_FOR_ALL =>
|
||||
Option
|
||||
.when(winningShardokPlayers.nonEmpty) {
|
||||
WonFreeForAllAction(
|
||||
battalions = battalions,
|
||||
battleProvince = provinces(battle.defenderProvince),
|
||||
winningUnits = winningResolvedPlayers.flatMap(_.units),
|
||||
winningArmyGroups =
|
||||
winningShardokPlayers.flatMap(_.armyGroup).toVector
|
||||
).immediateExecute
|
||||
startingState = battleEndedResults.last.gameState,
|
||||
battleProvince = battleEndedResults.last.gameState
|
||||
.provinces(battle.defenderProvince),
|
||||
winningUnits = for {
|
||||
wrp <- winningResolvedPlayers
|
||||
wsp = battle.players.find(_.eagleFid == wrp.eagleFid).get
|
||||
reu <- wrp.units
|
||||
if !unitReturned(
|
||||
hid = reu.hero.id,
|
||||
in = wsp,
|
||||
out = wrp,
|
||||
battleType = BattleType.BATTLE_TYPE_FREE_FOR_ALL
|
||||
)
|
||||
} yield reu,
|
||||
winningPlayers = winningShardokPlayers
|
||||
)
|
||||
}
|
||||
.getOrElse(
|
||||
FreeForAllDrawAction(
|
||||
defenderProvince = battle.defenderProvince,
|
||||
armiesFromPlayers =
|
||||
battle.players.flatMap(_.armyGroup).flatMap(_.armies).toVector,
|
||||
remainingUnits = battleResolution.resolvedPlayers.flatMap(_.units)
|
||||
).immediateExecute
|
||||
startingState = battleEndedResults.last.gameState,
|
||||
battle = battle,
|
||||
remainingUnits = battleResolution.resolvedPlayers.flatMap { rsp =>
|
||||
rsp.units.filterNot(reu =>
|
||||
unitReturned(
|
||||
hid = reu.hero.id,
|
||||
in = battle.players.find(_.eagleFid == rsp.eagleFid).get,
|
||||
out = rsp,
|
||||
battleType = BattleType.BATTLE_TYPE_FREE_FOR_ALL
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
case BattleType.Sally =>
|
||||
case BattleType.BATTLE_TYPE_SALLY =>
|
||||
throw new EagleInternalException("Sally battle unimplemented")
|
||||
case BattleType.Unrecognized(value) =>
|
||||
throw new EagleInternalException(s"Unrecognized battle type $value")
|
||||
}
|
||||
|
||||
Vector(finalResult)
|
||||
battleEndedResults :+ actionResultProtoApplier
|
||||
.applyActionResult(
|
||||
battleEndedResults.last.gameState,
|
||||
ActionResultProtoConverter.toProto(finalResult.immediateExecute)
|
||||
)
|
||||
}
|
||||
|
||||
def destroyedBattalionIds(
|
||||
@@ -141,15 +496,226 @@ case class ResolveBattleAction(
|
||||
): Vector[BattalionId] =
|
||||
(battleResolution.resolvedPlayers
|
||||
.flatMap(_.units)
|
||||
.flatMap(_.battalion)
|
||||
.filterNot(_.hasDefaultBattalion)
|
||||
.map(_.battalion)
|
||||
.filter(_.size == 0)
|
||||
.map(_.id)
|
||||
.filterNot(_ == 0) ++ battleResolution.resolvedPlayers
|
||||
.flatMap(_.units)
|
||||
.filter(unit =>
|
||||
unit.status == UnitStatus.Captured || unit.status == UnitStatus.Outlawed
|
||||
unit.status == CAPTURED_UNIT || unit.status == OUTLAWED_UNIT
|
||||
)
|
||||
.flatMap(_.optionalBattalionId)
|
||||
.map(_.battalion.id)
|
||||
.filterNot(_ == 0)).toVector
|
||||
|
||||
private def maybeDelta(newValue: Int, oldValue: Int): Option[Int] =
|
||||
Option(newValue - oldValue).filterNot(_ == 0)
|
||||
|
||||
private def changedHeroWithNewBackstoryRequest(
|
||||
newHero: Hero,
|
||||
newBattalion: Option[Battalion],
|
||||
status: UnitStatus,
|
||||
provinceId: ProvinceId,
|
||||
isVictorious: Boolean,
|
||||
isAttacker: Boolean,
|
||||
enemyFactionIds: Vector[FactionId],
|
||||
startingState: GameState
|
||||
): ChangedHero = (
|
||||
startingState.heroes(newHero.id),
|
||||
newBattalion.map(b => startingState.battalions(b.id))
|
||||
) match {
|
||||
case (originalHero, originalBattalion) =>
|
||||
val backstoryEvent = Option.when(enemyFactionIds.nonEmpty) {
|
||||
EventForHeroBackstory(
|
||||
date = startingState.currentDate,
|
||||
details = FoughtInBattleBackstoryEvent(
|
||||
provinceId = provinceId,
|
||||
battalion = originalBattalion.map(
|
||||
LegacyBattalionViewFilter.filteredBattalionViewBelongingToPlayer
|
||||
),
|
||||
isVictorious = isVictorious,
|
||||
isAttacker = isAttacker,
|
||||
status = status,
|
||||
enemyFactionIds = enemyFactionIds
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ChangedHero(
|
||||
id = newHero.id,
|
||||
vigor = Vigor.VigorAbsolute(newHero.vigor),
|
||||
strengthXpDelta =
|
||||
maybeDelta(newHero.strengthXp, originalHero.strengthXp),
|
||||
agilityXpDelta = maybeDelta(newHero.agilityXp, originalHero.agilityXp),
|
||||
wisdomXpDelta = maybeDelta(newHero.wisdomXp, originalHero.wisdomXp),
|
||||
charismaXpDelta =
|
||||
maybeDelta(newHero.charismaXp, originalHero.charismaXp),
|
||||
constitutionXpDelta =
|
||||
maybeDelta(newHero.constitutionXp, originalHero.constitutionXp),
|
||||
newBackstoryEvents = backstoryEvent.toVector
|
||||
)
|
||||
}
|
||||
|
||||
private def changedProvincesFromWithdrawnUnits(
|
||||
batt: ShardokBattle,
|
||||
br: BattleResolution,
|
||||
destroyedBattalionIds: Vector[BattalionId]
|
||||
): Vector[ChangedProvince] =
|
||||
(for {
|
||||
sp <- batt.players
|
||||
rsp = br.resolvedPlayers.find(_.eagleFid == sp.eagleFid).get
|
||||
ma <- sp.getArmyGroup.armies
|
||||
army <- armyFromResolvedArmy(
|
||||
ma = ma,
|
||||
sp = sp,
|
||||
rsp = rsp,
|
||||
bt = batt.battleType,
|
||||
destroyedBattalionIds = destroyedBattalionIds
|
||||
)
|
||||
} yield ma.getArmy.fleeProvinceId
|
||||
.map(fleeProvince =>
|
||||
changedProvinceFromFledUnits(
|
||||
fledArmy = army,
|
||||
arrivalRound = batt.roundStarted + 1,
|
||||
destinationProvince = fleeProvince,
|
||||
battleProvince = batt.defenderProvince
|
||||
)
|
||||
)
|
||||
.getOrElse(
|
||||
changedProvinceFromShatteredArmy(
|
||||
shatteredArmy = ma,
|
||||
battleProvinceId = batt.defenderProvince
|
||||
)
|
||||
)).toVector
|
||||
|
||||
private def armyFromResolvedArmy(
|
||||
ma: MovingArmy,
|
||||
sp: ShardokPlayer,
|
||||
rsp: ResolvedShardokPlayer,
|
||||
bt: BattleType,
|
||||
destroyedBattalionIds: Vector[BattalionId]
|
||||
): Option[Army] =
|
||||
armyFromResolvedUnits(
|
||||
units = ma.getArmy.units
|
||||
.filter(u =>
|
||||
unitReturned(hid = u.heroId, in = sp, out = rsp, battleType = bt)
|
||||
)
|
||||
.flatMap(cu => rsp.units.find(_.hero.id == cu.heroId))
|
||||
.toVector,
|
||||
removedBattalionIds = destroyedBattalionIds,
|
||||
fid = ma.getArmy.factionId,
|
||||
fleeProvinceId =
|
||||
Option.when(rsp.endGameCondition.condition.isAllyVictory)(
|
||||
ma.destinationProvince
|
||||
)
|
||||
)
|
||||
|
||||
private def armyFromResolvedUnits(
|
||||
units: Vector[ResolvedEagleUnit],
|
||||
removedBattalionIds: Vector[BattalionId],
|
||||
fid: FactionId,
|
||||
fleeProvinceId: Option[ProvinceId]
|
||||
): Option[Army] =
|
||||
if units.isEmpty then None
|
||||
else
|
||||
Some(
|
||||
Army(
|
||||
fleeProvinceId = fleeProvinceId,
|
||||
factionId = fid,
|
||||
units = units
|
||||
.map(ru =>
|
||||
CombatUnit(
|
||||
factionId = fid,
|
||||
heroId = ru.hero.id,
|
||||
battalionId = ru.optionalBattalionId
|
||||
.filterNot(
|
||||
removedBattalionIds.contains(_)
|
||||
)
|
||||
.filterNot(_ == 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private def changedProvinceFromFledUnits(
|
||||
fledArmy: Army,
|
||||
arrivalRound: RoundId,
|
||||
destinationProvince: ProvinceId,
|
||||
battleProvince: ProvinceId
|
||||
): ChangedProvince =
|
||||
ChangedProvince(
|
||||
id = destinationProvince,
|
||||
addedIncomingArmies = Vector(
|
||||
MovingArmy(
|
||||
army = Some(fledArmy),
|
||||
arrivalRound = arrivalRound,
|
||||
destinationProvince = destinationProvince,
|
||||
originProvince = battleProvince
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private def changedProvinceFromShatteredArmy(
|
||||
shatteredArmy: MovingArmy,
|
||||
battleProvinceId: ProvinceId
|
||||
): ChangedProvince =
|
||||
ChangedProvinceConverter.toProto(
|
||||
ShatteredArmyUtils.provinceWithOutlawsForIncomingArmy(
|
||||
currentRoundId = startingGameState.currentRoundId,
|
||||
province = ProvinceConverter.fromProto(
|
||||
startingGameState.provinces(battleProvinceId)
|
||||
),
|
||||
incomingArmy = ArmyConverter.fromProto(shatteredArmy)
|
||||
)
|
||||
)
|
||||
|
||||
private def unitReturned(
|
||||
hid: HeroId,
|
||||
in: ShardokPlayer,
|
||||
out: ResolvedShardokPlayer,
|
||||
battleType: BattleType
|
||||
): Boolean =
|
||||
out.units.find(_.hero.id == hid).get.status match {
|
||||
case FLED_UNIT => true
|
||||
case NEVER_ENTERED_UNIT =>
|
||||
!in.isDefender &&
|
||||
in.getArmyGroup.armies
|
||||
.find(_.getArmy.units.exists(_.heroId == hid))
|
||||
.get
|
||||
.getArmy
|
||||
.fleeProvinceId
|
||||
.isDefined &&
|
||||
!out.endGameCondition.condition.isVictory
|
||||
case CAPTURED_UNIT =>
|
||||
out.endGameCondition.condition.isDraw || out.endGameCondition.condition.isAllyVictory
|
||||
case NORMAL_UNIT =>
|
||||
out.endGameCondition.condition.isDraw || out.endGameCondition.condition.isAllyVictory
|
||||
case RETREATED_UNIT => false
|
||||
case OUTLAWED_UNIT => false
|
||||
case UNKNOWN_UNIT =>
|
||||
throw new EagleInternalException("Unknown unit status")
|
||||
case Unrecognized(value) =>
|
||||
throw new EagleInternalException(s"Unrecognized unit status $value")
|
||||
}
|
||||
|
||||
private def outstandingBattle(
|
||||
outstandingBattles: Vector[ShardokBattle],
|
||||
resolvedBattle: BattleResolution
|
||||
): Option[ShardokBattle] = {
|
||||
val obo = outstandingBattles
|
||||
.find(_.shardokGameId == resolvedBattle.battle.shardokGameId)
|
||||
.orElse {
|
||||
printf(
|
||||
s"Failed to locate outstanding battle ${resolvedBattle.battle.shardokGameId}; may already be resolved"
|
||||
)
|
||||
None
|
||||
}
|
||||
|
||||
if obo.exists(ob => ob != resolvedBattle.battle) then {
|
||||
throw new EagleInternalException(
|
||||
s"Battles do not match! ${obo.get} vs ${resolvedBattle.battle}"
|
||||
)
|
||||
}
|
||||
obo
|
||||
}
|
||||
}
|
||||
|
||||
+23
-23
@@ -1,45 +1,45 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.model.state.CombatUnit
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokPlayer
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessSimpleAction
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.concrete.ActionResultC
|
||||
import net.eagle0.eagle.model.action_result.types.WonFreeForAllResultType
|
||||
import net.eagle0.eagle.model.proto_converters.ArmyConverter
|
||||
import net.eagle0.eagle.model.state.HostileArmyGroup
|
||||
import net.eagle0.eagle.model.state.HostileArmyGroupStatus.AwaitingDecision
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.shardok_interface.ResolvedEagleUnit
|
||||
import net.eagle0.eagle.BattalionId
|
||||
|
||||
case class WonFreeForAllAction(
|
||||
battalions: Map[BattalionId, BattalionT],
|
||||
battleProvince: ProvinceT,
|
||||
startingState: GameState,
|
||||
battleProvince: Province,
|
||||
winningUnits: Vector[ResolvedEagleUnit],
|
||||
winningArmyGroups: Vector[HostileArmyGroup]
|
||||
winningPlayers: Vector[ShardokPlayer]
|
||||
) extends ProtolessSimpleAction {
|
||||
|
||||
override def immediateExecute: ActionResultT = {
|
||||
val notFledHids = winningUnits.map(_.hero.id)
|
||||
val incomingArmies = winningArmyGroups
|
||||
.flatMap(_.armies)
|
||||
.map { movingArmy =>
|
||||
movingArmy.copy(
|
||||
army = movingArmy.army.copy(
|
||||
units = movingArmy.army.units
|
||||
.filter(cu => notFledHids.contains(cu.heroId))
|
||||
.map(cu =>
|
||||
CombatUnit(
|
||||
heroId = cu.heroId,
|
||||
battalionId = cu.battalionId.filter(battalions.contains),
|
||||
factionId = cu.factionId
|
||||
)
|
||||
val incomingArmies = winningPlayers
|
||||
.flatMap(_.getArmyGroup.armies)
|
||||
.map { army =>
|
||||
army.update(
|
||||
_.army.units.modify(
|
||||
_.filter(cu => notFledHids.contains(cu.heroId)).map(cu =>
|
||||
CombatUnit(
|
||||
heroId = cu.heroId,
|
||||
battalionId =
|
||||
cu.battalionId.filter(startingState.battalions.contains),
|
||||
factionId = cu.factionId
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
.filter(_.army.units.nonEmpty)
|
||||
.filter(_.getArmy.units.nonEmpty)
|
||||
.toVector
|
||||
|
||||
ActionResultC(
|
||||
@@ -51,11 +51,11 @@ case class WonFreeForAllAction(
|
||||
removedHostileArmyFactionIds =
|
||||
battleProvince.hostileArmies.map(_.factionId).toVector,
|
||||
newHostileArmies = incomingArmies
|
||||
.groupBy(_.army.factionId)
|
||||
.groupBy(_.getArmy.factionId)
|
||||
.map { case (fid, armies) =>
|
||||
HostileArmyGroup(
|
||||
factionId = fid,
|
||||
armies = armies,
|
||||
armies = armies.map(ArmyConverter.fromProto),
|
||||
status = AwaitingDecision
|
||||
)
|
||||
}
|
||||
|
||||
@@ -93,7 +93,6 @@ scala_library(
|
||||
"//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_hero_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_type_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_type_id_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:combat_unit_converter",
|
||||
@@ -101,6 +100,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:proto_conversion_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:tribute_amount_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:unaffiliated_hero_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/diplomacy_offer/status",
|
||||
|
||||
@@ -53,7 +53,6 @@ import net.eagle0.eagle.model.proto_converters.province.{
|
||||
ProvinceOrderTypeConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
BattalionConverter,
|
||||
BattalionTypeConverter,
|
||||
ImprovementTypeConverter,
|
||||
ProtoConversionException,
|
||||
@@ -61,6 +60,7 @@ import net.eagle0.eagle.model.proto_converters.{
|
||||
UnaffiliatedHeroConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionTypeIdConverter
|
||||
import net.eagle0.eagle.model.proto_converters.battalion.BattalionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.diplomacy_option.DiplomacyOptionConverter
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.{
|
||||
AllianceOffer,
|
||||
@@ -439,9 +439,7 @@ class CommandFactory {
|
||||
gold = sc.gold,
|
||||
food = sc.food,
|
||||
availableDestinationProvinceIds =
|
||||
commandForOrigin.availableDestinationProvinces
|
||||
.map(_.provinceId)
|
||||
.toVector,
|
||||
commandForOrigin.availableDestinationProvinces.toVector,
|
||||
availableHeroIds = commandForOrigin.availableHeroIds.toVector,
|
||||
availableBattalionIds =
|
||||
commandForOrigin.availableBattalions.map(_.battalionId).toVector,
|
||||
|
||||
@@ -87,20 +87,6 @@ scala_library(
|
||||
],
|
||||
)
|
||||
|
||||
scala_library(
|
||||
name = "battalion_utils",
|
||||
srcs = ["BattalionUtils.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
],
|
||||
)
|
||||
|
||||
scala_library(
|
||||
name = "beast_utils",
|
||||
srcs = ["BeastUtils.scala"],
|
||||
@@ -127,8 +113,6 @@ scala_library(
|
||||
],
|
||||
visibility = [
|
||||
":__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util:__pkg__",
|
||||
],
|
||||
exports = [
|
||||
@@ -233,7 +217,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_power",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
],
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package net.eagle0.eagle.library.util
|
||||
|
||||
import net.eagle0.eagle.model.state.BattalionType
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
|
||||
object BattalionUtils {
|
||||
def monthlyConsumedFood(
|
||||
battalions: Iterable[BattalionT],
|
||||
bts: Vector[BattalionType]
|
||||
): Int =
|
||||
battalions
|
||||
.map(b => monthlyConsumedFood(b, bts))
|
||||
.sum
|
||||
.ceil
|
||||
.toInt
|
||||
|
||||
def monthlyConsumedFood(
|
||||
battalion: BattalionT,
|
||||
bts: Vector[BattalionType]
|
||||
): Double = {
|
||||
val battalionType = bts.find(_.typeId == battalion.typeId).get
|
||||
battalion.size.toDouble * battalionType.monthlyFoodCost
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.util.BattalionPower
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.battalion.BattalionConverter
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId, RoundId}
|
||||
|
||||
object IncomingArmyUtils {
|
||||
|
||||
+15
-4
@@ -96,8 +96,7 @@ object AttackCommandChooser {
|
||||
): Vector[PossibleAttackCommandInfo] =
|
||||
(for {
|
||||
opmc <- ac.oneProvinceCommands
|
||||
availableDestination <- opmc.availableDestinationProvinces
|
||||
destinationProvinceId = availableDestination.provinceId
|
||||
destinationProvinceId <- opmc.availableDestinationProvinces
|
||||
destinationProvince = gameState.provinces(destinationProvinceId)
|
||||
if destinationFactionIsEligible(
|
||||
actingFactionId = actingFactionId,
|
||||
@@ -130,8 +129,20 @@ object AttackCommandChooser {
|
||||
battalionTypes = gameState.battalionTypes.toVector
|
||||
)
|
||||
|
||||
// Get river crossing requirement from the available destination
|
||||
val requiresRiverCrossing = availableDestination.requiresRiverCrossing
|
||||
val startingPositionIndex = destinationProvince.neighbors
|
||||
.find(n => n.provinceId == originProvince.id)
|
||||
.get
|
||||
.startingPositionIndex
|
||||
|
||||
val isWinter = winterMonths.contains(gameState.currentDate.get.month)
|
||||
|
||||
val mapInfo = ShardokMapInfo.shardokMapInfos(destinationProvince.id)
|
||||
val destinationCastleCount =
|
||||
ShardokMapInfo.shardokMapInfos(destinationProvince.id).castleCount
|
||||
val requiresRiverCrossing = !isWinter && (10 - mapInfo
|
||||
.waterCrossingRequirementsByPosition(
|
||||
startingPositionIndex
|
||||
) > destinationCastleCount)
|
||||
|
||||
PossibleAttackCommandInfo(
|
||||
selectedUnits = selectedUnits,
|
||||
|
||||
+3
-3
@@ -1218,7 +1218,7 @@ object CommandChoiceHelpers {
|
||||
): Option[CommandSelection] = {
|
||||
val originProvince = gameState.provinces(opmc.originProvinceId)
|
||||
opmc.availableDestinationProvinces
|
||||
.map(dest => gameState.provinces(dest.provinceId))
|
||||
.map(gameState.provinces)
|
||||
.filter(_.rulingFactionId.contains(actingFactionId))
|
||||
.filter(p => p.rulingFactionHeroIds.length < p.heroCap)
|
||||
.maxByOption(p => p.rulingFactionHeroIds.length - p.heroCap)
|
||||
@@ -1288,7 +1288,7 @@ object CommandChoiceHelpers {
|
||||
functionalRandom
|
||||
.nextRandomElementOpt(
|
||||
opmc.availableDestinationProvinces
|
||||
.map(dest => gameState.provinces(dest.provinceId))
|
||||
.map(gameState.provinces)
|
||||
.filter(_.rulingFactionId.isEmpty)
|
||||
) match {
|
||||
case RandomState(Some(destProvince), fr) =>
|
||||
@@ -1744,7 +1744,7 @@ object CommandChoiceHelpers {
|
||||
else {
|
||||
val interestingDestinations =
|
||||
fromHere.get.availableDestinationProvinces
|
||||
.map(dest => gs.provinces(dest.provinceId))
|
||||
.map(gs.provinces)
|
||||
.filter(_.rulingFactionId.contains(actingFactionId))
|
||||
.filterNot(p =>
|
||||
LegacyProvinceUtils.containsFactionLeader(p.id, gs)
|
||||
|
||||
+2
-4
@@ -51,9 +51,7 @@ object ExpandCommandSelector {
|
||||
expansionCandidateProvinces(
|
||||
gameState,
|
||||
oneProvinceCommand.originProvinceId,
|
||||
oneProvinceCommand.availableDestinationProvinces
|
||||
.map(_.provinceId)
|
||||
.toVector
|
||||
oneProvinceCommand.availableDestinationProvinces.toVector
|
||||
).partition(p =>
|
||||
LegacyProvinceUtils
|
||||
.isAdjacentEnemy(p.id, actingFactionId, gameState)
|
||||
@@ -392,7 +390,7 @@ object ExpandCommandSelector {
|
||||
spreadHeroesMarchCandidates(
|
||||
gameState,
|
||||
opmc.originProvinceId,
|
||||
opmc.availableDestinationProvinces.map(_.provinceId).toVector
|
||||
opmc.availableDestinationProvinces.toVector
|
||||
).map(
|
||||
(opmc, _)
|
||||
)
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package net.eagle0.eagle.library.util.hero
|
||||
|
||||
import net.eagle0.eagle.HeroId
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.hero.Profession.NoProfession
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
object HeroUtils {
|
||||
def power(hero: HeroT): Double = (
|
||||
hero.strength
|
||||
@@ -20,47 +17,4 @@ object HeroUtils {
|
||||
private def professionPower(hero: HeroT): Double =
|
||||
if hero.profession == NoProfession then 0.0
|
||||
else professionPowerValue
|
||||
|
||||
def sortKeys(
|
||||
hero: HeroT,
|
||||
factionLeaderIds: Vector[HeroId],
|
||||
includePower: Boolean = true
|
||||
): Vector[Int] = {
|
||||
val factionHeadScore =
|
||||
if !includePower then 0
|
||||
else if factionLeaderIds.headOption.contains(hero.id) then 2
|
||||
else if factionLeaderIds.contains(hero.id) then 1
|
||||
else 0
|
||||
|
||||
val professionScore =
|
||||
if hero.profession == NoProfession then 0
|
||||
else professionPower(hero).toInt
|
||||
|
||||
val powerScore =
|
||||
if includePower then power(hero).toInt // max ~700
|
||||
else 0
|
||||
|
||||
Vector(
|
||||
factionHeadScore,
|
||||
professionScore,
|
||||
powerScore,
|
||||
hero.id // just to make the sort stable
|
||||
)
|
||||
}
|
||||
|
||||
def sortOrderer(
|
||||
factionLeaderIds: Vector[HeroId]
|
||||
): (HeroT, HeroT) => Boolean = { case (h1, h2) =>
|
||||
@tailrec def go(keys: Vector[(Int, Int)]): Boolean = keys match {
|
||||
case (key1, key2) +: t =>
|
||||
if key1 == key2 then go(t)
|
||||
else key1 > key2
|
||||
case _ => false
|
||||
}
|
||||
|
||||
go(sortKeys(h1, factionLeaderIds).zip(sortKeys(h2, factionLeaderIds)))
|
||||
}
|
||||
|
||||
def sortOrdering(factionLeaderIds: Vector[HeroId]): Ordering[HeroT] =
|
||||
Ordering.fromLessThan(sortOrderer(factionLeaderIds))
|
||||
}
|
||||
|
||||
@@ -69,9 +69,6 @@ object LegacyHeroUtils {
|
||||
go(sortKeys(gameState, hid1).zip(sortKeys(gameState, hid2)))
|
||||
}
|
||||
|
||||
def sortOrdering(gameState: GameState): Ordering[HeroId] =
|
||||
Ordering.fromLessThan(sortOrderer(gameState))
|
||||
|
||||
// Returns a compatibility level from 0 to 100
|
||||
def discordance(a: Hero, b: Hero): Double = {
|
||||
def oneTraitFactor(aTrait: Double, bTrait: Double): Double =
|
||||
|
||||
@@ -9,7 +9,6 @@ import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.model.state.shardok_battle.ShardokBattle
|
||||
import net.eagle0.eagle.{BattalionId, FactionId, HeroId, ProvinceId, RoundId}
|
||||
|
||||
object ActionResultT {
|
||||
@@ -28,7 +27,7 @@ trait ActionResultT {
|
||||
|
||||
// def lastCommandTypeForActingProvince: Option[CommandType]
|
||||
|
||||
def newBattle: Option[ShardokBattle]
|
||||
// def newBattle: Option[ShardokBattleT]
|
||||
// def resolvedBattle: Option[String]
|
||||
// def resolvedImminentBattle: Option[Int]
|
||||
|
||||
@@ -73,7 +72,7 @@ trait ActionResultT {
|
||||
newRoundId: Option[RoundId] = newRoundId,
|
||||
newDate: Option[Date] = newDate,
|
||||
// lastCommandTypeForActingProvince: Option[CommandType] = None,
|
||||
newBattle: Option[ShardokBattle] = newBattle,
|
||||
// newBattle: Option[ShardokBattleT] = None,
|
||||
// resolvedBattle: Option[String] = None,
|
||||
// resolvedImminentBattle: Option[Int] = None,
|
||||
// newChronicleEntry: Option[ChronicleEntry] = None,
|
||||
|
||||
@@ -41,7 +41,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//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/shardok_battle",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.model.state.shardok_battle.ShardokBattle
|
||||
import net.eagle0.eagle.{BattalionId, FactionId, HeroId, ProvinceId, RoundId}
|
||||
|
||||
object ActionResultC {
|
||||
@@ -33,7 +32,6 @@ case class ActionResultC(
|
||||
newRoundPhase: Option[RoundPhase] = None,
|
||||
newRoundId: Option[RoundId] = None,
|
||||
newDate: Option[Date] = None,
|
||||
newBattle: Option[ShardokBattle] = None,
|
||||
changedBattalions: Vector[ChangedBattalionT] = Vector.empty,
|
||||
changedFactions: Vector[ChangedFactionT] = Vector.empty,
|
||||
changedHeroes: Vector[ChangedHeroT] = Vector.empty,
|
||||
@@ -67,7 +65,6 @@ case class ActionResultC(
|
||||
newRoundPhase: Option[RoundPhase] = newRoundPhase,
|
||||
newRoundId: Option[RoundId] = newRoundId,
|
||||
newDate: Option[Date] = newDate,
|
||||
newBattle: Option[ShardokBattle] = newBattle,
|
||||
changedBattalions: Vector[ChangedBattalionT] = changedBattalions,
|
||||
changedFactions: Vector[ChangedFactionT] = changedFactions,
|
||||
changedHeroes: Vector[ChangedHeroT] = changedHeroes,
|
||||
@@ -99,7 +96,6 @@ case class ActionResultC(
|
||||
newRoundPhase = newRoundPhase,
|
||||
newRoundId = newRoundId,
|
||||
newDate = newDate,
|
||||
newBattle = newBattle,
|
||||
changedBattalions = changedBattalions,
|
||||
changedFactions = changedFactions,
|
||||
changedHeroes = changedHeroes,
|
||||
|
||||
@@ -33,7 +33,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//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/shardok_battle",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+1
-4
@@ -29,6 +29,7 @@ import net.eagle0.eagle.model.action_result.{
|
||||
ClientTextVisibilityExtensionT,
|
||||
NotificationT
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.battalion.BattalionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.{
|
||||
@@ -36,14 +37,12 @@ import net.eagle0.eagle.model.proto_converters.hero.{
|
||||
HeroConverter
|
||||
}
|
||||
import net.eagle0.eagle.model.proto_converters.generated_text_request.LlmRequestConverter
|
||||
import net.eagle0.eagle.model.proto_converters.shardok_battle.ShardokBattleConverter
|
||||
import net.eagle0.eagle.model.state.RoundPhase
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.model.state.shardok_battle.ShardokBattle
|
||||
import net.eagle0.eagle.{BattalionId, FactionId, HeroId, ProvinceId, RoundId}
|
||||
|
||||
object ActionResultProtoConverter {
|
||||
@@ -58,7 +57,6 @@ object ActionResultProtoConverter {
|
||||
newRoundPhase: Option[RoundPhase],
|
||||
newRoundId: Option[RoundId],
|
||||
newDate: Option[Date],
|
||||
newBattle: Option[ShardokBattle],
|
||||
changedBattalions: Vector[ChangedBattalionT],
|
||||
changedFactions: Vector[ChangedFactionT],
|
||||
changedHeroes: Vector[ChangedHeroT],
|
||||
@@ -92,7 +90,6 @@ object ActionResultProtoConverter {
|
||||
),
|
||||
newRoundId = newRoundId,
|
||||
newDate = newDate.map(DateConverter.toProto),
|
||||
newBattle = newBattle.map(ShardokBattleConverter.toProto),
|
||||
changedBattalions = changedBattalions.map {
|
||||
case ChangedBattalionC(to) => BattalionConverter.toProto(to)
|
||||
},
|
||||
|
||||
@@ -12,7 +12,6 @@ scala_library(
|
||||
"//src/test/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
":battalion_converter",
|
||||
":changed_faction_converter",
|
||||
":changed_hero_converter",
|
||||
":changed_province_converter",
|
||||
@@ -30,18 +29,17 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:changed_hero_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:client_text_visibility_extension_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types/base:action_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:gender",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/llm_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/shardok_battle:shardok_battle_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:round_phase",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/shardok_battle",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -69,29 +67,6 @@ scala_library(
|
||||
],
|
||||
)
|
||||
|
||||
scala_library(
|
||||
name = "battalion_converter",
|
||||
srcs = ["BattalionConverter.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
":battalion_type_id_converter",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:battalion_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:proto_conversion_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
],
|
||||
)
|
||||
|
||||
scala_library(
|
||||
name = "battalion_type_id_converter",
|
||||
srcs = ["BattalionTypeIdConverter.scala"],
|
||||
@@ -99,9 +74,8 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/view_filters:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/view/battalion:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:battalion_type_scala_proto",
|
||||
@@ -115,10 +89,8 @@ scala_library(
|
||||
name = "battalion_type_converter",
|
||||
srcs = ["BattalionTypeConverter.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
@@ -189,7 +161,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:event_for_hero_backstory",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:event_for_hero_backstory_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
load("@rules_scala//scala:scala.bzl", "scala_library")
|
||||
|
||||
scala_library(
|
||||
name = "battalion",
|
||||
srcs = ["BattalionConverter.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:battalion_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_type_id_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:proto_conversion_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
],
|
||||
)
|
||||
+5
-2
@@ -1,9 +1,12 @@
|
||||
package net.eagle0.eagle.model.proto_converters
|
||||
package net.eagle0.eagle.model.proto_converters.battalion
|
||||
|
||||
import net.eagle0.eagle.BattalionId
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionTypeId as BattalionTypeIdProto
|
||||
import net.eagle0.eagle.internal.battalion.Battalion as BattalionProto
|
||||
import net.eagle0.eagle.model.proto_converters.ProtoConversionException
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
BattalionTypeIdConverter,
|
||||
ProtoConversionException
|
||||
}
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
load("@rules_scala//scala:scala.bzl", "scala_library")
|
||||
|
||||
scala_library(
|
||||
name = "battalion_status",
|
||||
srcs = ["BattalionStatusConverter.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion_status",
|
||||
],
|
||||
)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package net.eagle0.eagle.model.proto_converters.battalion_status
|
||||
|
||||
import net.eagle0.common.common_unit.UnitStatus
|
||||
import net.eagle0.eagle.model.state.battalion_status.BattalionStatus
|
||||
|
||||
object BattalionStatusConverter {
|
||||
def fromProto(proto: UnitStatus): BattalionStatus = proto match {
|
||||
case UnitStatus.NORMAL_UNIT => BattalionStatus.Normal
|
||||
case UnitStatus.CAPTURED_UNIT => BattalionStatus.Captured
|
||||
case UnitStatus.FLED_UNIT => BattalionStatus.Fled
|
||||
case UnitStatus.RETREATED_UNIT => BattalionStatus.Retreated
|
||||
case UnitStatus.NEVER_ENTERED_UNIT => BattalionStatus.NeverEntered
|
||||
case UnitStatus.OUTLAWED_UNIT => BattalionStatus.Outlawed
|
||||
case UnitStatus.UNKNOWN_UNIT => BattalionStatus.Normal
|
||||
case UnitStatus.Unrecognized(value) =>
|
||||
throw new IllegalArgumentException(s"Unknown UnitStatus: $value")
|
||||
}
|
||||
|
||||
def toProto(model: BattalionStatus): UnitStatus = model match {
|
||||
case BattalionStatus.Normal => UnitStatus.NORMAL_UNIT
|
||||
case BattalionStatus.Captured => UnitStatus.CAPTURED_UNIT
|
||||
case BattalionStatus.Fled => UnitStatus.FLED_UNIT
|
||||
case BattalionStatus.Retreated => UnitStatus.RETREATED_UNIT
|
||||
case BattalionStatus.NeverEntered => UnitStatus.NEVER_ENTERED_UNIT
|
||||
case BattalionStatus.Outlawed => UnitStatus.OUTLAWED_UNIT
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
],
|
||||
exports = [
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
|
||||
@@ -14,11 +14,11 @@ scala_library(
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:notification_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_type_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:notification_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:proto_conversion_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:round_phase_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/chronicle_entry:chronicle_entry_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
|
||||
+1
-1
@@ -13,7 +13,6 @@ import net.eagle0.eagle.internal.run_status.RunStatus as RunStatusProto
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle as ShardokBattleProto
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionType as BattalionTypeProto
|
||||
import net.eagle0.eagle.model.proto_converters.{
|
||||
BattalionConverter,
|
||||
BattalionTypeConverter,
|
||||
NotificationConverter,
|
||||
ProtoConversionException,
|
||||
@@ -27,6 +26,7 @@ import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.proto_converters.run_status.RunStatusConverter
|
||||
import net.eagle0.eagle.model.proto_converters.shardok_battle.ShardokBattleConverter
|
||||
import net.eagle0.eagle.model.action_result.NotificationT
|
||||
import net.eagle0.eagle.model.proto_converters.battalion.BattalionConverter
|
||||
import net.eagle0.eagle.model.state.BattalionType
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.chronicle_entry.ChronicleEntry
|
||||
|
||||
@@ -23,7 +23,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:event_for_hero_backstory_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/quest",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/quest/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/model/view/battalion",
|
||||
],
|
||||
)
|
||||
@@ -56,8 +55,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
],
|
||||
exports = [
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
@@ -74,7 +71,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/backstory_version",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+18
-18
@@ -2,7 +2,6 @@ package net.eagle0.eagle.model.proto_converters.hero
|
||||
|
||||
import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus
|
||||
import net.eagle0.eagle.common.unaffiliated_hero_quest.Quest as QuestProto
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.internal.event_for_hero_backstory.FoughtInBattleBackstoryEvent.UnitStatus.{
|
||||
UNIT_STATUS_CAPTURED,
|
||||
UNIT_STATUS_FLED,
|
||||
@@ -190,7 +189,7 @@ object EventForHeroBackstoryConverter {
|
||||
isVictorious: Boolean,
|
||||
isAttacker: Boolean,
|
||||
enemyFactionIds: Vector[FactionId],
|
||||
status: UnitStatus
|
||||
status: FoughtInBattleBackstoryEvent.BattalionStatus
|
||||
) =>
|
||||
FoughtInBattleBackstoryEventProto(
|
||||
provinceId = provinceId,
|
||||
@@ -349,14 +348,14 @@ object EventForHeroBackstoryConverter {
|
||||
)
|
||||
|
||||
def toProto(
|
||||
unitStatus: UnitStatus
|
||||
): UnitStatusProto = unitStatus match {
|
||||
case UnitStatus.Normal => UNIT_STATUS_NORMAL
|
||||
case UnitStatus.Captured => UNIT_STATUS_CAPTURED
|
||||
case UnitStatus.Fled => UNIT_STATUS_FLED
|
||||
case UnitStatus.Retreated => UNIT_STATUS_RETREATED
|
||||
case UnitStatus.NeverEntered => UNIT_STATUS_NEVER_ENTERED
|
||||
case UnitStatus.Outlawed => UNIT_STATUS_OUTLAWED
|
||||
battalionStatus: FoughtInBattleBackstoryEvent.BattalionStatus
|
||||
): UnitStatusProto = battalionStatus match {
|
||||
case FoughtInBattleBackstoryEvent.Normal => UNIT_STATUS_NORMAL
|
||||
case FoughtInBattleBackstoryEvent.Captured => UNIT_STATUS_CAPTURED
|
||||
case FoughtInBattleBackstoryEvent.Fled => UNIT_STATUS_FLED
|
||||
case FoughtInBattleBackstoryEvent.Retreated => UNIT_STATUS_RETREATED
|
||||
case FoughtInBattleBackstoryEvent.NeverEntered => UNIT_STATUS_NEVER_ENTERED
|
||||
case FoughtInBattleBackstoryEvent.Outlawed => UNIT_STATUS_OUTLAWED
|
||||
}
|
||||
|
||||
def fromProto(
|
||||
@@ -669,15 +668,16 @@ object EventForHeroBackstoryConverter {
|
||||
|
||||
def fromProto(
|
||||
statusProto: UnitStatusProto
|
||||
): UnitStatus = {
|
||||
): FoughtInBattleBackstoryEvent.BattalionStatus = {
|
||||
statusProto match {
|
||||
case UNIT_STATUS_NORMAL => UnitStatus.Normal
|
||||
case UNIT_STATUS_CAPTURED => UnitStatus.Captured
|
||||
case UNIT_STATUS_FLED => UnitStatus.Fled
|
||||
case UNIT_STATUS_RETREATED => UnitStatus.Retreated
|
||||
case UNIT_STATUS_NEVER_ENTERED => UnitStatus.NeverEntered
|
||||
case UNIT_STATUS_OUTLAWED => UnitStatus.Outlawed
|
||||
case UNIT_STATUS_UNKNOWN =>
|
||||
case UNIT_STATUS_NORMAL => FoughtInBattleBackstoryEvent.Normal
|
||||
case UNIT_STATUS_CAPTURED => FoughtInBattleBackstoryEvent.Captured
|
||||
case UNIT_STATUS_FLED => FoughtInBattleBackstoryEvent.Fled
|
||||
case UNIT_STATUS_RETREATED => FoughtInBattleBackstoryEvent.Retreated
|
||||
case UNIT_STATUS_NEVER_ENTERED =>
|
||||
FoughtInBattleBackstoryEvent.NeverEntered
|
||||
case UNIT_STATUS_OUTLAWED => FoughtInBattleBackstoryEvent.Outlawed
|
||||
case UNIT_STATUS_UNKNOWN =>
|
||||
throw new ProtoConversionException(
|
||||
s"Unknown UnitStatusProto"
|
||||
)
|
||||
|
||||
@@ -12,7 +12,8 @@ object GenderConverter {
|
||||
}
|
||||
|
||||
def fromProto(genderProto: GenderProto): Gender = genderProto match {
|
||||
case GenderProto.GENDER_UNKNOWN => Gender.Other
|
||||
case GenderProto.GENDER_UNKNOWN =>
|
||||
throw new ProtoConversionException("unknown gender proto")
|
||||
case GenderProto.GENDER_FEMALE => Gender.Female
|
||||
case GenderProto.GENDER_MALE => Gender.Male
|
||||
case GenderProto.GENDER_OTHER => Gender.Other
|
||||
|
||||
@@ -27,10 +27,8 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/diplomacy_offer/status",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:event_for_hero_backstory",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/llm_request/chronicle_event",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/backstory_version",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/quest",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -9,7 +9,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
],
|
||||
exports = [
|
||||
|
||||
@@ -3,12 +3,7 @@ load("@rules_scala//scala:scala.bzl", "scala_library")
|
||||
scala_library(
|
||||
name = "shardok_battle_converter",
|
||||
srcs = ["ShardokBattleConverter.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl/action:__pkg__",
|
||||
],
|
||||
visibility = ["//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/common:victory_condition_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:shardok_battle_scala_proto",
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
load("@rules_scala//scala:scala.bzl", "scala_library")
|
||||
|
||||
scala_library(
|
||||
name = "unit_status_converter",
|
||||
srcs = ["UnitStatusConverter.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl/action:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
],
|
||||
)
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package net.eagle0.eagle.model.proto_converters.unit_status
|
||||
|
||||
import net.eagle0.common.common_unit.UnitStatus as UnitStatusProto
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
|
||||
object UnitStatusConverter {
|
||||
def fromProto(unitStatus: UnitStatusProto): UnitStatus = unitStatus match {
|
||||
case UnitStatusProto.NORMAL_UNIT => UnitStatus.Normal
|
||||
case UnitStatusProto.CAPTURED_UNIT => UnitStatus.Captured
|
||||
case UnitStatusProto.FLED_UNIT => UnitStatus.Fled
|
||||
case UnitStatusProto.RETREATED_UNIT => UnitStatus.Retreated
|
||||
case UnitStatusProto.NEVER_ENTERED_UNIT => UnitStatus.NeverEntered
|
||||
case UnitStatusProto.OUTLAWED_UNIT => UnitStatus.Outlawed
|
||||
case UnitStatusProto.Unrecognized(value) =>
|
||||
throw new IllegalArgumentException(s"Unknown UnitStatus: $value")
|
||||
}
|
||||
|
||||
def toProto(unitStatus: UnitStatus): UnitStatusProto =
|
||||
unitStatus match {
|
||||
case UnitStatus.Normal => UnitStatusProto.NORMAL_UNIT
|
||||
case UnitStatus.Captured => UnitStatusProto.CAPTURED_UNIT
|
||||
case UnitStatus.Fled => UnitStatusProto.FLED_UNIT
|
||||
case UnitStatus.Retreated => UnitStatusProto.RETREATED_UNIT
|
||||
case UnitStatus.NeverEntered => UnitStatusProto.NEVER_ENTERED_UNIT
|
||||
case UnitStatus.Outlawed => UnitStatusProto.OUTLAWED_UNIT
|
||||
}
|
||||
}
|
||||
@@ -2,22 +2,15 @@ load("@rules_scala//scala:scala.bzl", "scala_library")
|
||||
|
||||
scala_library(
|
||||
name = "battalion",
|
||||
srcs = [
|
||||
"BattalionT.scala",
|
||||
],
|
||||
srcs = ["BattalionT.scala"],
|
||||
visibility = [
|
||||
":__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/battalion:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/llm_request:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/resolved_eagle_unit:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util:__subpackages__",
|
||||
],
|
||||
|
||||
@@ -5,8 +5,7 @@ scala_library(
|
||||
srcs = ["BattalionC.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/battalion:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util:__subpackages__",
|
||||
],
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
load("@rules_scala//scala:scala.bzl", "scala_library")
|
||||
|
||||
scala_library(
|
||||
name = "battalion_status",
|
||||
srcs = ["BattalionStatus.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/resolved_eagle_unit:__pkg__",
|
||||
],
|
||||
)
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package net.eagle0.eagle.model.state.unit_status
|
||||
package net.eagle0.eagle.model.state.battalion_status
|
||||
|
||||
enum UnitStatus {
|
||||
enum BattalionStatus {
|
||||
case Normal
|
||||
case Captured
|
||||
case Fled
|
||||
@@ -18,7 +18,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/diplomacy_offer/status",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/quest",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/model/view/battalion",
|
||||
],
|
||||
)
|
||||
@@ -33,8 +32,8 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/resolved_eagle_unit:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/main/scala/net/eagle0/util/hero_generation:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
|
||||
@@ -48,7 +47,6 @@ scala_library(
|
||||
visibility = [
|
||||
":__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util:__subpackages__",
|
||||
],
|
||||
@@ -75,7 +73,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/main/scala/net/eagle0/util/hero_generation:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions:__subpackages__",
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package net.eagle0.eagle.model.state.hero
|
||||
|
||||
import net.eagle0.eagle.{FactionId, HeroId, ProvinceId}
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.Status
|
||||
import net.eagle0.eagle.model.state.quest.QuestT
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.model.view.battalion.BattalionView
|
||||
|
||||
sealed trait EventForHeroBackstoryT { def date: Date }
|
||||
@@ -73,6 +71,16 @@ case class ExiledBackstoryEvent(
|
||||
exiledFromProvinceId: ProvinceId
|
||||
) extends EventForHeroBackstoryT
|
||||
|
||||
object FoughtInBattleBackstoryEvent {
|
||||
sealed trait BattalionStatus
|
||||
case object Normal extends BattalionStatus
|
||||
case object Captured extends BattalionStatus
|
||||
case object Fled extends BattalionStatus
|
||||
case object Retreated extends BattalionStatus
|
||||
case object NeverEntered extends BattalionStatus
|
||||
case object Outlawed extends BattalionStatus
|
||||
}
|
||||
|
||||
case class FoughtInBattleBackstoryEvent(
|
||||
date: Date,
|
||||
provinceId: ProvinceId,
|
||||
@@ -80,7 +88,7 @@ case class FoughtInBattleBackstoryEvent(
|
||||
isVictorious: Boolean,
|
||||
isAttacker: Boolean,
|
||||
enemyFactionIds: Vector[FactionId],
|
||||
status: UnitStatus
|
||||
status: FoughtInBattleBackstoryEvent.BattalionStatus
|
||||
) extends EventForHeroBackstoryT
|
||||
|
||||
case class HeroDepartedBackstoryEvent(
|
||||
|
||||
@@ -8,7 +8,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/service/new_game_creation:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util:__subpackages__",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
load("@rules_scala//scala:scala.bzl", "scala_library")
|
||||
|
||||
scala_library(
|
||||
name = "resolved_eagle_unit",
|
||||
srcs = ["ResolvedEagleUnit.scala"],
|
||||
visibility = [
|
||||
":__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion_status",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:gender",
|
||||
],
|
||||
)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package net.eagle0.eagle.model.state.resolved_eagle_unit
|
||||
|
||||
import net.eagle0.eagle.BattalionId
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.battalion_status.BattalionStatus
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
|
||||
case class ResolvedEagleUnit(
|
||||
hero: HeroT,
|
||||
battalion: BattalionT,
|
||||
status: BattalionStatus
|
||||
) {
|
||||
def optionalBattalionId: Option[BattalionId] =
|
||||
if battalion.id == 0 then None else Some(battalion.id)
|
||||
}
|
||||
@@ -4,16 +4,9 @@ scala_library(
|
||||
name = "shardok_battle",
|
||||
srcs = ["ShardokBattle.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/concrete:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/game_state:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/shardok_battle:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl/action:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
load("@rules_scala//scala:scala.bzl", "scala_library")
|
||||
|
||||
scala_library(
|
||||
name = "unit_status",
|
||||
srcs = ["UnitStatus.scala"],
|
||||
visibility = [
|
||||
":__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle:__subpackages__",
|
||||
],
|
||||
deps = [],
|
||||
)
|
||||
@@ -10,8 +10,8 @@ scala_library(
|
||||
],
|
||||
deps = [
|
||||
":resolved_shardok_player",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:shardok_battle_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/shardok_battle",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -92,13 +92,11 @@ scala_library(
|
||||
],
|
||||
deps = [
|
||||
":eagle_unit",
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:battalion_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//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/hero:gender",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero:profession",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -155,13 +153,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:vigor_to_constitution_xp_multiplier",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:legacy_hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package net.eagle0.eagle.shardok_interface
|
||||
|
||||
import net.eagle0.eagle.model.state.shardok_battle.ShardokBattle
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle
|
||||
|
||||
case class BattleResolution(
|
||||
battle: ShardokBattle,
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
package net.eagle0.eagle.shardok_interface
|
||||
|
||||
import net.eagle0.common.common_unit.UnitStatus
|
||||
import net.eagle0.eagle.BattalionId
|
||||
import net.eagle0.eagle.model.state.battalion.BattalionT
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.model.state.hero.HeroT
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.internal.battalion.Battalion as EagleBattalion
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero as EagleHero
|
||||
|
||||
object ResolvedEagleUnit {
|
||||
def apply(eagleUnit: CombatUnit, gs: GameState): ResolvedEagleUnit =
|
||||
ResolvedEagleUnit(
|
||||
hero = gs.heroes(eagleUnit.heroId),
|
||||
battalion = eagleUnit.battalionId
|
||||
.map(gs.battalions)
|
||||
.getOrElse(EagleUnit.defaultBattalion),
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
)
|
||||
}
|
||||
|
||||
case class ResolvedEagleUnit(
|
||||
hero: HeroT,
|
||||
battalion: Option[BattalionT],
|
||||
hero: EagleHero,
|
||||
battalion: EagleBattalion,
|
||||
status: UnitStatus
|
||||
) {
|
||||
def hasDefaultBattalion: Boolean =
|
||||
battalion.id == EagleUnit.defaultBattalionId
|
||||
|
||||
def optionalBattalionId: Option[BattalionId] =
|
||||
battalion.map(_.id)
|
||||
if hasDefaultBattalion then None else Some(battalion.id)
|
||||
|
||||
def validate(gameState: GameState): Unit = {
|
||||
battalion.foreach { b =>
|
||||
require(
|
||||
gameState.battalions.contains(b.id),
|
||||
s"Unknown battalion ID in $b"
|
||||
)
|
||||
}
|
||||
require(
|
||||
gameState.battalions.contains(battalion.id) || hasDefaultBattalion,
|
||||
s"Unknown battalion ID in $battalion"
|
||||
)
|
||||
|
||||
require(
|
||||
gameState.heroes.contains(hero.id),
|
||||
|
||||
@@ -19,9 +19,6 @@ import net.eagle0.eagle.internal.shardok_battle.{ShardokBattle, ShardokPlayer}
|
||||
import net.eagle0.eagle.library.settings.VigorToConstitutionXpMultiplier
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.hero.LegacyHeroUtils
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.{FactionId, GameId, ShardokGameId}
|
||||
import net.eagle0.shardok.api.placement_command.PlacementCommand
|
||||
import net.eagle0.shardok.common.hex_map.HexMap
|
||||
@@ -73,21 +70,13 @@ class ShardokInterfaceGrpcClient(
|
||||
ResolvedShardokPlayer(
|
||||
eagleFid = ui.eagleFactionId,
|
||||
endGameCondition = ui.endGameCondition.get,
|
||||
units = ui.units.map(ru => {
|
||||
val commonUnit = ru.unit.get
|
||||
val eagleUnit = fromCommon(commonUnit)
|
||||
units = ui.units.map(ru =>
|
||||
ResolvedEagleUnit(
|
||||
HeroConverter.fromProto(eagleUnit.hero),
|
||||
if commonUnit.battalion.get.eagleBattalionId == EagleUnit.defaultBattalionId
|
||||
then None
|
||||
else
|
||||
Some(
|
||||
BattalionConverter.fromProto(eagleUnit.battalion)
|
||||
)
|
||||
,
|
||||
convertUnitStatus(ru.status)
|
||||
ru.unit.map(fromCommon).get.hero,
|
||||
ru.unit.map(fromCommon).get.battalion,
|
||||
ru.status
|
||||
)
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -392,23 +381,4 @@ class ShardokInterfaceGrpcClient(
|
||||
armament = commonUnit.battalion.get.armament
|
||||
)
|
||||
)
|
||||
|
||||
private def convertUnitStatus(
|
||||
protoStatus: net.eagle0.common.common_unit.UnitStatus
|
||||
): UnitStatus =
|
||||
protoStatus match {
|
||||
case net.eagle0.common.common_unit.UnitStatus.NORMAL_UNIT =>
|
||||
UnitStatus.Normal
|
||||
case net.eagle0.common.common_unit.UnitStatus.CAPTURED_UNIT =>
|
||||
UnitStatus.Captured
|
||||
case net.eagle0.common.common_unit.UnitStatus.FLED_UNIT => UnitStatus.Fled
|
||||
case net.eagle0.common.common_unit.UnitStatus.RETREATED_UNIT =>
|
||||
UnitStatus.Retreated
|
||||
case net.eagle0.common.common_unit.UnitStatus.NEVER_ENTERED_UNIT =>
|
||||
UnitStatus.NeverEntered
|
||||
case net.eagle0.common.common_unit.UnitStatus.OUTLAWED_UNIT =>
|
||||
UnitStatus.Outlawed
|
||||
case other =>
|
||||
throw new IllegalArgumentException(s"Unrecognized UnitStatus: $other")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package net.eagle0.eagle.ai
|
||||
|
||||
import net.eagle0.common.SeededRandom
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableDestinationProvince,
|
||||
MarchAvailableCommand,
|
||||
MarchCommandFromOneProvince
|
||||
}
|
||||
@@ -80,12 +79,7 @@ class FixLeaderAloneCommandSelectorTest
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 15,
|
||||
availableHeroIds = Vector(151),
|
||||
availableDestinationProvinces = Vector(14, 16).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
)
|
||||
availableDestinationProvinces = Vector(14, 16)
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -95,12 +89,7 @@ class FixLeaderAloneCommandSelectorTest
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 16,
|
||||
availableHeroIds = Vector(161),
|
||||
availableDestinationProvinces = Vector(15, 17).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
)
|
||||
availableDestinationProvinces = Vector(15, 17)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -139,12 +128,7 @@ class FixLeaderAloneCommandSelectorTest
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 16,
|
||||
availableHeroIds = Vector(161),
|
||||
availableDestinationProvinces = Vector(15, 17).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
)
|
||||
availableDestinationProvinces = Vector(15, 17)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -2,7 +2,6 @@ package net.eagle0.eagle.ai
|
||||
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableCommand,
|
||||
AvailableDestinationProvince,
|
||||
ImproveAvailableCommand,
|
||||
MarchAvailableCommand,
|
||||
MarchCommandFromOneProvince,
|
||||
@@ -130,22 +129,12 @@ class MidGameAIClientTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 2,
|
||||
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(1, 3, 4),
|
||||
availableHeroIds = Vector(87)
|
||||
),
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 3,
|
||||
availableDestinationProvinces = Vector(1, 2).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(1, 2),
|
||||
availableHeroIds = Vector(11, 12, 13, 14),
|
||||
goldAvailable = 6000,
|
||||
foodAvailable = 7000
|
||||
@@ -204,22 +193,12 @@ class MidGameAIClientTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 2,
|
||||
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(1, 3, 4),
|
||||
availableHeroIds = Vector(87)
|
||||
),
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 3,
|
||||
availableDestinationProvinces = Vector(1, 2).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(1, 2),
|
||||
availableHeroIds = Vector(11, 12, 13, 14),
|
||||
goldAvailable = 6000,
|
||||
foodAvailable = 7000
|
||||
@@ -262,32 +241,17 @@ class MidGameAIClientTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 1,
|
||||
availableDestinationProvinces = Vector(2, 3, 4).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(2, 3, 4),
|
||||
availableHeroIds = Vector(123, 124, 125, 126, 127, 128)
|
||||
),
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 2,
|
||||
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(1, 3, 4),
|
||||
availableHeroIds = Vector(87)
|
||||
),
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 3,
|
||||
availableDestinationProvinces = Vector(1, 2).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(1, 2),
|
||||
availableHeroIds = Vector(11, 12, 13, 14),
|
||||
goldAvailable = 6000,
|
||||
foodAvailable = 7000
|
||||
@@ -904,12 +868,7 @@ class MidGameAIClientTest
|
||||
oneProvinceCommands = Vector[MarchCommandFromOneProvince](
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 7,
|
||||
availableDestinationProvinces = Vector(12, 15).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(12, 15),
|
||||
availableHeroIds = Vector(10, 20, 30, 40, 50)
|
||||
)
|
||||
),
|
||||
|
||||
+6
-37
@@ -1,7 +1,6 @@
|
||||
package net.eagle0.eagle.ai
|
||||
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableDestinationProvince,
|
||||
MarchAvailableCommand,
|
||||
MarchCommandFromOneProvince
|
||||
}
|
||||
@@ -92,22 +91,12 @@ class MoveLeaderToBetterProvinceCommandChooserTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 2,
|
||||
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(1, 3, 4),
|
||||
availableHeroIds = Vector(87)
|
||||
),
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 1,
|
||||
availableDestinationProvinces = Vector(2, 3).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(2, 3),
|
||||
availableHeroIds = Vector(leaderId),
|
||||
goldAvailable = 6000,
|
||||
foodAvailable = 7000
|
||||
@@ -164,24 +153,14 @@ class MoveLeaderToBetterProvinceCommandChooserTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 1,
|
||||
availableDestinationProvinces = Vector(2, 3).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(2, 3),
|
||||
availableHeroIds = Vector(leaderId),
|
||||
goldAvailable = 6000,
|
||||
foodAvailable = 7000
|
||||
),
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 2,
|
||||
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(1, 3, 4),
|
||||
availableHeroIds = Vector(123, 58, 59, 70),
|
||||
goldAvailable = 6000,
|
||||
foodAvailable = 7000
|
||||
@@ -236,22 +215,12 @@ class MoveLeaderToBetterProvinceCommandChooserTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 2,
|
||||
availableDestinationProvinces = Vector(1, 3, 4).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(1, 3, 4),
|
||||
availableHeroIds = Vector(87)
|
||||
),
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 1,
|
||||
availableDestinationProvinces = Vector(2, 3).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(2, 3),
|
||||
availableHeroIds = Vector(leaderId),
|
||||
goldAvailable = 6000,
|
||||
foodAvailable = 7000
|
||||
|
||||
@@ -3,7 +3,6 @@ package net.eagle0.eagle.ai
|
||||
import net.eagle0.eagle.api.available_command.SwearBrotherhoodAvailableCommand.HeroAndBackstory
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableCommand,
|
||||
AvailableDestinationProvince,
|
||||
EligibleGift,
|
||||
FeastAvailableCommand,
|
||||
HeroGiftAvailableCommand,
|
||||
@@ -300,12 +299,7 @@ class SeekMoreLeadersCommandChooserTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 20,
|
||||
availableDestinationProvinces = Vector(17, 909).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(17, 909),
|
||||
availableHeroIds = Vector(12, 15, 16)
|
||||
)
|
||||
),
|
||||
|
||||
+35
-373
@@ -16,7 +16,6 @@ import net.eagle0.eagle.library.settings.{
|
||||
MaxGoldTribute,
|
||||
MinVigorForMarch
|
||||
}
|
||||
import net.eagle0.eagle.library.util.ShardokMapInfo
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
@@ -42,11 +41,7 @@ class AvailableMarchCommandFactoryTest
|
||||
|
||||
private val actingProvince = Province(
|
||||
id = 7,
|
||||
neighbors = Vector(
|
||||
Neighbor(provinceId = 4, startingPositionIndex = 0),
|
||||
Neighbor(provinceId = 8, startingPositionIndex = 1),
|
||||
Neighbor(provinceId = 12, startingPositionIndex = 2)
|
||||
),
|
||||
neighbors = Vector(4, 8, 12).map(pid => Neighbor(provinceId = pid)),
|
||||
rulingFactionId = Some(actingPlayerId),
|
||||
rulingFactionHeroIds = sufficientVigorHeroes
|
||||
.map(_.id) ++ insufficientVigorHeroes.map(_.id),
|
||||
@@ -71,24 +66,9 @@ class AvailableMarchCommandFactoryTest
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
7 -> actingProvince,
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = Some(6),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = Some(6)),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes ++ insufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -101,8 +81,7 @@ class AvailableMarchCommandFactoryTest
|
||||
7
|
||||
)
|
||||
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces
|
||||
.map(_.provinceId) should contain allElementsOf Vector(
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces should contain allElementsOf Vector(
|
||||
8,
|
||||
12
|
||||
)
|
||||
@@ -112,24 +91,9 @@ class AvailableMarchCommandFactoryTest
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
7 -> actingProvince,
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = Some(6),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = Some(6)),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes ++ insufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -142,8 +106,7 @@ class AvailableMarchCommandFactoryTest
|
||||
7
|
||||
)
|
||||
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces
|
||||
.map(_.provinceId) should contain(
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces should contain(
|
||||
4
|
||||
)
|
||||
}
|
||||
@@ -152,24 +115,9 @@ class AvailableMarchCommandFactoryTest
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
7 -> actingProvince,
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = None,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = None),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes ++ insufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -182,8 +130,7 @@ class AvailableMarchCommandFactoryTest
|
||||
7
|
||||
)
|
||||
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces
|
||||
.map(_.provinceId) should contain(
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces should contain(
|
||||
8
|
||||
)
|
||||
}
|
||||
@@ -195,22 +142,10 @@ class AvailableMarchCommandFactoryTest
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
activeEvents = Vector(BlizzardEvent()),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
activeEvents = Vector(BlizzardEvent())
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = None,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
8 -> Province(id = 8, rulingFactionId = None),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes ++ insufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -223,32 +158,16 @@ class AvailableMarchCommandFactoryTest
|
||||
7
|
||||
)
|
||||
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces
|
||||
.map(_.provinceId) should not contain 4
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces should not contain 4
|
||||
}
|
||||
|
||||
it should "include heroes with sufficient vigor" in {
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
7 -> actingProvince,
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = None,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = None),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes ++ insufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -269,24 +188,9 @@ class AvailableMarchCommandFactoryTest
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
7 -> actingProvince,
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = None,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = None),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes ++ insufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -307,24 +211,9 @@ class AvailableMarchCommandFactoryTest
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
7 -> actingProvince,
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = None,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = None),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes ++ insufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -349,24 +238,9 @@ class AvailableMarchCommandFactoryTest
|
||||
rulingFactionHeroIds = actingProvince.rulingFactionHeroIds
|
||||
.filterNot(id => sufficientVigorHeroes.map(_.id).contains(id))
|
||||
),
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = None,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = None),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(insufficientVigorHeroes ++ sufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -387,24 +261,9 @@ class AvailableMarchCommandFactoryTest
|
||||
7 -> actingProvince.withActiveEvents(
|
||||
Vector(BlizzardEvent())
|
||||
),
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = None,
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = None),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes ++ insufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -424,24 +283,9 @@ class AvailableMarchCommandFactoryTest
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
7 -> actingProvince,
|
||||
4 -> Province(
|
||||
id = 4,
|
||||
rulingFactionId = Some(5),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 0))
|
||||
),
|
||||
8 -> Province(
|
||||
id = 8,
|
||||
rulingFactionId = Some(6),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 1))
|
||||
),
|
||||
12 -> Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(9),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 7, startingPositionIndex = 2))
|
||||
)
|
||||
4 -> Province(id = 4, rulingFactionId = Some(5)),
|
||||
8 -> Province(id = 8, rulingFactionId = Some(6)),
|
||||
12 -> Province(id = 12, rulingFactionId = Some(9))
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes ++ insufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
@@ -456,186 +300,4 @@ class AvailableMarchCommandFactoryTest
|
||||
|
||||
availableCommand.get.oneProvinceCommands.head.goldAvailable shouldBe actingProvince.gold
|
||||
}
|
||||
|
||||
// Helper method to create a destination province with proper neighbor back-reference
|
||||
private def createDestinationProvince(
|
||||
provinceId: Int,
|
||||
backReferencePositionIndex: Int,
|
||||
rulingFactionId: Option[Int] = None
|
||||
): Province = {
|
||||
Province(
|
||||
id = provinceId,
|
||||
neighbors = Vector(
|
||||
Neighbor(
|
||||
provinceId = 7,
|
||||
startingPositionIndex = backReferencePositionIndex
|
||||
)
|
||||
),
|
||||
rulingFactionId = rulingFactionId
|
||||
)
|
||||
}
|
||||
|
||||
"availableCommand" should "set requiresRiverCrossing correctly based on map info" in {
|
||||
// We need to use real province IDs that exist in the ShardokMapInfo
|
||||
// Let's use province 1 as the acting province and 37 as destination
|
||||
val actingProvinceId = 1
|
||||
val destinationProvinceId = 37
|
||||
|
||||
val testActingProvince = Province(
|
||||
id = actingProvinceId,
|
||||
neighbors = Vector(
|
||||
Neighbor(provinceId = destinationProvinceId, startingPositionIndex = 0)
|
||||
),
|
||||
rulingFactionId = Some(actingPlayerId),
|
||||
rulingFactionHeroIds = sufficientVigorHeroes.map(_.id),
|
||||
battalionIds = validBattalions.map(_.id),
|
||||
gold = 3232,
|
||||
food = 6263
|
||||
)
|
||||
|
||||
val testDestinationProvince = Province(
|
||||
id = destinationProvinceId,
|
||||
neighbors = Vector(
|
||||
Neighbor(provinceId = actingProvinceId, startingPositionIndex = 0)
|
||||
),
|
||||
rulingFactionId = Some(6)
|
||||
)
|
||||
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
actingProvinceId -> testActingProvince,
|
||||
destinationProvinceId -> testDestinationProvince
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
val availableCommand = AvailableMarchCommandFactory.availableCommand(
|
||||
gameState,
|
||||
actingPlayerId,
|
||||
actingProvinceId
|
||||
)
|
||||
|
||||
val destinationProvince =
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces
|
||||
.find(_.provinceId == destinationProvinceId)
|
||||
|
||||
destinationProvince should be(defined)
|
||||
|
||||
// Calculate expected river crossing requirement based on map info
|
||||
val mapInfo = ShardokMapInfo.shardokMapInfos(destinationProvinceId)
|
||||
val expectedRequiresRiverCrossing =
|
||||
mapInfo.waterCrossingRequirementsByPosition(0) >= 10
|
||||
|
||||
destinationProvince.get.requiresRiverCrossing shouldBe expectedRequiresRiverCrossing
|
||||
}
|
||||
|
||||
it should "set requiresRiverCrossing based on map info regardless of specific values" in {
|
||||
// Use the same province combinations as the first river crossing test for consistency
|
||||
val actingProvinceId = 1
|
||||
val destinationProvinceId = 37
|
||||
|
||||
val testActingProvince = Province(
|
||||
id = actingProvinceId,
|
||||
neighbors = Vector(
|
||||
Neighbor(provinceId = destinationProvinceId, startingPositionIndex = 0)
|
||||
),
|
||||
rulingFactionId = Some(actingPlayerId),
|
||||
rulingFactionHeroIds = sufficientVigorHeroes.map(_.id),
|
||||
battalionIds = validBattalions.map(_.id),
|
||||
gold = 3232,
|
||||
food = 6263
|
||||
)
|
||||
|
||||
val testDestinationProvince = Province(
|
||||
id = destinationProvinceId,
|
||||
neighbors = Vector(
|
||||
Neighbor(provinceId = actingProvinceId, startingPositionIndex = 0)
|
||||
),
|
||||
rulingFactionId = Some(6)
|
||||
)
|
||||
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
actingProvinceId -> testActingProvince,
|
||||
destinationProvinceId -> testDestinationProvince
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
val availableCommand = AvailableMarchCommandFactory.availableCommand(
|
||||
gameState,
|
||||
actingPlayerId,
|
||||
actingProvinceId
|
||||
)
|
||||
|
||||
val destinationProvince =
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces
|
||||
.find(_.provinceId == destinationProvinceId)
|
||||
|
||||
destinationProvince should be(defined)
|
||||
|
||||
// Calculate expected river crossing requirement
|
||||
val mapInfo = ShardokMapInfo.shardokMapInfos(destinationProvinceId)
|
||||
val expectedRequiresRiverCrossing =
|
||||
mapInfo.waterCrossingRequirementsByPosition(0) >= 10
|
||||
|
||||
destinationProvince.get.requiresRiverCrossing shouldBe expectedRequiresRiverCrossing
|
||||
}
|
||||
|
||||
it should "correctly handle multiple destination provinces" in {
|
||||
// Use a simpler test that just verifies the feature works with one province
|
||||
val actingProvinceId = 1
|
||||
val destinationProvinceId = 37
|
||||
|
||||
val testActingProvince = Province(
|
||||
id = actingProvinceId,
|
||||
neighbors = Vector(
|
||||
Neighbor(provinceId = destinationProvinceId, startingPositionIndex = 0)
|
||||
),
|
||||
rulingFactionId = Some(actingPlayerId),
|
||||
rulingFactionHeroIds = sufficientVigorHeroes.map(_.id),
|
||||
battalionIds = validBattalions.map(_.id),
|
||||
gold = 3232,
|
||||
food = 6263
|
||||
)
|
||||
|
||||
val testDestinationProvince = Province(
|
||||
id = destinationProvinceId,
|
||||
neighbors = Vector(
|
||||
Neighbor(provinceId = actingProvinceId, startingPositionIndex = 0)
|
||||
),
|
||||
rulingFactionId = Some(6)
|
||||
)
|
||||
|
||||
val gameState = GameState(
|
||||
provinces = Map(
|
||||
actingProvinceId -> testActingProvince,
|
||||
destinationProvinceId -> testDestinationProvince
|
||||
),
|
||||
heroes = HeroMap(sufficientVigorHeroes),
|
||||
battalions = BattalionMap(validBattalions),
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
val availableCommand = AvailableMarchCommandFactory.availableCommand(
|
||||
gameState,
|
||||
actingPlayerId,
|
||||
actingProvinceId
|
||||
)
|
||||
|
||||
val availableDestinations =
|
||||
availableCommand.get.oneProvinceCommands.head.availableDestinationProvinces
|
||||
|
||||
availableDestinations should have size 1
|
||||
|
||||
// Verify the destination has a river crossing requirement (boolean value)
|
||||
val destination = availableDestinations.head
|
||||
destination.provinceId shouldBe destinationProvinceId
|
||||
// The requiresRiverCrossing should be a boolean value (we don't need to test the exact logic here)
|
||||
destination.requiresRiverCrossing shouldBe a[Boolean]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +375,6 @@ scala_test(
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:max_food_tribute",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:max_gold_tribute",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:min_vigor_for_march",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:shardok_map_info",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -57,16 +57,6 @@ scala_test(
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:portion_of_capacity_for_upgrade_battalion_quest",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:quest_recruitment_bonus",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:idable",
|
||||
"//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_faction_trait",
|
||||
"//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/concrete:action_result_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:battle_ended_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:province_conquered_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:province_held_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types/base:action_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
@@ -609,6 +599,7 @@ scala_test(
|
||||
name = "perform_uncontested_conquest_action_test",
|
||||
srcs = ["PerformUncontestedConquestActionTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library:eagle_validation_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:perform_uncontested_conquest_action",
|
||||
@@ -623,18 +614,11 @@ scala_test(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:province_conquered_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:sent_supplies_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:withdrawal_for_truce_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:action_result_proto_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:combat_unit",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:supplies",
|
||||
"//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",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province:orders",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province/concrete",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:action_impl_pkg",
|
||||
],
|
||||
)
|
||||
@@ -713,6 +697,7 @@ scala_test(
|
||||
name = "province_conquered_action_test",
|
||||
srcs = ["ProvinceConqueredActionTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:action_result_type_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
@@ -748,25 +733,13 @@ scala_test(
|
||||
"//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:province_conquered_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:unaffiliated_hero_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:captured_hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//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/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
"//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/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:eagle_unit",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_eagle_unit",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:action_impl_pkg",
|
||||
@@ -777,10 +750,9 @@ scala_test(
|
||||
name = "province_held_action_test",
|
||||
srcs = ["ProvinceHeldActionTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:action_result_type_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:gender_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:profession_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:army_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:faction_scala_proto",
|
||||
@@ -800,13 +772,8 @@ scala_test(
|
||||
"//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:province_held_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:captured_hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_eagle_unit",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:action_impl_pkg",
|
||||
],
|
||||
@@ -816,24 +783,25 @@ scala_test(
|
||||
name = "request_battles_action_test",
|
||||
srcs = ["RequestBattlesActionTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:action_result_type_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:army_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:faction_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:province_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:supplies_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:unaffiliated_hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library:eagle_internal_exception",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/applier:action_result_proto_applier_impl",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:request_battles_action",
|
||||
"//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_faction_trait",
|
||||
"//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/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types/base:action_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//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/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/shardok_battle",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:eagle_unit",
|
||||
"//src/test/scala/net/eagle0/common:proto_matchers",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:action_impl_pkg",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/validations:testing_noop_validator",
|
||||
"@maven//:org_scalamock_scalamock_3",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -841,36 +809,30 @@ scala_test(
|
||||
name = "resolve_battle_action_test",
|
||||
srcs = ["ResolveBattleActionTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/common:victory_condition_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:shardok_battle_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/common:shardok_internal_interface_scala_grpc",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:action_result_type_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:army_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:faction_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:province_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:supplies_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:unaffiliated_hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:resolve_battle_action",
|
||||
"//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",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:notification_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province",
|
||||
"//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/base:action_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//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",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/backstory_version",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/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/shardok_battle",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/shardok_battle:shardok_battle_converter",
|
||||
"//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/util:idable",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_resolution",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_eagle_unit",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_shardok_player",
|
||||
"//src/test/scala/net/eagle0/common:proto_matchers",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:action_impl_pkg",
|
||||
"@maven//:org_scalamock_scalamock_3",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -993,8 +955,23 @@ scala_test(
|
||||
name = "won_free_for_all_action_test",
|
||||
srcs = ["WonFreeForAllActionTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/common:common_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:action_result_type_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:combat_unit_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:action_result_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:army_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:faction_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:hero_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:province_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:supplies_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:unaffiliated_hero_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/applier:action_result_proto_applier_impl",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:won_free_for_all_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action_with_resulting_state",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:idable",
|
||||
"//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",
|
||||
@@ -1002,11 +979,11 @@ scala_test(
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/changed_province/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types:won_free_for_all_result_type",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:army_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/hero/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province/concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/unit_status",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:resolved_eagle_unit",
|
||||
"//src/test/scala/net/eagle0/common:proto_matchers",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:action_impl_pkg",
|
||||
"//src/test/scala/net/eagle0/eagle/library/util/validations:testing_noop_validator",
|
||||
],
|
||||
)
|
||||
|
||||
-32
@@ -43,36 +43,4 @@ class EndDiplomacyResolutionPhaseActionTest
|
||||
result.newRoundPhase shouldBe Some(RoundPhase.ReconResolution)
|
||||
}
|
||||
}
|
||||
|
||||
it should "handle game state updates through the action sequence" in {
|
||||
// Regression test for bug where invalidated offers used stale game state
|
||||
// This would previously throw an exception when later actions in the sequence
|
||||
// tried to process offers that had been invalidated but were still marked
|
||||
// as Unresolved in the stale initial game state
|
||||
|
||||
// The fix ensures that each action in the sequence sees the updated game state
|
||||
// from previous actions via the RandomStateTSequencer
|
||||
|
||||
// Create a game state with diplomacy offers
|
||||
val testGameState = gameState.copy(
|
||||
gameId = 12345L,
|
||||
currentRoundId = 5
|
||||
)
|
||||
|
||||
// This should not throw an exception even if offers get invalidated
|
||||
// The RandomStateTSequencer ensures later actions see updated state
|
||||
val results = EndDiplomacyResolutionPhaseAction(testGameState, applier)
|
||||
.randomResults(functionalRandom)
|
||||
.newValue
|
||||
|
||||
// Should always end with the end phase result
|
||||
inside(results.last) { result =>
|
||||
result.actionResultType shouldBe EndDiplomacyResolutionPhaseResultType
|
||||
result.newRoundPhase shouldBe Some(RoundPhase.ReconResolution)
|
||||
}
|
||||
|
||||
// The key improvement is that this no longer throws exceptions
|
||||
// when invalidation occurs, because later actions now receive
|
||||
// the updated game state via RandomStateTSequencer
|
||||
}
|
||||
}
|
||||
|
||||
+317
-439
@@ -1,7 +1,28 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionTypeId.{
|
||||
HEAVY_CAVALRY,
|
||||
LIGHT_INFANTRY
|
||||
}
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit as CombatUnitProto
|
||||
import net.eagle0.eagle.common.province_order_type.ProvinceOrderType
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase.UNCONTESTED_CONQUEST
|
||||
import net.eagle0.eagle.internal.army.{
|
||||
Attacking,
|
||||
HostileArmyGroup,
|
||||
Army as ArmyProto,
|
||||
MovingArmy as MovingArmyProto
|
||||
}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.supplies.Supplies as SuppliesProto
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
import net.eagle0.eagle.library.actions.impl.{BattalionMap, HeroMap}
|
||||
import net.eagle0.eagle.library.util.IDable.mapifyProvinces
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.types.{
|
||||
EndUncontestedConquestPhaseResultType,
|
||||
@@ -9,24 +30,9 @@ import net.eagle0.eagle.model.action_result.types.{
|
||||
SentSuppliesResultType,
|
||||
WithdrawalForTruceResultType
|
||||
}
|
||||
import net.eagle0.eagle.model.state.{
|
||||
Army,
|
||||
BattalionTypeId,
|
||||
CombatUnit,
|
||||
HostileArmyGroup,
|
||||
HostileArmyGroupStatus,
|
||||
MovingArmy,
|
||||
Supplies
|
||||
}
|
||||
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
|
||||
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.hero.{Gender, Profession}
|
||||
import net.eagle0.eagle.model.state.hero.concrete.HeroC
|
||||
import net.eagle0.eagle.model.state.province.ProvinceOrderType
|
||||
import net.eagle0.eagle.model.state.province.concrete.ProvinceC
|
||||
import net.eagle0.eagle.model.proto_converters.ActionResultProtoConverter
|
||||
import net.eagle0.eagle.model.state.province.ProvinceOrderType.Entrust
|
||||
import net.eagle0.eagle.model.state.{Army, CombatUnit, MovingArmy, Supplies}
|
||||
import org.scalatest.Inside.inside
|
||||
import org.scalatest.Inspectors.forExactly
|
||||
import org.scalatest.LoneElement.convertToCollectionLoneElementWrapper
|
||||
@@ -36,34 +42,18 @@ import org.scalatest.matchers.should.Matchers
|
||||
class PerformUncontestedConquestActionTest extends AnyFlatSpec with Matchers {
|
||||
private val attackerId = 1
|
||||
|
||||
private val heroes: Map[Int, HeroC] = Map(
|
||||
92 -> HeroC(
|
||||
id = 92,
|
||||
factionId = Some(attackerId),
|
||||
nameTextId = "hn_92",
|
||||
profession = Profession.Mage,
|
||||
pronounGender = Gender.Male
|
||||
),
|
||||
102 -> HeroC(
|
||||
id = 102,
|
||||
factionId = Some(attackerId),
|
||||
nameTextId = "hn_102",
|
||||
profession = Profession.Ranger,
|
||||
pronounGender = Gender.Female
|
||||
)
|
||||
private val heroes: Seq[Hero] = Vector(
|
||||
Hero(id = 92, factionId = Some(attackerId), nameTextId = "hn_92"),
|
||||
Hero(id = 102, factionId = Some(attackerId), nameTextId = "hn_102")
|
||||
)
|
||||
|
||||
private val battalions: Map[Int, BattalionC] = Map(
|
||||
34 -> BattalionC(
|
||||
id = 34,
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
size = 230
|
||||
),
|
||||
99 -> BattalionC(id = 99, typeId = BattalionTypeId.HeavyCavalry, size = 600)
|
||||
private val battalions = Vector(
|
||||
Battalion(id = 34, `type` = LIGHT_INFANTRY, size = 230),
|
||||
Battalion(id = 99, `type` = HEAVY_CAVALRY, size = 600)
|
||||
)
|
||||
|
||||
private val attackerCombatUnits = Vector(
|
||||
CombatUnit(
|
||||
CombatUnitProto(
|
||||
factionId = attackerId,
|
||||
heroId = 92,
|
||||
battalionId = Some(34)
|
||||
@@ -71,7 +61,7 @@ class PerformUncontestedConquestActionTest extends AnyFlatSpec with Matchers {
|
||||
)
|
||||
|
||||
private val moreAttackerCombatUnits = Vector(
|
||||
CombatUnit(
|
||||
CombatUnitProto(
|
||||
factionId = attackerId,
|
||||
heroId = 102,
|
||||
battalionId = Some(99)
|
||||
@@ -79,494 +69,382 @@ class PerformUncontestedConquestActionTest extends AnyFlatSpec with Matchers {
|
||||
)
|
||||
|
||||
"Execute with no uncontested provinces" should "return only the END action" in {
|
||||
val provinces = Map(
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = Some(15),
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UnknownOrderType,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
incomingArmies = Vector(
|
||||
MovingArmy(
|
||||
id = 1,
|
||||
army = Army(factionId = 1, units = attackerCombatUnits),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(0, 0),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
val startingState = GameState(
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentPhase = UNCONTESTED_CONQUEST,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
rulingFactionId = Some(15),
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UNKNOWN_ORDERS,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
incomingArmies = Vector(
|
||||
MovingArmyProto(
|
||||
army =
|
||||
Some(ArmyProto(factionId = 1, units = attackerCombatUnits)),
|
||||
arrivalRound = 9,
|
||||
originProvince = 17,
|
||||
supplies = None
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map(
|
||||
1 -> FactionC(
|
||||
id = 1,
|
||||
factionHeadId = 92,
|
||||
name = "Faction 1",
|
||||
leaderIds = Vector(92)
|
||||
),
|
||||
2 -> FactionC(
|
||||
id = 2,
|
||||
factionHeadId = 102,
|
||||
name = "Faction 2",
|
||||
leaderIds = Vector(102)
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
2 -> Faction(id = 2)
|
||||
)
|
||||
)
|
||||
|
||||
val results = PerformUncontestedConquestAction(
|
||||
gameId = 12345L,
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1, month = Date.Month.January),
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions
|
||||
).results
|
||||
val results = PerformUncontestedConquestAction(startingState).results
|
||||
|
||||
results.loneElement.actionResultType.shouldBe(
|
||||
EndUncontestedConquestPhaseResultType
|
||||
)
|
||||
results.loneElement.actionResultType shouldBe EndUncontestedConquestPhaseResultType
|
||||
}
|
||||
|
||||
"Execute against an undefended province" should "return an uncontested conquest" in {
|
||||
val provinces = Map(
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = None,
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UnknownOrderType,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
hostileArmies = Vector(
|
||||
HostileArmyGroup(
|
||||
factionId = 1,
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 1,
|
||||
army = Army(factionId = 1, units = attackerCombatUnits),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(0, 0),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
val startingState = GameState(
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentPhase = UNCONTESTED_CONQUEST,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
rulingFactionId = None,
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UNKNOWN_ORDERS,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
hostileArmies = Vector(
|
||||
HostileArmyGroup(
|
||||
factionId = 1,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmyProto(
|
||||
army =
|
||||
Some(ArmyProto(factionId = 1, units = attackerCombatUnits)),
|
||||
arrivalRound = 9,
|
||||
originProvince = 17,
|
||||
supplies = None
|
||||
)
|
||||
)
|
||||
),
|
||||
status = HostileArmyGroupStatus.Attacking
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map(
|
||||
1 -> FactionC(
|
||||
id = 1,
|
||||
factionHeadId = 92,
|
||||
name = "Faction 1",
|
||||
leaderIds = Vector(92)
|
||||
),
|
||||
2 -> FactionC(
|
||||
id = 2,
|
||||
factionHeadId = 102,
|
||||
name = "Faction 2",
|
||||
leaderIds = Vector(102)
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
2 -> Faction(id = 2)
|
||||
)
|
||||
)
|
||||
|
||||
val results = PerformUncontestedConquestAction(
|
||||
gameId = 12345L,
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1, month = Date.Month.January),
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions
|
||||
).results
|
||||
val results = PerformUncontestedConquestAction(startingState).results
|
||||
|
||||
results.size.shouldBe(3)
|
||||
results.head.actionResultType.shouldBe(SentSuppliesResultType)
|
||||
results(1).actionResultType.shouldBe(ProvinceConqueredResultType)
|
||||
results(2).actionResultType.shouldBe(EndUncontestedConquestPhaseResultType)
|
||||
results.foreach { ar =>
|
||||
ar.newBattle.isEmpty.shouldBe(true)
|
||||
}
|
||||
results.size shouldBe 3
|
||||
results.head.actionResultType shouldBe SentSuppliesResultType
|
||||
results(1).actionResultType shouldBe ProvinceConqueredResultType
|
||||
results(2).actionResultType shouldBe EndUncontestedConquestPhaseResultType
|
||||
results.forall(art =>
|
||||
ActionResultProtoConverter.toProto(art).newBattle.isEmpty
|
||||
) shouldBe true
|
||||
}
|
||||
|
||||
it should "add the attacking army's food and gold to the province" in {
|
||||
val attackingArmy = HostileArmyGroup(
|
||||
factionId = 1,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 1,
|
||||
army = Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnits
|
||||
MovingArmyProto(
|
||||
army = Some(
|
||||
ArmyProto(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnits
|
||||
)
|
||||
),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(200, 300),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
originProvince = 17,
|
||||
supplies = Some(SuppliesProto(gold = 200, food = 300))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val startingState = GameState(
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentPhase = UNCONTESTED_CONQUEST,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
hostileArmies = Vector(attackingArmy),
|
||||
gold = 125,
|
||||
food = 256
|
||||
)
|
||||
),
|
||||
status = HostileArmyGroupStatus.Attacking
|
||||
)
|
||||
|
||||
val provinces = Map(
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
hostileArmies = Vector(attackingArmy),
|
||||
gold = 125,
|
||||
food = 256
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
2 -> Faction(id = 2)
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map(
|
||||
1 -> FactionC(
|
||||
id = 1,
|
||||
factionHeadId = 92,
|
||||
name = "Faction 1",
|
||||
leaderIds = Vector(92)
|
||||
),
|
||||
2 -> FactionC(
|
||||
id = 2,
|
||||
factionHeadId = 102,
|
||||
name = "Faction 2",
|
||||
leaderIds = Vector(102)
|
||||
)
|
||||
)
|
||||
|
||||
val results = PerformUncontestedConquestAction(
|
||||
gameId = 12345L,
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1, month = Date.Month.January),
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions
|
||||
).results
|
||||
val results = PerformUncontestedConquestAction(startingState).results
|
||||
|
||||
inside(results.head.changedProvinces.head) { case cp: ChangedProvinceC =>
|
||||
cp.provinceId.shouldBe(5)
|
||||
cp.goldDelta.shouldBe(Some(200))
|
||||
cp.foodDelta.shouldBe(Some(300))
|
||||
cp.provinceId shouldBe 5
|
||||
cp.goldDelta shouldBe Some(200)
|
||||
cp.foodDelta shouldBe Some(300)
|
||||
}
|
||||
}
|
||||
|
||||
"attacking with two armies from the same faction" should "add both armies to the province" in {
|
||||
val provinces = Map(
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = None,
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UnknownOrderType,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
hostileArmies = Vector(
|
||||
HostileArmyGroup(
|
||||
factionId = 1,
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 17,
|
||||
army = Army(factionId = 1, units = attackerCombatUnits),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(300, 200),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
),
|
||||
MovingArmy(
|
||||
id = 717,
|
||||
army = Army(factionId = 1, units = moreAttackerCombatUnits),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 27,
|
||||
supplies = Supplies(500, 400),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
val startingState = GameState(
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentPhase = UNCONTESTED_CONQUEST,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
rulingFactionId = None,
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UNKNOWN_ORDERS,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
hostileArmies = Vector(
|
||||
HostileArmyGroup(
|
||||
factionId = 1,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmyProto(
|
||||
id = 17,
|
||||
army =
|
||||
Some(ArmyProto(factionId = 1, units = attackerCombatUnits)),
|
||||
arrivalRound = 9,
|
||||
originProvince = 17,
|
||||
supplies = Some(SuppliesProto(food = 200, gold = 300))
|
||||
),
|
||||
MovingArmyProto(
|
||||
id = 717,
|
||||
army = Some(
|
||||
ArmyProto(factionId = 1, units = moreAttackerCombatUnits)
|
||||
),
|
||||
arrivalRound = 9,
|
||||
originProvince = 27,
|
||||
supplies = Some(SuppliesProto(food = 400, gold = 500))
|
||||
)
|
||||
)
|
||||
),
|
||||
status = HostileArmyGroupStatus.Attacking
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map(
|
||||
1 -> FactionC(
|
||||
id = 1,
|
||||
factionHeadId = 92,
|
||||
name = "Faction 1",
|
||||
leaderIds = Vector(92)
|
||||
),
|
||||
2 -> FactionC(
|
||||
id = 2,
|
||||
factionHeadId = 102,
|
||||
name = "Faction 2",
|
||||
leaderIds = Vector(102)
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
2 -> Faction(id = 2)
|
||||
)
|
||||
)
|
||||
|
||||
val results = PerformUncontestedConquestAction(
|
||||
gameId = 12345L,
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1, month = Date.Month.January),
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions
|
||||
).results
|
||||
val results =
|
||||
PerformUncontestedConquestAction(startingState).results
|
||||
|
||||
results.size.shouldBe(3)
|
||||
results.head.actionResultType.shouldBe(SentSuppliesResultType)
|
||||
results.head.changedProvinces.should(have).size(1)
|
||||
results.head.changedProvinces.head.shouldBe(
|
||||
ChangedProvinceC(
|
||||
provinceId = 5,
|
||||
foodDelta = Some(600),
|
||||
goldDelta = Some(800),
|
||||
removedHostileArmyFactionIds = Vector(1)
|
||||
)
|
||||
results.size shouldBe 3
|
||||
results.head.actionResultType shouldBe SentSuppliesResultType
|
||||
results.head.changedProvinces should have size 1
|
||||
results.head.changedProvinces.head shouldBe ChangedProvinceC(
|
||||
provinceId = 5,
|
||||
foodDelta = Some(600),
|
||||
goldDelta = Some(800),
|
||||
removedHostileArmyFactionIds = Vector(1)
|
||||
)
|
||||
results(1).actionResultType.shouldBe(ProvinceConqueredResultType)
|
||||
results(1).changedProvinces.should(have).size(1)
|
||||
results(1).changedProvinces.head.shouldBe(
|
||||
ChangedProvinceC(
|
||||
provinceId = 5,
|
||||
newRulingFactionHeroIds = Vector(92, 102),
|
||||
newBattalionIds = Vector(34, 99),
|
||||
newRulingFactionId = Some(attackerId),
|
||||
removedHostileArmyFactionIds = Vector(attackerId),
|
||||
supportDelta = Some(-0.0),
|
||||
newProvinceOrders = Some(Entrust),
|
||||
clearDefendingArmy = true
|
||||
)
|
||||
results(1).actionResultType shouldBe ProvinceConqueredResultType
|
||||
results(1).changedProvinces should have size 1
|
||||
results(1).changedProvinces.head shouldBe ChangedProvinceC(
|
||||
provinceId = 5,
|
||||
newRulingFactionHeroIds = Vector(92, 102),
|
||||
newBattalionIds = Vector(34, 99),
|
||||
newRulingFactionId = Some(attackerId),
|
||||
removedHostileArmyFactionIds = Vector(attackerId),
|
||||
supportDelta = Some(-0.0),
|
||||
newProvinceOrders = Some(Entrust),
|
||||
clearDefendingArmy = true
|
||||
)
|
||||
results(2).actionResultType.shouldBe(EndUncontestedConquestPhaseResultType)
|
||||
results.foreach { ar =>
|
||||
ar.newBattle.isEmpty.shouldBe(true)
|
||||
}
|
||||
results(2).actionResultType shouldBe EndUncontestedConquestPhaseResultType
|
||||
results.forall(art =>
|
||||
ActionResultProtoConverter.toProto(art).newBattle.isEmpty
|
||||
) shouldBe true
|
||||
}
|
||||
|
||||
"attacking with two armies from different factions" should "fail" in {
|
||||
val provinces = Map(
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = None,
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UnknownOrderType,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
hostileArmies = Vector(
|
||||
HostileArmyGroup(
|
||||
factionId = 1,
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 17,
|
||||
army = Army(factionId = 1, units = attackerCombatUnits),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(300, 200),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
val startingState = GameState(
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentPhase = UNCONTESTED_CONQUEST,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
rulingFactionId = None,
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UNKNOWN_ORDERS,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
hostileArmies = Vector(
|
||||
HostileArmyGroup(
|
||||
factionId = 1,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmyProto(
|
||||
id = 17,
|
||||
army =
|
||||
Some(ArmyProto(factionId = 1, units = attackerCombatUnits)),
|
||||
arrivalRound = 9,
|
||||
originProvince = 17,
|
||||
supplies = Some(SuppliesProto(food = 200, gold = 300))
|
||||
)
|
||||
)
|
||||
),
|
||||
status = HostileArmyGroupStatus.Attacking
|
||||
),
|
||||
HostileArmyGroup(
|
||||
factionId = 2,
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 717,
|
||||
army = Army(factionId = 2, units = moreAttackerCombatUnits),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 27,
|
||||
supplies = Supplies(500, 400),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
HostileArmyGroup(
|
||||
factionId = 2,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmyProto(
|
||||
id = 717,
|
||||
army = Some(
|
||||
ArmyProto(factionId = 2, units = moreAttackerCombatUnits)
|
||||
),
|
||||
arrivalRound = 9,
|
||||
originProvince = 27,
|
||||
supplies = Some(SuppliesProto(food = 400, gold = 500))
|
||||
)
|
||||
)
|
||||
),
|
||||
status = HostileArmyGroupStatus.Attacking
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map(
|
||||
1 -> FactionC(
|
||||
id = 1,
|
||||
factionHeadId = 92,
|
||||
name = "Faction 1",
|
||||
leaderIds = Vector(92)
|
||||
),
|
||||
2 -> FactionC(
|
||||
id = 2,
|
||||
factionHeadId = 102,
|
||||
name = "Faction 2",
|
||||
leaderIds = Vector(102)
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
2 -> Faction(id = 2)
|
||||
)
|
||||
)
|
||||
|
||||
val ex = the[EagleInternalException] thrownBy {
|
||||
PerformUncontestedConquestAction(
|
||||
gameId = 12345L,
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1, month = Date.Month.January),
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions
|
||||
).results
|
||||
PerformUncontestedConquestAction(startingState).results
|
||||
}
|
||||
|
||||
ex.getMessage.shouldBe(
|
||||
"requirement failed: Attacking armies Vector(1, 2) are not mutually allied"
|
||||
)
|
||||
ex.getMessage shouldBe "requirement failed: Attacking armies Vector(1, 2) are not mutually allied"
|
||||
}
|
||||
|
||||
"attacking with two armies from truce-ing factions" should "bounce the armies" in {
|
||||
val faction2CombatUnits = Vector(
|
||||
CombatUnit(
|
||||
factionId = 2,
|
||||
heroId = 102,
|
||||
battalionId = Some(99)
|
||||
)
|
||||
)
|
||||
|
||||
val provinces = Map(
|
||||
17 -> ProvinceC(id = 17, rulingFactionId = Some(1)),
|
||||
27 -> ProvinceC(id = 27, rulingFactionId = Some(2)),
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = None,
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UnknownOrderType,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
hostileArmies = Vector(
|
||||
HostileArmyGroup(
|
||||
factionId = 1,
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 17,
|
||||
army = Army(factionId = 1, units = attackerCombatUnits),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(300, 200),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
val startingState = GameState(
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentPhase = UNCONTESTED_CONQUEST,
|
||||
provinces = mapifyProvinces(
|
||||
Province(id = 17, rulingFactionId = Some(1)),
|
||||
Province(id = 27, rulingFactionId = Some(2)),
|
||||
Province(
|
||||
id = 5,
|
||||
rulingFactionId = None,
|
||||
rulingHeroId = None,
|
||||
provinceOrders = ProvinceOrderType.UNKNOWN_ORDERS,
|
||||
rulingFactionHeroIds = Vector.empty,
|
||||
hostileArmies = Vector(
|
||||
HostileArmyGroup(
|
||||
factionId = 1,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmyProto(
|
||||
id = 17,
|
||||
army =
|
||||
Some(ArmyProto(factionId = 1, units = attackerCombatUnits)),
|
||||
arrivalRound = 9,
|
||||
originProvince = 17,
|
||||
supplies = Some(SuppliesProto(food = 200, gold = 300))
|
||||
)
|
||||
)
|
||||
),
|
||||
status = HostileArmyGroupStatus.Attacking
|
||||
),
|
||||
HostileArmyGroup(
|
||||
factionId = 2,
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 717,
|
||||
army = Army(factionId = 2, units = faction2CombatUnits),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 27,
|
||||
supplies = Supplies(500, 400),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
HostileArmyGroup(
|
||||
factionId = 2,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmyProto(
|
||||
id = 717,
|
||||
army = Some(
|
||||
ArmyProto(factionId = 2, units = moreAttackerCombatUnits)
|
||||
),
|
||||
arrivalRound = 9,
|
||||
originProvince = 27,
|
||||
supplies = Some(SuppliesProto(food = 400, gold = 500))
|
||||
)
|
||||
)
|
||||
),
|
||||
status = HostileArmyGroupStatus.Attacking
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map(
|
||||
1 -> FactionC(
|
||||
id = 1,
|
||||
factionHeadId = 92,
|
||||
name = "Faction 1",
|
||||
leaderIds = Vector(92),
|
||||
factionRelationships = Vector(
|
||||
FactionRelationship(
|
||||
targetFactionId = 2,
|
||||
relationshipLevel = RelationshipLevel.Truce
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
2 -> FactionC(
|
||||
id = 2,
|
||||
factionHeadId = 102,
|
||||
name = "Faction 2",
|
||||
leaderIds = Vector(102),
|
||||
factionRelationships = Vector(
|
||||
FactionRelationship(
|
||||
targetFactionId = 1,
|
||||
relationshipLevel = RelationshipLevel.Truce
|
||||
factions = Map(
|
||||
1 -> Faction(
|
||||
id = 1,
|
||||
factionRelationships = Vector(
|
||||
FactionRelationship(
|
||||
targetFactionId = 2,
|
||||
relationshipLevel = FactionRelationship.RelationshipLevel.TRUCE
|
||||
)
|
||||
)
|
||||
),
|
||||
2 -> Faction(
|
||||
id = 2,
|
||||
factionRelationships = Vector(
|
||||
FactionRelationship(
|
||||
targetFactionId = 1,
|
||||
relationshipLevel = FactionRelationship.RelationshipLevel.TRUCE
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val result = PerformUncontestedConquestAction(
|
||||
gameId = 12345L,
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1, month = Date.Month.January),
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
heroes = heroes,
|
||||
battalions = battalions
|
||||
).results.head
|
||||
val result = PerformUncontestedConquestAction(startingState).results.head
|
||||
|
||||
result.actionResultType.shouldBe(WithdrawalForTruceResultType)
|
||||
result.changedProvinces.should(
|
||||
contain(
|
||||
ChangedProvinceC(
|
||||
provinceId = 5,
|
||||
removedHostileArmyFactionIds = Vector(1, 2)
|
||||
)
|
||||
result.actionResultType shouldBe WithdrawalForTruceResultType
|
||||
result.changedProvinces should contain(
|
||||
ChangedProvinceC(
|
||||
provinceId = 5,
|
||||
removedHostileArmyFactionIds = Vector(1, 2)
|
||||
)
|
||||
)
|
||||
forExactly(1, result.changedProvinces) { case cp: ChangedProvinceC =>
|
||||
cp.provinceId.shouldBe(17)
|
||||
cp.provinceId shouldBe 17
|
||||
inside(cp.newIncomingArmies.loneElement) { case ma: MovingArmy =>
|
||||
ma.id.shouldBe(17)
|
||||
ma.originProvinceId.shouldBe(5)
|
||||
ma.arrivalRound.shouldBe(10)
|
||||
ma.supplies.shouldBe(Supplies(300, 200))
|
||||
ma.startingPositionIndex.shouldBe(None)
|
||||
ma.id shouldBe 17
|
||||
ma.originProvinceId shouldBe 5
|
||||
ma.arrivalRound shouldBe 10
|
||||
ma.supplies shouldBe Supplies(food = 200, gold = 300)
|
||||
ma.startingPositionIndex shouldBe None
|
||||
|
||||
inside(ma.army) { case army: Army =>
|
||||
army.factionId.shouldBe(1)
|
||||
army.units.loneElement.shouldBe(
|
||||
CombatUnit(
|
||||
heroId = 92,
|
||||
battalionId = Some(34),
|
||||
factionId = 1
|
||||
)
|
||||
army.factionId shouldBe 1
|
||||
army.units.loneElement shouldBe CombatUnit(
|
||||
heroId = 92,
|
||||
battalionId = Some(34),
|
||||
factionId = attackerId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
forExactly(1, result.changedProvinces) { case cp: ChangedProvinceC =>
|
||||
cp.provinceId.shouldBe(27)
|
||||
cp.provinceId shouldBe 27
|
||||
inside(cp.newIncomingArmies.loneElement) { case ma: MovingArmy =>
|
||||
ma.id.shouldBe(717)
|
||||
ma.originProvinceId.shouldBe(5)
|
||||
ma.arrivalRound.shouldBe(10)
|
||||
ma.supplies.shouldBe(Supplies(500, 400))
|
||||
ma.startingPositionIndex.shouldBe(None)
|
||||
ma.id shouldBe 717
|
||||
ma.originProvinceId shouldBe 5
|
||||
ma.arrivalRound shouldBe 10
|
||||
ma.supplies shouldBe Supplies(food = 400, gold = 500)
|
||||
ma.startingPositionIndex shouldBe None
|
||||
|
||||
inside(ma.army) { case army: Army =>
|
||||
army.factionId.shouldBe(2)
|
||||
army.units.loneElement.shouldBe(
|
||||
CombatUnit(
|
||||
heroId = 102,
|
||||
battalionId = Some(99),
|
||||
factionId = 2
|
||||
)
|
||||
army.factionId shouldBe 2
|
||||
army.units.loneElement shouldBe CombatUnit(
|
||||
heroId = 102,
|
||||
battalionId = Some(99),
|
||||
factionId = attackerId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-8
@@ -272,14 +272,8 @@ class PerformVassalCommandsPhaseActionTest
|
||||
BattalionWithFoodCost(battalionId = 2, monthlyFood = 50),
|
||||
BattalionWithFoodCost(battalionId = 3, monthlyFood = 50)
|
||||
),
|
||||
availableDestinationProvinces = actingProvince.neighbors
|
||||
.map(_.provinceId)
|
||||
.map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
)
|
||||
availableDestinationProvinces =
|
||||
actingProvince.neighbors.map(_.provinceId)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
+158
-245
@@ -1,10 +1,9 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.common.common_unit.UnitStatus
|
||||
import net.eagle0.eagle.common.date.Date as DateProto
|
||||
import net.eagle0.eagle.common.hero_backstory_version.BackstoryVersion
|
||||
import net.eagle0.eagle.common.province_order_type.ProvinceOrderType
|
||||
import net.eagle0.eagle.common.profession.Profession
|
||||
import net.eagle0.eagle.common.gender.Gender
|
||||
import net.eagle0.eagle.common.recruitment_info.RecruitmentStatus.{
|
||||
RECRUITMENT_STATUS_HAS_QUEST,
|
||||
RECRUITMENT_STATUS_PRISONER
|
||||
@@ -19,16 +18,13 @@ import net.eagle0.eagle.common.unaffiliated_hero_type.UnaffiliatedHeroType.{
|
||||
UNAFFILIATED_HERO_PRISONER,
|
||||
UNAFFILIATED_HERO_RESIDENT
|
||||
}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.{Neighbor, Province}
|
||||
import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero as UnaffiliatedHeroProto
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
|
||||
import net.eagle0.eagle.model.state.hero.concrete.HeroC
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.library.actions.impl.{BattalionMap, HeroMap}
|
||||
import net.eagle0.eagle.library.settings.{
|
||||
FreedPrisonerLoyalty,
|
||||
@@ -56,16 +52,13 @@ import net.eagle0.eagle.model.action_result.concrete.{
|
||||
import net.eagle0.eagle.model.action_result.types.ProvinceConqueredResultType
|
||||
import net.eagle0.eagle.model.proto_converters.UnaffiliatedHeroConverter
|
||||
import net.eagle0.eagle.model.proto_converters.diplomacy_offer.DiplomacyOfferConverter
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionConverter
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.RansomOfferDetails.PrisonerToBeRansomed
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.RansomOffer
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{
|
||||
Unresolved,
|
||||
Accepted,
|
||||
Rejected,
|
||||
Invalidated
|
||||
}
|
||||
import net.eagle0.eagle.model.state.hero.ReleasedByProvinceCaptureBackstoryEvent
|
||||
@@ -78,118 +71,36 @@ import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
private def createProvinceConqueredAction(
|
||||
gameState: GameState = startingState,
|
||||
province: Province = conqueredProvince,
|
||||
conquerors: Vector[ResolvedEagleUnit] = notFledAttackers,
|
||||
defenders: Vector[ResolvedEagleUnit] = Vector.empty
|
||||
): ProvinceConqueredAction = {
|
||||
ProvinceConqueredAction(
|
||||
gameId = gameState.gameId,
|
||||
currentRoundId = gameState.currentRoundId,
|
||||
currentDate = DateConverter.fromProto(gameState.currentDate),
|
||||
conqueredProvince = ProvinceConverter.fromProto(province),
|
||||
notFledConquerors = conquerors,
|
||||
notFledDefenders = defenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = province.rulingFactionId,
|
||||
factions =
|
||||
gameState.factions.view.mapValues(FactionConverter.fromProto).toMap,
|
||||
heroes = gameState.heroes.view.mapValues(HeroConverter.fromProto).toMap,
|
||||
battalions =
|
||||
gameState.battalions.view.mapValues(BattalionConverter.fromProto).toMap,
|
||||
provinces =
|
||||
gameState.provinces.view.mapValues(ProvinceConverter.fromProto).toMap
|
||||
)
|
||||
}
|
||||
val attackingFactionId = 8
|
||||
private val attackingHero1 =
|
||||
Hero(
|
||||
id = 23,
|
||||
factionId = Some(attackingFactionId),
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_MALE
|
||||
)
|
||||
Hero(id = 23, factionId = Some(attackingFactionId))
|
||||
private val attackingHero2 =
|
||||
Hero(
|
||||
id = 24,
|
||||
factionId = Some(attackingFactionId),
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_FEMALE
|
||||
)
|
||||
Hero(id = 24, factionId = Some(attackingFactionId))
|
||||
private val attackingBattalion1 = Battalion(id = 18)
|
||||
private val attackingBattalion2 = Battalion(id = 17)
|
||||
|
||||
private val notFledAttackers = Vector(
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 23,
|
||||
factionId = Some(attackingFactionId),
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Champion,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Male
|
||||
),
|
||||
battalion = Some(
|
||||
BattalionC(
|
||||
id = 18,
|
||||
typeId = net.eagle0.eagle.model.state.BattalionTypeId.LightInfantry,
|
||||
size = 100,
|
||||
training = 50.0,
|
||||
armament = 50.0,
|
||||
name = "Test Battalion 1"
|
||||
)
|
||||
),
|
||||
status = UnitStatus.Normal
|
||||
hero = attackingHero1,
|
||||
battalion = attackingBattalion1,
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
),
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 24,
|
||||
factionId = Some(attackingFactionId),
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Champion,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Female
|
||||
),
|
||||
battalion = Some(
|
||||
BattalionC(
|
||||
id = 17,
|
||||
typeId = net.eagle0.eagle.model.state.BattalionTypeId.LightInfantry,
|
||||
size = 100,
|
||||
training = 50.0,
|
||||
armament = 50.0,
|
||||
name = "Test Battalion 2"
|
||||
)
|
||||
),
|
||||
status = UnitStatus.Normal
|
||||
hero = attackingHero2,
|
||||
battalion = attackingBattalion2,
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
)
|
||||
)
|
||||
|
||||
private val defendingFactionId = 3
|
||||
private val defendingHero =
|
||||
Hero(
|
||||
id = 55,
|
||||
factionId = Some(defendingFactionId),
|
||||
profession = Profession.PALADIN,
|
||||
pronounGender = Gender.GENDER_MALE
|
||||
)
|
||||
Hero(id = 55, factionId = Some(defendingFactionId))
|
||||
|
||||
private val notFledDefenders = Vector(
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 55,
|
||||
factionId = Some(defendingFactionId),
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Paladin,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Male
|
||||
),
|
||||
battalion = Some(
|
||||
BattalionC(
|
||||
id = EagleUnit.defaultBattalionId,
|
||||
typeId = net.eagle0.eagle.model.state.BattalionTypeId.LightInfantry,
|
||||
size = 0,
|
||||
training = 0.0,
|
||||
armament = 0.0,
|
||||
name = "Default Battalion"
|
||||
)
|
||||
),
|
||||
status = UnitStatus.Captured
|
||||
hero = defendingHero,
|
||||
battalion = EagleUnit.defaultBattalion,
|
||||
status = UnitStatus.CAPTURED_UNIT
|
||||
)
|
||||
)
|
||||
|
||||
@@ -227,7 +138,14 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
)
|
||||
|
||||
"execute" should "set the basics in the result" in {
|
||||
val result = createProvinceConqueredAction().immediateExecute
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = Vector.empty,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
result.actionResultType shouldBe ProvinceConqueredResultType
|
||||
result.provinceId shouldBe Some(conqueredProvince.id)
|
||||
@@ -235,8 +153,13 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "change the ruling player to the attacking army's player" in {
|
||||
val result = createProvinceConqueredAction(
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) {
|
||||
@@ -246,8 +169,13 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "change the ruling player heroes to the attacking army's heroes" in {
|
||||
val result = createProvinceConqueredAction(
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) {
|
||||
@@ -261,8 +189,13 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "change the battalions to the attacking army's battalions" in {
|
||||
val result = createProvinceConqueredAction(
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) {
|
||||
@@ -276,8 +209,13 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "set the defending player heroes as unaffiliated prisoners" in {
|
||||
val result = createProvinceConqueredAction(
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) {
|
||||
@@ -347,9 +285,13 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
).map(DiplomacyOfferConverter.toProto)
|
||||
)
|
||||
|
||||
val result = createProvinceConqueredAction(
|
||||
gameState = gsWithRansomOffers,
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = gsWithRansomOffers,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
forExactly(1, result.changedFactions) {
|
||||
@@ -397,24 +339,17 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
_.provinces(
|
||||
conqueredProvince.id
|
||||
) := conqueredProvinceWithImprisonedHeroesFromAttacker,
|
||||
_.heroes(97) := Hero(
|
||||
id = 97,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_MALE
|
||||
),
|
||||
_.heroes(94) := Hero(
|
||||
id = 94,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_FEMALE
|
||||
)
|
||||
_.heroes(97) := Hero(id = 97, factionId = None),
|
||||
_.heroes(94) := Hero(id = 94, factionId = None)
|
||||
)
|
||||
|
||||
val result = createProvinceConqueredAction(
|
||||
gameState = startingStateWithImprisonedHeroes,
|
||||
province = conqueredProvinceWithImprisonedHeroesFromAttacker,
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingStateWithImprisonedHeroes,
|
||||
conqueredProvince = conqueredProvinceWithImprisonedHeroesFromAttacker,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) {
|
||||
@@ -486,24 +421,17 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
val startingStateWithImprisonedHeroes = startingState.update(
|
||||
_.provinces(conqueredProvince.id) := conqueredProvinceWithQuests,
|
||||
_.heroes(97) := Hero(
|
||||
id = 97,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_MALE
|
||||
),
|
||||
_.heroes(94) := Hero(
|
||||
id = 94,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_FEMALE
|
||||
)
|
||||
_.heroes(97) := Hero(id = 97, factionId = None),
|
||||
_.heroes(94) := Hero(id = 94, factionId = None)
|
||||
)
|
||||
|
||||
val result = createProvinceConqueredAction(
|
||||
gameState = startingStateWithImprisonedHeroes,
|
||||
province = conqueredProvinceWithQuests,
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingStateWithImprisonedHeroes,
|
||||
conqueredProvince = conqueredProvinceWithQuests,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) {
|
||||
@@ -525,8 +453,13 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "remove the conquering fid from the attacking factions list" in {
|
||||
val result = createProvinceConqueredAction(
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) {
|
||||
@@ -538,8 +471,13 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "remove any scouting info from factions" in {
|
||||
val result = createProvinceConqueredAction(
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
result.changedFactions shouldBe Vector(
|
||||
@@ -559,12 +497,15 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "remove the focus province if it matches the conquered province" in {
|
||||
val result = createProvinceConqueredAction(
|
||||
gameState = startingState.update(
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState.update(
|
||||
_.factions(defendingFactionId).focusProvinceId := conqueredProvince.id
|
||||
),
|
||||
province = conqueredProvince,
|
||||
defenders = notFledDefenders
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
result.changedFactions should contain(
|
||||
@@ -576,16 +517,19 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "not remove the focus province if it's another province" in {
|
||||
val result = createProvinceConqueredAction(
|
||||
gameState = startingState.update(
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState.update(
|
||||
_.factions(defendingFactionId).focusProvinceId := 12,
|
||||
_.provinces(12) := Province(
|
||||
id = 12,
|
||||
rulingFactionId = Some(defendingFactionId)
|
||||
)
|
||||
),
|
||||
province = conqueredProvince,
|
||||
defenders = notFledDefenders
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
result.changedFactions should not contain (
|
||||
@@ -597,8 +541,14 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
"execute" should "include a ProvinceExpansion notification if the province is not already occupied" in {
|
||||
val result = createProvinceConqueredAction(
|
||||
province = conqueredProvince.update(_.optionalRulingFactionId := None)
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState,
|
||||
conqueredProvince =
|
||||
conqueredProvince.update(_.optionalRulingFactionId := None),
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = Vector.empty,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = None
|
||||
).immediateExecute
|
||||
|
||||
forExactly(1, result.newNotifications) { case notification: NotificationC =>
|
||||
@@ -611,7 +561,14 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
}
|
||||
|
||||
it should "include a ProvinceConquered notification if the province was occupied" in {
|
||||
val result = createProvinceConqueredAction().immediateExecute
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingState,
|
||||
conqueredProvince = conqueredProvince,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = Vector.empty,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
forExactly(1, result.newNotifications) { case notification: NotificationC =>
|
||||
notification.deferred shouldBe true
|
||||
@@ -651,33 +608,22 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
_.heroes(97) := Hero(
|
||||
id = 97,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_MALE,
|
||||
backstoryVersions = Vector(
|
||||
BackstoryVersion(
|
||||
textId = "backstory_97",
|
||||
date = Some(DateProto(year = 2020, month = 1))
|
||||
)
|
||||
)
|
||||
backstoryVersions = Vector(BackstoryVersion(textId = "backstory_97"))
|
||||
),
|
||||
_.heroes(94) := Hero(
|
||||
id = 94,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_FEMALE,
|
||||
backstoryVersions = Vector(
|
||||
BackstoryVersion(
|
||||
textId = "backstory_94",
|
||||
date = Some(DateProto(year = 2020, month = 1))
|
||||
)
|
||||
)
|
||||
backstoryVersions = Vector(BackstoryVersion(textId = "backstory_94"))
|
||||
)
|
||||
)
|
||||
|
||||
val result = createProvinceConqueredAction(
|
||||
gameState = startingStateWithPrisoners,
|
||||
province = conqueredProvinceWithPrisoners,
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingStateWithPrisoners,
|
||||
conqueredProvince = conqueredProvinceWithPrisoners,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) {
|
||||
@@ -714,33 +660,22 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
_.heroes(97) := Hero(
|
||||
id = 97,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_MALE,
|
||||
backstoryVersions = Vector(
|
||||
BackstoryVersion(
|
||||
textId = "backstory_97",
|
||||
date = Some(DateProto(year = 2020, month = 1))
|
||||
)
|
||||
)
|
||||
backstoryVersions = Vector(BackstoryVersion(textId = "backstory_97"))
|
||||
),
|
||||
_.heroes(94) := Hero(
|
||||
id = 94,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_FEMALE,
|
||||
backstoryVersions = Vector(
|
||||
BackstoryVersion(
|
||||
textId = "backstory_94",
|
||||
date = Some(DateProto(year = 2020, month = 1))
|
||||
)
|
||||
)
|
||||
backstoryVersions = Vector(BackstoryVersion(textId = "backstory_94"))
|
||||
)
|
||||
)
|
||||
|
||||
val result = createProvinceConqueredAction(
|
||||
gameState = startingStateWithPrisoners,
|
||||
province = conqueredProvinceWithPrisoners,
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingStateWithPrisoners,
|
||||
conqueredProvince = conqueredProvinceWithPrisoners,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
forExactly(1, result.changedHeroes) { case changedHero: ChangedHeroC =>
|
||||
@@ -820,26 +755,12 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
_.heroes(97) := Hero(
|
||||
id = 97,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_MALE,
|
||||
backstoryVersions = Vector(
|
||||
BackstoryVersion(
|
||||
textId = "backstory_97",
|
||||
date = Some(DateProto(year = 2020, month = 1))
|
||||
)
|
||||
)
|
||||
backstoryVersions = Vector(BackstoryVersion(textId = "backstory_97"))
|
||||
),
|
||||
_.heroes(94) := Hero(
|
||||
id = 94,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_FEMALE,
|
||||
backstoryVersions = Vector(
|
||||
BackstoryVersion(
|
||||
textId = "backstory_94",
|
||||
date = Some(DateProto(year = 2020, month = 1))
|
||||
)
|
||||
)
|
||||
backstoryVersions = Vector(BackstoryVersion(textId = "backstory_94"))
|
||||
),
|
||||
_.provinces(alliedPid) := Province(
|
||||
id = alliedPid,
|
||||
@@ -851,10 +772,13 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
)
|
||||
)
|
||||
|
||||
val result = createProvinceConqueredAction(
|
||||
gameState = startingStateWithPrisoners,
|
||||
province = conqueredProvinceWithPrisoners,
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingStateWithPrisoners,
|
||||
conqueredProvince = conqueredProvinceWithPrisoners,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
forExactly(1, result.changedProvinces) {
|
||||
@@ -915,33 +839,22 @@ class ProvinceConqueredActionTest extends AnyFlatSpec with Matchers {
|
||||
_.heroes(97) := Hero(
|
||||
id = 97,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_MALE,
|
||||
backstoryVersions = Vector(
|
||||
BackstoryVersion(
|
||||
textId = "backstory_97",
|
||||
date = Some(DateProto(year = 2020, month = 1))
|
||||
)
|
||||
)
|
||||
backstoryVersions = Vector(BackstoryVersion(textId = "backstory_97"))
|
||||
),
|
||||
_.heroes(94) := Hero(
|
||||
id = 94,
|
||||
factionId = None,
|
||||
profession = Profession.CHAMPION,
|
||||
pronounGender = Gender.GENDER_FEMALE,
|
||||
backstoryVersions = Vector(
|
||||
BackstoryVersion(
|
||||
textId = "backstory_94",
|
||||
date = Some(DateProto(year = 2020, month = 1))
|
||||
)
|
||||
)
|
||||
backstoryVersions = Vector(BackstoryVersion(textId = "backstory_94"))
|
||||
)
|
||||
)
|
||||
|
||||
val result = createProvinceConqueredAction(
|
||||
gameState = startingStateWithPrisoners,
|
||||
province = conqueredProvinceWithPrisoners,
|
||||
defenders = notFledDefenders
|
||||
val result = ProvinceConqueredAction(
|
||||
startingState = startingStateWithPrisoners,
|
||||
conqueredProvince = conqueredProvinceWithPrisoners,
|
||||
notFledConquerors = notFledAttackers,
|
||||
notFledDefenders = notFledDefenders,
|
||||
destroyedBattalions = Vector.empty,
|
||||
defendingFactionId = Some(defendingFactionId)
|
||||
).immediateExecute
|
||||
|
||||
result.changedHeroes should contain allOf (
|
||||
|
||||
+7
-17
@@ -1,6 +1,6 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.common.common_unit.UnitStatus
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.internal.army.{Army, MovingArmy}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
@@ -15,9 +15,7 @@ import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedPro
|
||||
import net.eagle0.eagle.model.action_result.concrete.NotificationC
|
||||
import net.eagle0.eagle.model.action_result.types.ProvinceHeldResultType
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionConverter
|
||||
import net.eagle0.eagle.model.state.CapturedHero
|
||||
import net.eagle0.eagle.model.state.hero.concrete.HeroC
|
||||
import net.eagle0.eagle.shardok_interface.ResolvedEagleUnit
|
||||
import org.scalatest.Inspectors.forExactly
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
@@ -50,22 +48,14 @@ class ProvinceHeldActionTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
private val notFledAttackers = Vector(
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 5,
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Mage,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Female
|
||||
),
|
||||
battalion = None,
|
||||
status = UnitStatus.Normal
|
||||
hero = Hero(id = 5),
|
||||
battalion = Battalion(id = 99),
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
),
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 10,
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Ranger,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Male
|
||||
),
|
||||
battalion = None,
|
||||
status = UnitStatus.Normal
|
||||
hero = Hero(id = 10),
|
||||
battalion = Battalion(id = 7),
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
+471
-365
@@ -1,305 +1,315 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.eagle.*
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.state.shardok_battle.{
|
||||
BattleType,
|
||||
VictoryCondition
|
||||
import net.eagle0.eagle.library.actions.impl.WaitingAction
|
||||
import net.eagle0.common.victory_condition.VictoryCondition.{
|
||||
VICTORY_CONDITION_HOLDS_CRITICAL_TILES,
|
||||
VICTORY_CONDITION_LAST_PLAYER_STANDING,
|
||||
VICTORY_CONDITION_WIN_AFTER_MAX_ROUNDS
|
||||
}
|
||||
import net.eagle0.eagle.model.state.*
|
||||
import net.eagle0.eagle.model.state.HostileArmyGroupStatus.{
|
||||
import net.eagle0.eagle.common.action_result_type.ActionResultType.{
|
||||
PROVINCE_CONQUERED,
|
||||
SENT_SUPPLIES,
|
||||
START_BATTLE
|
||||
}
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionType
|
||||
import net.eagle0.eagle.common.battalion_type.BattalionTypeId.LIGHT_INFANTRY
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit
|
||||
import net.eagle0.eagle.common.date.Date
|
||||
import net.eagle0.eagle.common.province_order_type.ProvinceOrderType
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase.FOOD_CONSUMPTION
|
||||
import net.eagle0.eagle.internal.action_result.ActionResult
|
||||
import net.eagle0.eagle.internal.army.{
|
||||
Army,
|
||||
Attacking,
|
||||
AwaitingDecision
|
||||
AwaitingDecision,
|
||||
HostileArmyGroup,
|
||||
MovingArmy
|
||||
}
|
||||
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.faction.concrete.FactionC
|
||||
import net.eagle0.eagle.model.state.hero.Gender.{Female, Male}
|
||||
import net.eagle0.eagle.model.state.hero.Profession.NoProfession
|
||||
import net.eagle0.eagle.model.state.hero.concrete.HeroC
|
||||
import net.eagle0.eagle.model.state.province.{Neighbor, ProvinceOrderType}
|
||||
import net.eagle0.eagle.model.state.province.concrete.ProvinceC
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.changed_province.ChangedProvince
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.{Neighbor, Province}
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle.BattleType
|
||||
import net.eagle0.eagle.internal.shardok_battle.{ShardokBattle, ShardokPlayer}
|
||||
import net.eagle0.eagle.internal.supplies.Supplies
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplierImpl
|
||||
import net.eagle0.eagle.library.actions.impl.{BattalionMap, HeroMap}
|
||||
import net.eagle0.eagle.library.util.validations.TestingNoopValidator
|
||||
import net.eagle0.eagle.shardok_interface.EagleUnit
|
||||
import org.scalamock.scalatest.MockFactory
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.Inside.inside
|
||||
|
||||
class RequestBattlesActionTest extends AnyFlatSpec with Matchers {
|
||||
class RequestBattlesActionTest
|
||||
extends AnyFlatSpec
|
||||
with MockFactory
|
||||
with Matchers {
|
||||
|
||||
import net.eagle0.common.ProtoMatchers.*
|
||||
|
||||
private val actionResultProtoApplier = new ActionResultProtoApplierImpl(
|
||||
validator = TestingNoopValidator
|
||||
)
|
||||
|
||||
private val gameId = 0xa878ab
|
||||
|
||||
private val attackerId = 1
|
||||
private val defenderId = 2
|
||||
|
||||
private val heroes = Map[HeroId, HeroC](
|
||||
92 -> HeroC(
|
||||
id = 92,
|
||||
factionId = Some(attackerId),
|
||||
nameTextId = "hn_92",
|
||||
profession = NoProfession,
|
||||
pronounGender = Female
|
||||
),
|
||||
22 -> HeroC(
|
||||
id = 22,
|
||||
factionId = Some(defenderId),
|
||||
nameTextId = "hn_22",
|
||||
profession = NoProfession,
|
||||
pronounGender = Male
|
||||
)
|
||||
private val heroes: Vector[Hero] = Vector(
|
||||
Hero(id = 92, factionId = Some(attackerId), nameTextId = "hn_92"),
|
||||
Hero(id = 22, factionId = Some(defenderId), nameTextId = "hn_22")
|
||||
)
|
||||
|
||||
private val lightInfantryType = BattalionTypeId.LightInfantry
|
||||
|
||||
private val battalions = Map[BattalionId, BattalionC](
|
||||
34 -> BattalionC(id = 34, typeId = lightInfantryType, size = 230),
|
||||
33 -> BattalionC(id = 33, typeId = lightInfantryType, size = 210)
|
||||
private val battalions = Vector(
|
||||
Battalion(id = 34, `type` = LIGHT_INFANTRY, size = 230),
|
||||
Battalion(id = 33, `type` = LIGHT_INFANTRY, size = 210)
|
||||
)
|
||||
|
||||
private val attackerCombatUnits = Vector(
|
||||
CombatUnit(factionId = attackerId, heroId = 92, battalionId = Some(34))
|
||||
CombatUnit(
|
||||
factionId = attackerId,
|
||||
heroId = 92,
|
||||
battalionId = Some(34)
|
||||
)
|
||||
)
|
||||
|
||||
private val defenderCombatUnits = Vector(
|
||||
CombatUnit(factionId = defenderId, heroId = 22, battalionId = Some(33))
|
||||
CombatUnit(
|
||||
factionId = defenderId,
|
||||
heroId = 22,
|
||||
battalionId = Some(33)
|
||||
)
|
||||
)
|
||||
|
||||
private val battalionTypes = Map[BattalionTypeId, BattalionType](
|
||||
lightInfantryType -> BattalionType(
|
||||
typeId = lightInfantryType,
|
||||
name = "Light Infantry",
|
||||
capacity = 1000,
|
||||
private val attackerExpandedUnits = Vector(
|
||||
EagleUnit(
|
||||
hero = Hero(id = 92, factionId = Some(attackerId), nameTextId = "hn_92"),
|
||||
battalion = Battalion(id = 34, size = 230)
|
||||
)
|
||||
)
|
||||
|
||||
private val defenderExpandedUnits = Vector(
|
||||
EagleUnit(
|
||||
hero = Hero(id = 22, factionId = Some(defenderId), nameTextId = "hn_22"),
|
||||
battalion = Battalion(id = 33, size = 210)
|
||||
)
|
||||
)
|
||||
|
||||
private val battalionTypes = Vector(
|
||||
BattalionType(
|
||||
typeId = LIGHT_INFANTRY,
|
||||
allowsCasting = true,
|
||||
allowsStealth = true,
|
||||
alwaysArcheryCapable = false,
|
||||
baseCost = 10.0,
|
||||
monthlyFoodCost = 0.05,
|
||||
baseArmamentCostPerTroop = 1.0,
|
||||
trainingDifficulty = 1.0,
|
||||
minimumAgriculture = 0,
|
||||
minimumEconomy = 0
|
||||
monthlyFoodCost = 0.05
|
||||
)
|
||||
)
|
||||
|
||||
// Helper to create minimal faction
|
||||
private def createFaction(id: FactionId, headId: HeroId): FactionC =
|
||||
FactionC(
|
||||
id = id,
|
||||
factionHeadId = headId,
|
||||
name = s"Faction $id",
|
||||
leaderIds = Vector(headId),
|
||||
prestigeModifiers = Vector.empty,
|
||||
factionRelationships = Vector.empty,
|
||||
incomingDiplomacyOffers = Vector.empty,
|
||||
focusProvinceId = None,
|
||||
reconnedProvinces = Vector.empty,
|
||||
lastActedProvinceIdThisRound = 1,
|
||||
lastOutgoingTruceOfferRounds = Vector.empty,
|
||||
lastOutgoingAllianceOfferRounds = Vector.empty,
|
||||
lastOutgoingInvitationRounds = Vector.empty,
|
||||
lastOutgoingRansomOfferRounds = Vector.empty,
|
||||
earliestRoundForInvitation = 1
|
||||
"Execute against a defended province" should "return a started battle" in {
|
||||
val defendingArmy = Army(
|
||||
factionId = 3,
|
||||
units = defenderCombatUnits
|
||||
)
|
||||
|
||||
"Execute against a defended province" should "return a started battle" in {
|
||||
val defendingArmy = Army(factionId = 3, units = defenderCombatUnits)
|
||||
|
||||
val attackingArmy = HostileArmyGroup(
|
||||
status = Attacking,
|
||||
status = Attacking(),
|
||||
factionId = 1,
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 1, // MovingArmyId is just an Int
|
||||
army = Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnits,
|
||||
fleeProvinceId = Some(17)
|
||||
army = Some(
|
||||
Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnits,
|
||||
fleeProvinceId = Some(17)
|
||||
)
|
||||
),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(gold = 0, food = 1000),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
originProvince = 17,
|
||||
supplies = Some(Supplies(food = 1000))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val provinces = Map[ProvinceId, ProvinceC](
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 17, startingPositionIndex = 4)),
|
||||
provinceOrders = ProvinceOrderType.Entrust,
|
||||
rulingFactionHeroIds = Vector(22),
|
||||
defendingArmy = Some(defendingArmy),
|
||||
hostileArmies = Vector(attackingArmy),
|
||||
hexMapName = "Lower_Lake",
|
||||
food = 1000
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map[FactionId, FactionC](
|
||||
1 -> createFaction(1, 92),
|
||||
3 -> createFaction(3, 22)
|
||||
)
|
||||
|
||||
val action = RequestBattlesAction(
|
||||
val startingState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
randomSeed = 1L,
|
||||
gameId = 0xcafe,
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1501, month = Date.Month.June),
|
||||
battleCounter = 0x17,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentPhase = FOOD_CONSUMPTION,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 17, startingPositionIndex = 4)),
|
||||
provinceOrders = ProvinceOrderType.ENTRUST,
|
||||
rulingFactionHeroIds = Vector(22),
|
||||
defendingArmy = Some(defendingArmy),
|
||||
hostileArmies = Vector(attackingArmy),
|
||||
hexMapName = "Lower_Lake",
|
||||
food = 1000
|
||||
)
|
||||
),
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
3 -> Faction(id = 3)
|
||||
),
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
val results = action.results
|
||||
val action = RequestBattlesAction(startingState)
|
||||
|
||||
val results = action.resultsOfExecute()
|
||||
|
||||
results.size shouldBe 1
|
||||
val battleResult = results.head
|
||||
battleResult.newBattle should not be empty
|
||||
|
||||
val battle = battleResult.newBattle.get
|
||||
battle.shardokGameId shouldBe "cafe_18_a884c092"
|
||||
battle.battleIndex shouldBe 24
|
||||
battle.battleType shouldBe BattleType.AssaultProvince
|
||||
battle.eagleGameId shouldBe 0xcafe
|
||||
battle.hexMapName shouldBe "Lower_Lake"
|
||||
battle.defenderProvince shouldBe 5
|
||||
battle.roundStarted shouldBe 9
|
||||
|
||||
battle.players should have size 2
|
||||
val attacker = battle.players.find(_.eagleFid == 1).get
|
||||
attacker.isDefender shouldBe false
|
||||
attacker.armyGroup shouldBe Some(attackingArmy)
|
||||
attacker.food shouldBe 12
|
||||
attacker.victoryConditions should contain(
|
||||
VictoryCondition.HoldsCriticalTiles
|
||||
)
|
||||
attacker.victoryConditions should contain(
|
||||
VictoryCondition.LastPlayerStanding
|
||||
)
|
||||
|
||||
val defender = battle.players.find(_.eagleFid == 3).get
|
||||
defender.eagleFid shouldBe 3
|
||||
defender.isDefender shouldBe true
|
||||
defender.food shouldBe 11
|
||||
defender.victoryConditions should contain(
|
||||
VictoryCondition.LastPlayerStanding
|
||||
)
|
||||
defender.victoryConditions should contain(
|
||||
VictoryCondition.WinAfterMaxRounds
|
||||
results.head.`type` shouldBe START_BATTLE
|
||||
results.head.newBattle.get should equalProto(
|
||||
ShardokBattle(
|
||||
shardokGameId = "cafe_18_7c083557",
|
||||
battleIndex = 24,
|
||||
battleType = BattleType.BATTLE_TYPE_ASSAULT_PROVINCE,
|
||||
eagleGameId = 0xcafe,
|
||||
hexMapName = "Lower_Lake",
|
||||
players = Vector(
|
||||
ShardokPlayer(
|
||||
eagleFid = 1,
|
||||
isDefender = false,
|
||||
armyGroup = Some(attackingArmy),
|
||||
food = 12,
|
||||
victoryConditions = Vector(
|
||||
VICTORY_CONDITION_HOLDS_CRITICAL_TILES,
|
||||
VICTORY_CONDITION_LAST_PLAYER_STANDING
|
||||
)
|
||||
),
|
||||
ShardokPlayer(
|
||||
eagleFid = 3,
|
||||
isDefender = true,
|
||||
armyGroup = Some(
|
||||
HostileArmyGroup(
|
||||
factionId = 3,
|
||||
armies = Vector(MovingArmy(army = Some(defendingArmy)))
|
||||
)
|
||||
),
|
||||
food = 11,
|
||||
victoryConditions = Vector(
|
||||
VICTORY_CONDITION_LAST_PLAYER_STANDING,
|
||||
VICTORY_CONDITION_WIN_AFTER_MAX_ROUNDS
|
||||
)
|
||||
)
|
||||
),
|
||||
defenderProvince = 5,
|
||||
roundStarted = 9
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"Execute against an undefended province" should "return an uncontested conquest" in {
|
||||
val attackingArmy = HostileArmyGroup(
|
||||
factionId = 1,
|
||||
status = Attacking,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 1,
|
||||
army = Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnits,
|
||||
fleeProvinceId = Some(17)
|
||||
army = Some(
|
||||
Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnits,
|
||||
fleeProvinceId = Some(17)
|
||||
)
|
||||
),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(gold = 0, food = 0),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
originProvince = 17
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val provinces = Map[ProvinceId, ProvinceC](
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
provinceOrders = ProvinceOrderType.Entrust,
|
||||
rulingFactionHeroIds = Vector(22),
|
||||
hostileArmies = Vector(attackingArmy)
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map[FactionId, FactionC](
|
||||
1 -> createFaction(1, 92),
|
||||
2 -> createFaction(2, 22)
|
||||
)
|
||||
|
||||
val results = RequestBattlesAction(
|
||||
gameId = 0x123,
|
||||
val startingState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1501, month = Date.Month.June),
|
||||
battleCounter = 0,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
currentPhase = FOOD_CONSUMPTION,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
provinceOrders = ProvinceOrderType.ENTRUST,
|
||||
rulingFactionHeroIds = Vector(22),
|
||||
hostileArmies = Vector(attackingArmy)
|
||||
)
|
||||
),
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
2 -> Faction(id = 2)
|
||||
),
|
||||
battalionTypes = battalionTypes
|
||||
).results
|
||||
)
|
||||
|
||||
val results =
|
||||
RequestBattlesAction(startingState)
|
||||
.execute(actionResultProtoApplier)
|
||||
.map(_.actionResult)
|
||||
|
||||
results.size shouldBe 2
|
||||
// First result is sending supplies, second is province conquered
|
||||
results.last.changedProvinces.exists(_.provinceId == 5) shouldBe true
|
||||
results.head.`type` shouldBe SENT_SUPPLIES
|
||||
results.last.`type` shouldBe PROVINCE_CONQUERED
|
||||
results.last.province should contain(5)
|
||||
results.forall(_.newBattle.isEmpty) shouldBe true
|
||||
}
|
||||
|
||||
it should "throw if a hostile army is not Attacking" in {
|
||||
val undecidedArmy = HostileArmyGroup(
|
||||
factionId = 1,
|
||||
status = AwaitingDecision,
|
||||
status = AwaitingDecision(),
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 1,
|
||||
army = Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnits,
|
||||
fleeProvinceId = Some(17)
|
||||
army = Some(
|
||||
Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnits,
|
||||
fleeProvinceId = Some(17)
|
||||
)
|
||||
),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(gold = 0, food = 0),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
originProvince = 17
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val provinces = Map[ProvinceId, ProvinceC](
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
provinceOrders = ProvinceOrderType.Entrust,
|
||||
rulingFactionHeroIds = Vector(22),
|
||||
hostileArmies = Vector(undecidedArmy)
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map[FactionId, FactionC](
|
||||
1 -> createFaction(1, 92),
|
||||
2 -> createFaction(2, 22)
|
||||
val startingState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentPhase = FOOD_CONSUMPTION,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
provinceOrders = ProvinceOrderType.ENTRUST,
|
||||
rulingFactionHeroIds = Vector(22),
|
||||
hostileArmies = Vector(undecidedArmy)
|
||||
)
|
||||
),
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
2 -> Faction(id = 2)
|
||||
),
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
val ex = the[EagleInternalException] thrownBy {
|
||||
RequestBattlesAction(
|
||||
gameId = 0x123,
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1501, month = Date.Month.June),
|
||||
battleCounter = 0,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
battalionTypes = battalionTypes
|
||||
).results
|
||||
RequestBattlesAction(startingState)
|
||||
.execute(actionResultProtoApplier)
|
||||
.map(_.actionResult)
|
||||
}
|
||||
|
||||
ex.getMessage shouldBe "requirement failed: all armies must be attacking in the battle request phase"
|
||||
@@ -308,58 +318,60 @@ class RequestBattlesActionTest extends AnyFlatSpec with Matchers {
|
||||
it should "add the attacking army's food and gold to the province" in {
|
||||
val attackingArmy = HostileArmyGroup(
|
||||
factionId = 1,
|
||||
status = Attacking,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 1,
|
||||
army = Army(factionId = 1, units = attackerCombatUnits),
|
||||
army = Some(
|
||||
Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnits
|
||||
)
|
||||
),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(gold = 200, food = 300),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
originProvince = 17,
|
||||
supplies = Some(Supplies(gold = 200, food = 300))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val provinces = Map[ProvinceId, ProvinceC](
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
hostileArmies = Vector(attackingArmy),
|
||||
gold = 125,
|
||||
food = 256
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map[FactionId, FactionC](
|
||||
1 -> createFaction(1, 92),
|
||||
2 -> createFaction(2, 22)
|
||||
)
|
||||
|
||||
val results = RequestBattlesAction(
|
||||
gameId = 0x123,
|
||||
val startingState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1501, month = Date.Month.June),
|
||||
battleCounter = 0,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
currentPhase = FOOD_CONSUMPTION,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
hostileArmies = Vector(attackingArmy),
|
||||
gold = 125,
|
||||
food = 256
|
||||
)
|
||||
),
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
2 -> Faction(id = 2)
|
||||
),
|
||||
battalionTypes = battalionTypes
|
||||
).results
|
||||
)
|
||||
|
||||
inside(results.head.changedProvinces.head) {
|
||||
case changedProvinceC: ChangedProvinceC =>
|
||||
changedProvinceC.provinceId shouldBe 5
|
||||
changedProvinceC.goldDelta should contain(200)
|
||||
changedProvinceC.foodDelta should contain(300)
|
||||
}
|
||||
val results =
|
||||
RequestBattlesAction(startingState)
|
||||
.execute(actionResultProtoApplier)
|
||||
.map(_.actionResult)
|
||||
|
||||
results.head.changedProvinces.head.id shouldBe 5
|
||||
results.head.changedProvinces.head.goldDelta should contain(200)
|
||||
results.head.changedProvinces.head.foodDelta should contain(300)
|
||||
}
|
||||
|
||||
"Execute with an empty battalion" should "send an empty battalion" in {
|
||||
val attackerCombatUnitsWithNoBattalion = Vector(
|
||||
CombatUnit(factionId = attackerId, heroId = 92, battalionId = None)
|
||||
CombatUnit(
|
||||
factionId = attackerId,
|
||||
heroId = 92,
|
||||
battalionId = None
|
||||
)
|
||||
)
|
||||
|
||||
val defendingArmy = Army(
|
||||
@@ -370,130 +382,224 @@ class RequestBattlesActionTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
val attackingArmy = HostileArmyGroup(
|
||||
factionId = 1,
|
||||
status = Attacking,
|
||||
status = Attacking(),
|
||||
armies = Vector(
|
||||
MovingArmy(
|
||||
id = 1,
|
||||
army = Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnitsWithNoBattalion,
|
||||
fleeProvinceId = Some(17)
|
||||
army = Some(
|
||||
Army(
|
||||
factionId = 1,
|
||||
units = attackerCombatUnitsWithNoBattalion,
|
||||
fleeProvinceId = Some(17)
|
||||
)
|
||||
),
|
||||
arrivalRound = 9,
|
||||
destinationProvinceId = 5,
|
||||
originProvinceId = 17,
|
||||
supplies = Supplies(gold = 0, food = 0),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
originProvince = 17
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val provinces = Map[ProvinceId, ProvinceC](
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 17, startingPositionIndex = 4)),
|
||||
provinceOrders = ProvinceOrderType.Entrust,
|
||||
rulingFactionHeroIds = Vector(22),
|
||||
defendingArmy = Some(defendingArmy),
|
||||
hostileArmies = Vector(attackingArmy),
|
||||
hexMapName = "Lower_Lake",
|
||||
food = 1000
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map[FactionId, FactionC](
|
||||
1 -> createFaction(1, 92),
|
||||
3 -> createFaction(3, 22)
|
||||
)
|
||||
|
||||
val results = RequestBattlesAction(
|
||||
val startingState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
gameId = 0xabc123,
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1501, month = Date.Month.June),
|
||||
battleCounter = 0xa5,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
battalionTypes = battalionTypes
|
||||
).results
|
||||
|
||||
results should have size 1
|
||||
val battleResult = results.head
|
||||
battleResult.newBattle should not be empty
|
||||
|
||||
val battle = battleResult.newBattle.get
|
||||
battle.eagleGameId shouldBe 0xabc123
|
||||
battle.roundStarted shouldBe 9
|
||||
battle.shardokGameId shouldBe "abc123_a6_a884c092"
|
||||
battle.battleIndex shouldBe 0xa6
|
||||
battle.battleType shouldBe BattleType.AssaultProvince
|
||||
battle.hexMapName shouldBe "Lower_Lake"
|
||||
battle.defenderProvince shouldBe 5
|
||||
|
||||
battle.players should have size 2
|
||||
battle.players.find(_.eagleFid == 1).get.isDefender shouldBe false
|
||||
battle.players.find(_.eagleFid == 1).get.armyGroup shouldBe Some(
|
||||
attackingArmy
|
||||
)
|
||||
battle.players.find(_.eagleFid == 1).get.victoryConditions should contain(
|
||||
VictoryCondition.HoldsCriticalTiles
|
||||
)
|
||||
battle.players.find(_.eagleFid == 1).get.victoryConditions should contain(
|
||||
VictoryCondition.LastPlayerStanding
|
||||
)
|
||||
|
||||
battle.players.find(_.eagleFid == 3).get.isDefender shouldBe true
|
||||
battle.players.find(_.eagleFid == 3).get.food shouldBe 11
|
||||
battle.players.find(_.eagleFid == 3).get.victoryConditions should contain(
|
||||
VictoryCondition.LastPlayerStanding
|
||||
)
|
||||
battle.players.find(_.eagleFid == 3).get.victoryConditions should contain(
|
||||
VictoryCondition.WinAfterMaxRounds
|
||||
)
|
||||
|
||||
inside(battleResult.changedProvinces.head) {
|
||||
case changedProvinceC: ChangedProvinceC =>
|
||||
changedProvinceC.provinceId shouldBe 5
|
||||
changedProvinceC.removedHostileArmyFactionIds should contain(
|
||||
attackingArmy.factionId
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentPhase = FOOD_CONSUMPTION,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
neighbors =
|
||||
Vector(Neighbor(provinceId = 17, startingPositionIndex = 4)),
|
||||
provinceOrders = ProvinceOrderType.ENTRUST,
|
||||
rulingFactionHeroIds = Vector(22),
|
||||
defendingArmy = Some(defendingArmy),
|
||||
hostileArmies = Vector(attackingArmy),
|
||||
hexMapName = "Lower_Lake",
|
||||
food = 1000
|
||||
)
|
||||
changedProvinceC.foodDelta should contain(-11)
|
||||
}
|
||||
),
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
3 -> Faction(id = 3)
|
||||
),
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
val results =
|
||||
RequestBattlesAction(startingState).execute(actionResultProtoApplier)
|
||||
results should have size 1
|
||||
results.head.actionResult should equalProto(
|
||||
ActionResult(
|
||||
`type` = START_BATTLE,
|
||||
province = Some(5),
|
||||
changedProvinces = Vector(
|
||||
ChangedProvince(
|
||||
id = 5,
|
||||
removedHostileArmyFactionIds = Vector(attackingArmy.factionId),
|
||||
foodDelta = Some(-11)
|
||||
)
|
||||
),
|
||||
newBattle = Some(
|
||||
ShardokBattle(
|
||||
eagleGameId = 0xabc123,
|
||||
roundStarted = 9,
|
||||
shardokGameId = "abc123_a6_b7de69e8",
|
||||
battleIndex = 0xa6,
|
||||
battleType = BattleType.BATTLE_TYPE_ASSAULT_PROVINCE,
|
||||
hexMapName = "Lower_Lake",
|
||||
players = Vector(
|
||||
ShardokPlayer(
|
||||
eagleFid = 1,
|
||||
isDefender = false,
|
||||
armyGroup = Some(attackingArmy),
|
||||
victoryConditions = Vector(
|
||||
VICTORY_CONDITION_HOLDS_CRITICAL_TILES,
|
||||
VICTORY_CONDITION_LAST_PLAYER_STANDING
|
||||
)
|
||||
),
|
||||
ShardokPlayer(
|
||||
eagleFid = 3,
|
||||
isDefender = true,
|
||||
armyGroup = Some(
|
||||
HostileArmyGroup(
|
||||
factionId = 3,
|
||||
armies = Vector(MovingArmy(army = Some(defendingArmy)))
|
||||
)
|
||||
),
|
||||
food = 11,
|
||||
victoryConditions = Vector(
|
||||
VICTORY_CONDITION_LAST_PLAYER_STANDING,
|
||||
VICTORY_CONDITION_WIN_AFTER_MAX_ROUNDS
|
||||
)
|
||||
)
|
||||
),
|
||||
defenderProvince = 5
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"execute" should "return nothing if there is no incoming army" in {
|
||||
val provinces = Map[ProvinceId, ProvinceC](
|
||||
5 -> ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
rulingFactionHeroIds = Vector(22)
|
||||
)
|
||||
)
|
||||
|
||||
val factions = Map[FactionId, FactionC](
|
||||
1 -> createFaction(1, 92),
|
||||
3 -> createFaction(3, 22)
|
||||
)
|
||||
|
||||
val action = RequestBattlesAction(
|
||||
gameId = 0x123,
|
||||
val startingState = GameState(
|
||||
currentDate = Some(Date(year = 1501, month = 6)),
|
||||
heroes = HeroMap(heroes),
|
||||
battalions = BattalionMap(battalions),
|
||||
currentRoundId = 9,
|
||||
currentDate = Date(year = 1501, month = Date.Month.June),
|
||||
battleCounter = 0,
|
||||
heroes = heroes,
|
||||
battalions = battalions,
|
||||
provinces = provinces,
|
||||
factions = factions,
|
||||
currentPhase = FOOD_CONSUMPTION,
|
||||
provinces = Map(
|
||||
5 -> Province(
|
||||
id = 5,
|
||||
rulingFactionId = Some(defenderId),
|
||||
rulingHeroId = Some(22),
|
||||
rulingFactionHeroIds = Vector(22)
|
||||
)
|
||||
),
|
||||
factions = Map(
|
||||
1 -> Faction(id = 1),
|
||||
3 -> Faction(id = 3)
|
||||
),
|
||||
battalionTypes = battalionTypes
|
||||
)
|
||||
|
||||
val results = action.results
|
||||
val action = RequestBattlesAction(startingState)
|
||||
|
||||
val results = action.execute(actionResultProtoApplier).map(_.actionResult)
|
||||
|
||||
results shouldBe empty
|
||||
}
|
||||
|
||||
// "Execute with multiple incoming armies" should "return a started battle and the rest just return" in {
|
||||
// val defendingArmy = Army(
|
||||
// factionId = 3,
|
||||
// units = defenderCombatUnits
|
||||
// )
|
||||
//
|
||||
// val attackingArmy1 = HostileArmyGroup(
|
||||
// factionId = 1,
|
||||
//
|
||||
// MovingArmy(
|
||||
// army = Some(Army(factionId = 1, units = attackerCombatUnits)),
|
||||
// arrivalRound = 9,
|
||||
// originProvince = 17
|
||||
// )
|
||||
// val attackingArmy2 = MovingArmy(
|
||||
// army = Some(
|
||||
// Army(
|
||||
// factionId = 1,
|
||||
// units = Vector(CombatUnit(factionId = 1, heroId = 212))
|
||||
// )
|
||||
// ),
|
||||
// arrivalRound = 9,
|
||||
// originProvince = 23
|
||||
// )
|
||||
// val attackingArmy3 = MovingArmy(
|
||||
// army = Some(
|
||||
// Army(
|
||||
// factionId = 7,
|
||||
// units = Vector(CombatUnit(factionId = 7, heroId = 929))
|
||||
// )
|
||||
// ),
|
||||
// arrivalRound = 9,
|
||||
// originProvince = 33
|
||||
// )
|
||||
//
|
||||
// val startingState = GameState(
|
||||
// gameId = 0xCAFE,
|
||||
// battleCounter = 0x17,
|
||||
// heroes = HeroMap(heroes),
|
||||
// battalions = BattalionMap(battalions),
|
||||
// currentRoundId = 9,
|
||||
// currentPhase = FOOD_CONSUMPTION,
|
||||
// provinces = Map(
|
||||
// 5 -> Province(
|
||||
// id = 5,
|
||||
// rulingFactionId = Some(defenderId),
|
||||
// rulingHeroId = Some(22),
|
||||
// neighbors = Vector(
|
||||
// Neighbor(provinceId = 17, startingPositionIndex = 4),
|
||||
// Neighbor(provinceId = 23, startingPositionIndex = 6),
|
||||
// Neighbor(provinceId = 33, startingPositionIndex = 0)
|
||||
// ),
|
||||
// provinceOrders = ProvinceOrderType.ENTRUST,
|
||||
// rulingFactionHeroIds = Vector(22),
|
||||
// defendingArmy = Some(defendingArmy),
|
||||
// incomingArmies = Vector(attackingArmy1, attackingArmy2, attackingArmy3)
|
||||
// ),
|
||||
// 17 -> Province(id = 17),
|
||||
// 23 -> Province(id = 23),
|
||||
// 33 -> Province(id = 33)
|
||||
// ),
|
||||
// factions = Map(
|
||||
// 1 -> Faction(id = 1),
|
||||
// 3 -> Faction(id = 3),
|
||||
// 7 -> Faction(id = 7)
|
||||
// )
|
||||
// )
|
||||
//
|
||||
// val action = RequestBattlesAction(startingState)
|
||||
//
|
||||
// val results = action.resultsOfExecute()
|
||||
//
|
||||
// results.size shouldBe 3
|
||||
// results.map(_.`type`) shouldBe Vector(START_BATTLE, BATTLE_ENDED, BATTLE_ENDED)
|
||||
// results(1).changedProvinces should have size 2
|
||||
// // remove the incoming army
|
||||
// results(1).changedProvinces.head.id shouldBe 5
|
||||
// results(1).changedProvinces.head.removedIncomingArmyIds should have size 1
|
||||
// // add an incoming army to the origin
|
||||
// Vector(17, 23, 33).contains(results(1).changedProvinces.last.id) shouldBe true
|
||||
// results(1).changedProvinces.last.addedIncomingArmies should have size 1
|
||||
//
|
||||
// results(2).changedProvinces should have size 2
|
||||
// // remove the incoming army
|
||||
// results(2).changedProvinces.head.id shouldBe 5
|
||||
// results(2).changedProvinces.head.removedIncomingArmyIds should have size 1
|
||||
// // add an incoming army to the origin
|
||||
// Vector(17, 23, 33).contains(results(2).changedProvinces.last.id) shouldBe true
|
||||
// results(2).changedProvinces.last.addedIncomingArmies should have size 1
|
||||
// }
|
||||
}
|
||||
|
||||
+1552
-650
File diff suppressed because it is too large
Load Diff
+173
-168
@@ -1,117 +1,117 @@
|
||||
package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.common.ProtoMatchers
|
||||
import net.eagle0.common.common_unit.UnitStatus
|
||||
import net.eagle0.eagle.common.combat_unit.CombatUnit as CombatUnitProto
|
||||
import net.eagle0.eagle.internal.army.{
|
||||
AwaitingFreeForAll,
|
||||
HostileArmyGroup,
|
||||
Army as ArmyProto,
|
||||
MovingArmy as MovingArmyProto
|
||||
}
|
||||
import net.eagle0.eagle.internal.battalion.Battalion
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship.RelationshipLevel.TRUCE
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokPlayer
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultProtoApplierImpl
|
||||
import net.eagle0.eagle.library.util.IDable.{
|
||||
mapifyBattalions,
|
||||
mapifyFactions,
|
||||
mapifyProvinces
|
||||
}
|
||||
import net.eagle0.eagle.library.util.validations.TestingNoopValidator
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.types.WonFreeForAllResultType
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
import net.eagle0.eagle.model.state.{
|
||||
Army,
|
||||
BattalionTypeId,
|
||||
CombatUnit,
|
||||
HostileArmyGroup,
|
||||
MovingArmy,
|
||||
Supplies
|
||||
}
|
||||
import net.eagle0.eagle.model.state.HostileArmyGroupStatus.AwaitingDecision
|
||||
import net.eagle0.eagle.model.state.battalion.concrete.BattalionC
|
||||
import net.eagle0.eagle.model.state.hero.concrete.HeroC
|
||||
import net.eagle0.eagle.model.state.province.concrete.ProvinceC
|
||||
import net.eagle0.eagle.model.proto_converters.ArmyConverter
|
||||
import net.eagle0.eagle.model.state.{Army, CombatUnit, MovingArmy, Supplies}
|
||||
import net.eagle0.eagle.shardok_interface.ResolvedEagleUnit
|
||||
import net.eagle0.eagle.{FactionId, ProvinceId, RoundId}
|
||||
import org.scalatest.Inside.inside
|
||||
import org.scalatest.Inspectors.forExactly
|
||||
import org.scalatest.LoneElement.convertToCollectionLoneElementWrapper
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class WonFreeForAllActionTest extends AnyFlatSpec with Matchers {
|
||||
private val winningFid: FactionId = 37
|
||||
private val roundId: RoundId = 88
|
||||
private val provinceId: ProvinceId = 19
|
||||
class WonFreeForAllActionTest
|
||||
extends AnyFlatSpec
|
||||
with Matchers
|
||||
with ProtoMatchers {
|
||||
private val winningFid = 37
|
||||
private val roundId = 88
|
||||
private val provinceId = 19
|
||||
|
||||
private val winningArmy = MovingArmy(
|
||||
id = 0,
|
||||
army = Army(
|
||||
factionId = winningFid,
|
||||
units = Vector(
|
||||
CombatUnit(
|
||||
heroId = 11,
|
||||
battalionId = Some(33),
|
||||
factionId = winningFid
|
||||
),
|
||||
CombatUnit(
|
||||
heroId = 12,
|
||||
battalionId = Some(34),
|
||||
factionId = winningFid
|
||||
val actionResultProtoApplier = new ActionResultProtoApplierImpl(
|
||||
validator = TestingNoopValidator
|
||||
)
|
||||
|
||||
private val winningArmy = MovingArmyProto(
|
||||
army = Some(
|
||||
ArmyProto(
|
||||
factionId = winningFid,
|
||||
units = Vector(
|
||||
CombatUnitProto(
|
||||
heroId = 11,
|
||||
battalionId = Some(33),
|
||||
factionId = winningFid
|
||||
),
|
||||
CombatUnitProto(
|
||||
heroId = 12,
|
||||
battalionId = Some(34),
|
||||
factionId = winningFid
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
arrivalRound = roundId,
|
||||
destinationProvinceId = provinceId,
|
||||
originProvinceId = 1,
|
||||
supplies = Supplies(0, 0),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
arrivalRound = roundId
|
||||
)
|
||||
|
||||
private val winningArmyGroup = HostileArmyGroup(
|
||||
status = AwaitingDecision,
|
||||
status = AwaitingFreeForAll(),
|
||||
factionId = winningFid,
|
||||
armies = Vector(winningArmy)
|
||||
)
|
||||
|
||||
private val province = ProvinceC(
|
||||
id = provinceId,
|
||||
name = "Test Province"
|
||||
)
|
||||
|
||||
private val battalions = Map(
|
||||
33 -> BattalionC(
|
||||
id = 33,
|
||||
size = 100,
|
||||
typeId = BattalionTypeId.LightInfantry
|
||||
),
|
||||
34 -> BattalionC(
|
||||
id = 34,
|
||||
size = 150,
|
||||
typeId = BattalionTypeId.LightInfantry
|
||||
),
|
||||
37 -> BattalionC(
|
||||
id = 37,
|
||||
size = 100,
|
||||
typeId = BattalionTypeId.LightInfantry
|
||||
private val province =
|
||||
Province(id = provinceId)
|
||||
private val gameState = GameState(
|
||||
factions = mapifyFactions(Faction(id = winningFid)),
|
||||
provinces = mapifyProvinces(province),
|
||||
battalions = mapifyBattalions(
|
||||
Battalion(id = 33, size = 100),
|
||||
Battalion(id = 34, size = 150),
|
||||
Battalion(id = 37, size = 100)
|
||||
)
|
||||
)
|
||||
|
||||
private val winningUnits = Vector(
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 11,
|
||||
factionId = Some(winningFid),
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Mage,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Male
|
||||
),
|
||||
battalion = None,
|
||||
status = UnitStatus.Normal
|
||||
hero = Hero(id = 11, factionId = Some(winningFid)),
|
||||
battalion = Battalion(id = 33),
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
),
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 12,
|
||||
factionId = Some(winningFid),
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Ranger,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Female
|
||||
),
|
||||
battalion = None,
|
||||
status = UnitStatus.Normal
|
||||
hero = Hero(id = 12, factionId = Some(winningFid)),
|
||||
battalion = Battalion(id = 34),
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
)
|
||||
)
|
||||
private val winningPlayers = Vector(
|
||||
ShardokPlayer(
|
||||
eagleFid = winningFid,
|
||||
isDefender = false,
|
||||
armyGroup = Some(winningArmyGroup)
|
||||
)
|
||||
)
|
||||
private val winningArmyGroups = Vector(winningArmyGroup)
|
||||
|
||||
"immediateExecute" should "set the basics" in {
|
||||
val result = WonFreeForAllAction(
|
||||
battalions,
|
||||
gameState,
|
||||
province,
|
||||
winningUnits,
|
||||
winningArmyGroups
|
||||
winningPlayers
|
||||
).immediateExecute
|
||||
|
||||
result.actionResultType shouldBe WonFreeForAllResultType
|
||||
@@ -123,10 +123,10 @@ class WonFreeForAllActionTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
it should "have a single changed province with the defending province ID" in {
|
||||
val result = WonFreeForAllAction(
|
||||
battalions,
|
||||
gameState,
|
||||
province,
|
||||
winningUnits,
|
||||
winningArmyGroups
|
||||
winningPlayers
|
||||
).immediateExecute
|
||||
|
||||
result.changedProvinces.loneElement.provinceId shouldBe provinceId
|
||||
@@ -134,43 +134,39 @@ class WonFreeForAllActionTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
it should "add the victor's incoming army back to the province" in {
|
||||
val result = WonFreeForAllAction(
|
||||
battalions,
|
||||
gameState,
|
||||
province,
|
||||
winningUnits,
|
||||
winningArmyGroups
|
||||
winningPlayers
|
||||
).immediateExecute
|
||||
|
||||
forExactly(1, result.changedProvinces) { case cp: ChangedProvinceC =>
|
||||
cp.newHostileArmies.loneElement.armies.loneElement shouldBe winningArmy
|
||||
cp.newHostileArmies.loneElement.armies.loneElement shouldBe ArmyConverter
|
||||
.fromProto(winningArmy)
|
||||
}
|
||||
}
|
||||
|
||||
it should "not include a unit that is not in the winningUnits arg" in {
|
||||
val fewerWinningUnits = Vector(
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 12,
|
||||
factionId = Some(winningFid),
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Ranger,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Female
|
||||
),
|
||||
battalion = None,
|
||||
status = UnitStatus.Normal
|
||||
hero = Hero(id = 12, factionId = Some(winningFid)),
|
||||
battalion = Battalion(id = 34),
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
)
|
||||
)
|
||||
|
||||
val result = WonFreeForAllAction(
|
||||
battalions,
|
||||
gameState,
|
||||
province,
|
||||
fewerWinningUnits,
|
||||
winningArmyGroups
|
||||
winningPlayers
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) { case cp: ChangedProvinceC =>
|
||||
cp.newHostileArmies.loneElement.armies.loneElement shouldBe MovingArmy(
|
||||
id = 0,
|
||||
destinationProvinceId = provinceId,
|
||||
originProvinceId = 1,
|
||||
destinationProvinceId = 0,
|
||||
originProvinceId = 0,
|
||||
army = Army(
|
||||
factionId = winningFid,
|
||||
units = Vector(
|
||||
@@ -184,94 +180,103 @@ class WonFreeForAllActionTest extends AnyFlatSpec with Matchers {
|
||||
),
|
||||
arrivalRound = roundId,
|
||||
supplies = Supplies(0, 0),
|
||||
suppliesLoss = 0.0,
|
||||
suppliesLoss = 0,
|
||||
startingPositionIndex = None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
it should "add multiple incoming armies if they are all winners" in {
|
||||
val sameFidWinningArmy = MovingArmy(
|
||||
id = 1,
|
||||
val sameFidWinningArmy = MovingArmyProto(
|
||||
arrivalRound = roundId,
|
||||
army = Army(
|
||||
factionId = winningFid,
|
||||
units = Vector(
|
||||
CombatUnit(heroId = 13, battalionId = None, factionId = winningFid)
|
||||
army = Some(
|
||||
ArmyProto(
|
||||
factionId = winningFid,
|
||||
units = Vector(CombatUnitProto(heroId = 13, battalionId = None))
|
||||
)
|
||||
),
|
||||
destinationProvinceId = provinceId,
|
||||
originProvinceId = 1,
|
||||
supplies = Supplies(0, 0),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
)
|
||||
|
||||
val differentFidWinningArmy = MovingArmy(
|
||||
id = 2,
|
||||
arrivalRound = roundId,
|
||||
army = Army(
|
||||
factionId = 777,
|
||||
units = Vector(
|
||||
CombatUnit(
|
||||
heroId = 14,
|
||||
battalionId = Some(35),
|
||||
factionId = 777
|
||||
)
|
||||
)
|
||||
),
|
||||
destinationProvinceId = provinceId,
|
||||
originProvinceId = 2,
|
||||
supplies = Supplies(0, 0),
|
||||
suppliesLoss = 0.0,
|
||||
startingPositionIndex = None
|
||||
)
|
||||
|
||||
val testBattalions = battalions + (35 -> BattalionC(
|
||||
typeId = BattalionTypeId.LightInfantry,
|
||||
id = 35,
|
||||
size = 100
|
||||
))
|
||||
|
||||
val multipleWinningArmyGroups = Vector(
|
||||
HostileArmyGroup(
|
||||
status = AwaitingDecision,
|
||||
factionId = winningFid,
|
||||
armies = Vector(winningArmy, sameFidWinningArmy)
|
||||
),
|
||||
HostileArmyGroup(
|
||||
status = AwaitingDecision,
|
||||
factionId = 777,
|
||||
armies = Vector(differentFidWinningArmy)
|
||||
)
|
||||
)
|
||||
|
||||
val extendedWinningUnits = winningUnits ++ Vector(
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 13,
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Engineer,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Male
|
||||
val differentFidWinningArmy = MovingArmyProto(
|
||||
arrivalRound = roundId,
|
||||
army = Some(
|
||||
ArmyProto(
|
||||
factionId = 777,
|
||||
units = Vector(CombatUnitProto(heroId = 14, battalionId = Some(35)))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val p = Province(id = provinceId)
|
||||
|
||||
val gs = GameState(
|
||||
provinces = mapifyProvinces(p),
|
||||
battalions = gameState.battalions ++ mapifyBattalions(
|
||||
Battalion(id = 35, size = 100)
|
||||
),
|
||||
factions = mapifyFactions(
|
||||
Faction(
|
||||
id = winningFid,
|
||||
factionRelationships = Vector(
|
||||
FactionRelationship(
|
||||
targetFactionId = 777,
|
||||
relationshipLevel = TRUCE
|
||||
)
|
||||
)
|
||||
),
|
||||
battalion = None,
|
||||
status = UnitStatus.Normal
|
||||
Faction(
|
||||
id = 777,
|
||||
factionRelationships = Vector(
|
||||
FactionRelationship(
|
||||
targetFactionId = winningFid,
|
||||
relationshipLevel = TRUCE
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val wp = Vector(
|
||||
ShardokPlayer(
|
||||
eagleFid = winningFid,
|
||||
isDefender = false,
|
||||
armyGroup = Some(
|
||||
HostileArmyGroup(
|
||||
factionId = winningFid,
|
||||
armies = Vector(winningArmy, sameFidWinningArmy)
|
||||
)
|
||||
)
|
||||
),
|
||||
ShardokPlayer(
|
||||
eagleFid = 777,
|
||||
isDefender = false,
|
||||
armyGroup = Some(
|
||||
HostileArmyGroup(
|
||||
factionId = 777,
|
||||
armies = Vector(differentFidWinningArmy)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val wu = winningUnits ++ Vector(
|
||||
ResolvedEagleUnit(
|
||||
hero = Hero(id = 13),
|
||||
battalion = Battalion(),
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
),
|
||||
ResolvedEagleUnit(
|
||||
hero = HeroC(
|
||||
id = 14,
|
||||
profession = net.eagle0.eagle.model.state.hero.Profession.Paladin,
|
||||
pronounGender = net.eagle0.eagle.model.state.hero.Gender.Female
|
||||
),
|
||||
battalion = None,
|
||||
status = UnitStatus.Normal
|
||||
hero = Hero(id = 14),
|
||||
battalion = Battalion(id = 35),
|
||||
status = UnitStatus.NORMAL_UNIT
|
||||
)
|
||||
)
|
||||
|
||||
val result = WonFreeForAllAction(
|
||||
battalions = testBattalions,
|
||||
battleProvince = province,
|
||||
winningUnits = extendedWinningUnits,
|
||||
winningArmyGroups = multipleWinningArmyGroups
|
||||
startingState = gs,
|
||||
battleProvince = p,
|
||||
winningUnits = wu,
|
||||
winningPlayers = wp
|
||||
).immediateExecute
|
||||
|
||||
inside(result.changedProvinces.head) { case cp: ChangedProvinceC =>
|
||||
@@ -279,9 +284,9 @@ class WonFreeForAllActionTest extends AnyFlatSpec with Matchers {
|
||||
cp.newHostileArmies.flatMap(
|
||||
_.armies
|
||||
) should contain theSameElementsAs Seq(
|
||||
winningArmy,
|
||||
sameFidWinningArmy,
|
||||
differentFidWinningArmy
|
||||
ArmyConverter.fromProto(winningArmy),
|
||||
ArmyConverter.fromProto(sameFidWinningArmy),
|
||||
ArmyConverter.fromProto(differentFidWinningArmy)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-79
@@ -147,12 +147,7 @@ class AttackCommandChooserTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = originProvinceId,
|
||||
availableDestinationProvinces = Vector(destinationProvinceId).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(destinationProvinceId),
|
||||
availableHeroIds = Vector(1, 2, 3, 4),
|
||||
availableBattalions = battalions.map { batt =>
|
||||
BattalionWithFoodCost(
|
||||
@@ -281,77 +276,4 @@ class AttackCommandChooserTest
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"chosenAttackCommand" should "respect river crossing requirements from AvailableDestinationProvince" in {
|
||||
// Create a march command with river crossing requirement
|
||||
val marchCommandWithRiverCrossing = MarchAvailableCommand(
|
||||
actingProvinceId = originProvinceId,
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = originProvinceId,
|
||||
availableDestinationProvinces = Vector(
|
||||
AvailableDestinationProvince(
|
||||
provinceId = destinationProvinceId,
|
||||
requiresRiverCrossing = true
|
||||
)
|
||||
),
|
||||
availableHeroIds = Vector(1, 2, 3, 4),
|
||||
availableBattalions = battalions.map { batt =>
|
||||
BattalionWithFoodCost(
|
||||
battalionId = batt.id,
|
||||
monthlyFood = 15
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Create a game state in winter (should still respect river crossing from proto)
|
||||
val winterGameState = gameState.copy(
|
||||
currentDate = Some(Date(year = 1, month = 1)) // January = winter
|
||||
)
|
||||
|
||||
val attackCommands = AttackCommandChooser.possibleAttackCommands(
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = winterGameState,
|
||||
ac = marchCommandWithRiverCrossing
|
||||
)
|
||||
|
||||
// Verify that the river crossing requirement is taken from the proto
|
||||
attackCommands should have length 1
|
||||
attackCommands.head.requiresRiverCrossing shouldBe true
|
||||
|
||||
// Also test with requiresRiverCrossing = false
|
||||
val marchCommandWithoutRiverCrossing = MarchAvailableCommand(
|
||||
actingProvinceId = originProvinceId,
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = originProvinceId,
|
||||
availableDestinationProvinces = Vector(
|
||||
AvailableDestinationProvince(
|
||||
provinceId = destinationProvinceId,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableHeroIds = Vector(1, 2, 3, 4),
|
||||
availableBattalions = battalions.map { batt =>
|
||||
BattalionWithFoodCost(
|
||||
battalionId = batt.id,
|
||||
monthlyFood = 15
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val attackCommandsNoRiverCrossing =
|
||||
AttackCommandChooser.possibleAttackCommands(
|
||||
actingFactionId = actingFactionId,
|
||||
gameState = winterGameState,
|
||||
ac = marchCommandWithoutRiverCrossing
|
||||
)
|
||||
|
||||
attackCommandsNoRiverCrossing should have length 1
|
||||
attackCommandsNoRiverCrossing.head.requiresRiverCrossing shouldBe false
|
||||
}
|
||||
}
|
||||
|
||||
+1
-6
@@ -692,12 +692,7 @@ class CommandChoiceHelpersTest
|
||||
goldCost = 0,
|
||||
goldAvailable = 0,
|
||||
foodAvailable = 0,
|
||||
availableDestinationProvinces = Vector(3, 4, 6, 7).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(3, 4, 6, 7),
|
||||
availableHeroIds = (1 to 30).toVector,
|
||||
availableBattalions = Vector(),
|
||||
recommendedBattleConfig = None
|
||||
|
||||
+6
-45
@@ -2,7 +2,6 @@ package net.eagle0.eagle.library.util.command_choice_helpers
|
||||
|
||||
import net.eagle0.common.SeededRandom
|
||||
import net.eagle0.eagle.api.available_command.{
|
||||
AvailableDestinationProvince,
|
||||
ImproveAvailableCommand,
|
||||
MarchAvailableCommand,
|
||||
MarchCommandFromOneProvince,
|
||||
@@ -151,12 +150,7 @@ class ExpandCommandSelectorTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 9,
|
||||
availableDestinationProvinces = Vector(10).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(10),
|
||||
availableHeroIds = Vector(1, 2, 3)
|
||||
)
|
||||
)
|
||||
@@ -220,12 +214,7 @@ class ExpandCommandSelectorTest
|
||||
oneProvinceCommands = Vector(
|
||||
MarchCommandFromOneProvince(
|
||||
originProvinceId = 9,
|
||||
availableDestinationProvinces = Vector(10).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
),
|
||||
availableDestinationProvinces = Vector(10),
|
||||
availableHeroIds = Vector(1, 2, 3)
|
||||
)
|
||||
)
|
||||
@@ -466,15 +455,8 @@ class ExpandCommandSelectorTest
|
||||
BattalionWithFoodCost(battalionId = 2, monthlyFood = 50),
|
||||
BattalionWithFoodCost(battalionId = 3, monthlyFood = 50)
|
||||
),
|
||||
availableDestinationProvinces = actingProvince.neighbors
|
||||
.map(_.provinceId)
|
||||
.map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
)
|
||||
.toVector
|
||||
availableDestinationProvinces =
|
||||
actingProvince.neighbors.map(_.provinceId).toVector
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -531,9 +513,9 @@ class ExpandCommandSelectorTest
|
||||
.update(
|
||||
_.oneProvinceCommands.head.availableDestinationProvinces
|
||||
.modify(
|
||||
_.filterNot(adp =>
|
||||
_.filterNot(pid =>
|
||||
marchGameState
|
||||
.provinces(adp.provinceId)
|
||||
.provinces(pid)
|
||||
.rulingFactionId
|
||||
.contains(actingFactionId)
|
||||
)
|
||||
@@ -571,11 +553,6 @@ class ExpandCommandSelectorTest
|
||||
),
|
||||
_.oneProvinceCommands.head.availableDestinationProvinces := Vector(
|
||||
ownedProvince.id
|
||||
).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -616,11 +593,6 @@ class ExpandCommandSelectorTest
|
||||
),
|
||||
_.oneProvinceCommands.head.availableDestinationProvinces := Vector(
|
||||
unownedProvince1.id
|
||||
).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -671,11 +643,6 @@ class ExpandCommandSelectorTest
|
||||
),
|
||||
_.oneProvinceCommands.head.availableDestinationProvinces := Vector(
|
||||
unownedProvince1.id
|
||||
).map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -723,12 +690,6 @@ class ExpandCommandSelectorTest
|
||||
_.oneProvinceCommands.head.availableHeroIds := (1 to 12).toVector,
|
||||
_.oneProvinceCommands.head.availableDestinationProvinces := actingProvince.neighbors
|
||||
.map(_.provinceId)
|
||||
.map(pid =>
|
||||
AvailableDestinationProvince(
|
||||
provinceId = pid,
|
||||
requiresRiverCrossing = false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val selection = ExpandCommandSelector
|
||||
|
||||
Reference in New Issue
Block a user