2023-11-07 18:24:46 -05:00

286 lines
14 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
*/
#include "ReagentBank.h"
// Add player scripts
class npc_reagent_banker : public CreatureScript
{
private:
std::string GetItemLink(uint32 entry, WorldSession* session) const
{
int loc_idx = session->GetSessionDbLocaleIndex();
const ItemTemplate *temp = sObjectMgr->GetItemTemplate(entry);
std::string name = temp->Name1;
if (ItemLocale const* il = sObjectMgr->GetItemLocale(temp->ItemId))
ObjectMgr::GetLocaleString(il->Name, loc_idx, name);
std::ostringstream oss;
oss << "|c" << std::hex << ItemQualityColors[temp->Quality] << std::dec <<
"|Hitem:" << temp->ItemId << ":" <<
(uint32)0 << "|h[" << name << "]|h|r";
return oss.str();
}
std::string GetItemIcon(uint32 entry, uint32 width, uint32 height, int x, int y) const
{
std::ostringstream ss;
ss << "|TInterface";
const ItemTemplate *temp = sObjectMgr->GetItemTemplate(entry);
const ItemDisplayInfoEntry *dispInfo = NULL;
if (temp)
{
dispInfo = sItemDisplayInfoStore.LookupEntry(temp->DisplayInfoID);
if (dispInfo)
ss << "/ICONS/" << dispInfo->inventoryIcon;
}
if (!dispInfo)
ss << "/InventoryItems/WoWUnknownItem01";
ss << ":" << width << ":" << height << ":" << x << ":" << y << "|t";
return ss.str();
}
void WithdrawItem(Player* player, uint32 entry)
{
// This query can be changed to async to improve performance, but there will be some visual bugs because the query will not be done executing when the menu refreshes
std::string query = "SELECT amount FROM custom_reagent_bank WHERE character_id = " + std::to_string(player->GetGUID().GetCounter()) + " AND item_entry = " + std::to_string(entry);
QueryResult result = CharacterDatabase.Query("SELECT amount FROM custom_reagent_bank WHERE character_id = " + std::to_string(player->GetGUID().GetCounter()) + " AND item_entry = " + std::to_string(entry));
if (result)
{
uint32 storedAmount = (*result)[0].Get<uint32>();
const ItemTemplate *temp = sObjectMgr->GetItemTemplate(entry);
uint32 stackSize = temp->GetMaxStackSize();
if (storedAmount <= stackSize)
{
// Give the player all of the item and remove it from the DB
ItemPosCountVec dest;
InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, entry, storedAmount);
if (msg == EQUIP_ERR_OK)
{
CharacterDatabase.Execute("DELETE FROM custom_reagent_bank WHERE character_id = {} AND item_entry = {}", player->GetGUID().GetCounter(), entry);
Item* item = player->StoreNewItem(dest, entry, true);
player->SendNewItem(item, storedAmount, true, false);
}
else
{
player->SendEquipError(msg, nullptr, nullptr, entry);
return;
}
}
else
{
// Give the player a single stack
ItemPosCountVec dest;
InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, entry, stackSize);
if (msg == EQUIP_ERR_OK)
{
CharacterDatabase.Execute("UPDATE custom_reagent_bank SET amount = {} WHERE character_id = {} AND item_entry = {}", storedAmount - stackSize, player->GetGUID().GetCounter(), entry);
Item* item = player->StoreNewItem(dest, entry, true);
player->SendNewItem(item, stackSize, true, false);
}
else
{
player->SendEquipError(msg, nullptr, nullptr, entry);
return;
}
}
}
}
void UpdateItemCount(std::map<uint32, uint32> &entryToAmountMap, std::map<uint32, uint32> &entryToSubclassMap, Item* pItem, Player* player, uint32 bagSlot, uint32 itemSlot)
{
uint32 count = pItem->GetCount();
ItemTemplate const *itemTemplate = pItem->GetTemplate();
if (!(itemTemplate->Class == ITEM_CLASS_TRADE_GOODS || itemTemplate->Class == ITEM_CLASS_GEM) || itemTemplate->GetMaxStackSize() == 1)
return;
uint32 itemEntry = itemTemplate->ItemId;
uint32 itemSubclass = itemTemplate->SubClass;
// Put gems to ITEM_SUBCLASS_JEWELCRAFTING section
if (itemTemplate->Class == ITEM_CLASS_GEM)
{
itemSubclass = ITEM_SUBCLASS_JEWELCRAFTING;
}
if (!entryToAmountMap.count(itemEntry))
{
// Item does not exist yet in storage
entryToAmountMap[itemEntry] = count;
entryToSubclassMap[itemEntry] = itemSubclass;
}
else
{
entryToAmountMap[itemEntry] = entryToAmountMap.find(itemEntry)->second + count;
}
// The item counts have been updated, remove the original items from the player
player->DestroyItem(bagSlot, itemSlot, true);
}
void DepositAllReagents(Player* player) {
WorldSession *session = player->GetSession();
std::string query = "SELECT item_entry, item_subclass, amount FROM custom_reagent_bank WHERE character_id = " + std::to_string(player->GetGUID().GetCounter());
session->GetQueryProcessor().AddCallback( CharacterDatabase.AsyncQuery(query).WithCallback([=, this](QueryResult result) {
std::map<uint32, uint32> entryToAmountMap;
std::map<uint32, uint32> entryToSubclassMap;
if (result)
{
do {
uint32 itemEntry = (*result)[0].Get<uint32>();
uint32 itemSubclass = (*result)[1].Get<uint32>();
uint32 itemAmount = (*result)[2].Get<uint32>();
entryToAmountMap[itemEntry] = itemAmount;
entryToSubclassMap[itemEntry] = itemSubclass;
} while (result->NextRow());
}
// Inventory Items
for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
{
if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
{
UpdateItemCount(entryToAmountMap, entryToSubclassMap, pItem, player, INVENTORY_SLOT_BAG_0, i);
}
}
// Bag Items
for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
{
Bag* bag = player->GetBagByPos(i);
if (!bag)
continue;
for (uint32 j = 0; j < bag->GetBagSize(); j++) {
if (Item * pItem = player->GetItemByPos(i, j))
{
UpdateItemCount(entryToAmountMap, entryToSubclassMap, pItem, player, i, j);
}
}
}
if (entryToAmountMap.size() != 0)
{
auto trans = CharacterDatabase.BeginTransaction();
for (std::pair<uint32, uint32> mapEntry : entryToAmountMap)
{
uint32 itemEntry = mapEntry.first;
uint32 itemAmount = mapEntry.second;
uint32 itemSubclass = entryToSubclassMap.find(itemEntry)->second;
trans->Append("REPLACE INTO custom_reagent_bank (character_id, item_entry, item_subclass, amount) VALUES ({}, {}, {}, {})", player->GetGUID().GetCounter(), itemEntry, itemSubclass, itemAmount);
}
CharacterDatabase.CommitTransaction(trans);
}
}));
ChatHandler(player->GetSession()).PSendSysMessage("All reagents deposited successfully.");
CloseGossipMenuFor(player);
}
public:
npc_reagent_banker() : CreatureScript("npc_reagent_banker") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(4359, 30, 30, -18, 0) + "Parts", ITEM_SUBCLASS_PARTS, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(4358, 30, 30, -18, 0) + "Explosives", ITEM_SUBCLASS_EXPLOSIVES, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(4388, 30, 30, -18, 0) + "Devices", ITEM_SUBCLASS_DEVICES, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(1206, 30, 30, -18, 0) + "Jewelcrafting", ITEM_SUBCLASS_JEWELCRAFTING, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(2589, 30, 30, -18, 0) + "Cloth", ITEM_SUBCLASS_CLOTH, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(2318, 30, 30, -18, 0) + "Leather", ITEM_SUBCLASS_LEATHER, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(2772, 30, 30, -18, 0) + "Metal & Stone", ITEM_SUBCLASS_METAL_STONE, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(12208, 30, 30, -18, 0) + "Meat", ITEM_SUBCLASS_MEAT, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(2453, 30, 30, -18, 0) + "Herb", ITEM_SUBCLASS_HERB, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(7068, 30, 30, -18, 0) + "Elemental", ITEM_SUBCLASS_ELEMENTAL, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(10940, 30, 30, -18, 0) + "Enchanting", ITEM_SUBCLASS_ENCHANTING, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(23572, 30, 30, -18, 0) + "Nether Material", ITEM_SUBCLASS_MATERIAL, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(2604, 30, 30, -18, 0) + "Other Trade Goods", ITEM_SUBCLASS_TRADE_GOODS_OTHER, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(38682, 30, 30, -18, 0) + "Armor Vellum", ITEM_SUBCLASS_ARMOR_ENCHANTMENT, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(39349, 30, 30, -18, 0) + "Weapon Vellum", ITEM_SUBCLASS_WEAPON_ENCHANTMENT, 0);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "Deposit All Reagents", DEPOSIT_ALL_REAGENTS, 0);
SendGossipMenuFor(player, NPC_TEXT_ID, creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 item_subclass, uint32 gossipPageNumber) override
{
player->PlayerTalkClass->ClearMenus();
if (item_subclass > MAX_PAGE_NUMBER)
{
// item_subclass is actually an item ID to withdraw
// Get the actual item subclass from the template
const ItemTemplate *temp = sObjectMgr->GetItemTemplate(item_subclass);
WithdrawItem(player, item_subclass);
if (temp->Class == ITEM_CLASS_GEM)
{
// Get back to ITEM_SUBCLASS_JEWELCRAFTING section when withdrawing gems
ShowReagentItems(player, creature, ITEM_SUBCLASS_JEWELCRAFTING, gossipPageNumber);
}
else
{
ShowReagentItems(player, creature, temp->SubClass, gossipPageNumber);
}
return true;
}
if (item_subclass == DEPOSIT_ALL_REAGENTS)
{
DepositAllReagents(player);
return true;
}
else if (item_subclass == MAIN_MENU)
{
OnGossipHello(player, creature);
return true;
}
else
{
ShowReagentItems(player, creature, item_subclass, gossipPageNumber);
return true;
}
}
void ShowReagentItems(Player* player, Creature* creature, uint32 item_subclass, uint16 gossipPageNumber)
{
WorldSession* session = player->GetSession();
std::string query = "SELECT item_entry, amount FROM custom_reagent_bank WHERE character_id = " + std::to_string(player->GetGUID().GetCounter()) + " AND item_subclass = " +
std::to_string(item_subclass) + " ORDER BY item_entry";
session->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(query).WithCallback([=, this](QueryResult result)
{
uint32 startValue = (gossipPageNumber * (MAX_OPTIONS));
uint32 endValue = (gossipPageNumber + 1) * (MAX_OPTIONS) - 1;
std::map<uint32, uint32> entryToAmountMap;
std::vector<uint32> itemEntries;
if (result) {
do {
uint32 itemEntry = (*result)[0].Get<uint32>();
uint32 itemAmount = (*result)[1].Get<uint32>();
entryToAmountMap[itemEntry] = itemAmount;
itemEntries.push_back(itemEntry);
} while (result->NextRow());
}
for (uint32 i = startValue; i <= endValue; i++)
{
if (itemEntries.empty() || i > itemEntries.size() - 1)
{
break;
}
uint32 itemEntry = itemEntries.at(i);
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, GetItemIcon(itemEntry, 30, 30, -18, 0) + GetItemLink(itemEntry, session) + " (" + std::to_string(entryToAmountMap.find(itemEntry)->second) + ")", itemEntry, gossipPageNumber);
}
if (gossipPageNumber > 0)
{
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Previous Page", item_subclass, gossipPageNumber - 1);
}
if (endValue < entryToAmountMap.size())
{
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Next Page", item_subclass, gossipPageNumber + 1);
}
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, "|TInterface/ICONS/Ability_Spy:30:30:-18:0|tBack...", MAIN_MENU, 0);
SendGossipMenuFor(player, NPC_TEXT_ID, creature->GetGUID());
}));
}
};
// Add all scripts in one
void AddSC_mod_reagent_bank()
{
new npc_reagent_banker();
}