Game

1. char.cpp

Add:

	#ifdef ENABLE_ITEMSHOP
	#include "itemshop.h"
	#endif

Search:

	void CHARACTER::Initialize()

Add to the end of this function:

	#ifdef ENABLE_ITEMSHOP
		m_iItemshopCooldownTime = 0;
		m_iPromotionCodeCooldownTime = 0;
	#endif

Add to end of file:

	#ifdef ENABLE_ITEMSHOP
	void CHARACTER::SendPromotionRewardPacket(BYTE byAnswer, std::vector<TPromotionItemTable> items)
	{
		TEMP_BUFFER buf;

		TPacketGCItemshopInfo itemshop_info;
		itemshop_info.header = HEADER_GC_ITEMSHOP;
		itemshop_info.subheader = SUBHEADER_PROMOTION_CODE_REWARDS;
		buf.write(&itemshop_info, sizeof(TPacketGCItemshopInfo));

		buf.write(&byAnswer, sizeof(BYTE));

		size_t size = items.size();
		buf.write(&size, sizeof(size_t));
		buf.write(items.data(), sizeof(TPromotionItemTable) * size);
		GetDesc()->Packet(buf.read_peek(), buf.size());

	}

	void CHARACTER::SendItemshopSingleItemRefreshPacket(TItemshopItemTable* item)
	{
		TEMP_BUFFER buf;

		TPacketGCItemshopInfo itemshop_info;
		itemshop_info.header = HEADER_GC_ITEMSHOP;
		itemshop_info.subheader = SUBHEADER_ITEMSHOP_REFRESH_SINGLE_ITEM;
		buf.write(&itemshop_info, sizeof(TPacketGCItemshopInfo));
		buf.write(item, sizeof(TItemshopItemTable));
		GetDesc()->Packet(buf.read_peek(), buf.size());
	}

	void CHARACTER::SendItemshopSingleItemRemovePacket(TItemshopItemTable* item)
	{
		TEMP_BUFFER buf;

		TPacketGCItemshopInfo itemshop_info;
		itemshop_info.header = HEADER_GC_ITEMSHOP;
		itemshop_info.subheader = SUBHEADER_ITEMSHOP_REMOVE_SINGLE_ITEM;
		buf.write(&itemshop_info, sizeof(TPacketGCItemshopInfo));
		buf.write(item, sizeof(TItemshopItemTable));
		GetDesc()->Packet(buf.read_peek(), buf.size());
	}

	void CHARACTER::SendItemshopSingleItemAddPacket(TItemshopItemTable* item, TItemshopCategoryInfo category)
	{
		TEMP_BUFFER buf;

		TPacketGCItemshopInfo itemshop_info;
		itemshop_info.header = HEADER_GC_ITEMSHOP;
		itemshop_info.subheader = SUBHEADER_ITEMSHOP_ADD_SINGLE_ITEM;

		buf.write(&itemshop_info, sizeof(TPacketGCItemshopInfo));
		buf.write(&category, sizeof(TItemshopCategoryInfo));
		buf.write(item, sizeof(TItemshopItemTable));
		GetDesc()->Packet(buf.read_peek(), buf.size());
	}

	void CHARACTER::SendItemshopItemsPacket(const std::unordered_map <BYTE, std::vector<TItemshopItemTable>> items, const std::unordered_map <BYTE, TItemshopCategoryInfo> categories)
	{
		SendItemshopCoinPacket();

		TEMP_BUFFER buf;

		TPacketGCItemshopInfo itemshop_info;
		itemshop_info.header = HEADER_GC_ITEMSHOP;
		itemshop_info.subheader = SUBHEADER_ITEMSHOP_REFRESH_ITEMS;
		buf.write(&itemshop_info, sizeof(TPacketGCItemshopInfo));

		TPacketGCitemshopCategorySize itemshop_categorie_size;
		itemshop_categorie_size.size = categories.size();
		buf.write(&itemshop_categorie_size, sizeof(TPacketGCitemshopCategorySize));

		for (const auto& [key, val] : categories)
		{
			buf.write(&key, sizeof(BYTE));
			buf.write(&val, sizeof(TItemshopCategoryInfo));
		}

		TPacketGCItemshopItemSize itemshop_item_size;
		itemshop_item_size.size = items.size();
		buf.write(&itemshop_item_size, sizeof(TPacketGCItemshopItemSize));

		size_t temp = 0;
		for (const auto& [key, vec] : items) {
			buf.write(&key, sizeof(BYTE));
			temp = vec.size();
			buf.write(&temp, sizeof(size_t));
			buf.write(vec.data(), sizeof(TItemshopItemTable) * temp);
		}
		GetDesc()->Packet(buf.read_peek(), buf.size());
	}

	void CHARACTER::SendItemshopCoinPacket()
	{
		const TAccountTable& accTable = GetDesc()->GetAccountTable();

		unsigned long long ulltotalCoins = 0;

		std::unique_ptr<SQLMsg> pkMsg(DBManager::instance().DirectQuery("SELECT coins from account.account WHERE id = %ld", accTable.id));
		SQLResult* pRes = pkMsg->Get();
		if (pRes->uiNumRows > 0)
		{
			MYSQL_ROW data = mysql_fetch_row(pRes->pSQLResult);
			str_to_number(ulltotalCoins, data[0]);
		}

		TEMP_BUFFER buf;

		TPacketGCItemshopInfo itemshop_info;
		itemshop_info.header = HEADER_GC_ITEMSHOP;
		itemshop_info.subheader = SUBHEADER_ITEMSHOP_REFRESH_COINS;
		buf.write(&itemshop_info, sizeof(TPacketGCItemshopInfo));

		buf.write(&ulltotalCoins, sizeof(unsigned long long));
		GetDesc()->Packet(buf.read_peek(), buf.size());
	}
	#endif

