/*=================================================================
	File created by Yohann NICOLAS.
	Add support 1.13d by L'Autour.
	Add support 1.14d by haxifix.

	Interface stats page functions

=================================================================*/

#include "interface_Stash.h"
#include "updateServer.h"
#include "infinityStash.h"
#include "plugYFiles.h"		// Install_PlugYImagesFiles()
#include "common.h"
#include <stdio.h>
#include <ctime>      // Include ctime for srand and rand functions
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstdlib>
#include <Windows.h>

static struct
{
	union{
		DWORD all;
		struct{
			DWORD previous:1;
			DWORD next:1;
			DWORD toggleToSharedStash:1;
			DWORD previousIndex:1;
			DWORD nextIndex:1;
			DWORD putGold:1;
			DWORD takeGold:1;
			DWORD stashName:1;
		};
	};
} isDownBtn;

bool displaySharedSetItemNameInGreen = true;
int posXPreviousBtn = -1;
int posYPreviousBtn = -1;
int posWPreviousBtn = 32;
int posHPreviousBtn = 32;
int posXNextBtn = -1;
int posYNextBtn = -1;
int posWNextBtn = 32;
int posHNextBtn = 32;
int posXSharedBtn = -1;
int posYSharedBtn = -1;
int posWSharedBtn = 32;
int posHSharedBtn = 32;
int posXPreviousIndexBtn = -1;
int posYPreviousIndexBtn = -1;
int posWPreviousIndexBtn = 32;
int posHPreviousIndexBtn = 32;
int posXNextIndexBtn = -1;
int posYNextIndexBtn = -1;
int posWNextIndexBtn = 32;
int posHNextIndexBtn = 32;
int posXPutGoldBtn = -1;
int posYPutGoldBtn = -1;
int posWPutGoldBtn = 32;
int posHPutGoldBtn = 32;
int posXTakeGoldBtn = -1;
int posYTakeGoldBtn = -1;
int posWTakeGoldBtn = 32;
int posHTakeGoldBtn = 32;
int posXStashNameField = -1;
int posYStashNameField = -1;
int posWStashNameField = 175;
int posHStashNameField = 20;
int posXStashGoldField = -1;
int posYStashGoldField = -1;
int posWStashGoldField = 152;
int posHStashGoldField = 18;

#include <chrono>

// Define a type alias for clarity
using namespace std::chrono;

// Global variable to store start time
time_point<steady_clock> startTime;

// Function to start the timer
void StartTimer() {
	startTime = steady_clock::now();
}

// Function to check if 5000ms have elapsed
bool HasElapsed() {
	auto currentTime = steady_clock::now();
	auto elapsed = duration_cast<milliseconds>(currentTime - startTime);
	return elapsed.count() >= 5000;
}


DWORD PersonalNormalPageColor = WHITE;
DWORD PersonalIndexPageColor = DARK_GREEN;
DWORD PersonalMainIndexPageColor = YELLOW;
DWORD SharedNormalPageColor = GOLD;
DWORD SharedIndexPageColor = ORANGE;
DWORD SharedMainIndexPageColor = RED;


DWORD	getXPreviousBtn()		{return RX(posXPreviousBtn<0? D2GetResolution()?0x80:0xAF : posXPreviousBtn);}
DWORD	getYPreviousBtn()		{return RY(posYPreviousBtn<0 ? 0x40 : posYPreviousBtn);}
DWORD	getXNextBtn()			{return RX(posXNextBtn<0 ? D2GetResolution()?0xA0:0xCF :posXNextBtn);}//?169:SEL_X(0x63, 0x63, 0xCF, 0xA0));}
DWORD	getYNextBtn()			{return RY(posYNextBtn<0 ? 0x40 : posYNextBtn);}
DWORD	getXSharedBtn()			{return RX(posXSharedBtn<0 ? D2GetResolution()?0x10:0x6F :posXSharedBtn);}//17:SEL_X(0xE3, 0xE3, 0x6F, 0x10));}//0xD8
DWORD	getYSharedBtn()			{return RY(posYSharedBtn<0 ? 0x40 : posYSharedBtn);}
DWORD	getXPreviousIndexBtn()	{return RX(posXPreviousIndexBtn<0 ?  D2GetResolution()?0x60:0x8F :posXPreviousIndexBtn);}//73:SEL_X(0x18, 0x60, 0x8F, 0x60));}
DWORD	getYPreviousIndexBtn()	{return RY(posYPreviousIndexBtn<0 ? 0x40 : posYPreviousIndexBtn);}
DWORD	getXNextIndexBtn()		{return RX(posXNextIndexBtn<0? D2GetResolution()?0xC0:0xEF : posXNextIndexBtn);}//217:SEL_X(0x128, 0xC0, 0xEF, 0xC0));}
DWORD	getYNextIndexBtn()		{return RY(posYNextIndexBtn<0 ? 0x40 : posYNextIndexBtn);}
DWORD	getXPutGoldBtn()		{return RX(posXPutGoldBtn<0? 0x1C : posXPutGoldBtn);}
DWORD	getYPutGoldBtn()		{return RY(posYPutGoldBtn<0 ? 0x1A8 : posYPutGoldBtn);}
DWORD	getXTakeGoldBtn()		{return RX(posXTakeGoldBtn<0? 0x105 : posXTakeGoldBtn);}
DWORD	getYTakeGoldBtn()		{return RY(posYTakeGoldBtn<0 ? 0x1A8 : posYTakeGoldBtn);}
DWORD	getXStashNameField()	{return RX(posXStashNameField<0 ? 0x4A : posXStashNameField);}
DWORD	getYStashNameField()	{return RY(posYStashNameField<0 ? 0x19A : posYStashNameField);}
DWORD	getXStashGoldField()	{return RX(posXStashGoldField<0 ? 0x61 : posXStashGoldField);}
DWORD	getYStashGoldField()	{return RY(posYStashGoldField<0 ? 0x1B6 : posYStashGoldField);}

//closeBtn x: D2C=0x113 LOD=0x110(0->0x28)		y: D2C=0x41 LOD=0x40 (-0x23 -> 5)


#define isOnButtonNextStash(x,y) isOnRect(x, y, getXNextBtn(), getYNextBtn(), posWNextBtn, posHNextBtn)
#define isOnButtonPreviousStash(x,y) isOnRect(x, y, getXPreviousBtn(), getYPreviousBtn(), posWPreviousBtn, posHPreviousBtn)
#define isOnButtonToggleSharedStash(x,y) isOnRect(x, y, getXSharedBtn(), getYSharedBtn(), posWSharedBtn, posHSharedBtn)
#define isOnButtonNextIndexStash(x,y) isOnRect(x, y, getXNextIndexBtn(), getYNextIndexBtn(), posWNextIndexBtn, posHNextIndexBtn)
#define isOnButtonPreviousIndexStash(x,y) isOnRect(x, y, getXPreviousIndexBtn(), getYPreviousIndexBtn(), posWPreviousIndexBtn, posHPreviousIndexBtn)
#define isOnButtonPutGold(x,y) isOnRect(x, y, getXPutGoldBtn(), getYPutGoldBtn(), posWPutGoldBtn, posHPutGoldBtn)
#define isOnButtonTakeGold(x,y) isOnRect(x, y, getXTakeGoldBtn(), getYTakeGoldBtn(), posWTakeGoldBtn, posHTakeGoldBtn)
#define isOnStashNameField(x,y) isOnRect(x, y, getXStashNameField(), getYStashNameField(), posWStashNameField, posHStashNameField)
#define isOnStashGoldField(x,y) isOnRect(x, y, getXStashGoldField(), getYStashGoldField(), posWStashGoldField, posHStashGoldField)

