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

383 lines
No EOL
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Foundatio.Utility;
using WingsEmu.DTOs.Skills;
using WingsEmu.Game;
using WingsEmu.Game._enum;
using WingsEmu.Game.Battle;
using WingsEmu.Game.Characters;
using WingsEmu.Game.Configurations;
using WingsEmu.Game.Entities;
using WingsEmu.Game.Extensions;
using WingsEmu.Game.Extensions.Mates;
using WingsEmu.Game.Helpers.Damages;
using WingsEmu.Game.Maps;
using WingsEmu.Game.Mates;
using WingsEmu.Game.Networking;
using WingsEmu.Game.Skills;
using WingsEmu.Packets.ClientPackets;
using WingsEmu.Packets.Enums;
namespace WingsEmu.Plugins.PacketHandling.Game.Mate;
public class SuctlPacketHandler : GenericGamePacketHandlerBase<SuctlPacket>
{
private readonly IPartnerSpecialistBasicConfig _partnerSpecialistBasic;
private readonly IRandomGenerator _randomGenerator;
public SuctlPacketHandler(IRandomGenerator randomGenerator, IPartnerSpecialistBasicConfig partnerSpecialistBasic)
{
_randomGenerator = randomGenerator;
_partnerSpecialistBasic = partnerSpecialistBasic;
}
protected override async Task HandlePacketAsync(IClientSession session, SuctlPacket packet)
{
if (session.IsMuted())
{
session.SendMuteMessage();
return;
}
if (packet == null)
{
session.SendDebugMessage("packet == null");
return;
}
// check, if owner of mate is in PvP cell
if (session.CurrentMapInstance.IsPvp && session.CurrentMapInstance.PvpZone(session.PlayerEntity.PositionX, session.PlayerEntity.PositionY))
{
session.SendDebugMessage("Character in No-PvP zone");
return;
}
if (session.PlayerEntity.IsOnVehicle)
{
session.SendDebugMessage("Mate - session.IsVehicled");
return;
}
if (session.PlayerEntity.Invisible)
{
session.SendDebugMessage("Mate - session.PlayerEntity.Invisible");
return;
}
if (session.PlayerEntity.IsInvisible())
{
session.SendDebugMessage("Mate - session.PlayerEntity.IsInvisible()");
return;
}
if (session.PlayerEntity.CheatComponent.IsInvisible)
{
session.SendDebugMessage("Mate - session.IsVehicled, InvisibleGm");
return;
}
if (session.PlayerEntity.IsInvisible())
{
return;
}
INpcEntity npc = session.PlayerEntity.MapInstance.GetNpcById(packet.MateTransportId);
if (npc != null)
{
await CheckNpcAttack(session, npc, packet);
return;
}
IMateEntity attacker = session.PlayerEntity.MateComponent.GetMate(x => x.Id == packet.MateTransportId);
if (attacker == null)
{
session.SendDebugMessage("Mate == null");
return;
}
if (!attacker.IsAlive())
{
session.SendDebugMessage("Mate is dead");
return;
}
if (attacker.IsCastingSkill)
{
session.SendDebugMessage("Mate IsCasting");
return;
}
if (attacker.IsSitting)
{
session.SendDebugMessage("Mate IsSitting");
return;
}
if (!attacker.CanAttack())
{
return;
}
// check, if mate is in PvP cell
if (attacker.MapInstance.IsPvp && attacker.MapInstance.PvpZone(attacker.PositionX, attacker.PositionY))
{
session.SendDebugMessage("Mate in No-PvP zone");
return;
}
IBattleEntity target = session.PlayerEntity.MapInstance.GetBattleEntity(packet.TargetType, packet.TargetId);
switch (target)
{
case null:
session.SendDebugMessage("Target == null");
return;
case IMonsterEntity { MonsterRaceType: MonsterRaceType.Fixed }:
case IPlayerEntity { IsSeal: true }:
return;
}
// check, if target is in PvP cell
if (target.MapInstance.IsPvp && target.MapInstance.PvpZone(target.PositionX, target.PositionY))
{
session.SendDebugMessage("Target in No-PvP zone");
return;
}
// check, if target owner is in PvP cell
IPlayerEntity targetMateOwner = (target as IMateEntity)?.Owner;
if (targetMateOwner != null && target.MapInstance.PvpZone(targetMateOwner.PositionX, targetMateOwner.PositionY) && target.MapInstance.IsPvp)
{
session.SendDebugMessage("Target Mate Owner in No-PvP zone");
return;
}
if (!target.IsAlive())
{
session.SendDebugMessage("Target is dead");
return;
}
if (target is IMonsterEntity mob && mob.SummonerId != 0 && mob.SummonerType == VisualType.Player && !mob.IsMateTrainer)
{
return;
}
IEnumerable<IBattleEntitySkill> mateSkills = attacker.Skills;
if (mateSkills == null)
{
session.SendDebugMessage("mateSkills == null");
return;
}
IBattleEntitySkill ski = mateSkills.FirstOrDefault(s => s.Skill.SkillType == SkillType.MonsterSkill);
SkillDTO skill = null;
if (ski is NpcMonsterSkill npcMonsterSkill)
{
if (npcMonsterSkill.IsBasicAttack)
{
skill = npcMonsterSkill.Skill;
}
else if (npcMonsterSkill.Rate == 0 || _randomGenerator.RandomNumber() <= npcMonsterSkill.Rate)
{
skill = ski.Skill;
}
}
SkillInfo skillInfo = skill == null ? attacker.BasicSkill.DeepClone() : skill.GetInfo(battleEntity: attacker);
if (attacker.IsUsingSp && attacker.Specialist != null)
{
skillInfo.Element = attacker.Specialist.GameItem.Element;
}
if (skillInfo.Vnum != 0 && !attacker.SkillCanBeUsed(ski, DateTime.UtcNow))
{
skill = null;
skillInfo = attacker.BasicSkill;
}
if (session.PlayerEntity.IsAllyWith(target))
{
return;
}
if (attacker.Mp < skill?.MpCost)
{
skill = null;
skillInfo = attacker.BasicSkill;
}
if (skillInfo.Vnum == 0 && attacker.LastBasicSkill > DateTime.UtcNow)
{
return;
}
if (!session.PlayerEntity.CheatComponent.HasGodMode && skill != null)
{
attacker.RemoveEntityMp(skill.MpCost, skill);
}
int cellSizeBonus = 3;
if (target is INpcMonsterEntity npcMonsterEntity)
{
cellSizeBonus += npcMonsterEntity.CellSize;
}
if (!attacker.Position.IsInRange(target.Position, skillInfo.Range + cellSizeBonus))
{
return;
}
session.SendMateEffect(attacker, EffectType.PetAttack);
session.SendMateLife(attacker);
DateTime castTime = attacker.GenerateSkillCastTime(skillInfo);
attacker.LastSkillUse = DateTime.UtcNow;
if (skillInfo.Vnum == 0)
{
if (skillInfo.Cooldown > 10) // Cooldown is > 1 second
{
skillInfo.Cooldown = 10;
}
attacker.LastBasicSkill = DateTime.UtcNow.AddMilliseconds(attacker.ApplyCooldownReduction(skillInfo) * 100);
if (attacker.IsUsingSp && attacker.Specialist != null)
{
skillInfo.HitEffect = _partnerSpecialistBasic.GetAttackEffect(attacker.Specialist.GameItem.Morph);
}
}
await attacker.EmitEventAsync(new BattleExecuteSkillEvent(attacker, target, skillInfo, castTime));
}
private async Task CheckNpcAttack(IClientSession session, INpcEntity npc, SuctlPacket packet)
{
if (npc.MapInstance.MapInstanceType != MapInstanceType.TimeSpaceInstance)
{
return;
}
if (!npc.IsTimeSpaceMate)
{
return;
}
List<INpcEntity> partners = session.PlayerEntity.TimeSpaceComponent.Partners;
INpcEntity partner = partners.FirstOrDefault(x => x.MonsterVNum == npc.MonsterVNum);
if (partner == null || !npc.CharacterPartnerId.HasValue || npc.Id != partner.Id)
{
return;
}
if (!npc.IsAlive())
{
session.SendDebugMessage("Mate is dead");
return;
}
if (npc.IsCastingSkill)
{
session.SendDebugMessage("Mate IsCasting");
return;
}
if (npc.IsSitting)
{
session.SendDebugMessage("Mate IsSitting");
return;
}
if (!npc.CanPerformAttack())
{
return;
}
IBattleEntity target = session.PlayerEntity.MapInstance.GetBattleEntity(packet.TargetType, packet.TargetId);
switch (target)
{
case null:
session.SendDebugMessage("Target == null");
return;
case IMonsterEntity { MonsterRaceType: MonsterRaceType.Fixed }:
return;
}
if (!target.IsAlive())
{
session.SendDebugMessage("Target is dead");
return;
}
if (target is IMonsterEntity mob && mob.SummonerId != 0 && mob.SummonerType == VisualType.Player)
{
return;
}
IBattleEntitySkill skillToUse = null;
DateTime now = DateTime.UtcNow;
foreach (IBattleEntitySkill skill in npc.Skills)
{
if (skill is not NpcMonsterSkill npcMonsterSkill)
{
continue;
}
if (npcMonsterSkill.IsBasicAttack && npc.SkillCanBeUsed(npcMonsterSkill, now))
{
skillToUse = npcMonsterSkill;
break;
}
if (!npc.SkillCanBeUsed(npcMonsterSkill, now))
{
continue;
}
skillToUse = npcMonsterSkill;
break;
}
SkillInfo skillInfo = skillToUse?.Skill.GetInfo() ?? npc.BasicSkill;
if (session.PlayerEntity.IsAllyWith(target))
{
return;
}
if (skillInfo.Vnum == 0 && npc.Skills.Any(x => x is NpcMonsterSkill { IsBasicAttack: true }))
{
return;
}
if (npc.LastBasicAttack > DateTime.UtcNow && skillInfo.Vnum == 0)
{
return;
}
int cellSizeBonus = 3;
if (target is INpcMonsterEntity npcMonsterEntity)
{
cellSizeBonus += npcMonsterEntity.CellSize;
}
if (!npc.Position.IsInRange(target.Position, skillInfo.Range + cellSizeBonus))
{
return;
}
session.SendNpcEffect(npc, EffectType.PetAttack);
DateTime castTime = npc.GenerateSkillCastTime(skillInfo);
npc.LastBasicAttack = DateTime.UtcNow.AddMilliseconds(npc.ApplyCooldownReduction(npc.BasicSkill) * 100);
await npc.EmitEventAsync(new BattleExecuteSkillEvent(npc, target, skillInfo, castTime));
}
}