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>
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>
* 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>
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>
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>
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>
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>
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>
* 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
* 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>
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>
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>
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>
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>
* 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>
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>
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>
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>
* 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>
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>
* 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>
- 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>
* 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>
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>
- 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>
* 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>
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>
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>
* 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>
* 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>
* 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>
- 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>
- 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>
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>