Source Development Multiple banks for the character.

zLuke

Well-known Pirate
Contributor
Developer
Registered
LV
4
 
Joined
Jan 29, 2026
Messages
103
Reaction score
94
Points
73
Age
41

Multiple banks for the character.



Libraries\common\include\CompCommand.h
Find
C++:
#define defESPE_KBGRID_NUM				4
Replace
C++:
#define defESPE_KBGRID_NUM				4

#define defDEF_BANK0_CAPACITY			defMAX_KBITEM_NUM_PER_TYPE	// main bank tab
#define defDEF_BANK1_CAPACITY			0	// bank tab 2
#define defDEF_BANK2_CAPACITY			0	// bank tab 3
#define defBANK_EXPAND_STEP				6	// slots per expand item use

inline short GetDefaultBankCapacity(char chBankNO)
{
	switch (chBankNO)
	{
	case 0: return defDEF_BANK0_CAPACITY;
	case 1: return defDEF_BANK1_CAPACITY;
	case 2: return defDEF_BANK2_CAPACITY;
	default: return defDEF_BANK0_CAPACITY;
	}
}

Server\GameServer\src\Player.h
Delete string
C++:
#define MAX_BANK_NUM	1
Server\GameServer\src\Player.cpp
Find
C++:
m_CBank[i].Init();
Replace
C++:
m_CBank[i].Init(GetDefaultBankCapacity(i));
Find
C++:
bool CPlayer::SynBank(char chBankNO, char chType)
Replace the entire function with
C++:
bool CPlayer::SynBank(char chBankNO, char chType)
{
	if (GetCurBankNum() == 0)
		return true;

	char chStart, chEnd;
	if (chBankNO < 0)
	{
		chStart = 0;
		chEnd = GetCurBankNum() - 1;
	}
	else
	{
		if (chBankNO >= GetCurBankNum())
			return false;
		chStart = chEnd = chBankNO;
	}

	for (char i = chStart; i <= chEnd; i++)
	{
		CKitbag* pCBank = GetBank(i);
		if (!pCBank)
			continue;

		WPACKET WtPk = GETWPACKET();
		WRITE_CMD(WtPk, CMD_MC_NOTIACTION);
		WRITE_LONG(WtPk, m_pCtrlCha->GetID());
		WRITE_LONG(WtPk, m_pCtrlCha->m_ulPacketID);
		WRITE_CHAR(WtPk, enumACTION_BANK);
		WRITE_CHAR(WtPk, i);
		m_pCtrlCha->WriteKitbag(*pCBank, WtPk, chType);
		m_pCtrlCha->ReflectINFof(m_pCtrlCha, WtPk);
	}

	return true;
}
Find
C++:
if (!SetBankChangeFlag(0))
Replace
C++:
if (!SetBankChangeFlag(-1))
Find
C++:
if (!SynBank(0))
Replace
C++:
if (!SynBank(-1))
Find
C++:
bool CPlayer::BankHasItem(USHORT sItemID, USHORT& sCount)
Replace the entire function with
C++:
bool CPlayer::BankHasItem(USHORT sItemID, USHORT& sCount)
{
	int nCount = 0;
	CItemRecord* pItem = GetItemRecordInfo( sItemID );
	if( pItem == NULL )
	{
		SystemNotice( RES_STRING(GM_PLAYER_CPP_00013), sItemID );
		return FALSE;
	}

	SItemGrid *pGridCont;
	for (char chBank = 0; chBank < m_chBankNum; chBank++)
	{
		USHORT sNum = m_CBank[chBank].GetUseGridNum();
		if( !pItem->GetIsPile() )
		{
			for( int i = 0; i < sNum; i++ )
			{
				pGridCont = m_CBank[chBank].GetGridContByNum( i );
				if( pGridCont && sItemID == pGridCont->sID )
				{
					nCount++;
					if( nCount >= sCount )
						return true;
				}
			}
		}
		else
		{
			for( int i = 0; i < sNum; i++ )
			{
				pGridCont = m_CBank[chBank].GetGridContByNum( i );
				if( pGridCont && sItemID == pGridCont->sID )
				{
					nCount += (USHORT)pGridCont->sNum;
					if( nCount >= sCount )
						return true;
				}
			}
		}
	}
	return nCount >= sCount;
}

Server\GameServer\src\GameDB.cpp
Find
C++:
sprintf(g_sql, "update %s set \
				bank=%s \
				where atorID=%d", \
				_get_table(), \
				szIDBuf, \
				atorID);
Replace
C++:
sprintf(g_sql, "update %s set \
				bank='%s' \
				where atorID=%d", \
				_get_table(), \
				szIDBuf, \
				atorID);
Find in CGameDB::ReadPlayer
C++:
if (lBankNum == 0)
		if (!_tab_cha->SaveBankDBID(pPlayer))
			return false;
Replace
C++:
if (lBankNum == 0)
	{
		if (!_tab_cha->SaveBankDBID(pPlayer))
			return false;
	}
	// Ensure MAX_BANK_NUM banks exist (online migration for old characters)
	while (pPlayer->GetCurBankNum() < MAX_BANK_NUM)
	{
		if (!CreatePlyBank(pPlayer))
			return false;
	}

Server\GameServer\src\Expand.h
Find
C++:
extern const char* GetResPath(const char *pszRes);
Replace
C++:
extern const char* GetResPath(const char *pszRes);

// Add personal bank tab capacity
// Args: character, bank index (0..MAX_BANK_NUM-1), capacity delta
// Returns: 1 success, 0 failure
inline int lua_AddBankCap(lua_State *pLS)
{
	bool	bSuccess = true;

#ifdef defPARSE_LOG
	g_pCLogObj->Log("AddBankCap\n");
#endif

	int nParaNum = lua_gettop(pLS);

	if (nParaNum != 3)
	{
#ifdef defPARSE_LOG
		g_pCLogObj->Log("\tThe parameter numbers [%d] is unlawful,transfer failed!\n", nParaNum);
#endif
		bSuccess = false;
		goto End;
	}
	{
		CCharacter* pCCha = (CCharacter*)lua_touserdata(pLS, 1);
		if (!pCCha)
		{
#ifdef defPARSE_LOG
			g_pCLogObj->Log("\tThe character object is inexistence,transfer failed ! \n");
#endif
			bSuccess = false;
			goto End;
		}

		char chBankNO = (char)lua_tonumber(pLS, 2);
		short sAddVal = (short)lua_tonumber(pLS, 3);
		if (!pCCha->AddBankCapacity(chBankNO, sAddVal))
		{
			bSuccess = false;
			goto End;
		}
	}
End:
	if (bSuccess)
	{
#ifdef defPARSE_LOG
		g_pCLogObj->Log("AddBankCapSucceed\n");
#endif
		lua_pushnumber(pLS, 1);
	}
	else
		lua_pushnumber(pLS, 0);

	return 1;
}

