server-master/srcs/BazaarServer/Managers/BazaarSearchManager.cs
2026-02-10 18:21:30 +01:00

719 lines
No EOL
34 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using PhoenixLib.Caching;
using PhoenixLib.Logging;
using WingsAPI.Communication.Bazaar;
using WingsAPI.Data.Bazaar;
using WingsAPI.Data.GameData;
using WingsAPI.Game.Extensions.Bazaar;
using WingsAPI.Packets.Enums.Bazaar;
using WingsAPI.Packets.Enums.Bazaar.Filter;
using WingsAPI.Packets.Enums.Bazaar.SubFilter;
using WingsEmu.Core.Generics;
using WingsEmu.DTOs.Items;
using WingsEmu.Game._enum;
using WingsEmu.Packets.Enums;
using WingsEmu.Packets.Enums.Battle;
using WingsEmu.Packets.Enums.Character;
namespace BazaarServer.Managers
{
public class BazaarSearchManager
{
private readonly ILongKeyCachedRepository<ConcurrentDictionary<long, BazaarItemDTO>> _bazaarItemByItemVnum;
private readonly ILongKeyCachedRepository<(ItemDTO, BazaarLevelFilterType)> _cachedItems;
private readonly IResourceLoader<ItemDTO> _itemDao;
private readonly Dictionary<BazaarCategoryFilterType, Dictionary<byte, HashSet<int>>> _itemVnumByCategoryAndSubCategory = new();
private readonly ThreadSafeHashSet<int> _itemVnumsRegistered = new();
public BazaarSearchManager(ILongKeyCachedRepository<ConcurrentDictionary<long, BazaarItemDTO>> bazaarItemByItemVnum, IResourceLoader<ItemDTO> itemDao,
ILongKeyCachedRepository<(ItemDTO, BazaarLevelFilterType)> cachedItems)
{
_bazaarItemByItemVnum = bazaarItemByItemVnum;
_itemDao = itemDao;
_cachedItems = cachedItems;
}
public async Task Initialize(IEnumerable<BazaarItemDTO> bazaarItemDtos)
{
await CategorizeItems();
await CacheSearchableBazaarItems(bazaarItemDtos);
}
private async Task CacheSearchableBazaarItems(IEnumerable<BazaarItemDTO> bazaarItemDtos)
{
Log.Info("[BAZAAR_SEARCH_MANAGER] Caching searchable BazaarItems");
int count = 0;
foreach (BazaarItemDTO bazaarItemDto in bazaarItemDtos)
{
if (bazaarItemDto.GetBazaarItemStatus() != BazaarListedItemType.ForSale)
{
continue;
}
AddItem(bazaarItemDto);
count++;
}
Log.Info($"[BAZAAR_SEARCH_MANAGER] Cached: {count} searchable BazaarItems");
}
private async Task CategorizeItems()
{
Log.Info("[BAZAAR_SEARCH_MANAGER] Categorizing items from DB");
IEnumerable<ItemDTO> items = await _itemDao.LoadAsync();
int count = 0;
var itemsToRegister = new Dictionary<BazaarCategoryFilterType, HashSet<int>>();
foreach (ItemDTO item in items)
{
(BazaarCategoryFilterType category, IEnumerable<byte> subCategories, bool registerForAllSubcategories) = GetCategoryAndSubCategoriesByItemVnum(item);
if (registerForAllSubcategories && !itemsToRegister.TryAdd(category, new HashSet<int> { item.Id }))
{
itemsToRegister[category].Add(item.Id);
}
if (!_itemVnumByCategoryAndSubCategory.ContainsKey(category))
{
_itemVnumByCategoryAndSubCategory.Add(category, new Dictionary<byte, HashSet<int>>());
}
foreach (byte subCategory in subCategories)
{
if (!_itemVnumByCategoryAndSubCategory[category].ContainsKey(subCategory))
{
_itemVnumByCategoryAndSubCategory[category].Add(subCategory, new HashSet<int>());
}
_itemVnumByCategoryAndSubCategory[category][subCategory].Add(item.Id);
}
if (!_itemVnumByCategoryAndSubCategory[category].ContainsKey(0))
{
_itemVnumByCategoryAndSubCategory[category].Add(0, new HashSet<int>());
}
_itemVnumByCategoryAndSubCategory[category][0].Add(item.Id);
_cachedItems.Set(item.Id, (item, GetItemLevelFilterByLevel(item.LevelMinimum, item.IsHeroic)));
count++;
}
foreach (KeyValuePair<BazaarCategoryFilterType, HashSet<int>> itemToRegister in itemsToRegister)
{
Type type = GetSubCategoryTypeByCategory(itemToRegister.Key);
if (type == null)
{
continue;
}
foreach (byte value in Enum.GetValues(type))
{
if (!_itemVnumByCategoryAndSubCategory[itemToRegister.Key].ContainsKey(value))
{
_itemVnumByCategoryAndSubCategory[itemToRegister.Key].Add(value, new HashSet<int>());
}
foreach (int vnum in itemToRegister.Value)
{
_itemVnumByCategoryAndSubCategory[itemToRegister.Key][value].Add(vnum);
}
}
}
Log.Info($"[BAZAAR_SEARCH_MANAGER] Categorized: {count.ToString()} items");
}
private static Type GetSubCategoryTypeByCategory(BazaarCategoryFilterType category)
{
switch (category)
{
case BazaarCategoryFilterType.Specialist:
return typeof(BazaarCategorySpecialistSubFilterType);
case BazaarCategoryFilterType.Partner:
return typeof(BazaarCategoryPartnerSubFilterType);
case BazaarCategoryFilterType.Pet:
return typeof(BazaarCategoryPetSubFilterType);
case BazaarCategoryFilterType.StoreMount:
return typeof(BazaarCategoryConsumerItemSubFilterType);
default:
return null;
}
}
public void AddItem(BazaarItemDTO bazaarItemDto)
{
ItemInstanceDTO itemInstanceDto = bazaarItemDto?.ItemInstance;
if (itemInstanceDto == null)
{
return;
}
ConcurrentDictionary<long, BazaarItemDTO> dictionary = _bazaarItemByItemVnum.GetOrSet(itemInstanceDto.ItemVNum, () => new ConcurrentDictionary<long, BazaarItemDTO>());
dictionary[bazaarItemDto.Id] = bazaarItemDto;
_itemVnumsRegistered.Add(itemInstanceDto.ItemVNum);
}
public void RemoveItem(BazaarItemDTO bazaarItemDto)
{
if (bazaarItemDto == null)
{
return;
}
ConcurrentDictionary<long, BazaarItemDTO> dictionary = _bazaarItemByItemVnum.GetOrSet(bazaarItemDto.ItemInstance.ItemVNum, () => new ConcurrentDictionary<long, BazaarItemDTO>());
dictionary.Remove(bazaarItemDto.Id, out _);
if (!dictionary.IsEmpty)
{
return;
}
_bazaarItemByItemVnum.Remove(bazaarItemDto.ItemInstance.ItemVNum);
_itemVnumsRegistered.Remove(bazaarItemDto.ItemInstance.ItemVNum);
}
public IReadOnlyCollection<BazaarItemDTO> SearchBazaarItems(BazaarSearchContext bazaarSearchContext)
{
IReadOnlyCollection<int> desiredItemVNums =
(bazaarSearchContext.ItemVNumFilter ?? GetItemVNumsByCategoryAndSubCategory(bazaarSearchContext.CategoryFilterType, bazaarSearchContext.SubTypeFilter)) ??
_itemVnumsRegistered.ToArray();
var itemList = new List<BazaarItemDTO>();
var tempItemList = new List<BazaarItemDTO>();
int ignoreCounter = bazaarSearchContext.Index * bazaarSearchContext.AmountOfItemsPerIndex;
int sendCounter = 0;
foreach (int itemVnum in desiredItemVNums.OrderBy(x => x))
{
ConcurrentDictionary<long, BazaarItemDTO> dictionary = _bazaarItemByItemVnum.Get(itemVnum);
if (dictionary == null)
{
continue;
}
if (ignoreCounter > dictionary.Count && bazaarSearchContext.LevelFilter == BazaarLevelFilterType.All && bazaarSearchContext.RareFilter == BazaarRarityFilterType.All &&
bazaarSearchContext.UpgradeFilter == BazaarUpgradeFilterType.All)
{
ignoreCounter -= dictionary.Count;
continue;
}
(ItemDTO itemDto, BazaarLevelFilterType baseItemLevelFilter) = _cachedItems.Get(itemVnum);
bool itemLevelIsInstanceDependant = false;
if (baseItemLevelFilter == BazaarLevelFilterType.All)
{
itemLevelIsInstanceDependant = true;
}
else
{
if (bazaarSearchContext.LevelFilter != BazaarLevelFilterType.All && !ItemLevelFilterChecker(bazaarSearchContext.LevelFilter, baseItemLevelFilter))
{
continue;
}
}
IOrderedEnumerable<KeyValuePair<long, BazaarItemDTO>> values;
switch (bazaarSearchContext.OrderFilter)
{
case BazaarSortFilterType.PriceAscending:
values = dictionary.OrderBy(x => x.Value.PricePerItem);
break;
case BazaarSortFilterType.PriceDescending:
values = dictionary.OrderByDescending(x => x.Value.PricePerItem);
break;
case BazaarSortFilterType.AmountAscending:
values = dictionary.OrderBy(x => x.Value.Amount - x.Value.SoldAmount);
break;
case BazaarSortFilterType.AmountDescending:
values = dictionary.OrderByDescending(x => x.Value.Amount - x.Value.SoldAmount);
break;
default:
return null;
}
foreach ((long _, BazaarItemDTO bazaarItemDto) in values)
{
BazaarListedItemType itemStatus = bazaarItemDto.GetBazaarItemStatus();
if (itemStatus != BazaarListedItemType.ForSale)
{
RemoveItem(bazaarItemDto);
continue;
}
if (bazaarItemDto.ItemInstance.Type == ItemInstanceType.BoxInstance)
{
BazaarPerfectionFilterType itemRarityFilter = GetPerfectionFilterByInstance(bazaarItemDto.ItemInstance);
if ((int)bazaarSearchContext.RareFilter != (int)BazaarPerfectionFilterType.All && (int)itemRarityFilter != (int)bazaarSearchContext.RareFilter)
{
continue;
}
}
else
{
BazaarRarityFilterType itemRarityFilter = GetRarityFilterByInstance(bazaarItemDto.ItemInstance);
if (bazaarSearchContext.RareFilter != BazaarRarityFilterType.All && itemRarityFilter != bazaarSearchContext.RareFilter)
{
continue;
}
}
BazaarUpgradeFilterType itemUpgradeFilter = GetUpgradeFilterByInstance(bazaarItemDto.ItemInstance);
if (bazaarSearchContext.UpgradeFilter != BazaarUpgradeFilterType.All && itemUpgradeFilter != bazaarSearchContext.UpgradeFilter)
{
continue;
}
if (itemLevelIsInstanceDependant)
{
BazaarLevelFilterType itemLevelFilter = GetItemLevelFilterByInstance(bazaarItemDto.ItemInstance, bazaarItemDto.ItemInstance.Type);
if (bazaarSearchContext.LevelFilter != BazaarLevelFilterType.All && itemLevelFilter != bazaarSearchContext.LevelFilter)
{
continue;
}
}
if (bazaarSearchContext.SubTypeFilter != 0 && bazaarItemDto.ItemInstance.Type == ItemInstanceType.BoxInstance)
{
if ((UsableItemSubType)itemDto.ItemSubType != UsableItemSubType.RaidBoxOrSealedJajamaruSpOrSealedSakuraBead)
{
ItemInstanceDTO boxInstanceDto = bazaarItemDto.ItemInstance;
if (!GetBoxInstanceSubCategories(boxInstanceDto, itemDto).Contains(bazaarSearchContext.SubTypeFilter))
{
continue;
}
}
}
if (ignoreCounter > 0)
{
ignoreCounter--;
continue;
}
if (sendCounter >= bazaarSearchContext.AmountOfItemsPerIndex)
{
break;
}
sendCounter++;
tempItemList.Add(bazaarItemDto);
}
itemList.AddRange(tempItemList);
tempItemList.Clear();
if (sendCounter >= bazaarSearchContext.AmountOfItemsPerIndex)
{
break;
}
}
return itemList;
}
private List<byte> GetBoxInstanceSubCategories(ItemInstanceDTO boxInstanceDto, ItemDTO itemInfo)
{
var subCategory = new List<byte>();
switch ((UsableItemSubType)itemInfo.ItemSubType)
{
case UsableItemSubType.PetBead:
subCategory.Add(
(byte)(boxInstanceDto.HoldingVNum is 0 or null ? BazaarCategoryPetSubFilterType.EmptyPetBead : BazaarCategoryPetSubFilterType.PetBead));
break;
case UsableItemSubType.PartnerBead:
if (boxInstanceDto.HoldingVNum is 0 or null)
{
subCategory.Add((byte)BazaarCategoryPartnerSubFilterType.EmptyPartnerBead);
break;
}
subCategory.Add((byte)BazaarCategoryPartnerSubFilterType.PartnerBead);
subCategory.Add((byte)GetPartnerSubCategoryOfAttack(_cachedItems.Get(boxInstanceDto.HoldingVNum.Value).Item1));
break;
case UsableItemSubType.PartnerSpHolder:
if (boxInstanceDto.HoldingVNum is 0 or null)
{
subCategory.Add((byte)BazaarCategoryPartnerSubFilterType.EmptyCardHolder);
break;
}
subCategory.Add((byte)GetPartnerSubCategoryOfAttack(_cachedItems.Get(boxInstanceDto.HoldingVNum.Value).Item1));
break;
case UsableItemSubType.SpecialistHolder:
if (boxInstanceDto.HoldingVNum is 0 or null)
{
subCategory.Add((byte)BazaarCategorySpecialistSubFilterType.EmptyCardHolder);
break;
}
subCategory.Add(SpMorphToSubCategory(_cachedItems.Get(boxInstanceDto.HoldingVNum.Value).Item1));
break;
case UsableItemSubType.VehicleBead:
subCategory.Add((byte)(boxInstanceDto.HoldingVNum is 0 or null ? BazaarCategoryStoreMountSubFilterType.EmptyMountBead : BazaarCategoryStoreMountSubFilterType.MountBead));
break;
}
return subCategory;
}
private (BazaarCategoryFilterType, IEnumerable<byte>, bool) GetCategoryAndSubCategoriesByItemVnum(ItemDTO itemDto)
{
BazaarCategoryFilterType category = BazaarCategoryFilterType.Miscellaneous;
var subCategories = new List<byte>();
bool registerForAllSubcategories = false;
switch (itemDto.ItemType)
{
case ItemType.Weapon:
category = BazaarCategoryFilterType.Weapon;
subCategories.Add(ItemTypeClassToSubCategory(itemDto.Class));
break;
case ItemType.Armor:
category = BazaarCategoryFilterType.Armour;
subCategories.Add(ItemTypeClassToSubCategory(itemDto.Class));
break;
case ItemType.Fashion:
category = BazaarCategoryFilterType.Equipment;
subCategories.Add(EquipmentTypeToSubCategory1(itemDto.EquipmentSlot));
byte? secondSubCategory = EquipmentTypeToSubCategory2(itemDto.EquipmentSlot, itemDto.Sex switch
{
0 => GenderType.Unisex,
1 => GenderType.Male,
_ => GenderType.Female
});
if (secondSubCategory != null)
{
subCategories.Add(secondSubCategory.Value);
}
break;
case ItemType.Jewelry:
category = BazaarCategoryFilterType.Accessories;
subCategories.Add(EquipmentTypeToSubCategory3(itemDto.EquipmentSlot));
break;
case ItemType.Specialist:
category = BazaarCategoryFilterType.Specialist;
subCategories.Add(SpMorphToSubCategory(itemDto));
break;
case ItemType.Box:
var usableItemSubType = (UsableItemSubType)itemDto.ItemSubType;
switch (usableItemSubType)
{
case UsableItemSubType.PetBead:
category = BazaarCategoryFilterType.Pet;
registerForAllSubcategories = true;
break;
case UsableItemSubType.PartnerBead:
category = BazaarCategoryFilterType.Partner;
registerForAllSubcategories = true;
break;
case UsableItemSubType.PartnerSpHolder:
category = BazaarCategoryFilterType.Partner;
registerForAllSubcategories = true;
break;
case UsableItemSubType.SpecialistHolder:
category = BazaarCategoryFilterType.Specialist;
registerForAllSubcategories = true;
break;
case UsableItemSubType.FairyBead:
category = BazaarCategoryFilterType.Accessories;
subCategories.Add((byte)BazaarCategoryAccessoriesSubFilterType.Fairy);
break;
case UsableItemSubType.VehicleBead:
category = BazaarCategoryFilterType.StoreMount;
registerForAllSubcategories = true;
break;
}
break;
case ItemType.Shell:
category = BazaarCategoryFilterType.Shell;
var shellItemSubType = (ShellItemSubType)itemDto.ItemSubType;
subCategories.Add(shellItemSubType switch
{
ShellItemSubType.Weapon => (byte)BazaarCategoryShellSubFilterType.Weapon,
ShellItemSubType.Armor => (byte)BazaarCategoryShellSubFilterType.Clothing,
_ => default
});
break;
case ItemType.Main:
category = BazaarCategoryFilterType.MainItem;
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.GeneralItems);
break;
case ItemType.Upgrade:
category = BazaarCategoryFilterType.MainItem;
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.Material);
break;
case ItemType.Production:
category = BazaarCategoryFilterType.MainItem;
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.ProductionItem);
break;
case ItemType.Special:
category = BazaarCategoryFilterType.MainItem;
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.SpecialItems);
break;
case ItemType.Potion:
category = BazaarCategoryFilterType.MainItem;
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.HealingPotion);
break;
case ItemType.Event:
category = BazaarCategoryFilterType.MainItem;
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.Event);
break;
case ItemType.Title:
category = BazaarCategoryFilterType.MainItem;
subCategories.Add((byte)BazaarCategoryMainItemSubFilterType.Title);
break;
case ItemType.Sell:
category = BazaarCategoryFilterType.ConsumerItem;
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.SaleItem);
break;
case ItemType.Food:
category = BazaarCategoryFilterType.ConsumerItem;
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.Food);
break;
case ItemType.Snack:
category = BazaarCategoryFilterType.ConsumerItem;
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.Snack);
break;
case ItemType.Magical:
category = BazaarCategoryFilterType.ConsumerItem;
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.MagicItem);
break;
case ItemType.Material:
category = BazaarCategoryFilterType.ConsumerItem;
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.Ingredients);
break;
case ItemType.PetPartnerItem:
category = BazaarCategoryFilterType.ConsumerItem;
subCategories.Add((byte)BazaarCategoryConsumerItemSubFilterType.PartnerItem);
break;
}
return (category, subCategories, registerForAllSubcategories);
}
private static byte ItemTypeClassToSubCategory(byte itemTypeClass)
{
return itemTypeClass switch
{
(int)ItemClassType.Adventurer => (byte)BazaarCategoryWeaponArmourSubFilterType.Adventurer,
(int)ItemClassType.Swordsman => (byte)BazaarCategoryWeaponArmourSubFilterType.Swordsman,
(int)ItemClassType.Archer => (byte)BazaarCategoryWeaponArmourSubFilterType.Archer,
(int)ItemClassType.Mage => (byte)BazaarCategoryWeaponArmourSubFilterType.Magician,
(int)ItemClassType.MartialArtist => (byte)BazaarCategoryWeaponArmourSubFilterType.MartialArtist,
_ => default
};
}
private static byte EquipmentTypeToSubCategory1(EquipmentType equipmentType)
{
return equipmentType switch
{
EquipmentType.Hat => (byte)BazaarCategoryEquipmentSubFilterType.Hat,
EquipmentType.Mask => (byte)BazaarCategoryEquipmentSubFilterType.Accessory,
EquipmentType.Gloves => (byte)BazaarCategoryEquipmentSubFilterType.Gloves,
EquipmentType.Boots => (byte)BazaarCategoryEquipmentSubFilterType.Shoes,
EquipmentType.CostumeSuit => (byte)BazaarCategoryEquipmentSubFilterType.Costume,
EquipmentType.CostumeHat => (byte)BazaarCategoryEquipmentSubFilterType.CostumeHat,
EquipmentType.WeaponSkin => (byte)BazaarCategoryEquipmentSubFilterType.CostumeWeapon,
EquipmentType.Wings => (byte)BazaarCategoryEquipmentSubFilterType.CostumeWings,
_ => default
};
}
private static byte? EquipmentTypeToSubCategory2(EquipmentType equipmentType, GenderType genderType)
{
if (genderType == GenderType.Unisex)
{
return null;
}
bool isMale = genderType == GenderType.Male;
return equipmentType switch
{
EquipmentType.CostumeSuit => (byte)(isMale ? BazaarCategoryEquipmentSubFilterType.CostumeMale : BazaarCategoryEquipmentSubFilterType.CostumeFemale),
EquipmentType.CostumeHat => (byte)(isMale ? BazaarCategoryEquipmentSubFilterType.CostumeHatMale : BazaarCategoryEquipmentSubFilterType.CostumeHatFemale),
_ => null
};
}
private static byte EquipmentTypeToSubCategory3(EquipmentType equipmentType)
{
return equipmentType switch
{
EquipmentType.Necklace => (byte)BazaarCategoryAccessoriesSubFilterType.Necklace,
EquipmentType.Ring => (byte)BazaarCategoryAccessoriesSubFilterType.Ring,
EquipmentType.Bracelet => (byte)BazaarCategoryAccessoriesSubFilterType.Bracelet,
EquipmentType.Fairy => (byte)BazaarCategoryAccessoriesSubFilterType.Fairy,
EquipmentType.Amulet => (byte)BazaarCategoryAccessoriesSubFilterType.Amulet,
_ => default
};
}
private static byte SpMorphToSubCategory(ItemDTO item)
{
int morphId = Convert.ToInt32(item.Morph);
if (!Enum.IsDefined(typeof(MorphIdType), morphId))
{
return default;
}
var morph = (MorphIdType)morphId;
return morph switch
{
MorphIdType.Pyjama => (byte)BazaarCategorySpecialistSubFilterType.Pyjama,
MorphIdType.Warrior => (byte)BazaarCategorySpecialistSubFilterType.Warrior,
MorphIdType.Ninja => (byte)BazaarCategorySpecialistSubFilterType.Ninja,
MorphIdType.Ranger => (byte)BazaarCategorySpecialistSubFilterType.Ranger,
MorphIdType.Assassin => (byte)BazaarCategorySpecialistSubFilterType.Assassin,
MorphIdType.RedMagician => (byte)BazaarCategorySpecialistSubFilterType.RedMagician,
MorphIdType.HolyMage => (byte)BazaarCategorySpecialistSubFilterType.HolyMage,
MorphIdType.Chicken => (byte)BazaarCategorySpecialistSubFilterType.ChickenCostume,
MorphIdType.Jajamaru => (byte)BazaarCategorySpecialistSubFilterType.Jajamaru,
MorphIdType.Crusader => (byte)BazaarCategorySpecialistSubFilterType.Crusader,
MorphIdType.Berserker => (byte)BazaarCategorySpecialistSubFilterType.Berserker,
MorphIdType.Destroyer => (byte)BazaarCategorySpecialistSubFilterType.Destroyer,
MorphIdType.WildKeeper => (byte)BazaarCategorySpecialistSubFilterType.WildKeeper,
MorphIdType.BlueMagician => (byte)BazaarCategorySpecialistSubFilterType.BlueMagician,
MorphIdType.DarkGunner => (byte)BazaarCategorySpecialistSubFilterType.DarkGunner,
MorphIdType.Pirate => (byte)BazaarCategorySpecialistSubFilterType.Pirate,
MorphIdType.Gladiator => (byte)BazaarCategorySpecialistSubFilterType.Gladiator,
MorphIdType.FireCannoneer => (byte)BazaarCategorySpecialistSubFilterType.FireCannoneer,
MorphIdType.Volcano => (byte)BazaarCategorySpecialistSubFilterType.Volcano,
MorphIdType.BattleMonk => (byte)BazaarCategorySpecialistSubFilterType.BattleMonk,
MorphIdType.Scout => (byte)BazaarCategorySpecialistSubFilterType.Scout,
MorphIdType.TideLord => (byte)BazaarCategorySpecialistSubFilterType.TideLord,
MorphIdType.DeathReaper => (byte)BazaarCategorySpecialistSubFilterType.DeathReaper,
MorphIdType.DemonHunter => (byte)BazaarCategorySpecialistSubFilterType.DemonHunter,
MorphIdType.Seer => (byte)BazaarCategorySpecialistSubFilterType.Seer,
MorphIdType.Renegade => (byte)BazaarCategorySpecialistSubFilterType.Renegade,
MorphIdType.AvengingAngel => (byte)BazaarCategorySpecialistSubFilterType.AvengingAngel,
MorphIdType.Archmage => (byte)BazaarCategorySpecialistSubFilterType.Archmage,
MorphIdType.DraconicFist => (byte)BazaarCategorySpecialistSubFilterType.DraconicFist,
MorphIdType.MysticArts => (byte)BazaarCategorySpecialistSubFilterType.MysticArts,
MorphIdType.WeddingCostume => (byte)BazaarCategorySpecialistSubFilterType.WeddingCostume,
MorphIdType.MasterWolf => (byte)BazaarCategorySpecialistSubFilterType.MasterWolf,
MorphIdType.DemonWarrior => (byte)BazaarCategorySpecialistSubFilterType.DemonWarrior,
_ => default
};
}
private BazaarLevelFilterType GetItemLevelFilterByInstance(ItemInstanceDTO itemInstanceDto, ItemInstanceType instanceType)
{
switch (instanceType)
{
case ItemInstanceType.BoxInstance:
case ItemInstanceType.SpecialistInstance:
ItemInstanceDTO specialistInstanceDto = itemInstanceDto;
return GetItemLevelFilterByLevel(specialistInstanceDto.SpLevel, false);
default:
(ItemDTO itemDto, BazaarLevelFilterType bazaarLevelFilterType) = _cachedItems.Get(itemInstanceDto.ItemVNum);
return itemDto.ItemType == ItemType.Shell
? GetItemLevelFilterByLevel(itemInstanceDto.Upgrade, false)
: bazaarLevelFilterType;
}
}
private static BazaarLevelFilterType GetItemLevelFilterByLevel(byte level, bool isHeroic)
{
if (isHeroic)
{
if (level is < 1 or > 60)
{
return BazaarLevelFilterType.ChampionGear;
}
return (BazaarLevelFilterType)(Convert.ToByte(Math.Ceiling(level / 10f)) + (byte)BazaarLevelFilterType.ChampionGear);
}
if (level is < 1 or > 99)
{
return BazaarLevelFilterType.All;
}
return (BazaarLevelFilterType)Convert.ToByte(Math.Ceiling(level / 10f));
}
private static bool ItemLevelFilterChecker(BazaarLevelFilterType demandedFilterType, BazaarLevelFilterType itemFilterType)
{
return demandedFilterType == itemFilterType || demandedFilterType == BazaarLevelFilterType.All || demandedFilterType == BazaarLevelFilterType.ChampionGear && itemFilterType switch
{
BazaarLevelFilterType.ChampionLevelOneToTen => true,
BazaarLevelFilterType.ChampionLevelElevenToTwenty => true,
BazaarLevelFilterType.ChampionLevelTwentyOneToThirty => true,
BazaarLevelFilterType.ChampionLevelThirtyOneToForty => true,
BazaarLevelFilterType.ChampionLevelFortyOneToFifty => true,
BazaarLevelFilterType.ChampionLevelFiftyOneToSixty => true,
_ => false
};
}
private static BazaarUpgradeFilterType GetUpgradeFilterByInstance(ItemInstanceDTO itemInstanceDto)
{
if (15 < itemInstanceDto.Upgrade)
{
return BazaarUpgradeFilterType.All;
}
return (BazaarUpgradeFilterType)(itemInstanceDto.Upgrade + 1);
}
private static BazaarRarityFilterType GetRarityFilterByInstance(ItemInstanceDTO itemInstanceDto)
{
if (itemInstanceDto.Rarity is < 0 or > 8)
{
return BazaarRarityFilterType.All;
}
return (BazaarRarityFilterType)(itemInstanceDto.Rarity + 1);
}
private static BazaarPerfectionFilterType GetPerfectionFilterByInstance(ItemInstanceDTO itemInstanceDto)
{
const int max = 100;
return itemInstanceDto.SpStoneUpgrade switch
{
< 1 or < max => BazaarPerfectionFilterType.All,
max => BazaarPerfectionFilterType.NintyOneToHundred,
_ => (BazaarPerfectionFilterType)Convert.ToByte(Math.Ceiling(itemInstanceDto.SpStoneUpgrade / 10f))
};
}
private static BazaarCategoryPartnerSubFilterType GetPartnerSubCategoryOfAttack(ItemDTO item)
{
return item.PartnerClass switch
{
(byte)AttackType.Melee => BazaarCategoryPartnerSubFilterType.CloseAttack,
(byte)AttackType.Ranged => BazaarCategoryPartnerSubFilterType.RemoteAttack,
(byte)AttackType.Magical => BazaarCategoryPartnerSubFilterType.Magic,
_ => BazaarCategoryPartnerSubFilterType.All
};
}
private IReadOnlyCollection<int> GetItemVNumsByCategoryAndSubCategory(BazaarCategoryFilterType categoryFilterType, byte subTypeFilter)
{
if (categoryFilterType == BazaarCategoryFilterType.All || !_itemVnumByCategoryAndSubCategory.ContainsKey(categoryFilterType) ||
!_itemVnumByCategoryAndSubCategory[categoryFilterType].ContainsKey(subTypeFilter))
{
return null;
}
return _itemVnumByCategoryAndSubCategory[categoryFilterType][subTypeFilter];
}
}
}