Commit Graph
100 Commits
Author SHA1 Message Date
adminandClaude Opus 4.5 8a0b7d406b Add AI quest fulfillment for 7 more quest types
New quest command choosers:
- SpendOnFeastsQuestCommandChooser: Selects FeastCommand when quest active
- RestProvinceQuestCommandChooser: Rests in target province
- SendSuppliesQuestCommandChooser: Sends food to target province
- ExecutePrisonerQuestCommandChooser: Executes specific prisoner
- ReturnPrisonerQuestCommandChooser: Returns prisoner to faction
- StartEpidemicQuestCommandChooser: Starts epidemic in target province
- SwearBrotherhoodQuestCommandChooser: Swears brotherhood with target hero

Also adds Execute and Return helper methods to ManagePrisonersCommandSelector.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 16:37:46 -08:00
be1f9cc67c Fix SendSuppliesQuest not incrementing when sending supplies (#5942)
SendSuppliesCommand was missing quest fulfillment logic. This adds
the incrementing of SendSuppliesQuest progress using the existing
QuestFulfillmentUtils pattern from RestCommand.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 06:59:59 -08:00
5e173d55ce Add narrow layout support for March command battalions (#5941)
* Add narrow layout support for March command battalions

- Add battalionsColumnNarrow view for narrow screen layouts
- Add availableBattalionsContentNarrow to EventBasedUnitSelector for dual population
- Add PopulateBattalionsContent() helper to populate both content areas
- Toggle between original and narrow battalion views based on aspect ratio
- Keep heroes column always visible (user may march from different province)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix selectionChanged event binding on March unit selector

Clear the erroneous binding to AddTargetedBattalion - the original
SelectionChanged method was removed in 2022 and the binding should
be empty.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 22:32:54 -08:00
0830ccf5d9 Consolidate Overlay Canvas into Shardok Canvas (#5940)
Remove the separate Overlay Canvas and move overlay content into the
main Shardok Canvas. This eliminates the need to duplicate view hierarchy
between the two canvases.

- Remove overlayCanvas reference from HexGrid
- Remove Start() that was setting overlayCanvas.overrideSorting
- Move overlay content to be a sibling after hex grid in Shardok Canvas

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 17:26:14 -08:00
776790e44d Add StartDroughtQuest fulfillment (PR3 of 3) (#5935)
Add quest fulfillment logic to ControlWeatherCommand:
- StartDroughtQuest is fulfilled when a Mage uses ControlWeather to start
  a drought in the target province
- StartBlizzardQuest is also now fulfilled via the same mechanism
- Both quest types were previously marked as "action quest - fulfilled in
  ControlWeatherCommand" but the implementation was missing

Changes:
- ControlWeatherCommand now extends ProtolessSequentialResultsAction instead
  of ProtolessSimpleAction to support returning quest fulfillment results
- Added QuestFulfillmentChecker and allProvinces parameters to ControlWeatherCommand
- Updated CommandFactory to pass new parameters
- Updated tests to use .results instead of .immediateExecute

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 17:16:58 -08:00
34ff1b9a1b Add StartDroughtQuest creation (PR2 of 3) (#5934)
Add quest creation logic for StartDroughtQuest, following the same pattern
as StartBlizzardQuest:
- Requires a Mage in the faction
- Targets provinces reachable by Control Weather (faction provinces + neighbors)
- Filters out provinces that already have a drought

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 17:16:15 -08:00
70079782ec Add AI quest completion for prisoner management quests (#5933)
Implement prisoner management quest choosers that enable the AI to
complete ReleasePrisonerQuest, ExilePrisonerQuest, and
ReleaseAllPrisonersQuest.

New components:
- ManagePrisonersCommandSelector: Reusable selector for prisoner
  management commands (release, exile, return)
- ReleasePrisonerQuestCommandChooser: Releases specific prisoner
- ExilePrisonerQuestCommandChooser: Exiles specific prisoner
- ReleaseAllPrisonersQuestCommandChooser: Releases any available
  prisoner to make progress toward quest completion

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 17:15:47 -08:00
d514606b00 Add StartDroughtQuest client display (PR2 of 3) (#5939)
Add client-side display code for StartDroughtQuest:
- DisplayNames.cs: Add "Start Drought" quest type string
- UnaffiliatedHeroRowController.cs: Add quest description "Start a drought in {provinceName}"

Following the 3-PR approach from docs/ADDING_NEW_QUESTS.md:
- PR1 #5924: Types (merged)
- PR2: Client handling (this PR)
- PR3 #5934/#5935: Quest generation and fulfillment

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 16:50:23 -08:00
61df596aa1 Fix hex grid centering to eliminate magic Y=32 offset (#5938)
HexMetrics: Properly center the grid vertically by setting center.y to
0.75*OuterRadius. Previously the grid center ended up at -0.75*OuterRadius
instead of 0, requiring a compensating Y=32 offset on GameObjects.

Added detailed comments explaining the centering math for future reference.

HexGrid: Remove the OuterRadius/2 vertical offsets from overlay elements
(text labels, unit type images, profession images, secondary images,
terrain modifier images) that were compensating for the old centering bug.

Unity: Set Hex Grid and Overlay Mesh Y positions to 0.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 16:47:44 -08:00
3e33c3c932 Shardok widescreen layout support (#5937)
* Update Shardok layout for widescreen support

Unity scene changes for Shardok layout variants.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Set Weather Canvas screen overlay alpha to 0 in editor

The overlay color is set programmatically at runtime, so setting it
transparent in the editor makes it easier to work with the scene.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update Shardok layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update Shardok layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add overlay container spacer references for widescreen layout

Add overlayContainerTopSpacer and overlayContainerLeftSpacer references:
- Top spacer active on normal/narrow screens (with top row)
- Left spacer active on widescreen (with left column)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add vertical offset to overlay elements for new layout

Move all overlay elements (labels, unit type images, profession images,
secondary images, terrain modifier images) up by metrics.OuterRadius/2
to align with hex cells in the new layout group structure.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Simplify Shardok mesh and hierarchy structure

- Remove -90 rotation requirement from HexMesh by swapping Y/Z coordinates
- HexMesh now uses Y for vertical position, Z for depth (was reversed)
- HexMetrics corners updated to match new coordinate system
- Replace gridCanvas with gridTransform reference in HexGrid
- Update all animators to work with simplified hierarchy

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Consolidate HexGrid onto HexMesh GameObject

- Merge HexGrid component onto the HexMesh GameObject (renamed to Hex Grid)
- Remove redundant separate Hex Grid GameObject
- Move Overlay Mesh under Overlay Content to match Hex Grid hierarchy
- Fix Overlay Mesh transform to match Hex Grid (Y=32, no rotation)
- Update component references

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 16:35:41 -08:00
88d3b37a4c Add AI quest completion for TotalDevelopmentQuest (#5932)
Implement TotalDevelopmentQuestCommandChooser that enables the AI to
complete TotalDevelopmentQuest by improving the province until the
total development reaches the quest target.

The chooser:
- Prioritizes repairing devastation if total devastation >= 4
- Otherwise improves the province's lowest development stat (economy,
  agriculture, or infrastructure)
- Uses existing ImproveCommandSelector for command generation

Also reorders quest choosers in FulfillQuestsCommandSelector:
- Move TotalDevelopment right after Improve (higher priority)
- Move Alliance to end of list (lower priority)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 09:48:58 -08:00
794a1fb503 Add automatic failover to backup LLM providers on 5xx errors (#5931)
When the primary LLM provider returns a 5xx error (like 503 Service
Unavailable), the system now automatically fails over to backup providers.

Key changes:
- Add ServerError case to ExternalTextGenerationError for 5xx detection
- OkHttpSseListener now creates appropriate error types based on HTTP status
- ApiKeys gains hasOpenAI/hasAnthropic/hasGemini and availableProviders methods
- LlmResolver tracks provider health with circuit breaker pattern:
  - 3 consecutive 5xx failures marks provider unhealthy for 5 minutes
  - Automatic failover tries providers in order: primary → openai → claude → gemini
  - All failover events are logged with [LLM] prefix

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 08:19:23 -08:00
97c243fa95 Add StartDroughtQuest types (PR1 of 3) (#5924)
Add StartDroughtQuest similar to StartBlizzardQuest and StartEpidemicQuest.
This is PR1 of the 3-PR pattern for new quests (types only).

Changes:
- Proto: Added StartDroughtQuest message and field to QuestDetails oneof
- Scala: Added StartDroughtQuest case class
- Converter: Added toProto/fromProto cases
- Fulfillment: Added case (returns false - action quest)
- Failure: Added case (returns false - never fails)
- LLM prompts: Added descriptions for DivineMessage and QuestEnded
- DivineCommand: Added province ID extraction for notifications

Note: Also added missing StartEpidemicQuest cases in DivineCommand.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 08:00:10 -08:00
e0e0f88358 Fix Shardok reconnect bug passing empty NewGameRequest (#5930)
On stream disconnect, scheduleReconnect was passing None for newGameRequest
instead of using the stored request from pendingBattles. This caused an
infinite error loop when Shardok lost the game state (e.g., after a pod
restart) because it couldn't recreate the game without the map path.

The fix retrieves the stored NewGameRequest and passes it on reconnect,
allowing Shardok to recreate the game from scratch if needed.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-07 07:40:07 -08:00
8e541e6d11 Restrict iOS app to iPad only (#5929)
The UI is too small on iPhone screens. Change targetDevice from 2
(Universal) to 1 (iPad only) so the app only appears in the App Store
for iPad users.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 22:58:53 -08:00
7afbde69f1 Fix TestFlight upload for Xcode 14+ (#5927)
Replace deprecated altool (removed in Xcode 14) with xcodebuild
-exportArchive using App Store Connect API authentication.

Changes:
- upload_testflight.sh: Use xcodebuild with destination=upload
  and API key authentication instead of altool
- ios_testflight.yml: Pass xcarchive path and use new API key secrets

Required new GitHub secrets:
- APP_STORE_CONNECT_API_KEY_ID
- APP_STORE_CONNECT_API_ISSUER_ID
- APP_STORE_CONNECT_API_KEY (contents of .p8 file)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 22:58:43 -08:00
e02afd112e Add documentation for AI quest completion behavior (#5928)
Documents which quests the AI proactively attempts to complete via
FulfillQuestsCommandSelector and which quests are not handled.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 22:49:23 -08:00
20b82af479 Allow Feast command when no heroes need vigor/loyalty boost (#5926)
Previously, Feast was only available when at least one hero had vigor < constitution
or loyalty < 100. This prevented players from completing SpendOnFeastsQuest when
all heroes were already at full stats.

Now Feast is available whenever the province has enough gold.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 22:37:01 -08:00
282b5fefa9 Add right-click province selection for Move in Manage Prisoners (#5923)
Override TargetedProvince to allow selecting the Move destination by
right-clicking a province on the map. When a valid move destination is
clicked, the Move toggle is enabled and the dropdown is set to that
province.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:47:26 -08:00
9549cf32e4 Fix RestProvinceQuest not incrementing when resting (#5925)
* Fix RestProvinceQuest not incrementing when resting

RestProvinceQuest is a ComponentQuest that should increment progress when the
player uses the Rest command in the target province. The increment logic was
missing.

Changes:
- RestCommand: Added quest increment logic to check for RestProvinceQuest
  where targetProvinceId matches the resting province
- CommandFactory: Pass factionProvinces to RestCommand.make
- BUILD.bazel: Added quest and unaffiliated_hero dependencies

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix RestCommandTest to pass factionProvinces parameter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 21:40:22 -08:00
a1df9e87ee Allow ally-controlled provinces to count for BorderSecurityQuest (#5922)
The "secure borders" quest (BorderSecurityQuest) now counts provinces owned
by allies (actual alliances, not just truces) towards fulfillment. Previously,
only provinces directly controlled by the quest faction would count.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:56:42 -08:00
0bd6319c29 Fix ReconProvincesQuest and ReconSpecificProvincesQuest progress not incrementing (#5921)
When a recon succeeds, the quest counter wasn't being updated. Added logic to
PerformReconResolutionAction to increment quest progress using the existing
QuestFulfillmentUtils.withCountersIncremented pattern.

- ReconProvincesQuest: increments by 1 for any successful recon
- ReconSpecificProvincesQuest: increments by 1 if the reconned province is a target

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:41:33 -08:00
608a5fc0a5 Fix SendSuppliesQuest immediately failing (#5920)
The failure check had inverted logic - it was checking if the target
province is owned by the same faction (which is always true at creation)
instead of checking if we no longer own it. Now uses the same logic as
RestProvinceQuest.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:15:33 -08:00
201f637843 Fix alliance resolution command selector crash (#5918)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 19:49:11 -08:00
fa43fd6f48 Fix RepairDevastationQuest progress not incrementing (#5919)
ImproveCommand was not updating quest progress when repairing devastation.
Added quest progress tracking similar to how FeastCommand handles
SpendOnFeastsQuest - increments componentsFulfilled for any unaffiliated
hero with a RepairDevastationQuest across all faction provinces.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 19:48:35 -08:00
37f9acf847 Add hero backstory popup on hover for free heroes (#5917)
* Add hero backstory popup on hover for free heroes

Add LongHoverRowChanged handler to FreeHeroesTableController to show
the hero backstory popup when hovering over unaffiliated heroes, matching
the behavior of resident heroes in HeroesAndBattalionsPanelController.

Unity wiring needed:
- Wire popupPanel, popupPanelDetailsController, popupPanelBackstory refs
- Set LongHoverRowChangedHandler on unaffiliatedHeroesTable to call
  FreeHeroesTableController.LongHoverRowChanged

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Wire up free hero backstory popup in Unity scene

Connect popup panel references and LongHoverRowChangedHandler for
unaffiliated heroes table.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add TableRowHoverDetector to UnaffiliatedHeroRow prefab

Required for hover detection to work on free hero rows.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 19:33:29 -08:00
2ce4c7f0fb Restrict blizzard and epidemic quests by hero profession (#5915)
Blizzard quests (StartBlizzardQuest) now require the faction to have a
Mage hero, and epidemic quests (StartEpidemicQuest) require a Necromancer.
This matches the profession requirements for the corresponding actions.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 19:14:03 -08:00
9cd57390e7 Handle missing user in streamUpdates gracefully (#5916)
Instead of throwing NoSuchElementException when a userId is not found
in userIdToFactionId (which can happen after server restart), send an
UNAUTHENTICATED error to the client so it knows to reconnect.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 19:13:15 -08:00
a1b26841dc Fix PhysX mesh collider error on empty hex mesh (#5914)
Defer mesh collider assignment to the next frame to avoid PhysX cooking
errors during initial Shardok setup. The error only occurs on the first
battle after launch, suggesting a timing issue with mesh initialization.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 18:47:16 -08:00
adminandGitHub 61a2d2d988 Shardok layout variants for widescreen support (#5913) 2026-02-06 17:15:03 -08:00
cd5aed4cde Minor layout and notification panel updates (#5912)
* Minor layout update

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update notification panel layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 16:35:11 -08:00
f1c825b37e Update diplomacy and quest panel layouts (#5911)
* Update Please Recruit Me quest UI layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update Ransom Offer Panel UI layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update Alliance, Break Alliance, and Truce offer panel layouts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 16:14:28 -08:00
6d68d5dc8b Update Recon, Start Epidemic, and Swear Brotherhood quest UI layouts (#5910)
* Update Recon quest UI layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update Start Epidemic quest UI layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update Swear Brotherhood quest UI layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 15:33:35 -08:00
748d5afee2 Show province count progress in Develop/Mobilize quest descriptions (#5909)
Quest descriptions now show both the current province count and months
completed, e.g., "Maintain 3 provinces with Develop orders for 4 months
(2/3 provinces, 1/4 months)"

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 15:19:46 -08:00
c1bbb11edc Convert Manage Prisoners to button-style toggles and update Ransom Panel layout (#5908)
* Convert Manage Prisoners to button-style toggles

- Add ToggleGroup and ConfigureToggle for consistent toggle styling
- Remove individual RawImage icon references
- Use CanvasGroup alpha for disabled state styling (35%)
- Default selection priority: Release > Move > Exile > Execute > Return
- Move dropdown only visible when Move option is selected

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update Ransom Panel layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 15:08:47 -08:00
1e9953bc56 Add ReconSpecificProvincesQuest generation (#5901)
- Add reconSpecificProvincesQuests to QuestCreationUtils
- Creates quests with 2-4 specific province IDs to recon
- Targets provinces not controlled by the faction

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 13:37:52 -08:00
647ad25bb4 Add SpendOnFeastsQuest generation (#5893)
* Add SpendOnFeastsQuest generation

- Add spendOnFeastsQuests to QuestCreationUtils that creates quests
  requiring 2-4 feasts spending 200-500 gold total
- Modify FeastCommand to track quest progress when feasts are held
- Pass factionLeaderProvinces to FeastCommand from CommandFactory
- Add quest_fulfillment_utils, quest, and unaffiliated_hero deps

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix FeastCommandTest to pass factionLeaderProvinces parameter

The FeastCommand.make method was updated to require a factionLeaderProvinces
parameter for quest progress tracking, but the test file wasn't updated.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Simplify SpendOnFeastsQuest to track total gold spent

- Remove factionLeaderProvinces parameter from FeastCommand
- Track gold spent (componentCount = totalGold, increment by goldCost per feast)
- Only check current province for quest progress
- Simplify quest creation (single random for totalGold target)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Change SpendOnFeastsQuest target gold range to 500-1000

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 13:26:46 -08:00
7221ee48d6 Improve DivineCommandTest stability for notification assertions (#5907)
Check each notification's field associations individually while ignoring
affectedProvinceIds, which varies based on random quest assignment. This
prevents the test from failing every time a new quest type is added.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 13:15:01 -08:00
6bd0934144 Add RepairDevastationQuest generation (#5897)
- Add repairDevastationQuests to QuestCreationUtils
- Creates quests requiring repair of 30-100 devastation points
- Update test expectations for changed random sequence

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 12:52:23 -08:00
b656ba4518 Add ReconProvincesQuest generation (#5896)
* Add ReconProvincesQuest generation

- Add reconProvincesQuests to QuestCreationUtils
- Creates quests requiring reconnaissance of 3-6 provinces
- Update test expectations for changed random sequence

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix DivineCommandTest expected affectedProvinceIds

The random sequence changed with the addition of reconProvincesQuests,
causing the quest generation to produce different quests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:25:56 -08:00
f4902727a5 Optimize BUILD.bazel dependency check and run lint in parallel (#5906)
1. Speed up check_build_deps.sh:
   - Cache expensive `bazel query deps(...)` results
   - Reuse cached deps for all three checks instead of running
     separate bazel query commands
   - Use grep filtering on cached results instead of bazel intersect

2. Run lint job in parallel with test job:
   - Split bazel_test.yml into separate lint and test jobs
   - Jobs run concurrently, reducing total CI time

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:24:09 -08:00
7aee0ee789 Add documentation for rules_apple workspace separation (#5905)
Documents why SparklePlugin is built in a separate workspace due to
rules_swift version conflicts, and provides a checklist for when/how
to reintegrate it into the main workspace.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:09:36 -08:00
961a7c81b2 Disable flaky abstract_mcts_ai_test (#5904)
MCTS is not currently in use. Mark the test as manual to exclude it
from normal test runs.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:09:07 -08:00
d675758a9e Add RestProvinceQuest generation (#5895)
* Add RestProvinceQuest generation

- Add restProvinceQuests to QuestCreationUtils
- Creates quests requiring 2-4 months of rest on faction provinces
- Update test expectations for changed random sequence

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix GrandArmyQuest test to use >= instead of >

The test was using `> 5700` but with the changed random sequence from
adding RestProvinceQuest, the generated value is exactly 5700. Using
`>=` is still valid since the test checks that the quest requests an
army larger than the current count.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:05:04 -08:00
2864bae977 Convert Handle Captured Hero to button-style toggles (#5903)
- Add ToggleGroup and ConfigureToggle for consistent toggle styling
- Remove individual RawImage icon references
- Use CanvasGroup alpha for disabled state styling (35%)
- Default selection priority: Recruit > Imprison > Exile > Execute > Return

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 10:15:31 -08:00
b41b937662 Add Crack Down UI and button-style toggles for decision selectors (#5902)
* Add Crack Down UI

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add button-style toggles to Free For All Decision selector

Match the Improve command selector pattern with ToggleGroup and
CanvasGroup for proper disabled state styling.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Convert Attack Decision to button-style toggles

- Add ToggleGroup and ConfigureToggle for consistent toggle styling
- Replace individual icon/slider/label references with tributeContainer
- Use CanvasGroup alpha for disabled state styling (35%)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Adjust Attack Decision spacing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 09:49:25 -08:00
201fa30acd Rename ReconSpecificProvincesQuest to ReconProvincesQuest and add new ReconSpecificProvincesQuest (#5898)
* Rename ReconSpecificProvincesQuest to ReconProvincesQuest and add new ReconSpecificProvincesQuest

- Rename existing ReconSpecificProvincesQuest (count-based) to ReconProvincesQuest
- Add new ReconSpecificProvincesQuest that tracks specific province IDs to recon
- Update proto definitions, Quest.scala, QuestConverter.scala
- Update LLM prompt generators for both quest types
- Update CheckForFulfilledQuestsAction comment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update C# client for ReconProvincesQuest rename and new ReconSpecificProvincesQuest

- Update DisplayNames.cs with renamed and new quest types
- Update UnaffiliatedHeroRowController.cs to handle both quest types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 08:23:13 -08:00
df1923f076 Improve Issue Orders panel layout and tutorial (#5900)
- Layout improvements to Issue Orders panel
- Update tutorial to explain focus province receives excess vassal supplies

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 07:20:05 -08:00
721382a9e9 Add SendSuppliesQuest generation (#5894)
- Add sendSuppliesQuests to QuestCreationUtils
- Creates quests to send 1000-3000 food to other faction provinces
- Only available if faction has at least 2 provinces

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 06:38:21 -08:00
f006618060 Add StartEpidemicQuest generation (#5888)
Add startEpidemicQuests function to QuestCreationUtils to generate
StartEpidemicQuest for provinces that don't already have an epidemic.

Update DivineCommandTest expectations for new random sequence.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 22:46:12 -08:00
93760dbfb4 Improve Issue Orders panel layout (#5892)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 22:40:20 -08:00
59a0632af2 Add client UI for new quest types (#5887)
Add display names and quest descriptions for:
- StartEpidemicQuest: "Start Epidemic" - Start an epidemic in {province}
- SpendOnFeastsQuest: "Host Feasts" - Host feasts costing X gold
- SendSuppliesQuest: "Send Supplies" - Send X food to {province}
- RestProvinceQuest: "Rest Province" - Keep {province} at rest for X months
- ReconSpecificProvincesQuest: "Recon Provinces" - Recon X provinces
- RepairDevastationQuest: "Repair Devastation" - Repair X devastation

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 22:31:07 -08:00
3c17e89f02 Add bug report system enhancements (#5863)
* Add bug report system enhancements

- Add "Report Bug" button on exception popup with pre-populated info
- Add prompt to include recent exceptions when opening bug report manually
- Add screenshot capture button in bug report panel with Discord upload

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Simplify exception handling: auto-include instead of prompting

Remove exception prompt UI and automatically include recent exceptions
in the bug report description when opening from settings menu.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Wire up bug report UI components in Gameplay scene

- ErrorHandler: reportBugButton, bugReportPanel reference
- BugReportPanelController: screenshot button, status text, preview

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix screenshot preview layout: hide container not just RawImage

Add screenshotPreviewContainer reference to hide/show the entire
container with LayoutElement, so layout group collapses the space
when no screenshot is present.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Wire screenshotPreviewContainer in Gameplay scene

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 22:30:28 -08:00
0dc5435b9a Add new quest type definitions (#5886)
* Add new quest type definitions

Add proto messages, Scala case classes, and converters for:
- StartEpidemicQuest: Start an epidemic in a target province
- SpendOnFeastsQuest: Spend gold on feasts (component-based)
- SendSuppliesQuest: Send food supplies to a province (component-based)
- RestProvinceQuest: Keep a province in rest orders (component-based)
- ReconSpecificProvincesQuest: Recon a target number of provinces
- RepairDevastationQuest: Repair devastation across provinces

Also adds failure conditions for StartEpidemicQuest, SendSuppliesQuest,
and RestProvinceQuest. Other quests have no specific failure conditions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add StartEpidemicQuest to CheckForFulfilledQuestsAction

Fix pattern match exhaustivity warning by adding case for
StartEpidemicQuest (action quest - fulfilled in ControlWeatherCommand).

Also add clarifying comment for other new ComponentQuest types.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add LLM prompt cases for new quest types

Add pattern match cases for StartEpidemicQuest, SpendOnFeastsQuest,
SendSuppliesQuest, RestProvinceQuest, ReconSpecificProvincesQuest,
and RepairDevastationQuest to both DivineMessagePromptGenerator
and QuestEndedGeneratorUtilities.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 21:56:55 -08:00
b55df7cf3e Upgrade dependencies: grpc-java, rules_scala, flatbuffers, and more (#5890)
Upgrades:
- grpc-java: 1.71.0 -> 1.78.0
- rules_scala: 7.1.5 -> 7.2.1
- flatbuffers: 25.9.23 -> 25.12.19
- aspect_bazel_lib: 2.22.4 -> 2.22.5
- rules_jvm_external: 6.9 -> 6.10
- Netty: 4.1.110.Final -> 4.1.127.Final (to match grpc-java)

All builds and tests pass.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 21:55:03 -08:00
19a76d3015 Fix crane path for Bazel 8 module extension naming (#5891)
Bazel 8 changed module extension repository naming from `~~` and `~` to
`++` and `+`. Update workflows to find crane dynamically instead of
using hardcoded paths that break with Bazel version changes.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 21:48:07 -08:00
adminandGitHub a50bfe36b0 Switch compile-time JDK from 21 to 25 (#5889) 2026-02-05 17:33:13 -08:00
73ba9a3acc Upgrade rules_java to 9.3.0 (enables JDK 25 toolchain support) (#5885)
* Upgrade rules_java to 9.3.0 (enables JDK 25 toolchain support)

This adds an explicit dependency on rules_java 9.3.0, which includes
support for JDK 25 remote toolchains. The build continues to use
Bazel's embedded JDK 24 by default, but JDK 25 toolchains are now
available for future use.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Mark MCTS basic test as flaky

The shardok_mcts_ai_basic_test uses Monte Carlo Tree Search which
has inherent non-determinism. Tests like PrefersArcheryOverEndTurn
and DoesNotPreferStartFireWhenNotBeneficial assert on AI decisions
that may vary between runs depending on random exploration paths.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 13:40:58 -08:00
adminandGitHub 2332626198 Add StartBlizzardQuest generation (3/3) (#5878) 2026-02-05 10:55:02 -08:00
adminandGitHub cc463700d9 Add StartBlizzardQuest client UI support (#5877) 2026-02-05 10:53:53 -08:00
adminandGitHub 0788f9a155 Add missing platforms dependency for Bazel 8 cross-compilation (#5884) 2026-02-05 10:52:56 -08:00
adminandGitHub 44f9eba77f Upgrade to Bazel 8.5.1, grpc 1.74.0, protobuf 33.5 (#5883) 2026-02-05 09:47:35 -08:00
adminandGitHub 22f68fc3df Add StartBlizzardQuest type (PR 1/3) (#5875) 2026-02-05 07:57:31 -08:00
b08c1755ac Enable ApprehendOutlawQuest generation (PR 3/3) (#5874)
* Add client UI support for ApprehendOutlawQuest (PR 2/3)

- DisplayNames.cs: Added "Apprehend Outlaw" display name
- UnaffiliatedHeroRowController.cs: Added quest description with
  dynamic hero name lookup for the target outlaw

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Enable ApprehendOutlawQuest generation (PR 3/3)

Add quest generation for ApprehendOutlawQuest. The quest is generated
when there are outlaws in the faction's territory:

- Finds all outlaws across all faction-controlled provinces
- Creates one quest candidate per outlaw
- Quest requires apprehending that specific outlaw

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 06:33:06 -08:00
1b0ede178c Add client UI support for ApprehendOutlawQuest (PR 2/3) (#5873)
- DisplayNames.cs: Added "Apprehend Outlaw" display name
- UnaffiliatedHeroRowController.cs: Added quest description with
  dynamic hero name lookup for the target outlaw

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 06:32:35 -08:00
258f739d02 Move SparklePlugin to separate Bazel workspace to resolve rules_swift conflict (#5882)
* Move SparklePlugin to separate Bazel workspace to resolve rules_swift conflict

The problem: grpc 1.76.0.bcr.1 requires rules_swift 3.x, but rules_apple 4.x
requires rules_swift 2.x. These have different compatibility levels, causing
bzlmod resolution to fail when both are in the same workspace.

The solution: Move SparklePlugin (the only thing using rules_apple) to a
separate Bazel workspace at `sparkle_workspace/`. This workspace has its own
MODULE.bazel with only rules_apple and Sparkle dependencies, completely
isolated from the grpc/flatbuffers dependency tree.

Changes:
- Create sparkle_workspace/ with isolated MODULE.bazel
- Move SparklePlugin source files to sparkle_workspace/
- Update build_sparkle_plugin.sh to build from subworkspace
- Remove rules_apple from main MODULE.bazel
- Add single_version_override for rules_swift 3.1.2 (for grpc/flatbuffers)

All 315 tests pass.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add WORKSPACE.bazel to sparkle_workspace to prevent parent workspace detection

Without this file, Bazel may walk up the directory tree and find the parent
workspace's MODULE.bazel, causing dependency conflicts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 06:23:46 -08:00
f125c8f5f6 Remove unused macos_command_line_application from action_point_distances (#5881)
This target was a development utility that's no longer used.
Removing it helps reduce rules_apple dependencies.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 22:40:54 -08:00
adminandGitHub 22b0bc00cc Remove unused api_keys_data filegroup (#5879) 2026-02-04 17:51:42 -08:00
45942688a9 Add ApprehendOutlawQuest type (PR 1/3) (#5872)
* Add ApprehendOutlawQuest type (PR 1/3)

Add a new quest type where unaffiliated heroes require the faction to
apprehend a specific outlaw currently in their territory. The quest:

- Proto: Added ApprehendOutlawQuest message with outlaw_hero_id field
- Scala: Added case class with quest fulfillment logic
- Failure: Quest fails if the outlaw is no longer in faction territory
- Fulfillment: Completed when ApprehendOutlawCommand targets the outlaw
- Updated ApprehendOutlawCommand to check for quest fulfillment and
  return multiple results (main result + quest completion if matched)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix ApprehendOutlawQuest failure condition

Only fail if the hero is no longer an outlaw anywhere. Moving to a
different province is fine - quest remains valid. Quest fails only
when someone else apprehends them.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 17:01:39 -08:00
a7b7b0e444 Upgrade Scala 3.7.4, rules_scala 7.1.5, protobuf 33.0, grpc 1.76.0, Bazel 7.7.1 (#5871)
* Upgrade rules_scala 7.1.5, Scala 3.7.4, protobuf 32.1

- Update rules_scala from 7.1.1 to 7.1.5
- Update Scala from 3.7.2 to 3.7.4
- Update protobuf from 29.2 to 32.1 (required by rules_scala 7.1.5+)
- Add force_version for Maven dependency conflicts (gson, errorprone, guava)
- Update -Wconf pattern in tools/BUILD.bazel for Bazel 7/8 compatibility
- Add -Wno-deprecated-copy-with-dtor to suppress protobuf 32.x warning

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add grpc 1.76.0, protobuf 33.0, and Bazel 7.7.1 compatibility fixes

- Upgrade grpc from 1.71.0 to 1.76.0.bcr.1 (required for protobuf 33.0)
- Upgrade protobuf from 32.1 to 33.0 (matches grpc 1.76.0 expectations)
- Upgrade Bazel from 7.6.1 to 7.7.1
- Add single_version_override for rules_swift 3.1.2 (grpc needs 3.x, rules_apple needs 2.x)
- Add --features=-module_maps workaround for grpc cf_event_engine macOS build issue

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 17:01:15 -08:00
76be41d841 Enable WinBattlesQuest generation (PR 3/3) (#5870)
* Enable WinBattlesQuest generation (PR 3/3)

Adds WinBattlesQuest to the available quest generators. The quest
requires winning 2-4 battles (randomly determined).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix DivineCommandTest for new quest type random sequence

Adding WinBattlesQuest changed the random sequence during quest creation,
causing hero 12 to get a quest with a province target. Updated test
expectation to match the new (correct) notification output.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 15:27:06 -08:00
8ee4e1475c Add client UI support for WinBattlesQuest (PR 2/3) (#5869)
- DisplayNames.cs: Add "Win Battles" display name
- UnaffiliatedHeroRowController.cs: Show progress (X/Y battles won)
- DivineMessagePromptGenerator: Add divine message text
- QuestEndedGeneratorUtilities: Add quest ended description

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 15:20:31 -08:00
c583e216d3 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>
2026-02-04 15:07:01 -08:00
f3dea0306d Enable BorderSecurityQuest and WinBattleOutnumberedQuest generation (#5858)
- BorderSecurityQuest: Creates one quest per border province (provinces
  with neighbors controlled by other factions)
- WinBattleOutnumberedQuest: Simple quest with no parameters

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 14:24:27 -08:00
acf57f3c96 Enable BetrayAllyQuest generation (PR 3b/3) (#5856)
Adds quest generation for BetrayAllyQuest, which is available when
the faction has at least one ally.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 14:20:33 -08:00
9cb7cadcdf Change BetrayAllyQuest to target specific ally (#5867)
Instead of a generic "betray any ally" quest, BetrayAllyQuest now
targets a specific faction with `targetFactionId`. This makes the
quest more meaningful and allows players to plan strategically.

Changes:
- Proto: Add target_faction_id field to BetrayAllyQuest message
- Quest.scala: Change from case object to case class with targetFactionId
- QuestConverter: Handle new targetFactionId field
- CheckForFailedQuestsAction: Update failure logic to check if specific
  target faction no longer exists or is no longer an ally
- BreakAllianceResolutionHelpers: Check if broken alliance matches
  quest's target faction for fulfillment
- DivineMessagePromptGenerator & QuestEndedGeneratorUtilities: Update
  quest descriptions to mention specific ally name
- UnaffiliatedHeroRowController (C#): Display specific ally name in UI

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 14:14:20 -08:00
1bbdd18f9f Fix reconnecting status stuck after sleep/wake (#5864)
* Fix reconnecting status stuck after sleep/wake

Race condition: when device wakes from sleep, CheckForIdleTimeout and
HandleStreamingCall both try to initiate reconnection, leading to
conflicting state updates and duplicate retry timers.

Fix: Cancel thread token in CheckForIdleTimeout before disposing the
streaming call, and check the token in HandleStreamingCall exception
handlers to skip ScheduleReconnect if another path is handling it.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix threadToken scope to be accessible in catch blocks

Move threadToken declaration outside try block so exception handlers
can check if the token was cancelled.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:58:14 -08:00
aad241ed77 Upgrade Java build toolchain from 17 to 21 (#5866)
Updates .bazelrc to use Java 21 for compilation.

Requires PR #5865 (URL deprecation fix) to be merged first, otherwise
the build will have deprecation warnings from the old URL constructor.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:57:08 -08:00
4ff93ac8fc Fix deprecated URL constructor in LLM integration classes (#5865)
JDK 20+ deprecates `new URL(String)`. Use `URI.create().toURL` instead.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:56:41 -08:00
e3b88215aa Enable SwearBrotherhoodWithHeroQuest generation (PR 3a/3) (#5855)
Adds quest generation for SwearBrotherhoodWithHeroQuest, which targets
vassal heroes in the province who are not already faction leaders.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:52:43 -08:00
c89a4ef51b Add client UI support for 4 new quest types (PR 2/3) (#5853)
Adds C# client display support for:
- SwearBrotherhoodWithHeroQuest: "Swear brotherhood with {heroName}"
- BetrayAllyQuest: "Break an alliance with an ally"
- BorderSecurityQuest: "Control all provinces bordering {provinceName}"
- WinBattleOutnumberedQuest: "Win a battle while outnumbered"

Changes:
- DisplayNames.cs: Add quest type strings
- UnaffiliatedHeroRowController.cs: Add quest descriptions in SetQuestText and ShortQuestString

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:38:58 -08:00
7d07bd97ad Add 4 new quest types with fulfillment logic (PR 1/3) (#5850)
Adds server-side support for:
- SwearBrotherhoodWithHeroQuest: fulfilled when player swears brotherhood with the target hero
- BetrayAllyQuest: fulfilled when player breaks an alliance with an ally
- BorderSecurityQuest: fulfilled when all neighboring provinces of the target province are controlled
- WinBattleOutnumberedQuest: fulfilled when winning a battle while having fewer troops

Each quest has proto definitions, Scala case classes, converter logic, fulfillment/failure
checks, and LLM prompt generators. PR 2 will add client UI and PR 3 will add quest generation.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:29:01 -08:00
5a80fcf765 Add hero faction membership utility functions to FactionUtils (#5861)
Adds two utility functions for checking hero faction membership:

- heroIsInFaction(heroId, factionId, heroes): Efficiently checks if a
  specific hero belongs to a faction by checking hero.factionId
- heroIdsInFaction(factionId, heroes): Returns Set of all hero IDs
  belonging to a faction

These functions check the hero's factionId field, which is the
authoritative source for faction membership. Heroes retain their
factionId when in a province, in a moving army, or imprisoned. They
lose it when exiled, become outlaws, or depart.

This is more reliable than checking province.rulingFactionHeroIds
which misses heroes in moving armies.

Includes comprehensive tests for various scenarios:
- Heroes in provinces
- Heroes in moving armies (retain factionId)
- Imprisoned heroes (retain factionId)
- Exiled heroes (factionId cleared)
- Outlaws (factionId cleared)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:14:21 -08:00
8a2bc8d984 Upgrade to Java 25 (LTS) (#5859)
Upgrade Eagle server and JFR sidecar from Java 21 to Java 25.

Java 25 is the latest LTS release (September 2025). Scala 3.7.2
fully supports JDK 25.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:04:32 -08:00
50c632a8ea Increase battalion backstory length limits (#5860)
- Initial backstory: 50 → 100 words
- Growth per update: 20 → 50 words
- Soft cap: 150 → 300 words

This allows battalion histories to build up more narrative over time
while still keeping them from growing unbounded.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:04:11 -08:00
ac11292778 Upgrade to Java 21 (LTS) (#5851)
Upgrade Eagle server and JFR sidecar from Java 17 to Java 21.

Benefits:
- Better garbage collection (Generational ZGC available)
- General JVM performance improvements
- Foundation for future virtual threads adoption
- Supported until 2031

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 12:07:48 -08:00
de01fc1451 Fix bug report JSON escaping using Newtonsoft.Json (#5852)
Use JsonConvert.ToString() from Newtonsoft.Json for proper JSON string
escaping instead of manual character replacement. This handles all
control characters and edge cases correctly.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 12:02:59 -08:00
fde8ccf675 Fix S3 credentials: separate credentials for Eagle and admin (#5854)
Eagle and admin server use different S3 buckets:
- Eagle: eagle0 bucket (game data) - uses DO_SPACES_* secrets
- Admin: eagle0-assets bucket (What's New) - uses ACCESS_KEY_ID/SECRET_KEY secrets

The previous change (#5847) broke Eagle by giving it credentials that only
have access to eagle0-assets. This fix:
- Reverts Eagle to use DO_SPACES_* secrets (eagle0 bucket access)
- Adds ADMIN_S3_* env vars for admin server (eagle0-assets bucket access)
- Admin container receives ADMIN_S3_* values as DO_SPACES_* env vars

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:28:30 -08:00
1033a4b730 Use JRE instead of JDK for Eagle server image (#5849)
The Eagle server only needs to run Java, not compile it. Using the JRE
base image instead of JDK reduces the image size from ~273MB to ~120MB.

The JFR sidecar still uses JDK because it needs jcmd for JFR dumps.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:11:27 -08:00
f3959c8c79 Fix collection modified exception in CancelAllPendingSubscriptionAcks (#5848)
TrySetCanceled() can trigger synchronous continuations that re-enter
the lock (C# locks are reentrant) and call Remove() on the dictionary
while the foreach is still iterating, causing InvalidOperationException.

Fix by snapshotting values and clearing the dictionary before cancelling
the TaskCompletionSources.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:02:55 -08:00
f7b76bb30d Fix admin server S3 credentials: use working secrets (#5847)
The docker_build workflow was using DO_SPACES_ACCESS_KEY/SECRET_KEY
secrets which don't have write access. Use ACCESS_KEY_ID/SECRET_KEY
instead, which are the same secrets that work for CI builds.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 10:26:13 -08:00
eaae3089de Enable release all prisoners quest generation (PR 3/3) (#5846)
Add quest creation logic to QuestCreationUtils.scala:
- Only available if faction's provinces have at least 3 prisoners
- Returns ReleaseAllPrisonersQuest when condition is met

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 10:09:38 -08:00
eb2e954c2c Add client handling for release all prisoners quest (PR 2/3) (#5845)
- Add QuestTypeString case in DisplayNames.cs
- Add ShortQuestString case in UnaffiliatedHeroRowController.cs

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 10:09:12 -08:00
95b4201819 Add release all prisoners quest types and handling (PR 1/3) (#5842)
Add ReleaseAllPrisonersQuest that requires the faction to release all
prisoners held in their provinces. The quest fails if any prisoner is
executed, moved, or traded away in ransom.

- Add proto definition and Scala case object
- Add proto converter for serialization
- Add fulfillment check (succeeds when 0 prisoners/moving prisoners)
- Add failure handling in ManagePrisonersCommand (Execute, Move)
- Add failure handling in RansomResolutionHelpers (ransom acceptance)
- Add LLM prompts for divine message and quest ended narratives

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 09:41:54 -08:00
5ba1948c8e Add missing meta file for duel_challenged.wav (#5844)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 09:36:25 -08:00
2f6ce2d0b7 Move WhatsNewPanelController to always-active Settings object (#5843)
The controller was on the What's New Panel itself, which starts disabled.
This meant Awake() wouldn't run until the panel was first enabled, but
the WhatsNewManager tries to call Show() before that happens - resulting
in the modal blocker appearing without the panel.

Moving the controller to the always-active Settings object ensures it
initializes properly at startup.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 09:33:30 -08:00
df916f9c29 Enable battalion diversity quest generation (PR 3/3) (#5838)
* Enable battalion diversity quest generation (PR 3/3)

Adds quest creation logic for BattalionDiversityQuest to QuestCreationUtils.

Quest availability:
- Only if province currently has 1-2 battalion types
- This encourages players to diversify their army composition

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Clarify PR dependency structure in quest docs

PR 2 (client) and PR 3 (generation) both depend on PR 1 (types),
but are independent of each other. They can be developed in parallel
and merged in either order after PR 1.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 09:11:03 -08:00
ddbc960945 Add aggressive docker image cleanup to prevent disk full (#5841)
The existing cleanup only removed dangling images. Now also removes
images older than 24h to prevent disk space exhaustion while keeping
recent images available for rollback.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 09:10:35 -08:00
e020a0f66c Add client handling for battalion diversity quest (PR 2/3) (#5837)
Adds C# client handling for BattalionDiversityQuest:
- DisplayNames.QuestTypeString() - returns "Battalion Diversity"
- UnaffiliatedHeroRowController.ShortQuestString() - shows quest description

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 08:53:23 -08:00
d60a9f79ec Fetch What's New data via public HTTP URL (#5839)
The S3 API requires credentials with bucket access, but the file is
publicly accessible at assets.eagle0.net. Use HTTP GET for reading
(no credentials needed) and keep S3 API only for writing.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 08:52:16 -08:00