mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 21:35:42 +00:00
Add WinBattlesQuest type (PR 1/3) (#5868)
* Add WinBattlesQuest type (PR 1/3) Adds a new quest type where a hero asks the player to win X battles (where X is 2-4). This is a ComponentQuest that tracks progress. - Proto: Add WinBattlesQuest message and field - Quest.scala: Add WinBattlesQuest case class extending ComponentQuest - QuestConverter: Handle WinBattlesQuest toProto/fromProto - ResolveBattleAction: Increment quest counter when winning battles - CheckForFulfilledQuestsAction: Quest is fulfilled via ComponentQuest handling when componentsFulfilled >= componentCount This quest never fails. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add WinBattlesQuest cases to prompt generators The exhaustive pattern matching on Quest types requires adding cases to all prompt generators when a new quest type is added. Also updates CLAUDE.md to require running full test suite before pushing. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,10 @@ bazel run gazelle # Update Go build files
|
||||
1. **If you modified any BUILD.bazel file:** Run `bazel run gazelle` and stage any changes it makes
|
||||
2. **If you modified C++ or C# files:** Run `clang-format -i` on the modified files
|
||||
3. **If you modified Scala files:** scalafmt will run automatically via pre-commit hook
|
||||
4. **ALWAYS run the full test suite for your language changes before pushing:**
|
||||
- For Scala changes: `bazel test //src/test/scala/...`
|
||||
- For C++ changes: `bazel test //src/test/cpp/...`
|
||||
- This catches exhaustive pattern match errors and other compile-time failures that single-target builds miss
|
||||
|
||||
The pre-commit hook runs gazelle but only checks if it succeeds - it does NOT verify the BUILD files are in canonical format. The `gazelle_test` will fail if deps are not alphabetically sorted. **Always run gazelle manually after BUILD file changes.**
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ message QuestDetails {
|
||||
BetrayAllyQuest betray_ally_quest = 34;
|
||||
BorderSecurityQuest border_security_quest = 35;
|
||||
WinBattleOutnumberedQuest win_battle_outnumbered_quest = 36;
|
||||
WinBattlesQuest win_battles_quest = 37;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,4 +195,7 @@ message BorderSecurityQuest {
|
||||
}
|
||||
|
||||
message WinBattleOutnumberedQuest {
|
||||
}
|
||||
|
||||
message WinBattlesQuest {
|
||||
}
|
||||
+1
@@ -211,6 +211,7 @@ case class CheckForFulfilledQuestsAction(
|
||||
}
|
||||
case WinBattleOutnumberedQuest =>
|
||||
false // action quest - fulfilled in ResolveBattleAction
|
||||
// Note: WinBattlesQuest is handled by ComponentQuest case above
|
||||
}
|
||||
|
||||
private def incrementProvinceOrderQuestProgress(
|
||||
|
||||
+36
-2
@@ -21,7 +21,7 @@ import net.eagle0.eagle.model.state.battalion.{BattalionT, EventForBattalionBack
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.model.state.hero.{EventForHeroBackstory, EventForHeroBackstoryT, HeroT}
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.model.state.quest.{DefeatFactionQuest, Quest, WinBattleOutnumberedQuest}
|
||||
import net.eagle0.eagle.model.state.quest.{DefeatFactionQuest, Quest, WinBattleOutnumberedQuest, WinBattlesQuest}
|
||||
import net.eagle0.eagle.model.state.shardok_battle.{BattleType, ShardokBattle, ShardokPlayer}
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.{RecruitmentInfo, UnaffiliatedHeroType}
|
||||
import net.eagle0.eagle.model.state.unaffiliated_hero.concrete.UnaffiliatedHeroC
|
||||
@@ -157,6 +157,34 @@ case class ResolveBattleAction(
|
||||
case _ => false
|
||||
}
|
||||
|
||||
private def winBattlesQuestIncrementResults(
|
||||
startingState: GameState,
|
||||
winningFids: Set[FactionId]
|
||||
): Vector[ActionResultT] = {
|
||||
val changedProvinces = winningFids.toVector
|
||||
.flatMap(fid => FactionUtils.provinces(fid, startingState.provinces.values.toVector))
|
||||
.flatMap { province =>
|
||||
QuestFulfillmentUtils.counterIncrementedCP(
|
||||
province = province,
|
||||
incrementAmount = 1,
|
||||
check = (uh, _) =>
|
||||
uh.quest.exists {
|
||||
case q: WinBattlesQuest => q.componentsFulfilled < q.componentCount
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Option
|
||||
.when(changedProvinces.nonEmpty) {
|
||||
ActionResultC(
|
||||
actionResultType = BattleEnded,
|
||||
changedProvinces = changedProvinces
|
||||
)
|
||||
}
|
||||
.toVector
|
||||
}
|
||||
|
||||
private def playerTroopCount(
|
||||
player: ShardokPlayer,
|
||||
battalions: Map[BattalionId, BattalionT]
|
||||
@@ -190,7 +218,11 @@ case class ResolveBattleAction(
|
||||
|
||||
val wasOutnumbered = winnerTroopCount < loserTroopCount && winners.nonEmpty
|
||||
|
||||
winners
|
||||
// Increment WinBattlesQuest counters for all winners
|
||||
val winBattlesIncrementResults = winBattlesQuestIncrementResults(startingState, winningFids)
|
||||
|
||||
// Check for quest fulfillments
|
||||
val questFulfillmentResults = winners
|
||||
.flatMap(rsp => FactionUtils.provinces(rsp.eagleFid, startingState.provinces.values.toVector))
|
||||
.flatMap(p =>
|
||||
QuestFulfillmentUtils.fulfilledQuestResults(
|
||||
@@ -206,6 +238,8 @@ case class ResolveBattleAction(
|
||||
currentDate = startingState.currentDate.get
|
||||
)
|
||||
)
|
||||
|
||||
winBattlesIncrementResults ++ questFulfillmentResults
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -288,6 +288,11 @@ case class DivineMessagePromptGenerator(
|
||||
TextGenerationSuccess(
|
||||
s"$divinedHeroName wants to see ${faction.name} prove their valor by winning a battle while outnumbered."
|
||||
)
|
||||
|
||||
case q: WinBattlesQuest =>
|
||||
TextGenerationSuccess(
|
||||
s"$divinedHeroName wants to see ${faction.name} prove their military prowess by winning ${q.componentCount} battles."
|
||||
)
|
||||
}
|
||||
|
||||
override def generate: TextGenerationResult = for {
|
||||
|
||||
+5
@@ -295,6 +295,11 @@ object QuestEndedGeneratorUtilities {
|
||||
for {
|
||||
unaffiliatedHeroName <- unaffiliatedHeroNameResult
|
||||
} yield s"$unaffiliatedHeroName wanted ${faction.name} to prove their valor by winning a battle while outnumbered."
|
||||
|
||||
case q: WinBattlesQuest =>
|
||||
for {
|
||||
unaffiliatedHeroName <- unaffiliatedHeroNameResult
|
||||
} yield s"$unaffiliatedHeroName wanted ${faction.name} to prove their military prowess by winning ${q.componentCount} battles."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ import net.eagle0.eagle.common.unaffiliated_hero_quest.{
|
||||
TruceWithFactionQuest as TruceWithFactionQuestProto,
|
||||
UpgradeBattalionQuest as UpgradeBattalionQuestProto,
|
||||
WealthQuest as WealthQuestProto,
|
||||
WinBattleOutnumberedQuest as WinBattleOutnumberedQuestProto
|
||||
WinBattleOutnumberedQuest as WinBattleOutnumberedQuestProto,
|
||||
WinBattlesQuest as WinBattlesQuestProto
|
||||
}
|
||||
import net.eagle0.eagle.model.state.quest.*
|
||||
import net.eagle0.eagle.model.state.BattalionTypeId
|
||||
@@ -196,6 +197,12 @@ object QuestConverter {
|
||||
QuestProto(details = BorderSecurityQuestProto(q.targetProvinceId))
|
||||
case WinBattleOutnumberedQuest =>
|
||||
QuestProto(details = WinBattleOutnumberedQuestProto())
|
||||
case q: WinBattlesQuest =>
|
||||
QuestProto(
|
||||
details = WinBattlesQuestProto(),
|
||||
componentCount = q.componentCount,
|
||||
componentsFulfilled = q.componentsFulfilled
|
||||
)
|
||||
}
|
||||
|
||||
def fromProto(questProto: QuestProto): Quest =
|
||||
@@ -375,6 +382,11 @@ object QuestConverter {
|
||||
BorderSecurityQuest(targetProvinceId)
|
||||
case WinBattleOutnumberedQuestProto(_ /* unknownFieldSet */ ) =>
|
||||
WinBattleOutnumberedQuest
|
||||
case WinBattlesQuestProto(_ /* unknownFieldSet */ ) =>
|
||||
WinBattlesQuest(
|
||||
componentCount = questProto.componentCount,
|
||||
componentsFulfilled = questProto.componentsFulfilled
|
||||
)
|
||||
case QuestDetailsProto.Empty =>
|
||||
throw new IllegalArgumentException("Empty quest details")
|
||||
}
|
||||
|
||||
@@ -158,3 +158,11 @@ case class BetrayAllyQuest(targetFactionId: FactionId) extends Quest
|
||||
case class BorderSecurityQuest(targetProvinceId: ProvinceId) extends Quest
|
||||
|
||||
case object WinBattleOutnumberedQuest extends Quest
|
||||
|
||||
case class WinBattlesQuest(
|
||||
override val componentCount: Int,
|
||||
override val componentsFulfilled: Int
|
||||
) extends ComponentQuest {
|
||||
override def withComponentsFulfilled(componentsFulfilled: Int): Quest =
|
||||
this.copy(componentsFulfilled = componentsFulfilled)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user