server-master/srcs/_plugins/WingsEmu.Plugins.BasicImplementation/BCards/Handlers/BCardSpecialActionsHandler.cs
2026-02-10 18:21:30 +01:00

381 lines
No EOL
12 KiB
C#

// 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;
}
}
}