2. char.h

Add to end of class CHARACTER :

	#ifdef ENABLE_ITEMSHOP
		public:
			void	SendItemshopItemsPacket(
				const std::unordered_map <BYTE, std::vector<TItemshopItemTable>> items,
				const std::unordered_map <BYTE, TItemshopCategoryInfo> categories
			);
			void	SendPromotionRewardPacket(BYTE byAnswer, std::vector<TPromotionItemTable>);
			void	SendItemshopSingleItemRefreshPacket(TItemshopItemTable* item);
			void	SendItemshopSingleItemRemovePacket(TItemshopItemTable* item);
			void	SendItemshopSingleItemAddPacket(TItemshopItemTable* item, TItemshopCategoryInfo category);
			void	SendItemshopCoinPacket();
			bool	CanUseItemshop() const { return  m_iItemshopCooldownTime < thecore_pulse(); }
			void	SetItemshopCooldown(int pulse) { m_iItemshopCooldownTime = pulse; }
			bool	CanUsePromotionCode() const { return  m_iPromotionCodeCooldownTime < thecore_pulse(); }
			void	SetPromotionCodedown(int pulse) { m_iPromotionCodeCooldownTime = pulse; }
		protected:
			int		m_iItemshopCooldownTime;
			int		m_iPromotionCodeCooldownTime;
	#endif

3. character_manager.cpp

Add:

	#ifdef ENABLE_ITEMSHOP
	#include "itemshop.h"
	#endif

Add to end of file:

	#ifdef ENABLE_ITEMSHOP
	void CHARACTER_MANAGER::SendItemshopSingleRemoveItem(TItemshopItemTable* item)
	{
		for (const auto& [player_id, ch] : m_map_pkChrByPID)
		{
			if (!ch)
				continue;

			ch->SendItemshopSingleItemRemovePacket(item);
		}
	}

	void CHARACTER_MANAGER::SendItemshopSingleAddItem(TItemshopItemTable* item)
	{
		const auto& categories = CItemshopManager::instance().GetItemshopCategories();

		const auto& category_info = categories.find(item->byCategory);

		if (category_info == categories.end())
		{
			// This could NEVER happen but it seems like it does
			return;
		}

		for (const auto& [player_id, ch] : m_map_pkChrByPID)
		{
			if (!ch)
				continue;

			ch->SendItemshopSingleItemAddPacket(item, category_info->second);
		}
	}

	void CHARACTER_MANAGER::SendItemshopSingleItemRefresh(TItemshopItemTable* item)
	{
		for (const auto& [player_id, ch] : m_map_pkChrByPID)
		{
			if (!ch)
				continue;

			ch->SendItemshopSingleItemRefreshPacket(item);
		}
	}

	void CHARACTER_MANAGER::SendItemshopItems()
	{
		const auto& items = CItemshopManager::instance().GetItemshopItems();
		const auto& categories = CItemshopManager::instance().GetItemshopCategories();

		for (const auto& [player_id, ch] : m_map_pkChrByPID)
		{
			if (!ch)
				continue;

			ch->SendItemshopItemsPacket(items, categories);
		}
	}
	#endif

4. character_manager.h

Add to end of class CHARACTER_MANAGER:


	#ifdef ENABLE_ITEMSHOP
		public:
			void			SendItemshopSingleRemoveItem(TItemshopItemTable* item);
			void			SendItemshopSingleAddItem(TItemshopItemTable* item);
			void			SendItemshopSingleItemRefresh(TItemshopItemTable* item);
			void			SendItemshopItems();
	#endif

