using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using WingsAPI.Packets.Enums.Shells; using WingsEmu.DTOs.BCards; using WingsEmu.Game._enum; using WingsEmu.Game.Buffs; using WingsEmu.Game.Buffs.Events; using WingsEmu.Game.Characters; using WingsEmu.Game.Entities; using WingsEmu.Game.Managers.StaticData; using WingsEmu.Game.Networking; using WingsEmu.Packets.Enums; namespace WingsEmu.Game.Extensions; public static class BuffExtension { private static readonly HashSet _buffResistance = new() { (short)BCardType.DebuffResistance, (short)BCardType.AngerSkill, (short)BCardType.SpecialisationBuffResistance, (short)BCardType.DragonSkills, (short)BCardType.Buff }; public static bool IsNormal(this Buff buff) => buff != null && buff.BuffFlags.HasFlag(BuffFlag.NORMAL); public static bool IsNotRemovedOnDeath(this Buff buff) => buff != null && buff.BuffFlags.HasFlag(BuffFlag.NOT_REMOVED_ON_DEATH); public static bool IsNotDisappearOnSpChange(this Buff buff) => buff != null && buff.BuffFlags.HasFlag(BuffFlag.NOT_REMOVED_ON_SP_CHANGE); public static bool IsRefreshAtExpiration(this Buff buff) => buff != null && buff.BuffFlags.HasFlag(BuffFlag.REFRESH_AT_EXPIRATION); public static bool IsDisappearOnPvp(this Buff buff) => buff != null && buff.BuffFlags.HasFlag(BuffFlag.DISAPPEAR_ON_PVP); public static bool IsBigBuff(this Buff buff) => buff != null && buff.BuffFlags.HasFlag(BuffFlag.BIG); public static bool IsNoDuration(this Buff buff) => buff != null && buff.BuffFlags.HasFlag(BuffFlag.NO_DURATION); public static bool IsSavingOnDisconnect(this Buff buff) => buff != null && buff.BuffFlags.HasFlag(BuffFlag.SAVING_ON_DISCONNECT); public static bool IsPartnerBuff(this Buff buff) => buff != null && buff.BuffFlags.HasFlag(BuffFlag.PARTNER); public static bool IsPartnerRankBuff(this int buffVnum) => buffVnum >= 2000; public static int RemainingTimeInMilliseconds(this Buff buff) => (int)(buff.Start - DateTime.UtcNow + buff.Duration).TotalMilliseconds; public static async Task CheckPartnerBuff(this IClientSession session) => await session.EmitEventAsync(new BuffPartnerCheckEvent()); public static double CheckForResistance(this IBattleEntity target, Buff b, ICardsManager cardsManager, out double buffCounter, out double specializedResistance) { var bCards = new List<(int casterLevel, BCardDTO bCard)>(); IReadOnlyList buffDebuffResistance = target.BCardComponent.GetAllBCards(); IReadOnlyList<(int casterLevel, BCardDTO bCard)> buffsBCards = target.BCardComponent.GetBuffBCards(); foreach (BCardDTO bCard in buffDebuffResistance) { bCards.Add((target.Level, bCard)); } bCards.AddRange(buffsBCards); double debuffCounter = 1; buffCounter = 1; specializedResistance = 1; int secondDataSum = 0; foreach ((int casterLevel, BCardDTO resistanceBCard) in bCards) { if (!_buffResistance.Contains(resistanceBCard.Type)) { continue; } int firstDataValue = resistanceBCard.FirstDataValue(casterLevel); int secondDataValue = resistanceBCard.SecondDataValue(casterLevel); switch (resistanceBCard.Type) { case (short)BCardType.SpecialisationBuffResistance when resistanceBCard.SubType == (byte)AdditionalTypes.SpecialisationBuffResistance.ResistanceToEffect || resistanceBCard.SubType == (byte)AdditionalTypes.SpecialisationBuffResistance.ResistanceToEffectNegated: { if (secondDataValue == 0) { continue; } Card anotherBuff = cardsManager.GetCardByCardId(secondDataValue); if (anotherBuff == null) { continue; } if (b.GroupId == anotherBuff.GroupId && b.Level <= anotherBuff.Level) { specializedResistance *= 1.0 - firstDataValue / 100.0; } break; } case (short)BCardType.DragonSkills when resistanceBCard.SubType == (byte)AdditionalTypes.DragonSkills.CannotUseBuffChance: { if (b.Level <= resistanceBCard.SecondData) { buffCounter *= 1.0 - firstDataValue / 100.0; } break; } case (short)BCardType.DebuffResistance when b.BuffGroup == BuffGroup.Bad: { switch (resistanceBCard.SubType) { case (byte)AdditionalTypes.DebuffResistance.NeverBadDiseaseEffectChance when b.BuffCategory == BuffCategory.DiseaseSeries && b.Level <= firstDataValue: secondDataSum -= secondDataValue; break; case (byte)AdditionalTypes.DebuffResistance.NeverBadGeneralEffectChance when b.BuffCategory == BuffCategory.GeneralEffect && b.Level <= firstDataValue: secondDataSum -= secondDataValue; break; case (byte)AdditionalTypes.DebuffResistance.NeverBadMagicEffectChance when b.BuffCategory == BuffCategory.MagicEffect && b.Level <= firstDataValue: secondDataSum -= secondDataValue; break; case (byte)AdditionalTypes.DebuffResistance.NeverBadToxicEffectChance when b.BuffCategory == BuffCategory.PoisonType && b.Level <= firstDataValue: secondDataSum -= secondDataValue; break; case (byte)AdditionalTypes.DebuffResistance.IncreaseBadDiseaseEffectChance when b.BuffCategory == BuffCategory.DiseaseSeries && b.Level <= firstDataValue: secondDataSum += secondDataValue; break; case (byte)AdditionalTypes.DebuffResistance.IncreaseBadGeneralEffectChance when b.BuffCategory == BuffCategory.GeneralEffect && b.Level <= firstDataValue: secondDataSum += secondDataValue; break; case (byte)AdditionalTypes.DebuffResistance.IncreaseBadMagicEffectChance when b.BuffCategory == BuffCategory.MagicEffect && b.Level <= firstDataValue: secondDataSum += secondDataValue; break; case (byte)AdditionalTypes.DebuffResistance.IncreaseBadToxicEffectChance when b.BuffCategory == BuffCategory.PoisonType && b.Level <= firstDataValue: secondDataSum += secondDataValue; break; } switch (resistanceBCard.SubType) { case (byte)AdditionalTypes.DebuffResistance.NeverBadEffectChance when b.Level <= firstDataValue: secondDataSum -= secondDataValue; break; case (byte)AdditionalTypes.DebuffResistance.IncreaseBadEffectChance when b.Level <= firstDataValue: secondDataSum += secondDataValue; break; } break; } case (short)BCardType.AngerSkill when b.BuffGroup == BuffGroup.Good: switch (resistanceBCard.SubType) { case (byte)AdditionalTypes.AngerSkill.BlockGoodEffectNegated when b.Level <= secondDataValue: case (byte)AdditionalTypes.AngerSkill.BlockGoodEffect when b.Level <= secondDataValue: buffCounter *= 1.0 - firstDataValue / 100.0; break; } break; case (short)BCardType.Buff when resistanceBCard.SubType == (byte)AdditionalTypes.Buff.PreventingBadEffect: if (firstDataValue != b.GroupId) { break; } if (b.Level > secondDataValue) { break; } secondDataSum -= 80; break; } } debuffCounter *= 1.0 + secondDataSum / 100.0; if (target is not IPlayerEntity playerEntity) { return debuffCounter; } int shell = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedAllNegativeEffect); debuffCounter *= 1.0 - shell / 100.0; int reducedByBuffVnum = 0; switch ((BuffVnums)b.CardId) { case BuffVnums.MINOR_BLEEDING: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedMinorBleeding); reducedByBuffVnum += playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedBleedingAndMinorBleeding); break; case BuffVnums.BLEEDING: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedBleedingAndMinorBleeding); break; case BuffVnums.BLACKOUT: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedStun); break; case BuffVnums.HAND_OF_DEATH: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedParalysis); break; case BuffVnums.FREEZE: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedFreeze); break; case BuffVnums.BLIND: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedBlind); break; case BuffVnums.BIND: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedSlow); break; case BuffVnums.WEAKEN_DEFENCE_POWER: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedArmorDeBuff); break; case BuffVnums.SHOCK: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedShock); break; case BuffVnums.PARALYSIS: reducedByBuffVnum = playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedPoisonParalysis); break; } debuffCounter *= 1.0 - reducedByBuffVnum / 100.0; int reducedByGroupId = (BuffGroupIds)b.GroupId switch { BuffGroupIds.STUNS => playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedAllStun), BuffGroupIds.BLEEDING => playerEntity.GetMaxArmorShellValue(ShellEffectType.ReducedAllBleedingType), _ => 0 }; debuffCounter *= 1.0 - reducedByGroupId / 100.0; return debuffCounter; } public static void SendBuffsPacket(this IClientSession session) { if (!session.PlayerEntity.BuffComponent.HasAnyBuff()) { return; } IReadOnlyList buffs = session.PlayerEntity.BuffComponent.GetAllBuffs(); foreach (Buff buff in buffs) { if (!buff.IsBigBuff()) { switch (buff.CardId) { case (short)BuffVnums.CHARGE when session.PlayerEntity.BCardComponent.GetChargeBCards().Any(): int sum = session.PlayerEntity.BCardComponent.GetChargeBCards().Sum(x => x.FirstDataValue(session.PlayerEntity.Level)); session.SendBfPacket(buff, sum, sum); break; case (short)BuffVnums.CHARGE: session.SendBfPacket(buff, session.PlayerEntity.ChargeComponent.GetCharge(), session.PlayerEntity.ChargeComponent.GetCharge()); break; default: session.SendBfLeftPacket(buff); break; } } else { session.SendStaticBuffUiPacket(buff, buff.RemainingTimeInMilliseconds()); } } } }