void UpdateStashPosition()
{
	//static int isInstalled = false;
	//if (isInstalled) return;

	InventoryBIN* inventory = D2GetInventoryBIN();
	if (inventory->gridY == 15)
	{
		posXPreviousBtn = 127;
		posYPreviousBtn = 500;
		posXNextBtn = 159;
		posYNextBtn = 500;
		posXSharedBtn = 28;
		posYSharedBtn = 21;
		posXPreviousIndexBtn = 95;
		posYPreviousIndexBtn = 500;
		posXNextIndexBtn = 191;
		posYNextIndexBtn = 500;
		posXPutGoldBtn = 28;
		posYPutGoldBtn = 500;
		posXTakeGoldBtn = 259;
		posYTakeGoldBtn = 500;
		//posXStashNameField = -1;
		posYStashNameField = 16;
		//posXStashGoldField = -1;
		posYStashGoldField = 36;
	}
}

void* STDCALL printBtns()
{
	if (onRealm || !D2isLODGame()) return D2LoadBuySelBtn();

	Unit* ptChar = D2GetClientPlayer();

	sDrawImageInfo data;
	ZeroMemory(&data,sizeof(data));
	setImage(&data, stashBtnsImages);

	setFrame(&data, 0 + isDownBtn.previous);
	D2PrintImage(&data, getXPreviousBtn(), getYPreviousBtn(), -1, 5, 0);

	setFrame(&data, 2 + isDownBtn.next);
	D2PrintImage(&data, getXNextBtn(), getYNextBtn(), -1, 5, 0);

	if (active_sharedStash)
	{
		setFrame(&data, 4 + isDownBtn.toggleToSharedStash + (PCPY->showSharedStash?2:0) );
		D2PrintImage(&data, getXSharedBtn(), getYSharedBtn(), -1, 5, 0);
	}

	setFrame(&data, 8 + isDownBtn.previousIndex);
	D2PrintImage(&data, getXPreviousIndexBtn(), getYPreviousIndexBtn(), -1, 5, 0);

	setFrame(&data, 10 + isDownBtn.nextIndex);
	D2PrintImage(&data, getXNextIndexBtn(), getYNextIndexBtn(), -1, 5, 0);

	if (active_sharedGold)
	{
		setImage(&data, sharedGoldBtnsImages);
		setFrame(&data, 0 + isDownBtn.putGold);
		D2PrintImage(&data, getXPutGoldBtn(), getYPutGoldBtn(), -1, 5, 0);

		setFrame(&data, 2 + isDownBtn.takeGold);
		D2PrintImage(&data, getXTakeGoldBtn(), getYTakeGoldBtn(), -1, 5, 0);
	}


	LPWSTR lpText;
	DWORD mx = D2GetMouseX();
	DWORD my = D2GetMouseY();

	D2SetFont(1);

	if (isOnButtonPreviousStash(mx,my)) {
		lpText = getLocalString(STR_STASH_PREVIOUS_PAGE);
		D2PrintPopup(lpText, getXPreviousBtn()+posWPreviousBtn/2, getYPreviousBtn()-posHPreviousBtn, WHITE, 1);

	} else if (isOnButtonNextStash(mx,my)) {
		lpText = getLocalString(STR_STASH_NEXT_PAGE);
		D2PrintPopup(lpText, getXNextBtn()+posWNextBtn/2, getYNextBtn()-posHNextBtn, WHITE, 1);

	} else if (isOnButtonToggleSharedStash(mx,my)) {
		if (active_sharedStash) {
			lpText = getLocalString(PCPY->showSharedStash ? STR_TOGGLE_TO_PERSONAL : STR_TOGGLE_TO_SHARED);
			D2PrintPopup(lpText, getXSharedBtn()+posWSharedBtn/2, getYSharedBtn()-posHSharedBtn, WHITE, 1);
		} else if (active_SharedStashInMultiPlayer == 1) {
			lpText = getLocalString(STR_TOGGLE_MULTI_DISABLED);
			D2PrintPopup(lpText, getXPreviousIndexBtn()+posWPreviousIndexBtn/2, getYPreviousIndexBtn()-posHPreviousIndexBtn, WHITE, 1);
		}

	} else if (isOnButtonPreviousIndexStash(mx,my)) {
		lpText = getLocalString(STR_STASH_PREVIOUS_INDEX);
		D2PrintPopup(lpText, getXPreviousIndexBtn()+posWPreviousIndexBtn/2, getYPreviousIndexBtn()-posHPreviousIndexBtn, WHITE, 1);

	} else if (isOnButtonNextIndexStash(mx,my))	{
		lpText = getLocalString(STR_STASH_NEXT_INDEX);
		D2PrintPopup(lpText, getXNextIndexBtn()+posWNextIndexBtn/2, getYNextIndexBtn()-posHNextIndexBtn, WHITE, 1);

	} else if (active_sharedGold && isOnButtonPutGold(mx,my))	{
		lpText = getLocalString(STR_PUT_GOLD);
		D2PrintPopup(lpText, getXPutGoldBtn()+posWPutGoldBtn/2, getYPutGoldBtn()-posHPutGoldBtn, WHITE, 1);

	} else if (active_sharedGold && isOnButtonTakeGold(mx,my))	{
		lpText = getLocalString(STR_TAKE_GOLD);
		D2PrintPopup(lpText, getXTakeGoldBtn()+posWTakeGoldBtn/2, getYTakeGoldBtn()-posHTakeGoldBtn, WHITE, 1);
	}

	return D2LoadBuySelBtn();
}


DWORD STDCALL manageBtnDown(sWinMessage* msg)
{
	if (onRealm || !D2isLODGame()) return 0;

	if (isOnButtonPreviousStash(msg->x,msg->y))
		isDownBtn.previous = 1;
	else if (isOnButtonNextStash(msg->x,msg->y))
		isDownBtn.next = 1;
	else if (active_sharedStash && isOnButtonToggleSharedStash(msg->x,msg->y))
		isDownBtn.toggleToSharedStash = 1;
	else if (isOnButtonPreviousIndexStash(msg->x,msg->y))
		isDownBtn.previousIndex = 1;
	else if (isOnButtonNextIndexStash(msg->x,msg->y))
		isDownBtn.nextIndex = 1;
	else if (active_sharedGold && isOnButtonPutGold(msg->x,msg->y))
		isDownBtn.putGold = 1;
	else if (active_sharedGold && isOnButtonTakeGold(msg->x,msg->y))
		isDownBtn.takeGold = 1;
	else if (isOnStashNameField(msg->x,msg->y))
		isDownBtn.stashName = 1;
	else return 0;

	D2PlaySound(4,0,0,0,0);
	freeMessage(msg);
	return 1;
}