5. cmd_gm.cpp

Search:

				case 'p':
					ch->ChatPacket(CHAT_TYPE_INFO, "Reloading prototype tables,");
					db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0);
					break;

Add:

	#ifdef ENABLE_ITEMSHOP
				case 'i':
					ch->ChatPacket(CHAT_TYPE_INFO, "Reloading itemshop tables,");
					db_clientdesc->DBPacket(HEADER_GD_RELOAD_ITEMSHOP, 0, NULL, 0);
					break;
	#endif

6. input.h

Search:

			void		Refine(LPCHARACTER ch, const char* c_pData);

Add:

	#ifdef ENABLE_ITEMSHOP
			void		BuyItemshopItem(LPCHARACTER ch, const char* c_pData);
			void		RedeemPromotionCode(LPCHARACTER ch, const char* c_pData);
	#endif

Search:

		protected:
			DWORD		m_dwHandle;

Add:

	#ifdef ENABLE_ITEMSHOP
		protected:
			void	ReloadItemshop(const char* c_pData);
			void	ItemshopBuyAnswer(LPDESC d, const char* c_pData);
			void	RedeemPromotionCode(LPDESC d, const char* c_pData);
	#endif

7. input_db.cpp

Add:

	#ifdef ENABLE_ITEMSHOP
	#include "itemshop.h"
	#endif

Search:

		if (decode_2bytes(data) != sizeof(TRefineTable))
		{
			sys_err("refine table size error");
			thecore_shutdown();
			return;
		}
		data += 2;

		size = decode_2bytes(data);
		data += 2;
		sys_log(0, "BOOT: REFINE: %d", size);

		if (size)
		{
			CRefineManager::instance().Initialize((TRefineTable*) data, size);
			data += size * sizeof(TRefineTable);
		}

Add:

	#ifdef ENABLE_ITEMSHOP
		if (decode_2bytes(data) != sizeof(TItemshopCategoryTable))
		{
			sys_err("itemshop table category size error");
			thecore_shutdown();
			return;
		}
		data += 2;

		size = decode_2bytes(data);
		data += 2;
		sys_log(0, "BOOT: ITEMSHOP: %d", size);

		if (size)
		{
			CItemshopManager::instance().InitializeCategories((TItemshopCategoryTable*)data, size);
			data += size * sizeof(TItemshopCategoryTable);
		}

		if (decode_2bytes(data) != sizeof(TItemshopItemTable))
		{
			sys_err("itemshop item table size error");
			thecore_shutdown();
			return;
		}
		data += 2;

		size = decode_2bytes(data);
		data += 2;
		sys_log(0, "BOOT: ITEMSHOP: %d", size);

		if (size)
		{
			CItemshopManager::instance().InitializeItems((TItemshopItemTable*)data, size);
			data += size * sizeof(TItemshopItemTable);
		}
	#endif

Search:

		case HEADER_DG_RELOAD_PROTO:
			ReloadProto(c_pData);
			break;

Add:

	#ifdef ENABLE_ITEMSHOP
		case HEADER_DG_RELOAD_ITEMSHOP:
			ReloadItemshop(c_pData);
			break;

		case HEADER_DG_REFRESH_ITEMSHOP_SINGLE_ITEM:
			CItemshopManager::instance().RefreshSingleItem((TItemshopItemTable*)c_pData);
			break;

		case HEADER_DG_REMOVE_ITEMSHOP_SINGLE_ITEM:
			CItemshopManager::instance().RemoveSingleItem((TItemshopItemTable*)c_pData);
			break;

		case HEADER_DG_ADD_ITEMSHOP_SINGLE_ITEM:
			CItemshopManager::instance().AddSingleItem((TItemshopItemTable*)c_pData);
			break;

		case HEADER_DG_BUY_ITEMSHOP_ITEM:
			ItemshopBuyAnswer(DESC_MANAGER::instance().FindByHandle(m_dwHandle), c_pData);
			break;

		case HEADER_DG_PROMOTION_CODE_REDEEM:
			RedeemPromotionCode(DESC_MANAGER::instance().FindByHandle(m_dwHandle), c_pData);
			break;
	#endif