// Get personal bank tab capacity
// Args: character, bank index (0..MAX_BANK_NUM-1)
// Returns: capacity
inline int lua_GetBankCap(lua_State *pLS)
{
	bool	bSuccess = true;
	short	sCap = 0;

#ifdef defPARSE_LOG
	g_pCLogObj->Log("GetBankCap\n");
#endif

	int nParaNum = lua_gettop(pLS);

	if (nParaNum != 2)
	{
#ifdef defPARSE_LOG
		g_pCLogObj->Log("\tThe parameter numbers [%d] is unlawful,transfer failed!\n", nParaNum);
#endif
		bSuccess = false;
		goto End;
	}
	{
		CCharacter* pCCha = (CCharacter*)lua_touserdata(pLS, 1);
		if (!pCCha)
		{
#ifdef defPARSE_LOG
			g_pCLogObj->Log("\tThe character object is inexistence,transfer failed ! \n");
#endif
			bSuccess = false;
			goto End;
		}

		char chBankNO = (char)lua_tonumber(pLS, 2);
		sCap = pCCha->GetBankCapacity(chBankNO);
	}
End:
	if (bSuccess)
	{
#ifdef defPARSE_LOG
		g_pCLogObj->Log("GetBankCapSucceed\n");
#endif
	}

	lua_pushnumber(pLS, sCap);

	return 1;
}
Find
C++:
REGFN(GetKbCap);
Replace
C++:
REGFN(GetKbCap);
REGFN(AddBankCap);
REGFN(GetBankCap);

Server\GameServer\src\CharacterSuperCmd.cpp
Find
C++:
p = player->GetPlayer()->GetBank()->GetGridContByID(positionID)->SetInstAttr(ATTR_TYPE, ATTR_VALUE);
Replace
C++:
p = player->GetPlayer()->GetBank(0)->GetGridContByID(positionID)->SetInstAttr(ATTR_TYPE, ATTR_VALUE);

Server\GameServer\src\CharacterPrl.cpp
Find
C++:
case enumACTION_BANK:
		{
			Char	chSrcType = READ_CHAR(pk);
			Short	sSrcGrid = READ_SHORT(pk);
			Short	sSrcNum = READ_SHORT(pk);
			Char	chTarType = READ_CHAR(pk);
			Short	sTarGrid = READ_SHORT(pk);
			Short sRet;
			
			sRet = Cmd_BankOper(chSrcType, sSrcGrid, sSrcNum, chTarType, sTarGrid);
			
			if (sRet != enumITEMOPT_SUCCESS)
				ItemOprateFailed(sRet);
		}
		break;
Replace
C++:
case enumACTION_BANK:
		{
			Char	chSrcType = READ_CHAR(pk);
			Short	sSrcGrid = READ_SHORT(pk);
			Short	sSrcNum = READ_SHORT(pk);
			Char	chTarType = READ_CHAR(pk);
			Short	sTarGrid = READ_SHORT(pk);
			Char	chSrcBankNO = READ_CHAR(pk);
			Char	chTarBankNO = READ_CHAR(pk);
			Short sRet;
			
			sRet = Cmd_BankOper(chSrcType, sSrcGrid, sSrcNum, chTarType, sTarGrid, chSrcBankNO, chTarBankNO);
			
			if (sRet != enumITEMOPT_SUCCESS)
				ItemOprateFailed(sRet);
		}
		break;

Server\GameServer\src\CharacterCmd.cpp
Find
C++:
	pCSrcBag = GetPlayer()->GetBank();
	if (chSrcType == chTarType)
		pCTarBag = pCSrcBag;
	else
	{
Replace
C++:
pCSrcBag = GetPlayer()->GetBank(chSrcBankNO);
Find
C++:
pCTarBag = GetPlayer()->GetBank();
	}
	if (pCSrcBag->IsLock() || pCTarBag->IsLock())
		return enumITEMOPT_ERROR_KBLOCK;
Replace
C++:
pCTarBag = GetPlayer()->GetBank(chTarBankNO);

	if (!pCSrcBag || !pCTarBag)
		return enumITEMOPT_ERROR_NONE;

	if (pCSrcBag->IsLock() || pCTarBag->IsLock())
		return enumITEMOPT_ERROR_KBLOCK;
Find
C++:
return enumITEMOPT_ERROR_DISTANCE;
Replace
C++:
return enumITEMOPT_ERROR_DISTANCE;

	if (chTarType == 1 && pCSrcBag != pCTarBag && pCTarBag->GetCapacity() <= 0)
	{
		SystemNotice("This bank tab has no open slots. Expand it with an unlock item.");
		return enumITEMOPT_ERROR_KBFULL;
	}
Find
C++:
{
		GetPlayer()->SynBank(0, enumSYN_KITBAG_BANK);
		GetPlayer()->SetBankSaveFlag(0);
	}
	if (chSrcType != chTarType)
	{
		if (chTarType == 0)
			SynKitbagNew(enumSYN_KITBAG_BANK);
		else
		{
			GetPlayer()->SynBank(0, enumSYN_KITBAG_BANK);
			GetPlayer()->SetBankSaveFlag(0);
		}
}
Replace
C++:
{
		GetPlayer()->SynBank(chSrcBankNO, enumSYN_KITBAG_BANK);
		GetPlayer()->SetBankSaveFlag(chSrcBankNO);
	}
	if (chSrcType != chTarType || (chSrcType == 1 && chSrcBankNO != chTarBankNO))
	{
		if (chTarType == 0)
			SynKitbagNew(enumSYN_KITBAG_BANK);
		else
		{
			GetPlayer()->SynBank(chTarBankNO, enumSYN_KITBAG_BANK);
			GetPlayer()->SetBankSaveFlag(chTarBankNO);
		}
}

Server\GameServer\src\Character.h

Find
C++:
dbc::Short	Cmd_BankOper(dbc::Char chSrcType, dbc::Short sSrcGridID, dbc::Short sSrcNum, dbc::Char chTarType, dbc::Short sTarGridID);
Replace
C++:
dbc::Short	Cmd_BankOper(dbc::Char chSrcType, dbc::Short sSrcGridID, dbc::Short sSrcNum, dbc::Char chTarType, dbc::Short sTarGridID, dbc::Char chSrcBankNO = 0, dbc::Char chTarBankNO = 0);
Find
C++:
	bool			AddKitbagCapacity(dbc::Short sAddVal);
Replace
C++:
	bool			AddKitbagCapacity(dbc::Short sAddVal);
	bool			AddBankCapacity(char chBankNO, dbc::Short sAddVal);
	dbc::Short		GetBankCapacity(char chBankNO);

Server\GameServer\src\Character.cpp

