Commit Graph
100 Commits
Author SHA1 Message Date
adminandClaude Opus 4.6 6ba59f65c9 Add [BATTLE] debug logging to trace battle animation lifecycle
Logs battle additions, removals (both Eagle and Shardok paths),
and outstanding battles at round/phase transitions to diagnose
why battle animations persist during PlayerCommands phase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:39:51 -07:00
85b620b644 Remove battle from ShardokBattles when Shardok game ends (#6515)
When a Shardok game ends (victory/defeat), the game model was
removed from ShardokGameModels but the battle was left in
ShardokBattles (OutstandingBattles). The only other removal path
was RemovedBattleIds in Eagle diffs, which may arrive late or not
at all for auto-resolved battles the player never entered.

This left the battle animation running on the strategic map
indefinitely after the battle ended.

Fix: also remove from ShardokBattles when processing ended games
in the Shardok update path.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:30:39 -07:00
fb36684e35 Update battle effects even when SwapModel skips due to commands (#6514)
SwapModel returns early when both old and new model have available
commands, skipping mapController.Model assignment and all province
effect updates. This meant battle animations persisted after a
battle ended if commands were available at the time.

Fix: call mapController.UpdateBattleEffects before the early return
so battle start/end animations are always kept in sync with the
model, even when the full model swap is skipped.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:14:42 -07:00
b6d83e3775 Fix battle animation appearing at center of conquered province (#6513)
When a province changed hands after a battle, UpdateBattleEffects
identified the defender by looking up the current ruling faction of
the defender province. After conquest, the attacker's faction matched
the province owner and was skipped, while the old defender was
processed — with OriginProvinceIds containing the defender province
itself, producing key (Wichel, Wichel) and an animation at the
province center.

Fix: skip any origin that equals the defender province. This can
never produce a meaningful animation (midpoint = province center)
and eliminates the dependency on current province ownership for
defender identification.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 16:43:12 -07:00
2d3050eded Fix BetrayAllyQuest: breaking an alliance was failing the quest instead of fulfilling it (#6512)
The failure check treated "no longer allied" as a failure condition, but
that's the success condition. Now: failure only if the target faction no
longer exists. Also added a general fulfillment check so the quest succeeds
even if the inline check in BreakAllianceResolutionHelpers doesn't fire
(e.g. when the ambassador is imprisoned).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 16:38:33 -07:00
bb848e73f3 Restrict battle observation to participants and allies (#6509)
Non-involved players can no longer observe tactical Shardok combat. They
still see the battle-in-progress animation on the strategic map and learn
the outcome, but won't receive individual Shardok actions or be offered
the observation UI. Allied non-participants can still observe.

Controlled by the RestrictBattleObservation setting (default 1/on). Set
to 0 in the admin console to revert to the old behavior.

Also syncs settings.tsv with LLM settings that were previously only in
BUILD.bazel, and fixes the settings generator to include string_setting
dep and correct battalion_type_loader deps.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 16:15:49 -07:00
37c96eceec Fix settings generator and sync settings.tsv with BUILD (#6511)
The settings generator had two issues:
- Missing string_setting dep when String-type settings exist
- Wrong battalion_type_loader deps (proto target instead of model state)

Also syncs settings.tsv with LLM settings (llmProvider, openAiModelName,
claudeModelName, geminiModelName) that were previously only manually
added to BUILD.bazel, and updates the openai_model_name target to match
the generator's snake_case convention.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 16:08:56 -07:00
64e41a3c12 Set cannotBecomeOutlaw for tutorial opening battle (#6508)
* Set cannotBecomeOutlaw for tutorial opening battle

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

* Fix: only set cannotBecomeOutlaw for the first battle, not all tutorial battles

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:55:06 -07:00
b81e795350 Direct March and Send Supplies animations toward destination province (#6506)
Instead of fixed directions (March slides right, Send Supplies launches
upward), both animations now compute the direction to the destination
province from the game state diff and animate toward it.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:48:11 -07:00
f8c2b19609 Prevent King from inviting Bridget and Hedrick in tutorial (#6507)
Add -500 trust toward the King (faction 2) for both Bridget (faction 4)
and Hedrick (faction 5) so the King's AI never sends them invitations.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:42:58 -07:00
95fbbf91a3 Add cannot_become_outlaw flag to battle request (#6505)
* Add cannot_become_outlaw flag to per-faction battle request

When set on a faction's PlayerSetupInfo, the BecomeOutlaw command
is suppressed for that faction's units. Default false preserves
existing behavior.

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

* Remove default values for cannotBecomeOutlaw, require explicit specification

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:42:02 -07:00
a1bd0a61ae Add tutorial Mobilization phase start logic (#6482)
* Add tutorial Mobilization phase start logic (#6478)

When starting a tutorial at Mobilization or EagleAppears, fast-forward
the game state past the opening battle to a plausible mid-game state
where Sadar controls two provinces and King's satellites are vacated.

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

* Replace dynamic tutorial phase setup with JSON resource loading

Add an extractor tool and loader to store pre-computed tutorial phase
transition results as JSON resources instead of computing them at
runtime from game state. This eliminates fragile dependencies on
knowing exact hero IDs and post-battle state deltas.

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

* Populate expansion_results.json with battle resolution data

Extracted from a tutorial playthrough: actions 4-7 covering battle
resolution, province held, Tarn disappearance, and end of resolution
phase. Actions 0-3 (GAME_START through FOOD_CONSUMED) are already
produced by the initial game creation flow.

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

* Pretty-print expansion_results.json

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:38:48 -07:00
0553da332e Boost tutorial defender unit sizes and Luke's faction bias (#6504)
Increase Sadar's three starting battalions by 15% (460→529, 270→311,
346→398) to give the player a stronger opening battle position.
Raise Luke the Prank-tricker's Sadar faction bias from 80 to 110.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:57:58 -07:00
d39038296f Update Unity prefab layouts and reimported assets (#6503)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:30:03 -07:00
36bc30f2bb Scale province action animations up ~30% (#6502)
Increase all spatial fields (icon sizes, distances, heights, spreads,
radii) by approximately 1.3x across all province action animation types.
Durations, counts, angles, and colors are unchanged.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:08:35 -07:00
1952ab796d Color March "keep" amount red when over province capacity (#6501)
Match the Send Supplies behavior: goldLeftLabel and foodLeftLabel turn
red when the amount remaining in the origin province would exceed its
gold or food capacity.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:43:40 -07:00
118311209f Add map validation: no starting positions on water, mountains, or overlapping (#6498)
Validates all maps to ensure starting positions are not placed on
impassable terrain (water, river, mountain) and that attacker starting
position sets don't overlap with each other.

Kojaria currently fails both checks and needs a map editor fix.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 07:40:47 -07:00
adminandGitHub 75b93ac3fc Update map: Kojaria (#6500) 2026-03-13 07:34:30 -07:00
fe5360d6f1 Fix missing flee/captured sound when battle ends (#6497)
When an AI unit flees and ends the battle, the GameStateViewDiff
removes the unit from UnitsById before ModelUpdated processes the
history. The sound-playing code required the actor to be found in
UnitsById to determine animation coordinates, silently skipping
the entire sound block when the lookup failed.

Add a fallback else branch that plays the sound without animation
when the actor unit has been removed from the model.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 07:26:29 -07:00
adminandGitHub 7dc0ce220f Update map: Chapellia (#6496) 2026-03-13 07:20:49 -07:00
74ade84dcd Replace Feast sound effect with crowd victory cheer (#6495)
The previous "battle_shout" sound felt more like rioting than
feasting. Replace with a group victory cry from freesound.org
(chripei, CC BY 4.0) that better conveys celebration.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 07:16:53 -07:00
f626eff404 Update Gameplay scene RectTransform anchors and prefab overrides (#6494)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 21:44:53 -07:00
0b06173e96 Fix Shardok overlay flash on unit selection (#6493)
* Stop label updates from clearing hex background highlights

SetUnitInfoLabels and SetOverlayLabel were calling SetCellBaseColor
(which sets Geometry.Color = Color.clear) and hexMesh.Triangulate on
every invocation. These methods manage TMP_Text labels, not hex
geometry, so the SetCellBaseColor and Triangulate calls were
unnecessary. Removing them eliminates O(N²) Triangulate calls per
selection change (one per cell, each iterating all cells).

ClearOverlays now explicitly resets Geometry.Color for cells that had
a BackgroundHighlightColor and does a single Triangulate when needed.

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

* Defer hex background highlight updates to HexGrid.Update for atomic mesh rendering

ClearOverlays was eagerly triangulating hexMesh (clearing background
highlights) while overlayMesh borders were only updated in Update().
This caused the two meshes to render out of sync for one frame when
Update order put HexGrid before MouseHandler. Now both meshes are
updated atomically in Update(), and ClearOverlays just clears the
data. Also reduces hexMesh.Triangulate calls from O(N) per overlay
cell per frame to at most one.

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

* Fix overlay label/image flash by deferring all cleanup to Update

ClearOverlays was eagerly clearing labels and follow-up images while
the overlay border mesh was only updated in HexGrid.Update(). When
Unity's script execution order put HexGrid.Update before
MouseHandler.Update (which changed after the Shardok prefab
extraction in #6485), the labels/images were cleared mid-frame but
not re-set until next frame — causing a visible one-frame flash on
every unit selection.

Now ClearOverlays just clears the data list. Update() owns all visual
state: it clears stale labels, images, and background highlights from
the previous frame, sets new ones from current overlayCells, and
triangulates both meshes — all atomically regardless of script order.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 21:41:24 -07:00
32a58fd8eb Clean up orphaned animation sprites when Map deactivates (#6492)
Animation objects are parented to mapContainer, not the animator. When
the Map deactivates mid-animation (e.g. entering a battle), coroutines
die but the partly-faded sprites linger. Track all objects created by
CreateUIObject and destroy any survivors in OnDisable.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 21:22:17 -07:00
53eb739cfe Add elephant/mammoth beast effect (#6489)
* Add elephant/mammoth beast effect using Animal Pack Deluxe v2

Wire up African and Indian elephant prefabs from the newly purchased
Animal Pack Deluxe v2 as a proper beast effect. Mammoths reuse the
same elephant models. Includes a custom ElephantMapAnims controller
to remap v2 animation state names to AnimalEffect's expected states.

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

* Fix elephant animation: use v2 pack's own controller, fix prefab refs

- Use the v2 pack's African_elephant_anim_controller directly instead
  of our custom ElephantMapAnims (which couldn't resolve FBX clips)
- Fix animalPrefabs fileIDs to match actual root GameObjects
- Regenerate animation FBX .meta files for Unity 6 compatibility

The v2 controller's state names don't fully match AnimalEffect's
expected names (idle A/attack A vs idle/attack), so animations are
partial. Next step: remap state names to get all 4 animations working.

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

* Rename v2 controller states to match AnimalEffect expectations

Rename 'idle A ' → 'idle' and 'attack A' → 'attack' in the v2 pack's
African_elephant_anim_controller so all 4 animation states work with
AnimalEffect's Animator.Play() calls.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:09:17 -07:00
4e39baf3fa Queue province animations while Map is inactive, replay on reactivate (#6490)
Action results arriving from the server while the Map game object is
inactive (e.g. during a battle) are queued and replayed 1s after the
Map reactivates, so the player sees what happened while they were away.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:53:24 -07:00
95ce201b20 Apply lightweight loop filter at root of iterative deepening search (#6491)
The full AICommandFilter was only applied during lookahead (inside
FindBestCommand), not at the root level of IterativeSearch. This
caused the AI to repeatedly choose METEOR_TARGET_COMMAND because the
re-target filter never ran on the top-level command list.

Adds FilterLoopingCommands — a minimal filter that only removes
genuinely never-useful commands (meteor re-target/cancel when a target
is already set). The full aggressive filter stays in lookahead only,
where pruning is a performance optimization. Applying the full filter
at the root could miss moves that look bad locally but prove
worthwhile with deeper search.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:47:28 -07:00
aeeb3ec8e0 Fix AI crash: meteor cancel with 0 AP after meteor start (#6487)
METEOR_START uses ActionCost::usesAll which drains AP to 0. The
MeteorTargetCommandFactory then unconditionally generates a
METEOR_CANCEL command (cost 1 AP) for the mage in TARGET state.
When the AI evaluates this command, MutatingSpendActionPoints throws
because 1 > 0.

Two fixes:
1. Set meteorCancelActionPointCost to 0, matching #6454's fix for
   meteorTargetActionPointCost.
2. Filter out meteor re-target/cancel in AICommandFilter when the
   mage already has a target. Re-targeting exists for human misclick
   correction; the AI commits to its first choice and could otherwise
   loop endlessly re-targeting at 0 AP cost.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 18:41:54 -07:00
6886964efa Fix mammoth stats from bandit template to large animal (#6488)
Mammoth had human-bandit-like stats (likelihood 0.10, power 1.5,
economy devastation) instead of large-animal stats. Update to be
rarer than elephants with appropriate power, count, and devastation.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 06:40:48 -07:00
aa77094790 Extract Command Panel and 35 sub-panels into prefabs (#6486)
* Extract Command Panel into prefab

Move the Command Panel hierarchy (37 command sub-panels) from
Gameplay.unity into its own prefab at Assets/Eagle/. This is the
largest single component in the scene at ~126K lines. Scene-level
cross-references (eagleGameController, connectionHandler, hexGrid,
shardokContainer, etc.) are preserved as PrefabInstance overrides.

Reduces Gameplay.unity from ~232K to ~102K lines (56% reduction).

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

* Extract 35 command sub-panels into individual prefabs

Break down Command Panel.prefab (126K lines) into 35 individual
prefabs under Assets/Eagle/CommandPanels/. Command Panel itself
becomes a thin container (~6K lines) with PrefabInstance references.

Largest sub-panel (March Panel) is 12.7K lines; smallest are ~230
lines. All are independently editable and produce focused diffs.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 21:45:54 -07:00
3ccdcaa6cd Extract Shardok Container into prefab (#6485)
Move the Shardok Container hierarchy (Shardok Canvas + Weather Canvas)
from Gameplay.unity into its own prefab at Assets/Shardok/. External
field references (mainCamera, eagleCanvas, soundManager, audioClipSource)
are preserved as scene-level PrefabInstance overrides.

Also adds UNITY_POST_PROCESSING_STACK_V2 scripting define symbols.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:18:52 -07:00
c8c95ac1b6 Refactor EagleCommonTextures to singleton pattern (#6484)
Eliminates 3 duplicate instances by adding a static Instance property,
replacing all GetComponentInParent lookups and serialized field references
with EagleCommonTextures.Instance. Removes the duplicate components from
ConnectionCanvas, Shardok Container, and Eagle Canvas in the scene, and
moves the single remaining instance to Main Camera so it's guaranteed to
have Awake() called regardless of which canvases are active.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:03:04 -07:00
bbbe4211e3 Extract Custom Battle Panel into prefab (#6483)
* Extract Custom Battle Panel into prefab

Remove baked-in Custom Unit Row instances from the prefab since
EventBasedTable.Awake() destroys them and recreates from rowPrefab
at runtime. Also remove stale SimpleFileBrowser references from
eagle0.sln.

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

* Convert Custom Battle Panel prefab from LFS to regular git

This prefab is actively edited line-by-line and benefits from normal
git diff/blame/merge. LFS is better for opaque binary assets.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:25:51 -07:00
a1391cdad2 Add missing Unity .meta files and import artifacts (#6481)
* Add missing Unity .meta files, HoneyBadger import artifacts, and fix_import.py

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

* Remove HoneyBadger import artifacts and gitignore FBX auto-generated files

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 09:21:22 -07:00
c25d042690 Remove SimpleFileBrowser plugin and associated UI (#6480)
Remove the SimpleFileBrowser plugin and its usage in CustomBattleHandler
(custom map loading and game request loading via file dialogs). Remove
the Custom Map Toggle, Load Button from the Custom Battle Panel, and
the obsolete Tutorial Lite button from the Lobby Panel prefab.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 08:58:21 -07:00
d9666dc01d Apply persisted settings at startup even when Settings Panel is inactive (#6479)
The Settings Panel prefab starts inactive, so Start() never runs until
the user opens it. This caused persisted values (province label font
size, audio volumes, tooltip behavior) to not be applied until Escape
was pressed, producing a visible font resize.

Split initialization: ApplyPersistedSettings() loads PlayerPrefs and
applies to game systems early via SettingsKeyListener.Start() on the
always-active PersistentCanvas. The deferred Start() just syncs UI
sliders/toggles to match the already-applied values.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 07:57:40 -07:00
cd9fc49bde Replace tutorial buttons with phase dropdown (#6474)
* Replace tutorial buttons with phase dropdown in ConnectionHandler

Replace the two separate tutorial buttons (tutorialButton, skipBattleTutorialButton)
with a TMP_Dropdown + Button combo so any tutorial phase can be selected from the lobby
without adding more buttons for each new phase.

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

* Fix C# TutorialPhase enum names to match protobuf codegen

C# protobuf strips the common prefix, so the enum values are
TutorialPhase.OpeningBattle (not TutorialPhaseOpeningBattle), etc.

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

* Wire tutorial phase dropdown and start button in Gameplay scene

Add missing using directive for Net.Eagle0.Eagle.Common namespace
and re-wire tutorialPhaseDropdown and startTutorialButton to the
Lobby Panel prefab instance.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 07:25:31 -07:00
1ca4ee5cc0 Extract Connection, Lobby, and DropGameConfirmation panels into prefabs (#6478)
Reduces Gameplay.unity by ~13k lines by extracting three connection UI
panels into standalone prefabs with scene-level overrides for callbacks
that reference the scene-level ConnectionHandler.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 07:12:39 -07:00
42957266ba Add wind-assisted range 3 archery for longbowmen (#6457)
Longbowmen can now target enemies at range 3 when shooting in the
direction the wind is blowing (±1 hex sector), provided wind speed
meets the configurable `longbowWindBonusRangeMinSpeed` threshold
(default 20 mph). The 20 mph threshold reflects the point where wind
meaningfully extends arrow range, below the 35 mph blizzard threshold
where archery is already disabled.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 07:06:31 -07:00
6a81ce97db Replace tutorial booleans with TutorialPhase enum (#6477)
* Replace tutorial booleans with TutorialPhase in CreateGameRequest

Replace `is_tutorial` + `skip_tutorial_battle` booleans with a single
`TutorialPhase tutorial_start_phase` field, making the API extensible
for future tutorial phases (Mobilization, etc.) without adding more
boolean flags.

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

* Add service visibility to tutorial_phase_converter BUILD target

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

* Keep old tutorial boolean fields in CreateGameRequest proto

Keep is_tutorial and skip_tutorial_battle fields so the Unity build
doesn't break. They'll be reserved in the client-side PR.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 07:05:11 -07:00
5b84d20f69 Extract Settings Panel into prefab (#6476)
* Extract Settings Panel into prefab to reduce Gameplay.unity size

The Settings Panel hierarchy (~11k lines) was inline in the scene file,
making Gameplay.unity harder to diff and merge. Extract it into a
standalone prefab with scene-level overrides for cross-references.

Fix 15 event callback targets that Unity zeroed out during extraction
(sliders, toggles, buttons all needed re-pointing to the
SettingsPanelController inside the prefab).

Add SettingsKeyListener on PersistentCanvas to handle Escape/F11 keys,
since the SettingsPanelController now lives on the (sometimes inactive)
prefab root and can't run Update() when hidden.

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

* Remove scene overrides that zeroed out prefab event callbacks

The prefab instance in Gameplay.unity had overrides that set 6 event
callback targets to null, overriding the correct values in the prefab.
Remove these overrides so the prefab's own targets take effect.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 22:14:49 -07:00
b3adebbb0a Add tiger beast effect using ithappy Animals FREE pack (#6475)
* Add tiger and lion beast effects using ithappy Animals FREE pack

Import the free ithappy Animals FREE asset pack and create tiger/lion
beast effects. The tiger model is reused at 1.7x scale for lion.
A custom TigerMapAnims controller maps ithappy's idle/walk/idle_rare/run
clips to AnimalEffect's expected idle/walk/eat/attack state names.

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

* Remove lion effect (scaled tiger is not convincing)

Lion falls back to generic vultures until a proper model is found.

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

* Fix ithappy files stored as LFS objects breaking Unity import

The broad ithappy/** LFS rule was storing text YAML files (.controller,
.mat, .prefab, .anim, etc.) as LFS objects, causing Unity to fail with
"File may be corrupted" errors. Remove the rule and re-add files so text
files are stored as regular git objects while binaries (.fbx, .png) are
still covered by per-extension LFS rules.

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

* Strip third-party scripts/physics from animal prefabs on spawn

ithappy Tiger_001 prefab includes CharacterController, CreatureMover,
and MovePlayerInput components that conflict with AnimalEffect's own
movement system. DestroyImmediate these after instantiation to prevent
"CharacterController.Move called on inactive controller" errors.

Also wires up tigerEffectPrefab in Gameplay.unity.

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

* Move beast effect prefabs to Addressables for CDN delivery (#6373)

Replace 33 serialized GameObject fields in ProvinceBeastsController with
Addressables.LoadAssetsAsync label-based loading ("beast-effects").
Prefabs stay in Assets/Eagle/Effects/ but are now delivered via CDN at
https://assets.eagle0.net/addressables/[BuildTarget] instead of being
bundled in the app binary. This removes ~80-100 MB of 3D models,
textures, and animations from the initial app download.

Loading strategy:
- Eager load in Start() (last priority — after music)
- Game state that arrives before loading completes is stashed and
  applied when ready
- If loading fails (offline, no cache), beasts simply don't render

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Add tiger and elephant beast effects using Animal Pack Deluxe

Route "tiger" and "elephant" beast names to dedicated TigerEffect and
ElephantEffect prefabs in ProvinceBeastsController. Prefabs to be wired
up in the Unity editor using existing Animal Pack Deluxe assets.

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

* Register TigerEffect in Addressable beast-effects group

The TigerEffect prefab was added in the add-tiger-effect branch but was
never registered in the Beast Effects Addressable group, so LookupPrefab
returned null at runtime. Also removes a duplicate "tiger" switch case
from the merge.

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

* Fix AnimalEffect component removal errors on spawn

Instantiate prefabs inactive to prevent CharacterController stepOffset
validation errors, and remove MonoBehaviours in reverse component order
to respect [RequireComponent] dependency chains before removing
CharacterController.

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

* Fix ithappy Animals_FREE material to use Built-in Standard shader

The material shipped with a URP Lit shader reference, causing solid pink
rendering on Built-in render pipeline. Swap to Built-in Standard shader
and remove the URP version tracker MonoBehaviour.

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

* Fix tiger animations by using standalone .anim clips

The ithappy Animals_FREE FBX was imported with avatarSetup: 0 (no
avatar), so its embedded animation clips had no bone binding data.
Switch TigerMapAnims.controller to reference the standalone .anim files
which have explicit transform path bindings. Also build a Generic avatar
at runtime for prefabs missing one, and set up the Animator before
activation so it initializes with correct avatar and controller.

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

* Fix duplicate Start/LoadBeastEffects from rebase

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

* Remove unused ithappy Animals_FREE assets

Only the tiger model, texture, material, and animations are needed.
Removes all other animals (chicken, deer, dog, horse, kitty, penguin),
demo scenes, skyboxes, scripts, floor material, animation controllers,
and render pipeline conversion files.

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

* Remove CharacterController and deleted scripts from Tiger prefab

The prefab still referenced CreatureMover and MovePlayerInput scripts
that were deleted in the previous commit, causing missing script errors
in the Unity editor.

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

* Update Tiger_001 prefab after Unity re-serialization

Unity re-serialized the prefab after we removed the CharacterController
and missing script components. This is the clean editor-saved version.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 16:44:05 -07:00
007fd039be Add battle-in-progress animation on strategic map (#6473)
* Add battle-in-progress animation on strategic map

Display looping weapon-clash effects between defender and attacker origin
provinces while battles are active. Follows the ProvinceFestivalController
pattern for centroid loading and effect lifecycle management.

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

* Load battle sound from sfx-combat addressables instead of inspector field

Use the "melee" clip from the "sfx-combat" addressable label, matching
the pattern used by ProvinceActionAnimator and SoundManager.

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

* Tune execution animation duration

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

* Fix battle effect: orientation, sound, two weapons, test provinces

- Fix upside-down weapons by flipping weapon2 Y-scale (mirroring)
- Play melee clip as one-shot at each impact instead of continuous loop
- Use two different weapon sprites (sword + axe) for visual variety
- Change test ContextMenu to use adjacent provinces 2 and 5
- Add Gameplay.unity scene wiring

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

* Add arrow volleys, fix weapon orientation, fix audio pattern

- Add arrow volley animations flying between province centers with
  parabolic arc, random direction, and archery sound
- Fix weapon rotation: per-weapon offsets (sword blade down = +90,
  mace head up = -90) so both swing business-end first
- Fix arrow sprite rotation offset (-135 for strategic map UI space)
- Fix audio: move playback to controller using Action callbacks,
  matching ProvinceActionAnimator pattern (same object owns AudioSource
  and calls PlayOneShot)
- Load audio in OnEnable with Dictionary<string,AudioClip> like
  ProvinceActionAnimator
- Add configurable volume and rotation offset inspector fields

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

* Add meta files for new battle effect scripts and addressable settings

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:31:54 -07:00
6472e1dabe Add origin province IDs to ShardokBattleView for battle-in-progress animations (#6472)
Expose attacker army origin provinces in the ShardokBattlePlayerInfo view so the
Unity client can draw battle-in-progress animations between defender and attacker
origin provinces on the strategic map.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 12:30:19 -07:00
d9e7b55279 Split captured hero recruitment ActionResultTypes and add animations (#6470)
* Split captured hero recruitment into distinct ActionResultTypes and add captured hero animations

HandleCapturedHeroesCommand previously used RecruitHeroes (21) for both successful
and failed captured hero recruitment, making them indistinguishable on the client.
Add CapturedHeroRecruited (162) and CapturedHeroRecruitmentRefused (163) so the
Unity client can play the correct animation for each outcome. Also add province
animations for all 6 captured hero dispensations (recruited, refused, imprisoned,
exiled, executed, returned).

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

* Add sound effects for captured hero animations

Map each captured hero ActionResultType to an appropriate sound clip,
reusing the same clips as the animations they share patterns with.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:15:24 -07:00
2e8d2bc83d Split captured hero recruitment into distinct ActionResultTypes (#6471)
HandleCapturedHeroesCommand previously used RecruitHeroes (21) for both successful
and failed captured hero recruitment, making them indistinguishable on the client.
Add CapturedHeroRecruited (162) and CapturedHeroRecruitmentRefused (163) so the
client can play the correct animation for each outcome.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:08:39 -07:00
1776198890 Fix tutorial duplicate hero bug by changing Tarn from Fled to Outlawed (#6469)
* Fix tutorial duplicate hero bug by changing Tarn from Fled to Outlawed

PR #6464 set fleeProvinceId to None for the tutorial attacker army, but
TutorialBattleAutoResolve still marked Tarn as Fled. With no flee
province, unitReturned() returned true for Fled, triggering the
shattered army path which created outlaws for ALL attacker heroes —
duplicating the heroes already created via defenderCP's newOutlaws.

Changing Tarn to Outlawed avoids the shattered army path entirely while
preserving the tutorial storyline: Tarn escapes capture and doesn't
appear in HandleCapturedHeroes, then EagleAppearsAction picks him up.

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

* Update TutorialTarnDisappearsAction to handle Outlawed status

With Tarn now marked as Outlawed (instead of Fled), he ends up as an
unaffiliated hero in the battle province rather than in an incoming
army to Nikemi. Update the disappears action to remove him from
unaffiliated heroes so he's properly hidden until EagleAppearsAction.

Also remove the now-dead fled/incoming-army logic and unused MovingArmy
import.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 09:50:28 -07:00
a74cf4fbbf Fix missing Ranil and Elena portraits in post-battle tutorial dialogue (#6468)
Set speakerImagePath directly in tutorial_strategic.json for John Ranil
and Elena Fyar instead of relying on dynamic resolution, which can fail
due to timing issues with the Eagle model.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 08:51:52 -07:00
90c9327b25 Add tutorial indicator to lobby running game items (#6467)
* Add tutorial indicator to lobby running game items

Add a tutorialIndicator GameObject field to RunningGameItem that is
shown/hidden based on the game_type from the server. Pass GameType
through from ConnectionHandler so the client can distinguish tutorial
games in the lobby list.

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

* Wire tutorial indicator in Running Game prefab

Connect the Tutorial TextMeshPro label to the tutorialIndicator
field on RunningGameItem so it shows/hides based on GameType.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 08:27:15 -07:00
51579752a2 Add 19 province action animations with sound effects (#6466)
Adds one-shot visual animations at province centers for all remaining
player commands: Rest, Train, OrganizeTroops, ArmTroops, RecruitHeroes,
Trade, OrdersIssued, SentSupplies, SuppressBeasts (success/fail),
Divined, March, SwearBrotherhood (success/fail), ControlWeather, Alms,
HeroGift, ApprehendOutlaw, Recon, ExileVassal, and StartEpidemic.

Each animation includes inspector-tunable parameters, a ContextMenu test
method, and a sound effect mapping wired through the actionSounds array.
21 audio clips added to the sfx-combat Addressables group.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:40:23 -07:00
e8be706370 Add game_type to lobby GameInfo proto and server (#6465)
Plumb the existing GameType enum (NORMAL/TUTORIAL) through to the lobby
response so clients can distinguish tutorial games from normal games.

- Add game_type field to RunningGame (persistence) and GameInfo (API) protos
- Add gameType to GamePlayerInfo case class and populate it in all 4
  construction sites (gamesFor cached/full-load, gamesForWithoutBlocking
  loaded/unloaded) plus save()
- Pass gameType through to GameInfo in EagleServiceImpl

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:37:04 -07:00
b313ad6507 Fix tutorial battle flee by setting fleeProvinceId to None and remove dead canFleeHeroIds (#6464)
The tutorial attacking army had fleeProvinceId set to its origin province, which
allowed Tarn's forces to flee during the opening battle. Since canFleeHeroIds was
always Set.empty, the flee logic fell back to fleeProvinceId.isDefined (true).
Fix by setting fleeProvinceId = None for the tutorial army, and remove the unused
canFleeHeroIds field entirely from ShardokBattle, its proto, converter, and all
call sites.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:11:49 -07:00
bb02481df9 Add TutorialPhase to GameType ADT (#6453)
* Add TutorialPhase to GameType ADT to track tutorial progression

Converts GameType from a simple Normal/Tutorial enum into an ADT where
Tutorial wraps a TutorialPhase (OpeningBattle, Expansion, Mobilization,
EagleAppears, Complete). This separates "what kind of game" from "what
phase of the tutorial", enabling phase-aware event logic and future
ability to start at a specific phase.

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

* Fix C# tutorial check to match on Tutorial specifically

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

* Use TUTORIAL_PHASE_ prefix for proto enum values

Follow Google's protobuf enum naming best practice of prefixing all
values with the enum name.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 06:47:00 -07:00
10b43e9225 Add necromancer control chain visual effect (#6463)
Pale yellow/white particle chain connects necromancers to their
controlled undead units. Shimmers in and out when not actively
controlled this round, becomes solid translucent when controlled.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:06:59 -07:00
f79b89a721 Widen meteor beam destination and speed up during Cast phase (#6462)
Increase destinationScale from 45 to 60 for a wider cone at the target.
Track the mage's meteor phase per beam and apply a 3x speed multiplier
to vibration and shimmer during the Cast phase.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:57:10 -07:00
bcf1641d39 Add sound effect for meteor start animation (#6461)
* Add sound effect for meteor start animation

Map ActionType.MeteorStart to Fire Spelll 01 from the magic spells
sound pack so the meteor preparation animation has an accompanying
magic charging sound.

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

* Add sound effect for meteor target animation

Map ActionType.MeteorTarget to Fire Spelll 02 so the targeting
crosshairs animation also plays a fire magic sound.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:55:52 -07:00
c43750c206 Fix tutorial button visibility not updating after login (#6460)
SetupLobbyUI ran at startup before login, so IsAdmin was stale.
Re-evaluate admin-gated button visibility in OnOAuthLoginSuccess.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:38:44 -07:00
4c81a90aac Add Feast province animation with food toss effect (#6459)
Add a feast animation to ProvinceActionAnimator that spawns food items
(drumstick, beer, wine, grapes, cheese, bread) tossed into the air on
parabolic arcs with spin and fade. Plays a battle_shout cheering sound.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:36:13 -07:00
491a9427e6 Skip auto-selecting mage with existing meteor target (#6458)
* Skip auto-selecting mage unit that already has a meteor target

When a mage already has a CastTarget set, don't auto-select them
via SelectAppropriateDefaultCommand. The player can still manually
click the unit to re-target, but we no longer default to showing
commands for a unit that has already targeted.

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

* Find untargeted mage when multiple have MeteorTarget available

Instead of checking the first unit with MeteorTargetCommand and
skipping the entire command type if that unit already has a target,
search through all available commands of the type to find a unit
that hasn't targeted yet.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:38:51 -07:00
f43a2b852c Wire ProvinceActionAnimator in Gameplay scene (#6456)
* Wire ProvinceActionAnimator in Gameplay scene

Add ProvinceActionAnimator component to the scene with Inspector
references and action sound configuration for Improve animations.

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

* Update ProvinceActionAnimator scene wiring

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:25:38 -07:00
85e499e47f Add game ID and backtrace to AI thread exceptions (#6455)
Log game ID, player ID, round, and stack trace to stderr before
re-throwing, so AI thread crashes are diagnosable without a debugger.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:17:05 -07:00
62730414bd Add province action animations to Eagle strategic map (#6451)
* Add province action animations to Eagle strategic map

Play one-shot animations at province centers when action results arrive.
Starting with the Improve action, which shows a hammer-strike animation
with sparks. Sound effects loaded from sfx-combat Addressables by name.

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

* Fix hammer head alignment and restore rotation defaults

Offset tool position upward by half the tool size so the hammer head
(bottom of rotated image) aligns with the province center where sparks
appear. With Image+RectTransform the pivot is at center, unlike
SpriteRenderer which uses the sprite's imported pivot — without the
offset the middle of the hammer sat on the province.

Restore hammerBaseRotation=180 and hammerFlipHorizontal=true to match
ToolAnimator defaults.

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

* Set hammer rotation default to 90, fix sound loading robustness

Change hammerBaseRotation default to 90 to match tested working value.

Sound effects were silently failing — the Addressables coroutine started
in Awake() could be interrupted if the GameObject was deactivated during
scene setup. Now uses lazy-load pattern: retries loading on first play
attempt if clips aren't ready. Added diagnostic logging for load
success/failure and missing clip names.

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

* Remove defensive null checks on inspector fields

Let missing inspector wiring throw NullReferenceException immediately
rather than silently skipping. This was hiding the sound bug — the old
guard clause bailed before any logging could fire.

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

* Fix sound loading: move coroutine to OnEnable, remove silent null guard

The LoadSoundEffects coroutine started in Awake() was being killed when
the GameObject gets deactivated during scene setup. Moving it to
OnEnable() ensures it retries on reactivation. Also removes the
_loadedClips null guard so failures are visible via NullReferenceException
rather than silently skipping sound playback.

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

* Play metal strike sound per hammer impact, add to Addressables

Switch from repair sound to Metal Weapon Hit Stone 2_1 for Improve
animation. Sound now plays once per hammer strike at impact rather
than once at the start of the animation. Added the clip to the
sfx-combat Addressables group.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:15:48 -07:00
5cd5163c79 Make meteor targeting free (0 AP) to fix re-targeting (#6454)
PR #6450 allowed meteor re-targeting, but it fails at runtime because
the initial target uses ActionCost::usesAll which drains all AP to 0,
leaving nothing for the re-target. Setting the target cost to 0 fixes
this: usesAll with minimum 0 always succeeds (0 >= 0), still drains
remaining AP (ending the mage's turn), and removes the vigor penalty.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:50:11 -07:00
ed35d00c60 Fix Eagle Appears tutorial event to match normal game faction setup (#6448)
Previously province 6 (Soria) had battalions but no heroes, and province
stats were hardcoded only for province 32. Now The Eagle's faction matches
the normal game: Tarn defects from faction 2, random vassals are generated
(5 for province 32, 4 for province 6), per-province stats are correct,
faction is hostile to Bregos with trust -100, and battalion names use
"Tarn's" prefix for province 6.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:43:04 -07:00
8772b5a988 Fix validation error when non-allied armies converge on unoccupied province (#6452)
The validateAtMostOneAttackingAlliance check was firing during
UncontestedConquest phase, before PerformUncontestedConquestAction could
resolve the situation (largest army conquers). Skip the validation during
UncontestedConquest, matching the existing AttackDecision exemption.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:41:09 -07:00
fa1f0ffcb0 Replace hardcoded IsTester with is_admin from UserInfo proto (#6449)
Add is_admin field to UserInfo proto and populate it through Go auth
handlers, Scala fallback handler, and C# client storage. Replace the
hardcoded display name check with the server-provided admin flag.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:25:49 -07:00
05cb22dcd7 Allow meteor re-targeting after initial target selection (#6450)
Previously, once a mage selected a meteor target, the command was
locked in with no way to correct a misclick. Now the target/cancel
commands remain available after targeting, but as optional (not
required to end turn), so the player can re-target, cancel, or
simply end their turn keeping the current target.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:23:58 -07:00
adminandGitHub 60203343ab Tutorial tuning: boost Luke's faction bias, add Hedrick prestige penalty (#6447) 2026-03-08 22:08:53 -07:00
adminandGitHub 072749c63f Add persistent meteor targeting beam effect (#6446) 2026-03-08 22:07:49 -07:00
e850ba7546 Trim two-stage animation doc to just the implementation pattern (#6445)
All two-stage actions now have distinct success/failure animations.
Remove the completed status tracking table and animation descriptions,
keeping only the 7-step wiring guide for adding new two-stage actions.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:39:27 -07:00
f2ff4cc912 Add distinct success/failure animations for BraveWater (#6443)
* Add distinct success/failure animations for BraveWater

Split the shared BraveWater animation into two-stage success/failure variants:
- Success: Strong upward splash with bright droplets, expanding ripples, blue-white flash
- Failure: Droplets rise partway then fall back, darkening color, foam churns turbulently

Updates ShardokGameController enum, mapping methods, and dispatch.
Adds AnimateBraveWaterSuccess/AnimateBraveWaterFailed to WaterEffectAnimator.

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

* Add BraveWater success/failed test buttons to AnimationTestController

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

* Use distinct splash sound for BraveWater success

Attempt uses braved_water (wading in), success now uses splash
(satisfying water crossing) so the two phases sound different.

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

* Make BraveWater failure animation more dramatic and violent

Rewrote AnimateBraveWaterFailedEffect with 4 phases: high promising rise
(14 large particles with wide spread), brief stall with shuddering and
color flickering at peak, violent cubic-acceleration crash back down past
center with particles growing on impact, and aggressive turbulent churn
with large oscillations darkening to murky water.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:36:26 -07:00
4e84dbc4a4 Add distinct success/failure animations for DismissUnit (#6442)
* Add distinct success/failure animations for DismissUnit

Split the shared DismissUnit animation into two-stage success/failure variants:
- Success: Golden particles rise upward with warm golden glow pulse
- Failure: Particles expand then snap back inward, turning red with rejection flash

Updates ShardokGameController enum, mapping methods, and dispatch.
Adds AnimateDismissSuccess/AnimateDismissFailed to DismissAnimator.

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

* Add DismissUnit success/failed test buttons to AnimationTestController

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

* Double the spread width of DismissUnit success and failure effects

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

* Use undead_break_control sound for DismissUnit failure

The undead have broken free — use the dramatic control-broken sound
instead of the generic failure horn.

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

* Scale up DismissUnit effects: attempt 50% wider/taller, particles 2x

Attempt: particle spread multiplied by 1.5x, rise height 1.5x.
Success and failure: particle sizes doubled (3-6 to 6-12, 3-5 to 6-10).

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

* Fix attempt particles: use visible sizes (4-8) instead of near-zero (0-4)

The original Random.Range(4f, 0f) produced values 0-4 with many near
zero, making particles invisible. Now uses 4-8 for consistently visible
particles that shrink to zero as they drift.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:25:04 -07:00
f040b4df3a Add distinct success/failure animations for RaiseDead (#6441)
* Add distinct success/failure animations for RaiseDead

Split the shared RaiseDead animation into two-stage success/failure variants:
- Success: More figures rise quickly with bright green glow and pulse
- Failure: Figures rise partway then collapse back, glow flickers green to grey

Updates ShardokGameController enum, mapping methods, and dispatch.
Adds AnimateRaiseDeadSuccess/AnimateRaiseDeadFailed to RaiseDeadAnimator.

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

* Add RaiseDead success/failed test buttons to AnimationTestController

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

* Use distinct success sound for RaiseDead (undead_grew instead of raise_undead)

Attempt uses raise_undead (eerie summoning), success now uses
undead_grew (undead power surge) so the two phases sound different.

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

* Make RaiseDead failure more dramatic and use impactful success sound

Failure: figures now rise 75% of the way (was 40%), fully visible at
near-full opacity, stall with flickering glow shifting green to grey,
then get yanked back down with cubic acceleration. Much more visible
contrast between "almost worked" and sudden collapse.

Success sound: use holy_wave_damage for a darker, more impactful surge
instead of undead_grew.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:01:21 -07:00
cab6883c66 Add distinct Scout success/failure animations (#6440)
* Add distinct Scout success/failure animations

Success: cone fully extended, glow pulses bright and expands at target.
Failure: cone extends partway, hits a dark barrier with red flash,
cone recoils, eye dims and shrinks to nothing.

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

* Add Scout success/failed test buttons to AnimationTestController

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

* Use boo sound for Scout failure instead of generic horn

Scout failure plays "boo" — a disappointed reaction when the scout
can't see through the barrier.

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

* Rework Scout failure animation as dark reversal of attempt

Replace the red-barrier-and-recoil failure animation with a reversal:
glow disk at target turns dark, then cone and glow retract back toward
the source eye, which darkens and shrinks away. Also change failure
sound from boo to generic negative effect horn.

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

* Remove leftover merge conflict marker from TWO_STAGE_ANIMATIONS.md

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:55:08 -07:00
732ffb2aa8 Move AnimationTestController to Right Sidebar for better visibility (#6444)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:50:01 -07:00
79a64e2be7 Add distinct FreezeWater success/failure animations (#6439)
* Add distinct FreezeWater success/failure animations

Success: crystals solidify with bright white flash and intense shimmer.
Failure: crystals form then crack, flicker, and shatter outward as
spinning shards fading from icy blue to dull grey.

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

* Add FreezeWater success/failed test buttons to AnimationTestController

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

* Use splash sound for FreezeWater failure instead of generic horn

Ice shatters and reverts to water — splash conveys the ice losing.

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

* Make freeze success 30% bigger in both spread and crystal scale

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 17:25:21 -07:00
d562266044 Add distinct ExtinguishFire success/failure animations (#6438)
* Add distinct ExtinguishFire success/failure animations

Success: heavy water deluge with thick rising steam cloud.
Failure: droplets evaporate mid-fall, fire flares back with
orange/red burst.

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

* Add ExtinguishFire success/failed test buttons to AnimationTestController

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

* Make ExtinguishFire failure more dramatic with fire flare-up

When extinguish fails, also trigger FireEffectAnimator.AnimateFireSuccess
so the fire blazes back up with tall flickering flames and rising embers
alongside the evaporating water droplets.

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

* Use distinct sounds for ExtinguishFire attempt/success/failure

- Attempt: splash (water thrown at fire)
- Success: fire_extinguish (satisfying sizzle/hiss)
- Failure: raging_fire (fire flares back up)

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 17:11:39 -07:00
11430be186 Filter destroyed battalion IDs from aftermath claimants and update doc (#6437)
Captured units' battalions are destroyed during BattleEnded processing,
but the multi-victor branch was storing those destroyed IDs in
AftermathClaimantUnit. This caused invalid battalion references when
claimants withdrew. Filter them out when building claimants and
notFledDefenders, and exclude them from armySize calculation.

Also update ALLIED_VICTORY_BATTLE_RESOLUTION.md to reflect PR 6429
changes (LastAllianceStanding for attackers, allied co-attacker added
to winners for solo castle holder) and document the dead-ally
GameOverResponsePopulator bug fixed in PR 6435.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:15:35 -07:00
57703ffbda Fix honey badger red stripes: remove shader replacement for 3D models (#6434)
* Remove ClipRectMeshUnlit shader replacement for 3D beast models

The shader replacement multiplied texture color by vertex colors, turning
honey badger white stripes red. RendererViewportClipper already handles
viewport clipping for all 3D models generically, making the shader
approach redundant and harmful.

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

* Set render queue on beast models to render above province texture

The old ClipRectMeshUnlit shader used Queue=Transparent+100 and
ZTest Always to draw above the province colour layer. Now that models
keep their original shaders, set mat.renderQueue = 3100 in
RendererViewportClipper.Awake() to match.

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

* Fix spelling: colour -> color

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:08:31 -07:00
dad8c9ebe7 Remove ally_victory and draw from Shardok victory_condition proto (#6436)
Reserve removed fields (ally_victory=2, draw=3) and delete the
DrawType enum to match the Eagle-side proto cleanup. Update C++
conversion code: ALLY_VICTORY now maps to victory, DRAW now throws.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:07:25 -07:00
13ecf9cd1a Fix dead-ally throw in GameOverResponsePopulator (#6435)
When your alliance wins a battle but your own units were destroyed,
you should still get a victory — not an internal error. Change the
else-if branch from throwing to assigning victory for allied
non-winners.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:24:38 -07:00
beabcede11 Fix Shardok tooltip positioning for Screen Space Camera canvas (#6433)
The buttonTooltip and oddsPanel were setting transform.position/localPosition
using screen-space coordinates directly, which placed them incorrectly when
the canvas uses Screen Space Camera mode. Convert screen positions to canvas
world space via ScreenPointToWorldPointInRectangle.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 12:50:16 -07:00
2b54f4aa7c Add raw game history fallback to admin server for broken games (#6432)
When GetGameHistory returns a non-empty error field (game failed to load),
the admin server now transparently falls back to GetRawGameHistory. The UI
shows a warning banner and uses raw-rewind instead of normal rewind. This
lets admins view and fix broken games without any manual switching.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 12:48:12 -07:00
b292644f76 Fix allied attacker victory conditions (#6429)
* Fix allied attacker victory conditions

Allied attackers couldn't win by elimination because LastAllianceStanding
wasn't in their victory conditions. When one attacker held all castles,
the ally was treated as a loser because they weren't in winning_shardok_ids.

- Add LastAllianceStanding to attacker victory conditions in RequestBattlesAction
- Add mutually-allied co-attackers to winning_shardok_ids when one player holds all castles
- Add tests for both fixes

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

* Extract PopulateGameOverResponse into testable library and add tests

Move PopulateGameOverResponse and its helpers (IncludeUnitProtoInReturn,
FromUnitFb, FromInternalStatus) from EagleInterfaceGrpcServer.cpp into a
separate GameOverResponsePopulator library so they can be unit tested
directly. Add 7 tests covering victory/loss assignment, allied player
validation, unit filtering, and status conversion.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 12:47:35 -07:00
528335c8d9 Remove AllyVictory from Eagle battle resolution (#6431)
Shardok now emits Victory for all winners instead of distinguishing
AllyVictory. Remove the AllyVictory case from EndGameCondition, simplify
all isWinOrAllyVictory checks to isVictory, reserve the ally_victory
proto field, and update the allied victory documentation.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 12:26:26 -07:00
4dbd6279cd Add GetRawGameHistory and RawRewindGame admin RPCs for broken games (#6430)
When a game's action history contains a broken action result, the game
can't load because formAwrs throws during state replay. This means the
admin console can't view history or rewind to fix it.

These two new RPCs operate directly on persisted chunk files, bypassing
the formAwrs state-replay pipeline:
- GetRawGameHistory: reads ActionResult protos from .e0a chunks and
  returns them as JSON without replaying game state
- RawRewindGame: truncates chunk files to a target action count without
  loading the game into memory

Also adds an error field to GetGameHistoryResponse so load failures are
reported instead of returning empty results.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 12:24:34 -07:00
ce18908688 Remove draw handling from EagleInterface game-over conversion (#6427)
PR #6425 removed DrawType from the Eagle common proto but didn't
update the C++ conversion code in EagleInterfaceGrpcServer, causing
a linux-aarch64 cross-compile failure.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 08:36:05 -07:00
fd89f9e059 Validate WIN_AFTER_MAX_ROUNDS at game setup and remove DRAW codepath (#6426)
Every battle must have exactly one player with WIN_AFTER_MAX_ROUNDS.
This adds startup validation in StartGame and replaces the unreachable
DRAW result with a ShardokInternalErrorException.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 08:07:25 -07:00
8602f1cf72 Remove Draw from battle resolution and add WinAfterMaxRounds validation (#6425)
Draws are dead code left over from removed Free For All battles. Shardok
battles always have exactly one defender with WinAfterMaxRounds, so if
max rounds is exceeded the defender wins — a draw is never produced.

- Remove DrawType enum and Draw case from EndGameCondition
- Remove isDraw method and all isDraw checks in unitReturned
- Remove Draw proto conversion code from ShardokBattleConverter
- Remove DrawType from victory_condition.proto (Eagle common only)
- Add internalRequire validation that exactly 1 player has
  WinAfterMaxRounds when building shardokPlayers

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 08:05:20 -07:00
43bb0bcf74 Fix tooltip positioning for Screen Space Camera canvas (#6421)
* Fix tooltip positioning for Screen Space Camera canvas

HoveringTooltip.SetPosition() was mixing world-space and screen-space
coordinates. With Screen Space Camera, GetWorldCorners returns world
coordinates (not screen pixels), and the tooltip was placed at the
wrong position. Now routes all positioning through screen space using
RectTransformUtility, with the correct canvas plane z for projection.

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

* Use world-space positioning instead of screen-space roundtrip

Work directly in world space using lossyScale for offsets and
canvas GetWorldCorners for overflow, avoiding broken coordinate
space conversions.

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

* Convert tooltip providers to pass screen-space Rects

The camera looks downward, so GetWorldCorners returns points where
the visual "height" is along z, not y. A 2D Rect from world coords
loses the z axis entirely (height=0). Fix by converting world corners
to screen space in the providers via WorldToScreenPoint, then
converting back to canvas world space in HoveringTooltip via
ScreenPointToWorldPointInRectangle for final placement.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 08:02:54 -07:00
36e61da168 Remove auto-create first game logic from lobby (#6424)
Players with no active games were being automatically thrown into a
new 7-faction game. Remove this behavior so they always land in the
lobby and can choose what to do.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 07:59:12 -07:00
4bdade4744 Fix allied battle victory: treat AllyVictory as winner in ResolveBattleAction (#6423)
* Fix allied battle victory: treat AllyVictory as winner in ResolveBattleAction

The multi-victor battle aftermath code (added in #6411) was dead code because
the winner/loser partition used `isVictory` which excludes `AllyVictory`.
Allied winners were classified as losers, so only one faction ever "won"
and the province was awarded immediately without a claim decision.

Changes:
- Add `isWinOrAllyVictory` to EndGameCondition
- Use it for the winner partition, backstory, and quest fulfillment
- Fix `unitReturned` so AllyVictory heroes stay at the battle province
  as claimants instead of being sent home

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

* Update tests to expect multi-winner behavior for allied victories

Tests previously expected AllyVictory units to be sent home. With
the partition fix, allied winners now stay as claimants for the
BattleAftermath claim decision.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 07:44:30 -07:00
42dfd143ea Fix bounced armies not processed by WithdrawnArmiesReturnHomeAction (#6422)
The auto-bounce results set hostile armies to Withdrawing status, but
WithdrawnArmiesReturnHomeAction received the original provinces where
those armies were still AwaitingDecision. This caused a validation
error when transitioning to DefenseDecision because non-allied factions
remained in hostileArmies. Apply bounce status changes to the province
list before passing to WithdrawnArmiesReturnHomeAction so it properly
removes the bounced armies.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 19:33:18 -08:00
db77a33003 Fix bounce for non-allied co-attackers when no army advanced (#6420)
When multiple non-allied factions attack the same occupied province and
none makes an AttackDecision, they all remain AwaitingDecision. The
existing auto-bounce logic only triggers when an army has Attacking
status. Add a new branch that detects non-allied undecided factions and
bounces all of them to Withdrawing, preventing the
validateAtMostOneAttackingAlliance runtime error.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 19:07:16 -08:00
468c863a63 Fix aftermath supply caps, auto-keep last claimant, and persist PendingConquestInfo (#6417)
Three correctness fixes for the battle aftermath decision system:
- Cap withdrawal gold/food at what each faction brought into battle (broughtGold/broughtFood)
- Auto-keep last undecided claimant when nobody has kept yet (prevents broken province state)
- Serialize PendingConquestInfo to proto so server restarts during BattleAftermath don't lose state

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:15:24 -08:00
865fe2c58d Fix Royal Lancers HEAVY_CAVALRY size exceeding capacity (800 → 600) (#6419)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:14:57 -08:00
7f01067d4c Fix duplicate province labels on map zoom (#6418)
Destroy previous label container before creating new ones when
OnMapLoaded fires multiple times. Add OnDestroy to unsubscribe
from the event.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:14:41 -08:00
b3d494d820 Fix battle aftermath finalization and hero-battalion pairing bugs (#6416)
Add FinalizeAftermathAction to handle the all-decided case (run
ProvinceConqueredAction for keeper, create withdrawal armies, clear
pendingConquestInfo), fix hero-battalion pairing by replacing separate
heroIds/battalionIds vectors with paired AftermathClaimantUnit, and
remove dead destroyedBattalions field from MultiVictorBattleSetupAction.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 16:41:15 -08:00
73bc28e13a Add BattleAftermathDecisionCommandSelector for Unity client (#6413)
* Add BattleAftermathDecisionCommandSelector for Unity client

Adds the UI component for the battle aftermath decision system. When
allied factions jointly conquer a province, each winning faction chooses
to Keep Province or Withdraw (with destination and supply sliders).

The selector follows the AttackDecisionCommandSelector pattern with
toggle group, province dropdown, and gold/food sliders. It is inert
until the server command infrastructure sends the corresponding
available command.

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

* Add missing EagleGUIUtils using for GUIUtils.DarkColoredProvinceName

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

* Wire up BattleAftermathDecisionCommandSelector in Gameplay scene

Add the command selector as a child of CommandPanelController with
all Inspector fields connected: toggles, toggle group, withdraw
container, destination dropdown, gold/food sliders and labels, info
text, and button image. Unity events wired for ToggleChanged,
GoldSliderChanged, and FoodSliderChanged.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 16:09:30 -08:00
a22041f975 Add battle aftermath decision command infrastructure (#6415)
Wire up the full command pipeline for battle aftermath decisions so
winning allied factions can choose Keep Province or Withdraw after a
multi-victor assault.

- Add proto messages for available/selected command (field 41) and command type
- Add Scala model types: CommandType, AvailableCommand, SelectedCommand cases
- Add proto converters for all new command types
- Add AvailableBattleAftermathDecisionCommandFactory (sequential by army size)
- Add BattleAftermathDecisionCommand (keep triggers conquest, withdraw creates army)
- Add AutoResolveBattleAftermathAction (last claimant auto-keeps)
- Add BattleAftermathDecisionCommandChooser for AI (always keeps when possible)
- Update AvailableCommandsFactory and RoundPhaseAdvancer for aftermath phase
- Update CommandFactory routing and AIClient integration

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 16:02:14 -08:00