Add to end of file:

	#ifdef ENABLE_ITEMSHOP
	void CInputDB::ItemshopBuyAnswer(LPDESC d, const char* c_pData)
	{
		if (!d)
			return;

		if (!d->GetCharacter())
			return;

		TItemshopBuyAnswer* buy_answer = (TItemshopBuyAnswer*)c_pData;

		LPCHARACTER ch = d->GetCharacter();

		if (buy_answer->canBuy)
		{
			CItemshopManager::instance().BuyItem(ch, buy_answer->code, buy_answer->wCount);
		}
		else
		{
			ch->ChatPacket(CHAT_TYPE_INFO, "cannot buy");
		}
	}

	void CInputDB::RedeemPromotionCode(LPDESC d, const char* c_pData)
	{
		if (!d)
			return;

		if (!d->GetCharacter())
			return;

		TPromotionRedeemAnswer* redeem_answer = (TPromotionRedeemAnswer*)c_pData;

		LPCHARACTER ch = d->GetCharacter();

		std::vector <TPromotionItemTable> itemsGiven;

		if (redeem_answer->byRedeemAnswer == REDEEM_SUCCESS)
		{
			TPromotionItemTable* rewards = (TPromotionItemTable*)(c_pData + sizeof(TPromotionRedeemAnswer));

			for (int i = 0; i < redeem_answer->reward_count; ++i, ++rewards)
			{
				const TItemTable* item_table = ITEM_MANAGER::instance().GetTable(rewards->dwVnum);
				if (item_table)
				{
					// NOTE: Stupid admin i guess
					if (rewards->wCount > 1 && ((IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_STACK)) || !(IS_SET(item_table->dwFlags, ITEM_FLAG_STACKABLE))))
						rewards->wCount = 1;
				}


				LPITEM item = ITEM_MANAGER::instance().CreateItem(rewards->dwVnum, rewards->wCount);

				if (!item)
				{
					ch->ChatPacket(CHAT_TYPE_INFO, "item does not exist, contact SA for free coins :)");
					return;
				}

				bool forcedSockets = false;

				if (rewards->alSockets[0])
				{
					for (int i = 0; i < ITEM_LIMIT_MAX_NUM; i++)
					{
						if (LIMIT_REAL_TIME == item->GetLimitType(i))
						{
							// NOTE: We set socket0 as time value, but only if socket is set so we still can use default times in createitem
							item->SetSocket(0, time(0) + rewards->alSockets[0]);
							// NOTE: We dont have to start realtime event because createitem does
							forcedSockets = true;

						}
						else if (LIMIT_TIMER_BASED_ON_WEAR == item->GetLimitType(i))
						{
							// NOTE: We force socket0 if set
							item->SetSocket(0, rewards->alSockets[0]);
							forcedSockets = true;
						}
					}
				}

				// NOTE: I dont have systems where i need to set multible sockets in createitem but you can still do:
				/*

				for(const auto& socket : itemInfo.alSockets){
					if (socket)
					{
						Funny socket method here
					}
				}

				*/
				if (!forcedSockets)
					item->SetSockets(rewards->alSockets);

				// NOTE: You can adjust this if you want to use bAlterToMagicItemPct
				item->SetAttributes(rewards->aAttr);

				ch->AutoGiveItem(item);
				itemsGiven.push_back(*rewards);
			}
		}

		ch->SendPromotionRewardPacket(redeem_answer->byRedeemAnswer, itemsGiven);
	}

	void CInputDB::ReloadItemshop(const char* c_pData)
	{
		WORD* category_size = (WORD*)c_pData;
		c_pData += sizeof(WORD);
		WORD* item_size = (WORD*)c_pData;
		c_pData += sizeof(WORD);

		CItemshopManager::instance().InitializeCategories((TItemshopCategoryTable*)c_pData, *category_size);
		c_pData += sizeof(TItemshopCategoryTable) * (*category_size);

		CItemshopManager::instance().InitializeItems((TItemshopItemTable*)c_pData, *item_size);
		c_pData += sizeof(TItemshopItemTable) * (*item_size);

		CHARACTER_MANAGER::instance().SendItemshopItems();
	}
	#endif

8. input_login.cpp

Add:

	#ifdef ENABLE_ITEMSHOP
	#include "itemshop.h"
	#endif

Search:

	void CInputLogin::Entergame(LPDESC d, const char * data)

Add to end of this function:

	#ifdef ENABLE_ITEMSHOP
		ch->SendItemshopItemsPacket(CItemshopManager::instance().GetItemshopItems(), CItemshopManager::instance().GetItemshopCategories());
	#endif

9. input_main.cpp

Add:

	#ifdef ENABLE_ITEMSHOP
	#include "itemshop.h"
	#endif

Search:

			case HEADER_CG_ACCE:
			{
				if ((iExtraLen = AcceRefine(ch, c_pData, m_iBufferLeft)) < 0)
				{
					return -1;
				}
			}
			break;

Add:

	#ifdef ENABLE_ITEMSHOP
			case HEADER_CG_BUY_ITEMSHOP_ITEM:
			{
				BuyItemshopItem(ch, c_pData);
			}
			break;

			case HEADER_CG_PROMOTION:
			{
				RedeemPromotionCode(ch, c_pData);
			}
			break;
	#endif