DWORD STDCALL manageBtnUp(sWinMessage* msg)
{
	if (onRealm || !D2isLODGame()) return 0;

	Unit* ptChar = D2GetClientPlayer();

	if (isOnButtonPreviousStash(msg->x,msg->y)) {
		log_msg("push up left button previous\n");
		if (isDownBtn.previous)
			if (GetKeyState(VK_SHIFT)<0)
				updateServer(US_SELECT_PREVIOUS2);
			else
				updateServer(US_SELECT_PREVIOUS);

	} else if (isOnButtonNextStash(msg->x,msg->y)) {
		log_msg("push up left button next\n");
		if (isDownBtn.next)
			if (GetKeyState(VK_SHIFT)<0)
				updateServer(US_SELECT_NEXT2);
			else
				updateServer(US_SELECT_NEXT);

	} else if (active_sharedStash && isOnButtonToggleSharedStash(msg->x,msg->y)) {
		log_msg("push up left button shared\n");
		if (isDownBtn.toggleToSharedStash)
			if (PCPY->showSharedStash)
				updateServer(US_SELECT_SELF);
			else
				updateServer(US_SELECT_SHARED);

	} else if (isOnButtonPreviousIndexStash(msg->x,msg->y)) {
		log_msg("select left button previous index\n");
		if (isDownBtn.previousIndex)
			if (GetKeyState(VK_SHIFT)<0)
				updateServer(US_SELECT_PREVIOUS_INDEX2);
			else
				updateServer(US_SELECT_PREVIOUS_INDEX);

	} else if (isOnButtonNextIndexStash(msg->x,msg->y)) {
		log_msg("push up left button next index\n");
		if (isDownBtn.nextIndex)
			if (GetKeyState(VK_SHIFT)<0)
				updateServer(US_SELECT_NEXT_INDEX2);
			else
				updateServer(US_SELECT_NEXT_INDEX);

	} else if (active_sharedGold && isOnButtonPutGold(msg->x,msg->y)) {
		log_msg("push up left put gold\n");
		if (isDownBtn.putGold)
			updateServer(US_PUTGOLD);

	} else if (active_sharedGold && isOnButtonTakeGold(msg->x,msg->y)) {
		log_msg("push up left take gold\n");
		if (isDownBtn.takeGold)
			updateServer(US_TAKEGOLD);

	} else if (isOnStashNameField(msg->x,msg->y)) {
		log_msg("push up left Stash Name\n");
		if (isDownBtn.stashName && PCPY->currentStash)
		{
			if (GetKeyState(VK_SHIFT)<0)
			{
				WCHAR text[21];
				getCurrentStashName(text, 21, ptChar);
				D2TogglePage(5,0,0);// Open command window
				_snwprintf(ptInputCommand, 25, L"/rp %s", text);
				InputCommandLen = wcslen(ptInputCommand);
			}
			else if (PCPY->currentStash->isMainIndex)
				updateServer(US_RESET_INDEX);
			else if (PCPY->currentStash->isIndex)
				updateServer(US_SET_MAIN_INDEX);
			else
				updateServer(US_SET_INDEX);
		}

	} else return 0;

	return 1;
}

