// WingsEmu // // Developed by NosWings Team using System; using WingsEmu.Game; using WingsEmu.Game._enum; using WingsEmu.Game.Battle; using WingsEmu.Game.Buffs; using WingsEmu.Game.Characters; using WingsEmu.Game.Characters.Events; using WingsEmu.Game.Entities; using WingsEmu.Game.Entities.Extensions; using WingsEmu.Game.Extensions; using WingsEmu.Game.Helpers.Damages; using WingsEmu.Game.Maps; using WingsEmu.Game.Mates; using WingsEmu.Game.Mates.Events; using WingsEmu.Packets.Enums; using WingsEmu.Packets.Enums.Chat; namespace WingsEmu.Plugins.BasicImplementations.BCards.Handlers; public class BCardSpecialActionsHandler : IBCardEffectAsyncHandler { private readonly IRandomGenerator _randomGenerator; public BCardSpecialActionsHandler(IRandomGenerator randomGenerator) => _randomGenerator = randomGenerator; public BCardType HandledType => BCardType.SpecialActions; public void Execute(IBCardEffectContext ctx) { IBattleEntity target = ctx.Target; IBattleEntity sender = ctx.Sender; byte subType = ctx.BCard.SubType; int firstData = ctx.BCard.FirstDataValue(sender.Level); int secondData = ctx.BCard.SecondDataValue(sender.Level); switch (subType) { case (byte)AdditionalTypes.SpecialActions.RunAway: if (target is IMonsterEntity monsterEntity) { if (sender.Level < monsterEntity.Level) { return; } monsterEntity.BroadcastChatBubble("!!!", ChatMessageColorType.PlayerSay); monsterEntity.IsRunningAway = true; } if (target is not INpcEntity npcEntity) { return; } if (sender.Level < npcEntity.Level) { return; } npcEntity.BroadcastChatBubble("!!!", ChatMessageColorType.PlayerSay); npcEntity.IsRunningAway = true; break; case (byte)AdditionalTypes.SpecialActions.Hide: if (target is not IPlayerEntity character) { return; } character.CharacterInvisible(); break; case (byte)AdditionalTypes.SpecialActions.FocusEnemies: if (!target.IsAlive()) { return; } switch (target) { case INpcEntity { CanAttack: false }: return; case IMonsterEntity { CanBePushed: false }: return; } (int resistance, _) = target.BCardComponent.GetAllBCardsInformation(BCardType.AbsorbedSpirit, (byte)AdditionalTypes.AbsorbedSpirit.ResistForcedMovement, target.Level); if (resistance != 0 && _randomGenerator.RandomNumber() <= resistance) { return; } short sX = sender.PositionX; short sY = sender.PositionY; short tX = target.PositionX; short tY = target.PositionY; int distance = sender.GetDistance(target); if (distance <= 0) { distance = 1; } int d = firstData; short maxX = (short)(sX - d * (sX - tX) / distance); short maxY = (short)(sY - d * (sY - tY) / distance); int dx = Math.Abs(maxX - tX); int sx = tX < maxX ? 1 : -1; int dy = -Math.Abs(maxY - tY); int sy = tY < maxY ? 1 : -1; int err = dx + dy; bool isLineOfSight = true; while (true) { if (sender.MapInstance.IsBlockedZone(tX, tY)) { isLineOfSight = false; break; } if (tX == maxX && tY == maxY) { break; } int e2 = 2 * err; if (e2 >= dy) { err += dy; tX += (short)sx; } if (e2 > dx) { continue; } err += dx; tY += (short)sy; } if (!isLineOfSight) { return; } var newPosition = new Position(tX, tY); switch (target) { case IPlayerEntity playerEntity: if (playerEntity.IsSitting) { playerEntity.Session.RestAsync(force: true); } break; case IMateEntity mate: if (mate.IsSitting) { mate.Owner.Session.EmitEvent(new MateRestEvent { MateEntity = mate, Rest = false, Force = true }); } break; } sender.MapInstance?.Broadcast(target.GeneratePushPacket(maxX, maxY, secondData)); target.BroadcastEffectInRange(EffectType.PushSmoke); target.ChangePosition(newPosition); break; case (byte)AdditionalTypes.SpecialActions.PushBack: { if (!target.IsAlive()) { return; } switch (target) { case INpcEntity { CanAttack: false }: return; case IMonsterEntity { CanBePushed: false }: return; } (int pushResistance, _) = target.BCardComponent.GetAllBCardsInformation(BCardType.AbsorbedSpirit, (byte)AdditionalTypes.AbsorbedSpirit.ResistForcedMovement, target.Level); if (pushResistance != 0 && _randomGenerator.RandomNumber() <= pushResistance) { return; } sX = sender.PositionX; sY = sender.PositionY; tX = target.PositionX; tY = target.PositionY; distance = sender.GetDistance(target); if (distance <= 0) { distance = 1; } d = distance + firstData; maxX = (short)(sX - d * (sX - tX) / distance); maxY = (short)(sY - d * (sY - tY) / distance); dx = Math.Abs(maxX - tX); sx = tX < maxX ? 1 : -1; dy = -Math.Abs(maxY - tY); sy = tY < maxY ? 1 : -1; err = dx + dy; short lastX = tX; short lastY = tY; while (true) { if (sender.MapInstance.IsBlockedZone(tX, tY)) { tX = lastX; tY = lastY; break; } lastX = tX; lastY = tY; if (tX == maxX && tY == maxY) { break; } int e2 = 2 * err; if (e2 >= dy) { err += dy; tX += (short)sx; } if (e2 > dx) { continue; } err += dx; tY += (short)sy; } newPosition = new Position(tX, tY); switch (target) { case IPlayerEntity playerEntity: if (playerEntity.IsSitting) { playerEntity.Session.RestAsync(force: true); } break; case IMateEntity mate: if (mate.IsSitting) { mate.Owner.Session.EmitEvent(new MateRestEvent { MateEntity = mate, Rest = false, Force = true }); } break; } sender.MapInstance?.Broadcast(target.GeneratePushPacket(tX, tY, secondData)); target.ChangePosition(newPosition); target.BroadcastEffectInRange(EffectType.PushSmoke); break; } case (byte)AdditionalTypes.SpecialActions.Charge: if (target == null) { return; } if (!target.IsAlive()) { return; } newPosition = target.Position; sender.MapInstance?.Broadcast(sender.GenerateDashGuriPacket(newPosition.X, newPosition.Y, secondData)); sender.ChangePosition(newPosition); break; case (byte)AdditionalTypes.SpecialActions.ChargeNegated: if (!target.IsAlive()) { return; } sX = target.PositionX; sY = target.PositionY; tX = sender.PositionX; tY = sender.PositionY; distance = sender.GetDistance(target); if (distance <= 0) { distance = 1; } d = firstData; maxX = (short)(sX - d * (sX - tX) / distance); maxY = (short)(sY - d * (sY - tY) / distance); dx = Math.Abs(maxX - tX); sx = tX < maxX ? 1 : -1; dy = -Math.Abs(maxY - tY); sy = tY < maxY ? 1 : -1; err = dx + dy; isLineOfSight = true; while (true) { if (sender.MapInstance.IsBlockedZone(tX, tY)) { isLineOfSight = false; break; } if (tX == maxX && tY == maxY) { break; } int e2 = 2 * err; if (e2 >= dy) { err += dy; tX += (short)sx; } if (e2 > dx) { continue; } err += dx; tY += (short)sy; } if (!isLineOfSight) { return; } newPosition = new Position(tX, tY); sender.MapInstance?.Broadcast(sender.GenerateDashGuriPacket(tX, tY, secondData)); sender.ChangePosition(newPosition); break; } } }