Paste after string 933
C++:
bool CCharacter::AddBankCapacity(char chBankNO, dbc::Short sAddVal)
{
	CPlayer* pPlayer = GetPlayer();
	if (!pPlayer)
		return false;
	CKitbag* pBank = pPlayer->GetBank(chBankNO);
	if (!pBank)
		return false;
	if (sAddVal <= 0)
		return false;
	if (pBank->GetCapacity() >= defMAX_KBITEM_NUM_PER_TYPE)
		return false;
	if (!pBank->AddCapacity(sAddVal))
		return false;

	pPlayer->SetBankSaveFlag(chBankNO);
	pPlayer->SetBankChangeFlag(chBankNO, true);
	pPlayer->SynBank(chBankNO, enumSYN_KITBAG_INIT);
	return true;
}

dbc::Short CCharacter::GetBankCapacity(char chBankNO)
{
	CPlayer* pPlayer = GetPlayer();
	if (!pPlayer)
		return 0;
	CKitbag* pBank = pPlayer->GetBank(chBankNO);
	if (!pBank)
		return 0;
	return pBank->GetCapacity();
}

Client\src\UIGuildBankForm.cpp
Find
C++:
m_kNetBank.chSrcType = EQUIP_TYPE;
Replace
C++:
m_kNetBank.chSrcType = 0;
Find
C++:
m_kNetBank.chTarType = BANK_TYPE;
m_kNetBank.sTarID = nGridID;
Replace
C++:
m_kNetBank.chTarType = 1;
m_kNetBank.sTarID = nGridID;
m_kNetBank.chSrcBankNO = 0;
m_kNetBank.chTarBankNO = 0;
Find
C++:
m_kNetBank.chTarType = EQUIP_TYPE;
m_kNetBank.sTarID = nGridID;
Replace
C++:
m_kNetBank.chTarType = EQUIP_TYPE;
m_kNetBank.sTarID = nGridID;
m_kNetBank.chSrcBankNO = 0;
m_kNetBank.chTarBankNO = 0;

Client\src\UIGoodsGrid.cpp
Find
C++:
#include "packetcmd.h"
Replace
C++:
#include "packetcmd.h"
#include "UIBankForm.h"
Find
C++:
CCompent* p = form->GetHitCommand( x, y );     
		if( !p ) return;
Replace
C++:
CCompent* p = form->GetHitCommand( x, y );     
		if( !p )
		{
			g_stUIBank.TryAcceptDropOnTab(this, x, y, _nDragIndex);
			return;
		}
Find
C++:
case enumRefuse:
			break;
		}
Replace
C++:
case enumRefuse:
			g_stUIBank.TryAcceptDropOnTab(this, x, y, _nDragIndex);
			break;
		}

Client\src\UIGlobalVar.cpp
Find
C++:
&& pDrag == g_stUIBank.GetBankGoodsGrid())
Replace
C++:
&& g_stUIBank.IsBankGrid(pDrag))
Find
C++:
if ( pSelf == g_stUIBank.GetBankGoodsGrid()
		&& pDrag == g_stUIEquip.GetGoodsGrid())
	{
		g_stUIBank.PushToBank(*pDrag, *pSelf, nGridID, *pItem);
	}
Replace
C++:
if ( g_stUIBank.IsBankGrid(pSelf)
		&& pDrag == g_stUIEquip.GetGoodsGrid())
	{
		g_stUIBank.PushToBank(*pDrag, *pSelf, nGridID, *pItem);
	}

	// Bank tab -> bank tab
	if ( g_stUIBank.IsBankGrid(pSelf)
		&& g_stUIBank.IsBankGrid(pDrag)
		&& pSelf != pDrag )
	{
		g_stUIBank.BankToBank(*pDrag, *pSelf, nGridID, *pItem);
	}

Client\src\UIBankForm.h

Replace the entire file
C++:
//--------------------------------------------------------------
// Bank UI manager (multi-tab personal bank)
//--------------------------------------------------------------
#pragma once

#include "UIGlobalVar.h"
#include "NetProtocol.h"
#include "CompCommand.h"

namespace GUI
{
	class CForm;
	class CGoodsGrid;
	class CItemCommand;
	class CLabel;
	class CPage;
	class CGuiData;

	struct stNumBox;

	class CBankMgr : public CUIInterface
	{
	public:
		void		ShowBank();
		CGoodsGrid* GetBankGoodsGrid(char chBankNO = -1);
		bool		IsBankGrid(CGoodsGrid* pGrid) const;
		char		GetBankNO(CGoodsGrid* pGrid) const;
		char		GetActiveBankNO() const { return m_chActiveBankNO; }

		bool		PushToBank(CGoodsGrid& rkDrag, CGoodsGrid& rkSelf, int nGridID, CCommandObj& rkItem);
		bool		PopFromBank(CGoodsGrid& rkDrag, CGoodsGrid& rkSelf, int nGridID, CCommandObj& rkItem);
		bool		BankToBank(CGoodsGrid& rkDrag, CGoodsGrid& rkSelf, int nGridID, CCommandObj& rkItem);

		/** Drop item onto a bank tab title: move into that tab's first free slot. */
		bool		TryAcceptDropOnTab(CGoodsGrid* pSrc, int x, int y, int nSrcIndex);

	protected:
		virtual bool Init();
		virtual void CloseForm();

	private:
		static void _MoveItemsEvent(CCompent *pSender, int nMsgType, int x, int y, DWORD dwKey);
		static void _MoveAItemEvent(CCompent *pSender, int nMsgType, int x, int y, DWORD dwKey);
		static void _evtBankToBank(CGuiData *pSender, int nFirst, int nSecond, bool& isSwap);
		static void _evtOnClose(CForm* pForm, bool& IsClose);
		static void _evtSelectPage(CGuiData *pSender);

		void		_SyncGridVisibility();
		char		HitBankTabTitle(int x, int y) const;
		int			FindFreeBankSlot(char chBankNO) const;
		bool		_IsBannedBankItem(CItemCommand* pkItemCmd) const;

	private:
		stNumBox*		m_pkNumberBox;
		stNetBank		m_kNetBank;

		CForm*			frmBank;
		CPage*			pgeBank;
		CGoodsGrid*		grdBanks[MAX_BANK_NUM];
		CGuiData*		labBankTabs[MAX_BANK_NUM];
		CLabel*			labCharName;
		char			m_chActiveBankNO;
	};

} // namespace GUI

Client\src\UIBankForm.cpp

Replace the entire file
C++:
#include "StdAfx.h"

#include "UIBankForm.h"
#include "uiform.h"
#include "uilabel.h"
#include "uiformmgr.h"
#include "uigoodsgrid.h"
#include "NetProtocol.h"
#include "uiboxform.h"
#include "uiEquipForm.h"
#include "UIGoodsGrid.h"
#include "uiItemCommand.h"
#include "uiform.h"
#include "uiBoatForm.h"
#include "packetcmd.h"
#include "Character.h"
#include "GameApp.h"
#include "UIPage.h"
#include "uiimage.h"

