481 lines
No EOL
15 KiB
C#
481 lines
No EOL
15 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using WingsEmu.Core.Extensions;
|
|
using WingsEmu.Core.Generics;
|
|
using WingsEmu.DTOs.BCards;
|
|
using WingsEmu.Packets.Enums;
|
|
|
|
namespace WingsEmu.Game.Buffs;
|
|
|
|
public class BCardComponent : IBCardComponent
|
|
{
|
|
private readonly List<BCardDTO> _allBCards = new();
|
|
private readonly ConcurrentDictionary<(BCardType, byte), ThreadSafeHashSet<BCardDTO>> _bCards = new();
|
|
private readonly ConcurrentDictionary<EquipmentType, List<BCardDTO>> _bCardsByEquipmentType = new();
|
|
|
|
private readonly ConcurrentDictionary<Guid, HashSet<BCardDTO>> _buffBCards = new();
|
|
private readonly List<(int casterLevel, BCardDTO bCard)> _buffBCardsCasterLevel = new();
|
|
private readonly ConcurrentDictionary<(BCardType, byte), Dictionary<Guid, (int casterLevel, BCardDTO bCard)>> _buffInformation = new();
|
|
|
|
private readonly ThreadSafeHashSet<BCardDTO> _chargeBCards = new();
|
|
|
|
private readonly ConcurrentDictionary<(BCardType, byte), byte> _hasBCardCache = new();
|
|
|
|
private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.SupportsRecursion);
|
|
|
|
private readonly List<BCardDTO> _onAttack = new();
|
|
private readonly List<BCardDTO> _onDefense = new();
|
|
private readonly IRandomGenerator _randomGenerator;
|
|
|
|
private readonly ConcurrentDictionary<bool, List<BCardDTO>> _shellTriggerOnAttack = new();
|
|
|
|
public BCardComponent(IRandomGenerator randomGenerator) => _randomGenerator = randomGenerator;
|
|
|
|
public void AddEquipmentBCards(EquipmentType equipmentType, IEnumerable<BCardDTO> bCards)
|
|
{
|
|
_lock.EnterWriteLock();
|
|
try
|
|
{
|
|
List<BCardDTO> existingBCards = _bCardsByEquipmentType.GetOrDefault(equipmentType);
|
|
if (existingBCards == null)
|
|
{
|
|
existingBCards = new List<BCardDTO>();
|
|
_bCardsByEquipmentType[equipmentType] = existingBCards;
|
|
}
|
|
|
|
foreach (BCardDTO bCard in bCards)
|
|
{
|
|
existingBCards.Add(bCard);
|
|
AddBCard(bCard);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
public void ClearEquipmentBCards(EquipmentType equipmentType)
|
|
{
|
|
_lock.EnterWriteLock();
|
|
try
|
|
{
|
|
_bCardsByEquipmentType.TryRemove(equipmentType, out List<BCardDTO> bCards);
|
|
if (bCards == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (BCardDTO bCard in bCards)
|
|
{
|
|
RemoveBCard(bCard);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
public IReadOnlyList<BCardDTO> GetEquipmentBCards(EquipmentType equipmentType)
|
|
{
|
|
List<BCardDTO> eqBCards = _bCardsByEquipmentType.GetOrDefault(equipmentType);
|
|
if (eqBCards == null)
|
|
{
|
|
return Array.Empty<BCardDTO>();
|
|
}
|
|
|
|
return eqBCards;
|
|
}
|
|
|
|
public IReadOnlyDictionary<EquipmentType, List<BCardDTO>> GetEquipmentBCards() => _bCardsByEquipmentType;
|
|
|
|
public (int firstData, int secondData) GetAllBCardsInformation(BCardType type, byte subType, int level)
|
|
{
|
|
int firstData = 0;
|
|
int secondData = 0;
|
|
double firstDataMultiply = 1;
|
|
double secondDataMultiply = 1;
|
|
|
|
if (_bCards.TryGetValue((type, subType), out ThreadSafeHashSet<BCardDTO> list))
|
|
{
|
|
foreach (BCardDTO bCard in list)
|
|
{
|
|
int firstDataValue = bCard.FirstDataValue(level);
|
|
int secondDataValue = bCard.SecondDataValue(level);
|
|
|
|
if (type == BCardType.Morale)
|
|
{
|
|
switch (subType)
|
|
{
|
|
case (byte)AdditionalTypes.Morale.SkillCooldownIncreased:
|
|
firstDataMultiply *= 1 + 0.01 * firstDataValue;
|
|
secondDataMultiply *= 1 + 0.01 * secondDataValue;
|
|
break;
|
|
case (byte)AdditionalTypes.Morale.SkillCooldownDecreased:
|
|
firstDataMultiply *= 0.01 * (100 - firstDataValue);
|
|
secondDataMultiply *= 0.01 * (100 - secondDataValue);
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
firstData += firstDataValue;
|
|
secondData += secondDataValue;
|
|
}
|
|
}
|
|
|
|
if (_buffInformation.TryGetValue((type, subType), out Dictionary<Guid, (int casterLevel, BCardDTO bCard)> buff))
|
|
{
|
|
foreach ((int casterLevel, BCardDTO bCard) in buff.Values)
|
|
{
|
|
int firstDataValue = bCard.FirstDataValue(casterLevel);
|
|
int secondDataValue = bCard.SecondDataValue(casterLevel);
|
|
|
|
if (type == BCardType.Morale)
|
|
{
|
|
switch (subType)
|
|
{
|
|
case (byte)AdditionalTypes.Morale.SkillCooldownIncreased:
|
|
firstDataMultiply *= 1 + 0.01 * firstDataValue;
|
|
secondDataMultiply *= 1 + 0.01 * secondDataValue;
|
|
break;
|
|
case (byte)AdditionalTypes.Morale.SkillCooldownDecreased:
|
|
firstDataMultiply *= 0.01 * (100 - firstDataValue);
|
|
secondDataMultiply *= 0.01 * (100 - secondDataValue);
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
firstData += firstDataValue;
|
|
secondData += secondDataValue;
|
|
}
|
|
}
|
|
|
|
if (type == BCardType.Morale && (subType == (byte)AdditionalTypes.Morale.SkillCooldownIncreased || subType == (byte)AdditionalTypes.Morale.SkillCooldownDecreased))
|
|
{
|
|
return ((int)(firstDataMultiply * 100), (int)(secondDataMultiply * 100));
|
|
}
|
|
|
|
return (firstData, secondData);
|
|
}
|
|
|
|
public IReadOnlyList<BCardDTO> GetAllBCards()
|
|
{
|
|
_lock.EnterReadLock();
|
|
try
|
|
{
|
|
return _allBCards.ToArray();
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitReadLock();
|
|
}
|
|
}
|
|
|
|
public IReadOnlyList<(int casterLevel, BCardDTO bCard)> GetBuffBCards(Func<(int, BCardDTO), bool> predicate = null)
|
|
{
|
|
_lock.EnterReadLock();
|
|
try
|
|
{
|
|
return _buffBCardsCasterLevel.FindAll(x => predicate == null || predicate(x));
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitReadLock();
|
|
}
|
|
}
|
|
|
|
public bool HasBCard(BCardType bCardType, byte subType) => _hasBCardCache.TryGetValue((bCardType, subType), out byte amount) && amount > 0;
|
|
|
|
public bool HasEquipmentsBCard(BCardType bCardType, byte subType)
|
|
{
|
|
return _bCardsByEquipmentType.Values.Any(x => x.Any(s => s.Type == (short)bCardType && s.SubType == subType));
|
|
}
|
|
|
|
public void AddBCard(BCardDTO bCard)
|
|
{
|
|
(BCardType, byte) key = ((BCardType)bCard.Type, bCard.SubType);
|
|
ThreadSafeHashSet<BCardDTO> existingBCards = _bCards.GetOrAdd(key, new ThreadSafeHashSet<BCardDTO>());
|
|
existingBCards.Add(bCard);
|
|
|
|
if (!_hasBCardCache.TryGetValue(key, out _))
|
|
{
|
|
_hasBCardCache.TryAdd(key, 1);
|
|
}
|
|
else
|
|
{
|
|
_hasBCardCache[key]++;
|
|
}
|
|
|
|
_allBCards.Add(bCard);
|
|
}
|
|
|
|
public void RemoveBCard(BCardDTO bCard)
|
|
{
|
|
(BCardType, byte) key = ((BCardType)bCard.Type, bCard.SubType);
|
|
ThreadSafeHashSet<BCardDTO> existingBCards = _bCards.GetOrDefault(key);
|
|
|
|
existingBCards?.Remove(bCard);
|
|
if (_hasBCardCache.TryGetValue(key, out byte amount))
|
|
{
|
|
amount -= 1;
|
|
if (amount > 0)
|
|
{
|
|
_hasBCardCache[key] = amount;
|
|
}
|
|
else
|
|
{
|
|
_hasBCardCache.TryRemove(key, out _);
|
|
}
|
|
}
|
|
|
|
_allBCards.Remove(bCard);
|
|
}
|
|
|
|
public void AddBuffBCards(Buff buff, IEnumerable<BCardDTO> bCards = null)
|
|
{
|
|
_lock.EnterWriteLock();
|
|
try
|
|
{
|
|
if (_buffBCards.ContainsKey(buff.BuffId) && bCards == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bCards != null) // Second BCards execution
|
|
{
|
|
foreach (BCardDTO bCard in bCards)
|
|
{
|
|
(BCardType, byte SubType) key = ((BCardType)bCard.Type, bCard.SubType);
|
|
if (bCard.ProcChance != 0)
|
|
{
|
|
int randomNumber = _randomGenerator.RandomNumber();
|
|
if (randomNumber > bCard.ProcChance)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!_buffBCards.TryGetValue(buff.BuffId, out HashSet<BCardDTO> hashSet))
|
|
{
|
|
hashSet = new HashSet<BCardDTO>();
|
|
_buffBCards[buff.BuffId] = hashSet;
|
|
}
|
|
|
|
hashSet.Add(bCard);
|
|
_buffBCardsCasterLevel.Add((buff.CasterLevel, bCard));
|
|
if (!_hasBCardCache.TryGetValue(key, out _))
|
|
{
|
|
_hasBCardCache.TryAdd(key, 1);
|
|
}
|
|
else
|
|
{
|
|
_hasBCardCache[key]++;
|
|
}
|
|
|
|
if (!_buffInformation.TryGetValue(key, out Dictionary<Guid, (int casterLevel, BCardDTO bCard)> newDictionary))
|
|
{
|
|
newDictionary = new Dictionary<Guid, (int casterLevel, BCardDTO bCard)>();
|
|
_buffInformation[((BCardType)bCard.Type, bCard.SubType)] = newDictionary;
|
|
}
|
|
|
|
newDictionary[bCard.Id] = (buff.CasterLevel, bCard);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (BCardDTO bCard in buff.BCards.Where(x => !x.IsSecondBCardExecution.HasValue || !x.IsSecondBCardExecution.Value))
|
|
{
|
|
(BCardType, byte SubType) key = ((BCardType)bCard.Type, bCard.SubType);
|
|
if (bCard.ProcChance != 0)
|
|
{
|
|
int randomNumber = _randomGenerator.RandomNumber();
|
|
if (randomNumber > bCard.ProcChance)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!_buffBCards.TryGetValue(buff.BuffId, out HashSet<BCardDTO> hashSet))
|
|
{
|
|
hashSet = new HashSet<BCardDTO>();
|
|
_buffBCards[buff.BuffId] = hashSet;
|
|
}
|
|
|
|
hashSet.Add(bCard);
|
|
_buffBCardsCasterLevel.Add((buff.CasterLevel, bCard));
|
|
if (!_hasBCardCache.TryGetValue(key, out _))
|
|
{
|
|
_hasBCardCache.TryAdd(key, 1);
|
|
}
|
|
else
|
|
{
|
|
_hasBCardCache[key]++;
|
|
}
|
|
|
|
if (!_buffInformation.TryGetValue(key, out Dictionary<Guid, (int casterLevel, BCardDTO bCard)> newDictionary))
|
|
{
|
|
newDictionary = new Dictionary<Guid, (int casterLevel, BCardDTO bCard)>();
|
|
_buffInformation[((BCardType)bCard.Type, bCard.SubType)] = newDictionary;
|
|
}
|
|
|
|
newDictionary[bCard.Id] = (buff.CasterLevel, bCard);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
public void RemoveBuffBCards(Buff buff)
|
|
{
|
|
_lock.EnterWriteLock();
|
|
try
|
|
{
|
|
if (!_buffBCards.TryRemove(buff.BuffId, out HashSet<BCardDTO> bCards))
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (BCardDTO bCard in bCards)
|
|
{
|
|
(BCardType, byte SubType) key = ((BCardType)bCard.Type, bCard.SubType);
|
|
if (!_buffInformation.TryGetValue(key, out Dictionary<Guid, (int casterLevel, BCardDTO bCard)> toRemove))
|
|
{
|
|
_hasBCardCache.TryRemove(key, out _);
|
|
continue;
|
|
}
|
|
|
|
toRemove.Remove(bCard.Id);
|
|
_buffBCardsCasterLevel.Remove((buff.CasterLevel, bCard));
|
|
if (_hasBCardCache.TryGetValue(key, out byte amount))
|
|
{
|
|
amount -= 1;
|
|
if (amount > 0)
|
|
{
|
|
_hasBCardCache[key] = amount;
|
|
continue;
|
|
}
|
|
|
|
_hasBCardCache.TryRemove(key, out _);
|
|
_buffInformation.TryRemove(key, out _);
|
|
continue;
|
|
}
|
|
|
|
_hasBCardCache.TryRemove(key, out _);
|
|
_buffInformation.TryRemove(key, out _);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
public void AddTriggerBCards(BCardTriggerType triggerType, List<BCardDTO> bCards)
|
|
{
|
|
_lock.EnterWriteLock();
|
|
try
|
|
{
|
|
if (bCards == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!bCards.Any())
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (triggerType)
|
|
{
|
|
case BCardTriggerType.ATTACK:
|
|
_onAttack.AddRange(bCards);
|
|
break;
|
|
case BCardTriggerType.DEFENSE:
|
|
_onDefense.AddRange(bCards);
|
|
break;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
public void RemoveAllTriggerBCards(BCardTriggerType triggerType)
|
|
{
|
|
_lock.EnterWriteLock();
|
|
try
|
|
{
|
|
if (triggerType == BCardTriggerType.ATTACK)
|
|
{
|
|
_onAttack.Clear();
|
|
return;
|
|
}
|
|
|
|
_onDefense.Clear();
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
public IReadOnlyList<BCardDTO> GetTriggerBCards(BCardTriggerType triggerType)
|
|
=> triggerType == BCardTriggerType.ATTACK ? _onAttack : _onDefense;
|
|
|
|
public void AddShellTrigger(bool isMainWeapon, List<BCardDTO> bCards)
|
|
{
|
|
if (!_shellTriggerOnAttack.TryGetValue(isMainWeapon, out List<BCardDTO> list))
|
|
{
|
|
list = new List<BCardDTO>();
|
|
_shellTriggerOnAttack[isMainWeapon] = list;
|
|
}
|
|
|
|
list.AddRange(bCards);
|
|
}
|
|
|
|
public void ClearShellTrigger(bool isMainWeapon)
|
|
{
|
|
if (!_shellTriggerOnAttack.TryGetValue(isMainWeapon, out List<BCardDTO> list))
|
|
{
|
|
return;
|
|
}
|
|
|
|
list.Clear();
|
|
}
|
|
|
|
public IReadOnlyList<BCardDTO> GetShellTriggers(bool isMainWeapon) => !_shellTriggerOnAttack.TryGetValue(isMainWeapon, out List<BCardDTO> list) ? Array.Empty<BCardDTO>() : list;
|
|
|
|
public void AddChargeBCard(BCardDTO bCard)
|
|
{
|
|
if (_chargeBCards.Contains(bCard))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_chargeBCards.Add(bCard);
|
|
}
|
|
|
|
public void RemoveChargeBCard(BCardDTO bCard)
|
|
{
|
|
_chargeBCards.Remove(bCard);
|
|
}
|
|
|
|
public void ClearChargeBCard()
|
|
{
|
|
_chargeBCards.Clear();
|
|
}
|
|
|
|
public IEnumerable<BCardDTO> GetChargeBCards() => _chargeBCards;
|
|
} |