server-master/srcs/WingsAPI.Game/Extensions/SkillExtension.cs
2026-02-10 18:21:30 +01:00

409 lines
No EOL
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Foundatio.Utility;
using PhoenixLib.Events;
using WingsEmu.DTOs.BCards;
using WingsEmu.DTOs.Buffs;
using WingsEmu.DTOs.Skills;
using WingsEmu.Game._enum;
using WingsEmu.Game.Battle;
using WingsEmu.Game.Buffs;
using WingsEmu.Game.Characters;
using WingsEmu.Game.Entities;
using WingsEmu.Game.Managers.StaticData;
using WingsEmu.Game.Monster.Event;
using WingsEmu.Game.Networking;
using WingsEmu.Game.Skills;
using WingsEmu.Packets.Enums;
using WingsEmu.Packets.Enums.Battle;
using WingsEmu.Packets.ServerPackets.Battle;
namespace WingsEmu.Game.Extensions;
public static class SkillExtension
{
public static SkillInfo GetInfo(this SkillDTO skill, PartnerSkill partnerSkill = null, IBattleEntity battleEntity = null)
{
var dictionary = new Dictionary<SkillCastType, HashSet<BCardDTO>>();
foreach (BCardDTO bCard in skill.BCards)
{
var key = (SkillCastType)bCard.CastType;
if (!dictionary.TryGetValue(key, out HashSet<BCardDTO> hashSet))
{
hashSet = new HashSet<BCardDTO>();
dictionary[key] = hashSet;
}
hashSet.Add(bCard);
}
if (battleEntity != null && !battleEntity.IsPlayer())
{
IReadOnlyList<BCardDTO> onAttackBCards = battleEntity.BCardComponent.GetTriggerBCards(BCardTriggerType.ATTACK);
foreach (BCardDTO bCard in onAttackBCards)
{
var key = (SkillCastType)bCard.CastType;
if (!dictionary.TryGetValue(key, out HashSet<BCardDTO> hashSet))
{
hashSet = new HashSet<BCardDTO>();
dictionary[key] = hashSet;
}
hashSet.Add(bCard);
}
}
return new SkillInfo
{
Vnum = skill.Id,
AttackType = skill.AttackType,
AoERange = skill.AoERange,
BCards = skill.BCards,
CastAnimation = skill.CtAnimation,
CastEffect = skill.CtEffect,
SkillType = skill.SkillType,
CastTime = skill.CastTime,
Cooldown = skill.Cooldown,
CastId = skill.CastId,
Element = skill.Element,
HitAnimation = skill.SuAnimation,
HitEffect = skill.SuEffect,
HitType = skill.HitType,
TargetType = skill.TargetType,
Combos = skill.Combos,
TargetAffectedEntities = skill.TargetAffectedEntities,
Range = skill.Range,
IsUsingSecondWeapon = skill.IsUsingSecondWeapon,
IsComboSkill = skill.SpecialCost == 999,
ManaCost = skill.MpCost,
PartnerSkillRank = partnerSkill?.Rank,
BCardsType = dictionary
};
}
public static SkillInfo GetUpgradedSkill(this IPlayerEntity playerEntity, SkillDTO originalSkill, ICardsManager cardsManager, ISkillsManager skillsManager)
{
if (!playerEntity.SkillComponent.SkillUpgrades.TryGetValue((short)originalSkill.Id, out HashSet<IBattleEntitySkill> hashSet))
{
return null;
}
if (!hashSet.Any())
{
return null;
}
hashSet = hashSet.OrderBy(x => x.Skill.UpgradeType).ToHashSet();
BCardDTO[] upgradesBCards = hashSet.SelectMany(x => x.Skill.BCards).ToArray();
SkillDTO getBasicSkill = skillsManager.GetSkill(originalSkill.Id).DeepClone();
var bCards = new List<BCardDTO>(getBasicSkill.BCards);
var dictionary = new Dictionary<SkillCastType, HashSet<BCardDTO>>();
foreach (BCardDTO bCard in bCards)
{
var key = (SkillCastType)bCard.CastType;
if (!dictionary.TryGetValue(key, out HashSet<BCardDTO> hashSetBCards))
{
hashSetBCards = new HashSet<BCardDTO>();
dictionary[key] = hashSetBCards;
}
hashSetBCards.Add(bCard);
}
foreach (BCardDTO upgradeBCard in upgradesBCards)
{
var key = (SkillCastType)upgradeBCard.CastType;
if (!dictionary.TryGetValue(key, out HashSet<BCardDTO> hashSetBCards))
{
hashSetBCards = new HashSet<BCardDTO>();
dictionary[key] = hashSetBCards;
}
if (upgradeBCard.Type != (short)BCardType.Buff)
{
if (upgradeBCard.FirstDataScalingType == BCardScalingType.NORMAL_VALUE && upgradeBCard.SecondDataScalingType == BCardScalingType.NORMAL_VALUE)
{
BCardDTO findOriginalBCard = getBasicSkill.BCards.FirstOrDefault(x => x.Type == upgradeBCard.Type && x.SubType == upgradeBCard.SubType);
if (findOriginalBCard == null)
{
bCards.Add(upgradeBCard);
if (!hashSetBCards.Contains(upgradeBCard))
{
hashSetBCards.Add(upgradeBCard);
}
continue;
}
findOriginalBCard.FirstData += upgradeBCard.FirstData;
findOriginalBCard.SecondData += upgradeBCard.SecondData;
hashSetBCards.Add(findOriginalBCard);
continue;
}
BCardDTO originalBCard = getBasicSkill.BCards.FirstOrDefault(x => x.Type == upgradeBCard.Type && x.SubType == upgradeBCard.SubType);
if (originalBCard == null)
{
bCards.Add(upgradeBCard);
hashSetBCards.Add(upgradeBCard);
continue;
}
hashSetBCards.Remove(originalBCard);
bCards.Remove(originalBCard);
bCards.Add(upgradeBCard);
if (!hashSetBCards.Contains(upgradeBCard))
{
hashSetBCards.Add(upgradeBCard);
}
continue;
}
BCardDTO[] buffBCards = getBasicSkill.BCards.Where(x => x.Type == (short)BCardType.Buff && x.SubType == (byte)AdditionalTypes.Buff.ChanceCausing).ToArray();
bool buffsExist = false;
if (buffBCards.Any())
{
buffsExist = true;
foreach (BCardDTO buffBCard in buffBCards)
{
int first = buffBCard.SecondData;
int second = upgradeBCard.SecondData;
Card firstBuff = cardsManager.GetCardByCardId(first);
Card secondBuff = cardsManager.GetCardByCardId(second);
if (firstBuff == null || secondBuff == null)
{
bCards.Add(upgradeBCard);
hashSetBCards.Add(upgradeBCard);
continue;
}
if (firstBuff.GroupId != secondBuff.GroupId)
{
bCards.Add(upgradeBCard);
hashSetBCards.Add(upgradeBCard);
continue;
}
hashSetBCards.Remove(buffBCard);
bCards.Remove(buffBCard);
bCards.Add(upgradeBCard);
hashSetBCards.Add(upgradeBCard);
}
}
if (buffsExist)
{
continue;
}
bCards.Add(upgradeBCard);
hashSetBCards.Add(upgradeBCard);
}
SkillDTO[] skills = hashSet.Select(x => x.Skill).ToArray();
int manaCost = originalSkill.MpCost + skills.Sum(x => x.MpCost);
byte aoeRange = (byte)(originalSkill.AoERange + skills.Sum(x => x.AoERange));
byte range = (byte)(originalSkill.Range + skills.Sum(x => x.Range));
short castTime = (short)(originalSkill.CastTime + skills.Sum(x => x.CastTime));
short cooldown = (short)(originalSkill.Cooldown + skills.Sum(x => x.Cooldown));
SkillDTO element = skills.LastOrDefault(x => x.Element != 0);
SkillDTO ctAnimation = skills.LastOrDefault(x => x.CtAnimation != -1);
SkillDTO ctEffect = skills.LastOrDefault(x => x.CtEffect != -1);
SkillDTO suAnimation = skills.LastOrDefault(x => x.SuAnimation != 0);
SkillDTO suEffect = skills.LastOrDefault(x => x.SuEffect != -1);
return new SkillInfo
{
Vnum = originalSkill.Id,
AttackType = originalSkill.AttackType,
AoERange = aoeRange,
BCards = bCards,
CastAnimation = ctAnimation?.CtAnimation ?? originalSkill.CtAnimation,
CastEffect = ctEffect?.CtEffect ?? originalSkill.CtEffect,
SkillType = originalSkill.SkillType,
CastTime = castTime,
Cooldown = cooldown,
CastId = originalSkill.CastId,
Element = element?.Element ?? originalSkill.Element,
HitAnimation = suAnimation?.SuAnimation ?? originalSkill.SuAnimation,
HitEffect = suEffect?.SuEffect ?? originalSkill.SuEffect,
HitType = originalSkill.HitType,
TargetType = originalSkill.TargetType,
Combos = originalSkill.Combos,
TargetAffectedEntities = originalSkill.TargetAffectedEntities,
Range = range,
IsUsingSecondWeapon = originalSkill.IsUsingSecondWeapon,
IsComboSkill = originalSkill.SpecialCost == 999,
ManaCost = manaCost,
BCardsType = dictionary
};
}
public static void CancelCastingSkill(this IBattleEntity entity)
{
entity.RemoveCastingSkill();
if (!(entity is IPlayerEntity character))
{
return;
}
character.Session.SendCancelPacket(CancelType.NotInCombatMode);
}
public static void SendSpecialistGuri(this IClientSession session, int castId)
{
if (session.PlayerEntity.Specialist == null)
{
return;
}
#region Types
switch (session.PlayerEntity.Specialist.GameItem.Morph)
{
case 1: // Pajama
int type = 0;
if (castId == 1)
{
type = 14;
}
if (castId >= 2 && castId <= 4)
{
type = 18 + castId;
}
if (castId == 5)
{
type = 26;
}
if (castId >= 6 && castId <= 10)
{
type = 24 + castId;
}
session.CurrentMapInstance.Broadcast(session.GenerateGuriPacket(6, 1, session.PlayerEntity.Id, type));
break;
case 16: // Pirate
session.CurrentMapInstance.Broadcast(session.GenerateGuriPacket(6, 1, session.PlayerEntity.Id, 43));
break;
}
#endregion
}
public static List<CharacterStaticBuffDto> GetSavedBuffs(this IPlayerEntity character)
{
var staticBuffList = new List<CharacterStaticBuffDto>();
if (!character.BuffComponent.HasAnyBuff())
{
return staticBuffList;
}
IReadOnlyList<Buff> savedBuffs = character.BuffComponent.GetAllBuffs(x => x.BuffFlags is BuffFlag.BIG_AND_KEEP_ON_LOGOUT or BuffFlag.SAVING_ON_DISCONNECT);
if (!savedBuffs.Any())
{
return staticBuffList;
}
foreach (Buff buff in savedBuffs)
{
var newBuff = new CharacterStaticBuffDto
{
CardId = buff.CardId,
CharacterId = character.Id,
RemainingTime = buff.RemainingTimeInMilliseconds()
};
staticBuffList.Add(newBuff);
}
return staticBuffList;
}
public static short GetCannoneerHitEffect(this IPlayerEntity character, int castId)
{
return castId switch
{
4 => 4522,
6 => 4561,
7 => 4573,
8 => -1,
10 => 4572
};
}
public static bool SkillCanBeUsed(this IBattleEntity entity, CharacterSkill skill) => skill.LastUse <= DateTime.UtcNow;
public static bool IsPassiveSkill(this SkillDTO skill) => skill.SkillType == SkillType.Passive;
public static bool SkillCanBeUsed(this IBattleEntity entity, PartnerSkill skill) => skill != null && skill.LastUse <= DateTime.UtcNow;
public static bool SkillCanBeUsed(this IBattleEntity entity, IBattleEntitySkill skill, in DateTime date) => skill != null && skill.LastUse <= date && entity.Mp >= skill.Skill.MpCost;
public static string GenerateOnyxGuriPacket(this IBattleEntity entity, short x, short y) => $"guri 31 {(byte)entity.Type} {entity.Id} {x} {y}";
public static SkillInfo GetFakeTeleportSkill(this IBattleEntity entity) =>
new()
{
Cooldown = 600,
CastId = 8
};
public static SkillInfo GetFakeBombSkill(this IBattleEntity entity) =>
new()
{
Cooldown = 600,
CastId = 4
};
public static async Task TryDespawnBomb(this IPlayerEntity playerEntity, IAsyncEventPipeline asyncEventPipeline)
{
if (!playerEntity.SkillComponent.BombEntityId.HasValue)
{
return;
}
IMonsterEntity bomb = playerEntity.MapInstance?.GetMonsterById(playerEntity.SkillComponent.BombEntityId.Value);
if (bomb?.MapInstance == null)
{
return;
}
await asyncEventPipeline.ProcessEventAsync(new MonsterDeathEvent(bomb));
SkillInfo fakeBombSkill = playerEntity.GetFakeBombSkill();
playerEntity.BroadcastSuPacket(playerEntity, fakeBombSkill, 0, SuPacketHitMode.NoDamageSuccess);
playerEntity.Session.SendSkillCooldownResetAfter(fakeBombSkill.CastId, (short)playerEntity.ApplyCooldownReduction(fakeBombSkill));
playerEntity.CancelCastingSkill();
playerEntity.SetSkillCooldown(fakeBombSkill);
playerEntity.SkillComponent.BombEntityId = null;
}
public static ElementType? GetBuffElementType(this IBattleEntity entity, short skillVnum)
{
return skillVnum switch
{
(short)SkillsVnums.FLAME => ElementType.Fire,
(short)SkillsVnums.ICE => ElementType.Water,
(short)SkillsVnums.HALO => ElementType.Light,
(short)SkillsVnums.DARKNESS => ElementType.Shadow,
(short)SkillsVnums.NO_ELEMENT => ElementType.Neutral,
_ => null
};
}
}