namespace GUI
{
	bool CBankMgr::Init()
	{
		CFormMgr &mgr = CFormMgr::s_Mgr;

		frmBank = mgr.Find("frmNPCstorage");
		if ( !frmBank)
		{
			LG("gui", g_oLangRec.GetString(438));
			return false;
		}
		frmBank->evtClose = _evtOnClose;

		for (int i = 0; i < MAX_BANK_NUM; i++)
		{
			grdBanks[i] = nullptr;
			labBankTabs[i] = nullptr;
		}
		m_chActiveBankNO = 0;
		memset(&m_kNetBank, 0, sizeof(m_kNetBank));
		m_pkNumberBox = nullptr;

		pgeBank = dynamic_cast<CPage*>(frmBank->Find("pgeBank"));
		if (pgeBank)
		{
			pgeBank->evtSelectPage = _evtSelectPage;
		}

		char szGridName[32];
		char szLabName[32];
		for (int i = 0; i < MAX_BANK_NUM; i++)
		{
			sprintf(szGridName, "grdNPCstorage%d", i);
			grdBanks[i] = dynamic_cast<CGoodsGrid*>(frmBank->Find(szGridName));
			if (!grdBanks[i])
			{
				// Legacy single-grid NPC.clu fallback
				if (i == 0)
				{
					grdBanks[0] = dynamic_cast<CGoodsGrid*>(frmBank->Find("grdNPCstorage"));
				}
				if (!grdBanks[i])
				{
					return Error(g_oLangRec.GetString(439), frmBank->GetName(), szGridName);
				}
			}
			grdBanks[i]->evtBeforeAccept = CUIInterface::_evtDragToGoodsEvent;
			grdBanks[i]->evtSwapItem = _evtBankToBank;

			sprintf(szLabName, "labBankTab%d", i);
			labBankTabs[i] = frmBank->Find(szLabName);
		}

		labCharName = dynamic_cast<CLabel*>(frmBank->Find("labOwnerName"));
		if (!labCharName)
		{
			return Error(g_oLangRec.GetString(439), frmBank->GetName(), "labOwnerName");
		}

		// Grids stay in form._mouse after UI_AddCompent; hide inactive so hit-test
		// (GetHitCommand) does not always pick grdNPCstorage0.
		_SyncGridVisibility();

		return true;
	}

	void CBankMgr::_SyncGridVisibility()
	{
		for (int i = 0; i < MAX_BANK_NUM; i++)
		{
			if (grdBanks[i])
			{
				grdBanks[i]->SetIsShow(i == m_chActiveBankNO);
			}
		}
	}

	CGoodsGrid* CBankMgr::GetBankGoodsGrid(char chBankNO)
	{
		if (chBankNO < 0)
		{
			chBankNO = m_chActiveBankNO;
		}
		if (chBankNO < 0 || chBankNO >= MAX_BANK_NUM)
		{
			return nullptr;
		}
		return grdBanks[chBankNO];
	}

	bool CBankMgr::IsBankGrid(CGoodsGrid* pGrid) const
	{
		if (!pGrid)
		{
			return false;
		}
		for (int i = 0; i < MAX_BANK_NUM; i++)
		{
			if (grdBanks[i] == pGrid)
			{
				return true;
			}
		}
		return false;
	}

	char CBankMgr::GetBankNO(CGoodsGrid* pGrid) const
	{
		for (int i = 0; i < MAX_BANK_NUM; i++)
		{
			if (grdBanks[i] == pGrid)
			{
				return static_cast<char>(i);
			}
		}
		return -1;
	}

	void CBankMgr::_evtSelectPage(CGuiData *pSender)
	{
		CPage* pPage = dynamic_cast<CPage*>(pSender);
		if (!pPage)
		{
			return;
		}
		int nIndex = pPage->GetIndex();
		if (nIndex >= 0 && nIndex < MAX_BANK_NUM)
		{
			g_stUIBank.m_chActiveBankNO = static_cast<char>(nIndex);
			g_stUIBank._SyncGridVisibility();
		}
	}

	void CBankMgr::_evtOnClose( CForm* pForm, bool& IsClose )
	{
		CS_BeginAction(g_stUIBoat.GetHuman(), enumACTION_CLOSE_BANK, NULL); 
		CFormMgr::s_Mgr.SetEnableHotKey(HOTKEY_BANK, true);
	}

	void CBankMgr::ShowBank()
	{
		if (!g_stUIBoat.GetHuman())
			return;

		char szBuf[32];
		sprintf(szBuf, "%s%s", g_stUIBoat.GetHuman()->getName(), g_oLangRec.GetString(440));
		labCharName->SetCaption(szBuf);

		if (pgeBank)
		{
			pgeBank->SetIndex(m_chActiveBankNO);
		}
		_SyncGridVisibility();

		frmBank->Show();

		if (!g_stUIEquip.GetItemForm()->GetIsShow())
		{
			int nLeft = frmBank->GetX2();
			int nTop = frmBank->GetY();

			g_stUIEquip.GetItemForm()->SetPos(nLeft, nTop);
			g_stUIEquip.GetItemForm()->Refresh();
			g_stUIEquip.GetItemForm()->Show();
		}

		CFormMgr::s_Mgr.SetEnableHotKey(HOTKEY_BANK, false);
	}

	bool CBankMgr::PushToBank(CGoodsGrid& rkDrag, CGoodsGrid& rkSelf, int nGridID, CCommandObj& rkItem)
	{
		// Always use the visible tab: form hit-test can return a hidden sibling grid.
		const char chBankNO = m_chActiveBankNO;
		if (chBankNO < 0 || chBankNO >= MAX_BANK_NUM)
		{
			return false;
		}

		CGoodsGrid* pBankGrid = GetBankGoodsGrid(chBankNO);
		if (!pBankGrid)
		{
			return false;
		}
		CItemCommand* pTarItem = dynamic_cast<CItemCommand*>(pBankGrid->GetItem(nGridID));
		if (pTarItem && pTarItem->GetItemInfo() && pTarItem->GetItemInfo()->lID == 32767)
		{
			g_pGameApp->SysInfo("This bank slot is locked. Expand the tab first.");
			return false;
		}

		m_kNetBank.chSrcType = 0;
		m_kNetBank.sSrcID = rkDrag.GetDragIndex();
		m_kNetBank.chTarType = 1;
		m_kNetBank.sTarID = nGridID;
		m_kNetBank.chSrcBankNO = 0;
		m_kNetBank.chTarBankNO = chBankNO;

		CItemCommand* pkItemCmd = dynamic_cast<CItemCommand*>(&rkItem);
		if (!pkItemCmd)	return false;
		CItemRecord* pkItemRecord = pkItemCmd->GetItemInfo();
		if (!pkItemRecord)	return false;

		if( pkItemRecord->lID == 2520 || pkItemRecord->lID == 2521 || pkItemRecord->lID == 6341 || pkItemRecord->lID == 6343
		 || pkItemRecord->lID == 6347 || pkItemRecord->lID == 6359 || pkItemRecord->lID == 6370 || pkItemRecord->lID == 6371
		 || pkItemRecord->lID == 6373 || pkItemRecord->lID >= 6376 && pkItemRecord->lID <= 6378
		 || pkItemRecord->lID >= 6383 && pkItemRecord->lID <= 6385 )
		{
			g_pGameApp->MsgBox(g_oLangRec.GetString(958));
			return false;
		}
		if ( pkItemCmd->GetItemInfo()->GetIsPile() && pkItemCmd->GetTotalNum() > 1 )
		{
			m_pkNumberBox = 
				g_stUIBox.ShowNumberBox(_MoveItemsEvent, pkItemCmd->GetTotalNum(), g_oLangRec.GetString(441), false);

			if (m_pkNumberBox->GetNumber() < pkItemCmd->GetTotalNum())
				return false;
			else
				return true;
		}
		else
		{
			g_stUIBank.m_kNetBank.sSrcNum = 1;
			CS_BeginAction(g_stUIBoat.GetHuman(), enumACTION_BANK, (void*)&(g_stUIBank.m_kNetBank));
			return true;
		}
	}

