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