Add to end of file:

	#ifdef ENABLE_ITEMSHOP
	void CInputMain::BuyItemshopItem(LPCHARACTER ch, const char* c_pData)
	{
		const TPacketCGBuyItemshopItem* p = reinterpret_cast<const TPacketCGBuyItemshopItem*>(c_pData);

		if (CItemshopManager::instance().CanBuyItem(ch, p->hash, p->wCount))
		{
			TItemshopCheckBuy p_gd;
			strlcpy(p_gd.hash, p->hash, sizeof(p_gd.hash));
			p_gd.wCount = p->wCount;
			db_clientdesc->DBPacket(HEADER_GD_BUY_ITEMSHOP, ch->GetDesc()->GetHandle(), &p_gd, sizeof(p_gd));
		}
	}

	void CInputMain::RedeemPromotionCode(LPCHARACTER ch, const char* c_pData)
	{
		if (!ch->CanUsePromotionCode())
		{
			ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have to wait 10 seconds after using promotion codes."));
			return;
		}

		ch->SetPromotionCodedown(thecore_pulse() + PASSES_PER_SEC(10));

		const TPacketCGRedeemPromotionCode* p = reinterpret_cast<const TPacketCGRedeemPromotionCode*>(c_pData);

		// We dont have to check but atleast we dont send gd -> dg -> gc
		if (!strcmp(p->promotion_code, ""))
			return;

		TPromotionRedeem gd_p;
		strlcpy(gd_p.code, p->promotion_code, sizeof(gd_p.code));
		gd_p.accID = ch->GetAID();
		db_clientdesc->DBPacket(HEADER_GD_PROMOTION_REDEEM, ch->GetDesc()->GetHandle(), &gd_p, sizeof(gd_p));
	}
	#endif

10. main.cpp

Add:


	#ifdef ENABLE_ITEMSHOP
	#include "itemshop.h"
	#endif

Search:


		CRefineManager	refine_manager;

Add:


	#ifdef ENABLE_ITEMSHOP
		CItemshopManager itemshop_manager;
	#endif

11. packet.h

Search:

		HEADER_GC_TIME_SYNC							= 0xfc,
		HEADER_GC_PHASE								= 0xfd,

Add above:

	#ifdef ENABLE_ITEMSHOP
		HEADER_CG_BUY_ITEMSHOP_ITEM						= 240,
		HEADER_CG_PROMOTION							= 241,
	#endif

Search:

		HEADER_GG_LOGIN								= 1,

Add above:

	#ifdef ENABLE_ITEMSHOP
			HEADER_GC_ITEMSHOP 						= 240,
	#endif

Add to the end of file before #pragma pack():

	#ifdef ENABLE_ITEMSHOP
	typedef struct SPacketGCitemshopCategorySize
	{
		DWORD size;
	} TPacketGCitemshopCategorySize;

	typedef struct SPacketGCItemshopItemSize
	{
		DWORD size;
	} TPacketGCItemshopItemSize;

	typedef struct SPacketGCItemshopInfo
	{
		BYTE header;
		DWORD subheader;
	} TPacketGCItemshopInfo;

	enum
	{
		SUBHEADER_ITEMSHOP_REFRESH_ITEMS,
		SUBHEADER_ITEMSHOP_REFRESH_COINS,
		SUBHEADER_ITEMSHOP_REFRESH_SINGLE_ITEM,
		SUBHEADER_ITEMSHOP_REMOVE_SINGLE_ITEM,
		SUBHEADER_ITEMSHOP_ADD_SINGLE_ITEM,
		SUBHEADER_PROMOTION_CODE_REWARDS,
	};

	typedef struct SPacketCGBuyItemshopItem
	{
		BYTE	header;
		char	hash[ITEMSHOP_HASH_MAX_LEN + 1];
		WORD	wCount;
	} TPacketCGBuyItemshopItem;

	typedef struct SPacketCGRedeemPromotionCode
	{
		BYTE	header;
		char	promotion_code[PROMOTION_CODE_MAX_LEN + 1];
	} TPacketCGRedeemPromotionCode;
	#endif

12. packet_info.cpp

Search:

	CPacketInfoCG::CPacketInfoCG()

Add to the end of this function:

	#ifdef ENABLE_ITEMSHOP
		Set(HEADER_CG_BUY_ITEMSHOP_ITEM, sizeof(TPacketCGBuyItemshopItem), "BuyItemshopItem", false);
		Set(HEADER_CG_PROMOTION, sizeof(TPacketCGRedeemPromotionCode), "RedeemPromotionCode", false);
	#endif

13. MakeFile / Solution

Add:

	itemshop.cpp\,

14. itemshop.h