	bool CBankMgr::PopFromBank(CGoodsGrid& rkDrag, CGoodsGrid& rkSelf, int nGridID, CCommandObj& rkItem)
	{
		const char chBankNO = m_chActiveBankNO;
		if (chBankNO < 0 || chBankNO >= MAX_BANK_NUM)
		{
			return false;
		}

		m_kNetBank.chSrcType = 1;
		m_kNetBank.sSrcID = rkDrag.GetDragIndex();
		m_kNetBank.chTarType = 0;
		m_kNetBank.sTarID = nGridID;
		m_kNetBank.chSrcBankNO = chBankNO;
		m_kNetBank.chTarBankNO = 0;

		CItemCommand* pkItemCmd = dynamic_cast<CItemCommand*>(&rkItem);
		if (!pkItemCmd)	return false;

		if ( pkItemCmd->GetItemInfo()->GetIsPile() && pkItemCmd->GetTotalNum() > 1 )
		{
			m_pkNumberBox = 
				g_stUIBox.ShowNumberBox( _MoveItemsEvent, pkItemCmd->GetTotalNum(), g_oLangRec.GetString(442), false);

			if (m_pkNumberBox->GetNumber() < pkItemCmd->GetTotalNum())
				return false;
			else
				return true;
		}
		else
		{
			g_stUIBank.m_kNetBank.sSrcNum = 1;
			CS_BeginAction(g_stUIBoat.GetHuman(), enumACTION_BANK, (void*)&(g_stUIBank.m_kNetBank));
			return true;
		}
	}

	void CBankMgr::_MoveItemsEvent(CCompent *pSender, int nMsgType, int x, int y, DWORD dwKey)
	{
		if(nMsgType != CForm::mrYes)
			return;

		int num =  g_stUIBank.m_pkNumberBox->GetNumber();
		if ( num > 0 )
		{
			g_stUIBank.m_kNetBank.sSrcNum = num;
			CS_BeginAction(g_stUIBoat.GetHuman(), enumACTION_BANK, (void*)&(g_stUIBank.m_kNetBank));
		}
	}

	void CBankMgr::_MoveAItemEvent(CCompent *pSender, int nMsgType, int x, int y, DWORD dwKey)
	{
		if(nMsgType != CForm::mrYes) 
			return;

		g_stUIBank.m_kNetBank.sSrcNum = 1;
		CS_BeginAction(g_stUIBoat.GetHuman(), enumACTION_BANK, (void*)&(g_stUIBank.m_kNetBank));
	}
	
	void CBankMgr::CloseForm()
	{
		if (frmBank->GetIsShow())
		{
			frmBank->Close();
			g_stUIEquip.GetItemForm()->Close();
		}
	}

	void CBankMgr::_evtBankToBank(CGuiData *pSender,int nFirst, int nSecond, bool& isSwap)
	{
		isSwap = false;
		if( !g_stUIBoat.GetHuman() ) return;

		const char chBankNO = g_stUIBank.m_chActiveBankNO;
		if (chBankNO < 0 || chBankNO >= MAX_BANK_NUM)
		{
			return;
		}

		CGoodsGrid* pGrid = g_stUIBank.GetBankGoodsGrid(chBankNO);
		if (pGrid)
		{
			CItemCommand* pFirst = dynamic_cast<CItemCommand*>(pGrid->GetItem(nFirst));
			CItemCommand* pSecond = dynamic_cast<CItemCommand*>(pGrid->GetItem(nSecond));
			if ((pFirst && pFirst->GetItemInfo() && pFirst->GetItemInfo()->lID == 32767)
				|| (pSecond && pSecond->GetItemInfo() && pSecond->GetItemInfo()->lID == 32767))
			{
				g_pGameApp->SysInfo("This bank slot is locked. Expand the tab first.");
				return;
			}
		}

		g_stUIBank.m_kNetBank.chSrcType = 1;
		g_stUIBank.m_kNetBank.sSrcID = nSecond;
		g_stUIBank.m_kNetBank.sSrcNum = 0; 
		g_stUIBank.m_kNetBank.chTarType = 1;
		g_stUIBank.m_kNetBank.sTarID = nFirst;
		g_stUIBank.m_kNetBank.chSrcBankNO = chBankNO;
		g_stUIBank.m_kNetBank.chTarBankNO = chBankNO;

		CS_BeginAction(g_stUIBoat.GetHuman(), enumACTION_BANK, (void*)&(g_stUIBank.m_kNetBank));
	}

	bool CBankMgr::BankToBank(CGoodsGrid& rkDrag, CGoodsGrid& rkSelf, int nGridID, CCommandObj& rkItem)
	{
		const char chSrcBankNO = GetBankNO(&rkDrag);
		const char chTarBankNO = GetBankNO(&rkSelf);
		if (chSrcBankNO < 0 || chTarBankNO < 0)
		{
			return false;
		}

		CItemCommand* pTarItem = dynamic_cast<CItemCommand*>(rkSelf.GetItem(nGridID));
		if (pTarItem && pTarItem->GetItemInfo() && pTarItem->GetItemInfo()->lID == 32767)
		{
			g_pGameApp->SysInfo("This bank slot is locked. Expand the tab first.");
			return false;
		}

		m_kNetBank.chSrcType = 1;
		m_kNetBank.sSrcID = rkDrag.GetDragIndex();
		m_kNetBank.sSrcNum = 1;
		m_kNetBank.chTarType = 1;
		m_kNetBank.sTarID = nGridID;
		m_kNetBank.chSrcBankNO = chSrcBankNO;
		m_kNetBank.chTarBankNO = chTarBankNO;

		CItemCommand* pkItemCmd = dynamic_cast<CItemCommand*>(&rkItem);
		if (!pkItemCmd) return false;

		if ( pkItemCmd->GetItemInfo()->GetIsPile() && pkItemCmd->GetTotalNum() > 1 )
		{
			m_pkNumberBox =
				g_stUIBox.ShowNumberBox( _MoveItemsEvent, pkItemCmd->GetTotalNum(), g_oLangRec.GetString(442), false);
			if (m_pkNumberBox->GetNumber() < pkItemCmd->GetTotalNum())
				return false;
			return true;
		}

		CS_BeginAction(g_stUIBoat.GetHuman(), enumACTION_BANK, (void*)&m_kNetBank);
		return true;
	}