void FASTCALL printPageNumber(LPWSTR maxGoldText, DWORD x, DWORD y, DWORD color, DWORD bfalse)
{
	if (onRealm || !D2isLODGame() )
	{
		D2PrintString(maxGoldText,x,y,color,bfalse);
		return;
	}

	DWORD mx = D2GetMouseX();
	DWORD my = D2GetMouseY();
	Unit* ptChar = D2GetClientPlayer();
	WCHAR popupText[0x400];

	if (!PCPY->currentStash)
		D2PrintString(getLocalString(STR_NO_SELECTED_PAGE), x, y, WHITE, bfalse);
	else
	{
		bool isShared = PCPY->currentStash->isShared;
		bool isIndex = PCPY->currentStash->isIndex;
		bool isMainIndex = PCPY->currentStash->isMainIndex;
		DWORD currentId = PCPY->currentStash->id + 1;

		WCHAR text[21];
		getCurrentStashName(text, 21, ptChar);
		// Replace character # by number page.
		int j = 0;
		for (int i = 0; text[i]; i++)
		{
			if (text[i] == L'#')
			{
				DWORD val = currentId;
				int nbDigits = 0;
				do
				{
					nbDigits++;
					val /= 10;
				} while (val);
				j += nbDigits;

				val = currentId;
				nbDigits = 0;
				do
				{
					nbDigits++;
					popupText[j - nbDigits] = (WCHAR)(val % 10 + 48);
					val /= 10;
				} while (val);
			}
			else
				popupText[j++] = text[i];
		}
		popupText[j] = NULL;

		// Check text length vs field name length
		int pixelLen = D2GetPixelLen(popupText);
		int len = wcslen(popupText);
		while (len > 0 && pixelLen > posWStashNameField - 5)
		{
			len--;
			popupText[len] = NULL;
			pixelLen = D2GetPixelLen(popupText);
		}

		DWORD color = isShared ? (isMainIndex ? SharedMainIndexPageColor : isIndex ? SharedIndexPageColor : SharedNormalPageColor) : (isMainIndex ? PersonalMainIndexPageColor : isIndex ? PersonalIndexPageColor : PersonalNormalPageColor);

		// By HashCasper
		D2PrintString(popupText, 152, 605, color, bfalse);

		WCHAR HashString[] = L"Ironman Mod by Hash";
		D2PrintString(HashString, 152, 75, color, bfalse);

		// Print Random Tips
		
		// Define an array of tips
		const wchar_t* tips[] = {
			L"Consume spirits like potions to increase Spirits Quaffed stat in character stat sheet page.",
			L"Cube Souls with Energy Sphere to capture Soul Energy and use it to create powerful items. Recipes will come later.",
			L"Collect Demon souls from demons and regular souls from monsters; Demon souls morph you into the demon whose soul you have.",
			L"Spirits can be consumed like potions or put on as rings to add to Souls Consumed count.",
			L"Cube underused magic/rare/set/unique items with Energy Sphere to collect their magic energy for creating new items.",
			L"Cube white items with Energy Sphere to break down into simple item parts for creating base item types with smithing hammers.",
			L"Use Rare Shard, Set Stone, or Unique Particle with smithing hammers and Energy Sphere to create rare/set/unique items.",
			L"Acquire smithing hammers to create base item types, each requiring X number of collected item parts.",
			L"Capture Talisman of Corruption and cube to corrupt items for new magical properties; a gamble with potential rewards.",
			L"Use Experience Book for extra experience, Infinite Mana Elixir for massive mana regeneration, and Regeneration Elixir for life regeneration.",
			L"Use Elixirs to permanently boost stats like Strength, Dexterity, Health, Stamina, Mana, and resistances.",
			L"Equip Sling Stone for increased throwing potion damage, various elemental stones for resistance boosts, and Mastery Stones for skill damage boosts.",
			L"Use Portable Shrines for temporary buffs like increased defense, damage, experience gained, mana regeneration, and skill levels.",
			L"In Ironman, gold is nonexistent, town portals are disabled; rely on items, waypoints, and strategic gameplay to progress.",
			L"Inventory management is critical; use items conservatively and intelligently switch gear based on monster types and situations.",
			L"Hybrid character builds and creative weapon choices are encouraged in Ironman; experiment and adapt strategies accordingly.",
			L"Protect hirelings actively as they are essential allies; they can be hired for 0 gold but lose items if they die.",
			L"Coordinate with teammates in MultiPlayer; teamwork, resource sharing, and strategy are key to success in Ironman.",
			L"In Hardcore Ironman, caution and careful planning are crucial; falling back, potion management, and adaptability are essential for survival.",
			L"Utilize waypoints for inter-level travel in Ironman; waypoints become crucial for navigating the game world.",
			L"Always have backup gear in your backpack in Ironman; switch out gear intelligently based on monster types and challenges.",
			L"Experiment with different weapons and gear in Ironman; adapt your strategies to overcome various monster types and situations.",
			L"Keep a close watch on your hirelings' health in Ironman; losing a hireling means losing their equipped items as well.",
			L"Plan and communicate effectively in MultiPlayer Ironman games; teamwork and coordination are key to overcoming challenges.",
			L"Be conservative with resources in Hardcore Ironman; strategic planning and careful execution are vital for survival.",
			L"Craft rare/set/unique items using collected item parts and smithing hammers in Ironman; choose item types wisely for your build.",
			L"Capture and corrupt items using Talisman of Corruption for new magical properties in Ironman; take calculated risks for potential rewards.",
			L"Use Portable Shrines strategically in Ironman for temporary buffs; timing and placement of shrines can turn the tide of battle.",
			L"Keep track of Souls Consumed and Spirits Quaffed stats in Ironman; they reflect your progress and resource utilization in the game.",
			L"Enhance your character with Elixirs and Stones for permanent stat boosts and bonuses in Ironman; plan your upgrades wisely.",
			L"Master the art of retreating and regrouping in Hardcore Ironman; strategic fallbacks can save your character's life in tough situations.",
			L"Stay vigilant and adapt your strategies in MultiPlayer Ironman games; synergy and cooperation with teammates are essential for success.",
			L"Experiment with different skill synergies and builds in Ironman; explore the full potential of your character's abilities.",
			L"Craft and equip powerful unique items using collected Magic Essence Points and recipes in Ironman; customize your gear for optimal performance.",
			L"Explore new areas and challenges in Ironman; discover hidden secrets and encounters by venturing off the beaten path.",
			L"Combine different magic/rare/set/unique items with the Energy Sphere to collect their magic energy in Ironman.",
			L"Cube white items with the Energy Sphere to break them down into simple item parts for crafting in Ironman.",
			L"Use smithing hammers with the Energy Sphere to create base item types for crafting in Ironman.",
			L"Combine Rare Shard, Set Stone, or Unique Particle with base item types and Magic Essence Points to create rare/set/unique items in Ironman.",
			L"Cube Souls with the Energy Sphere to capture their energy and use it for creating more powerful items in Ironman.",

			L"+ to Amazon Skills increases the Minimum and Maximum skill levels for Amazon skills.",
			L"+ to Assassin Skills increases the Minimum and Maximum skill levels for Assassin skills.",
			L"+ to Barbarian Skills increases the Minimum and Maximum skill levels for Barbarian skills.",
			L"+ to Druid Skills increases the Minimum and Maximum skill levels for Druid skills.",
			L"+ to Necromancer Skills increases the Minimum and Maximum skill levels for Necromancer skills.",
			L"+ to Paladin Skills increases the Minimum and Maximum skill levels for Paladin skills.",
			L"+ to Sorceress Skills increases the Minimum and Maximum skill levels for Sorceress skills.",
			L"+ to All Skills increases the Minimum and Maximum skill levels for all skills.",
			L"+ to Fire Skills increases the Minimum and Maximum skill levels for Fire skills.",
			L"+ to Cold Skills increases the Minimum and Maximum skill levels for Cold skills.",
			L"+ to Lightning Skills increases the Minimum and Maximum skill levels for Lightning skills.",
			L"+ to Poison Skills increases the Minimum and Maximum skill levels for Poison skills.",
			L"+ to Magic Skills increases the Minimum and Maximum skill levels for Magic skills.",
			L"+ to Single Skills increases the Minimum and Maximum levels for a specific skill.",
			L"+ to Single Skill Tabs increases the Minimum and Maximum levels for a specific skill tab.",
			L"Chance to cast a skill when attacking triggers a specified skill with a certain chance and skill level.",
			L"Chance to cast a skill when hitting triggers a specified skill with a certain chance and skill level.",
			L"Chance to cast a skill when getting hit triggers a specified skill with a certain chance and skill level.",
			L"Skill Charges grant a certain number of charges to a specific skill with a designated skill level.",
			L"Using an Aura when equipped activates a specified aura skill with Minimum and Maximum levels.",
			L"Cast a skill when a monster is killed has a chance to trigger a specified skill at a certain skill level.",
			L"Cast a skill when a player is killed has a chance to trigger a specified skill at a certain skill level.",
			L"Cast a skill when a player level-up has a chance to trigger a specified skill at a certain skill level.",
			L"Spawns a random skill within a range of specified skills with corresponding skill levels.",
			L"+ to other char classes grants skills from other character classes with Minimum and Maximum levels.",

			L"+ to AC increases the Armor Class within a specified range.",
			L"+ X to AC per level increases Armor Class by a certain value per character level.",
			L"+ X to AC Based on Strength increases Armor Class based on Strength by a certain value.",
			L"+ X to AC Based on Dexterity increases Armor Class based on Dexterity by a certain value.",
			L"+% to AC increases Armor Class by a percentage within a specified range.",
			L"+% X to AC per level increases Armor Class by a certain percentage per character level.",
			L"+% X to AC Based on Strength increases Armor Class based on Strength by a certain percentage.",
			L"+% X to AC Based on Dexterity increases Armor Class based on Dexterity by a certain percentage.",
			L"+ to AC vs Melee increases Armor Class against melee attacks within a specified range.",
			L"+ to AC vs Missiles increases Armor Class against missile attacks within a specified range.",

			L"+ to Durability increases the durability of an item within a specified range.",
			L"+% to Durability increases the durability of an item by a percentage within a specified range.",
			L"Indestructible makes an item not lose durability.",
			L"Repair 1 Dur per 100/X seconds repairs an item's durability over time.",
			L"Repair 1 Qnt per 100/X seconds repairs an item's quantity over time.",
			L"Increase Stack by X amount increases the stack size of an item by a certain value.",

			L"+ to AR increases Attack Rating within a specified range.",
			L"+ Attack Rating per level increases Attack Rating by a certain value per character level.",
			L"+ Attack Rating based on Strength increases Attack Rating based on Strength by a certain value.",
			L"+ Attack Rating based on Dexterity increases Attack Rating based on Dexterity by a certain value.",
			L"+% to AR increases Attack Rating by a percentage within a specified range.",
			L"+% AR per level increases Attack Rating by a certain percentage per character level.",
			L"+% AR based on Strength increases Attack Rating based on Strength by a certain percentage.",
			L"+% AR based on Dexterity increases Attack Rating based on Dexterity by a certain percentage.",
			L"+ to AR vs Demons increases Attack Rating against Demons within a specified range.",
			L"+ to AR vs Undead increases Attack Rating against Undead within a specified range.",
			L"+% to AR vs Montype increases Attack Rating against a specified monster type within a specified range.",

			L"+ to Damage increases both Minimum and Maximum damage values for an item within a specified range.",
			L"+% to Damage increases both Minimum and Maximum damage values by a percentage within a specified range.",
			L"+ to Minimum Damage increases Minimum damage within a specified range.",
			L"+ to Minimum Damage per Level increases Minimum damage by a certain value per character level.",
			L"+ to Minimum Damage based on Strength increases Minimum damage based on Strength by a certain value.",
			L"+ to Minimum Damage based on Dexterity increases Minimum damage based on Dexterity by a certain value.",
			L"+% to Minimum Damage per level increases Minimum damage by a percentage per character level.",
			L"+ to Maximum Damage increases Maximum damage within a specified range.",
			L"+ to Maximum Damage per level increases Maximum damage by a certain value per character level.",
			L"+ to Maximum Damage based on Strength increases Maximum damage based on Strength by a certain value.",
			L"+ to Maximum Damage based on Dexterity increases Maximum damage based on Dexterity by a certain value.",
			L"+% to Maximum damage per level increases Maximum damage by a percentage per character level.",
			L"+% to Maximum Damage based on Strength increases Maximum damage based on Strength by a certain percentage.",
			L"+% to Maximum Damage based on Dexterity increases Maximum damage based on Dexterity by a certain percentage.",
			L"+ to Minimum & Maximum Damage increases both Minimum and Maximum damage values for an item within a specified range.",
			L"+% to Damage vs Demons increases damage against Demons within a specified range.",
			L"+% damage to demons per level increases damage against Demons by a certain percentage per character level.",
			L"+% to Damage vs Undead increases damage against Undead within a specified range.",
			L"+% damage to undead per level increases damage against Undead by a certain percentage per character level.",
			L"+% to crushing blow increases the chance to cause a crushing blow within a specified range.",
			L"+% crushing blow per level increases the chance to cause a crushing blow by a certain percentage per character level.",
			L"+% crushing blow based on Strength increases the chance to cause a crushing blow based on Strength by a certain percentage.",
			L"+% crushing blow based on Dexterity increases the chance to cause a crushing blow based on Dexterity by a certain percentage.",
			L"+% to deadly strike increases the chance to cause a deadly strike within a specified range.",
			L"+% deadly strike per level increases the chance to cause a deadly strike by a certain percentage per character level.",
			L"+% deadly strike based on Strength increases the chance to cause a deadly strike based on Strength by a certain percentage.",
			L"+% deadly strike Based on Dexterity increases the chance to cause a deadly strike based on Dexterity by a certain percentage.",
			L"+ to throw damage increases throwing damage within a specified range.",
			L"+% to Damage vs Montype increases damage against a specified monster type within a specified range.",

			L"Ignores Target Defense (ITD) ignores the target's defense.",
			L"+% pierce increases the chance to pierce enemy resistances within a specified range.",
			L"+% pierce based on Level increases the chance to pierce enemy resistances based on character level.",
			L"+% pierce based on Strength increases the chance to pierce enemy resistances based on Strength.",
			L"Knockback has a chance to knockback enemies on hit.",
			L"+% life stolen grants life steal within a specified range.",
			L"+% mana stolen grants mana steal within a specified range.",
			L"+# mana after each kill grants mana regeneration after each kill within a specified range.",
			L"+# after each demon kill grants health after each demon kill within a specified range.",
			L"Prevents monster healing prevents monsters from healing.",
			L"+% chance to cause open wounds increases the chance to cause open wounds within a specified range.",
			L"+% open wounds per level increases the chance to cause open wounds based on character level.",
			L"+% open wounds based on Strength increases the chance to cause open wounds based on Strength.",
			L"+% open wounds Based on Dexterity increases the chance to cause open wounds based on Dexterity.",
			L"Enemy Is slowed by X% slows enemies by a percentage within a specified range.",
			L"Enemy Is slowed by X% based on Level slows enemies based on character level.",
			L"Chance to Blind Target has a chance to blind the target.",
			L"Chance to Blind Target based on Level has a chance to blind the target based on character level.",
			L"Freeze Target X seconds freezes the target for a specified duration.",
			L"Freeze Target X seconds based on Level freezes the target based on character level.",
			L"Freeze Target X seconds based on Energy freezes the target based on character Energy.",
			L"Reduce AC by X% reduces the target's Armor Class by a percentage within a specified range.",
			L"Reduce AC by X reduces the target's Armor Class within a specified range.",
			L"+ to Kicking Damage increases kicking damage within a specified range.",
			L"+ Kick damage per level increases kicking damage based on character level.",
			L"+ Kick damage based on Strength increases kicking damage based on Strength.",
			L"+ Kick damage based on Dexterity increases kicking damage based on Dexterity.",
			L"+% chance to reanimate target increases the chance to reanimate a target within a specified range.",
			L"Reduces monster fire resistance reduces a monster's fire resistance within a specified range.",
			L"Reduces monster lightning resistance reduces a monster's lightning resistance within a specified range.",
			L"Reduces monster cold resistance reduces a monster's cold resistance within a specified range.",
			L"Reduces monster poison resistance reduces a monster's poison resistance within a specified range.",
			L"HP gained after every kill grants health regeneration after each kill within a specified range.",
			L"Corspe cannot be ressurected/Targeted prevents corpses from being resurrected or targeted.",

			L"+% damage taken goes to mana converts a percentage of damage taken to mana within a specified range.",
			L"Damage reduced by X amount reduces incoming damage by a specific amount within a specified range.",
			L"Damage reduced by X% reduces incoming damage by a percentage within a specified range.",
			L"Damage reduced by X% based on Level reduces incoming damage based on character level.",
			L"Damage reduced by X% based on Vitality reduces incoming damage based on character Vitality.",
			L"Magic damage reduced by X reduces incoming magic damage within a specified range.",
			L"Magic damage reduced by X per Level reduces incoming magic damage based on character level.",
			L"Magic damage reduced by X per Energy reduces incoming magic damage based on character Energy.",
			L"Attacker takes X damage when hitting causes an attacker to take damage when hitting.",
			L"Attacker takes X damage per level causes an attacker to take damage based on character level.",
			L"Attacker takes X ltng dmg when hitting causes an attacker to take lightning damage when hitting.",
			L"Attacker takes X fire dmg when hitting causes an attacker to take fire damage when hitting.",
			L"Attacker takes X cold dmg when hitting causes an attacker to take cold damage when hitting.",
			L"Attacker X chance to flee when hitting gives the attacker a chance to flee when hitting.",
			L"Attacker X chance get blinded when hitting gives the attacker a chance to get blinded when hitting.",

			L"Cold Damage adds cold damage to attacks or skills over a specified duration.",
			L"Minimum Cold Damage sets the minimum cold damage for attacks or skills.",
			L"Maximum Cold Damage sets the maximum cold damage for attacks or skills.",
			L"Cold Duration sets the duration of cold effects caused by attacks or skills.",
			L"Poison Damage adds poison damage to attacks or skills over a specified duration.",
			L"Minimum Poison Damage sets the minimum poison damage for attacks or skills.",
			L"Maximum Poison Damage sets the maximum poison damage for attacks or skills.",
			L"Poison Duration sets the duration of poison effects caused by attacks or skills.",
			L"Damage per Poison Level increases poison damage per character level.",
			L"Poison Length Reduction reduces the duration of poison effects caused by attacks or skills.",
			L"Poison Length Reduction per level reduces the duration of poison effects based on character level.",
			L"Poison Length Reduction per Vitality reduces the duration of poison effects based on character Vitality.",
			L"Fire Damage adds fire damage to attacks or skills over a specified duration.",
			L"Minimum Fire Damage sets the minimum fire damage for attacks or skills.",
			L"Maximum Fire Damage sets the maximum fire damage for attacks or skills.",
			L"Fire Duration sets the duration of fire effects caused by attacks or skills.",
			L"Fire Length Reduction reduces the duration of fire effects caused by attacks or skills.",
			L"Fire Length Reduction per level reduces the duration of fire effects based on character level.",
			L"Fire Length Reduction per Vitality reduces the duration of fire effects based on character Vitality.",
			L"Lightning Damage adds lightning damage to attacks or skills over a specified duration.",
			L"Minimum Lightning Damage sets the minimum lightning damage for attacks or skills.",
			L"Maximum Lightning Damage sets the maximum lightning damage for attacks or skills.",
			L"Lightning Duration sets the duration of lightning effects caused by attacks or skills.",
			L"Magic Damage adds magic damage to attacks or skills over a specified duration.",
			L"Minimum Magic Damage sets the minimum magic damage for attacks or skills.",
			L"Maximum Magic Damage sets the maximum magic damage for attacks or skills.",
			L"Magic Duration sets the duration of magic effects caused by attacks or skills.",
			L"Damage per Magic Level increases magic damage per character level.",
			L"Magic Resistance Reduction reduces enemy resistance against magic attacks.",
			L"Monster Flee X% causes monsters to flee with a certain chance.",
			L"Chance to create an explosion creates an explosion with a certain chance.",
			L"Fire explosive arrows causes arrows to explode on impact with a certain chance.",
			L"Half Freeze Duration halves the duration of freeze effects.",
			L"Cannot be Frozen prevents the character from being frozen.",
			L"+% to Resist All increases resistance to all damage types within a specified range.",
			L"+% to Resist All based on Level increases resistance to all damage types based on character level.",
			L"+% to Resist Cold increases resistance to cold damage within a specified range.",
			L"+% to Resist Cold per level increases resistance to cold damage based on character level.",
			L"+% to Resist Fire increases resistance to fire damage within a specified range.",
			L"+% to Resist Fire per level increases resistance to fire damage based on character level.",
			L"+% to Resist Lightning increases resistance to lightning damage within a specified range.",
			L"+% to Resist Lightning per level increases resistance to lightning damage based on character level.",
			L"+% to Resist Poison increases resistance to poison damage within a specified range.",
			L"+% to Resist Poison per level increases resistance to poison damage based on character level.",
			L"+% to Resist Magic increases resistance to magic damage within a specified range.",
			L"+% to Resist Magic based on Level increases resistance to magic damage based on character level.",
			L"+% to Resist Magic based on Energy increases resistance to magic damage based on character Energy.",
			L"+% to Maximum Resist All increases maximum resistance to all damage types within a specified range.",
			L"+% to Maximum Resist Cold increases maximum resistance to cold damage within a specified range.",
			L"+% to Maximum Resist Fire increases maximum resistance to fire damage within a specified range.",
			L"+% to Maximum Resist Lightning increases maximum resistance to lightning damage within a specified range.",
			L"+% to Maximum Resist Poison increases maximum resistance to poison damage within a specified range.",
			L"+% to Maximum Resist Magic increases maximum resistance to magic damage within a specified range.",
			L"Reduce Poison Duration reduces the duration of poison effects on the character.",
			L"Reduce Poison Duration per Level reduces the duration of poison effects based on character level.",
			L"Reduce Poison Duration per Vitality reduces the duration of poison effects based on character Vitality.",
			L"Reduce Curse Duration reduces the duration of curse effects on the character.",
			L"Reduce Curse Duration per Level reduces the duration of curse effects based on character level.",
			L"Reduce Curse Duration per Energy reduces the duration of curse effects based on character Energy.",

			L"Presence of 'res-all-max-hidden', 'res-all-hidden', 'res-all-max%', 'all-zero-display' on the same item displays and modifies all resistances.",
			L"+ to Dexterity increases Dexterity within a specified range.",
			L"+ to Dexterity per level increases Dexterity by a certain value per character level.",
			L"+% to Dexterity increases Dexterity by a percentage within a specified range.",
			L"+% to Dexterity per level increases Dexterity by a percentage per character level.",
			L"+ to Strength increases Strength within a specified range.",
			L"+ to Strength per level increases Strength by a certain value per character level.",
			L"+% to Strength increases Strength by a percentage within a specified range.",
			L"+% to Strength per level increases Strength by a percentage per character level.",
			L"+ to Vitality increases Vitality within a specified range.",
			L"+ to Vitality per level increases Vitality by a certain value per character level.",
			L"+% to Vitality increases Vitality by a percentage within a specified range.",
			L"+% to Vitality per level increases Vitality by a percentage per character level.",
			L"+ to Energy increases Energy within a specified range.",
			L"+ to Energy per level increases Energy by a certain value per character level.",
			L"+% to Energy increases Energy by a percentage within a specified range.",
			L"+% to Energy per level increases Energy by a percentage per character level.",
			L"Adds + to All stats increases all stats within a specified range.",
			L"+ to Health increases Health within a specified range.",
			L"+ to Health per level increases Health by a certain value per character level.",
			L"+ to Health based on Vitality increases Health based on Vitality by a certain value.",
			L"+% to Health increases Health by a percentage within a specified range.",
			L"+ to Mana increases Mana within a specified range.",
			L"+ to Mana per level increases Mana by a certain value per character level.",
			L"+ to Mana based on Energy increases Mana based on Energy by a certain value.",
			L"+% to Mana increases Mana by a percentage within a specified range.",
			L"+ to Replenish Life grants life replenishment within a specified range.",
			L"+% to Mana Regeneration increases Mana regeneration by a percentage within a specified range.",
			L"+% run/walk speed increases running and walking speed within a specified range.",
			L"+% run/walk speed based on Level increases running and walking speed based on character level.",
			L"+% run/walk speed based on Vitality increases running and walking speed based on Vitality.",
			L"+ to stamina increases Stamina within a specified range.",
			L"+ stamina per level increases Stamina by a certain value per character level.",
			L"+% reduce stamina drain decreases the rate at which stamina is drained within a specified range.",
			L"+% stamina regeneration increases stamina regeneration rate within a specified range.",
			L"+% regenerate stamina per level increases stamina regeneration rate based on character level.",
			L"+% attack speed increases attack speed within a specified range.",
			L"+% attack speed based on Level increases attack speed based on character level.",
			L"+% attack speed based on Strength increases attack speed based on Strength.",
			L"+% attack speed based on Dexterity increases attack speed based on Dexterity.",
			L"+% chance to block increases the chance to block within a specified range.",
			L"+% chance to block based on Level increases the chance to block based on character level.",
			L"+% chance to block based on Strength increases the chance to block based on Strength.",
			L"+% chance to block based on Dexterity increases the chance to block based on Dexterity.",
			L"+% block speed increases block speed within a specified range.",
			L"+% block speed based on Level increases block speed based on character level.",
			L"+% block speed based on Strength increases block speed based on Strength.",
			L"+% block speed based on Dexterity increases block speed based on Dexterity.",
			L"+% cast speed increases casting speed within a specified range.",
			L"+% cast speed based on Level increases casting speed based on character level.",
			L"+% cast speed based on Energy increases casting speed based on character Energy.",
			L"+% hit recovery speed increases hit recovery speed within a specified range.",
			L"+% hit recovery speed based on Level increases hit recovery speed based on character level.",
			L"+% hit recovery speed based on Vitality increases hit recovery speed based on Vitality.",
			L"+ to Light Radius increases the light radius within a specified range.",
			L"Fire Magic Arrows enables firing magical arrows.",
			L"Fire Explosive Arrows enables firing explosive arrows.",
			L"+% chance to find magic items increases the chance to find magic items within a specified range.",
			L"+ Magic Find per level increases the chance to find magic items based on character level.",
			L"+% increased gold drops increases the amount of gold dropped by monsters within a specified range.",
			L"+ More Gold per Level increases the amount of gold dropped by monsters based on character level.",
			L"Altered Item Requirements reduces the requirements to equip an item within a specified range.",
			L"Chance to make enemies flee increases the chance of causing enemies to flee within a specified range.",
			L"Add X Sockets to an Item adds a specific number of sockets to an item.",
			L"Makes any item throwable enables throwing any item.",
			L"Adds additional blood increases the amount of blood displayed when hitting enemies.",
			L"+% additional xp gained increases the amount of experience gained within a specified range.",
			L"+ additional xp gained per level increases the amount of experience gained based on character level.",
			L"+ additional xp gained based on Energy increases the amount of experience gained based on character Energy.",
			L"Reduce vendor cost reduces the cost of items bought from vendors within a specified range.",
			L"Reduce vendor cost based on Level reduces the cost of items based on character level.",
			L"Applies fade state changes the character to a faded state within a specified range.",
			L"Adds additional level requirements increases the level requirement to use an item.",
			L"Force spawns item as ethereal makes an item always spawn as an ethereal item."

		};

		// Calculate the number of tips in the array
		int numTips = sizeof(tips) / sizeof(tips[0]);

		// Seed the random number generator
		srand(static_cast<unsigned int>(time(nullptr)));

		// Generate a random index
		int randomIndex = rand() % numTips;

		// Print the randomly selected tip using D2PrintString function
		D2PrintString(const_cast<LPWSTR>(tips[randomIndex]), 152, 95, WHITE, 0);

	}
}