Create: itemshop.h

Insert this:

	/*
	  .oooooo.   oooooo   oooo ooooo      ooo   .oooo.
	 d8P'  `Y8b   `888.   .8'  `888b.     `8' .dP""Y88b
	888            `888. .8'    8 `88b.    8        ]8P'
	888             `888.8'     8   `88b.  8      <88b.
	888              `888'      8     `88b.8       `88b.
	`88b    ooo       888       8       `888  o.   .88P
	 `Y8bood8P'      o888o     o8o        `8  `8bd88P'

	Version:
		Release 1.0.0

	Contacts:
		Metin2Downloads: https://www.metin2downloads.to/cms/user/11018-cyn3/
		Discord: Franky#6315

	Support:
		Discord: https://discord.gg/sura-head

	Special thanks to:
		- Rainer <- Design & Animations
		- SamaLamaDingDong <- Code review (Server - Source)
		- <> <- Mental support
	*/

	#include "../../common/singleton.h"
	#include "../../common/service.h"
	#include <unordered_map>
	#include "constants.h"
	#ifdef ENABLE_ITEMSHOP
	class CItemshopManager : public singleton<CItemshopManager>
	{
	public:
		CItemshopManager();
		~CItemshopManager();

		void InitializeItems(TItemshopItemTable* table, WORD size);
		void InitializeCategories(TItemshopCategoryTable* table, WORD size);

		void RefreshSingleItem(TItemshopItemTable* item);
		void RemoveSingleItem(TItemshopItemTable* item);
		void AddSingleItem(TItemshopItemTable* item);

		bool IsValidHash(const char* hash);
		bool HasEnoughCoins(DWORD accID, unsigned long long ullItemPrice);
		bool IsValidPurchase(TItemshopItemTable itemInfo, WORD wCount, unsigned long long &ullPrice);
		bool CanBuyItem(LPCHARACTER ch, const char* hash, WORD wCount);
		bool BuyItem(LPCHARACTER ch, const char* hash, WORD wCount);

		const std::unordered_map <BYTE, TItemshopCategoryInfo> GetItemshopCategories();
		const std::unordered_map <BYTE, std::vector<TItemshopItemTable>> GetItemshopItems();

		std::unordered_map <BYTE, TItemshopCategoryInfo> m_ItemshopCategories;
		std::unordered_map <BYTE, std::vector<TItemshopItemTable>> m_ItemshopItems;
	};
	#endif

15. itemshop.cpp

Create: itemshop.cpp

