Commit Graph
9987 Commits
Author SHA1 Message Date
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
6b55be9457 Add long-press support for right-click on touch devices (#5827)
* 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>
2026-02-04 08:35:58 -08:00
716dd97f75 Fix S3 credentials: support both env var formats (#5840)
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>
2026-02-04 08:09:18 -08:00
413f70a3b3 Add battalion diversity quest type (PR 1/3 - types only) (#5836)
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>
2026-02-04 07:52:59 -08:00
f2e08cdb5d Replace duel_challenged sound with licensed asset (#5825)
* 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>
2026-02-04 07:50:49 -08:00
056127c614 Fix S3 credentials: use DO_SPACES_* env vars (#5835)
* 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>
2026-02-04 07:20:22 -08:00
95dba55902 Add stale PR refs cleanup to iOS CI workflows (#5832)
* 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>
2026-02-04 07:14:06 -08:00
30379e3e81 Add S3 credentials to admin server container (#5834)
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>
2026-02-04 07:07:13 -08:00
d1a2434c4a Enable province order quest generation (PR 3/3) (#5833)
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>
2026-02-04 07:05:42 -08:00
801ce59e29 Add client handling for province order quests (PR 2/3) (#5830)
- 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>
2026-02-04 06:59:28 -08:00
67a57c73c8 Add click-to-dismiss on modal blockers as safety net (#5829)
* 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>
2026-02-04 06:58:56 -08:00
7bc5149e02 Fix What's New entries not showing (S3 fetch error was silently swallowed) (#5831)
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>
2026-02-04 06:57:17 -08:00
ffe40c0c7b Fix What's New 'Add Entry' button not working (#5828)
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>
2026-02-04 06:44:55 -08:00
2130313841 Add province order quest types (PR 1/3 - types only) (#5826)
* 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>
2026-02-04 06:41:47 -08:00
893bc3a009 Split Organize Troops cost display into separate value and warning labels (#5824)
- 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>
2026-02-03 21:20:59 -08:00
191dd0c3fd Request narrative prose output for backstory updates (#5823)
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>
2026-02-03 21:12:23 -08:00
faf53e642f Add clicked province panel support for narrow screens (#5822)
- 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>
2026-02-03 21:00:54 -08:00
45c6b22ef8 Add client update notification system (server-side) (#5820)
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>
2026-02-03 20:57:37 -08:00
7460b32098 Add battalion backstory to LLM prompt generators (#5813)
* 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>
2026-02-03 20:56:53 -08:00
d4c6f3f83c Change EventForHeroBackstory to use BattalionId instead of BattalionView (#5818)
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>
2026-02-03 20:27:17 -08:00
d1c30315c4 Update Notification Panel layout (#5817)
* 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>
2026-02-03 20:09:13 -08:00
b54a64becb Update Exile Vassal and Hero Gift panel layouts (#5816)
* Update Exile Vassal panel layout

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

* Update Hero Gift panel layout

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 19:31:21 -08:00
47e9af8808 Fix battalion backstory crash and generate initial backstories for raised battalions (#5814)
* 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>
2026-02-03 19:14:56 -08:00
627dab71c6 Improve Suppress Beasts layout and cost display formatting (#5815)
- 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>
2026-02-03 19:07:17 -08:00
465b1dbab0 Add battalion popup header with name and type icon (#5811)
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>
2026-02-03 18:55:18 -08:00
242df8d760 Fix None.get crash in OrganizeTroopsCommand backstory event creation (#5812)
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>
2026-02-03 18:47:03 -08:00
eb52ea5aaf Fix CI: detect and recover from git corruption (#5809)
* 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>
2026-02-03 18:32:10 -08:00
1bcac6ee27 Add battalion backstory events for riot, training, arming, and starvation (#5810)
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>
2026-02-03 18:31:12 -08:00
2562e99e3c Update TODO: mark shardok cancellation and what's new as done (#5807)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 18:11:23 -08:00
01bb6c47ab Add What's New feature to Unity client (#5805)
* 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>
2026-02-03 17:38:43 -08:00
85255d15d5 Style What's New summary as a button (#5808)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 17:24:34 -08:00
371d964922 Add battalion backstory events and LLM-driven updates (#5796)
* 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>
2026-02-03 16:55:46 -08:00
20f4ed5bc5 Cancel outstanding Shardok battles when game is deleted (#5806)
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>
2026-02-03 16:04:44 -08:00
7d69596176 Add What's New admin console and changelog script integration (#5804)
- 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>
2026-02-03 15:51:53 -08:00
689dc5e531 Add battalion backstory popup on hover (#5803)
* 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>
2026-02-03 15:03:36 -08:00
3a8f8a4aff Add backstory_text_id to BattalionView (#5802)
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>
2026-02-03 11:24:50 -08:00
b957cdeed7 Add command interaction guidance and hero stat gain tutorials (#5800)
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>
2026-02-03 10:57:49 -08:00
207f5e07ca Update alpha TODO: mark completed items, add end game ideas (#5801)
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>
2026-02-03 10:01:29 -08:00
fff739e144 Add battalion initial backstory generation (#5798)
* 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>
2026-02-03 09:44:48 -08:00
14fcfa0f03 Fix bug report panel not showing on first click (#5799)
* 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>
2026-02-03 09:39:06 -08:00
a098bf2f56 Add bug report form to Unity client (#5784)
* 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>
2026-02-03 09:10:44 -08:00
46b16ecbb7 Fix sporadic warmup binary build failure in CI (#5797)
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>
2026-02-03 07:06:25 -08:00
1112739c04 Unity layout updates: Recruit panel and Diplomacy button-style toggles (#5795)
* 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>
2026-02-03 07:05:48 -08:00
0e24333aaf Refactor HeroGenerator and BattalionNameGenerator for functional purity (#5791)
- 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>
2026-02-03 06:55:13 -08:00
4eb43010fd Fix casualtyCount to only reflect battalion losses (#5794)
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>
2026-02-03 06:31:02 -08:00
c9ca34e5e8 Fix None.get crash in SuppressBeastsSucceededPromptGenerator (#5793)
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>
2026-02-03 05:37:18 -08:00
5ffa08b0e5 Update Unity layout for command panel (#5792)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 21:53:42 -08:00