* 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>
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>
* 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>
* 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>
* 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>
- 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>
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>
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>
* 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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
- Add QuestTypeString case in DisplayNames.cs
- Add ShortQuestString case in UnaffiliatedHeroRowController.cs
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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>
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>
* 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>
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>
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>
* Add long-press support for right-click on touch devices
On iPad and other touch devices, right-click is not available. This adds
long-press detection to GeneralClickDetector (used by the strategic map)
to trigger right-click behavior after holding for 0.5 seconds.
- Tracks pointer down time and position
- Triggers right-click after 0.5s if finger hasn't moved >10 pixels
- Suppresses normal left-click if long-press was triggered
- Cancels long-press detection if finger moves too far
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Only enable long-press for touch input, not mouse
Mouse users can right-click, so long-press should only trigger for
touch devices. Check pointerId >= 0 to distinguish touch (0+) from
mouse (-1, -2, -3 for left, right, middle buttons).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Restore support for ACCESS_KEY_ID/SECRET_KEY env vars (used by CI)
while also supporting DO_SPACES_* env vars (used by docker-compose).
Priority order:
1. ~/.s3cfg file
2. DO_SPACES_* env vars
3. ACCESS_KEY_ID/SECRET_KEY env vars
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Adds BattalionDiversityQuest - a quest where unaffiliated heroes want
to see three different types of battalion at near-full strength (80%
capacity) in the current province.
This PR adds:
- Proto message definition
- Scala case object
- Proto converter
- Fulfillment check (near UpgradeBattalionQuest)
- LLM prompt generators
Does NOT generate the quest yet - that will be in PR 3 after client
handling is added in PR 2.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Replace duel_challenged sound with licensed asset
Replace duel_challenged.mp3 (unknown license) with Weapon Draw Metal 1.wav
from the purchased Medieval Combat Sounds Unity Asset Store pack.
The dramatic sword unsheathing sound fits well for a duel challenge.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Wire up Weapon Draw Metal 5 as duel_challenged sound
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add S3 credentials to admin server container
The admin server needs DO_SPACES_* environment variables to access
S3/Spaces for What's New data storage. Without these, the What's New
Management page fails with "static credentials are empty".
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix S3 credentials: use DO_SPACES_* env vars
The aws package was looking for ACCESS_KEY_ID/SECRET_KEY env vars,
but docker-compose passes DO_SPACES_ACCESS_KEY/DO_SPACES_SECRET_KEY.
Updated ReadAwsConfig() to use the correct env var names and also
respect DO_SPACES_ENDPOINT.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add stale PR refs cleanup to iOS CI workflows
Self-hosted runners persist .git between runs. When a PR is updated,
old local refs may point to commits that were never fetched, causing
"Could not scan for Git LFS files" errors.
Applied to:
- ios_addressables_build.yml
- ios_testflight.yml
(Same fix already present in mac_build.yml and unity_build.yml)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fetch LFS after checkout to avoid stale ref issues
Move LFS fetching out of actions/checkout and into a separate step.
This ensures our stale PR refs cleanup runs before any LFS operations,
avoiding "Could not scan for Git LFS files" errors.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix LFS fetch order in mac_build and unity_build workflows
Apply the same fix as ios_* workflows: set lfs: false in checkout
and fetch LFS files manually afterward to avoid stale ref issues.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The admin server needs DO_SPACES_* environment variables to access
S3/Spaces for What's New data storage. Without these, the What's New
Management page fails with "static credentials are empty".
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add quest creation logic for DevelopProvincesQuest and MobilizeProvincesQuest
to QuestCreationUtils.
Quest parameters:
- Availability: Only if faction has >= 3 provinces
- Target province count: Random 3-5, capped at current province count
- Target months: Random 3-6
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add ADDING_NEW_QUESTS.md documentation explaining the three-PR
strategy and listing all files that need modification for new quests
- Add DevelopProvincesQuest and MobilizeProvincesQuest cases to
DisplayNames.QuestTypeString()
- Add quest description strings to UnaffiliatedHeroRowController
showing progress (e.g., "3/5 months")
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add click-to-dismiss on modal blockers as safety net
If a modal panel somehow becomes invisible while its blocker remains
active, users can now tap the dimmed area to dismiss it. This prevents
getting stuck with an unresponsive UI.
Applied to all three controllers that share the modal blocker:
- WhatsNewPanelController
- BugReportPanelController
- SettingsPanelController
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Give each modal panel its own dedicated modal blocker
Previously Settings, Bug Report, and What's New panels shared a single
modal blocker, which could cause state conflicts if one panel's blocker
state affected another.
Now each panel has its own dedicated blocker:
- Settings Panel Modal Blocker
- Bug Report Modal Blocker
- What's New Modal Blocker
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The fetchWhatsNewData function was silently swallowing ALL errors and
returning empty data, not just "file not found" errors. This masked
real issues like permission errors.
Now:
- Only returns empty data for actual "NoSuchKey" (file not found) errors
- Logs and returns other errors so they appear on the page
- Logs successful fetches with entry count for debugging
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The htmx form was receiving both an HX-Redirect header and HTML body content.
This caused htmx to swap the content before processing the redirect, making
the button appear to do nothing.
Fix: Return only the HX-Redirect header without body content on success.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add Province Order Quests (Develop/Mobilize)
Add two new quest types where unaffiliated heroes want the faction to
maintain a certain number of provinces in specific order states
(Develop or Mobilize) for a cumulative number of months.
Quest mechanics:
- Availability: Only if faction has >= 3 provinces
- Target province count: Random 3-5, capped at current province count
- Target months: Random 3-6 (componentCount)
- Progress: Each round, if faction has >= X provinces with the required
order type, componentsFulfilled increments by 1
- Completion: When componentsFulfilled >= componentCount
- Failure: If faction falls below X total provinces
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove ProvinceOrderQuestProgress action result type
Fold the province order quest progress changes into the EndVassalCommandsPhase
action result instead of creating a separate action result type.
Also improve LLM prompt descriptions to explain what Develop and Mobilize
orders mean (Develop = improve agriculture/economy/infrastructure,
Mobilize = organize/train/arm troops for war).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove quest generation for province order quests
Keep the proto definitions, Scala types, converters, and handling code
(fulfillment, failure, progress tracking, LLM prompts) but remove the
actual quest creation logic from QuestCreationUtils.
This allows the client to be updated to understand these quest types
before the server starts generating them.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Change costLabel to only show the numeric cost value
- Add separate costWarningLabel for "(only X available)" warning
- Warning label only shown when insufficient gold available
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Update hero and battalion backstory prompt generators to explicitly
request flowing narrative prose rather than dated lists. The LLM
should weave events into a cohesive story that reads as a biography
or unit history, not a timeline.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add underProvinceNameText/underProvinceOwnerText to MapController
- Update SetUpCenterText to populate both regular and under text fields
- Add clickedProvincePanel/underClickedProvincePanel refs to EagleGameController
- Switch panel visibility based on screen width in ArrangeLayout
- Organize EagleGameController Inspector fields with Header attributes
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Notify connected clients when a new version is available after CI deploy.
Flow:
1. CI deploys new build and waits 60s for CDN cache
2. CI calls admin server HTTP endpoint with shared secret
3. Admin server calls Eagle via gRPC
4. Eagle broadcasts to all connected lobby users
Components:
- Proto: ClientUpdateAvailable message, NotifyClientUpdate RPC
- Eagle: notifyClientUpdate() broadcasts to lobby users
- Admin server: /notify-update HTTP endpoint with secret auth
- CI: Notify steps in mac_build.yml and unity_build.yml
- Docker: NOTIFY_SECRET env var passed to admin container
Client-side handling will be added in a follow-up PR.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add battalion backstory to LLM prompt generators
Add descriptionWithBackstory methods to BattalionDescriptions that include
the battalion's backstory text alongside the basic description (name, type,
size). This gives LLM prompts richer context about the units involved.
Updated prompt generators:
- SuppressBeastsPromptGenerator (Failed and Succeeded): Include battalion
backstory when describing beast hunting attempts
- HeroBackstoryUpdatePromptGenerator: Include battalion backstory in
FoughtInBattle, SuppressedBeasts, and SuppressedRiot events
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix battalion backstory usage in prompts
Use basic description inline in sentences and include backstory as
separate context block, matching how hero backstories are handled.
- In SuppressBeastsPromptGenerator: use basic description in
"$heroName took [battalion]" and add fullDescription as context
- In HeroBackstoryUpdatePromptGenerator: use basic description only
in event text (hero's story doesn't need battalion backstory detail)
- Renamed methods in BattalionDescriptions for clarity:
- backstoryText: just the backstory
- fullDescription: "Name is a battalion... $backstory"
- optionalFullDescription: for optional battalions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix duplicate code blocks from rebase
The rebase introduced duplicate code blocks in FoughtInBattle,
SuppressedBeasts, and SuppressedRiot cases that caused syntax errors.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove unused BattalionView methods from BattalionDescriptions
Since EventForHeroBackstory now uses BattalionId instead of BattalionView,
we no longer need the BattalionView overloads in BattalionDescriptions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The FoughtInBattle, SuppressedBeasts, and SuppressedRiot events now store
Option[BattalionId] instead of Option[BattalionView]. This allows looking
up the battalion from GameState at prompt generation time, providing access
to the battalion's current backstory text rather than a snapshot.
Changes:
- Proto: Changed battalion field from BattalionView to optional int32
- EventForHeroBackstoryT.scala: Changed battalion type to Option[BattalionId]
- EventForHeroBackstoryConverter: Updated toProto/fromProto conversion
- ResolveBattleAction, SuppressBeastsCommand, HandleRiotCrackDownCommand:
Now pass battalion.map(_.id) when creating events
- HeroBackstoryUpdatePromptGenerator: Looks up battalion from GameState
using gameState.battalions.getOrElse(id, gameState.destroyedBattalions(id))
- Updated tests and removed unused BattalionViewFilter deps
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Update Notification Panel layout
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Additional Notification Panel layout tweaks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update button text styling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Fix empty.tail crash in BattalionBackstoryUpdatePromptGenerator
When a battalion had no previous backstory versions, calling .last on
an empty vector caused a NoSuchElementException. Use lastOption with
a match to safely handle the empty case by returning an empty string.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Generate initial backstory for newly raised battalions
When a player raises a new battalion via OrganizeTroopsCommand, now
generates a BattalionInitialBackstoryRequest so the battalion gets
its first backstory. Previously only battalions created at game start
(in NewGameCreation) received initial backstories.
This ensures all battalions have backstories from their creation,
complementing the empty.tail fix which handles the transitional case.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Update Suppress Beasts command panel layout in Unity
- Only show "(only X available)" when insufficient gold, with newline
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add references for battalion name (GeneratedTextUpdater) and type icon
(RawImage) to the battalion popup panel header. Populate them when
hovering over a battalion row.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When creating backstory events for changed battalions, the code assumed
every battalion in changedBatt would also be in changedBattalions. This
is not true when a battalion is modified indirectly - for example, when
troops are transferred OUT of it to another battalion.
The fix uses find() and handles the None case with default values (0 for
hired, dismissed, and transferred in counts).
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add git corruption detection to Unity CI workflows
Self-hosted runners can accumulate corrupted git state over time.
Add a pre-checkout step that runs git fsck and nukes the repo if
corruption is detected, allowing a fresh clone.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix CI git corruption: check target ref objects instead of full fsck
Self-hosted runners can have stale refs (e.g., refs/remotes/pull/*/merge)
pointing to commits whose objects weren't fetched when the PR was updated.
Running git fsck is too broad and slow. Instead, check if the specific
target ref's objects are complete before checkout.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix CI: prune stale PR refs instead of checking all objects
Self-hosted runners persist .git between runs. When a PR is updated,
actions/checkout doesn't prune old local refs like refs/remotes/pull/*/merge.
These stale refs may point to commits whose objects were never fetched,
causing "missing object" errors during checkout.
Fix by explicitly removing PR refs before checkout. This is faster and
more targeted than validating object completeness.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add four new battalion backstory event types:
- SuppressedRiot: Records when a battalion helps suppress a riot, including
the riot size, casualties, and whether the suppression succeeded
- Trained: Records training sessions by a hero, with training improvement
- Armed: Records equipment upgrades with armament improvement and cost
- SurvivedStarvation: Records when battalions suffer casualties due to
food shortage, with loss proportion and whether on march or in garrison
The LLM prompt generator includes guidance to not over-emphasize routine
training and arming events in favor of more dramatic events like battles
and riots.
Also includes per-battalion cost tracking in ArmTroopsCommand to properly
attribute gold spending for each battalion's backstory event.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add What's New feature to Unity client
- Add WhatsNewManager singleton for fetching and tracking changelog
- Add WhatsNewPanelController for modal display with category styling
- Integrate CheckAndShow() call in ConnectionHandler after OAuth login
The manager fetches entries from assets.eagle0.net/whats-new.json,
tracks last seen date in PlayerPrefs, and shows new entries on login.
UI prefab setup required in Unity Editor.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Wire up What's New UI in Unity
- Move WhatsNewEntryUI to separate file for Unity component discovery
- Add What's New Entry prefab
- Wire up WhatsNewManager and WhatsNewPanelController in Gameplay scene
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add battalion backstory infrastructure
Adds support for battalions to have backstory events similar to heroes:
- New proto message EventForBattalionBackstory with event types:
- OrganizedTroops: tracks size changes, hiring, dismissals, transfers
- SuppressedBeasts: records beast suppression operations
- ApprehendedOutlaws: records outlaw apprehension (for future use)
- FoughtInBattle: records battle participation and casualties
- New Scala enum EventForBattalionBackstoryT matching the proto
- Updated BattalionT trait and BattalionC with:
- backstoryVersions: Vector[BackstoryVersion] for LLM-generated text
- backstoryEvents: Vector[EventForBattalionBackstoryT] for events
- Helper methods: withBackstoryVersions, withBackstoryEvents, addBackstoryEvent
- New converters:
- BackstoryVersionConverter (extracted for reuse)
- EventForBattalionBackstoryConverter
- Updated BattalionConverter to handle backstory fields
- Wired up backstory events in:
- SuppressBeastsCommand: adds SuppressedBeasts event to battalion
- ResolveBattleAction: adds FoughtInBattle event to battalions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add LLM-driven battalion backstory updates
This commit adds the system to actually use battalion backstory events
to trigger LLM-generated backstory updates, similar to the hero backstory
update system.
New components:
- BattalionBackstoryUpdateAction: finds battalions with events, generates
LLM requests, adds new backstory versions, and clears events
- BattalionBackstoryUpdateActionGenerator: creates action from game state
- BattalionBackstoryUpdatePromptGenerator: generates prompts describing
events for LLM to update backstory
- BattalionBackstoryUpdateRequest proto message and converter
The system is wired into:
- EndPlayerCommandsPhaseAction
- EndVassalCommandsPhaseAction
- EndBattleAftermathPhaseAction
- EngineImpl (command execution)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Track previousSize/newSize for battalion events, add heroId to OrganizedTroops
Changes:
- SuppressedBeasts event: replaced 'casualties' with 'previousSize' and 'newSize'
- ApprehendedOutlaws event: added 'previousSize' and 'newSize' fields
- OrganizedTroops event: added 'heroId' for the province ruler who ordered the reorganization
- OrganizeTroopsCommand now creates battalion backstory events when troops are organized
- Updated prompt generator to include hero names and size information
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When a game is deleted (via admin console or last player dropping),
cancel any pending Shardok battles to:
- Stop processing battles for non-existent games
- Prevent reconnection attempts for deleted games
- Clean up resources
The implementation removes matching entries from pendingBattles map,
which prevents scheduleReconnect from re-establishing streams and
handleStreamingResponse from processing further updates.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add CRUD handlers for managing what's-new entries stored in S3
- Add whats_new.html template with add/edit/delete forms
- Add navigation link to layout.html
- Add player-friendly summary generation to generate_changelog.sh
- Add seed JSON file for initial data
The admin console at /whats-new allows managing changelog entries
that will be displayed to players in the Unity client. The script
integration generates summaries and opens the admin console with
pre-populated content after sending weekly changelogs.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add battalion backstory popup on hover
Add support for displaying battalion backstories when hovering over
battalions in the Battalions panel, similar to hero backstories:
- Added battalionPopupPanel and battalionPopupPanelBackstory fields
- Added BattalionLongHoverRowChanged method to handle hover events
- Uses GeneratedTextUpdater for auto-scrolling text display
To wire up in Unity:
1. Create a popup panel similar to hero popup
2. Add GeneratedTextUpdater component for backstory text
3. Assign references in HeroesAndBattalionsPanelController
4. Wire battalionsTable.LongHoverRowChangedHandler to BattalionLongHoverRowChanged
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Eagle0 > Build Protos menu item for editor proto rebuilds
Provides a convenient way to rebuild protocol buffer files from within
Unity Editor (Cmd+Shift+P on Mac). This helps when proto files change
and the generated C# code needs to be updated.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Wire up battalion backstory popup panel in Unity
- Add TableRowHoverDetector to Battalion Row Prefab for hover events
- Configure battalion popup panel in Gameplay scene
- Wire LongHoverRowChangedHandler to BattalionLongHoverRowChanged
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Position popup column spacer via EagleGameController layout system
Set the popup column spacer width in ArrangeLayout() alongside the
heroes and battalions panel width, ensuring they stay synchronized
when resolution changes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Propagate battalion backstories to clients by adding backstory_text_id
field to BattalionView proto and Scala view classes.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1. Command Interaction Guidance (onboarding step 7):
- Explains clicking provinces for acting vs right-click for target
- Describes using left side panels for heroes/battalions selection
- Reminds that Commit finalizes the command
2. Hero Stat Gain Tutorial:
- Triggered first time a player's hero gains a stat point
- Uses HeroStatGained action result type for reliable detection
- Explains experience, stat growth, and professions
Also updated info button tutorial to mention the '?' keyboard shortcut.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Completed:
- Bug report form in Unity client
- Feedback channel (Discord server)
- Support plan (Discord + bug reporting + email)
- Art/music licenses and open source disclosures (attributions panel)
Added end game ideas:
- Win condition: all other factions defeated
- Mid-game progression: King recognition events
Moved "What's new" changelog to nice-to-have.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add battalion initial backstory generation
Adds LLM-generated backstories for battalions created at game start.
Changes:
- Add backstoryVersions field to Battalion proto and BattalionT/BattalionC
- Add BattalionInitialBackstoryRequest to GeneratedTextRequestT
- Create BattalionInitialBackstoryPromptGenerator for LLM prompts
- Wire up in LlmResolver and NewGameCreation
- Update BattalionConverter to handle backstory versions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Rename hero_backstory_version to backstory_version
Since BackstoryVersion is used for both heroes and battalions, rename:
- hero_backstory_version.proto -> backstory_version.proto
- hero_backstory_version_proto -> backstory_version_proto
- hero_backstory_version_scala_proto -> backstory_version_scala_proto
Also rename leading_hero_id to province_ruler_hero_id in
BattalionInitialBackstoryRequest to clarify this is the hero who
rules the province, not a battalion commander.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Fix bug report panel not showing on first click
Change Start() to Awake() so initialization happens at load time
rather than when the GameObject first becomes active. This prevents
Start() from disabling the panel after Show() has enabled it.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Enable BugReportPanelController GameObject at scene start
The controller needs to be active so Awake() runs at load time,
allowing Show() to work on first click.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add bug report form to Unity client
Adds a Report Bug button to the Settings panel that opens a bug report
form. Reports include:
- User description
- Game state (game ID, faction, date, active battles)
- System info (OS, CPU, GPU, resolution)
- Recent connection logs
Reports are sent via webhook (supports Discord and Slack).
Configure webhook URL with BugReportPanelController.SetWebhookUrl().
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Configure Discord webhook URL for bug reports
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add escape key handling for bug report and attributions panels
- Bug report panel closes on Escape
- Attributions panel closes on Escape
- Settings panel only toggles if other panels aren't open
- Wire up bug report panel UI in Gameplay scene
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The warmup binary was being copied from a hardcoded bazel-bin path that
doesn't work reliably with cross-compilation and remote caching. When the
binary was cached but not materialized locally, the cp command would fail.
Fix by:
- Create a warmup_tar pkg_tar target that packages the warmup binary
- Use tar extraction instead of cp, which forces Bazel to materialize
the output file before the command runs
- Rename the binary from warmup_linux_amd64 to warmup in the tar
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Update Recruit command panel layout
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update Diplomacy toggles to button-style like Improve
Change radio-style toggles to highlighted button toggles using
ToggleGroup and CanvasGroup alpha for disabled state.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Make HeroGenerator immutable with private constructor
- Change `var remainingPregeneratedHeroes` to `val`
- Return tuple `(HeroGenerationResponse, HeroGenerator)` from getHero()
- Add `forTesting()` factory method for test usage
- Filter excluded names during construction instead of lazily
- Make BattalionNameGenerator immutable
- Change `var excludingNames` to `val` (was never mutated anyway)
- Update callers to properly thread the generator through recursion/fold
- PerformUnaffiliatedHeroesAction: thread generator through `go` recursion
- NewGameCreation: thread generator through fold, extract at end
- EngineImplTest: use forTesting() instead of mock
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
casualtyCount was incorrectly set to total damage (including hero vigor
loss) instead of just battalion troop losses. Now:
- No battalion: 0 casualties
- Battalion survived: actual troop losses
- Battalion destroyed: entire battalion size
This ensures casualtyCount semantically represents battalion casualties,
not hero damage which is tracked separately.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When a hero suppresses beasts alone (no battalion), casualtyCount can
still be > 0 because casualties are applied to hero vigor. The code
incorrectly tried to access the battalion name for casualty text even
when no battalion existed.
Now only show battalion casualty text when a battalion actually exists.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add detailed help content for command tutorials
Update help text for many commands with more useful information:
- Rest: Vigor mechanics and Constitution bonus
- Train: Strength/Charisma stats, Champion bonus
- Hero Gift: Loyalty thresholds and January check
- Feast: Benefits and cost scaling
- Trade: Exchange rates and Economy bonus
- Arm Troops: Infrastructure requirements
- Organize Troops: Battalion types and requirements
- Recruit Heroes: Ready to Join mechanics
- Travel: List of available town activities
- Send Supplies: Destination selection tips
- Divine: What it reveals and cost considerations
- Recon: Intelligence gathered and risks
- Swear Kinship: Requirements and benefits
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Auto-switch help panel when selecting a different command
If the help panel is open and the user selects a different command,
the help panel now automatically switches to show help for the new
command instead of staying on the old one.
Also fix Rest tutorial: Constitution is max Vigor, not recovery rate.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add combat warning to March help
Mention that marching may trigger a battle if enemy occupies the
destination or is also marching there.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Feast help: only costs gold, not food
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Recon help: scouts can't be captured
Remove incorrect warning about capture risk.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Recon help: remove incorrect agility info
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Remove non-local returns and -Wconf suppression
Refactor code to avoid non-local returns (using `return` inside closures)
which are deprecated in Scala 3. Changes:
- OAuthService.scala: Replace early returns with if/else patterns in
exchangeCodeForToken, fetchUserInfo, generateAppleClientSecret,
exchangeAppleCode, and parseAppleIdToken methods
- PerformProvinceEventsAction.scala: Replace for-loop with early return
with foldLeft pattern in beastType method
Remove the `msg=Non local returns:silent` suppression from the toolchain.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix non-local returns in MapValidationTest
Refactor checkMonthlyWeather to use exists() instead of foreach with
early returns. Also fixes typo in error message (was "> 0" should be "> 100").
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add minimum width (480px) to tutorial panel
Prevents help text from being too tall and narrow.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Change tutorial panel minimum width to 720px
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Replace automatic command tutorials with info button system
Instead of showing one-time tutorial popups when command panels open,
users can now click an info button in the command panel header to view
help anytime. This makes help more accessible and less intrusive.
Changes:
- Remove automatic tutorial trigger from CommandSelector
- Add info button to CommandPanelController (created programmatically)
- Add ShowCommandHelp() to TutorialManager for repeatable help display
- Add info button intro step to onboarding sequence
- Update March, Defend, Improve help with detailed instructions
- Remove onboarding prerequisite from command tutorials
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove programmatic info button creation
Keep just the public field reference so it can be set up in Unity Editor.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix command tutorial ID lookup
Convert CommandType enum (e.g., "ImproveCommand") to tutorial ID format
(e.g., "command_improve") by removing "Command" suffix and lowercasing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add toggle behavior to info button and improve help content
- Clicking info button while help is showing now closes it
- Add ActiveSequenceId property to TutorialManager
- Update Improve help to mention agility/strength bonuses and engineer bonus
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix tutorial panel positioning to stay above command panel
- Simplify positioning logic for Top anchor to reliably place panel just above target
- Make panel at least 600px wide (or target width) for better readability
- Remove complex size-dependent clamping that caused inconsistent positioning
- Use simpler pivot-based positioning that doesn't depend on panel size calculations
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix command tutorial ID conversion for multi-word commands
Convert PascalCase command names to snake_case to match tutorial IDs:
- "OrganizeTroopsCommand" -> "command_organize_troops"
- "ArmTroopsCommand" -> "command_arm_troops"
- etc.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Reduce tutorial panel minimum width to 540px
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add Paladin bonus tip to Give Alms help
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add imprisonment warning to Diplomacy help
Warn that untrusted factions may imprison your ambassador.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix manage_prisoner tutorial ID to match command type
Command is ManagePrisonerCommand (singular), not ManagePrisonersCommand.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add '?' keyboard shortcut to toggle command help
Press Shift+/ (?) to open/close help for the current command.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove forced width matching for tutorial panel
Let the panel use its natural ContentSizeFitter width instead of
forcing it to match the command panel width.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add info button to command panel in Unity scene
Wire up info button UI in Gameplay.unity to show command help.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Scala 3 deprecates `_` for wildcard type arguments in favor of `?`.
This fixes the one occurrence in CommandChoiceHelpers.scala and removes
the -Wconf suppression for this warning.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Enable -Werror and fix compiler warnings (partial)
This commit enables -Werror in the Scala toolchain and fixes warnings
in the main server code and several test files. Main changes:
Server code fixes:
- Remove unused imports (HeroId, ActionResult, unused, Try)
- Remove unused parameters (playerCount cascade in NewGameCreation,
persister in synchronizedCreateGame, userServiceForGames in buildServer)
- Fix discarded value warnings with explicit : Unit type ascriptions
- Fix pattern match warning by removing unnecessary type annotation
Test code fixes:
- Remove unused imports across multiple test files
- Remove unused default parameter values that are always explicitly passed
- Fix ScalaMock expectation setup warnings with : Unit
- Fix MapValidationTest to properly combine boolean checks
- Fix loneElement usage to use proper assertions
The -Werror flag is now enabled along with -Wconf suppressions for:
- Initialization warnings (safe patterns Scala 3 warns about)
- ScalaTest assertion return values
- External/generated sources
Many test files still have warnings that need to be fixed in follow-up
commits before all 315 tests will build with -Werror.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Remove unused heroes variable in ReturningHeroesTest
Fix CI failure caused by unused private member warning.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix Scala compiler warnings in test files for -Werror compatibility
- Remove unused imports across multiple test files
- Remove unused private members and default parameters
- Add `: Unit` type ascription to ScalaMock expectations to silence discarded value warnings
- Fix deprecated `<function> _` syntax by removing trailing ` _`
- Replace deprecated `= _` with `scala.compiletime.uninitialized`
- Add `val _ =` to explicitly discard unused return values
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix remaining Scala compiler warnings in main sources for -Werror
- Remove unused imports: JString in OpenAIResponsesServiceImpl, FactionId in
DiplomacyOptionConverter and RansomOfferDetailsConverter,
OpenAIChatCompletionsServiceImpl in HeroLibraryGenerator
- Replace deprecated `= _` with `= uninitialized` in SseSubscriber and
ShardokInstanceManager
- Remove unused private member MessageType in SseSubscriber
- Remove unused private member openAICaller in HeroLibraryGenerator
- Add missing pattern match cases: UNKNOWN_UNIT in UnitStatusConverter,
Gender.Unknown in HeroLibraryGenerator
- Fix discarded value warnings with `val _ =` in ShardokInstanceManager
- Fix unused pattern variables in NameListChecker
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Previously the tutorial created a 1-faction game (solo with no AI).
Now it creates a 7-faction game (1 human + 6 AI), matching the
default when creating a game from the lobby.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
For the closed alpha, we're using a simple notice instead of a full
privacy policy. The notice explains:
- This is a private alpha test
- We collect OAuth email and gameplay data
- Users can delete their account via accounts.eagle0.net
- Contact email for questions
Added to:
- Invitation landing page (/invite/{code})
- Invitation email (HTML and plaintext)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Remove "The" from faction names where it sounds better without:
- Jade Caravan
- Maniacal Monks
- Bulwark Brotherhood
- Builders of a Better World
- Iron Fist Confederacy
- Mad Doombringers
- Shadow Army
- Verdant Fellowship
Keep "The" where it adds gravitas or feels essential:
- The Ashen Circle
- The Fracture Covenant
- The Hollow Throne
- The King's Loyalists
- The Syncopated Sanctum
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Set ITSAppUsesNonExemptEncryption=false in Info.plist during iOS build.
This indicates the app only uses standard OS-provided encryption (HTTPS)
and avoids the manual compliance questionnaire in App Store Connect.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unused imports (FactionT, HostileArmyGroupProto, GameId)
- Remove unused parameters (jwtService in AuthServiceImpl, gameId in MarchCommand, provinces in ProvinceDistances)
- Add exhaustive match handling for ImprovementTypeProto UNKNOWN case
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Wire up heroesColumn and battalionsColumn references in March Command Selector
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update command selector layout when aspect ratio changes
Previously, hero/battalion column visibility was only set when a command
selector was first opened. Now layout changes are propagated to the
active selector via OnLayoutModeChanged(), so columns hide/show
dynamically when the window is resized.
- Add OnLayoutModeChanged() virtual method to CommandSelector
- Override in MarchCommandSelector and DefendCommandSelector
- Add UpdateSelectorLayoutMode() to CommandPanelController
- Call from EagleGameController when layout mode changes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Increase narrow screen threshold from 1.35 to 1.45
The previous threshold was too low to catch some narrow aspect ratios
that should trigger narrow layout mode.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This is a stepping stone toward enabling -Werror for exhaustiveness checking
on sealed traits. Changes include:
- Enable -Wall in Scala toolchain configuration
- Add -Wconf suppressions for generated code and external dependencies
- Fix discarded value warnings by explicitly discarding with `val _ =`
- Fix non-local return warnings in JwtService using nested if/else
- Fix deprecated `= _` syntax using `= uninitialized` in OAuthHttpHandler
- Fix implicit parameter syntax: `(ec)` -> `(using ec)` in GrpcRetrier
- Remove unused imports across multiple files
- Add @unused annotation for intentionally unused parameters
- Fix wrong imports in SettingUpdater (was using wrong proto package)
- Fix BUILD.bazel exports to make GrpcRetrier available transitively
- BUG FIX: StringConstructionParser was silently ignoring parse errors
when closing character wasn't found (added missing return)
Note: -Werror is not yet enabled (TODO in tools/BUILD.bazel) because
there are still unused import warnings in some files. Once those are
fixed, -Werror can be uncommented to get compile-time exhaustiveness
checking.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Eagle's GameAdminServiceImpl now expects userId directly in requests,
removing its dependency on UserService for displayName lookups.
Changes:
- Rename new_username -> new_user_id in game_admin.proto
- Rename previous_username -> previous_user_id in ReassignFactionResponse
- Rename Result enum values: INVALID_USERNAME -> INVALID_USER_ID,
USERNAME_ALREADY_IN_USE -> USER_ALREADY_IN_USE
- Add resolveToUserId() to admin_server.go that accepts either userId
or displayName and resolves to userId using the Admin API
- Remove resolveToUserId and UserService dependency from GameAdminServiceImpl
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Shardok never used the user_name field - the C++ code completely ignores
it and the Unity client creates its own player names ("You" and "AI").
Changes:
- Mark user_name as reserved in player_info.proto
- Change ShardokInterfaceProxy to use humanFactions: Set[FactionId]
instead of playerToUserMap: Map[FactionId, String]
- Simplify GamesManager.resolveBattle to pass just the set of human factions
- Remove UserName assignments from CustomBattleHandler.cs
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The resolveDisplayName function calls adminClient.GetUser, which
requires admin authentication. Previously, fetchGames was passing
a plain context without the admin JWT token, causing GetUser to fail
and fall back to showing the UUID instead of the display name.
This fix changes fetchGames to accept the HTTP request and use
createAdminContext(r) which extracts the admin JWT token from the
request and adds it to the gRPC metadata.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>