mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-10 15:00:25 +00:00
[Bazaar] Improve Bazaar Search Performance (#4615)
* Create Alternate BazaarSearch Routine Establishes an alterative to the in memory bazaar search routine and instead uses a db query process. For large Bazaars (with 1000s of items) this is much faster. Testing with 30k items produced a search in ~1sec version 2.7sec for the in memory version. Default is false - Do not use this version. * Indexes for trader and items * Set query-based bazaar search the default * Update database_update_manifest.cpp --------- Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
+225
-286
@@ -6,7 +6,8 @@
|
||||
|
||||
std::vector<BazaarSearchResultsFromDB_Struct>
|
||||
Bazaar::GetSearchResults(
|
||||
SharedDatabase &db,
|
||||
Database &db,
|
||||
Database &content_db,
|
||||
BazaarSearchCriteria_Struct search,
|
||||
uint32 char_zone_id,
|
||||
int32 char_zone_instance_id
|
||||
@@ -31,8 +32,132 @@ Bazaar::GetSearchResults(
|
||||
char_zone_instance_id
|
||||
);
|
||||
|
||||
static std::map<uint8, uint32> item_slot_searches_new = {
|
||||
{EQ::invslot::slotCharm, 1},
|
||||
{EQ::invslot::slotEar1, 2},
|
||||
{EQ::invslot::slotHead, 4},
|
||||
{EQ::invslot::slotFace, 8},
|
||||
{EQ::invslot::slotEar2, 16},
|
||||
{EQ::invslot::slotNeck, 32},
|
||||
{EQ::invslot::slotShoulders, 64},
|
||||
{EQ::invslot::slotArms, 128},
|
||||
{EQ::invslot::slotBack, 256},
|
||||
{EQ::invslot::slotWrist1, 512},
|
||||
{EQ::invslot::slotWrist2, 1024},
|
||||
{EQ::invslot::slotRange, 2048},
|
||||
{EQ::invslot::slotHands, 4096},
|
||||
{EQ::invslot::slotPrimary, 8192},
|
||||
{EQ::invslot::slotSecondary, 16384},
|
||||
{EQ::invslot::slotFinger1, 32768},
|
||||
{EQ::invslot::slotFinger2, 65536},
|
||||
{EQ::invslot::slotChest, 131072},
|
||||
{EQ::invslot::slotLegs, 262144},
|
||||
{EQ::invslot::slotFeet, 524288},
|
||||
{EQ::invslot::slotWaist, 1048576},
|
||||
{EQ::invslot::slotPowerSource, 2097152},
|
||||
{EQ::invslot::slotAmmo, 4194304},
|
||||
};
|
||||
|
||||
struct ItemSearchType {
|
||||
EQ::item::ItemType type;
|
||||
std::string condition;
|
||||
};
|
||||
|
||||
std::vector<ItemSearchType> item_search_types_new = {
|
||||
{EQ::item::ItemType::ItemTypeBook, " AND (items.itemclass = 2 or items.itemclass = 31)"},
|
||||
{EQ::item::ItemType::ItemTypeContainer, " AND (items.itemclass = 1 or items.itemclass = 67)"},
|
||||
{EQ::item::ItemType::ItemTypeAllEffects, " AND (items.scrolleffect > 0 && items.scrolleffect < 65000)"},
|
||||
{EQ::item::ItemType::ItemTypeUnknown9, " AND items.worneffect = 998"},
|
||||
{EQ::item::ItemType::ItemTypeUnknown10, " AND (items.worneffect >= 1298 && items.worneffect <= 1307)"},
|
||||
{EQ::item::ItemType::ItemTypeFocusEffect, " AND items.focuseffect > 0"},
|
||||
{EQ::item::ItemType::ItemTypeArmor, " AND items.itemtype = 10"},
|
||||
{EQ::item::ItemType::ItemType1HBlunt, " AND items.itemtype = 3"},
|
||||
{EQ::item::ItemType::ItemType1HPiercing, " AND items.itemtype = 2"},
|
||||
{EQ::item::ItemType::ItemType1HSlash, " AND items.itemtype = 0"},
|
||||
{EQ::item::ItemType::ItemType2HBlunt, " AND items.itemtype = 4"},
|
||||
{EQ::item::ItemType::ItemType2HSlash, " AND items.itemtype = 1"},
|
||||
{EQ::item::ItemType::ItemTypeBow, " AND items.itemtype = 5"},
|
||||
{EQ::item::ItemType::ItemTypeShield, " AND items.itemtype = 8"},
|
||||
{EQ::item::ItemType::ItemTypeMisc, " AND items.itemtype = 11"},
|
||||
{EQ::item::ItemType::ItemTypeFood, " AND items.itemtype = 14"},
|
||||
{EQ::item::ItemType::ItemTypeDrink, " AND items.itemtype = 15"},
|
||||
{EQ::item::ItemType::ItemTypeLight, " AND items.itemtype = 16"},
|
||||
{EQ::item::ItemType::ItemTypeCombinable, " AND items.itemtype = 17"},
|
||||
{EQ::item::ItemType::ItemTypeBandage, " AND items.itemtype = 18"},
|
||||
{EQ::item::ItemType::ItemTypeSmallThrowing, " AND (items.itemtype = 19 OR items.itemtype = 7)"},
|
||||
{EQ::item::ItemType::ItemTypeSpell, " AND items.itemtype = 20"},
|
||||
{EQ::item::ItemType::ItemTypePotion, " AND items.itemtype = 21"},
|
||||
{EQ::item::ItemType::ItemTypeBrassInstrument, " AND items.itemtype = 25"},
|
||||
{EQ::item::ItemType::ItemTypeWindInstrument, " AND items.itemtype = 23"},
|
||||
{EQ::item::ItemType::ItemTypeStringedInstrument, " AND items.itemtype = 24"},
|
||||
{EQ::item::ItemType::ItemTypePercussionInstrument, " AND items.itemtype = 26"},
|
||||
{EQ::item::ItemType::ItemTypeArrow, " AND items.itemtype = 27"},
|
||||
{EQ::item::ItemType::ItemTypeJewelry, " AND items.itemtype = 29"},
|
||||
{EQ::item::ItemType::ItemTypeNote, " AND items.itemtype = 32"},
|
||||
{EQ::item::ItemType::ItemTypeKey, " AND items.itemtype = 33"},
|
||||
{EQ::item::ItemType::ItemType2HPiercing, " AND items.itemtype = 35"},
|
||||
{EQ::item::ItemType::ItemTypeAlcohol, " AND items.itemtype = 38"},
|
||||
{EQ::item::ItemType::ItemTypeMartial, " AND items.itemtype = 45"},
|
||||
{EQ::item::ItemType::ItemTypeAugmentation, " AND items.itemtype = 54"},
|
||||
{EQ::item::ItemType::ItemTypeAlternateAbility, " AND items.itemtype = 57"},
|
||||
{EQ::item::ItemType::ItemTypeCount, " AND items.itemtype = 65"},
|
||||
{EQ::item::ItemType::ItemTypeCollectible, " AND items.itemtype = 66"}
|
||||
};
|
||||
|
||||
// item stat searches
|
||||
struct ItemStatSearch {
|
||||
std::string query_string;
|
||||
EQ::skills::SkillType skill_type;
|
||||
};
|
||||
|
||||
std::map<uint32, ItemStatSearch> item_stat_searches_new = {
|
||||
{STAT_AC, {" items.ac" , static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_AGI, {" items.aagi", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_CHA, {" items.acha", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_DEX, {" items.adex", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_INT, {" items.aint", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_STA, {" items.asta", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_STR, {" items.astr", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_WIS, {" items.awis", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_COLD, {" items.cr", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_DISEASE, {" items.dr", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_FIRE, {" items.fr", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_MAGIC, {" items.mr", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_POISON, {" items.pr", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HP, {" items.hp", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_MANA, {" items.mana", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_ENDURANCE, {" items.endur", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_ATTACK, {" items.attack", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HP_REGEN, {" items.regen", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_MANA_REGEN, {" items.manaregen", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HASTE, {" items.haste", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_DAMAGE_SHIELD, {" items.damageshield", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_DS_MITIGATION, {" items.dsmitigation", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HEAL_AMOUNT, {" items.healamt", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_SPELL_DAMAGE, {" items.spelldmg", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_CLAIRVOYANCE, {" items.clairvoyance", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HEROIC_AGILITY, {" items.heroic_agi", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HEROIC_CHARISMA, {" items.heroic_cha", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HEROIC_DEXTERITY, {" items.heroic_dex", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HEROIC_INTELLIGENCE, {" items.heroic_int", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HEROIC_STAMINA, {" items.heroic_sta", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HEROIC_STRENGTH, {" items.heroic_str", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_HEROIC_WISDOM, {" items.heroic_wis", static_cast<EQ::skills::SkillType>(0)} },
|
||||
{STAT_BASH, {" items.skillmodvalue", EQ::skills::SkillBash} },
|
||||
{STAT_BACKSTAB, {" items.backstabdmg", EQ::skills::SkillBackstab} },
|
||||
{STAT_DRAGON_PUNCH, {" items.skillmodvalue", EQ::skills::SkillDragonPunch} },
|
||||
{STAT_EAGLE_STRIKE, {" items.skillmodvalue", EQ::skills::SkillEagleStrike} },
|
||||
{STAT_FLYING_KICK, {" items.skillmodvalue", EQ::skills::SkillFlyingKick} },
|
||||
{STAT_KICK, {" items.skillmodvalue", EQ::skills::SkillKick} },
|
||||
{STAT_ROUND_KICK, {" items.skillmodvalue", EQ::skills::SkillRoundKick} },
|
||||
{STAT_TIGER_CLAW, {" items.skillmodvalue", EQ::skills::SkillTigerClaw} },
|
||||
{STAT_FRENZY, {" items.skillmodvalue", EQ::skills::SkillFrenzy} },
|
||||
};
|
||||
|
||||
bool convert = false;
|
||||
std::string search_criteria_trader("TRUE ");
|
||||
std::string search_criteria_trader("TRUE");
|
||||
std::string field_criteria_items("FALSE");
|
||||
std::string where_criteria_items(" TRUE ");
|
||||
|
||||
if (search.search_scope == NonRoFBazaarSearchScope) {
|
||||
search_criteria_trader.append(
|
||||
@@ -77,301 +202,115 @@ Bazaar::GetSearchResults(
|
||||
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
|
||||
}
|
||||
|
||||
// not yet implemented
|
||||
// if (search.prestige != 0) {
|
||||
// 0xffffffff prestige only, 0xfffffffe non-prestige, 0 all
|
||||
// search_criteria.append(fmt::format(" AND items.type = {} ", search.prestige));
|
||||
// }
|
||||
if (search.slot != std::numeric_limits<uint32>::max()) {
|
||||
if (item_slot_searches_new.contains(search.slot)) {
|
||||
where_criteria_items.append(
|
||||
fmt::format(" AND items.slots & {0} = {0}", item_slot_searches_new[search.slot]));
|
||||
}
|
||||
}
|
||||
|
||||
std::string query = fmt::format(
|
||||
"SELECT COUNT(item_id), trader.char_id, trader.item_id, trader.item_sn, trader.item_charges, trader.item_cost, "
|
||||
"trader.slot_id, SUM(trader.item_charges), trader.char_zone_id, trader.char_entity_id, character_data.name, "
|
||||
"aug_slot_1, aug_slot_2, aug_slot_3, aug_slot_4, aug_slot_5, aug_slot_6, trader.char_zone_instance_id "
|
||||
"FROM trader, character_data "
|
||||
"WHERE {} AND trader.char_id = character_data.id "
|
||||
"GROUP BY trader.item_sn, trader.item_charges, trader.char_id",
|
||||
search_criteria_trader.c_str()
|
||||
);
|
||||
if (search.type != std::numeric_limits<uint32>::max()) {
|
||||
for (auto const &[type, condition]: item_search_types_new) {
|
||||
if (type == search.type) {
|
||||
where_criteria_items.append(condition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (search.race != std::numeric_limits<uint32>::max()) {
|
||||
where_criteria_items.append(
|
||||
fmt::format(" AND items.races & {0} = {0}", GetPlayerRaceBit(GetRaceIDFromPlayerRaceValue(search.race))));
|
||||
}
|
||||
|
||||
if (search._class != std::numeric_limits<uint32>::max()) {
|
||||
where_criteria_items.append(fmt::format(" AND items.classes & {0} = {0}", GetPlayerClassBit(search._class)));
|
||||
}
|
||||
|
||||
if (search.item_stat != std::numeric_limits<uint32>::max()) {
|
||||
if (item_stat_searches_new.contains(search.item_stat)) {
|
||||
field_criteria_items = fmt::format("{}", item_stat_searches_new[search.item_stat].query_string);
|
||||
if (item_stat_searches_new[search.item_stat].skill_type) {
|
||||
where_criteria_items.append(
|
||||
fmt::format(" AND items.skillmodtype = {} ", item_stat_searches_new[search.item_stat].skill_type));
|
||||
}
|
||||
else {
|
||||
where_criteria_items.append(
|
||||
fmt::format(" AND {} > 0 ", item_stat_searches_new[search.item_stat].query_string));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (search.augment) {
|
||||
where_criteria_items.append(fmt::format(
|
||||
" AND (items.augslot1type = {0} OR "
|
||||
"items.augslot2type = {0} OR "
|
||||
"items.augslot3type = {0} OR "
|
||||
"items.augslot4type = {0} OR "
|
||||
"items.augslot5type = {0} OR "
|
||||
"items.augslot6type = {0})",
|
||||
search.augment)
|
||||
);
|
||||
}
|
||||
|
||||
if (search.min_level != 1) {
|
||||
where_criteria_items.append(fmt::format(" AND items.reclevel >= {}", search.min_level));
|
||||
}
|
||||
|
||||
if (search.max_level != 100) {
|
||||
where_criteria_items.append(fmt::format(" AND items.reclevel <= {}", search.max_level));
|
||||
}
|
||||
|
||||
std::vector<BazaarSearchResultsFromDB_Struct> all_entries;
|
||||
std::vector<std::string> trader_items_ids{};
|
||||
|
||||
auto results = db.QueryDatabase(query);
|
||||
|
||||
if (!results.Success()) {
|
||||
auto const trader_results = TraderRepository::GetBazaarTraderDetails(db, search_criteria_trader);
|
||||
if (trader_results.empty()) {
|
||||
LogTradingDetail("Bazaar - No traders found in bazaar search.");
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
struct ItemSearchType {
|
||||
EQ::item::ItemType type;
|
||||
bool condition;
|
||||
};
|
||||
for (auto const &i: trader_results) {
|
||||
trader_items_ids.push_back(std::to_string(i.trader.item_id));
|
||||
}
|
||||
|
||||
struct AddititiveSearchCriteria {
|
||||
bool should_check;
|
||||
bool condition;
|
||||
};
|
||||
auto const item_results = ItemsRepository::GetItemsForBazaarSearch(
|
||||
content_db,
|
||||
trader_items_ids,
|
||||
std::string(search.item_name),
|
||||
field_criteria_items,
|
||||
where_criteria_items
|
||||
);
|
||||
|
||||
if (item_results.empty()) {
|
||||
LogError("Bazaar - No items found in bazaar search.");
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
all_entries.reserve(trader_results.size());
|
||||
|
||||
for (auto const& t:trader_results) {
|
||||
if (!item_results.contains(t.trader.item_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto row: results) {
|
||||
BazaarSearchResultsFromDB_Struct r{};
|
||||
r.count = 1;
|
||||
r.trader_id = t.trader.char_id;
|
||||
r.serial_number = t.trader.item_sn;
|
||||
r.cost = t.trader.item_cost;
|
||||
r.slot_id = t.trader.slot_id;
|
||||
r.sum_charges = t.trader.item_charges;
|
||||
r.stackable = item_results.at(t.trader.item_id).stackable;
|
||||
r.icon_id = item_results.at(t.trader.item_id).icon;
|
||||
r.trader_zone_id = t.trader.char_zone_id;
|
||||
r.trader_zone_instance_id = t.trader.char_zone_instance_id;
|
||||
r.trader_entity_id = t.trader.char_entity_id;
|
||||
r.serial_number_RoF = fmt::format("{:016}\0", t.trader.item_sn);
|
||||
r.item_name = fmt::format("{:.63}\0", item_results.at(t.trader.item_id).name);
|
||||
r.trader_name = fmt::format("{:.63}\0", t.trader_name);
|
||||
r.item_stat = item_results.at(t.trader.item_id).stats;
|
||||
|
||||
r.item_id = Strings::ToInt(row[2]);
|
||||
r.charges = Strings::ToInt(row[4]);
|
||||
|
||||
auto item = db.GetItem(r.item_id);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 aug_slot_1 = Strings::ToUnsignedInt(row[11]);
|
||||
uint32 aug_slot_2 = Strings::ToUnsignedInt(row[12]);
|
||||
uint32 aug_slot_3 = Strings::ToUnsignedInt(row[13]);
|
||||
uint32 aug_slot_4 = Strings::ToUnsignedInt(row[14]);
|
||||
uint32 aug_slot_5 = Strings::ToUnsignedInt(row[15]);
|
||||
uint32 aug_slot_6 = Strings::ToUnsignedInt(row[16]);
|
||||
|
||||
std::unique_ptr<EQ::ItemInstance> inst(
|
||||
db.CreateItem(
|
||||
item,
|
||||
r.charges,
|
||||
aug_slot_1,
|
||||
aug_slot_2,
|
||||
aug_slot_3,
|
||||
aug_slot_4,
|
||||
aug_slot_5,
|
||||
aug_slot_6
|
||||
)
|
||||
);
|
||||
|
||||
if (!inst->GetItem()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r.count = Strings::ToInt(row[0]);
|
||||
r.trader_id = Strings::ToInt(row[1]);
|
||||
r.serial_number = Strings::ToInt(row[3]);
|
||||
r.cost = Strings::ToInt(row[5]);
|
||||
r.slot_id = Strings::ToInt(row[6]);
|
||||
r.sum_charges = Strings::ToInt(row[7]);
|
||||
r.stackable = item->Stackable;
|
||||
r.icon_id = item->Icon;
|
||||
r.trader_zone_id = Strings::ToInt(row[8]);
|
||||
r.trader_zone_instance_id = Strings::ToInt(row[17]);
|
||||
r.trader_entity_id = Strings::ToInt(row[9]);
|
||||
r.serial_number_RoF = fmt::format("{:016}\0", Strings::ToInt(row[3]));
|
||||
r.item_name = fmt::format("{:.63}\0", item->Name);
|
||||
r.trader_name = fmt::format("{:.63}\0", std::string(row[10]).c_str());
|
||||
|
||||
LogTradingDetail(
|
||||
"Searching against item [{}] ({}) for trader [{}]",
|
||||
item->Name,
|
||||
item->ID,
|
||||
r.trader_name
|
||||
);
|
||||
|
||||
// item stat searches
|
||||
std::map<uint32, uint32> item_stat_searches = {
|
||||
|
||||
{STAT_AC, inst->GetItemArmorClass(true)},
|
||||
{STAT_AGI, static_cast<uint32>(inst->GetItemAgi(true))},
|
||||
{STAT_CHA, static_cast<uint32>(inst->GetItemCha(true))},
|
||||
{STAT_DEX, static_cast<uint32>(inst->GetItemDex(true))},
|
||||
{STAT_INT, static_cast<uint32>(inst->GetItemInt(true))},
|
||||
{STAT_STA, static_cast<uint32>(inst->GetItemSta(true))},
|
||||
{STAT_STR, static_cast<uint32>(inst->GetItemStr(true))},
|
||||
{STAT_WIS, static_cast<uint32>(inst->GetItemWis(true))},
|
||||
{STAT_COLD, static_cast<uint32>(inst->GetItemCR(true))},
|
||||
{STAT_DISEASE, static_cast<uint32>(inst->GetItemDR(true))},
|
||||
{STAT_FIRE, static_cast<uint32>(inst->GetItemFR(true))},
|
||||
{STAT_MAGIC, static_cast<uint32>(inst->GetItemMR(true))},
|
||||
{STAT_POISON, static_cast<uint32>(inst->GetItemPR(true))},
|
||||
{STAT_HP, static_cast<uint32>(inst->GetItemHP(true))},
|
||||
{STAT_MANA, static_cast<uint32>(inst->GetItemMana(true))},
|
||||
{STAT_ENDURANCE, static_cast<uint32>(inst->GetItemEndur(true))},
|
||||
{STAT_ATTACK, static_cast<uint32>(inst->GetItemAttack(true))},
|
||||
{STAT_HP_REGEN, static_cast<uint32>(inst->GetItemRegen(true))},
|
||||
{STAT_MANA_REGEN, static_cast<uint32>(inst->GetItemManaRegen(true))},
|
||||
{STAT_HASTE, static_cast<uint32>(inst->GetItemHaste(true))},
|
||||
{STAT_DAMAGE_SHIELD, static_cast<uint32>(inst->GetItemDamageShield(true))},
|
||||
{STAT_DS_MITIGATION, static_cast<uint32>(inst->GetItemDSMitigation(true))},
|
||||
{STAT_HEAL_AMOUNT, static_cast<uint32>(inst->GetItemHealAmt(true))},
|
||||
{STAT_SPELL_DAMAGE, static_cast<uint32>(inst->GetItemSpellDamage(true))},
|
||||
{STAT_CLAIRVOYANCE, static_cast<uint32>(inst->GetItemClairvoyance(true))},
|
||||
{STAT_HEROIC_AGILITY, static_cast<uint32>(inst->GetItemHeroicAgi(true))},
|
||||
{STAT_HEROIC_CHARISMA, static_cast<uint32>(inst->GetItemHeroicCha(true))},
|
||||
{STAT_HEROIC_DEXTERITY, static_cast<uint32>(inst->GetItemHeroicDex(true))},
|
||||
{STAT_HEROIC_INTELLIGENCE, static_cast<uint32>(inst->GetItemHeroicInt(true))},
|
||||
{STAT_HEROIC_STAMINA, static_cast<uint32>(inst->GetItemHeroicSta(true))},
|
||||
{STAT_HEROIC_STRENGTH, static_cast<uint32>(inst->GetItemHeroicStr(true))},
|
||||
{STAT_HEROIC_WISDOM, static_cast<uint32>(inst->GetItemHeroicWis(true))},
|
||||
{STAT_BASH, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillBash, true))},
|
||||
{STAT_BACKSTAB, static_cast<uint32>(inst->GetItemBackstabDamage(true))},
|
||||
{STAT_DRAGON_PUNCH, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillDragonPunch, true))},
|
||||
{STAT_EAGLE_STRIKE, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillEagleStrike, true))},
|
||||
{STAT_FLYING_KICK, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillFlyingKick, true))},
|
||||
{STAT_KICK, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillKick, true))},
|
||||
{STAT_ROUND_KICK, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillRoundKick, true))},
|
||||
{STAT_TIGER_CLAW, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillTigerClaw, true))},
|
||||
{STAT_FRENZY, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillFrenzy, true))},
|
||||
};
|
||||
|
||||
r.item_stat = item_stat_searches.contains(search.item_stat) ? item_stat_searches[search.item_stat] : 0;
|
||||
if (item_stat_searches.contains(search.item_stat) && item_stat_searches[search.item_stat] <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
static std::map<uint8, uint32> item_slot_searches = {
|
||||
{EQ::invslot::slotCharm, 1},
|
||||
{EQ::invslot::slotEar1, 2},
|
||||
{EQ::invslot::slotHead, 4},
|
||||
{EQ::invslot::slotFace, 8},
|
||||
{EQ::invslot::slotEar2, 16},
|
||||
{EQ::invslot::slotNeck, 32},
|
||||
{EQ::invslot::slotShoulders, 64},
|
||||
{EQ::invslot::slotArms, 128},
|
||||
{EQ::invslot::slotBack, 256},
|
||||
{EQ::invslot::slotWrist1, 512},
|
||||
{EQ::invslot::slotWrist2, 1024},
|
||||
{EQ::invslot::slotRange, 2048},
|
||||
{EQ::invslot::slotHands, 4096},
|
||||
{EQ::invslot::slotPrimary, 8192},
|
||||
{EQ::invslot::slotSecondary, 16384},
|
||||
{EQ::invslot::slotFinger1, 32768},
|
||||
{EQ::invslot::slotFinger2, 65536},
|
||||
{EQ::invslot::slotChest, 131072},
|
||||
{EQ::invslot::slotLegs, 262144},
|
||||
{EQ::invslot::slotFeet, 524288},
|
||||
{EQ::invslot::slotWaist, 1048576},
|
||||
{EQ::invslot::slotPowerSource, 2097152},
|
||||
{EQ::invslot::slotAmmo, 4194304},
|
||||
};
|
||||
|
||||
auto GetEquipmentSlotBit = [&](uint32 slot) -> uint32 {
|
||||
return item_slot_searches.contains(slot) ? item_slot_searches[slot] : 0;
|
||||
};
|
||||
|
||||
auto FindItemAugSlot = [&]() -> bool {
|
||||
for (auto const &s: inst->GetItem()->AugSlotType) {
|
||||
return s == search.augment;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// item type searches
|
||||
std::vector<ItemSearchType> item_search_types = {
|
||||
{EQ::item::ItemType::ItemTypeAll, true},
|
||||
{EQ::item::ItemType::ItemTypeBook, item->ItemClass == EQ::item::ItemType::ItemTypeBook},
|
||||
{EQ::item::ItemType::ItemTypeContainer, item->ItemClass == EQ::item::ItemType::ItemTypeContainer ||
|
||||
item->IsClassBag()},
|
||||
{EQ::item::ItemType::ItemTypeAllEffects, item->Scroll.Effect > 0 && item->Scroll.Effect < 65000},
|
||||
{EQ::item::ItemType::ItemTypeUnknown9, item->Worn.Effect == 998},
|
||||
{EQ::item::ItemType::ItemTypeUnknown10, item->Worn.Effect >= 1298 && item->Worn.Effect <= 1307},
|
||||
{EQ::item::ItemType::ItemTypeFocusEffect, item->Focus.Effect > 0},
|
||||
{EQ::item::ItemType::ItemTypeArmor, item->ItemType == EQ::item::ItemType::ItemTypeArmor},
|
||||
{EQ::item::ItemType::ItemType1HBlunt, item->ItemType == EQ::item::ItemType::ItemType1HBlunt},
|
||||
{EQ::item::ItemType::ItemType1HPiercing, item->ItemType == EQ::item::ItemType::ItemType1HPiercing},
|
||||
{EQ::item::ItemType::ItemType1HSlash, item->ItemType == EQ::item::ItemType::ItemType1HSlash},
|
||||
{EQ::item::ItemType::ItemType2HBlunt, item->ItemType == EQ::item::ItemType::ItemType2HBlunt},
|
||||
{EQ::item::ItemType::ItemType2HSlash, item->ItemType == EQ::item::ItemType::ItemType2HSlash},
|
||||
{EQ::item::ItemType::ItemTypeBow, item->ItemType == EQ::item::ItemType::ItemTypeBow},
|
||||
{EQ::item::ItemType::ItemTypeShield, item->ItemType == EQ::item::ItemType::ItemTypeShield},
|
||||
{EQ::item::ItemType::ItemTypeMisc, item->ItemType == EQ::item::ItemType::ItemTypeMisc},
|
||||
{EQ::item::ItemType::ItemTypeFood, item->ItemType == EQ::item::ItemType::ItemTypeFood},
|
||||
{EQ::item::ItemType::ItemTypeDrink, item->ItemType == EQ::item::ItemType::ItemTypeDrink},
|
||||
{EQ::item::ItemType::ItemTypeLight, item->ItemType == EQ::item::ItemType::ItemTypeLight},
|
||||
{EQ::item::ItemType::ItemTypeCombinable, item->ItemType == EQ::item::ItemType::ItemTypeCombinable},
|
||||
{EQ::item::ItemType::ItemTypeBandage, item->ItemType == EQ::item::ItemType::ItemTypeBandage},
|
||||
{EQ::item::ItemType::ItemTypeSmallThrowing, item->ItemType == EQ::item::ItemType::ItemTypeSmallThrowing ||
|
||||
item->ItemType == EQ::item::ItemType::ItemTypeLargeThrowing},
|
||||
{EQ::item::ItemType::ItemTypeSpell, item->ItemType == EQ::item::ItemType::ItemTypeSpell},
|
||||
{EQ::item::ItemType::ItemTypePotion, item->ItemType == EQ::item::ItemType::ItemTypePotion},
|
||||
{EQ::item::ItemType::ItemTypeBrassInstrument, item->ItemType == EQ::item::ItemType::ItemTypeBrassInstrument},
|
||||
{EQ::item::ItemType::ItemTypeWindInstrument, item->ItemType == EQ::item::ItemType::ItemTypeWindInstrument},
|
||||
{EQ::item::ItemType::ItemTypeStringedInstrument, item->ItemType == EQ::item::ItemType::ItemTypeStringedInstrument},
|
||||
{EQ::item::ItemType::ItemTypePercussionInstrument, item->ItemType == EQ::item::ItemType::ItemTypePercussionInstrument},
|
||||
{EQ::item::ItemType::ItemTypeArrow, item->ItemType == EQ::item::ItemType::ItemTypeArrow},
|
||||
{EQ::item::ItemType::ItemTypeJewelry, item->ItemType == EQ::item::ItemType::ItemTypeJewelry},
|
||||
{EQ::item::ItemType::ItemTypeNote, item->ItemType == EQ::item::ItemType::ItemTypeNote},
|
||||
{EQ::item::ItemType::ItemTypeKey, item->ItemType == EQ::item::ItemType::ItemTypeKey},
|
||||
{EQ::item::ItemType::ItemType2HPiercing, item->ItemType == EQ::item::ItemType::ItemType2HPiercing},
|
||||
{EQ::item::ItemType::ItemTypeAlcohol, item->ItemType == EQ::item::ItemType::ItemTypeAlcohol},
|
||||
{EQ::item::ItemType::ItemTypeMartial, item->ItemType == EQ::item::ItemType::ItemTypeMartial},
|
||||
{EQ::item::ItemType::ItemTypeAugmentation, item->ItemType == EQ::item::ItemType::ItemTypeAugmentation},
|
||||
{EQ::item::ItemType::ItemTypeAlternateAbility, item->ItemType == EQ::item::ItemType::ItemTypeAlternateAbility},
|
||||
{EQ::item::ItemType::ItemTypeCount, item->ItemType == EQ::item::ItemType::ItemTypeCount},
|
||||
{EQ::item::ItemType::ItemTypeCollectible, item->ItemType == EQ::item::ItemType::ItemTypeCollectible}
|
||||
};
|
||||
|
||||
bool met_filter = false;
|
||||
bool has_filter = false;
|
||||
|
||||
for (auto &i: item_search_types) {
|
||||
if (i.type == search.type) {
|
||||
has_filter = true;
|
||||
if (i.condition) {
|
||||
LogTradingDetail("Item [{}] met search criteria for type [{}]", item->Name, uint8(i.type));
|
||||
met_filter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (has_filter && !met_filter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Add catch-all item type filter for specific item types
|
||||
|
||||
// item additive searches
|
||||
std::vector<AddititiveSearchCriteria> item_additive_searches = {
|
||||
{
|
||||
.should_check = search.min_level != 1 && inst->GetItemRequiredLevel(true) > 0,
|
||||
.condition = inst->GetItemRequiredLevel(true) >= search.min_level
|
||||
},
|
||||
{
|
||||
.should_check = search.max_level != 1 && inst->GetItemRequiredLevel(true) > 0,
|
||||
.condition = inst->GetItemRequiredLevel(true) <= search.max_level
|
||||
},
|
||||
{
|
||||
.should_check = !std::string(search.item_name).empty(),
|
||||
.condition = Strings::ContainsLower(item->Name, search.item_name)
|
||||
},
|
||||
{
|
||||
.should_check = search._class != 0xFFFFFFFF,
|
||||
.condition = static_cast<bool>(item->Classes & GetPlayerClassBit(search._class))
|
||||
},
|
||||
{
|
||||
.should_check = search.race != 0xFFFFFFFF,
|
||||
.condition = static_cast<bool>(item->Races & GetPlayerRaceBit(GetRaceIDFromPlayerRaceValue(search.race)))
|
||||
},
|
||||
{
|
||||
.should_check = search.augment != 0,
|
||||
.condition = FindItemAugSlot()
|
||||
},
|
||||
{
|
||||
.should_check = search.slot != 0xFFFFFFFF,
|
||||
.condition = static_cast<bool>(item->Slots & GetEquipmentSlotBit(search.slot))
|
||||
},
|
||||
};
|
||||
|
||||
bool should_add = true;
|
||||
|
||||
for (auto &i: item_additive_searches) {
|
||||
LogTradingDetail(
|
||||
"Checking item [{}] for search criteria - should_check [{}] condition [{}]",
|
||||
item->Name,
|
||||
i.should_check,
|
||||
i.condition
|
||||
);
|
||||
if (i.should_check && !i.condition) {
|
||||
should_add = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!should_add) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LogTradingDetail("Found item [{}] meeting search criteria.", r.item_name);
|
||||
if (RuleB(Bazaar, UseAlternateBazaarSearch)) {
|
||||
if (convert || (r.trader_zone_id == Zones::BAZAAR && r.trader_zone_instance_id != char_zone_instance_id)) {
|
||||
r.trader_id = TraderRepository::TRADER_CONVERT_ID + r.trader_zone_instance_id;
|
||||
|
||||
Reference in New Issue
Block a user