static Stash* curStash=NULL;
static DWORD currentSawStash=0;
Unit* STDCALL getNextItemForSet(Unit* ptItem)
{
	Unit* item = ptItem?D2UnitGetNextItem(ptItem):NULL;
	if (item) return item;

	if (!curStash)
	{
		Unit* ptChar = D2GetClientPlayer();
		switch (currentSawStash)
		{
		case 0: curStash = PCPY->selfStash;
				currentSawStash = displaySharedSetItemNameInGreen ? 1 : 2;
				break;
		case 1: curStash = PCPY->sharedStash;
				currentSawStash = 2;
				break;
		default:return NULL;
		}
	} else {
		curStash = curStash->nextStash;
	}
	if (curStash)
	{
		item = curStash->ptListItem;
		if (item) return item;
	}
	return getNextItemForSet(item);
}

Unit* STDCALL initGetNextItemForSet(Inventory* ptInventory)
{
	Unit* ptChar = D2GetClientPlayer();
	if (ptChar->nUnitType != UNIT_PLAYER) return NULL;
	if (!PCPY) return NULL;

	curStash = NULL;
	currentSawStash = 0;
	Unit* item = D2InventoryGetFirstItem(ptInventory);
	if (item) return item;
	return getNextItemForSet(item);
}

FCT_ASM( caller_manageBtnDown_114 )
	PUSH EBX
	CALL manageBtnDown
	TEST EAX,EAX
	JE IS_NOT_ON_BUTTON
	POP EDX
	MOV EDX, DWORD PTR DS:[EDX+0xA]
	MOV DWORD PTR DS:[EDX],1
	POP EDI
	POP ESI
	POP EBX
	MOV ESP,EBP
	POP EBP
	RETN 4
