diff --git a/EOLib/Domain/Notifiers/IOtherCharacterAnimationNotifier.cs b/EOLib/Domain/Notifiers/IOtherCharacterAnimationNotifier.cs index 73f8b0493..9b90ca43f 100644 --- a/EOLib/Domain/Notifiers/IOtherCharacterAnimationNotifier.cs +++ b/EOLib/Domain/Notifiers/IOtherCharacterAnimationNotifier.cs @@ -12,6 +12,8 @@ public interface IOtherCharacterAnimationNotifier void NotifyStartSpellCast(short playerId, short spellId); + void NotifyTargetNpcSpellCast(short playerId); + void NotifySelfSpellCast(short playerId, short spellId, int spellHp, byte percentHealth); void NotifyTargetOtherSpellCast(short sourcePlayerID, short targetPlayerID, short spellId, int recoveredHP, byte targetPercentHealth); @@ -28,6 +30,8 @@ public class NoOpOtherCharacterAnimationNotifier : IOtherCharacterAnimationNotif public void NotifyStartSpellCast(short playerId, short spellId) { } + public void NotifyTargetNpcSpellCast(short playerId) { } + public void NotifySelfSpellCast(short playerId, short spellId, int spellHp, byte percentHealth) { } public void NotifyTargetOtherSpellCast(short sourcePlayerID, short targetPlayerID, short spellId, int recoveredHP, byte targetPercentHealth) { } diff --git a/EOLib/PacketHandlers/NPCLeaveMapHandler.cs b/EOLib/PacketHandlers/NPCLeaveMapHandler.cs index 569ed3759..5aa614bd5 100644 --- a/EOLib/PacketHandlers/NPCLeaveMapHandler.cs +++ b/EOLib/PacketHandlers/NPCLeaveMapHandler.cs @@ -19,6 +19,7 @@ public class NPCLeaveMapHandler : InGameOnlyPacketHandler private readonly ICharacterSessionRepository _characterSessionRepository; private readonly IEnumerable _npcActionNotifiers; private readonly IEnumerable _mainCharacterEventNotifiers; + private readonly IEnumerable _otherCharacterAnimationNotifiers; public override PacketFamily Family => PacketFamily.NPC; @@ -29,7 +30,8 @@ public class NPCLeaveMapHandler : InGameOnlyPacketHandler ICharacterRepository characterRepository, ICharacterSessionRepository characterSessionRepository, IEnumerable npcActionNotifiers, - IEnumerable mainCharacterEventNotifiers) + IEnumerable mainCharacterEventNotifiers, + IEnumerable otherCharacterAnimationNotifiers) : base(playerInfoProvider) { _currentMapStateRepository = currentMapStateRepository; @@ -37,25 +39,26 @@ public class NPCLeaveMapHandler : InGameOnlyPacketHandler _characterSessionRepository = characterSessionRepository; _npcActionNotifiers = npcActionNotifiers; _mainCharacterEventNotifiers = mainCharacterEventNotifiers; + _otherCharacterAnimationNotifiers = otherCharacterAnimationNotifiers; } public override bool HandlePacket(IPacket packet) { - var spellID = packet.Family + var spellId = packet.Family .SomeWhen(f => f == PacketFamily.Cast) .Map(f => packet.ReadShort()); - var playerID = packet.ReadShort(); //player that is protecting the item + var fromPlayerId = packet.ReadShort(); //player that is protecting the item var playerDirection = (EODirection)packet.ReadChar(); - if (playerID > 0) - UpdatePlayerDirection(playerID, playerDirection); + if (fromPlayerId > 0) + UpdatePlayerDirection(fromPlayerId, playerDirection); var deadNPCIndex = packet.ReadShort(); //packet is removing the NPC from view due to out of range of character if (packet.ReadPosition == packet.Length) { - RemoveNPCFromView(deadNPCIndex, playerID, spellID, damage: Option.None(), showDeathAnimation: false); + RemoveNPCFromView(deadNPCIndex, fromPlayerId, spellId, damage: Option.None(), showDeathAnimation: false); return true; } @@ -66,7 +69,7 @@ public override bool HandlePacket(IPacket packet) var droppedAmount = packet.ReadInt(); var damageDoneToNPC = packet.ReadThree(); - RemoveNPCFromView(deadNPCIndex, playerID, spellID, Option.Some(damageDoneToNPC), showDeathAnimation: true); + RemoveNPCFromView(deadNPCIndex, fromPlayerId, spellId, Option.Some(damageDoneToNPC), showDeathAnimation: true); if (packet.Family == PacketFamily.Cast) { @@ -90,7 +93,13 @@ public override bool HandlePacket(IPacket packet) } if (droppedItemID > 0) - ShowDroppedItem(playerID, droppedItemUID, droppedItemID, x, y, droppedAmount); + ShowDroppedItem(fromPlayerId, droppedItemUID, droppedItemID, x, y, droppedAmount); + + spellId.MatchSome(_ => + { + foreach (var notifier in _otherCharacterAnimationNotifiers) + notifier.NotifyTargetNpcSpellCast(fromPlayerId); + }); return true; } @@ -164,8 +173,9 @@ public class NPCDieFromSpellCastHandler : NPCLeaveMapHandler ICharacterRepository characterRepository, ICharacterSessionRepository characterSessionRepository, IEnumerable npcAnimationNotifiers, - IEnumerable mainCharacterEventNotifiers) + IEnumerable mainCharacterEventNotifiers, + IEnumerable otherCharacterAnimationNotifiers) : base(playerInfoProvider, currentMapStateRepository, characterRepository, characterSessionRepository, - npcAnimationNotifiers, mainCharacterEventNotifiers) { } + npcAnimationNotifiers, mainCharacterEventNotifiers, otherCharacterAnimationNotifiers) { } } } diff --git a/EOLib/PacketHandlers/NPCTakeDamageHandler.cs b/EOLib/PacketHandlers/NPCTakeDamageHandler.cs index 11b72a6df..fcc9ddf5b 100644 --- a/EOLib/PacketHandlers/NPCTakeDamageHandler.cs +++ b/EOLib/PacketHandlers/NPCTakeDamageHandler.cs @@ -17,18 +17,21 @@ public abstract class NPCTakeDamageHandler : InGameOnlyPacketHandler private readonly ICharacterRepository _characterRepository; private readonly ICurrentMapStateRepository _currentMapStateRepository; private readonly IEnumerable _npcNotifiers; + private readonly IEnumerable _otherCharacterAnimationNotifiers; public override PacketAction Action => PacketAction.Reply; public NPCTakeDamageHandler(IPlayerInfoProvider playerInfoProvider, ICharacterRepository characterRepository, ICurrentMapStateRepository currentMapStateRepository, - IEnumerable npcNotifiers) + IEnumerable npcNotifiers, + IEnumerable otherCharacterAnimationNotifiers) : base(playerInfoProvider) { _characterRepository = characterRepository; _currentMapStateRepository = currentMapStateRepository; _npcNotifiers = npcNotifiers; + _otherCharacterAnimationNotifiers = otherCharacterAnimationNotifiers; } public override bool HandlePacket(IPacket packet) @@ -89,6 +92,12 @@ public override bool HandlePacket(IPacket packet) return true; } + spellId.MatchSome(_ => + { + foreach (var notifier in _otherCharacterAnimationNotifiers) + notifier.NotifyTargetNpcSpellCast(fromPlayerId); + }); + return true; } } @@ -101,8 +110,9 @@ public class NPCTakeWeaponDamageHandler : NPCTakeDamageHandler public NPCTakeWeaponDamageHandler(IPlayerInfoProvider playerInfoProvider, ICharacterRepository characterRepository, ICurrentMapStateRepository currentMapStateRepository, - IEnumerable npcNotifiers) - : base(playerInfoProvider, characterRepository, currentMapStateRepository, npcNotifiers) { } + IEnumerable npcNotifiers, + IEnumerable otherCharacterAnimationNotifiers) + : base(playerInfoProvider, characterRepository, currentMapStateRepository, npcNotifiers, otherCharacterAnimationNotifiers) { } } [AutoMappedType] @@ -113,7 +123,8 @@ public class NPCTakeSpellDamageHandler : NPCTakeDamageHandler public NPCTakeSpellDamageHandler(IPlayerInfoProvider playerInfoProvider, ICharacterRepository characterRepository, ICurrentMapStateRepository currentMapStateRepository, - IEnumerable npcNotifiers) - : base(playerInfoProvider, characterRepository, currentMapStateRepository, npcNotifiers) { } + IEnumerable npcNotifiers, + IEnumerable otherCharacterAnimationNotifiers) + : base(playerInfoProvider, characterRepository, currentMapStateRepository, npcNotifiers, otherCharacterAnimationNotifiers) { } } } diff --git a/EOLib/PacketHandlers/PlayerLevelUpHandler.cs b/EOLib/PacketHandlers/PlayerLevelUpHandler.cs index 3a55a335a..a22972ab5 100644 --- a/EOLib/PacketHandlers/PlayerLevelUpHandler.cs +++ b/EOLib/PacketHandlers/PlayerLevelUpHandler.cs @@ -23,9 +23,10 @@ public class PlayerLevelUpHandler : NPCLeaveMapHandler ICharacterSessionRepository characterSessionRepository, IEnumerable npcAnimationNotifiers, IEnumerable mainCharacterEventNotifiers, + IEnumerable otherCharacterAnimationNotifiers, IEnumerable emoteNotifiers) : base(playerInfoProvider, currentMapStateRepository, characterRepository, characterSessionRepository, - npcAnimationNotifiers, mainCharacterEventNotifiers) + npcAnimationNotifiers, mainCharacterEventNotifiers, otherCharacterAnimationNotifiers) { _emoteNotifiers = emoteNotifiers; } @@ -71,8 +72,9 @@ public class PlayerLevelUpFromSpellCastHandler : PlayerLevelUpHandler ICharacterSessionRepository characterSessionRepository, IEnumerable npcAnimationNotifiers, IEnumerable mainCharacterEventNotifiers, + IEnumerable otherCharacterAnimationNotifiers, IEnumerable emoteNotifiers) : base(playerInfoProvider, currentMapStateRepository, characterRepository, characterSessionRepository, - npcAnimationNotifiers, mainCharacterEventNotifiers, emoteNotifiers) { } + npcAnimationNotifiers, mainCharacterEventNotifiers, otherCharacterAnimationNotifiers, emoteNotifiers) { } } } diff --git a/EndlessClient/Rendering/Character/CharacterAnimationActions.cs b/EndlessClient/Rendering/Character/CharacterAnimationActions.cs index f719e1cd7..1608a32a8 100644 --- a/EndlessClient/Rendering/Character/CharacterAnimationActions.cs +++ b/EndlessClient/Rendering/Character/CharacterAnimationActions.cs @@ -178,6 +178,17 @@ public void NotifyStartSpellCast(short playerId, short spellId) _characterRendererProvider.CharacterRenderers[playerId].ShoutSpellPrep(shoutName.ToLower()); } + public void NotifyTargetNpcSpellCast(short playerId) + { + // Main player starts its spell cast animation immediately when targeting NPC + // Other players need to wait for packet to be received and do it here + // this is some spaghetti... + if (_characterRendererProvider.CharacterRenderers.ContainsKey(playerId)) + { + Animator.StartOtherCharacterSpellCast(playerId); + } + } + public void NotifySelfSpellCast(short playerId, short spellId, int spellHp, byte percentHealth) { var spellGraphic = _pubFileProvider.ESFFile[spellId].Graphic; diff --git a/EndlessClient/Rendering/NPC/NPCActions.cs b/EndlessClient/Rendering/NPC/NPCActions.cs index 3749964d5..449ec09be 100644 --- a/EndlessClient/Rendering/NPC/NPCActions.cs +++ b/EndlessClient/Rendering/NPC/NPCActions.cs @@ -134,6 +134,8 @@ private void ShoutSpellCast(int playerId) { if (r.Character.ID == playerId) r.ShoutSpellCast(); + else if (_characterRendererRepository.CharacterRenderers.ContainsKey(playerId)) + _characterRendererRepository.CharacterRenderers[playerId].ShoutSpellCast(); }, none: () => {