From 13cb5945f0a54e4d0f727108d52ad4ce35145962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E4=BD=A9=E8=8C=B9?= Date: Fri, 11 Mar 2022 14:36:23 -0700 Subject: [PATCH] Initial commit. --- .editorconfig | 8 + .gitattributes | 105 +++++++++++ .github/workflows/core-build.yml | 49 +++++ .gitignore | 48 +++++ LICENSE | 21 +++ conf/conf.sh.dist | 32 ++++ conf/reagent_bank.conf.dist | 17 ++ include.sh | 10 + sql/auth/updates/.gitkeep | 0 sql/characters/base/create_table.sql | 7 + sql/characters/updates/.gitkeep | 0 sql/world/base/reagent_bank_NPC.sql | 7 + sql/world/updates/.gitkeep | 0 src/ReagentBank.cpp | 271 +++++++++++++++++++++++++++ src/ReagentBank.h | 25 +++ src/ReagentBank_loader.cpp | 15 ++ 16 files changed, 615 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/workflows/core-build.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 conf/conf.sh.dist create mode 100644 conf/reagent_bank.conf.dist create mode 100644 include.sh create mode 100644 sql/auth/updates/.gitkeep create mode 100644 sql/characters/base/create_table.sql create mode 100644 sql/characters/updates/.gitkeep create mode 100644 sql/world/base/reagent_bank_NPC.sql create mode 100644 sql/world/updates/.gitkeep create mode 100644 src/ReagentBank.cpp create mode 100644 src/ReagentBank.h create mode 100644 src/ReagentBank_loader.cpp diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..eb64e2f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +tab_width = 4 +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 80 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7ef9001 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,105 @@ +## AUTO-DETECT +## Handle line endings automatically for files detected as +## text and leave all files detected as binary untouched. +## This will handle all files NOT defined below. +* text=auto eol=lf + +# Text +*.conf text +*.conf.dist text +*.cmake text + +## Scripts +*.sh text +*.fish text +*.lua text + +## SQL +*.sql text + +## C++ +*.c text +*.cc text +*.cxx text +*.cpp text +*.c++ text +*.hpp text +*.h text +*.h++ text +*.hh text + + +## For documentation + +# Documents +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain + +## DOCUMENTATION +*.markdown text +*.md text +*.mdwn text +*.mdown text +*.mkd text +*.mkdn text +*.mdtxt text +*.mdtext text +*.txt text +AUTHORS text +CHANGELOG text +CHANGES text +CONTRIBUTING text +COPYING text +copyright text +*COPYRIGHT* text +INSTALL text +license text +LICENSE text +NEWS text +readme text +*README* text +TODO text + +## GRAPHICS +*.ai binary +*.bmp binary +*.eps binary +*.gif binary +*.ico binary +*.jng binary +*.jp2 binary +*.jpg binary +*.jpeg binary +*.jpx binary +*.jxr binary +*.pdf binary +*.png binary +*.psb binary +*.psd binary +*.svg text +*.svgz binary +*.tif binary +*.tiff binary +*.wbmp binary +*.webp binary + + +## ARCHIVES +*.7z binary +*.gz binary +*.jar binary +*.rar binary +*.tar binary +*.zip binary + +## EXECUTABLES +*.exe binary +*.pyc binary diff --git a/.github/workflows/core-build.yml b/.github/workflows/core-build.yml new file mode 100644 index 0000000..f66f694 --- /dev/null +++ b/.github/workflows/core-build.yml @@ -0,0 +1,49 @@ +name: core-build +on: + push: + pull_request: + +jobs: + build: + strategy: + fail-fast: false + matrix: + compiler: [clang] + runs-on: ubuntu-latest + name: ${{ matrix.compiler }} + env: + COMPILER: ${{ matrix.compiler }} + steps: + - uses: actions/checkout@v2 + with: + repository: 'azerothcore/azerothcore-wotlk' + ref: 'master' + submodules: 'recursive' + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + path: 'modules/skeleton-module' + - name: Cache + uses: actions/cache@v2 + with: + path: /home/runner/.ccache + key: ccache:${{ matrix.compiler }}:${{ github.ref }}:${{ github.sha }} + restore-keys: | + ccache:${{ matrix.compiler }}:${{ github.ref }} + ccache:${{ matrix.compiler }} + - name: Configure OS + run: source ./acore.sh install-deps + env: + CONTINUOUS_INTEGRATION: true + - name: Create conf/config.sh + run: source ./apps/ci/ci-conf.sh + - name: Import db + run: source ./apps/ci/ci-import-db.sh + - name: Build + run: source ./apps/ci/ci-compile.sh + - name: Dry run + run: source ./apps/ci/ci-worldserver-dry-run.sh + - name: Check startup errors + run: source ./apps/ci/ci-error-check.sh + - name: Run unit tests + run: source ./apps/ci/ci-run-unit-tests.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6e1299 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +!.gitignore + +# +#Generic +# + +.directory +.mailmap +*.orig +*.rej +*.*~ +.hg/ +*.kdev* +.DS_Store +CMakeLists.txt.user +*.bak +*.patch +*.diff +*.REMOTE.* +*.BACKUP.* +*.BASE.* +*.LOCAL.* + +# +# IDE & other softwares +# +/.settings/ +/.externalToolBuilders/* +# exclude in all levels +nbproject/ +.sync.ffs_db +*.kate-swp + +# +# Eclipse +# +*.pydevproject +.metadata +.gradle +tmp/ +*.tmp +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.project +.cproject diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9031171 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 AzerothCore + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/conf/conf.sh.dist b/conf/conf.sh.dist new file mode 100644 index 0000000..0e78e8a --- /dev/null +++ b/conf/conf.sh.dist @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +## CUSTOM SQL - Important file used by the db_assembler.sh +## Keep only the required variables (base sql files or updates, depending on the DB) + +## BASE SQL + +DB_AUTH_CUSTOM_PATHS+=( + "$MOD_SKELETON_ROOT/sql/auth/base/" +) + +DB_CHARACTERS_CUSTOM_PATHS+=( + "$MOD_SKELETON_ROOT/sql/characters/base/" +) + +DB_WORLD_CUSTOM_PATHS+=( + "$MOD_SKELETON_ROOT/sql/world/base/" +) + +## UPDATES + +DB_AUTH_UPDATES_PATHS+=( + "$MOD_SKELETON_ROOT/sql/auth/updates/" +) + +DB_CHARACTERS_UPDATES_PATHS+=( + "$MOD_SKELETON_ROOT/sql/characters/updates/" +) + +DB_WORLD_UPDATES_PATHS+=( + "$MOD_SKELETON_ROOT/sql/world/updates/" +) diff --git a/conf/reagent_bank.conf.dist b/conf/reagent_bank.conf.dist new file mode 100644 index 0000000..cb064b0 --- /dev/null +++ b/conf/reagent_bank.conf.dist @@ -0,0 +1,17 @@ +# +# Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 +# + +[worldserver] + +######################################## +# Reagent Bank Config +######################################## +# +# ReagentBank.Enable +# Description: Enable the reagent bank +# Default: 0 - Disabled +# 1 - Enabled +# + +ReagentBank.Enable = 1 diff --git a/include.sh b/include.sh new file mode 100644 index 0000000..a241572 --- /dev/null +++ b/include.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +## GETS THE CURRENT MODULE ROOT DIRECTORY +MOD_SKELETON_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/" && pwd )" + +source $MOD_SKELETON_ROOT"/conf/conf.sh.dist" + +if [ -f $MOD_SKELETON_ROOT"/conf/conf.sh" ]; then + source $MOD_SKELETON_ROOT"/conf/conf.sh" +fi diff --git a/sql/auth/updates/.gitkeep b/sql/auth/updates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/sql/characters/base/create_table.sql b/sql/characters/base/create_table.sql new file mode 100644 index 0000000..8d3e274 --- /dev/null +++ b/sql/characters/base/create_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS `custom_reagent_bank` ( + `character_id` int(11) NOT NULL, + `item_entry` int(11) NOT NULL, + `item_subclass` int(11) NOT NULL, + `amount` int(11) NOT NULL, + PRIMARY KEY (`character_id`,`item_entry`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/sql/characters/updates/.gitkeep b/sql/characters/updates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/sql/world/base/reagent_bank_NPC.sql b/sql/world/base/reagent_bank_NPC.sql new file mode 100644 index 0000000..a6ca124 --- /dev/null +++ b/sql/world/base/reagent_bank_NPC.sql @@ -0,0 +1,7 @@ +SET +@Entry = 190011, +@Name = "Ling"; +DELETE FROM `creature_template` WHERE `entry` = @Entry; + +INSERT INTO `creature_template` (`entry`, `modelid1`, `modelid2`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `scale`, `rank`, `dmgschool`, `baseattacktime`, `rangeattacktime`, `unit_class`, `unit_flags`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `AIName`, `MovementType`, `HoverHeight`, `RacialLeader`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`) VALUES +(@Entry, 15965, 0, @Name, 'Reagent Banker', NULL, 0, 6, 6, 0, 35, 1, 1, 0, 0, 2000, 0, 1, 0, 7, 138412032, 0, 0, 0, '', 0, 1, 0, 0, 1, 0, 2, 'npc_reagent_banker'); diff --git a/sql/world/updates/.gitkeep b/sql/world/updates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/ReagentBank.cpp b/src/ReagentBank.cpp new file mode 100644 index 0000000..e579fda --- /dev/null +++ b/src/ReagentBank.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2016+ AzerothCore , 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 + WorldSession *session = player->GetSession(); + + std::string query = "SELECT amount FROM custom_reagent_bank WHERE character_id = " + std::to_string(player->GetSession()->GetAccountId()) + " AND item_entry = " + std::to_string(entry); + QueryResult result = CharacterDatabase.Query("SELECT amount FROM custom_reagent_bank WHERE character_id = " + std::to_string(player->GetSession()->GetAccountId()) + " AND item_entry = " + std::to_string(entry)); + if (result) + { + uint32 storedAmount = (*result)[0].Get(); + 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 &entryToAmountMap, std::map &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->GetMaxStackSize() == 1) + return; + uint32 itemEntry = itemTemplate->ItemId; + uint32 itemSubclass = itemTemplate->SubClass; + 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([=](QueryResult result) { + std::map entryToAmountMap; + std::map entryToSubclassMap; + if (result) + { + do { + uint32 itemEntry = (*result)[0].Get(); + uint32 itemSubclass = (*result)[1].Get(); + uint32 itemAmount = (*result)[2].Get(); + 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 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); + 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->GetSession()->GetAccountId()) + " AND item_subclass = " + + std::to_string(item_subclass) + " ORDER BY item_entry"; + session->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(query).WithCallback([=](QueryResult result) + { + uint32 startValue = (gossipPageNumber * (MAX_OPTIONS)); + uint32 endValue = (gossipPageNumber + 1) * (MAX_OPTIONS) - 1; + std::map entryToAmountMap; + std::vector itemEntries; + if (result) { + do { + uint32 itemEntry = (*result)[0].Get(); + uint32 itemAmount = (*result)[1].Get(); + 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(); +} diff --git a/src/ReagentBank.h b/src/ReagentBank.h new file mode 100644 index 0000000..395b5d7 --- /dev/null +++ b/src/ReagentBank.h @@ -0,0 +1,25 @@ + +#ifndef AZEROTHCORE_REAGENTBANK_H +#define AZEROTHCORE_REAGENTBANK_H +#include "ScriptMgr.h" +#include "Player.h" +#include "Config.h" +#include "Chat.h" +#include "ScriptedCreature.h" +#include "ScriptedGossip.h" +#include "Item.h" +#include "ItemTemplate.h" +#include + +#define MAX_OPTIONS 23 +#define MAX_PAGE_NUMBER 700 // Values higher than this are considered Item IDs +#define NPC_TEXT_ID 4259 // Pre-existing NPC text + +enum GossipItemType : uint8 +{ + DEPOSIT_ALL_REAGENTS = 16, + MAIN_MENU = 17 +}; + + +#endif //AZEROTHCORE_REAGENTBANK_H diff --git a/src/ReagentBank_loader.cpp b/src/ReagentBank_loader.cpp new file mode 100644 index 0000000..aa59485 --- /dev/null +++ b/src/ReagentBank_loader.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + */ + +// From SC +void AddSC_mod_reagent_bank(); + +// Add all +// cf. the naming convention https://github.com/azerothcore/azerothcore-wotlk/blob/master/doc/changelog/master.md#how-to-upgrade-4 +// additionally replace all '-' in the module folder name with '_' here +void Addmod_reagent_bankScripts() +{ + AddSC_mod_reagent_bank(); +} +