3 mesaje
Membru Ucenic Membru Ucenic
  • Mesaje: 15
  • Reacții: 2
  • Mesaje utile: 0
  • Status: ^.^
  • Server: (Regal) // Trot-OSBD
  • Regat: Chunjo
  • [ID Discord]
  • Contact:

    Medalii

    *Problemă/Întrebare:
    Cum rezolv și eu asta, că orice linie aș modifica, mi se crează bug:
    Ori nu-mi merge skill bar-ul,
    Ori nu-mi merg unele pătrățele din inventar,
    Ori când apăs pe shop, se deschide, dau reload p și după ajung la login,
    Ori ajung la login și primesc game.core


    *Resursă folosită (sistem/funcție/modificare):
    personala 🤷‍♂️ (PlayGame)
    Sursă game - C++17
    Sursă binary - vs22
    Client - varză - eter
    ----------------------
    *Syserr:
    [Problemă] /reload p - cu îmbunătățiri - Mesaj 1 - Imagine 1

    [Problemă] /reload p - cu îmbunătățiri - Mesaj 1 - Imagine 2

    ----------------------
    input.cpp:
    Code:
    #include "stdafx.h"
    #include <sstream>
    
    #include "desc.h"
    #include "desc_manager.h"
    #include "char.h"
    #include "buffer_manager.h"
    #include "config.h"
    #include "profiler.h"
    #include "p2p.h"
    #include "log.h"
    #include "db.h"
    #include "questmanager.h"
    #include "login_sim.h"
    #include "fishing.h"
    #include "TrafficProfiler.h"
    #include "priv_manager.h"
    #include "castle.h"
    #include "dev_log.h"
    
    #ifndef __WIN32__
    	#include "limit_time.h"
    #endif
    
    extern time_t get_global_time();
    extern bool g_bNoPasspod;
    
    bool IsEmptyAdminPage()
    {
    	return g_stAdminPageIP.empty();
    }
    
    bool IsAdminPage(const char * ip)
    {
    	for (size_t n = 0; n < g_stAdminPageIP.size(); ++n)
    	{
    		if (g_stAdminPageIP[n] == ip)
    			return 1; 
    	}	
    	return 0;
    }
    
    void ClearAdminPages()
    {
    	for (size_t n = 0; n < g_stAdminPageIP.size(); ++n)
    		g_stAdminPageIP[n].clear();
    
    	g_stAdminPageIP.clear();
    }
    
    CInputProcessor::CInputProcessor() : m_pPacketInfo(NULL), m_iBufferLeft(0)
    {
    	if (!m_pPacketInfo)
    		BindPacketInfo(&m_packetInfoCG);
    }
    
    void CInputProcessor::BindPacketInfo(CPacketInfo * pPacketInfo)
    {
    	m_pPacketInfo = pPacketInfo;
    }
    
    bool CInputProcessor::Process(LPDESC lpDesc, const void * c_pvOrig, int iBytes, int & r_iBytesProceed)
    {
    	const char * c_pData = (const char *) c_pvOrig;
    
    	BYTE	bLastHeader = 0;
    	int		iLastPacketLen = 0;
    	int		iPacketLen;
    
    	if (!m_pPacketInfo)
    	{
    		sys_err("No packet info has been binded to");
    		return true;
    	}
    
    	for (m_iBufferLeft = iBytes; m_iBufferLeft > 0;)
    	{
    		BYTE bHeader = (BYTE) *(c_pData);
    		const char * c_pszName;
    
    		if (bHeader == 0) // 암호화 처리가 있으므로 0번 헤더는 스킵한다.
    			iPacketLen = 1;
    		else if (!m_pPacketInfo->Get(bHeader, &iPacketLen, &c_pszName))
    		{
    			sys_err("UNKNOWN HEADER: %d, LAST HEADER: %d(%d), REMAIN BYTES: %d, fd: %d",
    					bHeader, bLastHeader, iLastPacketLen, m_iBufferLeft, lpDesc->GetSocket());
    			//printdata((BYTE *) c_pvOrig, m_iBufferLeft);
    			lpDesc->SetPhase(PHASE_CLOSE);
    			return true;
    		}
    
    		if (m_iBufferLeft < iPacketLen)
    			return true;
    
    		if (bHeader)
    		{
    			if (test_server && bHeader != HEADER_CG_MOVE)
    				sys_log(0, "Packet Analyze [Header %d][bufferLeft %d] ", bHeader, m_iBufferLeft);
    
    			m_pPacketInfo->Start();
    
    			int iExtraPacketSize = Analyze(lpDesc, bHeader, c_pData);
    
    			if (iExtraPacketSize < 0)
    				return true;
    
    			iPacketLen += iExtraPacketSize;
    			lpDesc->Log("%s %d", c_pszName, iPacketLen);
    			m_pPacketInfo->End();
    		}
    
    		// TRAFFIC_PROFILER
    		if (g_bTrafficProfileOn)
    			TrafficProfiler::instance().Report(TrafficProfiler::IODIR_INPUT, bHeader, iPacketLen);
    		// END_OF_TRAFFIC_PROFILER
    
    		if (bHeader == HEADER_CG_PONG)
    			sys_log(0, "PONG! %u %u", m_pPacketInfo->IsSequence(bHeader), *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE)));
    
    		if (m_pPacketInfo->IsSequence(bHeader))
    		{
    			BYTE bSeq = lpDesc->GetSequence();
    			BYTE bSeqReceived = *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE));
    
    			if (bSeq != bSeqReceived)
    			{
    				sys_err("SEQUENCE %x mismatch 0x%x != 0x%x header %u", get_pointer(lpDesc), bSeq, bSeqReceived, bHeader);
    
    				LPCHARACTER	ch = lpDesc->GetCharacter();
    
    				char buf[1024];
    				int	offset, len;
    
    				offset = snprintf(buf, sizeof(buf), "SEQUENCE_LOG [%s]-------------\n", ch ? ch->GetName() : "UNKNOWN");
    
    				if (offset < 0 || offset >= (int) sizeof(buf))
    					offset = sizeof(buf) - 1;
    
    				for (size_t i = 0; i < lpDesc->m_seq_vector.size(); ++i)
    				{
    					len = snprintf(buf + offset, sizeof(buf) - offset, "\t[%03d : 0x%x]\n",
    							lpDesc->m_seq_vector[i].hdr,
    							lpDesc->m_seq_vector[i].seq);
    
    					if (len < 0 || len >= (int) sizeof(buf) - offset)
    						offset += (sizeof(buf) - offset) - 1;
    					else
    						offset += len;
    				}
    
    				snprintf(buf + offset, sizeof(buf) - offset, "\t[%03d : 0x%x]\n", bHeader, bSeq);
    				sys_err("%s", buf);
    
    				lpDesc->SetPhase(PHASE_CLOSE);
    				return true;
    			}
    			else
    			{
    				lpDesc->push_seq(bHeader, bSeq);
    				lpDesc->SetNextSequence();
    				//sys_err("SEQUENCE %x match %u next %u header %u", lpDesc, bSeq, lpDesc->GetSequence(), bHeader);
    			}
    		}
    
    		c_pData	+= iPacketLen;
    		m_iBufferLeft -= iPacketLen;
    		r_iBytesProceed += iPacketLen;
    
    		iLastPacketLen = iPacketLen;
    		bLastHeader	= bHeader;
    
    		if (GetType() != lpDesc->GetInputProcessor()->GetType())
    			return false;
    	}
    
    	return true;
    }
    
    void CInputProcessor::Pong(LPDESC d)
    {
    	d->SetPong(true);
    
    	extern bool Metin2Server_IsInvalid();
    
    #ifdef ENABLE_LIMIT_TIME
    	if (Metin2Server_IsInvalid())
    	{
    		extern bool g_bShutdown;
    		g_bShutdown = true;
    		ClearAdminPages();
    	}
    #endif
    }
    
    void CInputProcessor::Handshake(LPDESC d, const char * c_pData)
    {
    	TPacketCGHandshake * p = (TPacketCGHandshake *) c_pData;
    
    	if (d->GetHandshake() != p->dwHandshake)
    	{
    		sys_err("Invalid Handshake on %d", d->GetSocket());
    		d->SetPhase(PHASE_CLOSE);
    	}
    	else
    	{
    		if (d->IsPhase(PHASE_HANDSHAKE))
    		{
    			if (d->HandshakeProcess(p->dwTime, p->lDelta, false))
    			{
    #ifdef _IMPROVED_PACKET_ENCRYPTION_
    				d->SendKeyAgreement();
    #else
    				if (g_bAuthServer)
    					d->SetPhase(PHASE_AUTH);
    				else
    					d->SetPhase(PHASE_LOGIN);
    #endif // #ifdef _IMPROVED_PACKET_ENCRYPTION_
    			}
    		}
    		else
    			d->HandshakeProcess(p->dwTime, p->lDelta, true);
    	}
    }
    
    void CInputProcessor::Version(LPCHARACTER ch, const char* c_pData)
    {
    	if (!ch)
    	{
    		return;
    	}
    
    	TPacketCGClientVersion * p = (TPacketCGClientVersion *) c_pData;
    
    	// If the file name is not metin2client.exe and the GM level is not equal with GM_IMPLEMENTOR kick the player
    	if (strcmp(p->filename, "Trot-OSBD.exe") && ch->GetGMLevel() != GM_IMPLEMENTOR)
    	{
    		// immediately close the connection with the player
    		sys_err("%s[%d] has been disconnected: %s", ch->GetName(), ch->GetPlayerID(),  p->filename);
    		ch->GetDesc()->SetPhase(PHASE_CLOSE);
    		return;
    	}
    
    	sys_log(0, "VERSION: %s %s %s", ch->GetName(), p->timestamp, p->filename);
    	ch->GetDesc()->SetClientVersion(p->timestamp);
    }
    
    void LoginFailure(LPDESC d, const char * c_pszStatus)
    {
    	if (!d)
    		return;
    
    	TPacketGCLoginFailure failurePacket;
    
    	failurePacket.header = HEADER_GC_LOGIN_FAILURE;
    	strlcpy(failurePacket.szStatus, c_pszStatus, sizeof(failurePacket.szStatus));
    
    	d->Packet(&failurePacket, sizeof(failurePacket));
    }
    
    CInputHandshake::CInputHandshake()
    {
    	CPacketInfoCG * pkPacketInfo = M2_NEW CPacketInfoCG;
    	pkPacketInfo->SetSequence(HEADER_CG_PONG, false);
    
    	m_pMainPacketInfo = m_pPacketInfo;
    	BindPacketInfo(pkPacketInfo);
    }
    
    CInputHandshake::~CInputHandshake()
    {
    	if( NULL != m_pPacketInfo )
    	{
    		M2_DELETE(m_pPacketInfo);
    		m_pPacketInfo = NULL;
    	}
    }
    
    
    std::map<DWORD, CLoginSim *> g_sim;
    std::map<DWORD, CLoginSim *> g_simByPID;
    std::vector<TPlayerTable> g_vec_save;
    
    // BLOCK_CHAT
    ACMD(do_block_chat);
    // END_OF_BLOCK_CHAT
    
    int CInputHandshake::Analyze(LPDESC d, BYTE bHeader, const char * c_pData)
    {
    	if (bHeader == 10) // 엔터는 무시
    		return 0;
    
    	if (bHeader == HEADER_CG_TEXT)
    	{
    		++c_pData;
    		const char * c_pSep;
    
    		if (!(c_pSep = strchr(c_pData, '\n')))	// \n을 찾는다.
    			return -1;
    
    		if (*(c_pSep - 1) == '\r')
    			--c_pSep;
    
    		std::string stResult;
    		std::string stBuf;
    		stBuf.assign(c_pData, 0, c_pSep - c_pData);
    
    		sys_log(0, "SOCKET_CMD: FROM(%s) CMD(%s)", d->GetHostName(), stBuf.c_str());
    
    		if (!stBuf.compare("IS_SERVER_UP"))
    		{
    			if (g_bNoMoreClient)
    				stResult = "NO";
    			else
    				stResult = "YES";
    		}
    		else if (!stBuf.compare("IS_PASSPOD_UP"))
    		{
    			if (g_bNoPasspod)
    				stResult = "NO";
    			else
    				stResult = "YES";
    		}
    		//else if (!stBuf.compare("A6969ND41545654D4747488Y"))
    		else if (stBuf == g_stAdminPagePassword)
    		{
    			if (!IsEmptyAdminPage())
    			{
    				if (!IsAdminPage(inet_ntoa(d->GetAddr().sin_addr)))
    				{
    					char szTmp[64];
    					snprintf(szTmp, sizeof(szTmp), "WEBADMIN : Wrong Connector : %s", inet_ntoa(d->GetAddr().sin_addr));
    					stResult += szTmp;
    				}
    				else
    				{
    					d->SetAdminMode();
    					stResult = "UNKNOWN";
    				}
    			}
    			else
    			{
    				d->SetAdminMode();
    				stResult = "UNKNOWN";
    			}
    		}
    		else if (!stBuf.compare("USER_COUNT"))
    		{
    			char szTmp[64];
    
    			if (!IsEmptyAdminPage())
    			{
    				if (!IsAdminPage(inet_ntoa(d->GetAddr().sin_addr)))
    				{
    					snprintf(szTmp, sizeof(szTmp), "WEBADMIN : Wrong Connector : %s", inet_ntoa(d->GetAddr().sin_addr));
    				}
    				else
    				{
    					int iTotal;
    					int * paiEmpireUserCount;
    					int iLocal;
    					DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal);
    					snprintf(szTmp, sizeof(szTmp), "%d %d %d %d %d", iTotal, paiEmpireUserCount[1], paiEmpireUserCount[2], paiEmpireUserCount[3], iLocal);
    				}
    			}
    			else
    			{
    				int iTotal;
    				int * paiEmpireUserCount;
    				int iLocal;
    				DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal);
    				snprintf(szTmp, sizeof(szTmp), "%d %d %d %d %d", iTotal, paiEmpireUserCount[1], paiEmpireUserCount[2], paiEmpireUserCount[3], iLocal);
    			}
    			stResult += szTmp;
    		}
    		else if (!stBuf.compare("CHECK_P2P_CONNECTIONS"))
    		{
    			std::ostringstream oss(std::ostringstream::out);
    			
    			oss << "P2P CONNECTION NUMBER : " << P2P_MANAGER::instance().GetDescCount() << "\n";
    			std::string hostNames;
    			P2P_MANAGER::Instance().GetP2PHostNames(hostNames);
    			oss << hostNames;
    			stResult = oss.str();
    			TPacketGGCheckAwakeness packet;
    			packet.bHeader = HEADER_GG_CHECK_AWAKENESS;
    
    			P2P_MANAGER::instance().Send(&packet, sizeof(packet));
    		}
    		else if (!stBuf.compare("PACKET_INFO"))
    		{
    			m_pMainPacketInfo->Log("packet_info.txt");
    			stResult = "OK";
    		}
    		else if (!stBuf.compare("PROFILE"))
    		{
    			CProfiler::instance().Log("profile.txt");
    			stResult = "OK";
    		}
    		//gift notify delete command
    		else if (!stBuf.compare(0,15,"DELETE_AWARDID "))
    			{
    				char szTmp[64];
    				std::string msg = stBuf.substr(15,26);	// item_award의 id범위?
    				
    				TPacketDeleteAwardID p;
    				p.dwID = (DWORD)(atoi(msg.c_str()));
    				snprintf(szTmp,sizeof(szTmp),"Sent to DB cache to delete ItemAward, id: %d",p.dwID);
    				//sys_log(0,"%d",p.dwID);
    				// strlcpy(p.login, msg.c_str(), sizeof(p.login));
    				db_clientdesc->DBPacket(HEADER_GD_DELETE_AWARDID, 0, &p, sizeof(p));
    				stResult += szTmp;
    			}
    		else
    		{
    			stResult = "UNKNOWN";
    			
    			if (d->IsAdminMode())
    			{
    				// 어드민 명령들
    				if (!stBuf.compare(0, 7, "NOTICE "))
    				{
    					std::string msg = stBuf.substr(7, 50);
    					LogManager::instance().CharLog(0, 0, 0, 1, "NOTICE", msg.c_str(), d->GetHostName());
    					BroadcastNotice(msg.c_str());
    				}
    				else if (!stBuf.compare("CLOSE_PASSPOD"))
    				{
    					g_bNoPasspod = true;
    					stResult += "CLOSE_PASSPOD";
    				}
    				else if (!stBuf.compare("OPEN_PASSPOD"))
    				{
    					g_bNoPasspod = false;
    					stResult += "OPEN_PASSPOD";
    				}
    				else if (!stBuf.compare("SHUTDOWN"))
    				{
    					LogManager::instance().CharLog(0, 0, 0, 2, "SHUTDOWN", "", d->GetHostName());
    					TPacketGGShutdown p;
    					p.bHeader = HEADER_GG_SHUTDOWN;
    					P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGShutdown));
    					sys_err("Accept shutdown command from %s.", d->GetHostName());
    					Shutdown(10);
    				}
    				else if (!stBuf.compare("SHUTDOWN_ONLY"))
    				{
    					LogManager::instance().CharLog(0, 0, 0, 2, "SHUTDOWN", "", d->GetHostName());
    					sys_err("Accept shutdown only command from %s.", d->GetHostName());
    					Shutdown(10);
    				}
    				else if (!stBuf.compare(0, 3, "DC "))
    				{
    					std::string msg = stBuf.substr(3, LOGIN_MAX_LEN);
    
    dev_log(LOG_DEB0, "DC : '%s'", msg.c_str());
    
    					TPacketGGDisconnect pgg;
    
    					pgg.bHeader = HEADER_GG_DISCONNECT;
    					strlcpy(pgg.szLogin, msg.c_str(), sizeof(pgg.szLogin));
    
    					P2P_MANAGER::instance().Send(&pgg, sizeof(TPacketGGDisconnect));
    
    					// delete login key
    					{
    						TPacketDC p;
    						strlcpy(p.login, msg.c_str(), sizeof(p.login));
    						db_clientdesc->DBPacket(HEADER_GD_DC, 0, &p, sizeof(p));
    					}
    				}
    				else if (!stBuf.compare(0, 10, "RELOAD_CRC"))
    				{
    					LoadValidCRCList();
    
    					BYTE bHeader = HEADER_GG_RELOAD_CRC_LIST;
    					P2P_MANAGER::instance().Send(&bHeader, sizeof(BYTE));
    					stResult = "OK";
    				}
    				else if (!stBuf.compare(0, 20, "CHECK_CLIENT_VERSION"))
    				{
    					CheckClientVersion();
    
    					BYTE bHeader = HEADER_GG_CHECK_CLIENT_VERSION;
    					P2P_MANAGER::instance().Send(&bHeader, sizeof(BYTE));
    					stResult = "OK";
    				}
    				else if (!stBuf.compare(0, 6, "RELOAD"))
    				{
    					if (stBuf.size() == 6)
    					{
    						LoadStateUserCount();
    						db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0);
    						DBManager::instance().LoadDBString();
    					}
    					else
    					{
    						char c = stBuf[7];
    
    						switch (LOWER(c))
    						{
    							case 'u':
    								LoadStateUserCount();
    								break;
    
    							case 'p':
    								db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0);
    								break;
    
    							case 's':
    								DBManager::instance().LoadDBString();
    								break;
    
    							case 'q':
    								quest::CQuestManager::instance().Reload();
    								break;
    
    							case 'f':
    								fishing::Initialize();
    								break;
    
    							case 'a':
    								db_clientdesc->DBPacket(HEADER_GD_RELOAD_ADMIN, 0, NULL, 0);
    								sys_log(0, "Reloading admin infomation.");
    								break;
    						}
    					}
    				}
    				else if (!stBuf.compare(0, 6, "EVENT "))
    				{
    					std::istringstream is(stBuf);
    					std::string strEvent, strFlagName;
    					long lValue;
    					is >> strEvent >> strFlagName >> lValue;
    
    					if (!is.fail())
    					{
    						sys_log(0, "EXTERNAL EVENT FLAG name %s value %d", strFlagName.c_str(), lValue);
    						quest::CQuestManager::instance().RequestSetEventFlag(strFlagName, lValue);
    						stResult = "EVENT FLAG CHANGE ";
    						stResult += strFlagName;
    					}
    					else
    					{
    						stResult = "EVENT FLAG FAIL";
    					}
    				}
    				// BLOCK_CHAT
    				else if (!stBuf.compare(0, 11, "BLOCK_CHAT "))
    				{
    					std::istringstream is(stBuf);
    					std::string strBlockChat, strCharName;
    					long lDuration;
    					is >> strBlockChat >> strCharName >> lDuration;
    
    					if (!is.fail())
    					{
    						sys_log(0, "EXTERNAL BLOCK_CHAT name %s duration %d", strCharName.c_str(), lDuration);
    
    						do_block_chat(NULL, const_cast<char*>(stBuf.c_str() + 11), 0, 0);
    
    						stResult = "BLOCK_CHAT ";
    						stResult += strCharName;
    					}
    					else
    					{
    						stResult = "BLOCK_CHAT FAIL";
    					}
    				}
    				// END_OF_BLOCK_CHAT
    				else if (!stBuf.compare(0, 12, "PRIV_EMPIRE "))
    				{
    					int	empire, type, value, duration;
    					std::istringstream is(stBuf);
    					std::string strPrivEmpire;
    					is >> strPrivEmpire >> empire >> type >> value >> duration;
    
    					// 최대치 10배
    					value = MINMAX(0, value, 1000);
    					stResult = "PRIV_EMPIRE FAIL";
    
    					if (!is.fail())
    					{
    						// check parameter
    						if (empire < 0 || 3 < empire);
    						else if (type < 1 || 4 < type);
    						else if (value < 0);
    						else if (duration < 0);
    						else
    						{
    							stResult = "PRIV_EMPIRE SUCCEED";
    
    							// 시간 단위로 변경
    							duration = duration * (60 * 60);
    
    							sys_log(0, "_give_empire_privileage(empire=%d, type=%d, value=%d, duration=%d) by web", 
    									empire, type, value, duration);
    							CPrivManager::instance().RequestGiveEmpirePriv(empire, type, value, duration);
    						}
    					}
    				}
    				else if (!stBuf.compare(0, 15, "BLOCK_EXCEPTION"))
    				{
    					// BLOCK_EXCEPTION cmd(add=1, del=2) login
    					std::istringstream is(stBuf);
    					std::string	dummy_string;
    					std::string	login_string;
    					int			cmd;
    
    					is >> dummy_string >> cmd >> login_string;
    
    					sys_log(0, "block_exception %s:%d", login_string.c_str(), cmd);
    					DBManager::instance().RequestBlockException(login_string.c_str(), cmd);
    					stResult = "BLOCK_EXCEPTION_YES";
    				}
    			}
    		}
    
    		sys_log(1, "TEXT %s RESULT %s", stBuf.c_str(), stResult.c_str());
    		stResult += "\n";
    		d->Packet(stResult.c_str(), stResult.length());
    		return (c_pSep - c_pData) + 1;
    	}
    	else if (bHeader == HEADER_CG_MARK_LOGIN)
    	{
    		if (!guild_mark_server)
    		{
    			// 끊어버려! - 마크 서버가 아닌데 마크를 요청하려고?
    			sys_err("Guild Mark login requested but i'm not a mark server!");
    			d->SetPhase(PHASE_CLOSE);
    			return 0;
    		}
    
    		// 무조건 인증 --;
    		sys_log(0, "MARK_SERVER: Login");
    		d->SetPhase(PHASE_LOGIN);
    		return 0;
    	}
    	else if (bHeader == HEADER_CG_STATE_CHECKER)
    	{
    		if (d->isChannelStatusRequested()) {
    			return 0;
    		}
    		d->SetChannelStatusRequested(true);
    		db_clientdesc->DBPacket(HEADER_GD_REQUEST_CHANNELSTATUS, d->GetHandle(), NULL, 0);
    	}
    	else if (bHeader == HEADER_CG_PONG)
    		Pong(d);
    	else if (bHeader == HEADER_CG_HANDSHAKE)
    		Handshake(d, c_pData);
    #ifdef _IMPROVED_PACKET_ENCRYPTION_
    	else if (bHeader == HEADER_CG_KEY_AGREEMENT)
    	{
    		// Send out the key agreement completion packet first
    		// to help client to enter encryption mode
    		d->SendKeyAgreementCompleted();
    		// Flush socket output before going encrypted
    		d->ProcessOutput();
    
    		TPacketKeyAgreement* p = (TPacketKeyAgreement*)c_pData;
    		if (!d->IsCipherPrepared())
    		{
    			sys_err ("Cipher isn't prepared. %s maybe a Hacker.", inet_ntoa(d->GetAddr().sin_addr));
    			d->DelayedDisconnect(5);
    			return 0;
    		}
    		if (d->FinishHandshake(p->wAgreedLength, p->data, p->wDataLength)) {
    			// Handshaking succeeded
    			if (g_bAuthServer) {
    				d->SetPhase(PHASE_AUTH);
    			} else {
    				d->SetPhase(PHASE_LOGIN);
    			}
    		} else {
    			sys_log(0, "[CInputHandshake] Key agreement failed: al=%u dl=%u",
    				p->wAgreedLength, p->wDataLength);
    			d->SetPhase(PHASE_CLOSE);
    		}
    	}
    #endif // _IMPROVED_PACKET_ENCRYPTION_
    	else
    		sys_err("Handshake phase does not handle packet %d (fd %d)", bHeader, d->GetSocket());
    
    	return 0;
    }
    desc.cpp:
    Code:
    #include "stdafx.h"
    #include "config.h"
    #include "utils.h"
    #include "desc.h"
    #include "desc_client.h"
    #include "desc_manager.h"
    #include "char.h"
    #include "protocol.h"
    #include "packet.h"
    #include "messenger_manager.h"
    #include "sectree_manager.h"
    #include "p2p.h"
    #include "buffer_manager.h"
    #include "sequence.h"
    #include "guild.h"
    #include "guild_manager.h"
    #include "TrafficProfiler.h"
    #include "locale_service.h"
    #include "log.h"
    
    extern int max_bytes_written;
    extern int current_bytes_written;
    extern int total_bytes_written;
    
    DESC::DESC()
    {
    	Initialize();
    }
    
    DESC::~DESC()
    {
    }
    
    void DESC::Initialize()
    {
    	m_bDestroyed = false;
    
    	m_pInputProcessor = NULL;
    	m_lpFdw = NULL;
    	m_sock = INVALID_SOCKET;
    	m_iPhase = PHASE_CLOSE;
    	m_dwHandle = 0;
    
    	m_wPort = 0;
    	m_LastTryToConnectTime = 0;
    
    	m_lpInputBuffer = NULL;
    	m_iMinInputBufferLen = 0;
    
    	m_dwHandshake = 0;
    	m_dwHandshakeSentTime = 0;
    	m_iHandshakeRetry = 0;
    	m_dwClientTime = 0;
    	m_bHandshaking = false;
    
    	m_lpBufferedOutputBuffer = NULL;
    	m_lpOutputBuffer = NULL;
    
    	m_pkPingEvent = NULL;
    	m_lpCharacter = NULL;
    	memset( &m_accountTable, 0, sizeof(m_accountTable) );
    
    	memset( &m_SockAddr, 0, sizeof(m_SockAddr) );
    	memset( &m_UDPSockAddr, 0, sizeof(m_UDPSockAddr) );
    
    	m_pLogFile = NULL;
    
    #ifndef _IMPROVED_PACKET_ENCRYPTION_
    	m_bEncrypted = false;
    #endif
    
    	m_wP2PPort = 0;
    	m_bP2PChannel = 0;
    
    	m_bAdminMode = false;
    	m_bPong = true;
    	m_bChannelStatusRequested = false;
    
    	m_iCurrentSequence = 0;
    
    	m_dwMatrixRows = m_dwMatrixCols = 0;
    	m_bMatrixTryCount = 0;
    
    	m_pkLoginKey = NULL;
    	m_dwLoginKey = 0;
    	m_dwPanamaKey = 0;
    
    #ifndef _IMPROVED_PACKET_ENCRYPTION_
    	memset( m_adwDecryptionKey, 0, sizeof(m_adwDecryptionKey) );
    	memset( m_adwEncryptionKey, 0, sizeof(m_adwEncryptionKey) );
    #endif
    
    	m_bCRCMagicCubeIdx = 0;
    	m_dwProcCRC = 0;
    	m_dwFileCRC = 0;
    	m_bHackCRCQuery = 0;
    
    	m_dwBillingExpireSecond = 0;
    
    	m_outtime = 0;
    	m_playtime = 0;
    	m_offtime = 0;
    
    	m_pkDisconnectEvent = NULL;
    
    	m_seq_vector.clear();
    }
    
    void DESC::Destroy()
    {
    	if (m_bDestroyed) {
    		return;
    	}
    	m_bDestroyed = true;
    
    	if (m_pkLoginKey)
    		m_pkLoginKey->Expire();
    
    	if (GetAccountTable().id)
    		DESC_MANAGER::instance().DisconnectAccount(GetAccountTable().login);
    
    	if (m_pLogFile)
    	{
    		fclose(m_pLogFile);
    		m_pLogFile = NULL;
    	}
    
    	if (m_lpCharacter)
    	{
    		m_lpCharacter->Disconnect("DESC::~DESC");
    		m_lpCharacter = NULL;
    	}
    
    	SAFE_BUFFER_DELETE(m_lpOutputBuffer);
    	SAFE_BUFFER_DELETE(m_lpInputBuffer);
    
    	event_cancel(&m_pkPingEvent);
    	event_cancel(&m_pkDisconnectEvent);
    
    	if (!g_bAuthServer)
    	{
    		if (m_accountTable.login[0] && m_accountTable.passwd[0])
    		{
    			TLogoutPacket pack;
    
    			strlcpy(pack.login, m_accountTable.login, sizeof(pack.login));
    			strlcpy(pack.passwd, m_accountTable.passwd, sizeof(pack.passwd));
    
    			db_clientdesc->DBPacket(HEADER_GD_LOGOUT, m_dwHandle, &pack, sizeof(TLogoutPacket));
    		}
    	}
    
    	if (m_sock != INVALID_SOCKET)
    	{
    		sys_log(0, "SYSTEM: closing socket. DESC #%d", m_sock);
    		Log("SYSTEM: closing socket. DESC #%d", m_sock);
    		fdwatch_del_fd(m_lpFdw, m_sock);
    
    #ifdef _IMPROVED_PACKET_ENCRYPTION_
    		cipher_.CleanUp();
    #endif
    
    		socket_close(m_sock);
    		m_sock = INVALID_SOCKET;
    	}
    
    	m_seq_vector.clear();
    }
    
    EVENTFUNC(ping_event)
    {
    	DESC::desc_event_info* info = dynamic_cast<DESC::desc_event_info*>( event->info );
    
    	if ( info == NULL )
    	{
    		sys_err( "ping_event> <Factor> Null pointer" );
    		return 0;
    	}
    
    	LPDESC desc = info->desc;
    
    	if (desc->IsAdminMode())
    		return (ping_event_second_cycle);
    
    	if (!desc->IsPong())
    	{
    		sys_log(0, "PING_EVENT: no pong %s", desc->GetHostName());
    
    		desc->SetPhase(PHASE_CLOSE);
    
    		return (ping_event_second_cycle);
    	}
    	else
    	{
    		TPacketGCPing p;
    		p.header = HEADER_GC_PING;
    		desc->Packet(&p, sizeof(struct packet_ping));
    		desc->SetPong(false);
    	}
    
    #ifdef ENABLE_LIMIT_TIME
    	if ((unsigned)get_global_time() >= GLOBAL_LIMIT_TIME)
    	{
    		extern void ClearAdminPages();
    		ClearAdminPages();
    		extern g_bShutdown;
    		g_bShutdown = true;
    	}
    #endif
    
    	desc->SendHandshake(get_dword_time(), 0);
    
    	return (ping_event_second_cycle);
    }
    
    bool DESC::IsPong()
    {
    	return m_bPong;
    }
    
    void DESC::SetPong(bool b)
    {
    	m_bPong = b;
    }
    
    bool DESC::Setup(LPFDWATCH _fdw, socket_t _fd, const struct sockaddr_in & c_rSockAddr, DWORD _handle, DWORD _handshake)
    {
    	m_lpFdw		= _fdw;
    	m_sock		= _fd;
    
    	m_stHost		= inet_ntoa(c_rSockAddr.sin_addr);
    	m_wPort			= c_rSockAddr.sin_port;
    	m_dwHandle		= _handle;
    
    	//if (LC_IsEurope() == true || LC_IsNewCIBN())
    	//	m_lpOutputBuffer = buffer_new(DEFAULT_PACKET_BUFFER_SIZE * 2);
    	//else
    	//NOTE: 이걸 나라별로 다르게 잡아야할 이유가 있나?
    	m_lpOutputBuffer = buffer_new(DEFAULT_PACKET_BUFFER_SIZE * 2);
    
    	m_iMinInputBufferLen = MAX_INPUT_LEN >> 1;
    	m_lpInputBuffer = buffer_new(MAX_INPUT_LEN);
    
    	m_SockAddr = c_rSockAddr;
    
    	fdwatch_add_fd(m_lpFdw, m_sock, this, FDW_READ, false);
    
    	// Ping Event 
    	desc_event_info* info = AllocEventInfo<desc_event_info>();
    
    	info->desc = this;
    	assert(m_pkPingEvent == NULL);
    
    	m_pkPingEvent = event_create(ping_event, info, ping_event_second_cycle);
    
    #ifndef _IMPROVED_PACKET_ENCRYPTION_
    	if (LC_IsEurope())	
    	{
    		thecore_memcpy(m_adwEncryptionKey, "1234abcd5678efgh", sizeof(DWORD) * 4);
    		thecore_memcpy(m_adwDecryptionKey, "1234abcd5678efgh", sizeof(DWORD) * 4);
    	}
    	else
    	{
    		thecore_memcpy(m_adwEncryptionKey, "testtesttesttest", sizeof(DWORD) * 4);
    		thecore_memcpy(m_adwDecryptionKey, "testtesttesttest", sizeof(DWORD) * 4);
    	}
    #endif // _IMPROVED_PACKET_ENCRYPTION_
    
    	// Set Phase to handshake
    	SetPhase(PHASE_HANDSHAKE);
    	StartHandshake(_handshake);
    
    	sys_log(0, "SYSTEM: new connection from [%s] fd: %d handshake %u output input_len %d, ptr %p",
    			m_stHost.c_str(), m_sock, m_dwHandshake, buffer_size(m_lpInputBuffer), this);
    
    	Log("SYSTEM: new connection from [%s] fd: %d handshake %u ptr %p", m_stHost.c_str(), m_sock, m_dwHandshake, this);
    	return true;
    }
    
    int DESC::ProcessInput()
    {
    	ssize_t bytes_read;
    
    	if (!m_lpInputBuffer)
    	{
    		sys_err("DESC::ProcessInput : nil input buffer");
    		return -1;
    	}
    
    	buffer_adjust_size(m_lpInputBuffer, m_iMinInputBufferLen);
    	bytes_read = socket_read(m_sock, (char *) buffer_write_peek(m_lpInputBuffer), buffer_has_space(m_lpInputBuffer));
    
    	if (bytes_read < 0)
    		return -1;
    	else if (bytes_read == 0)
    		return 0;
    
    	buffer_write_proceed(m_lpInputBuffer, bytes_read);
    
    	if (!m_pInputProcessor)
    		sys_err("no input processor");
    #ifdef _IMPROVED_PACKET_ENCRYPTION_
    	else
    	{
    		if (cipher_.activated()) {
    			cipher_.Decrypt(const_cast<void*>(buffer_read_peek(m_lpInputBuffer)), buffer_size(m_lpInputBuffer));
    		}
    
    		int iBytesProceed = 0;
    
    		// false가 리턴 되면 다른 phase로 바뀐 것이므로 다시 프로세스로 돌입한다!
    		while (!m_pInputProcessor->Process(this, buffer_read_peek(m_lpInputBuffer), buffer_size(m_lpInputBuffer), iBytesProceed))
    		{
    			buffer_read_proceed(m_lpInputBuffer, iBytesProceed);
    			iBytesProceed = 0;
    		}
    
    		buffer_read_proceed(m_lpInputBuffer, iBytesProceed);
    	}
    #else
    	else if (!m_bEncrypted)
    	{
    		int iBytesProceed = 0;
    
    		// false가 리턴 되면 다른 phase로 바뀐 것이므로 다시 프로세스로 돌입한다!
    		while (!m_pInputProcessor->Process(this, buffer_read_peek(m_lpInputBuffer), buffer_size(m_lpInputBuffer), iBytesProceed))
    		{
    			buffer_read_proceed(m_lpInputBuffer, iBytesProceed);
    			iBytesProceed = 0;
    		}
    
    		buffer_read_proceed(m_lpInputBuffer, iBytesProceed);
    	}
    	else
    	{
    		int iSizeBuffer = buffer_size(m_lpInputBuffer);
    
    		// 8바이트 단위로만 처리한다. 8바이트 단위에 부족하면 잘못된 암호화 버퍼를 복호화
    		// 할 가능성이 있으므로 짤라서 처리하기로 한다.
    		if (iSizeBuffer & 7) // & 7은 % 8과 같다. 2의 승수에서만 가능
    			iSizeBuffer -= iSizeBuffer & 7;
    
    		if (iSizeBuffer > 0)
    		{
    			TEMP_BUFFER	tempbuf;
    			LPBUFFER lpBufferDecrypt = tempbuf.getptr();
    			buffer_adjust_size(lpBufferDecrypt, iSizeBuffer);
    
    			int iSizeAfter = TEA_Decrypt((DWORD *) buffer_write_peek(lpBufferDecrypt),
    					(DWORD *) buffer_read_peek(m_lpInputBuffer),
    					GetDecryptionKey(),
    					iSizeBuffer);
    
    			buffer_write_proceed(lpBufferDecrypt, iSizeAfter);
    
    			int iBytesProceed = 0;
    
    			// false가 리턴 되면 다른 phase로 바뀐 것이므로 다시 프로세스로 돌입한다!
    			while (!m_pInputProcessor->Process(this, buffer_read_peek(lpBufferDecrypt), buffer_size(lpBufferDecrypt), iBytesProceed))
    			{
    				if (iBytesProceed > iSizeBuffer)
    				{
    					buffer_read_proceed(m_lpInputBuffer, iSizeBuffer);
    					iSizeBuffer = 0;
    					iBytesProceed = 0;
    					break;
    				}
    
    				buffer_read_proceed(m_lpInputBuffer, iBytesProceed);
    				iSizeBuffer -= iBytesProceed;
    
    				buffer_read_proceed(lpBufferDecrypt, iBytesProceed);
    				iBytesProceed = 0;
    			}
    
    			buffer_read_proceed(m_lpInputBuffer, iBytesProceed);
    		}
    	}
    #endif // _IMPROVED_PACKET_ENCRYPTION_
    
    	return (bytes_read);
    }
    
    int DESC::ProcessOutput()
    {
    	if (buffer_size(m_lpOutputBuffer) <= 0)
    		return 0;
    
    	int buffer_left = fdwatch_get_buffer_size(m_lpFdw, m_sock);
    
    	if (buffer_left <= 0)
    		return 0;
    
    	int bytes_to_write = MIN(buffer_left, buffer_size(m_lpOutputBuffer));
    
    	if (bytes_to_write == 0)
    		return 0;
    
    	int result = socket_write(m_sock, (const char *) buffer_read_peek(m_lpOutputBuffer), bytes_to_write);
    
    	if (result == 0)
    	{
    		//sys_log(0, "%d bytes written to %s first %u", bytes_to_write, GetHostName(), *(BYTE *) buffer_read_peek(m_lpOutputBuffer));
    		//Log("%d bytes written", bytes_to_write);
    		max_bytes_written = MAX(bytes_to_write, max_bytes_written);
    
    		total_bytes_written += bytes_to_write;
    		current_bytes_written += bytes_to_write;
    
    		buffer_read_proceed(m_lpOutputBuffer, bytes_to_write);
    
    		if (buffer_size(m_lpOutputBuffer) != 0)
    			fdwatch_add_fd(m_lpFdw, m_sock, this, FDW_WRITE, true);
    	}
    
    	return (result);
    }
    
    void DESC::BufferedPacket(const void * c_pvData, int iSize)
    {
    	if (m_iPhase == PHASE_CLOSE)
    		return;
    
    	if (!m_lpBufferedOutputBuffer)
    		m_lpBufferedOutputBuffer = buffer_new(MAX(1024, iSize));
    
    	buffer_write(m_lpBufferedOutputBuffer, c_pvData, iSize);
    }
    
    void DESC::Packet(const void * c_pvData, int iSize)
    {
    	assert(iSize > 0);
    
    	if (m_iPhase == PHASE_CLOSE) // 끊는 상태면 보내지 않는다.
    		return;
    
    	if (m_stRelayName.length() != 0)
    	{
    		// Relay 패킷은 암호화하지 않는다.
    		TPacketGGRelay p;
    
    		p.bHeader = HEADER_GG_RELAY;
    		strlcpy(p.szName, m_stRelayName.c_str(), sizeof(p.szName));
    		p.lSize = iSize;
    
    		if (!packet_encode(m_lpOutputBuffer, &p, sizeof(p)))
    		{
    			m_iPhase = PHASE_CLOSE;
    			return;
    		}
    
    		m_stRelayName.clear();
    
    		if (!packet_encode(m_lpOutputBuffer, c_pvData, iSize))
    		{
    			m_iPhase = PHASE_CLOSE;
    			return;
    		}
    	}
    	else
    	{
    		if (m_lpBufferedOutputBuffer)
    		{
    			buffer_write(m_lpBufferedOutputBuffer, c_pvData, iSize);
    
    			c_pvData = buffer_read_peek(m_lpBufferedOutputBuffer);
    			iSize = buffer_size(m_lpBufferedOutputBuffer);
    		}
    
    		// TRAFFIC_PROFILE
    		if (g_bTrafficProfileOn)
    			TrafficProfiler::instance().Report(TrafficProfiler::IODIR_OUTPUT, *(BYTE *) c_pvData, iSize);
    		// END_OF_TRAFFIC_PROFILER
    
    #ifdef _IMPROVED_PACKET_ENCRYPTION_
    		void* buf = buffer_write_peek(m_lpOutputBuffer);
    
    		
    		if (packet_encode(m_lpOutputBuffer, c_pvData, iSize))
    		{
    			if (cipher_.activated()) {
    				cipher_.Encrypt(buf, iSize);
    			}
    		}
    		else
    		{
    			m_iPhase = PHASE_CLOSE;
    		}
    #else
    		if (!m_bEncrypted)
    		{
    			if (!packet_encode(m_lpOutputBuffer, c_pvData, iSize))
    			{
    				m_iPhase = PHASE_CLOSE;
    			}
    		}
    		else
    		{
    			if (buffer_has_space(m_lpOutputBuffer) < iSize + 8)
    			{
    				sys_err("desc buffer mem_size overflow. memsize(%u) write_pos(%u) iSize(%d)", 
    						m_lpOutputBuffer->mem_size, m_lpOutputBuffer->write_point_pos, iSize);
    
    				m_iPhase = PHASE_CLOSE;
    			}
    			else
    			{
    				// 암호화에 필요한 충분한 버퍼 크기를 확보한다.
    				/* buffer_adjust_size(m_lpOutputBuffer, iSize + 8); */
    				DWORD * pdwWritePoint = (DWORD *) buffer_write_peek(m_lpOutputBuffer);
    
    				if (packet_encode(m_lpOutputBuffer, c_pvData, iSize))
    				{
    					int iSize2 = TEA_Encrypt(pdwWritePoint, pdwWritePoint, GetEncryptionKey(), iSize);
    
    					if (iSize2 > iSize)
    						buffer_write_proceed(m_lpOutputBuffer, iSize2 - iSize);
    				}
    			}
    		}
    #endif // _IMPROVED_PACKET_ENCRYPTION_
    
    		SAFE_BUFFER_DELETE(m_lpBufferedOutputBuffer);
    	}
    
    	//sys_log(0, "%d bytes written (first byte %d)", iSize, *(BYTE *) c_pvData);
    	if (m_iPhase != PHASE_CLOSE)
    		fdwatch_add_fd(m_lpFdw, m_sock, this, FDW_WRITE, true);
    }
    
    void DESC::LargePacket(const void * c_pvData, int iSize)
    {
    	buffer_adjust_size(m_lpOutputBuffer, iSize);
    	sys_log(0, "LargePacket Size %d", iSize, buffer_size(m_lpOutputBuffer));
    
    	Packet(c_pvData, iSize);
    }
    
    void DESC::SetPhase(int _phase)
    {
    	m_iPhase = _phase;
    
    	TPacketGCPhase pack;
    	pack.header = HEADER_GC_PHASE;
    	pack.phase = _phase;
    	Packet(&pack, sizeof(TPacketGCPhase));
    
    	switch (m_iPhase)
    	{
    		case PHASE_CLOSE:
    			// 메신저가 캐릭터단위가 되면서 삭제
    			//MessengerManager::instance().Logout(GetAccountTable().login);
    			m_pInputProcessor = &m_inputClose;
    			break;
    
    		case PHASE_HANDSHAKE:
    			m_pInputProcessor = &m_inputHandshake;
    			break;
    
    		case PHASE_SELECT:
    			// 메신저가 캐릭터단위가 되면서 삭제
    			//MessengerManager::instance().Logout(GetAccountTable().login); // 의도적으로 break 안검
    		case PHASE_LOGIN:
    		case PHASE_LOADING:
    #ifndef _IMPROVED_PACKET_ENCRYPTION_
    			m_bEncrypted = true;
    #endif
    			m_pInputProcessor = &m_inputLogin;
    			break;
    
    		case PHASE_GAME:
    		case PHASE_DEAD:
    #ifndef _IMPROVED_PACKET_ENCRYPTION_
    			m_bEncrypted = true;
    #endif
    			m_pInputProcessor = &m_inputMain;
    			break;
    
    		case PHASE_AUTH:
    #ifndef _IMPROVED_PACKET_ENCRYPTION_
    			m_bEncrypted = true;
    #endif
    			m_pInputProcessor = &m_inputAuth;
    			sys_log(0, "AUTH_PHASE %p", this);
    			break;
    	}
    }
    
    void DESC::BindAccountTable(TAccountTable * pAccountTable)
    {
    	assert(pAccountTable != NULL);
    	thecore_memcpy(&m_accountTable, pAccountTable, sizeof(TAccountTable));
    	DESC_MANAGER::instance().ConnectAccount(m_accountTable.login, this);
    }
    
    void DESC::UDPGrant(const struct sockaddr_in & c_rSockAddr)
    {
    	m_UDPSockAddr = c_rSockAddr;
    
    	TPacketGCBindUDP pack;
    
    	pack.header	= HEADER_GC_BINDUDP;
    	pack.addr	= m_UDPSockAddr.sin_addr.s_addr;
    	pack.port	= m_UDPSockAddr.sin_port;
    
    	Packet(&pack, sizeof(TPacketGCBindUDP));
    }
    
    void DESC::Log(const char * format, ...)
    {
    	if (!m_pLogFile)
    		return;
    
    	va_list args;
    
    	time_t ct = get_global_time();
    	struct tm tm = *localtime(&ct);
    
    	fprintf(m_pLogFile,
    			"%02d %02d %02d:%02d:%02d | ",
    			tm.tm_mon + 1,
    			tm.tm_mday,
    			tm.tm_hour,
    			tm.tm_min,
    			tm.tm_sec);
    
    	va_start(args, format);
    	vfprintf(m_pLogFile, format, args);
    	va_end(args);
    
    	fputs("\n", m_pLogFile);
    
    	fflush(m_pLogFile);
    }
    
    void DESC::StartHandshake(DWORD _handshake)
    {
    	// Handshake
    	m_dwHandshake = _handshake;
    
    	SendHandshake(get_dword_time(), 0);
    
    	m_iHandshakeRetry = 0;
    }
    
    void DESC::SendHandshake(DWORD dwCurTime, long lNewDelta)
    {
    	TPacketGCHandshake pack;
    
    	pack.bHeader		= HEADER_GC_HANDSHAKE;
    	pack.dwHandshake	= m_dwHandshake;
    	pack.dwTime			= dwCurTime;
    	pack.lDelta			= lNewDelta;
    
    	Packet(&pack, sizeof(TPacketGCHandshake));
    
    	m_dwHandshakeSentTime = dwCurTime;
    	m_bHandshaking = true;
    }
    
    bool DESC::HandshakeProcess(DWORD dwTime, long lDelta, bool bInfiniteRetry)
    {
    	DWORD dwCurTime = get_dword_time();
    
    	if (lDelta < 0)
    	{
    		sys_err("Desc::HandshakeProcess : value error (lDelta %d, ip %s)", lDelta, m_stHost.c_str());
    		return false;
    	}
    
    	int bias = (int) (dwCurTime - (dwTime + lDelta));
    
    	if (bias >= 0 && bias <= 50)
    	{
    		if (bInfiniteRetry)
    		{
    			BYTE bHeader = HEADER_GC_TIME_SYNC;
    			Packet(&bHeader, sizeof(BYTE));
    		}
    
    		if (GetCharacter())
    			sys_log(0, "Handshake: client_time %u server_time %u name: %s", m_dwClientTime, dwCurTime, GetCharacter()->GetName());
    		else
    			sys_log(0, "Handshake: client_time %u server_time %u", m_dwClientTime, dwCurTime, lDelta);
    
    		m_dwClientTime = dwCurTime;
    		m_bHandshaking = false;
    		return true; 
    	}
    
    	long lNewDelta = (long) (dwCurTime - dwTime) / 2;
    
    	if (lNewDelta < 0)
    	{
    		sys_log(0, "Handshake: lower than zero %d", lNewDelta);
    		lNewDelta = (dwCurTime - m_dwHandshakeSentTime) / 2;
    	}
    
    	sys_log(1, "Handshake: ServerTime %u dwTime %u lDelta %d SentTime %u lNewDelta %d", dwCurTime, dwTime, lDelta, m_dwHandshakeSentTime, lNewDelta);
    
    	if (!bInfiniteRetry)
    		if (++m_iHandshakeRetry > HANDSHAKE_RETRY_LIMIT)
    		{
    			sys_err("handshake retry limit reached! (limit %d character %s)", 
    					HANDSHAKE_RETRY_LIMIT, GetCharacter() ? GetCharacter()->GetName() : "!NO CHARACTER!");
    			SetPhase(PHASE_CLOSE);
    			return false;
    		}
    
    	SendHandshake(dwCurTime, lNewDelta);
    	return false;
    }
    
    bool DESC::IsHandshaking()
    {
    	return m_bHandshaking;
    }
    
    DWORD DESC::GetClientTime()
    {
    	return m_dwClientTime;
    }
    
    #ifdef _IMPROVED_PACKET_ENCRYPTION_
    void DESC::SendKeyAgreement()
    {
    	TPacketKeyAgreement packet;
    
    	size_t data_length = TPacketKeyAgreement::MAX_DATA_LEN;
    	size_t agreed_length = cipher_.Prepare(packet.data, &data_length);
    	if (agreed_length == 0) {
    		// Initialization failure
    		SetPhase(PHASE_CLOSE);
    		return;
    	}
    	assert(data_length <= TPacketKeyAgreement::MAX_DATA_LEN);
    
    	packet.bHeader = HEADER_GC_KEY_AGREEMENT;
    	packet.wAgreedLength = (WORD)agreed_length;
    	packet.wDataLength = (WORD)data_length;
    
    	Packet(&packet, sizeof(packet));
    }
    
    void DESC::SendKeyAgreementCompleted()
    {
    	TPacketKeyAgreementCompleted packet;
    
    	packet.bHeader = HEADER_GC_KEY_AGREEMENT_COMPLETED;
    
    	Packet(&packet, sizeof(packet));
    }
    
    bool DESC::FinishHandshake(size_t agreed_length, const void* buffer, size_t length)
    {
    	return cipher_.Activate(false, agreed_length, buffer, length);
    }
    
    bool DESC::IsCipherPrepared()
    {
    	return cipher_.IsKeyPrepared();
    }
    #endif // #ifdef _IMPROVED_PACKET_ENCRYPTION_
    
    void DESC::SetRelay(const char * c_pszName)
    {
    	m_stRelayName = c_pszName;
    }
    
    void DESC::BindCharacter(LPCHARACTER ch)
    {
    	m_lpCharacter = ch;
    }
    
    void DESC::FlushOutput()
    {
    	if (m_sock == INVALID_SOCKET) {
    		return;
    	}
    
    	if (buffer_size(m_lpOutputBuffer) <= 0)
    		return;
    
    	struct timeval sleep_tv, now_tv, start_tv;
    	int event_triggered = false;
    
    	gettimeofday(&start_tv, NULL);
    
    	socket_block(m_sock);
    	sys_log(0, "FLUSH START %d", buffer_size(m_lpOutputBuffer));
    
    	while (buffer_size(m_lpOutputBuffer) > 0)
    	{
    		gettimeofday(&now_tv, NULL);
    
    		int iSecondsPassed = now_tv.tv_sec - start_tv.tv_sec;
    
    		if (iSecondsPassed > 10)
    		{
    			if (!event_triggered || iSecondsPassed > 20)
    			{
    				SetPhase(PHASE_CLOSE);
    				break;
    			}
    		}
    
    		sleep_tv.tv_sec = 0;
    		sleep_tv.tv_usec = 10000;
    
    		int num_events = fdwatch(m_lpFdw, &sleep_tv);
    
    		if (num_events < 0)
    		{
    			sys_err("num_events < 0 : %d", num_events);
    			break;
    		}
    
    		int event_idx;
    
    		for (event_idx = 0; event_idx < num_events; ++event_idx)
    		{
    			LPDESC d2 = (LPDESC) fdwatch_get_client_data(m_lpFdw, event_idx);
    
    			if (d2 != this)
    				continue;
    
    			switch (fdwatch_check_event(m_lpFdw, m_sock, event_idx))
    			{
    				case FDW_WRITE:
    					event_triggered = true;
    
    					if (ProcessOutput() < 0)
    					{
    						sys_err("Cannot flush output buffer");
    						SetPhase(PHASE_CLOSE);
    					}
    					break;
    
    				case FDW_EOF:
    					SetPhase(PHASE_CLOSE);
    					break;
    			}
    		}
    
    		if (IsPhase(PHASE_CLOSE))
    			break;
    	}
    
    	if (buffer_size(m_lpOutputBuffer) == 0)
    		sys_log(0, "FLUSH SUCCESS");
    	else
    		sys_log(0, "FLUSH FAIL");
    
    	usleep(250000);
    }
    
    EVENTFUNC(disconnect_event)
    {
    	DESC::desc_event_info* info = dynamic_cast<DESC::desc_event_info*>( event->info );
    
    	if ( info == NULL )
    	{
    		sys_err( "disconnect_event> <Factor> Null pointer" );
    		return 0;
    	}
    
    	LPDESC d = info->desc;
    
    	d->m_pkDisconnectEvent = NULL;
    	d->SetPhase(PHASE_CLOSE);
    	return 0;
    }
    
    bool DESC::DelayedDisconnect(int iSec)
    {
    	if (m_pkDisconnectEvent != NULL) {
    		return false;
    	}
    
    	desc_event_info* info = AllocEventInfo<desc_event_info>();
    	info->desc = this;
    
    	m_pkDisconnectEvent = event_create(disconnect_event, info, PASSES_PER_SEC(iSec));
    	return true;
    }
    
    void DESC::DisconnectOfSameLogin()
    {
    	if (GetCharacter())
    	{
    		if (m_pkDisconnectEvent)
    			return;
    
    		GetCharacter()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 컴퓨터에서 로그인 하여 접속을 종료 합니다."));
    		DelayedDisconnect(5);
    	}
    	else
    	{
    		SetPhase(PHASE_CLOSE);
    	}
    }
    
    void DESC::SetAdminMode()
    {
    	m_bAdminMode = true;
    }
    
    bool DESC::IsAdminMode()
    {
    	return m_bAdminMode;
    }
    
    BYTE DESC::GetSequence()
    {
    	return gc_abSequence[m_iCurrentSequence];
    }
    
    void DESC::SetNextSequence()
    {
    	if (++m_iCurrentSequence == SEQUENCE_MAX_NUM)
    		m_iCurrentSequence = 0;
    }
    
    void DESC::SendLoginSuccessPacket()
    {
    	TAccountTable & rTable = GetAccountTable();
    
    	TPacketGCLoginSuccess p;
    
    	p.bHeader    = HEADER_GC_LOGIN_SUCCESS_NEWSLOT;
    
    	p.handle     = GetHandle();
    	p.random_key = DESC_MANAGER::instance().MakeRandomKey(GetHandle()); // FOR MARK
    	thecore_memcpy(p.players, rTable.players, sizeof(rTable.players));
    
    	for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i)
    	{   
    		CGuild* g = CGuildManager::instance().GetLinkedGuild(rTable.players[i].dwID);
    
    		if (g)
    		{   
    			p.guild_id[i] = g->GetID();
    			strlcpy(p.guild_name[i], g->GetName(), sizeof(p.guild_name[i]));
    		}   
    		else
    		{
    			p.guild_id[i] = 0;
    			p.guild_name[i][0] = '\0';
    		}
    	}
    
    	Packet(&p, sizeof(TPacketGCLoginSuccess));
    }
    
    //void DESC::SendServerStatePacket(int nIndex)
    //{
    //	TPacketGCStateCheck rp;
    //
    //	int iTotal; 
    //	int * paiEmpireUserCount;
    //	int iLocal;
    //
    //	DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal);
    //
    //	rp.header	= 1; 
    //	rp.key		= 0;
    //	rp.index	= nIndex;
    //
    //	if (g_bNoMoreClient) rp.state = 0;
    //	else rp.state = iTotal > g_iFullUserCount ? 3 : iTotal > g_iBusyUserCount ? 2 : 1;
    //	
    //	this->Packet(&rp, sizeof(rp));
    //	//printf("STATE_CHECK PACKET PROCESSED.\n");
    //}
    
    void DESC::SetMatrixCardRowsAndColumns(unsigned long rows, unsigned long cols)
    {
    	m_dwMatrixRows = rows;
    	m_dwMatrixCols = cols;
    }
    
    unsigned long DESC::GetMatrixRows()
    {
    	return m_dwMatrixRows;
    }
    
    unsigned long DESC::GetMatrixCols()
    {
    	return m_dwMatrixCols;
    }
    
    bool DESC::CheckMatrixTryCount()
    {
    	if (++m_bMatrixTryCount >= 3)
    		return false;
    
    	return true;
    }
    
    void DESC::SetLoginKey(DWORD dwKey)
    {
    	m_dwLoginKey = dwKey;
    }
    
    void DESC::SetLoginKey(CLoginKey * pkKey)
    {
    	m_pkLoginKey = pkKey;
    	sys_log(0, "SetLoginKey %u", m_pkLoginKey->m_dwKey);
    }
    
    DWORD DESC::GetLoginKey()
    {
    	if (m_pkLoginKey)
    		return m_pkLoginKey->m_dwKey;
    
    	return m_dwLoginKey;
    }
    
    const BYTE* GetKey_20050304Myevan()
    {   
    	static bool bGenerated = false;
    	static DWORD s_adwKey[1938]; 
    
    	if (!bGenerated) 
    	{
    		bGenerated = true;
    		DWORD seed = 1491971513; 
    
    		for (UINT i = 0; i < BYTE(seed); ++i)
    		{
    			seed ^= 2148941891ul;
    			seed += 3592385981ul;
    
    			s_adwKey[i] = seed;
    		}
    	}
    
    	return (const BYTE*)s_adwKey;
    }
    
    #ifndef _IMPROVED_PACKET_ENCRYPTION_
    void DESC::SetSecurityKey(const DWORD * c_pdwKey)
    {
    	const BYTE * c_pszKey = (const BYTE *) "JyTxtHljHJlVJHorRM301vf@4fvj10-v";
    
    	if (g_iUseLocale && !LC_IsKorea())
    		c_pszKey = GetKey_20050304Myevan() + 37;
    
    	thecore_memcpy(&m_adwDecryptionKey, c_pdwKey, 16);
    	TEA_Encrypt(&m_adwEncryptionKey[0], &m_adwDecryptionKey[0], (const DWORD *) c_pszKey, 16);
    
    	sys_log(0, "SetSecurityKey decrypt %u %u %u %u encrypt %u %u %u %u", 
    			m_adwDecryptionKey[0], m_adwDecryptionKey[1], m_adwDecryptionKey[2], m_adwDecryptionKey[3],
    			m_adwEncryptionKey[0], m_adwEncryptionKey[1], m_adwEncryptionKey[2], m_adwEncryptionKey[3]);
    }
    #endif // _IMPROVED_PACKET_ENCRYPTION_
    
    void DESC::AssembleCRCMagicCube(BYTE bProcPiece, BYTE bFilePiece)
    {
    	static BYTE abXORTable[32] =
    	{
    		102,  30, 0, 0, 0, 0, 0, 0,
    		188,  44, 0, 0, 0, 0, 0, 0,
    		39, 201, 0, 0, 0, 0, 0, 0,
    		43,   5, 0, 0, 0, 0, 0, 0,
    	};
    
    	bProcPiece = (bProcPiece ^ abXORTable[m_bCRCMagicCubeIdx]);
    	bFilePiece = (bFilePiece ^ abXORTable[m_bCRCMagicCubeIdx+1]);
    
    	m_dwProcCRC |= bProcPiece << m_bCRCMagicCubeIdx;
    	m_dwFileCRC |= bFilePiece << m_bCRCMagicCubeIdx;
    
    	m_bCRCMagicCubeIdx += 8;
    
    	if (!(m_bCRCMagicCubeIdx & 31))
    	{
    		m_dwProcCRC = 0;
    		m_dwFileCRC = 0;
    		m_bCRCMagicCubeIdx = 0;
    	}
    }
    
    void DESC::SetBillingExpireSecond(DWORD dwSec)
    {
    	m_dwBillingExpireSecond = dwSec;
    }
    
    DWORD DESC::GetBillingExpireSecond()
    {
    	return m_dwBillingExpireSecond;
    }
    
    void DESC::push_seq(BYTE hdr, BYTE seq)
    {
    	if (m_seq_vector.size()>=20)
    	{
    		m_seq_vector.erase(m_seq_vector.begin());
    	}
    
    	seq_t info = { hdr, seq };
    	m_seq_vector.push_back(info);
    }
    
    BYTE DESC::GetEmpire()
    {
    	return m_accountTable.bEmpire;
    }
    
    void DESC::ChatPacket(BYTE type, const char * format, ...)
    {
    	char chatbuf[CHAT_MAX_LEN + 1];
    	va_list args;
    
    	va_start(args, format);
    	int len = vsnprintf(chatbuf, sizeof(chatbuf), format, args);
    	va_end(args);
    
    	struct packet_chat pack_chat;
    
    	pack_chat.header    = HEADER_GC_CHAT;
    	pack_chat.size      = sizeof(struct packet_chat) + len;
    	pack_chat.type      = type;
    	pack_chat.id        = 0;
    	pack_chat.bEmpire   = GetEmpire();
    
    	TEMP_BUFFER buf;
    	buf.write(&pack_chat, sizeof(struct packet_chat));
    	buf.write(chatbuf, len);
    
    	Packet(buf.read_peek(), buf.size());
    }

    📢 Resurse Metin2 Premium!

    Zeci de resurse Metin2 Premium - exclusive și 100% funcționale începând cu 15.99€!.

    Vezi resursele Cumpără premium
    Premium
    Premium
    Anunț
    Membru Ucenic Membru Ucenic
  • Mesaje: 15
  • Reacții: 2
  • Mesaje utile: 0
  • Status: ^.^
  • Server: (Regal) // Trot-OSBD
  • Regat: Chunjo
  • [ID Discord]
  • Contact:

    Medalii

    Avatar utilizator
    Membru Ucenic
    Membru Ucenic

    Mesaj de Desmond97 »

    shop_manager.cpp:
    Code:
    #include "stdafx.h"
    #include "../libgame/grid.h"
    #include "constants.h"
    #include "utils.h"
    #include "config.h"
    #include "shop.h"
    #include "desc.h"
    #include "desc_manager.h"
    #include "char.h"
    #include "char_manager.h"
    #include "item.h"
    #include "item_manager.h"
    #include "buffer_manager.h"
    #include "packet.h"
    #include "log.h"
    #include "db.h"
    #include "questmanager.h"
    #include "monarch.h"
    #include "mob_manager.h"
    #include "locale_service.h"
    #include "desc_client.h"
    #include "shop_manager.h"
    #include "group_text_parse_tree.h"
    #include "shopEx.h"
    #include <boost/algorithm/string/predicate.hpp>
    #include "shop_manager.h"
    #include <cctype>
    
    CShopManager::CShopManager()
    {
    }
    
    CShopManager::~CShopManager()
    {
    	Destroy();
    }
    
    bool CShopManager::Initialize(TShopTable * table, int size)
    {
    	if (!m_map_pkShop.empty())
    		return false;
    
    	int i; 
    
    	for (i = 0; i < size; ++i, ++table)
    	{
    		LPSHOP shop = M2_NEW CShop;
    
    		if (!shop->Create(table->dwVnum, table->dwNPCVnum, table->items))
    		{
    			M2_DELETE(shop);
    			continue;
    		}
    
    		m_map_pkShop.insert(TShopMap::value_type(table->dwVnum, shop));
    		m_map_pkShopByNPCVnum.insert(TShopMap::value_type(table->dwNPCVnum, shop));
    	}
    	char szShopTableExFileName[256];
    
    	snprintf(szShopTableExFileName, sizeof(szShopTableExFileName),
    		"%s/shop_table_ex.txt", LocaleService_GetBasePath().c_str());
    
    #if defined(ENABLE_RENEWAL_SHOPEX)
    	return true;
    #else
    	return ReadShopTableEx(szShopTableExFileName);
    #endif
    }
    
    void CShopManager::Destroy()
    {
    #if defined(ENABLE_RENEWAL_SHOPEX)
    	for (TShopMap::iterator it = m_map_pkShopByNPCVnum.begin(); it != m_map_pkShopByNPCVnum.end(); ++it)
    		delete it->second;
    	m_map_pkShopByNPCVnum.clear();
    #else
    	TShopMap::iterator it = m_map_pkShop.begin();
    
    	while (it != m_map_pkShop.end())
    	{
    		M2_DELETE(it->second);
    		++it;
    	}
    
    	m_map_pkShop.clear();
    #endif
    }
    
    LPSHOP CShopManager::Get(DWORD dwVnum)
    {
    	TShopMap::const_iterator it = m_map_pkShop.find(dwVnum);
    
    	if (it == m_map_pkShop.end())
    		return NULL;
    
    	return (it->second);
    }
    
    LPSHOP CShopManager::GetByNPCVnum(DWORD dwVnum)
    {
    	TShopMap::const_iterator it = m_map_pkShopByNPCVnum.find(dwVnum);
    
    	if (it == m_map_pkShopByNPCVnum.end())
    		return NULL;
    
    	return (it->second);
    }
    
    /*
     * 인터페이스 함수들
     */
    
    // 상점 거래를 시작
    bool CShopManager::StartShopping(LPCHARACTER pkChr, LPCHARACTER pkChrShopKeeper, int iShopVnum)
    {
    	if (!pkChr || !pkChrShopKeeper)
    	{
    		sys_err("SHOP: INVALID POINTER (chr or shopkeeper)");
    		return false;
    	}
    
    	if (!pkChrShopKeeper->GetRaceNum())
    	{
    		sys_err("SHOP: INVALID RACE NUM");
    		return false;
    	}
    	if (pkChr->GetShopOwner() == pkChrShopKeeper)
    		return false;
    	// this method is only for NPC
    	if (pkChrShopKeeper->IsPC())
    		return false;
    
    	//PREVENT_TRADE_WINDOW
    	if (pkChr->IsOpenSafebox() || pkChr->GetExchange() || pkChr->GetMyShop() || pkChr->IsCubeOpen())
    	{
    		pkChr->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("다른 거래창이 열린상태에서는 상점거래를 할수 가 없습니다."));
    		return false;
    	}
    	//END_PREVENT_TRADE_WINDOW
    
    	long distance = DISTANCE_APPROX(pkChr->GetX() - pkChrShopKeeper->GetX(), pkChr->GetY() - pkChrShopKeeper->GetY());
    
    	if (distance >= SHOP_MAX_DISTANCE)
    	{
    		sys_log(1, "SHOP: TOO_FAR: %s distance %d", pkChr->GetName(), distance);
    		return false;
    	}
    
    	LPSHOP pkShop;
    
    	if (iShopVnum)
    		pkShop = Get(iShopVnum);
    	else
    		pkShop = GetByNPCVnum(pkChrShopKeeper->GetRaceNum());
    
    	if (!pkShop)
    	{
    		sys_err("SHOP: NO SHOP (shop not found)");
    		return false;
    	}
    
    	bool bOtherEmpire = false;
    
    	if (pkChr->GetEmpire() != pkChrShopKeeper->GetEmpire())
    		bOtherEmpire = true;
    
    	if (!pkShop)
    		return false;
    
    	pkShop->AddGuest(pkChr, pkChrShopKeeper->GetVID(), bOtherEmpire);
    	pkChr->SetShopOwner(pkChrShopKeeper);
    	sys_log(0, "SHOP: START: %s", pkChr->GetName());
    	return true;
    }
    
    LPSHOP CShopManager::FindPCShop(DWORD dwVID)
    {
    	TShopMap::iterator it = m_map_pkShopByPC.find(dwVID);
    
    	if (it == m_map_pkShopByPC.end())
    		return NULL;
    
    	return it->second;
    }
    
    LPSHOP CShopManager::CreatePCShop(LPCHARACTER ch, TShopItemTable * pTable, BYTE bItemCount)
    {
    	if (FindPCShop(ch->GetVID()))
    		return NULL;
    
    	LPSHOP pkShop = M2_NEW CShop;
    	pkShop->SetPCShop(ch);
    	pkShop->SetShopItems(pTable, bItemCount);
    
    	m_map_pkShopByPC.insert(TShopMap::value_type(ch->GetVID(), pkShop));
    	return pkShop;
    }
    
    #include <boost/algorithm/string/replace.hpp>
    #ifdef OFFLINE_SHOP
    extern std::map<int, TShopCost> g_ShopCosts;
    bool CShopManager::CreateOfflineShop(LPCHARACTER owner, const char *szSign, const std::vector<TShopItemTable *> pTable, DWORD id)
    {
    	if (g_ShopCosts.find(id) == g_ShopCosts.end())
    	{
    		sys_log(0, "CreateOfflineShop:Shop days error %d", id);
    		return false;
    	}
    
    	DWORD map_index = owner->GetMapIndex();
    	long x = (owner->GetX() + number(50, 200));
    	long y = (owner->GetY() + number(50, 200));
    	long z = owner->GetZ();
    	char szQuery[4096];
    	DWORD date_close = get_global_time() + (g_ShopCosts[id].time * g_ShopCosts[id].days);
    	sprintf(szQuery, "insert into player_shop set item_count=%d,player_id=%d,name=\"%s\",map_index=%d,x=%ld,y=%ld,z=%ld,ip='%s',date=NOW(),date_close=FROM_UNIXTIME(%d),channel=%d",
    		pTable.size(), owner->GetPlayerID(), szSign, map_index, x, y, z, inet_ntoa(owner->GetDesc()->GetAddr().sin_addr), date_close, g_bChannel);
    	SQLMsg * pkMsg(DBManager::instance().DirectQuery(szQuery));
    	SQLResult * pRes = pkMsg->Get();
    	DWORD shop_id = pRes->uiInsertID;
    	if (shop_id>0)
    	{
    		for (int i = 0;i < pTable.size();i++)
    		{
    			LPITEM item = owner->GetItem(pTable[i]->pos);
    			if (item)
    			{
    				
    				char query[1024];
    				sprintf(query, "INSERT INTO player_shop_items SET");
    				sprintf(query, "%s player_id='%d'", query, owner->GetPlayerID());
    				sprintf(query, "%s, shop_id='%d'", query, shop_id);
    				sprintf(query, "%s, vnum='%d'", query, item->GetVnum());
    				sprintf(query, "%s, count='%d'", query, item->GetCount());
    #ifdef FULL_YANG
    				sprintf(query, "%s, price='%lld'", query, pTable[i]->price);
    #else
    				sprintf(query, "%s, price='%d'", query, pTable[i]->price);
    #endif
    				sprintf(query, "%s, display_pos='%u'", query, pTable[i]->display_pos);
    				for (int s = 0; s < ITEM_SOCKET_MAX_NUM; s++)
    				{
    					sprintf(query, "%s, socket%d='%ld'", query, s, item->GetSocket(s));
    					
    				}
    				for (int ia = 0; ia < ITEM_ATTRIBUTE_MAX_NUM; ia++)
    				{
    					const TPlayerItemAttribute& attr = item->GetAttribute(ia);
    					if (ia < 7)
    					{
    						sprintf(query, "%s, attrtype%d='%u'", query, ia, attr.bType);
    						sprintf(query, "%s, attrvalue%d='%d'", query, ia, attr.sValue);
    					}
    					else{
    						sprintf(query, "%s, applytype%d='%u'", query, ia - 7, attr.bType);
    						sprintf(query, "%s, applyvalue%d='%d'", query, ia - 7, attr.sValue);
    					}
    
    
    				}
    				DBManager::instance().DirectQuery(query);
    				ITEM_MANAGER::Instance().RemoveItem(item, "Priv shop");
    			}
    		}
    		StartOfflineShop(shop_id);
    		owner->SendShops();
    		return true;
    	}
    	return false;
    }
    
    bool CShopManager::StartOfflineShop(DWORD id, bool onboot)
    {
    
    	std::string name;
    	std::string shop_name(LC_TEXT("#PLAYER_NAME# magazin"));
    	DWORD pid,time;
    	long map_index, x, y, z;
    	SQLMsg * pkMsg(DBManager::instance().DirectQuery("SELECT player_shop.name,player_shop.player_id,player.name as player_name,player_shop.map_index,player_shop.x,player_shop.y,player_shop.z,UNIX_TIMESTAMP(player_shop.date_close),player_shop.id from player_shop left join player on player.id=player_shop.player_id where player_shop.id='%d'", id));
    	SQLResult * pRes = pkMsg->Get();
    	if (pRes->uiNumRows>0)
    	{
    		MYSQL_ROW row;
    		while ((row = mysql_fetch_row(pRes->pSQLResult)) != NULL)
    		{
    			name = row[0];
    			str_to_number(pid, row[1]);
    			boost::replace_all(shop_name, "#PLAYER_NAME#", row[2]);
    			str_to_number(map_index, row[3]);
    			str_to_number(x, row[4]);
    			str_to_number(y, row[5]);
    			str_to_number(z, row[6]);
    			str_to_number(time, row[7]);
    		}
    	}
    	if (map_index <= 0 || x <= 0 || y <= 0)
    	{
    		sys_err("location is null %d", id);
    		return false;
    	}
    
    	LPCHARACTER ch = CHARACTER_MANAGER::Instance().SpawnMob(30000, map_index, x, y, z, true, 0, false);
    	if (ch)
    	{
    		ch->SetName(shop_name.c_str());
    		ch->SetPrivShop(id);
    		ch->SetPrivShopOwner(pid);
    		ch->SetShopTime(time);
    		ch->Show(map_index, x, y, z);
    		ch->OpenShop(id, name.c_str(), onboot);
    
    		DBManager::instance().DirectQuery("UPDATE player_shop SET shop_vid=%d WHERE id=%d", (DWORD)ch->GetVID(), id);
    		LPCHARACTER owner = CHARACTER_MANAGER::instance().FindByPID(ch->GetPrivShopOwner());
    		if (owner)
    			owner->LoadPrivShops();
    		return true;
    	}
    	return false;
    }
    
    LPSHOP CShopManager::CreateNPCShop(LPCHARACTER ch, std::vector<TShopItemTable *> map_shop)
    {
    	if (FindPCShop(ch->GetPrivShop()))
    		return NULL;
    
    	LPSHOP pkShop = M2_NEW CShop;
    	pkShop->SetPCShop(ch);
    	pkShop->SetPrivShopItems(map_shop);
    	pkShop->SetLocked(false);
    	m_map_pkShopByPC.insert(TShopMap::value_type(ch->GetPrivShop(), pkShop));
    	return pkShop;
    }
    void CShopManager::DestroyPCShop(LPCHARACTER ch)
    {
    	LPSHOP pkShop = FindPCShop(ch->IsPrivShop() && ch->GetRaceNum() == 30000 ? ch->GetPrivShop() : ch->GetVID());
    
    	if (!pkShop)
    		return;
    
    	//PREVENT_ITEM_COPY;
    	ch->SetMyShopTime();
    	//END_PREVENT_ITEM_COPY
    
    	m_map_pkShopByPC.erase(ch->IsPrivShop() && ch->GetRaceNum() == 30000 ? ch->GetPrivShop() : ch->GetVID());
    	M2_DELETE(pkShop);
    }
    #else
    
    void CShopManager::DestroyPCShop(LPCHARACTER ch)
    {
    	LPSHOP pkShop = FindPCShop(ch->GetVID());
    
    	if (!pkShop)
    		return;
    
    	//PREVENT_ITEM_COPY;
    	ch->SetMyShopTime();
    	//END_PREVENT_ITEM_COPY
    
    	m_map_pkShopByPC.erase(ch->GetVID());
    	M2_DELETE(pkShop);
    }
    #endif
    
    // 상점 거래를 종료
    void CShopManager::StopShopping(LPCHARACTER ch)
    {
    	LPSHOP shop;
    
    	if (!(shop = ch->GetShop()))
    		return;
    
    	//PREVENT_ITEM_COPY;
    	ch->SetMyShopTime();
    	//END_PREVENT_ITEM_COPY
    	
    	shop->RemoveGuest(ch);
    	sys_log(0, "SHOP: END: %s", ch->GetName());
    }
    
    // 아이템 구입
    void CShopManager::Buy(LPCHARACTER ch, BYTE pos)
    {
    	if (!ch->GetShop())
    		return;
    
    	if (!ch->GetShopOwner())
    		return;
    
    	if (DISTANCE_APPROX(ch->GetX() - ch->GetShopOwner()->GetX(), ch->GetY() - ch->GetShopOwner()->GetY()) > 2000)
    	{
    		ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상점과의 거리가 너무 멀어 물건을 살 수 없습니다."));
    		return;
    	}
    
    	CShop* pkShop = ch->GetShop();
    
    	if (!pkShop->IsPCShop())
    	{
    		//if (pkShop->GetVnum() == 0)
    		//	return;
    		//const CMob* pkMob = CMobManager::instance().Get(pkShop->GetNPCVnum());
    		//if (!pkMob)
    		//	return;
    
    		//if (pkMob->m_table.bType != CHAR_TYPE_NPC)
    		//{
    		//	return;
    		//}
    	}
    	else
    	{
    	}
    
    	//PREVENT_ITEM_COPY
    	ch->SetMyShopTime();
    	//END_PREVENT_ITEM_COPY
    
    	int ret = pkShop->Buy(ch, pos);
    
    	if (SHOP_SUBHEADER_GC_OK != ret) // 문제가 있었으면 보낸다.
    	{
    		TPacketGCShop pack;
    
    		pack.header	= HEADER_GC_SHOP;
    		pack.subheader	= ret;
    		pack.size	= sizeof(TPacketGCShop);
    
    		ch->GetDesc()->Packet(&pack, sizeof(pack));
    	}
    }
    
    void CShopManager::Sell(LPCHARACTER ch, WORD bCell, BYTE bCount)
    {
    	if (!ch->GetShop())
    		return;
    
    	if (!ch->GetShopOwner())
    		return;
    
    	if (!ch->CanHandleItem())
    		return;
    
    	if (ch->GetShop()->IsPCShop())
    		return;
    
    	if (DISTANCE_APPROX(ch->GetX()-ch->GetShopOwner()->GetX(), ch->GetY()-ch->GetShopOwner()->GetY())>2000)
    	{
    		ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("상점과의 거리가 너무 멀어 물건을 팔 수 없습니다."));
    		return;
    	}
    	
    	LPITEM item = ch->GetInventoryItem(bCell);
    
    	if (!item)
    		return;
    
    	if (item->IsEquipped() == true)
    	{
    		ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("착용 중인 아이템은 판매할 수 없습니다."));
    		return;
    	}
    
    	if (true == item->isLocked())
    	{
    		return;
    	}
    
    	if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_SELL))
    		return;
    
    	long long dwPrice;
    
    	if (bCount == 0 || bCount > item->GetCount())
    		bCount = item->GetCount();
    
    	dwPrice = item->GetShopBuyPrice();
    
    	if (IS_SET(item->GetFlag(), ITEM_FLAG_COUNT_PER_1GOLD))
    	{
    		if (dwPrice == 0)
    			dwPrice = bCount;
    		else
    			dwPrice = bCount / dwPrice;
    	}
    	else
    		dwPrice *= bCount;
    
    	dwPrice /= 5;
    	
    	//세금 계산
    	DWORD dwTax = 0;
    	int iVal = 3;
    	
    	if (LC_IsYMIR() ||  LC_IsKorea())
    	{
    		dwTax = dwPrice * iVal / 100;
    		dwPrice -= dwTax;
    	}
    	else
    	{
    		dwTax = dwPrice * iVal/100;
    		dwPrice -= dwTax;
    	}
    
    	if (test_server)
    		sys_log(0, "Sell Item price id %d %s itemid %d", ch->GetPlayerID(), ch->GetName(), item->GetID());
    
    	const long long nTotalMoney = static_cast<long long>(ch->GetGold()) + static_cast<long long>(dwPrice);
    
    	if (GOLD_MAX <= nTotalMoney)
    	{
    		sys_err("[OVERFLOW_GOLD] id %u name %s gold %lld", ch->GetPlayerID(), ch->GetName(), ch->GetGold());
    		ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("20??? ???? ??? ?? ????."));
    		return;
    	}
    
    	sys_log(0, "SHOP: SELL: %s item name: %s(x%d):%u price: %lld", ch->GetName(), item->GetName(), bCount, item->GetID(), dwPrice);
    
    	if (iVal > 0)
    		ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("판매금액의 %d %% 가 세금으로 나가게됩니다"), iVal);
    
    	DBManager::instance().SendMoneyLog(MONEY_LOG_SHOP, item->GetVnum(), dwPrice);
    
    	if (bCount == item->GetCount())
    	{
    		// 한국에는 아이템을 버리고 복구해달라는 진상유저들이 많아서
    		// 상점 판매시 속성로그를 남긴다.
    		if (LC_IsYMIR())
    			item->AttrLog();
    
    		ITEM_MANAGER::instance().RemoveItem(item, "SELL");
    	}
    	else
    		item->SetCount(item->GetCount() - bCount);
    
    	//군주 시스템 : 세금 징수
    	CMonarch::instance().SendtoDBAddMoney(dwTax, ch->GetEmpire(), ch);
    
    	ch->PointChange(POINT_GOLD, dwPrice, false);
    }
    
    bool CompareShopItemName(const SShopItemTable& lhs, const SShopItemTable& rhs)
    {
    	TItemTable* lItem = ITEM_MANAGER::instance().GetTable(lhs.vnum);
    	TItemTable* rItem = ITEM_MANAGER::instance().GetTable(rhs.vnum);
    	if (lItem && rItem)
    		return strcmp(lItem->szLocaleName, rItem->szLocaleName) < 0;
    	else
    		return true;
    }
    
    bool ConvertToShopItemTable(IN CGroupNode* pNode, OUT TShopTableEx& shopTable)
    {
    	if (!pNode->GetValue("vnum", 0, shopTable.dwVnum))
    	{
    		sys_err("Group %s does not have vnum.", pNode->GetNodeName().c_str());
    		return false;
    	}
    
    	if (!pNode->GetValue("name", 0, shopTable.name))
    	{
    		sys_err("Group %s does not have name.", pNode->GetNodeName().c_str());
    		return false;
    	}
    	
    	if (shopTable.name.length() >= SHOP_TAB_NAME_MAX)
    	{
    		sys_err("Shop name length must be less than %d. Error in Group %s, name %s", SHOP_TAB_NAME_MAX, pNode->GetNodeName().c_str(), shopTable.name.c_str());
    		return false;
    	}
    
    	std::string stCoinType;
    	if (!pNode->GetValue("cointype", 0, stCoinType))
    	{
    		stCoinType = "Gold";
    	}
    	
    	if (boost::iequals(stCoinType, "Gold"))
    	{
    		shopTable.coinType = SHOP_COIN_TYPE_GOLD;
    	}
    	else if (boost::iequals(stCoinType, "SecondaryCoin"))
    	{
    		shopTable.coinType = SHOP_COIN_TYPE_SECONDARY_COIN;
    	}
    	else
    	{
    		sys_err("Group %s has undefine cointype(%s).", pNode->GetNodeName().c_str(), stCoinType.c_str());
    		return false;
    	}
    
    	CGroupNode* pItemGroup = pNode->GetChildNode("items");
    	if (!pItemGroup)
    	{
    		sys_err("Group %s does not have 'group items'.", pNode->GetNodeName().c_str());
    		return false;
    	}
    
    	int itemGroupSize = pItemGroup->GetRowCount();
    	std::vector <TShopItemTable> shopItems(itemGroupSize);
    	if (itemGroupSize >= SHOP_HOST_ITEM_MAX_NUM)
    	{
    		sys_err("count(%d) of rows of group items of group %s must be smaller than %d", itemGroupSize, pNode->GetNodeName().c_str(), SHOP_HOST_ITEM_MAX_NUM);
    		return false;
    	}
    
    	for (int i = 0; i < itemGroupSize; i++)
    	{
    		if (!pItemGroup->GetValue(i, "vnum", shopItems[i].vnum))
    		{
    			sys_err("row(%d) of group items of group %s does not have vnum column", i, pNode->GetNodeName().c_str());
    			return false;
    		}
    		
    		if (!pItemGroup->GetValue(i, "count", shopItems[i].count))
    		{
    			sys_err("row(%d) of group items of group %s does not have count column", i, pNode->GetNodeName().c_str());
    			return false;
    		}
    		if (!pItemGroup->GetValue(i, "price", shopItems[i].price))
    		{
    			sys_err("row(%d) of group items of group %s does not have price column", i, pNode->GetNodeName().c_str());
    			return false;
    		}
    	}
    	std::string stSort;
    	if (!pNode->GetValue("sort", 0, stSort))
    	{
    		stSort = "None";
    	}
    
    	if (boost::iequals(stSort, "Asc"))
    	{
    		std::sort(shopItems.begin(), shopItems.end(), CompareShopItemName);
    	}
    	else if(boost::iequals(stSort, "Desc"))
    	{
    		std::sort(shopItems.rbegin(), shopItems.rend(), CompareShopItemName);
    	}
    
    	CGrid grid = CGrid(5, 9);
    	int iPos;
    
    	memset(&shopTable.items[0], 0, sizeof(shopTable.items));
    
    	for (unsigned int i = 0; i < shopItems.size(); i++)
    	{
    		TItemTable * item_table = ITEM_MANAGER::instance().GetTable(shopItems[i].vnum);
    		if (!item_table)
    		{
    			sys_err("vnum(%d) of group items of group %s does not exist", shopItems[i].vnum, pNode->GetNodeName().c_str());
    			return false;
    		}
    
    		iPos = grid.FindBlank(1, item_table->bSize);
    
    		grid.Put(iPos, 1, item_table->bSize);
    		shopTable.items[iPos] = shopItems[i];
    	}
    
    	shopTable.byItemCount = shopItems.size();
    	return true;
    }
    
    bool CShopManager::ReadShopTableEx(const char* stFileName)
    {
    	// file 유무 체크.
    	// 없는 경우는 에러로 처리하지 않는다.
    	FILE* fp = fopen(stFileName, "rb");
    	if (NULL == fp)
    		return true;
    	fclose(fp);
    
    	CGroupTextParseTreeLoader loader;
    	if (!loader.Load(stFileName))
    	{
    		sys_err("%s Load fail.", stFileName);
    		return false;
    	}
    
    	CGroupNode* pShopNPCGroup = loader.GetGroup("shopnpc");
    	if (NULL == pShopNPCGroup)
    	{
    		sys_err("Group ShopNPC is not exist.");
    		return false;
    	}
    
    	typedef std::multimap <DWORD, TShopTableEx> TMapNPCshop;
    	TMapNPCshop map_npcShop;
    	for (int i = 0; i < pShopNPCGroup->GetRowCount(); i++)
    	{
    		DWORD npcVnum;
    		std::string shopName;
    		if (!pShopNPCGroup->GetValue(i, "npc", npcVnum) || !pShopNPCGroup->GetValue(i, "group", shopName))
    		{
    			sys_err("Invalid row(%d). Group ShopNPC rows must have 'npc', 'group' columns", i);
    			return false;
    		}
    		std::transform(shopName.begin(), shopName.end(), shopName.begin(), (int(*)(int))std::tolower);
    		CGroupNode* pShopGroup = loader.GetGroup(shopName.c_str());
    		if (!pShopGroup)
    		{
    			sys_err("Group %s is not exist.", shopName.c_str());
    			return false;
    		}
    		TShopTableEx table;
    		if (!ConvertToShopItemTable(pShopGroup, table))
    		{
    			sys_err("Cannot read Group %s.", shopName.c_str());
    			return false;
    		}
    		if (m_map_pkShopByNPCVnum.find(npcVnum) != m_map_pkShopByNPCVnum.end())
    		{
    			sys_err("%d cannot have both original shop and extended shop", npcVnum);
    			return false;
    		}
    		
    		map_npcShop.insert(TMapNPCshop::value_type(npcVnum, table));	
    	}
    
    	for (TMapNPCshop::iterator it = map_npcShop.begin(); it != map_npcShop.end(); ++it)
    	{
    		DWORD npcVnum = it->first;
    		TShopTableEx& table = it->second;
    		if (m_map_pkShop.find(table.dwVnum) != m_map_pkShop.end())
    		{
    			sys_err("Shop vnum(%d) already exists", table.dwVnum);
    			return false;
    		}
    		TShopMap::iterator shop_it = m_map_pkShopByNPCVnum.find(npcVnum);
    		
    		LPSHOPEX pkShopEx = NULL;
    		if (m_map_pkShopByNPCVnum.end() == shop_it)
    		{
    			pkShopEx = M2_NEW CShopEx;
    			pkShopEx->Create(0, npcVnum);
    			m_map_pkShopByNPCVnum.insert(TShopMap::value_type(npcVnum, pkShopEx));
    		}
    		else
    		{
    			pkShopEx = dynamic_cast <CShopEx*> (shop_it->second);
    			if (NULL == pkShopEx)
    			{
    				sys_err("WTF!!! It can't be happend. NPC(%d) Shop is not extended version.", shop_it->first);
    				return false;
    			}
    		}
    
    		if (pkShopEx->GetTabCount() >= SHOP_TAB_COUNT_MAX)
    		{
    			sys_err("ShopEx cannot have tab more than %d", SHOP_TAB_COUNT_MAX);
    			return false;
    		}
    
    		if (pkShopEx->GetVnum() != 0 && m_map_pkShop.find(pkShopEx->GetVnum()) != m_map_pkShop.end())
    		{
    			sys_err("Shop vnum(%d) already exist.", pkShopEx->GetVnum());
    			return false;
    		}
    		m_map_pkShop.insert(TShopMap::value_type (pkShopEx->GetVnum(), pkShopEx));
    		pkShopEx->AddShopTable(table);
    	}
    
    	return true;
    }
    
    #if defined(ENABLE_RENEWAL_SHOPEX)
    bool CShopManager::InitializeShopEX(TShopTable* table, int size)
    {
    	typedef std::multimap <DWORD, TShopTableEx> TMapNPCshop;
    	TMapNPCshop map_npcShop;
    
    	for (int i = 0; i < size; ++i, ++table)
    	{
    		TShopTableEx shopTable;
    		shopTable.dwVnum = table->dwVnum;
    		shopTable.name = table->szShopName;
    		shopTable.coinType = SHOP_COIN_TYPE_GOLD;
    
    		if (shopTable.name.length() >= SHOP_TAB_NAME_MAX) {
    			sys_err("Shop name length must be less than %d. Error %s", SHOP_TAB_NAME_MAX, shopTable.name.c_str());
    			return false;
    		}
    
    		CGrid grid(10, 8);
    
    		memset(&shopTable.items[0], 0, sizeof(shopTable.items));
    
    		for (size_t j = 0; j < table->byItemCount; j++)
    		{
    			TItemTable* item_table = ITEM_MANAGER::instance().GetTable(table->items[j].vnum);
    			if (!item_table) {
    				sys_err("vnum(%d) of group items of group %s does not exist.", table->items[j].vnum, shopTable.name.c_str());
    				return false;
    			}
    
    			int iPos = grid.FindBlank(1, item_table->bSize);
    			if (iPos == -1) {
    				sys_err("vnum(%d) of group items of group %s there is no space!", table->items[j].vnum, shopTable.name.c_str());
    				return false;
    			}
    
    			grid.Put(iPos, 1, item_table->bSize);
    			shopTable.items[iPos] = table->items[j];
    			shopTable.byItemCount++;
    		}
    		if (m_map_pkShopByNPCVnum.find(table->dwNPCVnum) != m_map_pkShopByNPCVnum.end()) {
    			sys_err("NPCVNUM(%d) already used.", table->dwNPCVnum);
    			return false;
    		}
    
    		map_npcShop.insert(TMapNPCshop::value_type(table->dwNPCVnum, shopTable));
    	}
    
    	for (TMapNPCshop::iterator it = map_npcShop.begin(); it != map_npcShop.end(); ++it)
    	{
    		const DWORD npcVnum = it->first;
    		TShopTableEx& table = it->second;
    
    		if (m_map_pkShop.find(table.dwVnum) != m_map_pkShop.end()) {
    			sys_err("Shop vnum(%d) already exists.", table.dwVnum);
    			return false;
    		}
    
    		TShopMap::const_iterator shop_it = m_map_pkShopByNPCVnum.find(npcVnum);
    		LPSHOPEX pkShopEx = NULL;
    
    		if (m_map_pkShopByNPCVnum.end() == shop_it) {
    			pkShopEx = M2_NEW CShopEx;
    			if (!pkShopEx->Create(0, npcVnum))
    			{
    				sys_err("ShopEx cannot create shop with npc vnum %u.", npcVnum);
    				M2_DELETE(pkShopEx);
    				return false;
    			}
    			m_map_pkShopByNPCVnum.insert(TShopMap::value_type(npcVnum, pkShopEx));
    		}
    		else {
    			pkShopEx = dynamic_cast <CShopEx*> (shop_it->second);
    			if (!pkShopEx) {
    				sys_err("NPC(%d) Shop is not extended version.", shop_it->first);
    				return false;
    			}
    		}
    
    		if (pkShopEx->GetTabCount() >= SHOP_TAB_COUNT_MAX) {
    			sys_err("ShopEx cannot have tab more than %d.", SHOP_TAB_COUNT_MAX);
    			return false;
    		}
    
    		pkShopEx->AddShopTable(table);
    		m_map_pkShop.insert(TShopMap::value_type(table.dwVnum, pkShopEx));
    	}
    
    	return true;
    }
    #endif
    
    void CShopManager::ReloadShops()
    {
    	sys_log(0, "SHOP RELOAD START");
    	Destroy();
    	char szShopTableExFileName[256];
    	snprintf(szShopTableExFileName, sizeof(szShopTableExFileName),
    		"%s/shop_table_ex.txt", LocaleService_GetBasePath().c_str());
    	ReadShopTableEx(szShopTableExFileName);
    	sys_log(0, "SHOP RELOAD DONE");
    }
    
    trigger.cpp:
    Code:
    #include "stdafx.h"
    #include "utils.h"
    #include "config.h"
    #include "char.h"
    #include "sectree_manager.h"
    #include "battle.h"
    #include "affect.h"
    #include "shop_manager.h"
    
    int	OnClickShop(TRIGGERPARAM);
    int	OnClickTalk(TRIGGERPARAM);
    
    int	OnIdleDefault(TRIGGERPARAM);
    int	OnAttackDefault(TRIGGERPARAM);
    
    typedef struct STriggerFunction
    {
    	int (*func) (TRIGGERPARAM);
    } TTriggerFunction;
    
    TTriggerFunction OnClickTriggers[ON_CLICK_MAX_NUM] =
    {
    	{ NULL,          	},	// ON_CLICK_NONE,
    	{ OnClickShop,	},	// ON_CLICK_SHOP,
    };
    
    void CHARACTER::AssignTriggers(const TMobTable * table)
    {
    	if (table->bOnClickType >= ON_CLICK_MAX_NUM)
    	{
    		sys_err("%s has invalid OnClick value %d", GetName(), table->bOnClickType);
    		abort();
    	}
    
    	m_triggerOnClick.bType = table->bOnClickType;
    	m_triggerOnClick.pFunc = OnClickTriggers[table->bOnClickType].func;
    }
    
    /*
     * ON_CLICK
     */
    int OnClickShop(TRIGGERPARAM)
    {
    	if (!ch || !causer)
    	{
    		sys_err("OnClickShop: null pointer");
    		return 0;
    	}
    
    	CShopManager::instance().StartShopping(causer, ch);
    	return 1;
    }
    
    /*
     * 몬스터 AI 함수들을 BattleAI 클래스로 수정
     */
    int OnIdleDefault(TRIGGERPARAM)
    {
    	if (ch->OnIdle())
    		return PASSES_PER_SEC(1);
    
    	return PASSES_PER_SEC(1);
    }
    
    class FuncFindMobVictim
    {
    	public:
    		FuncFindMobVictim(LPCHARACTER pkChr, int iMaxDistance) :
    			m_pkChr(pkChr),
    			m_iMinDistance(~(1L << 31)),
    			m_iMaxDistance(iMaxDistance),
    			m_lx(pkChr->GetX()),
    			m_ly(pkChr->GetY()),
    			m_pkChrVictim(NULL),
    			m_pkChrBuilding(NULL)
    	{
    	};
    
    		bool operator () (LPENTITY ent)
    		{
    			if (!ent->IsType(ENTITY_CHARACTER))
    				return false;
    
    			LPCHARACTER pkChr = (LPCHARACTER) ent;
    
    			if (pkChr->IsBuilding() && 
    				(pkChr->IsAffectFlag(AFF_BUILDING_CONSTRUCTION_SMALL) ||
    				 pkChr->IsAffectFlag(AFF_BUILDING_CONSTRUCTION_LARGE) ||
    				 pkChr->IsAffectFlag(AFF_BUILDING_UPGRADE)))
    			{
    				m_pkChrBuilding = pkChr;
    			}
    
    			if (pkChr->IsNPC())
    			{
    				if ( !pkChr->IsMonster() || !m_pkChr->IsAttackMob() || m_pkChr->IsAggressive()  )
    					return false;
    					
    			}
    
    			if (pkChr->IsDead())
    				return false;
    
    			if (pkChr->IsAffectFlag(AFF_EUNHYUNG) || 
    					pkChr->IsAffectFlag(AFF_INVISIBILITY) ||
    					pkChr->IsAffectFlag(AFF_REVIVE_INVISIBLE))
    				return false;
    
    			if (pkChr->IsAffectFlag(AFF_TERROR) && m_pkChr->IsImmune(IMMUNE_TERROR) == false )	// 공포 처리
    			{
    				if ( pkChr->GetLevel() >= m_pkChr->GetLevel() )
    					return false;
    			}
    
    		 	if ( m_pkChr->IsNoAttackShinsu() )
    			{
    				if ( pkChr->GetEmpire() == 1 )
    					return false;
    			}
    
    			if ( m_pkChr->IsNoAttackChunjo() )
    			{
    				if ( pkChr->GetEmpire() == 2 )
    					return false;
    			}
    			
    
    			if ( m_pkChr->IsNoAttackJinno() )
    			{
    				if ( pkChr->GetEmpire() == 3 )
    					return false;
    			}
    
    			int iDistance = DISTANCE_APPROX(m_lx - pkChr->GetX(), m_ly - pkChr->GetY());
    
    			if (iDistance < m_iMinDistance && iDistance <= m_iMaxDistance)
    			{
    				m_pkChrVictim = pkChr;
    				m_iMinDistance = iDistance;
    			}
    			return true;
    		}
    
    		LPCHARACTER GetVictim()
    		{
    			// 근처에 건물이 있고 피가 많이 있다면 건물을 공격한다. 건물만 있어도 건물을 공격
    			if (m_pkChrBuilding && m_pkChr->GetHP() * 2 > m_pkChr->GetMaxHP() || !m_pkChrVictim)
    			{
    				return m_pkChrBuilding;
    			}
    
    			return (m_pkChrVictim);
    		}
    
    	private:
    		LPCHARACTER	m_pkChr;
    
    		int		m_iMinDistance;
    		int		m_iMaxDistance;
    		long		m_lx;
    		long		m_ly;
    
    		LPCHARACTER	m_pkChrVictim;
    		LPCHARACTER	m_pkChrBuilding;
    };
    
    LPCHARACTER FindVictim(LPCHARACTER pkChr, int iMaxDistance)
    {
    	FuncFindMobVictim f(pkChr, iMaxDistance);
    	if (pkChr->GetSectree() != NULL) {
    		pkChr->GetSectree()->ForEachAround(f);	
    	}
    	return f.GetVictim();
    }
    
    
    Membru Ucenic Membru Ucenic
  • Mesaje: 15
  • Reacții: 2
  • Mesaje utile: 0
  • Status: ^.^
  • Server: (Regal) // Trot-OSBD
  • Regat: Chunjo
  • [ID Discord]
  • Contact:

    Medalii

    Avatar utilizator
    Membru Ucenic
    Membru Ucenic

    Mesaj de Desmond97 »

    item_manager.h:
    Code:
    #ifndef __INC_ITEM_MANAGER__
    #define __INC_ITEM_MANAGER__
    
    #ifdef M2_USE_POOL
    #include "pool.h"
    #endif
    
    // special_item_group.txt에서 정의하는 속성 그룹
    // type attr로 선언할 수 있다.
    // 이 속성 그룹을 이용할 수 있는 것은 special_item_group.txt에서 Special type으로 정의된 그룹에 속한 UNIQUE ITEM이다.
    class CSpecialAttrGroup
    {
    public:
    	CSpecialAttrGroup(DWORD vnum)
    		: m_dwVnum(vnum)
    	{}
    	struct CSpecialAttrInfo
    	{
    		CSpecialAttrInfo (DWORD _apply_type, DWORD _apply_value)
    			: apply_type(_apply_type), apply_value(_apply_value)
    		{}
    		DWORD apply_type;
    		DWORD apply_value;
    
    	};
    	DWORD m_dwVnum;
    	std::string	m_stEffectFileName;
    	std::vector<CSpecialAttrInfo> m_vecAttrs;
    };
    
    class CSpecialItemGroup
    {
    	public:
    		enum EGiveType
    		{
    			NONE,
    			GOLD,
    			EXP,
    			MOB,
    			SLOW,
    			DRAIN_HP,
    			POISON,
    			MOB_GROUP,
    		};
    
    		// QUEST 타입은 퀘스트 스크립트에서 vnum.sig_use를 사용할 수 있는 그룹이다.
    		//		단, 이 그룹에 들어가기 위해서는 ITEM 자체의 TYPE이 QUEST여야 한다.
    		// SPECIAL 타입은 idx, item_vnum, attr_vnum을 입력한다. attr_vnum은 위에 CSpecialAttrGroup의 Vnum이다.
    		//		이 그룹에 들어있는 아이템은 같이 착용할 수 없다.
    		enum ESIGType { NORMAL, PCT, QUEST, SPECIAL };
    
    		struct CSpecialItemInfo
    		{
    			DWORD vnum;
    			int count;
    			int rare;
    
    			CSpecialItemInfo(DWORD _vnum, int _count, int _rare)
    				: vnum(_vnum), count(_count), rare(_rare)
    				{}
    		};
    
    		CSpecialItemGroup(DWORD vnum, BYTE type=0)
    			: m_dwVnum(vnum), m_bType(type)
    			{}
    
    		void AddItem(DWORD vnum, int count, int prob, int rare)
    		{
    			if (!prob)
    				return;
    			if (!m_vecProbs.empty())
    				prob += m_vecProbs.back();
    			m_vecProbs.push_back(prob);
    			m_vecItems.push_back(CSpecialItemInfo(vnum, count, rare));
    		}
    
    		bool IsEmpty() const
    		{
    			return m_vecProbs.empty();
    		}
    
    		// Type Multi, 즉 m_bType == PCT 인 경우,
    		// 확률을 더해가지 않고, 독립적으로 계산하여 아이템을 생성한다.
    		// 따라서 여러 개의 아이템이 생성될 수 있다.
    		// by rtsummit
    		int GetMultiIndex(std::vector <int> &idx_vec) const
    		{
    			idx_vec.clear();
    			if (m_bType == PCT)
    			{
    				int count = 0;
    				if (number(1,100) <= m_vecProbs[0])
    				{
    					idx_vec.push_back(0);
    					count++;
    				}
    				for (uint i = 1; i < m_vecProbs.size(); i++)
    				{
    					if (number(1,100) <= m_vecProbs[i] - m_vecProbs[i-1])
    					{
    						idx_vec.push_back(i);
    						count++;
    					}
    				}
    				return count;
    			}
    			else
    			{
    				idx_vec.push_back(GetOneIndex());
    				return 1;
    			}
    		}
    
    		int GetOneIndex() const
    		{
    			int n = number(1, m_vecProbs.back());
    			auto it = lower_bound(m_vecProbs.begin(), m_vecProbs.end(), n);
    			return std::distance(m_vecProbs.begin(), it);
    		}
    
    		int GetVnum(int idx) const
    		{
    			return m_vecItems[idx].vnum;
    		}
    
    		int GetCount(int idx) const
    		{
    			return m_vecItems[idx].count;
    		}
    
    		int GetRarePct(int idx) const
    		{
    			return m_vecItems[idx].rare;
    		}
    
    		bool Contains(DWORD dwVnum) const
    		{
    			for (DWORD i = 0; i < m_vecItems.size(); i++)
    			{
    				if (m_vecItems[i].vnum == dwVnum)
    					return true;
    			}
    			return false;
    		}
    		
    		// Group의 Type이 Special인 경우에
    		// dwVnum에 매칭되는 AttrVnum을 return해준다.
    		DWORD GetAttrVnum(DWORD dwVnum) const
    		{
    			if (CSpecialItemGroup::SPECIAL != m_bType)
    				return 0;
    			for (auto it = m_vecItems.begin(); it != m_vecItems.end(); it++)
    			{
    				if (it->vnum == dwVnum)
    				{
    					return it->count;
    				}
    			}
    			return 0;
    		}
    
    		DWORD m_dwVnum;
    		BYTE	m_bType;
    		std::vector<int> m_vecProbs;
    		std::vector<CSpecialItemInfo> m_vecItems; // vnum, count
    };
    
    class CMobItemGroup
    {
    	public:
    		struct SMobItemGroupInfo
    		{
    			DWORD dwItemVnum;
    			int iCount;
    			int iRarePct;
    
    			SMobItemGroupInfo(DWORD dwItemVnum, int iCount, int iRarePct)
    				: dwItemVnum(dwItemVnum),
    			iCount(iCount),
    			iRarePct(iRarePct)
    			{
    			}
    		};
    
    		CMobItemGroup(DWORD dwMobVnum, int iKillDrop, const std::string& r_stName)
    			:
    			m_dwMobVnum(dwMobVnum),
    		m_iKillDrop(iKillDrop),
    		m_stName(r_stName)
    		{
    		}
    
    		int GetKillPerDrop() const
    		{
    			return m_iKillDrop;
    		}
    
    		void AddItem(DWORD dwItemVnum, int iCount, int iPartPct, int iRarePct)
    		{
    			if (!m_vecProbs.empty())
    				iPartPct += m_vecProbs.back();
    			m_vecProbs.push_back(iPartPct);
    			m_vecItems.push_back(SMobItemGroupInfo(dwItemVnum, iCount, iRarePct));
    		}
    
    		// MOB_DROP_ITEM_BUG_FIX
    		bool IsEmpty() const
    		{
    			return m_vecProbs.empty();
    		}
    
    		int GetOneIndex() const
    		{
    			int n = number(1, m_vecProbs.back());
    			auto it = lower_bound(m_vecProbs.begin(), m_vecProbs.end(), n);
    			return std::distance(m_vecProbs.begin(), it);
    		}
    		// END_OF_MOB_DROP_ITEM_BUG_FIX
    
    		const SMobItemGroupInfo& GetOne() const
    		{
    			return m_vecItems[GetOneIndex()];
    		}
    
    	private:
    		DWORD m_dwMobVnum;
    		int m_iKillDrop;
    		std::string m_stName;
    		std::vector<int> m_vecProbs;
    		std::vector<SMobItemGroupInfo> m_vecItems;
    };
    
    class CDropItemGroup
    {
    	struct SDropItemGroupInfo
    	{
    		DWORD	dwVnum;
    		DWORD	dwPct;
    		int	iCount;
    
    		SDropItemGroupInfo(DWORD dwVnum, DWORD dwPct, int iCount)
    			: dwVnum(dwVnum), dwPct(dwPct), iCount(iCount)
    			{}
    	};
    
    	public:
    	CDropItemGroup(DWORD dwVnum, DWORD dwMobVnum, const std::string& r_stName)
    		:
    		m_dwVnum(dwVnum),
    	m_dwMobVnum(dwMobVnum),
    	m_stName(r_stName)
    	{
    	}
    
    	const std::vector<SDropItemGroupInfo> & GetVector()
    	{
    		return m_vec_items;
    	}
    
    	void AddItem(DWORD dwItemVnum, DWORD dwPct, int iCount)
    	{
    		m_vec_items.push_back(SDropItemGroupInfo(dwItemVnum, dwPct, iCount));
    	}
    
    	private:
    	DWORD m_dwVnum;
    	DWORD m_dwMobVnum;
    	std::string m_stName;
    	std::vector<SDropItemGroupInfo> m_vec_items;
    };
    
    class CLevelItemGroup
    {
    	struct SLevelItemGroupInfo
    	{
    		DWORD dwVNum;
    		DWORD dwPct;
    		int iCount;
    
    		SLevelItemGroupInfo(DWORD dwVnum, DWORD dwPct, int iCount)
    			: dwVNum(dwVnum), dwPct(dwPct), iCount(iCount)
    		{ }
    	};
    
    	private :
    		DWORD m_dwLevelLimit;
    		std::string m_stName;
    		std::vector<SLevelItemGroupInfo> m_vec_items;
    
    	public :
    		CLevelItemGroup(DWORD dwLevelLimit)
    			: m_dwLevelLimit(dwLevelLimit)
    		{}
    
    		DWORD GetLevelLimit() { return m_dwLevelLimit; }
    
    		void AddItem(DWORD dwItemVnum, DWORD dwPct, int iCount)
    		{
    			m_vec_items.push_back(SLevelItemGroupInfo(dwItemVnum, dwPct, iCount));
    		}
    
    		const std::vector<SLevelItemGroupInfo> & GetVector()
    		{
    			return m_vec_items;
    		}
    };
    
    class CBuyerThiefGlovesItemGroup
    {
    	struct SThiefGroupInfo
    	{
    		DWORD	dwVnum;
    		DWORD	dwPct;
    		int	iCount;
    
    		SThiefGroupInfo(DWORD dwVnum, DWORD dwPct, int iCount)
    			: dwVnum(dwVnum), dwPct(dwPct), iCount(iCount)
    			{}
    	};
    
    	public:
    	CBuyerThiefGlovesItemGroup(DWORD dwVnum, DWORD dwMobVnum, const std::string& r_stName)
    		:
    		m_dwVnum(dwVnum),
    	m_dwMobVnum(dwMobVnum),
    	m_stName(r_stName)
    	{
    	}
    
    	const std::vector<SThiefGroupInfo> & GetVector()
    	{
    		return m_vec_items;
    	}
    
    	void AddItem(DWORD dwItemVnum, DWORD dwPct, int iCount)
    	{
    		m_vec_items.push_back(SThiefGroupInfo(dwItemVnum, dwPct, iCount));
    	}
    
    	private:
    	DWORD m_dwVnum;
    	DWORD m_dwMobVnum;
    	std::string m_stName;
    	std::vector<SThiefGroupInfo> m_vec_items;
    };
    
    class ITEM;
    
    class ITEM_MANAGER : public singleton<ITEM_MANAGER>
    {
    	public:
    		ITEM_MANAGER();
    		virtual ~ITEM_MANAGER();
    
    		bool			Initialize(TItemTable * table, int size);
    	public:
    		void			ReloadProto();
    		void			Destroy();
    		void			Update();	// 매 루프마다 부른다.
    		void			GracefulShutdown();
    
    		DWORD			GetNewID();
    		bool			SetMaxItemID(TItemIDRangeTable range); // 최대 고유 아이디를 지정
    		bool			SetMaxSpareItemID(TItemIDRangeTable range);
    
    		// DelayedSave: 어떠한 루틴 내에서 저장을 해야 할 짓을 많이 하면 저장
    		// 쿼리가 너무 많아지므로 "저장을 한다" 라고 표시만 해두고 잠깐
    		// (예: 1 frame) 후에 저장시킨다.
    		void			DelayedSave(LPITEM item);
    		void			FlushDelayedSave(LPITEM item); // Delayed 리스트에 있다면 지우고 저장한다. 끊김 처리시 사용 됨.
    		void			SaveSingleItem(LPITEM item);
    
    		LPITEM                  CreateItem(DWORD vnum, DWORD count = 1, DWORD dwID = 0, bool bTryMagic = false, int iRarePct = -1, bool bSkipSave = false);
    #ifndef DEBUG_ALLOC
    		void DestroyItem(LPITEM item);
    #else
    		void DestroyItem(LPITEM item, const char* file, size_t line);
    #endif
    		void			RemoveItem(LPITEM item, const char * c_pszReason=NULL); // 사용자로 부터 아이템을 제거
    
    		LPITEM			Find(DWORD id);
    		LPITEM                  FindByVID(DWORD vid);
    		TItemTable *            GetTable(DWORD vnum);
    		bool			GetVnum(const char * c_pszName, DWORD & r_dwVnum);
    		bool			GetVnumByOriginalName(const char * c_pszName, DWORD & r_dwVnum);
    
    		bool			GetDropPct(LPCHARACTER pkChr, LPCHARACTER pkKiller, OUT int& iDeltaPercent, OUT int& iRandRange);
    		bool			CreateDropItem(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item);
    
    		bool			ReadCommonDropItemFile(const char * c_pszFileName);
    		bool			ReadEtcDropItemFile(const char * c_pszFileName);
    		bool			ReadDropItemGroup(const char * c_pszFileName);
    		bool			ReadMonsterDropItemGroup(const char * c_pszFileName);
    		bool			ReadSpecialDropItemFile(const char * c_pszFileName);
    		
    		// convert name -> vnum special_item_group.txt
    		bool			ConvSpecialDropItemFile();
    		// convert name -> vnum special_item_group.txt
    
    		DWORD			GetRefineFromVnum(DWORD dwVnum);
    
    		static void		CopyAllAttrTo(LPITEM pkOldItem, LPITEM pkNewItem);		// pkNewItem으로 모든 속성과 소켓 값들을 목사하는 함수.
    
    
    		const CSpecialItemGroup* GetSpecialItemGroup(DWORD dwVnum);
    		const CSpecialAttrGroup* GetSpecialAttrGroup(DWORD dwVnum);
    
    		const std::vector<TItemTable> & GetTable() { return m_vec_prototype; }
    
    		// CHECK_UNIQUE_GROUP
    		int			GetSpecialGroupFromItem(DWORD dwVnum) const { auto it = m_ItemToSpecialGroup.find(dwVnum); return (it == m_ItemToSpecialGroup.end()) ? 0 : it->second; }
    		// END_OF_CHECK_UNIQUE_GROUP
    
    	protected:
    		int                     RealNumber(DWORD vnum);
    		void			CreateQuestDropItem(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item, int iDeltaPercent, int iRandRange);
    
    	protected:
    		typedef std::map<DWORD, LPITEM> ITEM_VID_MAP;		
    
    		std::vector<TItemTable>		m_vec_prototype;
    		std::vector<TItemTable*> m_vec_item_vnum_range_info;
    		std::map<DWORD, DWORD>		m_map_ItemRefineFrom;
    		int				m_iTopOfTable;
    
    		ITEM_VID_MAP			m_VIDMap;			///< m_dwVIDCount 의 값단위로 아이템을 저장한다.
    		DWORD				m_dwVIDCount;			///< 이녀석 VID가 아니라 그냥 프로세스 단위 유니크 번호다.
    		DWORD				m_dwCurrentID;
    		TItemIDRangeTable	m_ItemIDRange;
    		TItemIDRangeTable	m_ItemIDSpareRange;
    
    		TR1_NS::unordered_set<LPITEM> m_set_pkItemForDelayedSave;
    		std::map<DWORD, LPITEM>		m_map_pkItemByID;
    		std::map<DWORD, DWORD>		m_map_dwEtcItemDropProb;
    		std::map<DWORD, CDropItemGroup*> m_map_pkDropItemGroup;
    		std::map<DWORD, CSpecialItemGroup*> m_map_pkSpecialItemGroup;
    		std::map<DWORD, CSpecialItemGroup*> m_map_pkQuestItemGroup;
    		std::map<DWORD, CSpecialAttrGroup*> m_map_pkSpecialAttrGroup;
    		std::map<DWORD, CMobItemGroup*> m_map_pkMobItemGroup;
    		std::map<DWORD, CLevelItemGroup*> m_map_pkLevelItemGroup;
    		std::map<DWORD, CBuyerThiefGlovesItemGroup*> m_map_pkGloveItemGroup;
    
    		// CHECK_UNIQUE_GROUP
    		std::map<DWORD, int>		m_ItemToSpecialGroup;
    		// END_OF_CHECK_UNIQUE_GROUP
    	
    	private:
    		// 독일에서 기존 캐시 아이템과 같지만, 교환 가능한 캐시 아이템을 만든다고 하여,
    		// 오리지널 아이템에 교환 금지 플래그만 삭제한 새로운 아이템들을 만들어,
    		// 새로운 아이템 대역을 할당하였다.
    		// 문제는 새로운 아이템도 오리지널 아이템과 같은 효과를 내야하는데,
    		// 서버건, 클라건, vnum 기반으로 되어있어
    		// 새로운 vnum을 죄다 서버에 새로 다 박아야하는 안타까운 상황에 맞닿았다.
    		// 그래서 새 vnum의 아이템이면, 서버에서 돌아갈 때는 오리지널 아이템 vnum으로 바꿔서 돌고 하고,
    		// 저장할 때에 본래 vnum으로 바꿔주도록 한다.
    		// 이를 위해 오리지널 vnum과 새로운 vnum을 연결시켜주는 맵을 만듦.
    		typedef std::map <DWORD, DWORD> TMapDW2DW;
    		TMapDW2DW	m_map_new_to_ori;
    
    	public:	
    		DWORD	GetMaskVnum(DWORD dwVnum);
    		std::map<DWORD, TItemTable>  m_map_vid;
    		std::map<DWORD, TItemTable>&  GetVIDMap() { return m_map_vid; }
    		std::vector<TItemTable>& GetVecProto() { return m_vec_prototype; }	
    		
    		const static int MAX_NORM_ATTR_NUM = 5;
    		const static int MAX_RARE_ATTR_NUM = 2;
    		bool ReadItemVnumMaskTable(const char * c_pszFileName);
    	private:
    #ifdef M2_USE_POOL
    		ObjectPool<CItem> pool_;
    #endif
    };
    
    #ifndef DEBUG_ALLOC
    #define M2_DESTROY_ITEM(ptr) ITEM_MANAGER::instance().DestroyItem(ptr)
    #else
    #define M2_DESTROY_ITEM(ptr) ITEM_MANAGER::instance().DestroyItem(ptr, __FILE__, __LINE__)
    #endif
    
    #endif
    
    item_manager.cpp:
    Code:
    #include "stdafx.h"
    #include "utils.h"
    #include "config.h"
    #include "char.h"
    #include "char_manager.h"
    #include "desc_client.h"
    #include "db.h"
    #include "log.h"
    #include "skill.h"
    #include "text_file_loader.h"
    #include "priv_manager.h"
    #include "questmanager.h"
    #include "unique_item.h"
    #include "safebox.h"
    #include "blend_item.h"
    #include "dev_log.h"
    #include "locale_service.h"
    #include "item.h"
    #include "item_manager.h"
    
    #include "../common/VnumHelper.h"
    #include "DragonSoul.h"
    #include "cube.h"
    #include "acce.h"
    
    ITEM_MANAGER::ITEM_MANAGER()
    	: m_iTopOfTable(0), m_dwVIDCount(0), m_dwCurrentID(0)
    {
    	m_ItemIDRange.dwMin = m_ItemIDRange.dwMax = m_ItemIDRange.dwUsableItemIDMin = 0;
    	m_ItemIDSpareRange.dwMin = m_ItemIDSpareRange.dwMax = m_ItemIDSpareRange.dwUsableItemIDMin = 0;
    }
    
    ITEM_MANAGER::~ITEM_MANAGER()
    {
    	Destroy();
    }
    
    void ITEM_MANAGER::Destroy()
    {
    	itertype(m_VIDMap) it = m_VIDMap.begin();
    	for ( ; it != m_VIDMap.end(); ++it) {
    #ifdef M2_USE_POOL
    		pool_.Destroy(it->second);
    #else
    		M2_DELETE(it->second);
    #endif
    	}
    	m_VIDMap.clear();
    }
    
    void ITEM_MANAGER::GracefulShutdown()
    {
    	TR1_NS::unordered_set<LPITEM>::iterator it = m_set_pkItemForDelayedSave.begin();
    
    	while (it != m_set_pkItemForDelayedSave.end())
    		SaveSingleItem(*(it++));
    
    	m_set_pkItemForDelayedSave.clear();
    }
    
    bool ITEM_MANAGER::Initialize(TItemTable * table, int size)
    {
    	if (!m_vec_prototype.empty())
    		m_vec_prototype.clear();
    
    	m_map_vid.clear();
    	m_vec_item_vnum_range_info.clear();
    	m_map_pkDropItemGroup.clear();
    	m_map_pkSpecialItemGroup.clear();
    	m_map_pkQuestItemGroup.clear();
    	m_map_pkSpecialAttrGroup.clear();
    	m_map_pkMobItemGroup.clear();
    	m_map_pkLevelItemGroup.clear();
    	m_map_pkGloveItemGroup.clear();
    	m_ItemToSpecialGroup.clear();
    
    	int	i;
    
    	m_vec_prototype.resize(size);
    	if (size > 0)
    	thecore_memcpy(&m_vec_prototype[0], table, sizeof(TItemTable) * size);
    	sys_log(0, "ITEM_MANAGER: Reloaded %d items", size);
    	for (int i = 0; i < size; i++)
    	{
    		if (0 != m_vec_prototype[i].dwVnumRange)
    		{
    			m_vec_item_vnum_range_info.push_back( &m_vec_prototype[i]);
    		}
    	}
    
    	m_map_ItemRefineFrom.clear();
    	for (i = 0; i < size; ++i)
    	{
    
    		if (m_vec_prototype[i].dwRefinedVnum)
    			m_map_ItemRefineFrom.insert(std::make_pair(m_vec_prototype[i].dwRefinedVnum, m_vec_prototype[i].dwVnum));
    
    		// NOTE : QUEST_GIVE 플래그는 npc 이벤트로 발생.
    		if (m_vec_prototype[i].bType == ITEM_QUEST || IS_SET(m_vec_prototype[i].dwFlags, ITEM_FLAG_QUEST_USE | ITEM_FLAG_QUEST_USE_MULTIPLE))
    			quest::CQuestManager::instance().RegisterNPCVnum(m_vec_prototype[i].dwVnum);
    
    		m_map_vid[m_vec_prototype[i].dwVnum] = m_vec_prototype[i];
    		if ( test_server )
    			sys_log( 0, "ITEM_INFO %d %s ", m_vec_prototype[i].dwVnum, m_vec_prototype[i].szName );	
    	}
    
    	int len = 0, len2;
    	char buf[512];
    
    	for (i = 0; i < size; ++i)
    	{
    		len2 = snprintf(buf + len, sizeof(buf) - len, "%5u %-16s", m_vec_prototype[i].dwVnum, m_vec_prototype[i].szLocaleName);
    
    		if (len2 < 0 || len2 >= (int) sizeof(buf) - len)
    			len += (sizeof(buf) - len) - 1;
    		else
    			len += len2;
    
    		if (!((i + 1) % 4))
    		{
    			if ( !test_server )
    				sys_log(0, "%s", buf);
    			len = 0;
    		}
    		else
    		{
    			buf[len++] = '\t';
    			buf[len] = '\0';
    		}
    	}
    
    	if ((i + 1) % 4)
    	{
    		if ( !test_server )
    			sys_log(0, "%s", buf);
    	}
    
    	ITEM_VID_MAP::iterator it = m_VIDMap.begin();
    
    	sys_log (1, "ITEM_VID_MAP %d", m_VIDMap.size() );
    
    	while (it != m_VIDMap.end())
    	{
    		LPITEM item = it->second;
    		++it;
    
    		const TItemTable* tableInfo = GetTable(item->GetOriginalVnum());
    
    		if (NULL == tableInfo)
    		{
    			sys_err("cannot reset item table");
    			item->SetProto(NULL);
    		}
    
    		item->SetProto(tableInfo);
    	}
    
    	return true;
    }
    
    LPITEM ITEM_MANAGER::CreateItem(DWORD vnum, DWORD count, DWORD id, bool bTryMagic, int iRarePct, bool bSkipSave)
    {
    	if (0 == vnum)
    		return NULL;
    
    	DWORD dwMaskVnum = 0;
    	if (GetMaskVnum(vnum))
    	{
    		dwMaskVnum = GetMaskVnum(vnum);
    	}
    
    	if (LC_IsKorea() || LC_IsYMIR())
    	{
    		if (vnum == 50300 && bTryMagic)
    		{
    			// 수련서를 아이템 번호를 다르게 만들어 준다.
    			DWORD dwSkillVnum;
    
    			do
    			{
    				dwSkillVnum = number(1, 111);
    
    				CSkillProto * pkSk = CSkillManager::instance().Get(dwSkillVnum);
    
    				if (!pkSk)
    					continue;
    
    				break;
    			} while (1);
    
    			vnum = 50400 + dwSkillVnum;
    		}
    	}
    
    	const TItemTable* table = GetTable(vnum);
    
    	if (NULL == table)
    		return NULL;
    
    	LPITEM item = NULL;
    
    	//id로 검사해서 존재한다면 -- 리턴! 
    	if (m_map_pkItemByID.find(id) != m_map_pkItemByID.end())
    	{
    		item = m_map_pkItemByID[id];
    		LPCHARACTER owner = item->GetOwner();
    		sys_err("ITEM_ID_DUP: %u %s owner %p", id, item->GetName(), get_pointer(owner));
    		return NULL;
    	}
    
    	//아이템 하나 할당하고
    #ifdef M2_USE_POOL
    	item = pool_.Construct();
    #else
    	item = M2_NEW CItem(vnum);
    #endif
    
    	bool bIsNewItem = (0 == id);
    
    	//초기화 하고. 테이블 셋하고
    	item->Initialize();
    	item->SetProto(table);
    	item->SetMaskVnum(dwMaskVnum);
    
    	if (item->GetType() == ITEM_ELK) // 돈은 ID가 필요없고 저장도 필요없다.
    		item->SetSkipSave(true);
    
    	// Unique ID를 세팅하자
    	else if (!bIsNewItem)
    	{
    		item->SetID(id);
    		item->SetSkipSave(true);
    	}
    	else
    	{
    		item->SetID(GetNewID());
    
    		if (item->GetType() == ITEM_UNIQUE) // 유니크 아이템은 생성시에 소켓에 남은시간을 기록한다.
    		{
    			if (item->GetValue(2) == 0)
    				item->SetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME, item->GetValue(0)); // 게임 시간 유니크
    			else
    			{
    				//int globalTime = get_global_time();
    				//int lastTime = item->GetValue(0);
    				//int endTime = get_global_time() + item->GetValue(0);
    				item->SetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME, get_global_time() + item->GetValue(0)); // 실시간 유니크
    			}
    		}
    	}
    
    
    	switch (item->GetVnum())
    	{
    		case ITEM_AUTO_HP_RECOVERY_S:
    		case ITEM_AUTO_HP_RECOVERY_M:
    		case ITEM_AUTO_HP_RECOVERY_L:
    		case ITEM_AUTO_HP_RECOVERY_X:
    		case ITEM_AUTO_SP_RECOVERY_S:
    		case ITEM_AUTO_SP_RECOVERY_M:
    		case ITEM_AUTO_SP_RECOVERY_L:
    		case ITEM_AUTO_SP_RECOVERY_X:
    		case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_XS:
    		case REWARD_BOX_ITEM_AUTO_SP_RECOVERY_S:
    		case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_XS:
    		case REWARD_BOX_ITEM_AUTO_HP_RECOVERY_S:
    		case FUCKING_BRAZIL_ITEM_AUTO_SP_RECOVERY_S:
    		case FUCKING_BRAZIL_ITEM_AUTO_HP_RECOVERY_S:
    			if (bIsNewItem)
    				item->SetSocket(2, item->GetValue(0), true);
    			else
    				item->SetSocket(2, item->GetValue(0), false);
    			break;
    	}
    
    	if (item->GetType() == ITEM_ELK) // 돈은 아무 처리가 필요하지 않음
    		;
    	else if (item->IsStackable())  // 합칠 수 있는 아이템의 경우
    	{
    		count = MINMAX(1, count, ITEM_MAX_COUNT);
    
    		if (bTryMagic && count <= 1 && IS_SET(item->GetFlag(), ITEM_FLAG_MAKECOUNT))
    			count = item->GetValue(1);
    	}
    	else
    		count = 1;
    
    	item->SetVID(++m_dwVIDCount);
    
    	if (bSkipSave == false)
    		m_VIDMap.insert(ITEM_VID_MAP::value_type(item->GetVID(), item));
    
    	if (item->GetID() != 0 && bSkipSave == false)
    		m_map_pkItemByID.insert(std::map<DWORD, LPITEM>::value_type(item->GetID(), item));
    
    	if (!item->SetCount(count))
    		return NULL;
    
    	item->SetSkipSave(false);
    
    	if (item->GetType() == ITEM_UNIQUE && item->GetValue(2) != 0)
    		item->StartUniqueExpireEvent();
    
    	for (int i=0 ; i < ITEM_LIMIT_MAX_NUM ; i++)
    	{
    		// 아이템 생성 시점부터 사용하지 않아도 시간이 차감되는 방식
    		if (LIMIT_REAL_TIME == item->GetLimitType(i))
    		{
    			if (item->GetLimitValue(i))
    			{
    				item->SetSocket(0, time(0) + item->GetLimitValue(i)); 
    			}
    			else
    			{
    				item->SetSocket(0, time(0) + 60*60*24*7); 
    			}
    
    			item->StartRealTimeExpireEvent();
    		}
    
    		// 기존 유니크 아이템처럼 착용시에만 사용가능 시간이 차감되는 방식
    		else if (LIMIT_TIMER_BASED_ON_WEAR == item->GetLimitType(i))
    		{
    			// 이미 착용중인 아이템이면 타이머를 시작하고, 새로 만드는 아이템은 사용 가능 시간을 세팅해준다. (
    			// 아이템몰로 지급하는 경우에는 이 로직에 들어오기 전에 Socket0 값이 세팅이 되어 있어야 한다.
    			if (true == item->IsEquipped())
    			{
    				item->StartTimerBasedOnWearExpireEvent();
    			}
    			else if(0 == id)
    			{
    				long duration = item->GetSocket(0);
    				if (0 == duration)
    					duration = item->GetLimitValue(i);
    
    				if (0 == duration)
    					duration = 60 * 60 * 10;	// 정보가 아무것도 없으면 디폴트로 10시간 세팅
    
    				item->SetSocket(0, duration);
    			}
    		}
    	}
    
    	if (id == 0) // 새로 만드는 아이템일 때만 처리
    	{
    		// 새로추가되는 약초들일경우 성능을 다르게처리
    		if (ITEM_BLEND==item->GetType())
    		{
    			if (Blend_Item_find(item->GetVnum()))
    			{
    				Blend_Item_set_value(item);
    				return item;
    			}
    		}
    
    		if (table->sAddonType)
    		{
    			item->ApplyAddon(table->sAddonType);
    		}
    
    		if (bTryMagic)
    		{
    			if (iRarePct == -1)
    				iRarePct = table->bAlterToMagicItemPct;
    
    			if (number(1, 100) <= iRarePct)
    				item->AlterToMagicItem();
    		}
    
    		if (table->bGainSocketPct)
    			item->AlterToSocketItem(table->bGainSocketPct);
    
    		// 50300 == 기술 수련서
    		if (vnum == 50300 || vnum == ITEM_SKILLFORGET_VNUM)
    		{
    			DWORD dwSkillVnum;
    
    			do
    			{
    				dwSkillVnum = number(1, 111);
    
    				if (NULL != CSkillManager::instance().Get(dwSkillVnum))
    					break;
    			} while (true);
    
    			item->SetSocket(0, dwSkillVnum);
    		}
    		else if (ITEM_SKILLFORGET2_VNUM == vnum)
    		{
    			DWORD dwSkillVnum;
    
    			do
    			{
    				dwSkillVnum = number(112, 119);
    
    				if (NULL != CSkillManager::instance().Get(dwSkillVnum))
    					break;
    			} while (true);
    
    			item->SetSocket(0, dwSkillVnum);
    		}
    	}
    
    	if (item->GetType() == ITEM_QUEST)
    	{
    		for (itertype (m_map_pkQuestItemGroup) it = m_map_pkQuestItemGroup.begin(); it != m_map_pkQuestItemGroup.end(); it++)
    		{
    			if (it->second->m_bType == CSpecialItemGroup::QUEST && it->second->Contains(vnum))
    			{
    				item->SetSIGVnum(it->first);
    			}
    		}
    	}
    	else if (item->GetType() == ITEM_UNIQUE)
    	{
    		for (itertype (m_map_pkSpecialItemGroup) it = m_map_pkSpecialItemGroup.begin(); it != m_map_pkSpecialItemGroup.end(); it++)
    		{
    			if (it->second->m_bType == CSpecialItemGroup::SPECIAL && it->second->Contains(vnum))
    			{
    				item->SetSIGVnum(it->first);
    			}
    		}
    	}
    
    	// 새로 생성되는 용혼석 처리.
    	if (item->IsDragonSoul() && 0 == id)
    	{
    		DSManager::instance().DragonSoulItemInitialize(item);
    	}
    	return item;
    }
    
    void ITEM_MANAGER::DelayedSave(LPITEM item)
    {
    	if (item->GetID() != 0)
    		m_set_pkItemForDelayedSave.insert(item);
    }
    
    void ITEM_MANAGER::FlushDelayedSave(LPITEM item)
    {
    	TR1_NS::unordered_set<LPITEM>::iterator it = m_set_pkItemForDelayedSave.find(item);
    
    	if (it == m_set_pkItemForDelayedSave.end())
    	{
    		return;
    	}
    
    	m_set_pkItemForDelayedSave.erase(it);
    	SaveSingleItem(item);
    }
    
    void ITEM_MANAGER::SaveSingleItem(LPITEM item)
    {
    	if (!item->GetOwner())
    	{
    		DWORD dwID = item->GetID();
    		DWORD dwOwnerID = item->GetLastOwnerPID();
    
    		db_clientdesc->DBPacketHeader(HEADER_GD_ITEM_DESTROY, 0, sizeof(DWORD) + sizeof(DWORD));
    		db_clientdesc->Packet(&dwID, sizeof(DWORD));
    		db_clientdesc->Packet(&dwOwnerID, sizeof(DWORD));
    
    		sys_log(1, "ITEM_DELETE %s:%u", item->GetName(), dwID);
    		return;
    	}
    
    	sys_log(1, "ITEM_SAVE %s:%d in %s window %d", item->GetName(), item->GetID(), item->GetOwner()->GetName(), item->GetWindow());
    
    	TPlayerItem t;
    
    	t.id = item->GetID();
    	t.window = item->GetWindow();
    	t.pos = t.window == EQUIPMENT ? item->GetCell() - INVENTORY_MAX_NUM : item->GetCell();
    	t.count = item->GetCount();
    	t.vnum = item->GetOriginalVnum();
    	t.owner = (t.window == SAFEBOX || t.window == MALL) ? item->GetOwner()->GetDesc()->GetAccountTable().id : item->GetOwner()->GetPlayerID();
    	thecore_memcpy(t.alSockets, item->GetSockets(), sizeof(t.alSockets));
    	thecore_memcpy(t.aAttr, item->GetAttributes(), sizeof(t.aAttr));
    
    	db_clientdesc->DBPacketHeader(HEADER_GD_ITEM_SAVE, 0, sizeof(TPlayerItem));
    	db_clientdesc->Packet(&t, sizeof(TPlayerItem));
    }
    
    void ITEM_MANAGER::Update()
    {
    	TR1_NS::unordered_set<LPITEM>::iterator it = m_set_pkItemForDelayedSave.begin();
    	TR1_NS::unordered_set<LPITEM>::iterator this_it;
    
    	while (it != m_set_pkItemForDelayedSave.end())
    	{
    		this_it = it++;
    		LPITEM item = *this_it;
    
    		// SLOW_QUERY 플래그가 있는 것은 종료시에만 저장한다.
    		if (item->GetOwner() && IS_SET(item->GetFlag(), ITEM_FLAG_SLOW_QUERY))
    			continue;
    
    		SaveSingleItem(item);
    
    		m_set_pkItemForDelayedSave.erase(this_it);
    	}
    }
    
    void ITEM_MANAGER::RemoveItem(LPITEM item, const char * c_pszReason)
    {
    	LPCHARACTER o;
    	if ((o = item->GetOwner()))
    	{
    		char szHint[64];
    		snprintf(szHint, sizeof(szHint), "%s %u ", item->GetName(), item->GetCount());
    		if (item->GetOwner()->IsAcceOpen())
    		{
    			Acce_delete_item(item->GetOwner(), 0);
    			Acce_delete_item(item->GetOwner(), 1);
    		}
    		
    		LogManager::instance().ItemLog(o, item, c_pszReason ? c_pszReason : "REMOVE", szHint);
    		if (item->GetWindow() == MALL || item->GetWindow() == SAFEBOX)
    		{
    			CSafebox* pSafebox = item->GetWindow() == MALL ? o->GetMall() : o->GetSafebox();
    			if (pSafebox)
    			{
    				pSafebox->Remove(item->GetCell());
    			}
    		}
    		else
    		{
    			o->SyncQuickslot(QUICKSLOT_TYPE_ITEM, item->GetCell(), 1000);
    			item->RemoveFromCharacter();
    		}
    	}
    
    	M2_DESTROY_ITEM(item);
    }
    
    #ifndef DEBUG_ALLOC
    void ITEM_MANAGER::DestroyItem(LPITEM item)
    #else
    void ITEM_MANAGER::DestroyItem(LPITEM item, const char* file, size_t line)
    #endif
    {
    	if (item->GetSectree())
    		item->RemoveFromGround();
    
    	if (item->GetOwner())
    	{
    		if (CHARACTER_MANAGER::instance().Find(item->GetOwner()->GetPlayerID()) != NULL)
    		{
    			sys_err("DestroyItem: GetOwner %s %s!!", item->GetName(), item->GetOwner()->GetName());
    			item->RemoveFromCharacter();
    		}
    		else
    		{
    			sys_err ("WTH! Invalid item owner. owner pointer : %p", item->GetOwner());
    		}
    	}
    
    	TR1_NS::unordered_set<LPITEM>::iterator it = m_set_pkItemForDelayedSave.find(item);
    
    	if (it != m_set_pkItemForDelayedSave.end())
    		m_set_pkItemForDelayedSave.erase(it);
    
    	DWORD dwID = item->GetID();
    	sys_log(2, "ITEM_DESTROY %s:%u", item->GetName(), dwID);
    
    	if (!item->GetSkipSave() && dwID)
    	{
    		DWORD dwOwnerID = item->GetLastOwnerPID();
    
    		db_clientdesc->DBPacketHeader(HEADER_GD_ITEM_DESTROY, 0, sizeof(DWORD) + sizeof(DWORD));
    		db_clientdesc->Packet(&dwID, sizeof(DWORD));
    		db_clientdesc->Packet(&dwOwnerID, sizeof(DWORD));
    	}
    	else
    	{
    		sys_log(2, "ITEM_DESTROY_SKIP %s:%u (skip=%d)", item->GetName(), dwID, item->GetSkipSave());
    	}
    
    	if (dwID)
    		m_map_pkItemByID.erase(dwID);
    
    	m_VIDMap.erase(item->GetVID());
    
    #ifdef M2_USE_POOL
    	pool_.Destroy(item);
    #else
    #ifndef DEBUG_ALLOC
    	M2_DELETE(item);
    #else
    	M2_DELETE_EX(item, file, line);
    #endif
    #endif
    }
    
    LPITEM ITEM_MANAGER::Find(DWORD id)
    {
    	itertype(m_map_pkItemByID) it = m_map_pkItemByID.find(id);
    	if (it == m_map_pkItemByID.end())
    		return NULL;
    	return it->second;
    }
    
    LPITEM ITEM_MANAGER::FindByVID(DWORD vid)
    {
    	ITEM_VID_MAP::iterator it = m_VIDMap.find(vid);
    
    	if (it == m_VIDMap.end())
    		return NULL;
    
    	return (it->second);
    }
    
    TItemTable * ITEM_MANAGER::GetTable(DWORD vnum)
    {
    	int rnum = RealNumber(vnum);
    
    	if (rnum < 0)
    	{
    		for (unsigned int i = 0; i < m_vec_item_vnum_range_info.size(); i++)
    		{
    			TItemTable* p = m_vec_item_vnum_range_info[i];
    			if ((p->dwVnum < vnum) &&
    				vnum < (p->dwVnum + p->dwVnumRange))
    			{
    				return p;
    			}
    		}
    			
    		return NULL;
    	}
    
    	return &m_vec_prototype[rnum];
    }
    
    int ITEM_MANAGER::RealNumber(DWORD vnum)
    {
    	int bot, top, mid;
    
    	bot = 0;
    	top = m_vec_prototype.size();
    
    	TItemTable * pTable = &m_vec_prototype[0];
    
    	while (1)
    	{
    		mid = (bot + top) >> 1;
    
    		if ((pTable + mid)->dwVnum == vnum)
    			return (mid);
    
    		if (bot >= top)
    			return (-1);
    
    		if ((pTable + mid)->dwVnum > vnum)
    			top = mid - 1;
    		else        
    			bot = mid + 1;
    	}
    }
    
    bool ITEM_MANAGER::GetVnum(const char * c_pszName, DWORD & r_dwVnum)
    {
    	int len = strlen(c_pszName);
    
    	TItemTable * pTable = &m_vec_prototype[0];
    
    	for (DWORD i = 0; i < m_vec_prototype.size(); ++i, ++pTable)
    	{
    		if (!strncasecmp(c_pszName, pTable->szLocaleName, len))
    		{
    			r_dwVnum = pTable->dwVnum;
    			return true;
    		}
    	}
    
    	return false;
    }
    
    bool ITEM_MANAGER::GetVnumByOriginalName(const char * c_pszName, DWORD & r_dwVnum)
    {
    	int len = strlen(c_pszName);
    
    	TItemTable * pTable = &m_vec_prototype[0];
    
    	for (DWORD i = 0; i < m_vec_prototype.size(); ++i, ++pTable)
    	{
    		if (!strncasecmp(c_pszName, pTable->szName, len))
    		{
    			r_dwVnum = pTable->dwVnum;
    			return true;
    		}
    	}
    
    	return false;
    }
    
    std::set<DWORD> g_set_lotto;
    
    void load_lotto()
    {
    	static int bLoaded = false;
    
    	if (bLoaded)
    		return;
    
    	bLoaded = true;
    	FILE * fp = fopen("lotto.txt", "r");
    
    	if (!fp)
    		return;
    
    	char buf[256];
    
    	while (fgets(buf, 256, fp))
    	{
    		char * psz = strchr(buf, '\n');
    
    		if (NULL != psz)
    			*psz = '\0';
    
    		DWORD dw = 0;
    		str_to_number(dw, buf);
    		g_set_lotto.insert(dw);
    	}
    
    	fclose(fp);
    }
    
    DWORD lotto()
    {
    	load_lotto();
    
    	char szBuf[6 + 1];
    
    	do
    	{
    		for (int i = 0; i < 6; ++i)
    			szBuf[i] = 48 + number(1, 9);
    
    		szBuf[6] = '\0';
    
    		DWORD dw = 0;
    		str_to_number(dw, szBuf);
    
    		if (g_set_lotto.end() == g_set_lotto.find(dw))
    		{
    			FILE * fp = fopen("lotto.txt", "a+");
    			if (fp)
    			{
    				fprintf(fp, "%u\n", dw);
    				fclose(fp);
    			}
    			return dw;
    		}
    	}
    	while (1);
    }
    
    
    class CItemDropInfo
    {
    	public:
    		CItemDropInfo(int iLevelStart, int iLevelEnd, int iPercent, DWORD dwVnum) :
    			m_iLevelStart(iLevelStart), m_iLevelEnd(iLevelEnd), m_iPercent(iPercent), m_dwVnum(dwVnum)
    			{
    			}
    
    		int	m_iLevelStart;
    		int	m_iLevelEnd;
    		int	m_iPercent; // 1 ~ 1000
    		DWORD	m_dwVnum;
    
    		friend bool operator < (const CItemDropInfo & l, const CItemDropInfo & r)
    		{
    			return l.m_iLevelEnd < r.m_iLevelEnd;
    		}
    };
    
    extern std::vector<CItemDropInfo> g_vec_pkCommonDropItem[MOB_RANK_MAX_NUM];
    
    // 20050503.ipkn.
    // iMinimum 보다 작으면 iDefault 세팅 (단, iMinimum은 0보다 커야함)
    // 1, 0 식으로 ON/OFF 되는 방식을 지원하기 위해 존재
    int GetDropPerKillPct(int iMinimum, int iDefault, int iDeltaPercent, const char * c_pszFlag)
    {
    	int iVal = 0;
    
    	if ((iVal = quest::CQuestManager::instance().GetEventFlag(c_pszFlag)))
    	{
    		if (!test_server && !LC_IsJapan())
    		{
    			if (iVal < iMinimum)
    				iVal = iDefault;
    
    			if (iVal < 0)
    				iVal = iDefault;
    		}
    	}
    
    	if (iVal == 0)
    		return 0;
    
    	// 기본 세팅일때 (iDeltaPercent=100) 
    	// 40000 iVal 마리당 하나 느낌을 주기 위한 상수임
    	return (40000 * iDeltaPercent / iVal);
    }
    
    bool ITEM_MANAGER::GetDropPct(LPCHARACTER pkChr, LPCHARACTER pkKiller, OUT int& iDeltaPercent, OUT int& iRandRange)
    {
     	if (NULL == pkChr || NULL == pkKiller)
    		return false;
    
    	int iLevel = pkKiller->GetLevel();
    	iDeltaPercent = 100;
    
    	if (!pkChr->IsStone() && pkChr->GetMobRank() >= MOB_RANK_BOSS)
    		iDeltaPercent = PERCENT_LVDELTA_BOSS(pkKiller->GetLevel(), pkChr->GetLevel());
    	else
    		iDeltaPercent = PERCENT_LVDELTA(pkKiller->GetLevel(), pkChr->GetLevel());
    
    	BYTE bRank = pkChr->GetMobRank();
    
    	if (1 == number(1, 50000))
    		iDeltaPercent += 1000;
    	else if (1 == number(1, 10000))
    		iDeltaPercent += 500;
    
    	sys_log(3, "CreateDropItem for level: %d rank: %u pct: %d", iLevel, bRank, iDeltaPercent);
    	iDeltaPercent = iDeltaPercent * CHARACTER_MANAGER::instance().GetMobItemRate(pkKiller) / 100;
    
    	//if (pkKiller->GetPoint(POINT_MALL_ITEMBONUS) > 0)
    	//iDeltaPercent += iDeltaPercent * pkKiller->GetPoint(POINT_MALL_ITEMBONUS) / 100;
    	// ADD_PREMIUM
    	if (pkKiller->GetPremiumRemainSeconds(PREMIUM_ITEM) > 0 ||
    			pkKiller->IsEquipUniqueGroup(UNIQUE_GROUP_DOUBLE_ITEM))
    		iDeltaPercent += iDeltaPercent;
    	// END_OF_ADD_PREMIUM
    
    	// PC_BANG_ITEM_ADD
    	if (pkKiller->GetPoint(POINT_PC_BANG_DROP_BONUS) > 0)
    	{
    		if (pkKiller->IsPCBang() == true)
    			iDeltaPercent += iDeltaPercent * pkKiller->GetPoint(POINT_PC_BANG_DROP_BONUS)/100;
    	}
    	// END_PC_BANG_ITEM_ADD
    
    	iRandRange = 4000000;
    	iRandRange = iRandRange * 100 / 
    		(100 + 
    		 CPrivManager::instance().GetPriv(pkKiller, PRIV_ITEM_DROP) + 
    		 pkKiller->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_ITEM)?100:0);
    
    	if (distribution_test_server) iRandRange /= 3;
    
    	return true;
    }
    
    bool ITEM_MANAGER::CreateDropItem(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item)
    {
    	int iLevel = pkKiller->GetLevel();
    
    	int iDeltaPercent, iRandRange;
    	if (!GetDropPct(pkChr, pkKiller, iDeltaPercent, iRandRange))
    		return false;
    
    	BYTE bRank = pkChr->GetMobRank();
    	LPITEM item = NULL;
    
    	// Common Drop Items
    	std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin();
    
    	while (it != g_vec_pkCommonDropItem[bRank].end())
    	{
    		const CItemDropInfo & c_rInfo = *(it++);
    
    		if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd)
    			continue;
    
    		int iPercent = (c_rInfo.m_iPercent * iDeltaPercent) / 100;
    		sys_log(3, "CreateDropItem %d ~ %d %d(%d)", c_rInfo.m_iLevelStart, c_rInfo.m_iLevelEnd, c_rInfo.m_dwVnum, iPercent, c_rInfo.m_iPercent);
    
    		if (iPercent >= number(1, iRandRange))
    		{
    			TItemTable * table = GetTable(c_rInfo.m_dwVnum);
    
    			if (!table)
    				continue;
    
    			item = NULL;
    
    			if (table->bType == ITEM_POLYMORPH)
    			{
    				if (c_rInfo.m_dwVnum == pkChr->GetPolymorphItemVnum())
    				{
    					item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true);
    
    					if (item)
    						item->SetSocket(0, pkChr->GetRaceNum());
    				}
    			}
    			else
    				item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true);
    
    			if (item) vec_item.push_back(item);
    		}
    	}
    
    	// Drop Item Group
    	{
    		itertype(m_map_pkDropItemGroup) it;
    		it = m_map_pkDropItemGroup.find(pkChr->GetRaceNum());
    
    		if (it != m_map_pkDropItemGroup.end())
    		{
    			decltype(it->second->GetVector()) v = it->second->GetVector();
    
    			for (DWORD i = 0; i < v.size(); ++i)
    			{
    				int iPercent = (v[i].dwPct * iDeltaPercent) / 100;
    
    				if (iPercent >= number(1, iRandRange))
    				{
    					item = CreateItem(v[i].dwVnum, v[i].iCount, 0, true);
    
    					if (item)
    					{
    						if (item->GetType() == ITEM_POLYMORPH)
    						{
    							if (item->GetVnum() == pkChr->GetPolymorphItemVnum())
    							{
    								item->SetSocket(0, pkChr->GetRaceNum());
    							}
    						}
    
    						vec_item.push_back(item);
    					}
    				}
    			}
    		}
    	}
    
    	// MobDropItem Group
    	{
    		itertype(m_map_pkMobItemGroup) it;
    		it = m_map_pkMobItemGroup.find(pkChr->GetRaceNum());
    
    		if ( it != m_map_pkMobItemGroup.end() )
    		{
    			CMobItemGroup* pGroup = it->second;
    
    			// MOB_DROP_ITEM_BUG_FIX
    			// 20050805.myevan.MobDropItem 에 아이템이 없을 경우 CMobItemGroup::GetOne() 접근시 문제 발생 수정
    			if (pGroup && !pGroup->IsEmpty())
    			{
    				int iPercent = 40000 * iDeltaPercent / pGroup->GetKillPerDrop();
    				if (iPercent >= number(1, iRandRange))
    				{
    					const CMobItemGroup::SMobItemGroupInfo& info = pGroup->GetOne();
    					item = CreateItem(info.dwItemVnum, info.iCount, 0, true, info.iRarePct);
    
    					if (item) vec_item.push_back(item);
    				}
    			}
    			// END_OF_MOB_DROP_ITEM_BUG_FIX
    		}
    	}
    
    	// Level Item Group
    	{
    		itertype(m_map_pkLevelItemGroup) it;
    		it = m_map_pkLevelItemGroup.find(pkChr->GetRaceNum());
    
    		if ( it != m_map_pkLevelItemGroup.end() )
    		{
    			if ( it->second->GetLevelLimit() <= (DWORD)iLevel )
    			{
    				decltype(it->second->GetVector()) v = it->second->GetVector();
    
    				for ( DWORD i=0; i < v.size(); i++ )
    				{
    					if ( v[i].dwPct >= (DWORD)number(1, 1000000/*iRandRange*/) )
    					{
    						DWORD dwVnum = v[i].dwVNum;
    						item = CreateItem(dwVnum, v[i].iCount, 0, true);
    						if ( item ) vec_item.push_back(item);
    					}
    				}
    			}
    		}
    	}
    	
    	// BuyerTheitGloves Item Group
    	{
    		if (pkKiller->GetPremiumRemainSeconds(PREMIUM_ITEM) > 0 ||
    				pkKiller->IsEquipUniqueGroup(UNIQUE_GROUP_DOUBLE_ITEM))
    		{
    			itertype(m_map_pkGloveItemGroup) it;
    			it = m_map_pkGloveItemGroup.find(pkChr->GetRaceNum());
    
    			if (it != m_map_pkGloveItemGroup.end())
    			{
    				decltype(it->second->GetVector()) v = it->second->GetVector();
    
    				for (DWORD i = 0; i < v.size(); ++i)
    				{
    					int iPercent = (v[i].dwPct * iDeltaPercent) / 100;
    
    					if (iPercent >= number(1, iRandRange))
    					{
    						DWORD dwVnum = v[i].dwVnum;
    						item = CreateItem(dwVnum, v[i].iCount, 0, true);
    						if (item) vec_item.push_back(item);
    					}
    				}
    			}
    		}
    	}
    	
    	// 잡템
    	if (pkChr->GetMobDropItemVnum())
    	{
    		itertype(m_map_dwEtcItemDropProb) it = m_map_dwEtcItemDropProb.find(pkChr->GetMobDropItemVnum());
    
    		if (it != m_map_dwEtcItemDropProb.end())
    		{
    			int iPercent = (it->second * iDeltaPercent) / 100;
    
    			if (iPercent >= number(1, iRandRange))
    			{
    				item = CreateItem(pkChr->GetMobDropItemVnum(), 1, 0, true);
    				if (item) vec_item.push_back(item);
    			}
    		}
    	}
    
    	if (pkChr->IsStone())
    	{
    		if (pkChr->GetDropMetinStoneVnum())
    		{
    			int iPercent = (pkChr->GetDropMetinStonePct() * iDeltaPercent) * 400;
    
    			if (iPercent >= number(1, iRandRange))
    			{
    				item = CreateItem(pkChr->GetDropMetinStoneVnum(), 1, 0, true);
    				if (item) vec_item.push_back(item);
    			}
    		}
    	}
    
    	if (pkKiller->IsHorseRiding() && 
    			GetDropPerKillPct(1000, 1000000, iDeltaPercent, "horse_skill_book_drop") >= number(1, iRandRange))
    	{
    		sys_log(0, "EVENT HORSE_SKILL_BOOK_DROP");
    
    		if ((item = CreateItem(ITEM_HORSE_SKILL_TRAIN_BOOK, 1, 0, true)))
    			vec_item.push_back(item);
    	}
    
    
    	if (GetDropPerKillPct(100, 1000, iDeltaPercent, "lotto_drop") >= number(1, iRandRange))
    	{
    		DWORD * pdw = M2_NEW DWORD[3];
    
    		pdw[0] = 50001;
    		pdw[1] = 1;
    		pdw[2] = quest::CQuestManager::instance().GetEventFlag("lotto_round");
    
    		// 행운의 서는 소켓을 설정한다
    		DBManager::instance().ReturnQuery(QID_LOTTO, pkKiller->GetPlayerID(), pdw,
    				"INSERT INTO lotto_list VALUES(0, 'server%s', %u, NOW())",
    				get_table_postfix(), pkKiller->GetPlayerID());
    	}
    
    	//
    	// 스페셜 드롭 아이템
    	// 
    	CreateQuestDropItem(pkChr, pkKiller, vec_item, iDeltaPercent, iRandRange);
    
    	for (itertype(vec_item) it = vec_item.begin(); it != vec_item.end(); ++it)
    	{
    		LPITEM item = *it;
    		DBManager::instance().SendMoneyLog(MONEY_LOG_DROP, item->GetVnum(), item->GetCount());
    	}
    
    	//
    	// 승룡곡 천의동굴 2층에서만 수룡방 입장권
    	//
    	if (LC_IsYMIR() || LC_IsKorea())
    	{
    		if (73 == pkKiller->GetMapIndex())
    		{
    			int per = 25;	// 0.25%
    			if (number(1, 10000) <= per)
    			{
    				LPITEM item = 0;
    				if ((item = CreateItem(71122, 1, 0, true)))
    					vec_item.push_back(item);
    			}
    		}
    	}
    
    	//
    	// 승룡곡 1층, 2층에서만 7,8 스킬입문서 드롭
    	//
    	if (LC_IsYMIR() || LC_IsKorea())
    	{
    		switch (pkKiller->GetMapIndex())
    		{
    			case 72:	// 천의동굴 1층
    			case 73:	// 천의동굴 2층
    				{
    					int vnum = 0;
    
    					if (2403 == pkChr->GetRaceNum())	// 천의법사
    						vnum = 69200;	// 7스킬 입문서
    					else if (2411 == pkChr->GetRaceNum())	// 진천의병사
    						vnum = 69201;	// 8스킬 입문서
    					else
    						break;
    
    					int per = 5;	// 0.05%
    					if (number(1, 10000) <= per)
    					{
    						LPITEM item = 0;
    						item = CreateItem(vnum, 1, 0, true);
    						if (item)
    							vec_item.push_back(item);
    					}
    				}
    				break;
    		}
    	}
    
    	return vec_item.size();
    }
    
    // ADD_GRANDMASTER_SKILL
    int GetThreeSkillLevelAdjust(int level)
    {
    	if (level < 40)
    		return 32;
    	if (level < 45)
    		return 16;
    	if (level < 50)
    		return 8;
    	if (level < 55)
    		return 4;
    	if (level < 60)
    		return 2;
    	return 1;
    }
    // END_OF_ADD_GRANDMASTER_SKILL
    
    // DROPEVENT_CHARSTONE
    // drop_char_stone 1
    // drop_char_stone.percent_lv01_10 5
    // drop_char_stone.percent_lv11_30 10
    // drop_char_stone.percent_lv31_MX 15
    // drop_char_stone.level_range	   10
    static struct DropEvent_CharStone
    {
    	int percent_lv01_10;
    	int percent_lv11_30;
    	int percent_lv31_MX;
    	int level_range;
    	bool alive;
    
    	DropEvent_CharStone()
    	{
    		percent_lv01_10 =  100;
    		percent_lv11_30 =  200;
    		percent_lv31_MX =  300;
    		level_range = 10;
    		alive = false;
    	}
    } gs_dropEvent_charStone;
    
    static int __DropEvent_CharStone_GetDropPercent(int killer_level)
    {
    	int killer_levelStep = (killer_level-1)/10;
    
    	switch (killer_levelStep)
    	{
    		case 0:
    			return gs_dropEvent_charStone.percent_lv01_10;
    
    		case 1:
    		case 2:
    			return gs_dropEvent_charStone.percent_lv11_30;
    	}
    
    	return gs_dropEvent_charStone.percent_lv31_MX;
    }
    
    static void __DropEvent_CharStone_DropItem(CHARACTER & killer, CHARACTER & victim, ITEM_MANAGER& itemMgr, std::vector<LPITEM>& vec_item)
    {
    	if (!gs_dropEvent_charStone.alive)
    		return;
    
    	int killer_level = killer.GetLevel();
    	int dropPercent = __DropEvent_CharStone_GetDropPercent(killer_level);
    
    	int MaxRange = 10000;
    
    	if (LC_IsCanada() == true)
    		MaxRange = 20000;
    
    	if (number(1, MaxRange) <= dropPercent)
    	{
    		int log_level = (test_server || killer.GetGMLevel() >= GM_LOW_WIZARD) ? 0 : 1;
    		int victim_level = victim.GetLevel();
    		int level_diff = victim_level - killer_level;
    
    		if (level_diff >= +gs_dropEvent_charStone.level_range || level_diff <= -gs_dropEvent_charStone.level_range)
    		{
    			sys_log(log_level, 
    					"dropevent.drop_char_stone.level_range_over: killer(%s: lv%d), victim(%s: lv:%d), level_diff(%d)",
    					killer.GetName(), killer.GetLevel(), victim.GetName(), victim.GetLevel(), level_diff);	
    			return;
    		}
    
    		static const int Stones[] = { 30210, 30211, 30212, 30213, 30214, 30215, 30216, 30217, 30218, 30219, 30258, 30259, 30260, 30261, 30262, 30263 };
    		int item_vnum = Stones[number(0, _countof(Stones))];
    
    		LPITEM p_item = NULL;
    
    		if ((p_item = itemMgr.CreateItem(item_vnum, 1, 0, true)))
    		{
    			vec_item.push_back(p_item);
    
    			sys_log(log_level, 
    					"dropevent.drop_char_stone.item_drop: killer(%s: lv%d), victim(%s: lv:%d), item_name(%s)",
    					killer.GetName(), killer.GetLevel(), victim.GetName(), victim.GetLevel(), p_item->GetName());	
    		}
    	}
    }
    
    bool DropEvent_CharStone_SetValue(const std::string& name, int value)
    {
    	if (name == "drop_char_stone")
    	{
    		gs_dropEvent_charStone.alive = value;
    
    		if (value)
    			sys_log(0, "dropevent.drop_char_stone = on");
    		else
    			sys_log(0, "dropevent.drop_char_stone = off");
    
    	}
    	else if (name == "drop_char_stone.percent_lv01_10")
    		gs_dropEvent_charStone.percent_lv01_10 = value;
    	else if (name == "drop_char_stone.percent_lv11_30")
    		gs_dropEvent_charStone.percent_lv11_30 = value;
    	else if (name == "drop_char_stone.percent_lv31_MX")
    		gs_dropEvent_charStone.percent_lv31_MX = value;
    	else if (name == "drop_char_stone.level_range")
    		gs_dropEvent_charStone.level_range = value;
    	else
    		return false;
    
    	sys_log(0, "dropevent.drop_char_stone: %d", gs_dropEvent_charStone.alive ? true : false);
    	sys_log(0, "dropevent.drop_char_stone.percent_lv01_10: %f", gs_dropEvent_charStone.percent_lv01_10/100.0f);
    	sys_log(0, "dropevent.drop_char_stone.percent_lv11_30: %f", gs_dropEvent_charStone.percent_lv11_30/100.0f);
    	sys_log(0, "dropevent.drop_char_stone.percent_lv31_MX: %f", gs_dropEvent_charStone.percent_lv31_MX/100.0f);
    	sys_log(0, "dropevent.drop_char_stone.level_range: %d", gs_dropEvent_charStone.level_range);
    
    	return true;
    }
    
    // END_OF_DROPEVENT_CHARSTONE
    
    // fixme
    // 위의 것과 함께 quest로 뺄것 빼보자. 
    // 이거 너무 더럽잖아...
    // ”?. 하드코딩 싫다 ㅜㅠ
    // 계량 아이템 보상 시작.
    // by rtsummit 고치자 진짜
    static struct DropEvent_RefineBox
    {
    	int percent_low;
    	int low;
    	int percent_mid;
    	int mid;
    	int percent_high;
    	//int level_range;
    	bool alive;
    
    	DropEvent_RefineBox()
    	{
    		percent_low =  100;
    		low = 20;
    		percent_mid =  100;
    		mid = 45;
    		percent_high =  100;
    		//level_range = 10;
    		alive = false;
    	}
    } gs_dropEvent_refineBox;
    
    static LPITEM __DropEvent_RefineBox_GetDropItem(CHARACTER & killer, CHARACTER & victim, ITEM_MANAGER& itemMgr)
    {
    	static const int lowerBox[] = { 50197, 50198, 50199 };
    	static const int lowerBox_range = 3;
    	static const int midderBox[] = { 50203, 50204, 50205, 50206 };
    	static const int midderBox_range = 4;
    	static const int higherBox[] = { 50207, 50208, 50209, 50210, 50211 };
    	static const int higherBox_range = 5;
    
    	if (victim.GetMobRank() < MOB_RANK_KNIGHT)
    		return NULL;
    
    	int killer_level = killer.GetLevel();
    	//int level_diff = victim_level - killer_level;
    
    	//if (level_diff >= +gs_dropEvent_refineBox.level_range || level_diff <= -gs_dropEvent_refineBox.level_range)
    	//{
    	//	sys_log(log_level, 
    	//		"dropevent.drop_refine_box.level_range_over: killer(%s: lv%d), victim(%s: lv:%d), level_diff(%d)",
    	//		killer.GetName(), killer.GetLevel(), victim.GetName(), victim.GetLevel(), level_diff);	
    	//	return NULL;
    	//}
    
    	if (killer_level <= gs_dropEvent_refineBox.low)
    	{
    		if (number (1, gs_dropEvent_refineBox.percent_low) == 1)
    		{
    			return itemMgr.CreateItem(lowerBox [number (1,lowerBox_range) - 1], 1, 0, true);
    		}
    	}
    	else if (killer_level <= gs_dropEvent_refineBox.mid)
    	{
    		if (number (1, gs_dropEvent_refineBox.percent_mid) == 1)
    		{
    			return itemMgr.CreateItem(midderBox [number (1,midderBox_range) - 1], 1, 0, true);
    		}
    	}
    	else
    	{
    		if (number (1, gs_dropEvent_refineBox.percent_high) == 1)
    		{
    			return itemMgr.CreateItem(higherBox [number (1,higherBox_range) - 1], 1, 0, true);
    		}
    	}
    	return NULL;
    }
    
    static void __DropEvent_RefineBox_DropItem(CHARACTER & killer, CHARACTER & victim, ITEM_MANAGER& itemMgr, std::vector<LPITEM>& vec_item)
    {
    	if (!gs_dropEvent_refineBox.alive)
    		return;
    
    	int log_level = (test_server || killer.GetGMLevel() >= GM_LOW_WIZARD) ? 0 : 1;
    
    	LPITEM p_item = __DropEvent_RefineBox_GetDropItem(killer, victim, itemMgr);
    
    	if (p_item)
    	{
    		vec_item.push_back(p_item);
    
    		sys_log(log_level, 
    			"dropevent.drop_refine_box.item_drop: killer(%s: lv%d), victim(%s: lv:%d), item_name(%s)",
    			killer.GetName(), killer.GetLevel(), victim.GetName(), victim.GetLevel(), p_item->GetName());	
    	}
    }
    
    bool DropEvent_RefineBox_SetValue(const std::string& name, int value)
    {
    	if (name == "refine_box_drop")
    	{
    		gs_dropEvent_refineBox.alive = value;
    
    		if (value)
    			sys_log(0, "refine_box_drop = on");
    		else
    			sys_log(0, "refine_box_drop = off");
    
    	}
    	else if (name == "refine_box_low")
    		gs_dropEvent_refineBox.percent_low = value < 100 ? 100 : value;
    	else if (name == "refine_box_mid")
    		gs_dropEvent_refineBox.percent_mid = value < 100 ? 100 : value;
    	else if (name == "refine_box_high")
    		gs_dropEvent_refineBox.percent_high = value < 100 ? 100 : value;
    	//else if (name == "refine_box_level_range")
    	//	gs_dropEvent_refineBox.level_range = value;
    	else
    		return false;
    
    	sys_log(0, "refine_box_drop: %d", gs_dropEvent_refineBox.alive ? true : false);
    	sys_log(0, "refine_box_low: %d", gs_dropEvent_refineBox.percent_low);
    	sys_log(0, "refine_box_mid: %d", gs_dropEvent_refineBox.percent_mid);
    	sys_log(0, "refine_box_high: %d", gs_dropEvent_refineBox.percent_high);
    	//sys_log(0, "refine_box_low_level_range: %d", gs_dropEvent_refineBox.level_range);
    
    	return true;
    }
    // 개량 아이템 보상 끝.
    
    
    void ITEM_MANAGER::CreateQuestDropItem(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item, int iDeltaPercent, int iRandRange)
    {
    	LPITEM item = NULL;
    
    	if (!pkChr)
    		return;
    
    	if (!pkKiller)
    		return;
    
    	sys_log(1, "CreateQuestDropItem victim(%s), killer(%s)", pkChr->GetName(), pkKiller->GetName() );
    
    	// DROPEVENT_CHARSTONE
    	__DropEvent_CharStone_DropItem(*pkKiller, *pkChr, *this, vec_item);
    	// END_OF_DROPEVENT_CHARSTONE
    	__DropEvent_RefineBox_DropItem(*pkKiller, *pkChr, *this, vec_item);
    
    	// 크리스마스 양말
    	if (quest::CQuestManager::instance().GetEventFlag("xmas_sock"))
    	{
    		//const DWORD SOCK_ITEM_VNUM = 50010;
    		DWORD	SOCK_ITEM_VNUM	= 0;
    		if (LC_IsKorea() || LC_IsYMIR())
    		{
    			SOCK_ITEM_VNUM = (number(1,100)<=50) ? 50010 : 71111;
    		}
    		else
    		{
    			SOCK_ITEM_VNUM = 50010;
    		}
    
    		int iDropPerKill[MOB_RANK_MAX_NUM] =
    		{
    			2000,
    			1000,
    			300,
    			50,
    			0,
    			0,
    		};
    
    		if ( iDropPerKill[pkChr->GetMobRank()] != 0 )
    		{
    			int iPercent = 40000 * iDeltaPercent / iDropPerKill[pkChr->GetMobRank()];
    
    			if ( LC_IsHongKong() )
    			{
    				iPercent *= 10;
    			}
    
    			sys_log(0, "SOCK DROP %d %d", iPercent, iRandRange);
    			if (iPercent >= number(1, iRandRange))
    			{
    				if ((item = CreateItem(SOCK_ITEM_VNUM, 1, 0, true)))
    					vec_item.push_back(item);
    			}
    		}
    	}
    
    	// 월광 보합
    	if (quest::CQuestManager::instance().GetEventFlag("drop_moon"))
    	{
    		const DWORD ITEM_VNUM = 50011;
    
    		int iDropPerKill[MOB_RANK_MAX_NUM] =
    		{
    			2000,
    			1000,
    			300,
    			50,
    			0,
    			0,
    		};
    
    		if (iDropPerKill[pkChr->GetMobRank()])
    		{
    			int iPercent = 40000 * iDeltaPercent / iDropPerKill[pkChr->GetMobRank()];
    
    			if (iPercent >= number(1, iRandRange))
    			{
    				if ((item = CreateItem(ITEM_VNUM, 1, 0, true)))
    					vec_item.push_back(item);
    			}
    		}
    	}
    
    	if (LC_IsEurope())
    	{
    		if (pkKiller->GetLevel() >= 15 && abs(pkKiller->GetLevel() - pkChr->GetLevel()) <= 5)
    		{
    			int pct = quest::CQuestManager::instance().GetEventFlag("hc_drop");
    
    			if (pct > 0)
    			{
    				const DWORD ITEM_VNUM = 30178;
    
    				if (number(1,100) <= pct)
    				{
    					if ((item = CreateItem(ITEM_VNUM, 1, 0, true)))
    						vec_item.push_back(item);
    				}
    			}
    		}
    	}
    
    	//육각보합
    	if (GetDropPerKillPct(100, g_iUseLocale ? 2000 : 800, iDeltaPercent, "2006_drop") >= number(1, iRandRange))
    	{
    		sys_log(0, "육각보합 DROP EVENT ");
    
    		const static DWORD dwVnum = 50037;
    
    		if ((item = CreateItem(dwVnum, 1, 0, true)))
    			vec_item.push_back(item);
    
    	}
    
    	//육각보합+
    	if (GetDropPerKillPct(100, g_iUseLocale ? 2000 : 800, iDeltaPercent, "2007_drop") >= number(1, iRandRange))
    	{
    		sys_log(0, "육각보합 DROP EVENT ");
    
    		const static DWORD dwVnum = 50043;
    
    		if ((item = CreateItem(dwVnum, 1, 0, true)))
    			vec_item.push_back(item);
    	}
    
    	// 새해 폭죽 이벤트
    	if (GetDropPerKillPct(/* minimum */ 100, /* default */ 1000, iDeltaPercent, "newyear_fire") >= number(1, iRandRange))
    	{
    		// 중국은 폭죽, 한국 팽이
    		const DWORD ITEM_VNUM_FIRE = g_iUseLocale ? 50107 : 50108;
    
    		if ((item = CreateItem(ITEM_VNUM_FIRE, 1, 0, true)))
    			vec_item.push_back(item);
    	}
    
    	// 새해 대보름 원소 이벤트
    	if (GetDropPerKillPct(100, 500, iDeltaPercent, "newyear_moon") >= number(1, iRandRange))
    	{
    		sys_log(0, "EVENT NEWYEAR_MOON DROP");
    
    		const static DWORD wonso_items[6] = { 50016, 50017, 50018, 50019, 50019, 50019, };
    		DWORD dwVnum = wonso_items[number(0,5)];
    
    		if ((item = CreateItem(dwVnum, 1, 0, true)))
    			vec_item.push_back(item);
    	}
    
    	// 발렌타인 데이 이벤트. OGE의 요구에 따라 event 최소값을 1로 변경.(다른 이벤트는 일단 그대로 둠.)
    	if (GetDropPerKillPct(1, g_iUseLocale ? 2000 : 800, iDeltaPercent, "valentine_drop") >= number(1, iRandRange))
    	{
    		sys_log(0, "EVENT VALENTINE_DROP");
    
    		const static DWORD valentine_items[2] = { 50024, 50025 };
    		DWORD dwVnum = valentine_items[number(0, 1)];
    
    		if ((item = CreateItem(dwVnum, 1, 0, true)))
    			vec_item.push_back(item);
    	}
    
    	// 아이스크림 이벤트
    	if (GetDropPerKillPct(100, g_iUseLocale ? 2000 : 800, iDeltaPercent, "icecream_drop") >= number(1, iRandRange))
    	{
    		const static DWORD icecream = 50123;
    
    		if ((item = CreateItem(icecream, 1, 0, true)))
    			vec_item.push_back(item);
    	}
    
    	// new 크리스마스 이벤트
    	// 53002 : 아기 순록 소환권
    	if ((pkKiller->CountSpecifyItem(53002) > 0) && (GetDropPerKillPct(50, 100, iDeltaPercent, "new_xmas_event") >= number(1, iRandRange)))
    	{
    		const static DWORD xmas_sock = 50010;
    		pkKiller->AutoGiveItem (xmas_sock, 1);
    	}
    
    	if ((pkKiller->CountSpecifyItem(53007) > 0) && (GetDropPerKillPct(50, 100, iDeltaPercent, "new_xmas_event") >= number(1, iRandRange)))
    	{
    		const static DWORD xmas_sock = 50010;
    		pkKiller->AutoGiveItem (xmas_sock, 1);
    	}
    
    	//if (pkChr->GetLevel() >= 30 && (GetDropPerKillPct(50, 100, iDeltaPercent, "ds_drop") >= number(1, iRandRange)))
    	//{
    	//	const static DWORD dragon_soul_gemstone = 30270;
    	//	if ((item = CreateItem(dragon_soul_gemstone, 1, 0, true)))
    	//		vec_item.push_back(item);
    	//}
    
    	if ( GetDropPerKillPct(100, g_iUseLocale ? 2000 : 800, iDeltaPercent, "halloween_drop") >= number(1, iRandRange) )
    	{
    		const static DWORD halloween_item = 30321;
    
    		if ( (item=CreateItem(halloween_item, 1, 0, true)) )
    			vec_item.push_back(item);
    	}
    	
    	if ( GetDropPerKillPct(100, g_iUseLocale ? 2000 : 800, iDeltaPercent, "ramadan_drop") >= number(1, iRandRange) )
    	{
    		const static DWORD ramadan_item = 30315;
    
    		if ( (item=CreateItem(ramadan_item, 1, 0, true)) )
    			vec_item.push_back(item);
    	}
    
    	if ( GetDropPerKillPct(100, g_iUseLocale ? 2000 : 800, iDeltaPercent, "easter_drop") >= number(1, iRandRange) )
    	{
    		const static DWORD easter_item_base = 50160;
    
    		if ( (item=CreateItem(easter_item_base+number(0,19), 1, 0, true)) )
    			vec_item.push_back(item);
    	}
    
    	// 월드컵 이벤트
    	if ( GetDropPerKillPct(100, g_iUseLocale ? 2000 : 800, iDeltaPercent, "football_drop") >= number(1, iRandRange) )
    	{
    		const static DWORD football_item = 50096;
    
    		if ( (item=CreateItem(football_item, 1, 0, true)) )
    			vec_item.push_back(item);
    	}
    
    	// 화이트 데이 이벤트
    	if (GetDropPerKillPct(100, g_iUseLocale ? 2000 : 800, iDeltaPercent, "whiteday_drop") >= number(1, iRandRange))
    	{
    		sys_log(0, "EVENT WHITEDAY_DROP");
    		const static DWORD whiteday_items[2] = { ITEM_WHITEDAY_ROSE, ITEM_WHITEDAY_CANDY };
    		DWORD dwVnum = whiteday_items[number(0,1)];
    
    		if ((item = CreateItem(dwVnum, 1, 0, true)))
    			vec_item.push_back(item);
    	}
    
    	// 어린이날 수수께끼 상자 이벤트
    	if (pkKiller->GetLevel()>=50)
    	{
    		if (GetDropPerKillPct(100, 1000, iDeltaPercent, "kids_day_drop_high") >= number(1, iRandRange))
    		{
    			DWORD ITEM_QUIZ_BOX = 50034;
    
    			if ((item = CreateItem(ITEM_QUIZ_BOX, 1, 0, true)))
    				vec_item.push_back(item);
    		}
    	}
    	else
    	{
    		if (GetDropPerKillPct(100, 1000, iDeltaPercent, "kids_day_drop") >= number(1, iRandRange))
    		{
    			DWORD ITEM_QUIZ_BOX = 50034;
    
    			if ((item = CreateItem(ITEM_QUIZ_BOX, 1, 0, true)))
    				vec_item.push_back(item);
    		}
    	}
    
    	// 올림픽 드롭 이벤트
    	if (pkChr->GetLevel() >= 30 && GetDropPerKillPct(50, 100, iDeltaPercent, "medal_part_drop") >= number(1, iRandRange))
    	{
    		const static DWORD drop_items[] = { 30265, 30266, 30267, 30268, 30269 };
    		int i = number (0, 4);
    		item = CreateItem(drop_items[i]);
    		if (item != NULL)
    			vec_item.push_back(item);
    	}
    
    	// ADD_GRANDMASTER_SKILL
    	// 혼석 아이템 드롭
    	if (pkChr->GetLevel() >= 40 && pkChr->GetMobRank() >= MOB_RANK_BOSS && GetDropPerKillPct(/* minimum */ 1, /* default */ 1000, iDeltaPercent, "three_skill_item") / GetThreeSkillLevelAdjust(pkChr->GetLevel()) >= number(1, iRandRange))
    	{
    		const DWORD ITEM_VNUM = 50513;
    
    		if ((item = CreateItem(ITEM_VNUM, 1, 0, true)))
    			vec_item.push_back(item);
    	}
    	// END_OF_ADD_GRANDMASTER_SKILL
    
    	//
    	// 종자 아이템 drop
    	//
    	if (GetDropPerKillPct(100, 1000, iDeltaPercent, "dragon_boat_festival_drop") >= number(1, iRandRange))
    	{
    		const DWORD ITEM_SEED = 50085;
    
    		if ((item = CreateItem(ITEM_SEED, 1, 0, true)))
    			vec_item.push_back(item);
    	}
    
    	// 무신의 축복서용 만년한철 drop
    	if (pkKiller->GetLevel() >= 15 && quest::CQuestManager::instance().GetEventFlag("mars_drop"))
    	{
    		const DWORD ITEM_HANIRON = 70035;
    		int iDropMultiply[MOB_RANK_MAX_NUM] =
    		{
    			50,
    			30,
    			5,
    			1,
    			0,
    			0,
    		};
    
    		if (iDropMultiply[pkChr->GetMobRank()] &&
    				GetDropPerKillPct(1000, 1500, iDeltaPercent, "mars_drop") >= number(1, iRandRange) * iDropMultiply[pkChr->GetMobRank()])
    		{
    			if ((item = CreateItem(ITEM_HANIRON, 1, 0, true)))
    				vec_item.push_back(item);
    		}
    	}
    }
    
    DWORD ITEM_MANAGER::GetRefineFromVnum(DWORD dwVnum)
    {
    	itertype(m_map_ItemRefineFrom) it = m_map_ItemRefineFrom.find(dwVnum);
    	if (it != m_map_ItemRefineFrom.end())
    		return it->second;
    	return 0;
    }
    
    const CSpecialItemGroup* ITEM_MANAGER::GetSpecialItemGroup(DWORD dwVnum)
    {
    	itertype(m_map_pkSpecialItemGroup) it = m_map_pkSpecialItemGroup.find(dwVnum);
    	if (it != m_map_pkSpecialItemGroup.end())
    	{
    		return it->second;
    	}
    	return NULL;
    }
    
    const CSpecialAttrGroup* ITEM_MANAGER::GetSpecialAttrGroup(DWORD dwVnum)
    {
    	itertype(m_map_pkSpecialAttrGroup) it = m_map_pkSpecialAttrGroup.find(dwVnum);
    	if (it != m_map_pkSpecialAttrGroup.end())
    	{
    		return it->second;
    	}
    	return NULL;
    }
    
    DWORD ITEM_MANAGER::GetMaskVnum(DWORD dwVnum)
    {
    	TMapDW2DW::iterator it = m_map_new_to_ori.find (dwVnum);
    	if (it != m_map_new_to_ori.end())
    	{
    		return it->second;
    	}
    	else
    		return 0;
    }
    
    // pkNewItem으로 모든 속성과 소켓 값들을 목사하는 함수.
    // 기존에 char_item.cpp 파일에 있던 로컬함수인 TransformRefineItem 그대로 복사함
    void ITEM_MANAGER::CopyAllAttrTo(LPITEM pkOldItem, LPITEM pkNewItem)
    {
    	// ACCESSORY_REFINE
    	if (pkOldItem->IsAccessoryForSocket())
    	{
    		for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
    		{
    			pkNewItem->SetSocket(i, pkOldItem->GetSocket(i));
    		}
    		//pkNewItem->StartAccessorySocketExpireEvent();
    	}
    	// END_OF_ACCESSORY_REFINE
    	else
    	{
    		// 여기서 깨진석이 자동적으로 청소 됨
    		for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
    		{
    			if (!pkOldItem->GetSocket(i))
    				break;
    			else
    				pkNewItem->SetSocket(i, 1);
    		}
    
    		// 소켓 설정
    		int slot = 0;
    
    		for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
    		{
    			long socket = pkOldItem->GetSocket(i);
    			const int ITEM_BROKEN_METIN_VNUM = 28960; // 이건 뭐 똑같은 상수가 3군데나 있냐... 하나로 해놓지ㅠㅠㅠ 나는 패스 홍이 할꺼임
    			if (socket > 2 && socket != ITEM_BROKEN_METIN_VNUM)
    				pkNewItem->SetSocket(slot++, socket);
    		}
    
    	}
    
    	// 매직 아이템 설정
    	pkOldItem->CopyAttributeTo(pkNewItem);
    }
    
    void ITEM_MANAGER::ReloadProto()
    {
    	sys_log(0, "RELOAD PROTO START");
    	db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0);
    }
    
    shop_manager.h:
    Code:
    #ifndef __INC_METIN_II_GAME_SHOP_MANAGER_H__
    #define __INC_METIN_II_GAME_SHOP_MANAGER_H__
    
    class CShop;
    typedef class CShop * LPSHOP;
    
    class CShopManager : public singleton<CShopManager>
    {
    public:
    	typedef std::map<DWORD, CShop *> TShopMap;
    
    #ifdef OFFLINE_SHOP
    public:
    	bool	StartOfflineShop(DWORD id, bool onboot = false);
    	bool	CreateOfflineShop(LPCHARACTER owner, const char *szSign,const std::vector<TShopItemTable*> pTable, DWORD days);
    	LPSHOP	CreateNPCShop(LPCHARACTER ch, std::vector<TShopItemTable *> map_shop);
    	DWORD	GetShopCount() { return m_map_pkShop.size(); }
    #endif
    
    public:
    	CShopManager();
    	virtual ~CShopManager();
    
    	bool	Initialize(TShopTable * table, int size);
    #if defined(ENABLE_RENEWAL_SHOPEX)
    	bool	InitializeShopEX(TShopTable* table, int size);
    #endif
    	void	Destroy();
    	void	ReloadShops();
    
    	LPSHOP	Get(DWORD dwVnum);
    	LPSHOP	GetByNPCVnum(DWORD dwVnum);
    
    	bool	StartShopping(LPCHARACTER pkChr, LPCHARACTER pkShopKeeper, int iShopVnum = 0);
    	void	StopShopping(LPCHARACTER ch);
    
    	void	Buy(LPCHARACTER ch, BYTE pos);
    	void	Sell(LPCHARACTER ch, WORD bCell, BYTE bCount = 0);
    
    	LPSHOP	CreatePCShop(LPCHARACTER ch, TShopItemTable * pTable, BYTE bItemCount);
    	LPSHOP	FindPCShop(DWORD dwVID);
    	void	DestroyPCShop(LPCHARACTER ch);
    
    private:
    	TShopMap	m_map_pkShop;
    	TShopMap	m_map_pkShopByNPCVnum;
    	TShopMap	m_map_pkShopByPC;
    
    	bool	ReadShopTableEx(const char* stFileName);
    };
    
    #endif
    
    
    Scrie răspuns

    Creează-ți un cont sau autentifică-te pentru a participa la discuție

    Trebuie să fii membru pentru a răspunde

    Creează-ți un cont

    Membrii pot crea subiecte noi și pot descărca resurse Metin2 Gratuit!


    Te poți înregistra sau conecta rapid utilizând contul tău de Discord, Github sau Google.

    Înregistrare

    Autentifică-te

    Înapoi la “Support”

    Informații

    Utilizatori ce navighează pe acest forum: Niciun utilizator înregistrat și 4 vizitatori

    Discord ID copiat: