mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 20:55:44 +00:00
Add focus province quest type (#8701)
* Add focus province quest type * Test focus quest without focus province
This commit is contained in:
@@ -64,6 +64,7 @@ message QuestDetails {
|
||||
StartDroughtQuest start_drought_quest = 47;
|
||||
SpendOnFeastsInProvinceQuest spend_on_feasts_in_province_quest = 48;
|
||||
SpendOnFeastsAcrossRealmQuest spend_on_feasts_across_realm_quest = 49;
|
||||
FocusProvinceQuest focus_province_quest = 50;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,3 +261,7 @@ message RepairDevastationQuest {
|
||||
message ReconSpecificProvincesQuest {
|
||||
repeated int32 target_province_ids = 1;
|
||||
}
|
||||
|
||||
message FocusProvinceQuest {
|
||||
int32 target_province_id = 1;
|
||||
}
|
||||
|
||||
+7
@@ -18,6 +18,7 @@ import net.eagle0.eagle.model.state.quest.{
|
||||
DismissSpecificVassalQuest,
|
||||
ExecutePrisonerQuest,
|
||||
ExilePrisonerQuest,
|
||||
FocusProvinceQuest,
|
||||
MobilizeProvincesQuest,
|
||||
Quest,
|
||||
ReleasePrisonerQuest,
|
||||
@@ -188,6 +189,12 @@ case class CheckForFailedQuestsAction(
|
||||
p.id == targetProvinceId &&
|
||||
p.rulingFactionId == province.rulingFactionId
|
||||
)
|
||||
case FocusProvinceQuest(_, _, targetProvinceId) =>
|
||||
// Fails if we no longer own the target province
|
||||
!provinces.exists(p =>
|
||||
p.id == targetProvinceId &&
|
||||
p.rulingFactionId == province.rulingFactionId
|
||||
)
|
||||
case _ => false
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -31,6 +31,7 @@ import net.eagle0.eagle.model.state.quest.{
|
||||
ExilePrisonerQuest,
|
||||
ExpandToProvincesQuest,
|
||||
FightBeastsAloneQuest,
|
||||
FocusProvinceQuest,
|
||||
GrandArmyQuest,
|
||||
ImproveAgricultureQuest,
|
||||
ImproveEconomyQuest,
|
||||
@@ -277,6 +278,11 @@ case class CheckForFulfilledQuestsAction(
|
||||
Option.when(matchingCount >= q.targetProvinceCount) {
|
||||
uh.withQuest(q.withComponentsFulfilled(q.componentsFulfilled + 1))
|
||||
}
|
||||
case q: FocusProvinceQuest if q.componentsFulfilled < q.componentCount =>
|
||||
val faction = requiredFactionWithId(province.getRulingFactionId)
|
||||
Option.when(faction.focusProvinceId.contains(q.targetProvinceId)) {
|
||||
uh.withQuest(q.withComponentsFulfilled(q.componentsFulfilled + 1))
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
@@ -290,7 +296,8 @@ case class CheckForFulfilledQuestsAction(
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns changed provinces for province order quest progress, to be included in an EndVassalCommandsPhase result.
|
||||
* Returns changed provinces for monthly order and focus quest progress, to be included in an EndVassalCommandsPhase
|
||||
* result.
|
||||
*/
|
||||
def provinceOrderQuestChangedProvinces: Vector[ChangedProvinceC] =
|
||||
provinces
|
||||
|
||||
+2
-2
@@ -31,8 +31,8 @@ case class EndVassalCommandsPhaseAction(
|
||||
functionalRandom = functionalRandom
|
||||
).withActionResults { gs =>
|
||||
val currentDate = gs.currentDate
|
||||
// First, increment province order quest counters. This needs to happen
|
||||
// before the fulfillment check so the updated counters are visible.
|
||||
// First, increment monthly order and focus quest counters. This needs to
|
||||
// happen before the fulfillment check so the updated counters are visible.
|
||||
val checkAction = CheckForFulfilledQuestsAction(
|
||||
gameId = gs.gameId,
|
||||
currentDate = currentDate,
|
||||
|
||||
+6
@@ -388,6 +388,12 @@ case class DivineMessagePromptGenerator(
|
||||
TextGenerationSuccess(
|
||||
s"$divinedHeroName wants to see $targetDevastationRepaired devastation repaired across the realm."
|
||||
)
|
||||
|
||||
case FocusProvinceQuest(componentCount, _, targetProvinceId) =>
|
||||
val targetProvince = gameState.provinces(targetProvinceId)
|
||||
TextGenerationSuccess(
|
||||
s"$divinedHeroName wants ${faction.name} to make ${targetProvince.name} their focus province for a cumulative $componentCount months."
|
||||
)
|
||||
}
|
||||
|
||||
override def generate: TextGenerationResult = for {
|
||||
|
||||
+6
@@ -375,6 +375,12 @@ object QuestEndedGeneratorUtilities {
|
||||
for {
|
||||
unaffiliatedHeroName <- unaffiliatedHeroNameResult
|
||||
} yield s"$unaffiliatedHeroName wanted to see $targetDevastationRepaired devastation repaired across the realm."
|
||||
|
||||
case FocusProvinceQuest(componentCount, _, targetProvinceId) =>
|
||||
val targetProvince = gameState.provinces(targetProvinceId)
|
||||
for {
|
||||
unaffiliatedHeroName <- unaffiliatedHeroNameResult
|
||||
} yield s"$unaffiliatedHeroName wanted ${faction.name} to make ${targetProvince.name} their focus province for a cumulative $componentCount months."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import net.eagle0.eagle.common.unaffiliated_hero_quest.{
|
||||
ExilePrisonerQuest as ExilePrisonerQuestProto,
|
||||
ExpandToProvincesQuest as ExpandToProvincesQuestProto,
|
||||
FightBeastsAloneQuest as FightBeastsAloneQuestProto,
|
||||
FocusProvinceQuest as FocusProvinceQuestProto,
|
||||
GiveToHeroesAcrossRealmQuest as GiveToHeroesAcrossRealmQuestProto,
|
||||
GiveToHeroesInProvinceQuest as GiveToHeroesInProvinceQuestProto,
|
||||
GrandArmyQuest as GrandArmyQuestProto,
|
||||
@@ -268,6 +269,12 @@ object QuestConverter {
|
||||
componentCount = q.componentCount,
|
||||
componentsFulfilled = q.componentsFulfilled
|
||||
)
|
||||
case q: FocusProvinceQuest =>
|
||||
QuestProto(
|
||||
details = FocusProvinceQuestProto(q.targetProvinceId),
|
||||
componentCount = q.componentCount,
|
||||
componentsFulfilled = q.componentsFulfilled
|
||||
)
|
||||
}
|
||||
|
||||
def fromProto(questProto: QuestProto): Quest =
|
||||
@@ -534,6 +541,15 @@ object QuestConverter {
|
||||
componentsFulfilled = questProto.componentsFulfilled,
|
||||
targetDevastationRepaired = targetDevastationRepaired
|
||||
)
|
||||
case FocusProvinceQuestProto(
|
||||
targetProvinceId,
|
||||
_ /* unknownFieldSet */
|
||||
) =>
|
||||
FocusProvinceQuest(
|
||||
componentCount = questProto.componentCount,
|
||||
componentsFulfilled = questProto.componentsFulfilled,
|
||||
targetProvinceId = targetProvinceId
|
||||
)
|
||||
case QuestDetailsProto.Empty =>
|
||||
throw new IllegalArgumentException("Empty quest details")
|
||||
}
|
||||
|
||||
@@ -238,3 +238,12 @@ case class RepairDevastationQuest(
|
||||
override def withComponentsFulfilled(componentsFulfilled: Int): Quest =
|
||||
this.copy(componentsFulfilled = componentsFulfilled)
|
||||
}
|
||||
|
||||
case class FocusProvinceQuest(
|
||||
override val componentCount: Int,
|
||||
override val componentsFulfilled: Int,
|
||||
targetProvinceId: ProvinceId
|
||||
) extends ComponentQuest {
|
||||
override def withComponentsFulfilled(componentsFulfilled: Int): Quest =
|
||||
this.copy(componentsFulfilled = componentsFulfilled)
|
||||
}
|
||||
|
||||
+37
@@ -9,6 +9,7 @@ import net.eagle0.eagle.model.state.quest.{
|
||||
DismissSpecificVassalQuest,
|
||||
ExecutePrisonerQuest,
|
||||
ExilePrisonerQuest,
|
||||
FocusProvinceQuest,
|
||||
ReleasePrisonerQuest,
|
||||
ReturnPrisonerQuest,
|
||||
TruceCountQuest,
|
||||
@@ -697,4 +698,40 @@ class CheckForFailedQuestsActionTest extends AnyFlatSpec with Matchers {
|
||||
)
|
||||
.isQuestFailed(executePrisonerQuest, province) shouldBe false
|
||||
}
|
||||
|
||||
behavior of "focus province quest"
|
||||
|
||||
private val focusProvinceQuest = FocusProvinceQuest(
|
||||
componentCount = 3,
|
||||
componentsFulfilled = 1,
|
||||
targetProvinceId = 47
|
||||
)
|
||||
|
||||
it should "fail if the target province is no longer owned by the faction" in {
|
||||
val targetProvince = ProvinceC(id = 47, rulingFactionId = Some(enemyFactionId))
|
||||
|
||||
CheckForFailedQuestsAction(
|
||||
gameId = gameId,
|
||||
currentDate = date,
|
||||
currentRoundId = roundId,
|
||||
provinces = provinces :+ targetProvince,
|
||||
factions = factions,
|
||||
heroes = Map.empty,
|
||||
heroBackstoryTextIdLookup = hid => s"backstory $hid"
|
||||
).isQuestFailed(focusProvinceQuest, province) shouldBe true
|
||||
}
|
||||
|
||||
it should "not fail while the faction still owns the target province" in {
|
||||
val targetProvince = ProvinceC(id = 47, rulingFactionId = Some(actingFid))
|
||||
|
||||
CheckForFailedQuestsAction(
|
||||
gameId = gameId,
|
||||
currentDate = date,
|
||||
currentRoundId = roundId,
|
||||
provinces = provinces :+ targetProvince,
|
||||
factions = factions,
|
||||
heroes = Map.empty,
|
||||
heroBackstoryTextIdLookup = hid => s"backstory $hid"
|
||||
).isQuestFailed(focusProvinceQuest, province) shouldBe false
|
||||
}
|
||||
}
|
||||
|
||||
+88
@@ -20,6 +20,7 @@ import net.eagle0.eagle.model.state.quest.{
|
||||
BorderSecurityQuest,
|
||||
DevelopProvincesQuest,
|
||||
DismissSpecificVassalQuest,
|
||||
FocusProvinceQuest,
|
||||
GiveToHeroesAcrossRealmQuest,
|
||||
GrandArmyQuest,
|
||||
ImproveAgricultureQuest,
|
||||
@@ -157,6 +158,93 @@ class CheckForFulfilledQuestsActionTest extends AnyFlatSpec with Matchers with B
|
||||
}
|
||||
}
|
||||
|
||||
it should "progress a focus province quest when the target is the faction focus" in {
|
||||
val targetProvinceId = 45
|
||||
val questGiver = UnaffiliatedHeroC(
|
||||
heroId = 83,
|
||||
unaffiliatedHeroType = UnaffiliatedHeroType.Resident,
|
||||
recruitmentInfo = RecruitmentInfo.HasQuest(
|
||||
FocusProvinceQuest(
|
||||
componentCount = 3,
|
||||
componentsFulfilled = 1,
|
||||
targetProvinceId = targetProvinceId
|
||||
)
|
||||
)
|
||||
)
|
||||
val provinceWithQuest = province.copy(unaffiliatedHeroes = Vector(questGiver))
|
||||
|
||||
val changedProvinces = CheckForFulfilledQuestsAction(
|
||||
gameId = gameId,
|
||||
currentDate = date,
|
||||
currentRoundId = roundId,
|
||||
provinces = Vector(provinceWithQuest),
|
||||
factions = Vector(faction.copy(focusProvinceId = Some(targetProvinceId))),
|
||||
battalions = Vector.empty,
|
||||
getHero = _ => None,
|
||||
battalionTypes = battalionTypes,
|
||||
heroBackstoryTextIdLookup = hid => s"backstory_$hid"
|
||||
).provinceOrderQuestChangedProvinces
|
||||
|
||||
inside(changedProvinces.loneElement.changedUnaffiliatedHeroes.loneElement.quest) {
|
||||
case Some(changedQuest: FocusProvinceQuest) =>
|
||||
changedQuest.componentsFulfilled.shouldBe(2)
|
||||
}
|
||||
}
|
||||
|
||||
it should "not progress a focus province quest when another province is the faction focus" in {
|
||||
val questGiver = UnaffiliatedHeroC(
|
||||
heroId = 84,
|
||||
unaffiliatedHeroType = UnaffiliatedHeroType.Resident,
|
||||
recruitmentInfo = RecruitmentInfo.HasQuest(
|
||||
FocusProvinceQuest(
|
||||
componentCount = 3,
|
||||
componentsFulfilled = 1,
|
||||
targetProvinceId = 45
|
||||
)
|
||||
)
|
||||
)
|
||||
val provinceWithQuest = province.copy(unaffiliatedHeroes = Vector(questGiver))
|
||||
|
||||
CheckForFulfilledQuestsAction(
|
||||
gameId = gameId,
|
||||
currentDate = date,
|
||||
currentRoundId = roundId,
|
||||
provinces = Vector(provinceWithQuest),
|
||||
factions = Vector(faction.copy(focusProvinceId = Some(46))),
|
||||
battalions = Vector.empty,
|
||||
getHero = _ => None,
|
||||
battalionTypes = battalionTypes,
|
||||
heroBackstoryTextIdLookup = hid => s"backstory_$hid"
|
||||
).provinceOrderQuestChangedProvinces.shouldBe(empty)
|
||||
}
|
||||
|
||||
it should "not progress a focus province quest when the faction has no focus province" in {
|
||||
val questGiver = UnaffiliatedHeroC(
|
||||
heroId = 85,
|
||||
unaffiliatedHeroType = UnaffiliatedHeroType.Resident,
|
||||
recruitmentInfo = RecruitmentInfo.HasQuest(
|
||||
FocusProvinceQuest(
|
||||
componentCount = 3,
|
||||
componentsFulfilled = 1,
|
||||
targetProvinceId = 45
|
||||
)
|
||||
)
|
||||
)
|
||||
val provinceWithQuest = province.copy(unaffiliatedHeroes = Vector(questGiver))
|
||||
|
||||
CheckForFulfilledQuestsAction(
|
||||
gameId = gameId,
|
||||
currentDate = date,
|
||||
currentRoundId = roundId,
|
||||
provinces = Vector(provinceWithQuest),
|
||||
factions = Vector(faction.copy(focusProvinceId = None)),
|
||||
battalions = Vector.empty,
|
||||
getHero = _ => None,
|
||||
battalionTypes = battalionTypes,
|
||||
heroBackstoryTextIdLookup = hid => s"backstory_$hid"
|
||||
).provinceOrderQuestChangedProvinces.shouldBe(empty)
|
||||
}
|
||||
|
||||
behavior of "component based quest"
|
||||
|
||||
it should "return true if the quest components are fulfilled" in {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
load("@rules_scala//scala:scala.bzl", "scala_test")
|
||||
|
||||
scala_test(
|
||||
name = "quest_converter_test",
|
||||
srcs = ["QuestConverterTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/common:unaffiliated_hero_quest_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:quest_converter",
|
||||
"@rules_scala//scala/scalatest",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package net.eagle0.eagle.model.proto_converters
|
||||
|
||||
import net.eagle0.eagle.model.state.quest.FocusProvinceQuest
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class QuestConverterTest extends AnyFlatSpec with Matchers {
|
||||
"QuestConverter" should "round-trip a focus province quest" in {
|
||||
val quest = FocusProvinceQuest(
|
||||
componentCount = 4,
|
||||
componentsFulfilled = 2,
|
||||
targetProvinceId = 17
|
||||
)
|
||||
|
||||
QuestConverter.fromProto(QuestConverter.toProto(quest)) shouldBe quest
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user