Insert this:

	#include "stdafx.h"
	#include "itemshop.h"

	#include "char.h"
	#include "db.h"
	#include "char_manager.h"
	#include "sectree_manager.h"
	#include "config.h"
	#include "item_manager.h"
	#include "item.h"
	#include "desc.h"
	#ifdef ENABLE_ITEMSHOP
	CItemshopManager::CItemshopManager()
	{
	}

	CItemshopManager::~CItemshopManager()
	{
	}

	void CItemshopManager::InitializeItems(TItemshopItemTable * table, WORD size)
	{
		m_ItemshopItems.clear();

		for (int i = 0; i < size; ++i, ++table)
		{
			const TItemTable* item_table = ITEM_MANAGER::instance().GetTable(table->dwVnum);
			if (item_table)
			{
				if (table->wCount > 1 && ((IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_STACK)) || !(IS_SET(item_table->dwFlags, ITEM_FLAG_STACKABLE))))
				{
					sys_err("<Itemshop / Are you stupid?> Vnum %lld Count %d but item is not stackable. Adjust this shit!", table->dwVnum, table->wCount);
					continue;
				}

				m_ItemshopItems[table->byCategory].push_back(*table);
			}
		}
	}

	void CItemshopManager::InitializeCategories(TItemshopCategoryTable* table, WORD size)
	{
		m_ItemshopCategories.clear();

		for (int i = 0; i < size; ++i, ++table)
		{
			m_ItemshopCategories.insert(std::pair(table->index, table->info));
		}
	}

	void CItemshopManager::RefreshSingleItem(TItemshopItemTable* item)
	{
		for (auto& category : m_ItemshopItems)
		{
			for (auto& cur_item : category.second)
			{
				if (!strcmp(cur_item.hash, item->hash))
				{
					thecore_memcpy(&cur_item, item, sizeof(TItemshopItemTable));
					CHARACTER_MANAGER::instance().SendItemshopSingleItemRefresh(item);
					return;
				}
			}
		}
	}

	void CItemshopManager::RemoveSingleItem(TItemshopItemTable* item)
	{
		for (auto& category : m_ItemshopItems)
		{
			for(int i = 0; i < category.second.size(); i++)
			{
				if (!strcmp(category.second[i].hash, item->hash))
				{
					category.second.erase(category.second.begin() + i);
					CHARACTER_MANAGER::instance().SendItemshopSingleRemoveItem(item);
					return;
				}
			}
		}
	}

	void CItemshopManager::AddSingleItem(TItemshopItemTable* item)
	{
		const TItemTable* item_table = ITEM_MANAGER::instance().GetTable(item->dwVnum);
		if (item_table)
		{
			if (item->wCount > 1 && ((IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_STACK)) || !(IS_SET(item_table->dwFlags, ITEM_FLAG_STACKABLE))))
			{
				sys_err("<Itemshop / Are you stupid?> Vnum %ld Count %d but item is not stackable. Adjust this shit!", item->dwVnum, item->wCount);
				return;
			}

			m_ItemshopItems[item->byCategory].push_back(*item);
			CHARACTER_MANAGER::instance().SendItemshopSingleAddItem(item);
		}
	}

	bool CItemshopManager::IsValidHash(const char* hash)
	{
		for (const auto& category : m_ItemshopItems)
		{
			for (const auto& item : category.second) {
				if (!strcmp(hash, item.hash))
					return true;
			}
		}

		return false;
	}

	bool CItemshopManager::IsValidPurchase(TItemshopItemTable itemInfo, WORD wCount, unsigned long long &ullPrice)
	{
		const TItemTable* item_table = ITEM_MANAGER::instance().GetTable(itemInfo.dwVnum);
		if (item_table)
		{
			// NOTE: Log user reason custom packet
			if (wCount > 1 && ((IS_SET(item_table->dwAntiFlags, ITEM_ANTIFLAG_STACK)) || !(IS_SET(item_table->dwFlags, ITEM_FLAG_STACKABLE))))
				return false;
		}

		if (wCount <= 0
			|| check_mul_error(itemInfo.wCount, wCount)
			|| itemInfo.wCount * wCount > MAX_ITEM_STACK)
		{
			return false;
		}

		BYTE discountPercent = MINMAX(0, itemInfo.byDiscountPercent, 100);
		if (discountPercent > 0) {
			ullPrice = ullPrice - (ullPrice * discountPercent / 100);
		}

		if (check_mul_error<unsigned long long>(ullPrice, wCount)) {
			return false;
		}

		ullPrice *= wCount;

		return true;
	}

	bool CItemshopManager::HasEnoughCoins(DWORD accID, unsigned long long ullItemPrice)
	{
		std::unique_ptr<SQLMsg> pkMsg(DBManager::instance().DirectQuery("SELECT coins from account.account WHERE id = %ld", accID));
		SQLResult* pRes = pkMsg->Get();
		if (pRes->uiNumRows == 0)
		{
			return false;
		}

		MYSQL_ROW data = mysql_fetch_row(pRes->pSQLResult);
		unsigned long long ullTotalCoins;
		str_to_number(ullTotalCoins, data[0]);
		return ullTotalCoins >= ullItemPrice;
	}

	bool CItemshopManager::CanBuyItem(LPCHARACTER ch, const char* hash, WORD wCount)
	{
		if (!ch->CanUseItemshop())
		{
			ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You have to wait 10 seconds after an invalid itemshop action."));
			return false;
		}

		if (!IsValidHash(hash))
		{
			ch->SetItemshopCooldown(thecore_pulse() + PASSES_PER_SEC(10));
			return false;
		}

		TItemshopItemTable itemInfo{ 0 };

		for (const auto& category : m_ItemshopItems)
		{
			for (const auto& item : category.second)
			{
				if (!strcmp(hash, item.hash))
				{
					itemInfo = item;
					break;
				}
			}
		}

		if (itemInfo.llLimitCount == 0)
			return false;

		unsigned long long ullPrice = itemInfo.ullPrice;

		if (!IsValidPurchase(itemInfo, wCount, ullPrice))
		{
			sys_err("<Itemshop> OVERFLOW check failed. PID: %d - BuyCount %d - Hash %s - Vnum %lld - Count %d", ch->GetPlayerID(), wCount, itemInfo.hash, itemInfo.dwVnum, itemInfo.wCount);
			return false;
		}

		const TAccountTable& accTable = ch->GetDesc()->GetAccountTable();
		if (!HasEnoughCoins(accTable.id, ullPrice))
		{
			ch->SetItemshopCooldown(thecore_pulse() + PASSES_PER_SEC(10));
			ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You dont have enough coins."));
			return false;
		}

		LPITEM item = ITEM_MANAGER::instance().CreateItem(itemInfo.dwVnum, itemInfo.wCount * wCount);

		if (!item)
		{
			ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Invalid item -> Write your Serveradmin to get some free coins :)"));
			return false;
		}

		int iEmptyCell;
		if (item->IsDragonSoul())
		{
			if ((iEmptyCell = ch->GetEmptyDragonSoulInventory(item)) == -1)
			{
				ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You carry too many Items."));
				M2_DESTROY_ITEM(item);
				return false;
			}
		}
		else
		{
			// NOTE: Adjust for special inventory by sanii
			if ((iEmptyCell = ch->GetEmptyInventory(item->GetSize())) == -1)
			{
				ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You carry too many Items."));
				M2_DESTROY_ITEM(item);
				return false;
			}
		}

		M2_DESTROY_ITEM(item);
		return true;
	}

	// NOTE: CALL THIS ONLY FROM DB_MANAGER
	bool CItemshopManager::BuyItem(LPCHARACTER ch, const char* hash, WORD wCount)
	{
		if (!IsValidHash(hash))
		{
			ch->SendItemshopItemsPacket(GetItemshopItems(),GetItemshopCategories());
			return false;
		}

		TItemshopItemTable itemInfo{0};

		for (const auto& category : m_ItemshopItems)
		{
			for (const auto & item : category.second)
			{
				if (!strcmp(hash, item.hash))
				{
					itemInfo = item;
					break;
				}
			}
		}

		unsigned long long ullPrice = itemInfo.ullPrice;

		if (!IsValidPurchase(itemInfo, wCount, ullPrice))
		{
			sys_err("<Itemshop> OVERFLOW check failed. PID: %d - BuyCount %d - Hash %s - Vnum %lld - Count %d", ch->GetPlayerID(), wCount, itemInfo.hash, itemInfo.dwVnum, itemInfo.wCount);
			return false;
		}

		const TAccountTable& accTable = ch->GetDesc()->GetAccountTable();
		if (!HasEnoughCoins(accTable.id, ullPrice))
		{
			ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You dont have enough coins."));
			return false;
		}


		LPITEM item = ITEM_MANAGER::instance().CreateItem(itemInfo.dwVnum, itemInfo.wCount * wCount);

		if (!item)
		{
			ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Invalid item -> Write your Serveradmin to get some free coins :)"));
			return false;
		}

		// NOTE: adjust as needed

		bool forcedSockets = false;

		if (itemInfo.alSockets[0])
		{
			for (int i = 0; i < ITEM_LIMIT_MAX_NUM; i++)
			{
				if (LIMIT_REAL_TIME == item->GetLimitType(i))
				{
					// NOTE: We set socket0 as time value, but only if socket is set so we still can use default times in createitem
					item->SetSocket(0, time(0) + itemInfo.alSockets[0]);
					// NOTE: We dont have to start realtime event because createitem already does
					forcedSockets = true;

				}
				else if (LIMIT_TIMER_BASED_ON_WEAR == item->GetLimitType(i))
				{
					item->SetSocket(0, itemInfo.alSockets[0]);
					forcedSockets = true;
				}
			}
		}

		// NOTE: I dont have systems where i need to set multiple sockets in createitem but you can still do:
		// for(const auto& socket : itemInfo.alSockets)
		if(!forcedSockets)
			item->SetSockets(itemInfo.alSockets);

		// NOTE: You can adjust this if you want to use bAlterToMagicItemPct
		item->SetAttributes(itemInfo.aAttr);

		int iEmptyCell;
		if (item->IsDragonSoul())
		{
			if ((iEmptyCell = ch->GetEmptyDragonSoulInventory(item)) == -1)
			{
				ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You carry too many Items."));
				M2_DESTROY_ITEM(item);
				return false;
			}
		}
		else
		{
			// NOTE: Adjust for special inventory by sanii
			if ((iEmptyCell = ch->GetEmptyInventory(item->GetSize())) == -1)
			{
				ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You carry too many Items."));
				M2_DESTROY_ITEM(item);
				return false;
			}
		}
		DBManager::instance().DirectQuery("UPDATE account.account SET coins = coins - %lld WHERE id = %ld", ullPrice, accTable.id);

		if (item->IsDragonSoul())
			item->AddToCharacter(ch, TItemPos(DRAGON_SOUL_INVENTORY, iEmptyCell));
		else
			item->AddToCharacter(ch, TItemPos(INVENTORY, iEmptyCell));


		ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("The purchase of the item was successful."));

		ch->SendItemshopCoinPacket();
		return true;
	}

	const std::unordered_map <BYTE, std::vector<TItemshopItemTable>> CItemshopManager::GetItemshopItems()
	{
		return m_ItemshopItems;
	}

	const std::unordered_map < BYTE, TItemshopCategoryInfo> CItemshopManager::GetItemshopCategories()
	{
		return m_ItemshopCategories;
	}
	#endif