	bool CBankMgr::_IsBannedBankItem(CItemCommand* pkItemCmd) const
	{
		if (!pkItemCmd)
		{
			return true;
		}
		CItemRecord* pkItemRecord = pkItemCmd->GetItemInfo();
		if (!pkItemRecord)
		{
			return true;
		}
		const long id = pkItemRecord->lID;
		if (id == 2520 || id == 2521 || id == 6341 || id == 6343
			|| id == 6347 || id == 6359 || id == 6370 || id == 6371
			|| id == 6373 || (id >= 6376 && id <= 6378)
			|| (id >= 6383 && id <= 6385))
		{
			return true;
		}
		return false;
	}

	char CBankMgr::HitBankTabTitle(int x, int y) const
	{
		if (!frmBank || !frmBank->GetIsShow())
		{
			return -1;
		}

		if (pgeBank)
		{
			for (int i = 0; i < MAX_BANK_NUM; i++)
			{
				CPageItem* pItem = pgeBank->GetPage(static_cast<unsigned int>(i));
				if (!pItem)
				{
					continue;
				}
				CImage* pTitle = pItem->GetTitle();
				if (pTitle && pTitle->InRect(x, y))
				{
					return static_cast<char>(i);
				}
			}
		}

		for (int i = 0; i < MAX_BANK_NUM; i++)
		{
			if (labBankTabs[i] && labBankTabs[i]->InRect(x, y))
			{
				return static_cast<char>(i);
			}
		}
		return -1;
	}

	int CBankMgr::FindFreeBankSlot(char chBankNO) const
	{
		CGoodsGrid* pGrid = nullptr;
		if (chBankNO < 0 || chBankNO >= MAX_BANK_NUM)
		{
			return -1;
		}
		pGrid = grdBanks[chBankNO];
		if (!pGrid)
		{
			return -1;
		}

		const int nMax = pGrid->GetMaxNum();
		for (int i = 0; i < nMax; i++)
		{
			CCommandObj* pCmd = pGrid->GetItem(static_cast<unsigned int>(i));
			if (!pCmd)
			{
				return i;
			}
			CItemCommand* pItem = dynamic_cast<CItemCommand*>(pCmd);
			if (pItem && pItem->GetItemInfo() && pItem->GetItemInfo()->lID == 32767)
			{
				continue;
			}
		}
		return -1;
	}

	bool CBankMgr::TryAcceptDropOnTab(CGoodsGrid* pSrc, int x, int y, int nSrcIndex)
	{
		if (!pSrc || !frmBank || !frmBank->GetIsShow() || !g_stUIBoat.GetHuman())
		{
			return false;
		}
		if (nSrcIndex < 0)
		{
			return false;
		}

		const char chTarBankNO = HitBankTabTitle(x, y);
		if (chTarBankNO < 0 || chTarBankNO >= MAX_BANK_NUM)
		{
			return false;
		}

		CGoodsGrid* pInv = g_stUIEquip.GetGoodsGrid();
		const bool bFromInv = (pSrc == pInv);
		const bool bFromBank = IsBankGrid(pSrc);
		if (!bFromInv && !bFromBank)
		{
			return false;
		}

		char chSrcBankNO = 0;
		if (bFromBank)
		{
			chSrcBankNO = GetBankNO(pSrc);
			if (chSrcBankNO < 0)
			{
				return false;
			}
			if (chSrcBankNO == chTarBankNO)
			{
				return false;
			}
		}

		CItemCommand* pkItemCmd = dynamic_cast<CItemCommand*>(pSrc->GetItem(static_cast<unsigned int>(nSrcIndex)));
		if (!pkItemCmd)
		{
			return false;
		}

		if (bFromInv && _IsBannedBankItem(pkItemCmd))
		{
			g_pGameApp->MsgBox(g_oLangRec.GetString(958));
			return true; // consumed drop (ban message), do not throw
		}

		const int nFree = FindFreeBankSlot(chTarBankNO);
		if (nFree < 0)
		{
			g_pGameApp->SysInfo("This bank tab has no open slots.");
			return true;
		}

		if (bFromInv)
		{
			m_kNetBank.chSrcType = 0;
			m_kNetBank.chSrcBankNO = 0;
			m_kNetBank.chTarType = 1;
			m_kNetBank.chTarBankNO = chTarBankNO;
		}
		else
		{
			m_kNetBank.chSrcType = 1;
			m_kNetBank.chSrcBankNO = chSrcBankNO;
			m_kNetBank.chTarType = 1;
			m_kNetBank.chTarBankNO = chTarBankNO;
		}
		m_kNetBank.sSrcID = static_cast<short>(nSrcIndex);
		m_kNetBank.sTarID = static_cast<short>(nFree);

		if (pkItemCmd->GetItemInfo()->GetIsPile() && pkItemCmd->GetTotalNum() > 1)
		{
			const char* pszTip = bFromInv ? g_oLangRec.GetString(441) : g_oLangRec.GetString(442);
			m_pkNumberBox =
				g_stUIBox.ShowNumberBox(_MoveItemsEvent, pkItemCmd->GetTotalNum(), pszTip, false);
			if (m_pkNumberBox->GetNumber() < pkItemCmd->GetTotalNum())
			{
				return true;
			}
			return true;
		}

		m_kNetBank.sSrcNum = 1;
		CS_BeginAction(g_stUIBoat.GetHuman(), enumACTION_BANK, (void*)&m_kNetBank);
		return true;
	}

}

Client\src\ProCirculateCS.cpp

Find
C++:
case	enumACTION_BANK:
			{
				stNetBank	*pNetBank = (stNetBank *)param;
				pk.WriteChar(pNetBank->chSrcType);
				pk.WriteShort(pNetBank->sSrcID);
				pk.WriteShort(pNetBank->sSrcNum);
				pk.WriteChar(pNetBank->chTarType);
				pk.WriteShort(pNetBank->sTarID);
				pCNetIf->SendPacketMessage(pk);

                LG(szLogName, "###Send(Bank Req):\tTick:[%d]\n", GetTickCount());
                LG(szLogName, "\n");
				break;
			}
Replace
C++:
case	enumACTION_BANK:
			{
				stNetBank	*pNetBank = (stNetBank *)param;
				pk.WriteChar(pNetBank->chSrcType);
				pk.WriteShort(pNetBank->sSrcID);
				pk.WriteShort(pNetBank->sSrcNum);
				pk.WriteChar(pNetBank->chTarType);
				pk.WriteShort(pNetBank->sTarID);
				pk.WriteChar(pNetBank->chSrcBankNO);
				pk.WriteChar(pNetBank->chTarBankNO);
				pCNetIf->SendPacketMessage(pk);

                LG(szLogName, "###Send(Bank Req):\tTick:[%d]\n", GetTickCount());
                LG(szLogName, "\n");
				break;
			}

