diff --git a/EOBot/Program.cs b/EOBot/Program.cs
index 6dc93d8a8..778a0c7cb 100644
--- a/EOBot/Program.cs
+++ b/EOBot/Program.cs
@@ -158,7 +158,7 @@ public void NotifySelfSpellCast(short playerId, short spellId, int spellHp, byte
public void NotifyStartSpellCast(short playerId, short spellId) { }
public void NotifyTargetOtherSpellCast(short sourcePlayerID, short targetPlayerID, short spellId, int recoveredHP, byte targetPercentHealth) { }
- public void StartOtherCharacterAttackAnimation(int characterID) { }
+ public void StartOtherCharacterAttackAnimation(int characterID, int noteIndex) { }
public void StartOtherCharacterWalkAnimation(int characterID, byte destinationX, byte destinationY, EODirection direction) { }
}
diff --git a/EOLib/Domain/Character/CharacterRenderProperties.cs b/EOLib/Domain/Character/CharacterRenderProperties.cs
index 21ec5279b..e78481884 100644
--- a/EOLib/Domain/Character/CharacterRenderProperties.cs
+++ b/EOLib/Domain/Character/CharacterRenderProperties.cs
@@ -174,7 +174,11 @@ public ICharacterRenderProperties WithNextEmoteFrame()
{
var props = MakeCopy(this);
props.EmoteFrame = (props.EmoteFrame + 1) % MAX_NUMBER_OF_EMOTE_FRAMES;
- props.CurrentAction = props.EmoteFrame == 0 ? CharacterActionState.Standing : CharacterActionState.Emote;
+ props.CurrentAction = props.EmoteFrame == 0
+ ? CharacterActionState.Standing
+ : props.CurrentAction == CharacterActionState.Attacking // when using an instrument keep the current state as "Attacking"
+ ? CharacterActionState.Attacking
+ : CharacterActionState.Emote;
return props;
}
diff --git a/EOLib/Domain/Character/Emote.cs b/EOLib/Domain/Character/Emote.cs
index 955c4787e..2dfe99597 100644
--- a/EOLib/Domain/Character/Emote.cs
+++ b/EOLib/Domain/Character/Emote.cs
@@ -21,6 +21,7 @@ public enum Emote
///
/// 0 key
///
- Playful = 14
+ Playful = 14,
+ MusicNotes = 15,
}
}
diff --git a/EOLib/Domain/Jukebox/JukeboxActions.cs b/EOLib/Domain/Jukebox/JukeboxActions.cs
new file mode 100644
index 000000000..04e8ef6af
--- /dev/null
+++ b/EOLib/Domain/Jukebox/JukeboxActions.cs
@@ -0,0 +1,51 @@
+using AutomaticTypeMapper;
+using EOLib.Domain.Character;
+using EOLib.IO;
+using EOLib.IO.Repositories;
+using EOLib.Net;
+using EOLib.Net.Communication;
+using Optional.Collections;
+using System;
+
+namespace EOLib.Domain.Jukebox
+{
+ [AutoMappedType]
+ public class JukeboxActions : IJukeboxActions
+ {
+ private readonly IPacketSendService _packetSendService;
+ private readonly ICharacterProvider _characterProvider;
+ private readonly IEIFFileProvider _eifFileProvider;
+
+ public JukeboxActions(IPacketSendService packetSendService,
+ ICharacterProvider characterProvider,
+ IEIFFileProvider eifFileProvider)
+ {
+ _packetSendService = packetSendService;
+ _characterProvider = characterProvider;
+ _eifFileProvider = eifFileProvider;
+ }
+
+ public void PlayNote(int noteIndex)
+ {
+ if (noteIndex < 0 || noteIndex >= 36)
+ throw new ArgumentOutOfRangeException(nameof(noteIndex));
+
+ var weapon = _characterProvider.MainCharacter.RenderProperties.WeaponGraphic;
+ _eifFileProvider.EIFFile.SingleOrNone(x => x.Type == ItemType.Weapon && x.DollGraphic == weapon)
+ .MatchSome(rec =>
+ {
+ var packet = new PacketBuilder(PacketFamily.JukeBox, PacketAction.Use)
+ .AddChar((byte)rec.DollGraphic) // todo: determine what GameServer expects; eoserv sends DollGraphic as a response in Character::PlayBard
+ .AddChar((byte)(noteIndex + 1))
+ .Build();
+
+ _packetSendService.SendPacket(packet);
+ });
+ }
+ }
+
+ public interface IJukeboxActions
+ {
+ void PlayNote(int noteIndex);
+ }
+}
diff --git a/EOLib/Domain/Notifiers/IOtherCharacterAnimationNotifier.cs b/EOLib/Domain/Notifiers/IOtherCharacterAnimationNotifier.cs
index a096be63f..827343f5e 100644
--- a/EOLib/Domain/Notifiers/IOtherCharacterAnimationNotifier.cs
+++ b/EOLib/Domain/Notifiers/IOtherCharacterAnimationNotifier.cs
@@ -6,7 +6,7 @@ public interface IOtherCharacterAnimationNotifier
{
void StartOtherCharacterWalkAnimation(int characterID, byte destinationX, byte destinationY, EODirection direction);
- void StartOtherCharacterAttackAnimation(int characterID);
+ void StartOtherCharacterAttackAnimation(int characterID, int noteIndex = -1);
void NotifyStartSpellCast(short playerId, short spellId);
@@ -20,7 +20,7 @@ public class NoOpOtherCharacterAnimationNotifier : IOtherCharacterAnimationNotif
{
public void StartOtherCharacterWalkAnimation(int characterID, byte destinationX, byte destinationY, EODirection direction) { }
- public void StartOtherCharacterAttackAnimation(int characterID) { }
+ public void StartOtherCharacterAttackAnimation(int characterID, int noteIndex = -1) { }
public void NotifyStartSpellCast(short playerId, short spellId) { }
diff --git a/EOLib/PacketHandlers/Jukebox/JukeboxMessageHandler.cs b/EOLib/PacketHandlers/Jukebox/JukeboxMessageHandler.cs
new file mode 100644
index 000000000..c12e8d921
--- /dev/null
+++ b/EOLib/PacketHandlers/Jukebox/JukeboxMessageHandler.cs
@@ -0,0 +1,61 @@
+using AutomaticTypeMapper;
+using EOLib.Domain.Character;
+using EOLib.Domain.Login;
+using EOLib.Domain.Map;
+using EOLib.Domain.Notifiers;
+using EOLib.Net;
+using EOLib.Net.Handlers;
+using System.Collections.Generic;
+
+namespace EOLib.PacketHandlers.Jukebox
+{
+ [AutoMappedType]
+ public class JukeboxMessageHandler : InGameOnlyPacketHandler
+ {
+ private readonly ICharacterRepository _characterRepository;
+ private readonly ICurrentMapStateRepository _currentMapStateRepository;
+ private readonly IEnumerable _mainCharacterEventNotifiers;
+ private readonly IEnumerable _otherCharacterAnimationNotifiers;
+
+ public override PacketFamily Family => PacketFamily.JukeBox;
+
+ public override PacketAction Action => PacketAction.Message;
+
+ public JukeboxMessageHandler(IPlayerInfoProvider playerInfoProvider,
+ ICurrentMapStateRepository currentMapStateRepository,
+ IEnumerable otherCharacterAnimationNotifiers)
+ : base(playerInfoProvider)
+ {
+ _currentMapStateRepository = currentMapStateRepository;
+ _otherCharacterAnimationNotifiers = otherCharacterAnimationNotifiers;
+ }
+
+ public override bool HandlePacket(IPacket packet)
+ {
+ var playerId = packet.ReadShort();
+ var direction = (EODirection)packet.ReadChar();
+ var instrument = packet.ReadChar();
+ var note = packet.ReadChar();
+
+ if (_currentMapStateRepository.Characters.ContainsKey(playerId))
+ {
+ var c = _currentMapStateRepository.Characters[playerId];
+
+ if (c.RenderProperties.WeaponGraphic == instrument)
+ {
+ c = c.WithRenderProperties(c.RenderProperties.WithDirection(direction));
+ _currentMapStateRepository.Characters[playerId] = c;
+
+ foreach (var notifier in _otherCharacterAnimationNotifiers)
+ notifier.StartOtherCharacterAttackAnimation(playerId, note - 1);
+ }
+ }
+ else
+ {
+ _currentMapStateRepository.UnknownPlayerIDs.Add(playerId);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/EndlessClient/Controllers/BardController.cs b/EndlessClient/Controllers/BardController.cs
index 8f1b47583..dd84763a9 100644
--- a/EndlessClient/Controllers/BardController.cs
+++ b/EndlessClient/Controllers/BardController.cs
@@ -1,12 +1,38 @@
using AutomaticTypeMapper;
+using EndlessClient.Rendering.Character;
+using EOLib.Domain.Character;
+using EOLib.Domain.Extensions;
+using EOLib.Domain.Jukebox;
namespace EndlessClient.Controllers
{
[AutoMappedType]
public class BardController : IBardController
{
+ private readonly ICharacterAnimationActions _characterAnimationActions;
+ private readonly IJukeboxActions _jukeboxActions;
+ private readonly ICharacterProvider _characterProvider;
+
+ public BardController(ICharacterAnimationActions characterAnimationActions,
+ IJukeboxActions jukeboxActions,
+ ICharacterProvider characterProvider)
+ {
+ _characterAnimationActions = characterAnimationActions;
+ _jukeboxActions = jukeboxActions;
+ _characterProvider = characterProvider;
+ }
+
public void PlayInstrumentNote(int noteIndex)
{
+ _characterAnimationActions.StartAttacking(noteIndex);
+ _jukeboxActions.PlayNote(noteIndex);
+ }
+
+ private bool CanAttackAgain()
+ {
+ var rp = _characterProvider.MainCharacter.RenderProperties;
+ return rp.IsActing(CharacterActionState.Standing) ||
+ rp.RenderAttackFrame == CharacterRenderProperties.MAX_NUMBER_OF_ATTACK_FRAMES;
}
}
diff --git a/EndlessClient/Controllers/NumPadController.cs b/EndlessClient/Controllers/NumPadController.cs
index 467bf5f3d..be501e11a 100644
--- a/EndlessClient/Controllers/NumPadController.cs
+++ b/EndlessClient/Controllers/NumPadController.cs
@@ -1,6 +1,4 @@
using AutomaticTypeMapper;
-using EndlessClient.ControlSets;
-using EndlessClient.HUD.Controls;
using EndlessClient.Rendering.Character;
using EOLib.Domain.Character;
using EOLib.Domain.Extensions;
@@ -11,31 +9,25 @@ namespace EndlessClient.Controllers
public class NumPadController : INumPadController
{
private readonly ICharacterActions _characterActions;
- private readonly IHudControlProvider _hudControlProvider;
- private readonly ICharacterRendererProvider _characterRendererProvider;
+ private readonly ICharacterAnimationActions _characterAnimationActions;
+ private readonly ICharacterProvider _characterProvider;
public NumPadController(ICharacterActions characterActions,
- IHudControlProvider hudControlProvider,
- ICharacterRendererProvider characterRendererProvider)
+ ICharacterAnimationActions characterAnimationActions,
+ ICharacterProvider characterProvider)
{
_characterActions = characterActions;
- _hudControlProvider = hudControlProvider;
- _characterRendererProvider = characterRendererProvider;
+ _characterAnimationActions = characterAnimationActions;
+ _characterProvider = characterProvider;
}
public void Emote(Emote whichEmote)
{
- var mainRenderer = _characterRendererProvider.MainCharacterRenderer;
- mainRenderer.MatchSome(renderer =>
- {
- if (!renderer.Character.RenderProperties.IsActing(CharacterActionState.Standing))
- return;
+ if (!_characterProvider.MainCharacter.RenderProperties.IsActing(CharacterActionState.Standing))
+ return;
- _characterActions.Emote(whichEmote);
-
- var animator = _hudControlProvider.GetComponent(HudControlIdentifier.CharacterAnimator);
- animator.Emote(renderer.Character.ID, whichEmote);
- });
+ _characterActions.Emote(whichEmote);
+ _characterAnimationActions.Emote(whichEmote);
}
}
diff --git a/EndlessClient/Rendering/Character/CharacterAnimationActions.cs b/EndlessClient/Rendering/Character/CharacterAnimationActions.cs
index 38ac910d1..d0fbdd7de 100644
--- a/EndlessClient/Rendering/Character/CharacterAnimationActions.cs
+++ b/EndlessClient/Rendering/Character/CharacterAnimationActions.cs
@@ -1,4 +1,5 @@
using AutomaticTypeMapper;
+using EndlessClient.Audio;
using EndlessClient.ControlSets;
using EndlessClient.HUD;
using EndlessClient.HUD.Controls;
@@ -9,10 +10,13 @@
using EOLib.Domain.Map;
using EOLib.Domain.Notifiers;
using EOLib.Domain.Spells;
+using EOLib.IO;
using EOLib.IO.Map;
using EOLib.IO.Repositories;
using EOLib.Localization;
using Optional;
+using Optional.Collections;
+using System.Linq;
namespace EndlessClient.Rendering.Character
{
@@ -25,8 +29,9 @@ public class CharacterAnimationActions : ICharacterAnimationActions, IOtherChara
private readonly ICharacterRendererProvider _characterRendererProvider;
private readonly ICurrentMapProvider _currentMapProvider;
private readonly ISpikeTrapActions _spikeTrapActions;
- private readonly IESFFileProvider _esfFileProvider;
+ private readonly IPubFileProvider _pubFileProvider;
private readonly IStatusLabelSetter _statusLabelSetter;
+ private readonly ISfxPlayer _sfxPlayer;
public CharacterAnimationActions(IHudControlProvider hudControlProvider,
ICharacterRepository characterRepository,
@@ -34,8 +39,9 @@ public class CharacterAnimationActions : ICharacterAnimationActions, IOtherChara
ICharacterRendererProvider characterRendererProvider,
ICurrentMapProvider currentMapProvider,
ISpikeTrapActions spikeTrapActions,
- IESFFileProvider esfFileProvider,
- IStatusLabelSetter statusLabelSetter)
+ IPubFileProvider pubFileProvider,
+ IStatusLabelSetter statusLabelSetter,
+ ISfxPlayer sfxPlayer)
{
_hudControlProvider = hudControlProvider;
_characterRepository = characterRepository;
@@ -43,8 +49,9 @@ public class CharacterAnimationActions : ICharacterAnimationActions, IOtherChara
_characterRendererProvider = characterRendererProvider;
_currentMapProvider = currentMapProvider;
_spikeTrapActions = spikeTrapActions;
- _esfFileProvider = esfFileProvider;
+ _pubFileProvider = pubFileProvider;
_statusLabelSetter = statusLabelSetter;
+ _sfxPlayer = sfxPlayer;
}
public void Face(EODirection direction)
@@ -66,14 +73,16 @@ public void StartWalking()
ShowWaterSplashiesIfNeeded(CharacterActionState.Walking, _characterRepository.MainCharacter.ID);
}
- public void StartAttacking()
+ public void StartAttacking(int noteIndex = -1)
{
if (!_hudControlProvider.IsInGame)
return;
CancelSpellPrep();
- Animator.StartMainCharacterAttackAnimation();
+ Animator.StartMainCharacterAttackAnimation(isBard: noteIndex >= 0);
ShowWaterSplashiesIfNeeded(CharacterActionState.Attacking, _characterRepository.MainCharacter.ID);
+
+ PlayWeaponSound(_characterRepository.MainCharacter, noteIndex);
}
public bool PrepareMainCharacterSpell(int spellId, ISpellTargetable spellTarget)
@@ -81,11 +90,19 @@ public bool PrepareMainCharacterSpell(int spellId, ISpellTargetable spellTarget)
if (!_hudControlProvider.IsInGame)
return false;
- var spellData = _esfFileProvider.ESFFile[spellId];
+ var spellData = _pubFileProvider.ESFFile[spellId];
_characterRendererProvider.MainCharacterRenderer.MatchSome(r => r.ShoutSpellPrep(spellData.Shout));
return Animator.MainCharacterShoutSpellPrep(spellData, spellTarget);
}
+ public void Emote(Emote whichEmote)
+ {
+ if (!_hudControlProvider.IsInGame)
+ return;
+
+ Animator.Emote(_characterRepository.MainCharacter.ID, whichEmote);
+ }
+
public void StartOtherCharacterWalkAnimation(int characterID, byte destinationX, byte destinationY, EODirection direction)
{
if (!_hudControlProvider.IsInGame)
@@ -98,13 +115,16 @@ public void StartOtherCharacterWalkAnimation(int characterID, byte destinationX,
_spikeTrapActions.ShowSpikeTrap(characterID);
}
- public void StartOtherCharacterAttackAnimation(int characterID)
+ public void StartOtherCharacterAttackAnimation(int characterID, int noteIndex = -1)
{
if (!_hudControlProvider.IsInGame)
return;
- Animator.StartOtherCharacterAttackAnimation(characterID);
+ Animator.StartOtherCharacterAttackAnimation(characterID, noteIndex >= 0);
ShowWaterSplashiesIfNeeded(CharacterActionState.Attacking, characterID);
+
+ if (_currentMapStateProvider.Characters.ContainsKey(characterID))
+ PlayWeaponSound(_currentMapStateProvider.Characters[characterID], noteIndex);
}
public void NotifyWarpLeaveEffect(short characterId, WarpAnimation anim)
@@ -134,13 +154,13 @@ public void NotifyPotionEffect(short playerId, int effectId)
public void NotifyStartSpellCast(short playerId, short spellId)
{
- var shoutName = _esfFileProvider.ESFFile[spellId].Shout;
+ var shoutName = _pubFileProvider.ESFFile[spellId].Shout;
_characterRendererProvider.CharacterRenderers[playerId].ShoutSpellPrep(shoutName.ToLower());
}
public void NotifySelfSpellCast(short playerId, short spellId, int spellHp, byte percentHealth)
{
- var spellGraphic = _esfFileProvider.ESFFile[spellId].Graphic;
+ var spellGraphic = _pubFileProvider.ESFFile[spellId].Graphic;
if (playerId == _characterRepository.MainCharacter.ID)
{
@@ -172,7 +192,7 @@ public void NotifyTargetOtherSpellCast(short sourcePlayerID, short targetPlayerI
_characterRendererProvider.CharacterRenderers[sourcePlayerID].ShoutSpellCast();
}
- var spellData = _esfFileProvider.ESFFile[spellId];
+ var spellData = _pubFileProvider.ESFFile[spellId];
if (targetPlayerID == _characterRepository.MainCharacter.ID)
{
@@ -246,6 +266,25 @@ private void CancelSpellPrep()
_characterRendererProvider.MainCharacterRenderer.MatchSome(r => r.StopShout());
}
+ private void PlayWeaponSound(ICharacter character, int noteIndex = -1)
+ {
+ _pubFileProvider.EIFFile.SingleOrNone(x => x.Type == ItemType.Weapon && x.DollGraphic == character.RenderProperties.WeaponGraphic)
+ .MatchSome(x =>
+ {
+ var ndx = Constants.InstrumentIDs.ToList().FindIndex(y => y == x.ID);
+
+ if (ndx >= 0 && (noteIndex < 0 || noteIndex >= 36))
+ return;
+
+ switch (ndx)
+ {
+ case 0: _sfxPlayer.PlayHarpNote(noteIndex); break;
+ case 1: _sfxPlayer.PlayGuitarNote(noteIndex); break;
+ default: break; // todo: melee/bow/gun sounds
+ }
+ });
+ }
+
private ICharacterAnimator Animator => _hudControlProvider.GetComponent(HudControlIdentifier.CharacterAnimator);
}
@@ -255,8 +294,10 @@ public interface ICharacterAnimationActions
void StartWalking();
- void StartAttacking();
+ void StartAttacking(int noteIndex = -1);
bool PrepareMainCharacterSpell(int spellId, ISpellTargetable spellTarget);
+
+ void Emote(Emote whichEmote);
}
}
diff --git a/EndlessClient/Rendering/Character/CharacterAnimator.cs b/EndlessClient/Rendering/Character/CharacterAnimator.cs
index 09dbc3a48..a0832fabf 100644
--- a/EndlessClient/Rendering/Character/CharacterAnimator.cs
+++ b/EndlessClient/Rendering/Character/CharacterAnimator.cs
@@ -129,7 +129,7 @@ public void StartMainCharacterWalkAnimation(Option targetCoordina
_characterActions.Walk();
}
- public void StartMainCharacterAttackAnimation()
+ public void StartMainCharacterAttackAnimation(bool isBard = false)
{
if (_otherPlayerStartAttackingTimes.ContainsKey(_characterRepository.MainCharacter.ID))
{
@@ -139,6 +139,13 @@ public void StartMainCharacterAttackAnimation()
var startAttackingTime = new RenderFrameActionTime(_characterRepository.MainCharacter.ID);
_otherPlayerStartAttackingTimes.Add(_characterRepository.MainCharacter.ID, startAttackingTime);
+
+ if (isBard)
+ {
+ var rp = _characterRepository.MainCharacter.RenderProperties.WithEmote(EOLib.Domain.Character.Emote.MusicNotes);
+ _characterRepository.MainCharacter = _characterRepository.MainCharacter.WithRenderProperties(rp);
+ _startEmoteTimes[_characterRepository.MainCharacter.ID] = new RenderFrameActionTime(_characterRepository.MainCharacter.ID);
+ }
}
public bool MainCharacterShoutSpellPrep(ESFRecord spellData, ISpellTargetable target)
@@ -176,7 +183,7 @@ public void StartOtherCharacterWalkAnimation(int characterID, byte destinationX,
_otherPlayerStartWalkingTimes.Add(characterID, startWalkingTimeAndID);
}
- public void StartOtherCharacterAttackAnimation(int characterID)
+ public void StartOtherCharacterAttackAnimation(int characterID, bool isBard = false)
{
if (_otherPlayerStartAttackingTimes.TryGetValue(characterID, out var _))
{
@@ -184,8 +191,15 @@ public void StartOtherCharacterAttackAnimation(int characterID)
return;
}
- var startAttackingTimeAndID = new RenderFrameActionTime(characterID);
- _otherPlayerStartAttackingTimes.Add(characterID, startAttackingTimeAndID);
+ var startAttackingTime = new RenderFrameActionTime(characterID);
+ _otherPlayerStartAttackingTimes.Add(characterID, startAttackingTime);
+
+ if (isBard && _currentMapStateRepository.Characters.TryGetValue(characterID, out var otherCharacter))
+ {
+ var rp = otherCharacter.RenderProperties.WithEmote(EOLib.Domain.Character.Emote.MusicNotes);
+ _currentMapStateRepository.Characters[characterID] = otherCharacter.WithRenderProperties(rp);
+ _startEmoteTimes[characterID] = new RenderFrameActionTime(characterID);
+ }
}
public void StartOtherCharacterSpellCast(int characterID)
@@ -547,7 +561,7 @@ public interface ICharacterAnimator : IGameComponent
void StartMainCharacterWalkAnimation(Option targetCoordinate);
- void StartMainCharacterAttackAnimation();
+ void StartMainCharacterAttackAnimation(bool isBard = false);
bool MainCharacterShoutSpellPrep(ESFRecord spellData, ISpellTargetable spellTarget);
@@ -555,7 +569,7 @@ public interface ICharacterAnimator : IGameComponent
void StartOtherCharacterWalkAnimation(int characterID, byte targetX, byte targetY, EODirection direction);
- void StartOtherCharacterAttackAnimation(int characterID);
+ void StartOtherCharacterAttackAnimation(int characterID, bool isBard = false);
void StartOtherCharacterSpellCast(int characterID);
diff --git a/EndlessClient/Rendering/CharacterProperties/EmoteRenderer.cs b/EndlessClient/Rendering/CharacterProperties/EmoteRenderer.cs
index ec2309d43..a1cf5a5d6 100644
--- a/EndlessClient/Rendering/CharacterProperties/EmoteRenderer.cs
+++ b/EndlessClient/Rendering/CharacterProperties/EmoteRenderer.cs
@@ -12,8 +12,7 @@ public class EmoteRenderer : BaseCharacterPropertyRenderer
private readonly ISpriteSheet _skinSheet;
private readonly SkinRenderLocationCalculator _skinRenderLocationCalculator;
- public override bool CanRender => _renderProperties.IsActing(CharacterActionState.Emote) &&
- _renderProperties.EmoteFrame > 0;
+ public override bool CanRender => _renderProperties.EmoteFrame > 0;
protected override bool ShouldFlip => false;
diff --git a/EndlessClient/Rendering/CharacterProperties/FaceRenderer.cs b/EndlessClient/Rendering/CharacterProperties/FaceRenderer.cs
index e34a708f4..97ef88c5d 100644
--- a/EndlessClient/Rendering/CharacterProperties/FaceRenderer.cs
+++ b/EndlessClient/Rendering/CharacterProperties/FaceRenderer.cs
@@ -16,7 +16,8 @@ public class FaceRenderer : BaseCharacterPropertyRenderer
public override bool CanRender => _renderProperties.IsActing(CharacterActionState.Emote) &&
_renderProperties.EmoteFrame > 0 &&
_renderProperties.Emote != Emote.Trade &&
- _renderProperties.Emote != Emote.LevelUp;
+ _renderProperties.Emote != Emote.LevelUp &&
+ _renderProperties.Emote != Emote.MusicNotes;
public FaceRenderer(ICharacterRenderProperties renderProperties,
ISpriteSheet faceSheet,
diff --git a/EndlessClient/Rendering/Sprites/EmoteSpriteType.cs b/EndlessClient/Rendering/Sprites/EmoteSpriteType.cs
index 94043173c..aad9b755c 100644
--- a/EndlessClient/Rendering/Sprites/EmoteSpriteType.cs
+++ b/EndlessClient/Rendering/Sprites/EmoteSpriteType.cs
@@ -15,6 +15,7 @@ public enum EmoteSpriteType
Drunk = 10,
Trade = 11,
LevelUp = 12,
- Playful = 13
+ Playful = 13,
+ MusicNotes = 14,
}
}