server-master/srcs/_plugins/WingsEmu.Plugins.PacketHandling/Game/Basic/WalkPacketHandler.cs
2026-02-10 18:21:30 +01:00

189 lines
No EOL
7.4 KiB
C#

using System;
using System.Threading.Tasks;
using WingsAPI.Game.Extensions.CharacterExtensions;
using WingsEmu.DTOs.Maps;
using WingsEmu.Game._enum;
using WingsEmu.Game._i18n;
using WingsEmu.Game.Battle;
using WingsEmu.Game.Characters;
using WingsEmu.Game.Entities;
using WingsEmu.Game.Extensions;
using WingsEmu.Game.Helpers.Damages;
using WingsEmu.Game.Maps;
using WingsEmu.Game.Networking;
using WingsEmu.Game.Networking.Broadcasting;
using WingsEmu.Packets.ClientPackets;
using WingsEmu.Packets.Enums.Chat;
namespace WingsEmu.Plugins.PacketHandling.Game.Basic;
public class WalkPacketHandler : GenericGamePacketHandlerBase<WalkPacket>
{
private readonly IGameLanguageService _gameLanguage;
private readonly IMeditationManager _meditationManager;
private readonly ISacrificeManager _sacrificeManager;
public WalkPacketHandler(ISacrificeManager sacrificeManager, IGameLanguageService gameLanguage, IMeditationManager meditationManager)
{
_sacrificeManager = sacrificeManager;
_gameLanguage = gameLanguage;
_meditationManager = meditationManager;
}
protected override async Task HandlePacketAsync(IClientSession session, WalkPacket walkPacket)
{
DateTime actualTime = DateTime.UtcNow;
if (!session.HasCurrentMapInstance)
{
return;
}
if (walkPacket.Speed != session.PlayerEntity.Speed)
{
session.SendCondPacket();
}
if (session.CurrentMapInstance.IsBlockedZone(walkPacket.XCoordinate, walkPacket.YCoordinate) || session.PlayerEntity.HasShopOpened || session.PlayerEntity.IsInExchange())
{
BeforeReturnLogic(session);
return;
}
if (!session.PlayerEntity.CanPerformMove())
{
session.RefreshStat();
}
(bool IsValid, Position Position) calculatedPosition = GetExpectedClientSidePosition(session, actualTime);
Position expectedClientsidePosition = calculatedPosition.IsValid ? calculatedPosition.Position : session.PlayerEntity.Position;
double distance = expectedClientsidePosition.GetDoubleDistance(walkPacket.XCoordinate, walkPacket.YCoordinate);
int speed = session.PlayerEntity.Speed < 1 ? 1 : session.PlayerEntity.Speed;
double expectedMaximumDistance = speed * 0.4 + 3 + 2; // we sum 3 for basic distance and 2 for error margin
double waitingtime = distance / speed * 2.5;
if (distance > expectedMaximumDistance + (session.PlayerEntity.IsOnVehicle ? 3 : 0)) // margin error for vehicles
{
BeforeReturnLogic(session);
return;
}
session.PlayerEntity.RemoveMeditation(_meditationManager);
session.PlayerEntity.LastWalk = new LastWalk
{
MapId = session.PlayerEntity.MapId,
StartPosition = expectedClientsidePosition,
EndPosition = new Position(walkPacket.XCoordinate, walkPacket.YCoordinate),
WalkTimeStart = actualTime,
WalkTimeEnd = actualTime.AddSeconds(waitingtime)
};
if (session.PlayerEntity.MapInstance.HasMapFlag(MapFlags.IS_BASE_MAP) && session.PlayerEntity.MapInstance.MapInstanceType != MapInstanceType.TimeSpaceInstance)
{
session.PlayerEntity.MapX = walkPacket.XCoordinate;
session.PlayerEntity.MapY = walkPacket.YCoordinate;
}
session.PlayerEntity.ChangePosition(new Position(walkPacket.XCoordinate, walkPacket.YCoordinate));
await CheckNobleGesture(session);
await CheckSpiritOfSacrifice(session);
if (!session.PlayerEntity.CheatComponent.IsInvisible)
{
session.BroadcastMovement(session.PlayerEntity, new ExceptSessionBroadcast(session));
}
session.SendCondPacket();
session.PlayerEntity.LastMove = actualTime;
}
private (bool IsValid, Position Position) GetExpectedClientSidePosition(IClientSession session, DateTime actualTime)
{
if (session.PlayerEntity.LastSitting.AddSeconds(1) < actualTime || session.PlayerEntity.LastWalk.WalkTimeEnd < actualTime || session.PlayerEntity.LastWalk.MapId != session.PlayerEntity.MapId
|| session.PlayerEntity.PositionX != session.PlayerEntity.LastWalk.EndPosition.X || session.PlayerEntity.PositionY != session.PlayerEntity.LastWalk.EndPosition.Y)
{
return (false, default);
}
double expectedPositionRatio = (actualTime - session.PlayerEntity.LastWalk.WalkTimeStart) / (session.PlayerEntity.LastWalk.WalkTimeEnd - session.PlayerEntity.LastWalk.WalkTimeStart);
short x = GetExpectedClientSideCoordinate(session.PlayerEntity.LastWalk.StartPosition.X, session.PlayerEntity.LastWalk.EndPosition.X, expectedPositionRatio);
short y = GetExpectedClientSideCoordinate(session.PlayerEntity.LastWalk.StartPosition.Y, session.PlayerEntity.LastWalk.EndPosition.Y, expectedPositionRatio);
return (true, new Position(x, y));
}
private short GetExpectedClientSideCoordinate(short xStart, short xEnd, double ratio) => Convert.ToInt16((xEnd - xStart) * ratio + xStart);
private void BeforeReturnLogic(IClientSession session)
{
session.SendCondPacket();
session.BroadcastTeleportPacket();
}
private async Task CheckSpiritOfSacrifice(IClientSession session)
{
if (!session.PlayerEntity.HasBuff(BuffVnums.SPIRIT_OF_SACRIFICE))
{
return;
}
IBattleEntity target = _sacrificeManager.GetTarget(session.PlayerEntity);
if (target == null || target.GetDistance(session.PlayerEntity) <= 14)
{
return;
}
// WARNING
if (session.PlayerEntity.GetDistance(target) == 15)
{
string message;
if (target is IPlayerEntity character)
{
message = _gameLanguage.GetLanguage(GameDialogKey.SKILL_SHOUTMESSAGE_SACRIFICE_WARNING, character.Session.UserLanguage);
character.Session.SendMsg(message, MsgMessageType.SmallMiddle);
}
message = _gameLanguage.GetLanguage(GameDialogKey.SKILL_SHOUTMESSAGE_SACRIFICE_WARNING, session.UserLanguage);
session.SendMsg(message, MsgMessageType.SmallMiddle);
}
else
{
await session.PlayerEntity.RemoveSacrifice(target, _sacrificeManager, _gameLanguage);
}
}
private async Task CheckNobleGesture(IClientSession session)
{
if (!session.PlayerEntity.HasBuff(BuffVnums.NOBLE_GESTURE))
{
return;
}
IBattleEntity caster = _sacrificeManager.GetCaster(session.PlayerEntity);
if (caster == null || caster.GetDistance(session.PlayerEntity) <= 14)
{
return;
}
// WARNING
if (caster.GetDistance(session.PlayerEntity) == 15)
{
string message;
if (caster is IPlayerEntity character)
{
message = _gameLanguage.GetLanguage(GameDialogKey.SKILL_SHOUTMESSAGE_SACRIFICE_WARNING, character.Session.UserLanguage);
character.Session.SendMsg(message, MsgMessageType.SmallMiddle);
}
message = _gameLanguage.GetLanguage(GameDialogKey.SKILL_SHOUTMESSAGE_SACRIFICE_WARNING, session.UserLanguage);
session.SendMsg(message, MsgMessageType.SmallMiddle);
}
else
{
await caster.RemoveSacrifice(session.PlayerEntity, _sacrificeManager, _gameLanguage);
}
}
}