mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 20:55:44 +00:00
Animate enemy flee results (#8748)
This commit is contained in:
+6
-1
@@ -194,7 +194,10 @@ namespace eagle0.Tests {
|
||||
new Dictionary<int, string>(),
|
||||
new Dictionary<int, string>());
|
||||
shardokModel.UnitsById[fleeingUnitId] = new UnitViewWithName(
|
||||
new Net.Eagle0.Shardok.Api.UnitView { UnitId = fleeingUnitId },
|
||||
new Net.Eagle0.Shardok.Api.UnitView {
|
||||
UnitId = fleeingUnitId,
|
||||
Location = new Net.Eagle0.Shardok.Common.Coords { Row = 3, Column = 4 }
|
||||
},
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
@@ -238,6 +241,8 @@ namespace eagle0.Tests {
|
||||
|
||||
Assert.AreEqual(1, shardokModel.History.Count);
|
||||
Assert.IsFalse(shardokModel.UnitsById.ContainsKey(fleeingUnitId));
|
||||
Assert.AreEqual(3, shardokModel.ActionSourceCoords[0].Row);
|
||||
Assert.AreEqual(4, shardokModel.ActionSourceCoords[0].Column);
|
||||
}
|
||||
|
||||
private static AvailableCommands
|
||||
|
||||
+65
-12
@@ -895,16 +895,15 @@ namespace Shardok {
|
||||
} else if (
|
||||
isOtherPlayer && animationType != AnimationType.None &&
|
||||
historyEntry.Actor != null) {
|
||||
// Other player's action with animation
|
||||
if (Model.UnitsById.TryGetValue(
|
||||
historyEntry.Actor.Value,
|
||||
out var actorUnit)) {
|
||||
// For Move actions, use stored source coords (unit's pre-move location)
|
||||
// For other actions, use current unit location
|
||||
int sourceIndex =
|
||||
Model.MoveSourceCoords.TryGetValue(i, out var sourceCoords)
|
||||
? MapCoordsToGridIndex(sourceCoords)
|
||||
: MapCoordsToGridIndex(actorUnit.Location);
|
||||
// Other player's action with animation. Prefer the actor's position from
|
||||
// before the result diff, since the result may move or remove the unit.
|
||||
Model.UnitsById.TryGetValue(historyEntry.Actor.Value, out var actorUnit);
|
||||
bool hasSourceCoords =
|
||||
Model.ActionSourceCoords.TryGetValue(i, out var sourceCoords);
|
||||
if (hasSourceCoords || actorUnit != null) {
|
||||
int sourceIndex = hasSourceCoords
|
||||
? MapCoordsToGridIndex(sourceCoords)
|
||||
: MapCoordsToGridIndex(actorUnit.Location);
|
||||
|
||||
// Get target - either coords (archery) or unit location (melee)
|
||||
int targetIndex = -1;
|
||||
@@ -920,6 +919,14 @@ namespace Shardok {
|
||||
targetIndex = MapCoordsToGridIndex(targetUnit.Location);
|
||||
}
|
||||
|
||||
if (targetIndex < 0 && (animationType == AnimationType.FleeSuccess ||
|
||||
animationType == AnimationType.FleeCaptured)) {
|
||||
targetIndex = FindNearestOpponentGridIndex(
|
||||
hasSourceCoords ? sourceCoords : actorUnit.Location,
|
||||
historyEntry.Player.Value);
|
||||
if (targetIndex < 0) { targetIndex = sourceIndex; }
|
||||
}
|
||||
|
||||
if (targetIndex >= 0) {
|
||||
if (UsesTwoStageSounds(animationType)) {
|
||||
StartCoroutine(PlayTwoStageSequence(
|
||||
@@ -939,8 +946,8 @@ namespace Shardok {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unit removed from model (e.g. fled off map) — play sound
|
||||
// without animation since we can't determine coordinates
|
||||
// The actor was already unavailable before this result, so its
|
||||
// animation position cannot be reconstructed.
|
||||
if (soundManager.IsTwoStageActionType(type)) {
|
||||
StartCoroutine(PlayTwoStageSoundSequence(animationType, type));
|
||||
} else {
|
||||
@@ -2280,6 +2287,52 @@ namespace Shardok {
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the nearest visible unit opposed to the acting player. Hostility values are
|
||||
/// relative to the local player, so self and allied players oppose enemies and vice
|
||||
/// versa. Used to give untargeted flee results a believable direction.
|
||||
/// </summary>
|
||||
int FindNearestOpponentGridIndex(Coords sourceCoords, PlayerId actorPlayerId) {
|
||||
var actorPlayer = Model.players.Find(player => player.playerId == actorPlayerId);
|
||||
bool actorPlayerKnown =
|
||||
Model.players.Exists(player => player.playerId == actorPlayerId);
|
||||
int bestIndex = -1;
|
||||
int bestDistance = int.MaxValue;
|
||||
|
||||
foreach (var unit in Model.UnitsById.Values) {
|
||||
if (unit.PlayerId == actorPlayerId) continue;
|
||||
|
||||
bool isOpponent;
|
||||
if (actorPlayerKnown) {
|
||||
isOpponent = AreOpposingHostilities(
|
||||
actorPlayer.hostility,
|
||||
(Net.Eagle0.Common.Hostility)unit.HostilityStatus);
|
||||
} else {
|
||||
isOpponent = true;
|
||||
}
|
||||
|
||||
if (!isOpponent) continue;
|
||||
int dist = HexDistance(sourceCoords, unit.Location);
|
||||
if (dist < bestDistance) {
|
||||
bestDistance = dist;
|
||||
bestIndex = MapCoordsToGridIndex(unit.Location);
|
||||
}
|
||||
}
|
||||
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
static bool AreOpposingHostilities(
|
||||
Net.Eagle0.Common.Hostility first,
|
||||
Net.Eagle0.Common.Hostility second) {
|
||||
bool firstIsFriendly = first == Net.Eagle0.Common.Hostility.SelfHostility ||
|
||||
first == Net.Eagle0.Common.Hostility.AlliedHostility;
|
||||
bool secondIsFriendly = second == Net.Eagle0.Common.Hostility.SelfHostility ||
|
||||
second == Net.Eagle0.Common.Hostility.AlliedHostility;
|
||||
return (firstIsFriendly && second == Net.Eagle0.Common.Hostility.EnemyHostility) ||
|
||||
(secondIsFriendly && first == Net.Eagle0.Common.Hostility.EnemyHostility);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public entry point for testing animations with their correct sound flow.
|
||||
/// Calls PlayAnimationAndSound with no unit info, so animations that need
|
||||
|
||||
@@ -106,10 +106,11 @@ public class ShardokGameModel : eagle.IShardokGameStateProvider {
|
||||
public List<ActionResultView> History { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the source coordinates for Move actions, keyed by history index.
|
||||
/// Used for animations since the model is fully updated before animations play.
|
||||
/// Tracks each action actor's coordinates before its diff is applied, keyed by history index.
|
||||
/// Used for animations since the model is fully updated before animations play and an action
|
||||
/// may remove its actor (for example, when a unit flees or is captured).
|
||||
/// </summary>
|
||||
public Dictionary<int, Coords> MoveSourceCoords { get; } = new();
|
||||
public Dictionary<int, Coords> ActionSourceCoords { get; } = new();
|
||||
|
||||
public PlayerId PlayerId { get; private set; }
|
||||
public GameStatus GameStatus { get; private set; }
|
||||
@@ -175,7 +176,7 @@ public class ShardokGameModel : eagle.IShardokGameStateProvider {
|
||||
UnitsById.Clear();
|
||||
ReserveUnitsById.Clear();
|
||||
UnitPlayers.Clear();
|
||||
MoveSourceCoords.Clear();
|
||||
ActionSourceCoords.Clear();
|
||||
AvailableCommands = new List<CommandDescriptor>();
|
||||
_recommendedStartingPositionsApplied = false;
|
||||
GameStatus = new GameStatus { State = GameStatus.Types.State.SetUp };
|
||||
@@ -489,11 +490,14 @@ public class ShardokGameModel : eagle.IShardokGameStateProvider {
|
||||
}
|
||||
|
||||
private void HandleNewHistoryEntry(ActionResultView entry) {
|
||||
// For Move actions, record the source coordinates BEFORE applying the diff
|
||||
// This is needed for animations since all diffs are applied before animations play
|
||||
if (entry.Type == ActionType.Move && entry.Actor != null) {
|
||||
// Record the actor's coordinates BEFORE applying the diff. All diffs are applied before
|
||||
// animations play, and some results remove the actor from UnitsById entirely.
|
||||
if (entry.Actor != null) {
|
||||
if (UnitsById.TryGetValue(entry.Actor.Value, out var actorUnit)) {
|
||||
MoveSourceCoords[History.Count] = actorUnit.Location;
|
||||
ActionSourceCoords[History.Count] = new Coords {
|
||||
Row = actorUnit.Location.Row,
|
||||
Column = actorUnit.Location.Column
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user