IS_NOT_ON_BUTTON:
	JMP D2ClickOnStashButton
}}

FCT_ASM( caller_manageBtnDown_111 )
	PUSH EBP
	CALL manageBtnDown
	TEST EAX,EAX
	JE IS_NOT_ON_BUTTON
	POP EDX
	MOV EDX, DWORD PTR DS:[EDX+0x10]
	MOV DWORD PTR DS:[EDX],1
	POP EDI
	POP ESI
	POP EBP
	POP EBX
	RETN 4
IS_NOT_ON_BUTTON:
	JMP D2ClickOnStashButton
}}

FCT_ASM( caller_manageBtnDown )
	PUSH EDI
	CALL manageBtnDown
	TEST EAX,EAX
	JE IS_NOT_ON_BUTTON
	POP EDX
	MOV EDX, DWORD PTR DS:[EDX+0xF3]
	MOV DWORD PTR DS:[EDX],1
	POP EDI
	POP ESI
	POP EBP
	POP EBX
	RETN 4
IS_NOT_ON_BUTTON:
	JMP D2isLODGame
}}

FCT_ASM( caller_manageBtnUp_114 )
	PUSH EBX
	CALL manageBtnUp
	MOV isDownBtn.all,0
	TEST EAX,EAX
	JE IS_NOT_ON_BUTTON
	POP EDX
	MOV EDX, DWORD PTR DS:[EDX+0x33]
	MOV DWORD PTR DS:[EDX],0
	SUB EDX,8
	MOV DWORD PTR DS:[EDX],0
	ADD EDX,4
	MOV DWORD PTR DS:[EDX],0
	ADD EDX,0x64
	MOV DWORD PTR DS:[EDX],0
	POP EDI
	POP ESI
	POP EBX
	MOV ESP,EBP
	POP EBP
	RETN 4
