mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 20:55:44 +00:00
Prevent remote marches from acted provinces (#8743)
* Prevent remote marches from acted provinces * Clear vassal action flags before player commands * Preserve leader action flags after vassal phase
This commit is contained in:
+1
@@ -122,6 +122,7 @@ object AvailableMarchCommandFactory extends ScalaAvailableCommandsFactory {
|
||||
): Option[AvailableCommand] =
|
||||
HeroUtils
|
||||
.consideredProvinces(gameState, factionId, provinceId)
|
||||
.filter(p => p.id == provinceId || !p.hasActed)
|
||||
.flatMap(p => oneOriginProvinceCommand(gameState = gameState, originProvince = p)) match {
|
||||
case items if items.isEmpty => None
|
||||
case opcs =>
|
||||
|
||||
@@ -574,6 +574,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_random_sequential_results_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/random_state_sequencer",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province",
|
||||
"//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",
|
||||
|
||||
+7
@@ -4,7 +4,9 @@ import net.eagle0.common.{FunctionalRandom, RandomState}
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultApplier
|
||||
import net.eagle0.eagle.library.actions.impl.common.ProtolessRandomSequentialResultsAction
|
||||
import net.eagle0.eagle.library.actions.random_state_sequencer.RandomStateSequencer
|
||||
import net.eagle0.eagle.library.util.province.ProvinceUtils
|
||||
import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC}
|
||||
import net.eagle0.eagle.model.action_result.types.ActionResultType.EndVassalCommandsPhase
|
||||
import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
@@ -111,6 +113,11 @@ case class EndVassalCommandsPhaseAction(
|
||||
ActionResultC(
|
||||
actionResultType = EndVassalCommandsPhase,
|
||||
newRoundPhase = Some(RoundPhase.PlayerCommands),
|
||||
changedProvinces = gs.provinces.values
|
||||
.filter(_.hasActed)
|
||||
.filterNot(ProvinceUtils.ruledByFactionLeader(_, gs))
|
||||
.map(p => ChangedProvinceC(provinceId = p.id, setHasActed = Some(false)))
|
||||
.toVector,
|
||||
removedNotifications = gs.deferredNotifications.map(_.withDeferred(true)),
|
||||
newNotifications = gs.deferredNotifications.map(_.withDeferred(false)),
|
||||
changedFactions = staleFocusClears
|
||||
|
||||
+22
-7
@@ -704,7 +704,7 @@ class AvailableMarchCommandFactoryTest extends AnyFlatSpec with BeforeAndAfterEa
|
||||
destination.requiresRiverCrossing shouldBe a[Boolean]
|
||||
}
|
||||
|
||||
it should "leave no units behind for a same-origin support attack" in {
|
||||
it should "leave no units behind and exclude the acted origin from remote marches" in {
|
||||
val originProvinceId = 2
|
||||
val destinationProvinceId = 5
|
||||
val factionHeadProvinceId = 14
|
||||
@@ -791,10 +791,10 @@ class AvailableMarchCommandFactoryTest extends AnyFlatSpec with BeforeAndAfterEa
|
||||
initialMarch.marchingUnits.map(_.heroId) should contain(localLeaderId)
|
||||
initialMarch.marchingUnits.flatMap(_.battalionId).toSet shouldBe attackBattalions.map(_.id).toSet
|
||||
|
||||
val commandForOrigin = initialAvailable.oneProvinceCommands
|
||||
val commandForOrigin = initialAvailable.oneProvinceCommands
|
||||
.find(_.originProvinceId == originProvinceId)
|
||||
.get
|
||||
val marchResult = MarchCommand
|
||||
val marchResult = MarchCommand
|
||||
.make(
|
||||
actingFactionId = actingPlayerId,
|
||||
actingProvinceId = initialAvailable.actingProvinceId,
|
||||
@@ -812,22 +812,37 @@ class AvailableMarchCommandFactoryTest extends AnyFlatSpec with BeforeAndAfterEa
|
||||
currentRoundId = gameState.currentRoundId
|
||||
)
|
||||
.immediateExecute
|
||||
val stateAfterMarch = ActionResultApplierImpl(None)
|
||||
val stateAfterMarch = ActionResultApplierImpl(None)
|
||||
.applyActionResult(gameState, marchResult)
|
||||
.resultingState
|
||||
val supportAvailable = getMarchCommand(
|
||||
val actedOrigin = inside(stateAfterMarch.provinces(originProvinceId)) {
|
||||
case province: ProvinceC => province
|
||||
}
|
||||
val stateWithUnactedOrigin = stateAfterMarch.copy(
|
||||
provinces = stateAfterMarch.provinces.updated(
|
||||
originProvinceId,
|
||||
actedOrigin.copy(hasActed = false)
|
||||
)
|
||||
)
|
||||
val supportAvailable = getMarchCommand(
|
||||
AvailableMarchCommandFactory.availableCommand(
|
||||
stateAfterMarch,
|
||||
stateWithUnactedOrigin,
|
||||
actingPlayerId,
|
||||
factionHeadProvinceId
|
||||
)
|
||||
)
|
||||
|
||||
actedOrigin.hasActed shouldBe true
|
||||
supportAvailable.oneProvinceCommands.map(_.originProvinceId) should contain(originProvinceId)
|
||||
AttackCommandChooser.chosenSupportAttackCommand(
|
||||
actingFactionId = actingPlayerId,
|
||||
gameState = stateAfterMarch,
|
||||
gameState = stateWithUnactedOrigin,
|
||||
acs = Vector(supportAvailable)
|
||||
) shouldBe empty
|
||||
AvailableMarchCommandFactory.availableCommand(
|
||||
stateAfterMarch,
|
||||
actingPlayerId,
|
||||
factionHeadProvinceId
|
||||
) shouldBe empty
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,6 +282,7 @@ scala_test(
|
||||
"//src/main/scala/net/eagle0/common:functional_random",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/applier:action_result_applier_impl",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/action:end_vassal_commands_phase_action",
|
||||
"//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_faction_concrete",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/types",
|
||||
|
||||
+49
@@ -2,6 +2,7 @@ package net.eagle0.eagle.library.actions.impl.action
|
||||
|
||||
import net.eagle0.common.SeededRandom
|
||||
import net.eagle0.eagle.library.actions.applier.ActionResultApplierImpl
|
||||
import net.eagle0.eagle.model.action_result.changed_province.concrete.ChangedProvinceC
|
||||
import net.eagle0.eagle.model.action_result.concrete.{ActionResultC, ChangedFactionC}
|
||||
import net.eagle0.eagle.model.action_result.types.ActionResultType.EndVassalCommandsPhase
|
||||
import net.eagle0.eagle.model.state.{GameType, RoundPhase}
|
||||
@@ -64,6 +65,54 @@ class EndVassalCommandsPhaseActionTest extends AnyFlatSpec with Matchers {
|
||||
}.getOrElse(fail("expected a PlayerCommands transition action result"))
|
||||
|
||||
"EndVassalCommandsPhaseAction" should
|
||||
"clear hasActed only for vassal-ruled provinces before entering PlayerCommands" in {
|
||||
val leaderId = 1
|
||||
val vassalId = 2
|
||||
val factionId = 3
|
||||
val actedVassal = ProvinceC(
|
||||
id = 5,
|
||||
rulingFactionId = Some(factionId),
|
||||
rulingHeroId = Some(vassalId),
|
||||
rulingFactionHeroIds = Vector(vassalId),
|
||||
hasActed = true
|
||||
)
|
||||
val unactedVassal = actedVassal.copy(id = 17, hasActed = false)
|
||||
val actedLeader = ProvinceC(
|
||||
id = 23,
|
||||
rulingFactionId = Some(factionId),
|
||||
rulingHeroId = Some(leaderId),
|
||||
rulingFactionHeroIds = Vector(leaderId),
|
||||
hasActed = true
|
||||
)
|
||||
val faction = FactionC(
|
||||
id = factionId,
|
||||
factionHeadId = leaderId,
|
||||
name = "Faction",
|
||||
leaderIds = Vector(leaderId)
|
||||
)
|
||||
val heroes = Map(
|
||||
leaderId -> HeroC(id = leaderId, factionId = Some(factionId)),
|
||||
vassalId -> HeroC(id = vassalId, factionId = Some(factionId))
|
||||
)
|
||||
|
||||
val results = EndVassalCommandsPhaseAction(
|
||||
makeGameState(
|
||||
provinces = Map(5 -> actedVassal, 17 -> unactedVassal, 23 -> actedLeader),
|
||||
factions = Map(factionId -> faction),
|
||||
heroes = heroes
|
||||
),
|
||||
actionResultApplier
|
||||
).randomResults(seededRandom).newValue
|
||||
|
||||
val transition = endVassalResult(results)
|
||||
inside(transition.changedProvinces.loneElement) {
|
||||
case cp: ChangedProvinceC =>
|
||||
cp.provinceId shouldBe 5
|
||||
cp.setHasActed should contain(false)
|
||||
}
|
||||
}
|
||||
|
||||
it should
|
||||
"clear focusProvinceId when a faction's focus points to an unruled province" in {
|
||||
// Faction 1 still rules its home province (5) but its focus (17) is unruled.
|
||||
val hero = HeroC(id = 1, factionId = Some(1))
|
||||
|
||||
Reference in New Issue
Block a user