Client\src\PacketCmd_SC.cpp

Find
C++:
case enumACTION_BANK:
			{
				stNetKitbag SKitbag;
				ReadChaKitbagPacket(pk, SKitbag, szLogName);
				SKitbag.chBagType = 1;
				NetChangeKitbag(l_id, SKitbag);
			}
Replace
C++:
case enumACTION_BANK:
			{
				char chBankNO = pk.ReadChar();
				stNetKitbag SKitbag;
				ReadChaKitbagPacket(pk, SKitbag, szLogName);
				SKitbag.chBagType = 1;
				SKitbag.chBankNO = chBankNO;
				NetChangeKitbag(l_id, SKitbag);
			}

Client\src\NetProtocol.h

Find
C++:
struct stNetKitbag
{
	char	chBagType;
	char	chType;
    int     nKeybagNum;
    struct stGrid
    {
	    short	    sGridID;
	    SItemGrid	SGridContent;
    };
    int     nGridNum;
    stGrid  Grid[defMAX_KBITEM_NUM_PER_TYPE];     
};
Replace
C++:
struct stNetKitbag
{
	char	chBagType;
	char	chBankNO;
	char	chType;
    int     nKeybagNum;
    struct stGrid
    {
	    short	    sGridID;
	    SItemGrid	SGridContent;
    };
    int     nGridNum;
    stGrid  Grid[defMAX_KBITEM_NUM_PER_TYPE];     
};

Client\src\NetProtocol.cpp

Find
C++:
case 1:
		grd = g_stUIBank.GetBankGoodsGrid();
Replace
C++:
case 1:
		grd = g_stUIBank.GetBankGoodsGrid(SKitbag.chBankNO);