IS_NOT_ON_BUTTON:
	JMP D2ClickOnStashButton
}}

FCT_ASM( caller_manageBtnUp_111 )
	PUSH EBX
	CALL manageBtnUp
	MOV isDownBtn.all,0
	TEST EAX,EAX
	JE IS_NOT_ON_BUTTON
	POP EDX
	MOV EDX, DWORD PTR DS:[EDX+0x1A]
	MOV DWORD PTR DS:[EDX],0
	SUB EDX,8
	MOV DWORD PTR DS:[EDX],0
	ADD EDX,4
	MOV DWORD PTR DS:[EDX],0
	ADD EDX,0x68
	MOV DWORD PTR DS:[EDX],0
	POP EDI
	POP ESI
	POP EBP
	POP EBX
	POP ECX
	RETN 4
IS_NOT_ON_BUTTON:
	JMP D2ClickOnStashButton
}}

FCT_ASM( caller_manageBtnUp )
	PUSH EBP
	CALL manageBtnUp
	MOV isDownBtn.all,0
	TEST EAX,EAX
	JE IS_NOT_ON_BUTTON
	POP EDX
	MOV EDX, DWORD PTR DS:[EDX+0xEA]
	MOV DWORD PTR DS:[EDX],0
	SUB EDX,8
	MOV DWORD PTR DS:[EDX],0
	ADD EDX,4
	MOV DWORD PTR DS:[EDX],0
	ADD EDX,0x68
	MOV DWORD PTR DS:[EDX],0
	POP EDI
	POP ESI
	POP EBP
	POP EBX
	RETN 4
IS_NOT_ON_BUTTON:
	JMP D2isLODGame
}}

FCT_ASM ( initBtnsStates_114 )
	MOV isDownBtn.all,0
	POP ECX
	MOV ESP,EBP
	POP EBP
	RETN
}}

FCT_ASM ( initBtnsStates )
	MOV isDownBtn.all,0
	ADD ESP,0x104
	RETN
}}