Find
C++:
case enumSYN_KITBAG_INIT:
        {
			//todo
			if(SKitbag.chBagType == 0){
Replace
C++:
case enumSYN_KITBAG_INIT:
        {
			if(SKitbag.chBagType == 0 || SKitbag.chBagType == 1){
Find
C++:
case enumSYN_KITBAG_INIT:

		if (SKitbag.chBagType == 1)
Replace
C++:
case enumSYN_KITBAG_INIT:

		if (SKitbag.chBagType == 1 && SKitbag.chBankNO == 0)
Find
C++:
void NetKitbagLockedSpaces(short slots,CGoodsGrid* grd){
Replace the entire function with
C++:
void NetKitbagLockedSpaces(short slots,CGoodsGrid* grd){
	int lockedID = 32767;
	CItemRecord* blankItem = GetItemRecordInfo( lockedID );
	if(!grd || !blankItem){
		return;
	}

	const int maxSlots = 48;
	if(slots < 0){
		slots = 0;
	}
	if(slots > maxSlots){
		slots = maxSlots;
	}

	grd->Clear();
	grd->SetContent( 8, 6 );
	grd->Init();
	grd->Refresh();

	for (int index = slots; index < maxSlots; index++){
		CItemCommand* pObj = new CItemCommand( blankItem );
		pObj->SetCanDrag(false);
		grd->SetItem( index, pObj );
	}
}

server\GameServer\resource\ItemInfo.txt
Client\scripts\table\iteminfo.txt

C++:
15410	2 Bank Slot	n1320_2	10130005	0	0	0	0	0	00	31	0	0	0	0	0	1	1	1	1	1	0	100	-1	0	-1	0	0	-1	-1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0	0	0,0	0	0	0	0	0	0	0	0	0	BankTab2Slot	0	0	0	0	0	0	Increases the second bank tab by 6 cells.	1
15411	3 Bank Slot	n1321_2	10130005	0	0	0	0	0	00	31	0	0	0	0	0	1	1	1	1	1	0	100	-1	0	-1	0	0	-1	-1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0,0	0	0	0,0	0	0	0	0	0	0	0	0	0	BankTab3Slot	0	0	0	0	0	0	Increases the third bank tab by 6 cells.	1

server\GameServer\resource\script\calculate\ItemEffect.lua
C++:
-- Bank tab 2 expand (+6 slots per use, chBankNO = 1). Wire ItemInfo szEffect to BankTab2Slot.
function BankTab2Slot(role, Item)
    local Cha_Boat = GetCtrlBoat(role)
    if Cha_Boat ~= nil then
        SystemNotice(role, "Cannot use while sailing!")
        UseItemFailed(role)
        return
    end
    role = TurnToCha(role)
    local cap = GetBankCap(role, 1)
    if cap >= 48 then
        SystemNotice(role, "Bank tab 2 is already fully expanded!")
        UseItemFailed(role)
        return
    end
    if AddBankCap(role, 1, 6) ~= 1 then
        SystemNotice(role, "Failed to expand bank tab 2!")
        UseItemFailed(role)
        return
    end
end

-- Bank tab 3 expand (+6 slots per use, chBankNO = 2). Wire ItemInfo szEffect to BankTab3Slot.
function BankTab3Slot(role, Item)
    local Cha_Boat = GetCtrlBoat(role)
    if Cha_Boat ~= nil then
        SystemNotice(role, "Cannot use while sailing!")
        UseItemFailed(role)
        return
    end
    role = TurnToCha(role)
    local cap = GetBankCap(role, 2)
    if cap >= 48 then
        SystemNotice(role, "Bank tab 3 is already fully expanded!")
        UseItemFailed(role)
        return
    end
    if AddBankCap(role, 2, 6) ~= 1 then
        SystemNotice(role, "Failed to expand bank tab 3!")
        UseItemFailed(role)
        return
    end
end

Client\scripts\lua\forms\NPC.clu
Find form
C++:
frmNPCstorage
Replace the entire form script with
C++:
frmNPCstorage = UI_CreateForm( "frmNPCstorage", FALSE, 242, 400, 100, 100, TRUE, FALSE )
UI_ShowForm( frmNPCstorage, TRUE )
UI_AddFormToTemplete( frmNPCstorage, FORM_MAIN )
UI_SetIsDrag( frmNPCstorage, TRUE )

imgBakTop = UI_CreateCompent( frmNPCstorage, IMAGE_TYPE, "imgBakTop", 242, 400, 0, 0 )
UI_LoadImage( imgBakTop, "texture/ui/corsairs/bankform48.png", NORMAL, 242, 400, 0, 0 )

labTitle = UI_CreateCompent( frmNPCstorage, LABELEX_TYPE, "labTitle", 40, 12, 10, 2 )
UI_SetCaption( labTitle, "Bank")
UI_SetTextColor( labTitle, COLOR_BLACK )
UI_SetLabelExFont( labTitle, DEFAULT_FONT, TRUE, COLOR_WHITE )

btnClose = UI_CreateCompent( frmNPCstorage, BUTTON_TYPE, "btnClose", 14, 14, 225, 1 )
UI_LoadButtonImage( btnClose, "texture/ui/PublicC.tga", 14, 14, 116, 175, TRUE )
UI_SetButtonModalResult( btnClose, BUTTON_CLOSE )

labOwnerName = UI_CreateCompent( frmNPCstorage, LABELEX_TYPE, "labOwnerName", 100, 12, 50, 2 )
UI_SetCaption( labOwnerName, "" )
UI_SetTextColor( labOwnerName, COLOR_BLACK )
UI_SetLabelExFont( labOwnerName, DEFAULT_FONT, TRUE, COLOR_WHITE )

-- Grids: local (0,0); after UI_AddCompent parent is page at (17,55)
-- Height 280: 8 rows * (32 unit + 3 space) so all locked slots render (277 only fits 7 rows)
grdNPCstorage0 = UI_CreateCompent( frmNPCstorage, GOODS_GRID_TYPE, "grdNPCstorage0", 207, 280, 0, 0 )
UI_SetGridSpace( grdNPCstorage0, 3, 3 )
UI_SetGridContent( grdNPCstorage0, 8, 6 )
UI_SetGridUnitSize( grdNPCstorage0, 32, 32 )
UI_SetMargin( grdNPCstorage0, 0, 0, 0, 0 )

grdNPCstorage1 = UI_CreateCompent( frmNPCstorage, GOODS_GRID_TYPE, "grdNPCstorage1", 207, 280, 0, 0 )
UI_SetGridSpace( grdNPCstorage1, 3, 3 )
UI_SetGridContent( grdNPCstorage1, 8, 6 )
UI_SetGridUnitSize( grdNPCstorage1, 32, 32 )
UI_SetMargin( grdNPCstorage1, 0, 0, 0, 0 )

grdNPCstorage2 = UI_CreateCompent( frmNPCstorage, GOODS_GRID_TYPE, "grdNPCstorage2", 207, 280, 0, 0 )
UI_SetGridSpace( grdNPCstorage2, 3, 3 )
UI_SetGridContent( grdNPCstorage2, 8, 6 )
UI_SetGridUnitSize( grdNPCstorage2, 32, 32 )
UI_SetMargin( grdNPCstorage2, 0, 0, 0, 0 )

-- Page frame = exact white grid area on bankform48.png
pgeBank = UI_CreateCompent( frmNPCstorage, PAGE_TYPE, "pgeBank", 207, 280, 17, 55 )
UI_SetPageButton( pgeBank, PAGE_BUTTON_CUSTOM, 48, 18 )

-- Tab sprites live in bankform48 atlas (right margin), ivntab-style
local tabTex = "texture/ui/corsairs/bankform48.png"
local tabAtlasX = 242
local tabW = 51
local tabH = 28
local tabX = 4
local tabXoff = 54
local tabY = -38

itemid = UI_CreatePageItem( pgeBank )
ttlBank0 = UI_GetPageItemObj( itemid, PAGE_ITEM_TITLE )
UI_LoadImage( ttlBank0, tabTex, PAGE_ITEM_TITLE_ACTIVE, tabW, tabH, tabAtlasX, 0 )
UI_LoadImage( ttlBank0, tabTex, PAGE_ITEM_TITLE_NORMAL, tabW, tabH, tabAtlasX + tabW - 1, 0 )
UI_SetPos( ttlBank0, tabX, tabY )
UI_SetSize( ttlBank0, tabW, tabH )
UI_AddCompent( itemid, grdNPCstorage0 )

itemid = UI_CreatePageItem( pgeBank )
ttlBank1 = UI_GetPageItemObj( itemid, PAGE_ITEM_TITLE )
UI_LoadImage( ttlBank1, tabTex, PAGE_ITEM_TITLE_ACTIVE, tabW, tabH, tabAtlasX, 0 )
UI_LoadImage( ttlBank1, tabTex, PAGE_ITEM_TITLE_NORMAL, tabW, tabH, tabAtlasX + tabW - 1, 0 )
UI_SetPos( ttlBank1, tabX + tabXoff, tabY )
UI_SetSize( ttlBank1, tabW, tabH )
UI_AddCompent( itemid, grdNPCstorage1 )

itemid = UI_CreatePageItem( pgeBank )
ttlBank2 = UI_GetPageItemObj( itemid, PAGE_ITEM_TITLE )
UI_LoadImage( ttlBank2, tabTex, PAGE_ITEM_TITLE_ACTIVE, tabW, tabH, tabAtlasX, 0 )
UI_LoadImage( ttlBank2, tabTex, PAGE_ITEM_TITLE_NORMAL, tabW, tabH, tabAtlasX + tabW - 1, 0 )
UI_SetPos( ttlBank2, tabX + tabXoff * 2, tabY )
UI_SetSize( ttlBank2, tabW, tabH )
UI_AddCompent( itemid, grdNPCstorage2 )

-- Numbers on tabs (header strip y~28)
labBankTab0 = UI_CreateCompent( frmNPCstorage, LABELEX_TYPE, "labBankTab0", 16, 12, 38, 26 )
UI_SetCaption( labBankTab0, "1" )
UI_SetTextColor( labBankTab0, COLOR_BLACK )
UI_SetLabelExFont( labBankTab0, DEFAULT_FONT, TRUE, COLOR_WHITE )
labBankTab1 = UI_CreateCompent( frmNPCstorage, LABELEX_TYPE, "labBankTab1", 16, 12, 92, 26 )
UI_SetCaption( labBankTab1, "2" )
UI_SetTextColor( labBankTab1, COLOR_BLACK )
UI_SetLabelExFont( labBankTab1, DEFAULT_FONT, TRUE, COLOR_WHITE )
labBankTab2 = UI_CreateCompent( frmNPCstorage, LABELEX_TYPE, "labBankTab2", 16, 12, 146, 26 )
UI_SetCaption( labBankTab2, "3" )
UI_SetTextColor( labBankTab2, COLOR_BLACK )
UI_SetLabelExFont( labBankTab2, DEFAULT_FONT, TRUE, COLOR_WHITE )

Compile common, GameServer, Client
Compile clu scripts
Compile client table
Copy texture in client folder
Enjoy!!!
 

Attachments

Can you do one with more inventory slots? it would be nice to have a lot of inventory slots as well. thanks for this file.
 
Ppl will ask for the reputation Store/map timer/boss timer and more stuffs, good release, i havent tested, but good work bro
 
Ppl will ask for the reputation Store/map timer/boss timer and more stuffs, good release, i havent tested, but good work bro
all of that already in released files , rep shop/store shop same thing u just change currency type, and init item when switching pages
 
yes but new ppl will ask for full guide, where and what to replace, there was someone with compiled bins trying to edit with hex even when theres the full source….