void Install_InterfaceStash()
{
	static int isInstalled = false;
	if (isInstalled) return;

	Install_UpdateServer();
	Install_PlugYImagesFiles();

	log_msg("Patch D2Client for stash interface. (InterfaceStash)\n");

	// Print button images
	mem_seek R8(D2Client, 39060, 39060, 3F399, B1006, 7DF86, B3656, 99A56, 9DE26, 8F308);
	MEMC_REF4( D2LoadBuySelBtn, printBtns);
	//6FADF398  |. E8 734D0400    CALL D2Client.6FB24110
	//6FB61005  |. E8 A6D3FEFF    CALL D2Client.6FB4E3B0
	//6FB2DF85  |. E8 3642FEFF    CALL D2Client.6FB121C0
	//6FB63655  |. E8 9682FDFF    CALL D2Client.6FB3B8F0
	//6FB49A55  |. E8 96500200    CALL D2Client.6FB6EAF0
	//6FB4DE25  |. E8 76ACF7FF    CALL D2Client.6FAC8AA0
	//0048F307  |. E8 F452FCFF    CALL Game.00454600

	// print page number
	mem_seek R8(D2Client, 3903C, 3903C, 3F375, B0FE3, 7DF63, B3633, 99A33, 9DE03, 8F2E5);

	StartTimer();


	MEMJ_REF4( D2PrintString, printPageNumber);
	


	//6FADF374  |. E8 3FCC0800    CALL <JMP.&D2Win.#10117>
	//6FB60FE2  |. E8 99C2F5FF    CALL <JMP.&D2Win.#10020>
	//6FB2DF62  |. E8 19F3F8FF    CALL <JMP.&D2Win.#10064>
	//6FB63632  |. E8 299DF5FF    CALL <JMP.&D2Win.#10001>
	//6FB49A32  |. E8 4739F7FF    CALL <JMP.&D2Win.#10150>
	//6FB4DE02  |. E8 4739F7FF    CALL <JMP.&D2Win.#10076>
	//0048F2E4  |. E8 37300700    CALL Game.00502320                       ; \Game.00502320

	// Manage mouse down (Play sound)
	mem_seek R8(D2Client, 45091, 45091, 4BBA1, B4666, 7FDD6, B54A6, 9B8A6, 9FC76, 925B8);
	MEMC_REF4( version_D2Client >= V111 ? (DWORD)D2ClickOnStashButton : (DWORD)D2isLODGame, version_D2Client >= V114a ? caller_manageBtnDown_114 : version_D2Client >= V111 ? caller_manageBtnDown_111 : caller_manageBtnDown);
	//6FAEBBA0   > E8 DB04FCFF    CALL D2Client.6FAAC080
	//6FB64665   . E8 B61EFFFF    CALL D2Client.6FB56520
	//6FB2FDD5   . E8 E612FFFF    CALL D2Client.6FB210C0
	//6FB654A5   . E8 9611FFFF    CALL D2Client.6FB56640
	//6FB4B8A5   . E8 5614FFFF    CALL D2Client.6FB3CD00
	//6FB4FC75   . E8 960FFFFF    CALL D2Client.6FB40C10
	//004925B7  |. E8 C473FFFF    CALL Game.00489980

	// Manage mouse up
	mem_seek R8(D2Client, 455F9, 455F9, 4C0F9, B57B9, 7FC09, B52D9, 9B6D9, 9FAA9, 89B63);
	MEMC_REF4( version_D2Client >= V111 ? (DWORD)D2ClickOnStashButton : (DWORD)D2isLODGame, version_D2Client >= V114a ? caller_manageBtnUp_114 : version_D2Client >= V111 ? caller_manageBtnUp_111 : caller_manageBtnUp);
	//6FAEC0F8   > E8 83FFFBFF    CALL D2Client.6FAAC080
	//6FB657B8   > E8 630DFFFF    CALL D2Client.6FB56520
	//6FB2FC08   > E8 B314FFFF    CALL D2Client.6FB210C0
	//6FB652D8   > E8 6313FFFF    CALL D2Client.6FB56640
	//6FB4B6D8   > E8 2316FFFF    CALL D2Client.6FB3CD00
	//6FB4FAA8   > E8 6311FFFF    CALL D2Client.6FB40C10
	//00489B62  |> E8 19FEFFFF    CALL Game.00489980

	// init state of button on open stash page
	mem_seek R8(D2Client, 45B3A, 45B3A, 4C63A, A645A, 749DA, A9D9A, 8CC3A, 9441A, 89ED5);
	if (version_D2Client >= V114d)
	{
		memt_byte( 0x8B, 0xE9 );	// CALL initBtnsStates
		MEMT_REF4( 0xCCC35DE5, initBtnsStates_114);
	} else {
		memt_byte( 0x81, 0xE9 );	// CALL initBtnsStates
		MEMT_REF4( 0x000104C4, initBtnsStates);
		memt_byte( 0x00, 0x90 );
	}
	//6FAE5B3A  |> 81C4 04010000  ADD ESP,104
	//6FAE5B3A  |> 81C4 04010000  ADD ESP,104
	//6FAEC63A  |> 81C4 04010000  ADD ESP,104
	//6FB5645A  |> 81C4 04010000  ADD ESP,104
	//6FB249DA  |> 81C4 04010000  ADD ESP,104
	//6FB59D9A  |> 81C4 04010000  ADD ESP,104
	//6FB3CC3A  |> 81C4 04010000  ADD ESP,104
	//6FB4441A  |> 81C4 04010000  ADD ESP,104
	//00489ED5  |> 8BE5           MOV ESP,EBP
	//00489ED7  |. 5D             POP EBP
	//00489ED8  \. C3             RETN
	//00489ED9     CC             INT3

	// init the search of print in green the item set name we have in stash
	mem_seek R8(D2Client, 3F098, 3F098, 45997, A71F4, 71F64, A7314, 8D434, 91A24, 86774);
	MEMJ_REF4( D2InventoryGetFirstItem, initGetNextItemForSet);
	//6FAE5996  |. E8 D5500800    CALL <JMP.&D2Common.#10277>
	//6FB571F3  |. E8 D64EF6FF    CALL <JMP.&D2Common.#10402>
	//6FB21F63  |. E8 96A1F9FF    CALL <JMP.&D2Common.#10535>
	//6FB57313  |. E8 8250F6FF    CALL <JMP.&D2Common.#11151>
	//6FB3D433  |. E8 B8F0F7FF    CALL <JMP.&D2Common.#10460>
	//6FB41A23  |. E8 96A7F7FF    CALL <JMP.&D2Common.#11040>
	//00486773  |. E8 484B1B00    CALL Game.0063B2C0                       ; \Game.0063B2C0

	// Get next item for print in green the item set name we have in stash
	mem_seek R8(D2Client, 3F0FA, 3F0FA, 45A1C, A728B, 71FFB, A73AB, 8D4CB, 91ABB, 86807);
	MEMJ_REF4( D2UnitGetNextItem, getNextItemForSet);
	//6FAE5A1B   E8 4A500800      CALL <JMP.&D2Common.#10304>
	//6FB5728A  |. E8 0B4FF6FF    |CALL <JMP.&D2Common.#10934>
	//6FB21FFA  |. E8 A1A1F9FF    |CALL <JMP.&D2Common.#11140>
	//6FB573AA  |. E8 7550F6FF    |CALL <JMP.&D2Common.#10770>
	//6FB3D4CA  |. E8 99F0F7FF    |CALL <JMP.&D2Common.#10464>
	//6FB41ABA  |. E8 A3A8F7FF    |CALL <JMP.&D2Common.#10879>
	//00486806  |. E8 95771B00    |CALL Game.0063DFA0                      ; \Game.0063DFA0

	log_msg("\n");

	isInstalled = true;
}

/*================================= END OF FILE =================================*/