#include "stdafx.h" #include "DNScriptAPICommon.h" #include "DnLuaAPIDefine.h" #include "DNGameDataManager.h" #include "DNQuestManager.h" #include "DNUserSession.h" #include "DNNpc.h" #include "DNReplaceString.h" #include "EtUIXML.h" #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) #include "ReputationSystemRepository.h" #include "NpcReputationProcessor.h" #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) #include "DNQuest.h" #if defined (_GAMESERVER) #include "DnPlayerActor.h" #endif #if defined(_VILLAGESERVER) && defined( PRE_DRAGONBUFF ) #include "DNUserSessionManager.h" #include "DNMasterConnection.h" #endif extern CLog g_ScriptLog; namespace DNScriptAPI { // desc : 인벤토리의 특정 빈슬롯에 아이템을 세팅 (P.S.> api_user_CheckInvenForAddItemList 에서 사용됨) bool __SetBlankInventorySlot(TInvenItemCnt pTempInven[INVENTORYMAX], const TItem& Item, int iIndex) { DN_ASSERT(NULL != pTempInven, "Invalid!"); DN_ASSERT(0 != Item.nItemID, "Invalid!"); DN_ASSERT(0 < Item.wCount, "Invalid!"); DN_ASSERT(-1 < iIndex, "Invalid!"); if (pTempInven[iIndex].nItemID != 0) { return false; } pTempInven[iIndex].Set( Item.nItemID, Item.wCount, Item.bSoulbound, Item.cSealCount ); return true; } // desc : 인벤토리의 빈슬롯들에 아이템을 특정 개수만큼 세팅 (P.S.> api_user_CheckInvenForAddItemList 에서 사용됨) bool __SetBlankInventorySlotCount(TInvenItemCnt pTempInven[INVENTORYMAX], const TItem& Item, int nOverlapCount, int nCount /* 채울 슬롯개수 */) { DN_ASSERT(NULL != pTempInven, "Invalid!"); DN_ASSERT(0 != Item.nItemID, "Invalid!"); DN_ASSERT(0 < Item.wCount, "Invalid!"); DN_ASSERT(0 < nOverlapCount, "Invalid!"); DN_ASSERT(0 < nCount, "Invalid!"); // !!! 주의 - 본 함수 호출 전에 체크가 확실히 되었음을 전제하고 세팅하는 부분만 수행 int nBundle = Item.wCount / nOverlapCount; int nRemain = Item.wCount % nOverlapCount; for (int iIndex = 0 ; INVENTORYMAX > iIndex ; ++iIndex) { if (0 < nBundle) { if (pTempInven[iIndex].nItemID == 0) { pTempInven[iIndex].Set( Item.nItemID, nOverlapCount, Item.bSoulbound, Item.cSealCount ); --nBundle; continue; } } if (0 < nRemain) { if( CDNUserItem::bIsSameItem( &Item, &pTempInven[iIndex] ) && nOverlapCount >= pTempInven[iIndex].wCount + nRemain ) { pTempInven[iIndex].wCount += nRemain; nRemain = 0; continue; } } } if (0 < nBundle) { return false; } if (0 < nRemain) { for (int iIndex = 0 ; INVENTORYMAX > iIndex ; ++iIndex) { if (pTempInven[iIndex].nItemID == 0) { pTempInven[iIndex].Set( Item.nItemID, nRemain, Item.bSoulbound, Item.cSealCount ); nRemain = 0; break; } } } if (0 < nRemain) { return false; } return true; } // desc : 인벤토리의 특정 슬롯에 아이템을 중첩 세팅 (P.S.> api_user_CheckInvenForAddItemList 에서 사용됨) bool __SetOverlapInventorySlot(TInvenItemCnt pTempInven[INVENTORYMAX], const TItem& Item, int nOverlapCount, int iIndex) { DN_ASSERT(NULL != pTempInven, "Invalid!"); DN_ASSERT(0 != Item.nItemID, "Invalid!"); DN_ASSERT(0 < Item.wCount, "Invalid!"); DN_ASSERT(0 < nOverlapCount, "Invalid!"); DN_ASSERT(-1 < iIndex, "Invalid!"); if ( CDNUserItem::bIsDifferentItem( &Item, &pTempInven[iIndex] ) || nOverlapCount < pTempInven[iIndex].wCount + Item.wCount ) { return false; } pTempInven[iIndex].wCount += Item.wCount; return true; } // desc : 인벤토리에 빈슬롯 찾기 (P.S.> api_user_CheckInvenForAddItemList 에서 사용됨) int __FindBlankInventorySlot(TInvenItemCnt pTempInven[INVENTORYMAX], int nTempInvenCount) { DN_ASSERT(NULL != pTempInven, "Invalid!"); DN_ASSERT(CHECK_RANGE(nTempInvenCount, 0, INVENTORYMAX), "Invalid!"); for (int iIndex = 0 ; nTempInvenCount > iIndex ; ++iIndex) { if (pTempInven[iIndex].nItemID == 0) { return iIndex; } } return -1; } // desc : 인벤토리에 빈슬롯이 몇 개나 있는지 개수 찾기 (P.S.> api_user_CheckInvenForAddItemList 에서 사용됨) int __FindBlankInventorySlotCount(TInvenItemCnt pTempInven[INVENTORYMAX], int nTempInvenCount) { DN_ASSERT(NULL != pTempInven, "Invalid!"); DN_ASSERT(CHECK_RANGE(nTempInvenCount, 0, INVENTORYMAX), "Invalid!"); int nCount = 0; for (int iIndex = 0 ; nTempInvenCount > iIndex ; ++iIndex) { if (pTempInven[iIndex].nItemID == 0) { ++nCount; } } return nCount; } // desc : 인벤토리에 셀 수 있는 아이템 중 남은 공간에 wCount 넣을 수 있는 Index 찾기 (P.S.> api_user_CheckInvenForAddItemList 에서 사용됨) int __FindOverlapInventorySlot(TInvenItemCnt pTempInven[INVENTORYMAX], int nTempInvenCount, const TItem& Item) { DN_ASSERT(NULL != pTempInven, "Invalid!"); DN_ASSERT(CHECK_RANGE(nTempInvenCount, 0, INVENTORYMAX), "Invalid!"); if ((Item.nItemID <= 0) || (Item.wCount <= 0)) DN_RETURN(-1); TItemData *pItemData = NULL; for (int iIndex = 0 ; nTempInvenCount > iIndex ; ++iIndex) { if (pTempInven[iIndex].nItemID == 0) { // 빈칸이면 패스 continue; } pItemData = g_pDataManager->GetItemData(pTempInven[iIndex].nItemID); if (!pItemData) continue; if (pItemData->nOverlapCount == 1) continue; // 겹치지 않으면 패스 if( CDNUserItem::bIsDifferentItem( &Item, &pTempInven[iIndex] ) ) continue; // 같은 아이템이 아님 if (pItemData->nOverlapCount >= pTempInven[iIndex].wCount + Item.wCount) { return iIndex; } } return -1; } // desc : 인벤토리에 아이템을 넣을 수 있는 공간이 충분한지 검사 (P.S.> api_user_CheckInvenForAddItemList 에서 사용됨) bool __IsValidSpaceInventorySlotAndSet(TInvenItemCnt pTempInven[INVENTORYMAX], int nTempInvenCount, const TItem& Item ) { DN_ASSERT(NULL != pTempInven, "Invalid!"); DN_ASSERT(CHECK_RANGE(nTempInvenCount, 0, INVENTORYMAX), "Invalid!"); if ((Item.nItemID <= 0) || (Item.wCount <= 0)) DN_RETURN(false); TItemData *pItem = g_pDataManager->GetItemData(Item.nItemID); if (!pItem) return false; #if defined (_VILLAGESERVER) if (ITEMTYPE_QUEST == pItem->nType) { return false; } #endif int nCount = 0; if (pItem->nOverlapCount == 1){ // 겹치지 않는 아이템 nCount = __FindBlankInventorySlotCount(pTempInven, nTempInvenCount); if (Item.wCount > nCount) { // 빈슬롯이랑 비교하기 return false; } return(__SetBlankInventorySlotCount(pTempInven, Item, 1, nCount)); } else { // 겹쳐지는 아이템 (인벤에 동일 아이템들이 몇개씩 남아있어도 신경안쓰고 깔끔하게 넣는 경우만 생각하쟈) if (pItem->nOverlapCount < Item.wCount){ // 한번에 겹치는 양보다 많을경우 int nBundle = Item.wCount / pItem->nOverlapCount; if ((Item.wCount % pItem->nOverlapCount) > 0) nBundle++; nCount = __FindBlankInventorySlotCount(pTempInven, nTempInvenCount); if (nBundle > nCount) { // 묶음이랑 빈슬롯이랑 비교하기 return false; } return(__SetBlankInventorySlotCount(pTempInven, Item, pItem->nOverlapCount, nCount)); } else { int nBlank = __FindOverlapInventorySlot(pTempInven, nTempInvenCount, Item ); // 기존 아이템중에 한방에 들어갈 아이템이 있는지 if (nBlank < 0){ nBlank = __FindBlankInventorySlot(pTempInven, nTempInvenCount); if (nBlank < 0) { return false; } return(__SetBlankInventorySlot(pTempInven, Item, nBlank)); } return(__SetOverlapInventorySlot(pTempInven, Item, pItem->nOverlapCount, nBlank)); } } return true; } // desc : 퀘스트 인벤토리의 특정 빈슬롯에 아이템을 세팅 (P.S.> api_quest_CheckQuestInvenForAddItemList 에서 사용됨) bool __SetBlankQuestInventorySlot(TQuestItemCnt pTempQuestInven[QUESTINVENTORYMAX], int nItemID, short wCount, int iIndex) { DN_ASSERT(NULL != pTempQuestInven, "Invalid!"); DN_ASSERT(0 != nItemID, "Invalid!"); DN_ASSERT(0 < wCount, "Invalid!"); DN_ASSERT(-1 < iIndex, "Invalid!"); if (pTempQuestInven[iIndex].nItemID != 0) { return false; } pTempQuestInven[iIndex].Set(nItemID, wCount); return true; } // desc : 퀘스트 인벤토리의 빈슬롯들에 아이템을 특정 개수만큼 세팅 (P.S.> api_quest_CheckQuestInvenForAddItemList 에서 사용됨) bool __SetBlankQuestInventorySlotCount(TQuestItemCnt pTempQuestInven[QUESTINVENTORYMAX], int nItemID, short wCount, int nOverlapCount, int nCount /* 채울 슬롯개수 */) { DN_ASSERT(NULL != pTempQuestInven, "Invalid!"); DN_ASSERT(0 != nItemID, "Invalid!"); DN_ASSERT(0 < wCount, "Invalid!"); DN_ASSERT(0 < nOverlapCount, "Invalid!"); DN_ASSERT(0 < nCount, "Invalid!"); // !!! 주의 - 본 함수 호출 전에 체크가 확실히 되었음을 전제하고 세팅하는 부분만 수행 int nBundle = wCount / nOverlapCount; int nRemain = wCount % nOverlapCount; for (int iIndex = 0 ; QUESTINVENTORYMAX > iIndex ; ++iIndex) { if (0 < nBundle) { if (pTempQuestInven[iIndex].nItemID == 0) { pTempQuestInven[iIndex].Set(nItemID, nOverlapCount); --nBundle; continue; } } if (0 < nRemain) { if (pTempQuestInven[iIndex].nItemID == nItemID && nOverlapCount >= pTempQuestInven[iIndex].wCount + nRemain ) { pTempQuestInven[iIndex].wCount += nRemain; nRemain = 0; continue; } } } if (0 < nBundle) { return false; } if (0 < nRemain) { for (int iIndex = 0 ; QUESTINVENTORYMAX > iIndex ; ++iIndex) { if (pTempQuestInven[iIndex].nItemID == 0) { pTempQuestInven[iIndex].Set(nItemID, nRemain); nRemain = 0; break; } } } if (0 < nRemain) { return false; } return true; } // desc : 퀘스트 인벤토리의 특정 슬롯에 아이템을 중첩 세팅 (P.S.> api_quest_CheckQuestInvenForAddItemList 에서 사용됨) bool __SetOverlapQuestInventorySlot(TQuestItemCnt pTempQuestInven[QUESTINVENTORYMAX], int nItemID, short wCount, int nOverlapCount, int iIndex) { DN_ASSERT(NULL != pTempQuestInven, "Invalid!"); DN_ASSERT(0 != nItemID, "Invalid!"); DN_ASSERT(0 < wCount, "Invalid!"); DN_ASSERT(0 < nOverlapCount, "Invalid!"); DN_ASSERT(-1 < iIndex, "Invalid!"); if (pTempQuestInven[iIndex].nItemID != nItemID || nOverlapCount < pTempQuestInven[iIndex].wCount + wCount ) { return false; } pTempQuestInven[iIndex].wCount += wCount; return true; } // desc : 퀘스트 인벤토리에 빈슬롯이 몇 개나 있는지 개수 찾기 (P.S.> api_quest_CheckQuestInvenForAddItemList 에서 사용됨) int __FindBlankQuestInventorySlotCount(TQuestItemCnt pTempQuestInven[QUESTINVENTORYMAX]) { DN_ASSERT(NULL != pTempQuestInven, "Invalid!"); int nCount = 0; for (int iIndex = 0 ; QUESTINVENTORYMAX > iIndex ; ++iIndex) { if (pTempQuestInven[iIndex].nItemID == 0) { ++nCount; } } return nCount; } // desc : 퀘스트 인벤토리에 빈슬롯 찾기 (P.S.> api_quest_CheckQuestInvenForAddItemList 에서 사용됨) int __FindBlankQuestInventorySlot(TQuestItemCnt pTempQuestInven[QUESTINVENTORYMAX]) { DN_ASSERT(NULL != pTempQuestInven, "Invalid!"); for (int iIndex = 0 ; QUESTINVENTORYMAX > iIndex ; ++iIndex) { if (pTempQuestInven[iIndex].nItemID == 0) { return iIndex; } } return -1; } // desc : 퀘스트 인벤토리에 셀 수 있는 아이템 중 남은 공간에 wCount 넣을 수 있는 Index 찾기 (P.S.> api_quest_CheckQuestInvenForAddItemList 에서 사용됨) int __FindOverlapQuestInventorySlot(TQuestItemCnt pTempQuestInven[QUESTINVENTORYMAX], int nItemID, short wCount) { DN_ASSERT(NULL != pTempQuestInven, "Invalid!"); if ((nItemID <= 0) || (wCount <= 0)) DN_RETURN(-1); bool boFlag = false; TItemData *pQuestItem = NULL; for (int iIndex = 0 ; QUESTINVENTORYMAX > iIndex ; ++iIndex) { if (pTempQuestInven[iIndex].nItemID == 0) continue; // 빈칸이면 패스 pQuestItem = g_pDataManager->GetItemData(pTempQuestInven[iIndex].nItemID); if (!pQuestItem) continue; if (pQuestItem->nOverlapCount == 1) continue; // 겹치지 않으면 패스 if (pTempQuestInven[iIndex].nItemID != nItemID) continue; // 같은 아이템이 아니다 if (pQuestItem->nOverlapCount >= pTempQuestInven[iIndex].wCount + wCount){ return iIndex; } } return -1; } // desc : 퀘스트 인벤토리에 아이템을 넣을 수 있는 공간이 충분한지 검사 (P.S.> api_quest_CheckQuestInvenForAddItemList 에서 사용됨) bool __IsValidSpaceQuestInventorySlotAndSet(TQuestItemCnt pTempQuestInven[QUESTINVENTORYMAX], int nItemID, short wCount) { DN_ASSERT(NULL != pTempQuestInven, "Invalid!"); if ((nItemID <= 0) || (wCount <= 0)) DN_RETURN(false); TItemData *pItem = g_pDataManager->GetItemData(nItemID); if (!pItem) return false; if (ITEMTYPE_QUEST != pItem->nType) { return false; } int nCount = 0; if (pItem->nOverlapCount == 1){ // 겹치지 않는 아이템 nCount = __FindBlankQuestInventorySlotCount(pTempQuestInven); if (wCount > nCount) { // 빈슬롯이랑 비교하기 return false; } return(__SetBlankQuestInventorySlotCount(pTempQuestInven, nItemID, wCount, 1, nCount)); } else { // 겹치는 아이템 int nRemain = 0, nBlank = 0, nBundle = 0; int nCount = __FindBlankQuestInventorySlotCount(pTempQuestInven); // 빈 슬롯개수 if (pItem->nOverlapCount >= wCount){ nRemain = wCount; } else { nBundle = wCount / pItem->nOverlapCount; nRemain = wCount % pItem->nOverlapCount; if (nBundle > nCount) return false; // 공간부족 } if (nRemain > 0){ nBlank = __FindOverlapQuestInventorySlot(pTempQuestInven, nItemID, nRemain); // 짜투리가 들어갈 공간이 있는지 if (nBlank < 0){ // 짜투리 공간은 없다 if ((nBundle + 1) > nCount) return false; // 한칸의 여유가 더 있는지... } } return(__SetBlankQuestInventorySlotCount(pTempQuestInven, nItemID, wCount, pItem->nOverlapCount, nCount)); } return true; } int CheckInvenForQuestReward(CDNUserBase* pUser, const TQuestReward& QuestReward, const bool bSelectedArray[]) { if (!pUser) return -1; const CDNUserItem* pUserItem = pUser->GetItem(); if (!pUserItem) return -2; TInvenItemCnt TempInven[INVENTORYMAX]; ::memset(TempInven, 0, sizeof(TempInven)); #if defined(PRE_PERIOD_INVENTORY) for (int i = 0; i < INVENTORYTOTALMAX; i++) { if (i == pUserItem->GetInventoryCount()){ if (pUserItem->IsEnablePeriodInventory()) i = INVENTORYMAX; else break; } #else // #if defined(PRE_PERIOD_INVENTORY) for (int i = 0 ; i < INVENTORYMAX; ++i) { #endif // #if defined(PRE_PERIOD_INVENTORY) const TItem* pItem = pUserItem->GetInventory(i); if (pItem) TempInven[i].Set(pItem->nItemID, pItem->wCount, pItem->bSoulbound, pItem->cSealCount); } //만약 빌리지, 게임 둘다 아니면 컴파일 에러. #if defined (_VILLAGESERVER) UINT nUserObjectID = static_cast(pUser)->GetObjectID(); #endif #if defined (_GAMESERVER) UINT nUserObjectID = static_cast(pUser)->GetSessionID(); #endif bool bSelectedItems[QUESTREWARD_INVENTORYITEMMAX]; CopyMemory(bSelectedItems, bSelectedArray, sizeof(bSelectedItems)); if (QuestReward.cType == 1) // 전체 보상이면 아이템을 전부 선택한것으로 취급. (전체 보상과 선택 보상 로직 통일.) { for (int i = 0; i < QUESTREWARD_INVENTORYITEMMAX; ++i) bSelectedItems[i] = true; } for (int i = 0; i < QUESTREWARD_INVENTORYITEMMAX; ++i) { if(!bSelectedItems[i]) continue; if (QuestReward.ItemArray[i].nItemID <= 0 || QuestReward.ItemArray[i].nItemCount <= 0) continue; TItem Item; if (!CDNUserItem::MakeItemStruct(QuestReward.ItemArray[i].nItemID, Item)) return -3; Item.wCount = QuestReward.ItemArray[i].nItemCount; if (!__IsValidSpaceInventorySlotAndSet(TempInven, pUserItem->GetInventoryCount(), Item)) return -4; } for (int i = 0; i < MAX_QUEST_LEVEL_CAP_REWARD; ++i) { if (QuestReward.LevelCapItemArray[i].nItemID <= 0 || QuestReward.LevelCapItemArray[i].nItemCount <= 0) continue; TItem Item; if (!CDNUserItem::MakeItemStruct(QuestReward.LevelCapItemArray[i].nItemID, Item)) return -3; Item.wCount = QuestReward.LevelCapItemArray[i].nItemCount; if (!__IsValidSpaceInventorySlotAndSet(TempInven, pUserItem->GetInventoryCount(), Item)) return -4; } return 1; } void api_npc_NextTalk(CDNUserBase* pUser, UINT nNpcObjectID, const char* szTalkIndex, const char* szTargetFile) { if (!pUser) return; WCHAR wszTalkIndex[512] = {0,}; WCHAR wszTargetFile[512] = {0,}; ZeroMemory(&wszTalkIndex, sizeof(WCHAR)*512); MultiByteToWideChar(CP_ACP, 0, szTalkIndex, -1, wszTalkIndex, 512 ); ZeroMemory(&wszTargetFile, sizeof(WCHAR)*512); MultiByteToWideChar(CP_ACP, 0, szTargetFile, -1, wszTargetFile, 512 ); // 다음 대사문단을 세팅해놓는다. // 유저한테 온 패킷이 이 대사 목록중에 없으면 뭔가가 조작된거임.. TALK_PARAGRAPH& para = pUser->GetLastTalkParagraph(); bool bResult = g_pDataManager->GetTalkParagraph(std::wstring(wszTalkIndex), std::wstring(wszTargetFile), para); if ( bResult == false ) { #if defined (_VILLAGESERVER) if (!g_pDataManager->CheckTalkAnswer(std::wstring(wszTalkIndex), std::wstring(wszTargetFile))) { // 컷신 등은 talk_paragraph 없이 talk_answer 만 존재하는 경우가 있으며 이것은 유효함 pUser->SendNextTalkError(); } #endif return; } pUser->SetCalledNpcResponse(true, true); std::vector& talkParamList = pUser->GetTalkParamList(); pUser->NextTalk(nNpcObjectID, wszTalkIndex, wszTargetFile, talkParamList ); talkParamList.clear(); } void api_npc_NextScript(CDNUserBase* pUser, CDnNpc* pNpc, UINT nNpcObjectID, const char* szTalkIndex, const char* szTargetFile) { WCHAR wszTalkIndex[512]; WCHAR wszTargetFile[512]; ZeroMemory(&wszTalkIndex, sizeof(WCHAR)*512); MultiByteToWideChar(CP_ACP, 0, szTalkIndex, -1, wszTalkIndex, 512 ); ZeroMemory(&wszTargetFile, sizeof(WCHAR)*512); MultiByteToWideChar(CP_ACP, 0, szTargetFile, -1, wszTargetFile, 512 ); // 다음 대사문단을 세팅해놓는다. // 유저한테 온 패킷이 이 대사 목록중에 없으면 뭔가가 조작된거임.. TALK_PARAGRAPH& para = pUser->GetLastTalkParagraph(); bool bResult = g_pDataManager->GetTalkParagraph(std::wstring(wszTalkIndex), std::wstring(wszTargetFile), para); if ( bResult == false ) return; bool& bSkipCheck = pUser->GetSkipParagraphCheck(); bSkipCheck = true; pUser->SetCalledNpcResponse(true, true); pNpc->Talk(pUser, nNpcObjectID, std::wstring(wszTalkIndex), std::wstring(wszTargetFile) ); } int api_npc_SetParamString(CDNUserBase* pUser, const char* szParamKey, int nParamID) { if (!pUser) return -1; std::vector& talkParamList = pUser->GetTalkParamList(); if ( talkParamList.size() > 10 ) return -2; std::wstring wszParam; ToWideString(std::string(szParamKey), wszParam); TalkParam talkparam; ZeroMemory(&talkparam, sizeof(TalkParam)); talkparam.cType = TalkParam::STRING; _wcscpy(talkparam.wszKey, _countof(talkparam.wszKey), wszParam.c_str(), (int)wcslen(wszParam.c_str())); talkparam.nValue = nParamID; talkParamList.push_back(talkparam); return 1; } int api_npc_SetParamInt(CDNUserBase* pUser, const char* szParamKey, int nValue) { if (!pUser) return -1; std::vector& talkParamList = pUser->GetTalkParamList(); if ( talkParamList.size() > 10 ) return -2; std::wstring wszParam; ToWideString(std::string(szParamKey), wszParam); TalkParam talkparam; ZeroMemory(&talkparam, sizeof(TalkParam)); talkparam.cType = TalkParam::INT; _wcscpy(talkparam.wszKey, _countof(talkparam.wszKey), wszParam.c_str(), (int)wcslen(wszParam.c_str())); talkparam.nValue = nValue; talkParamList.push_back(talkparam); return 1; } void api_log_AddLog(const char* szLog) { #if !defined(_FINAL_BUILD) WCHAR wszMsg[512]; ZeroMemory(&wszMsg, sizeof(WCHAR)*512); MultiByteToWideChar(CP_ACP, 0, szLog, -1, wszMsg, 512 ); g_ScriptLog.Log(LogType::_FILELOG, wszMsg); #endif //#if !defined(_FINAL_BUILD) } void api_log_UserLog(CDNUserBase* pUser, const char* szLog) { #if !defined(_FINAL_BUILD) if (!pUser) return; WCHAR wszMsg[512]={0,}; ZeroMemory(&wszMsg, sizeof(WCHAR)*512); MultiByteToWideChar(CP_ACP, 0, szLog, -1, wszMsg, 512 ); std::wstring wszString; wszString = wszMsg; static_cast(pUser)->SendChat(CHATTYPE_NORMAL, (int)wszString.size()*sizeof(WCHAR), L"", (WCHAR*)wszString.c_str()); #endif //#if !defined(_FINAL_BUILD) } /* desc : 유저에게 퀘스트 관련 정보를 채팅으로 보내준다. param : return : void */ void api_quest_DumpQuest(CDNUserBase* pUser) { #if !defined(_FINAL_BUILD) if (!pUser) return; std::vector QuestList; pUser->GetQuest()->DumpQuest(QuestList); for ( size_t i = 0 ; i < QuestList.size() ; i++ ) { TQuest& quest = QuestList[i]; std::wstring wszString; wszString = FormatW( L"[Slot:%d] QID:%d Type:%d QStep:%d JStep:%d Marking:%d\n", i, (int)quest.nQuestID, g_pQuestManager->GetQuestType(quest.nQuestID), (int)quest.nQuestStep, (int)quest.cQuestJournal, (!quest.nQuestID)?(0):(pUser->GetQuest()->IsClearQuest(quest.nQuestID)) ); static_cast(pUser)->SendChat(CHATTYPE_NORMAL, (int)wszString.size()*sizeof(WCHAR), L"", (WCHAR*)wszString.c_str()); } #endif //#if !defined(_FINAL_BUILD) } int api_quest_AddHuntingQuest(CDNUserBase* pUser, int nQuestID, int nQuestStep, int nJournalStep, int nCountingSlot, int nCountingType, int nCountingIndex, int nTargetCnt) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif return pUser->GetQuest()->AddHuntingQuest(nQuestID, nQuestStep, nJournalStep, nCountingSlot, nCountingType, nCountingIndex, nTargetCnt); } // nQuestType : 사용하지 않는 매개변수임. int api_quest_AddQuest(CDNUserBase* pUser, int nQuestID, int nQuestType) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif return pUser->GetQuest()->AddQuest(nQuestID); } int api_quest_CompleteQuest(CDNUserBase* pUser, int nQuestID, bool bDelPlayList) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif // 퀘스트 완료 int nReturn = pUser->GetQuest()->RewardAfterCompletingQuest(nQuestID, bDelPlayList); // 보상관련 변수 초기화 pUser->GetQuest()->ResetRewardFlag(); return nReturn; } int api_quest_IsMarkingCompleteQuest(CDNUserBase* pUser, int nQuestID) { if (!pUser) return -1; return (pUser->GetQuest()->IsClearQuest(nQuestID)) ? 1 : 0; } int api_quest_UserHasQuest(CDNUserBase* pUser, int nQuestID) { if (!pUser) return -1; return pUser->GetQuest()->HasQuest(nQuestID); } int api_quest_GetPlayingQuestCnt(CDNUserBase* pUser) { if (!pUser) return -1; return pUser->GetQuest()->GetPlayingQuestCount(); } int api_npc_GetNpcIndex(CDnNpc* pNpc) { if (!pNpc) return -2; return pNpc->GetNpcData()->nNpcID; } int api_quest_SetQuestStepAndJournalStep(CDNUserBase* pUser, int nQuestID, short nQuestStep, int nJournalStep) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif if ( nJournalStep < 0 || nJournalStep > 255 ) return -3; return pUser->GetQuest()->SetQuestStepAndJournalStep(nQuestID, nQuestStep, nJournalStep, true); } int api_quest_SetQuestStep(CDNUserBase* pUser, int nQuestID, short nQuestStep) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif return pUser->GetQuest()->SetQuestStep(nQuestID, nQuestStep); } int api_quest_GetQuestStep(CDNUserBase* pUser, int nQuestID) { if (!pUser) return -1; return pUser->GetQuest()->GetQuestStep(nQuestID); } int api_quest_SetJournalStep(CDNUserBase* pUser, int nQuestID, int nJournalStep) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif if ( nJournalStep < 0 || nJournalStep > 255 ) return -3; return pUser->GetQuest()->SetJournalStep(nQuestID, nJournalStep); } int api_quest_GetJournalStep(CDNUserBase* pUser, int nQuestID) { if (!pUser) return -1; return pUser->GetQuest()->GetJournalStep(nQuestID); } int api_quest_SetQuestMemo(CDNUserBase* pUser, int nQuestID, char nMemoIndex, int iVal) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif if (QUESTMEMOREWARDCHECK == nMemoIndex) { // 퀘스트 보상 무한지급을 방지하기 위하여 할당받은 인덱스는 사용할 수 없음 DN_RETURN(-3); } if (!CHECK_RANGE(nMemoIndex, 1, QUESTMEMOMAX)) { // 퀘스트 메모 인덱스 범위를 벗어남 (1~10) DN_RETURN(-3); } return pUser->GetQuest()->SetQuestMemo(nQuestID, nMemoIndex-1, iVal); } int api_quest_GetQuestMemo(CDNUserBase* pUser, int nQuestID, char nMemoIndex) { if (!pUser) return -1; if (!CHECK_RANGE(nMemoIndex, 1, QUESTMEMOMAX)) { // 퀘스트 메모 인덱스 범위를 벗어남 (1~10) DN_RETURN(-3); } return pUser->GetQuest()->GetQuestMemo(nQuestID, nMemoIndex-1); } int api_quest_SetCountingInfo(CDNUserBase* pUser, int nQuestID, int nSlot, int nCountingType, int nCountingIndex, int nTargetCnt) { if (!pUser) return -1; return pUser->GetQuest()->SetCountingInfo(nQuestID, nSlot, nCountingType, nCountingIndex, nTargetCnt); } int api_quest_ClearCountingInfo(CDNUserBase* pUser, int nQuestID) { if (!pUser) return -1; return pUser->GetQuest()->ClearCountingInfo(nQuestID); } int api_quest_IsAllCompleteCounting(CDNUserBase* pUser, int nQuestID) { if (!pUser) return -1; return pUser->GetQuest()->IsAllCompleteCounting(nQuestID); } int api_user_CheckInvenForAddItem(CDNUserBase* pUser, int nItemIndex, int nItemCnt) { if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if (!pUserItem) { return -2; } TItem ResultItem; if( CDNUserItem::MakeItemStruct( nItemIndex, ResultItem ) == false ) return -2; // 일단은 임시로 빈공간만 확인한다. // 인벤에 빈공간이 없으면 if (!pUserItem->IsValidSpaceInventorySlot(nItemIndex, nItemCnt, ResultItem.bSoulbound, ResultItem.cSealCount, ResultItem.bEternity)) { #if defined (_GAMESERVER) pUser->SendQuestResult(ERROR_ITEM_INVENTORY_NOTENOUGH); #endif return -3; } return 1; } int api_user_CheckInvenForAddItemList(CDNUserBase* pUser, lua_tinker::table ItemTable) { if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if (!pUserItem) { return -2; } TInvenItemCnt TempInven[INVENTORYMAX]; { // 임시 인벤토리에 현재 인벤토리의 아이템 개수 정보들을 복사 ::memset(TempInven, 0, sizeof(TempInven)); for (int iIndex = 0 ; INVENTORYMAX > iIndex ; ++iIndex) { const TItem* pItem = pUserItem->GetInventory(iIndex); if (pItem) { TempInven[iIndex].Set(pItem->nItemID, pItem->wCount, pItem->bSoulbound, pItem->cSealCount ); } else { TempInven[iIndex].Reset(); } } } if (0 >= ItemTable.getSize()) { // 아이템 테이블에 아무값도 없음 ? DN_RETURN(false); } for (int iIndex = 1 ; ItemTable.getSize() >= iIndex ; ++iIndex) { lua_tinker::table pTable = ItemTable.get(iIndex); if (0 >= pTable.getSize()) { continue; } TItem Item; if( CDNUserItem::MakeItemStruct( pTable.get(1), Item ) == false ) return -2; Item.wCount = pTable.get(2); bool bResult = __IsValidSpaceInventorySlotAndSet(TempInven, pUserItem->GetInventoryCount(), Item ); // RandomSeed 는 어떻게 ? if (!bResult) { #if defined (_GAMESERVER) pUser->SendQuestResult(ERROR_ITEM_INVENTORY_NOTENOUGH); #endif return -3; } } return 1; } int api_user_AddItem(CDNUserBase* pUser, int nItemIndex, int nItemCnt, int nQuestID) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; #if defined (_VILLAGESERVER) int nResult = api_user_CheckInvenForAddItem(pUser, nItemIndex, nItemCnt); if ( nResult < 0 ) return nResult; bool bResult = pUserItem->AddInventoryByQuest(nItemIndex, nItemCnt, nQuestID, 0); // 아이템에 따라서 randomseed 를 어떻게 넣을껀지 결정해주시길! #ifndef _FINAL_BUILD if ( bResult ) { std::wstring wszMsg; wszMsg = FormatW( L"아이템 지급 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } else { std::wstring wszMsg; wszMsg = FormatW( L"아이템 지급 실패 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } #endif //#ifndef _FINAL_BUILD #endif //#if defined (_VILLAGESERVER) #if defined (_GAMESERVER) TItem ResultItem; if( CDNUserItem::MakeItemStruct(nItemIndex, ResultItem) == false ) return -2; // 일단은 임시로 빈공간만 확인한다. // 인벤에 빈공간이 없으면 if ( !pUserItem->IsValidSpaceInventorySlot(nItemIndex, nItemCnt, ResultItem.bSoulbound, ResultItem.cSealCount, ResultItem.bEternity ) ) { return -3; } bool bResult = pUserItem->AddInventoryByQuest(nItemIndex, nItemCnt, nQuestID, 0); // 아이템에 따라서 randomseed 를 어떻게 넣을껀지 결정해주시길! #if defined( PRE_ENABLE_QUESTCHATLOG ) if ( bResult ) { // 임시로 실제로 주진 않고 유저 채팅창에만 찍어준다. std::wstring wszMsg; wszMsg = FormatW( L"아이템 지급 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } else { // 임시로 실제로 주진 않고 유저 채팅창에만 찍어준다. std::wstring wszMsg; wszMsg = FormatW( L"아이템 지급 실패 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } #endif // #if defined( PRE_ENABLE_QUESTCHATLOG ) #endif //#if defined (_GAMESERVER) return 1; } int api_user_DelItem(CDNUserBase* pUser, int nItemIndex, int nItemCnt, int nQuestID) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; int nCount = pUserItem->GetInventoryItemCount(nItemIndex); if ( nCount < nItemCnt ) { return -3; } bool bResult = pUserItem->DeleteInventoryByQuest(nItemIndex, nItemCnt, nQuestID); #if defined (_VILLAGESERVER) #ifndef _FINAL_BUILD if ( bResult ) { std::wstring wszMsg; wszMsg = FormatW( L"아이템 제거 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } else { std::wstring wszMsg; wszMsg = FormatW( L"아이템 제거 실패 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } #endif #endif #if defined (_GAMESERVER) #if defined (PRE_ENABLE_QUESTCHATLOG) if ( bResult ) { std::wstring wszMsg; wszMsg = FormatW( L"아이템 제거 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } else { std::wstring wszMsg; wszMsg = FormatW( L"아이템 제거 실패 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } #endif #endif return 1; } int api_user_AllDelItem(CDNUserBase* pUser, int nItemIndex) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; int nCount = pUserItem->GetInventoryItemCount(nItemIndex); bool bResult = pUserItem->DeleteInventoryByQuest(nItemIndex, nCount, 0); #if defined (_VILLAGESERVER) #ifndef _FINAL_BUILD if ( bResult ) { std::wstring wszMsg; wszMsg = FormatW( L"아이템 제거 : id %d count : %d" , nItemIndex, nCount ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } else { std::wstring wszMsg; wszMsg = FormatW( L"아이템 제거 실패 : id %d count : %d" , nItemIndex, nCount ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } #endif #endif #if defined (_GAMESERVER) #if defined (PRE_ENABLE_QUESTCHATLOG) if ( bResult ) { std::wstring wszMsg; wszMsg = FormatW( L"아이템 제거 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } else { std::wstring wszMsg; wszMsg = FormatW( L"아이템 제거 실패 : id %d count : %d" , nItemIndex, nItemCnt ); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); } #endif #endif return 1; } int api_user_HasItem(CDNUserBase* pUser, int nItemIndex, int nItemCnt) { if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; int nCount = pUserItem->GetInventoryItemCount(nItemIndex); if (nCount < nItemCnt) { return -3; } return nCount; } int api_user_GetUserClassID(CDNUserBase* pUser) { if (!pUser) return -1; return int(pUser->GetClassID()); } int api_user_GetUserJobID(CDNUserBase* pUser) { if (!pUser) return -1; return pUser->GetUserJob(); } int api_user_GetUserLevel(CDNUserBase* pUser) { if (!pUser) return -1; return int(pUser->GetLevel()); } int api_user_GetUserInvenBlankCount(CDNUserBase* pUser) { if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; return pUserItem->FindBlankInventorySlotCount(); } int api_quest_GetUserQuestInvenBlankCount(CDNUserBase* pUser) { if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; return pUserItem->FindBlankQuestInventorySlotCount(); } int api_user_PlayCutScene(CDNUserBase* pUser, UINT nNpcObjectID, int nCutSceneTableID, bool bIgnoreFadeIn) { if (!pUser) return -1; #ifndef _FINAL_BUILD DNTableFileFormat* pSox = GetDNTable( CDnTableDB::TCUTSCENE ); if( pSox->IsExistItem( nCutSceneTableID ) == false ) { std::wstring wszLog; wszLog = FormatW( L"동영상 플레이 파일 인덱스가 잘못되었습니다. : %d", nCutSceneTableID); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); return -1; } std::string szFileName = pSox->GetFieldFromLablePtr( nCutSceneTableID, "_FileName" )->GetString(); WCHAR wszMsg[512]; ZeroMemory(&wszMsg, sizeof(WCHAR)*512); MultiByteToWideChar(CP_ACP, 0, szFileName.c_str(), -1, wszMsg, 512 ); std::wstring wszLog; wszLog = FormatW( L"동영상 플레이 %s : ", wszMsg); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendPlayCutScene( nCutSceneTableID,-1, -1, nNpcObjectID, !bIgnoreFadeIn, UINT_MAX ); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenWareHouse(CDNUserBase* pUser, int iItemID/*=0*/ ) { if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"창고열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendShowWarehouse( iItemID ); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } #if defined (PRE_MOD_GAMESERVERSHOP) int api_ui_OpenShop(CDNUserBase* pUser, int nShopID, Shop::Type::eCode Type) { #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"샵열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif TShopData *pShopData = g_pDataManager->GetShopData(nShopID); if ( !pShopData ) { if( false == g_pDataManager->IsCombinedShop( nShopID ) ) return -2; } #if defined(PRE_ADD_REMOTE_OPENSHOP) pUser->m_bRemoteShopOpen = false; #endif // #if defined(PRE_ADD_REMOTE_OPENSHOP) pUser->m_nShopID = nShopID; pUser->SendShopOpen(nShopID,Type); pUser->SetShopType( Type ); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenSkillShop(CDNUserBase* pUser) { #ifndef _FINAL_BUILD //std::wstring wszLog; //wszLog = FormatW( L"돈주고 언락하는 스킬샵 열기"); //pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendSkillShopOpen(); //TSkillShopData *pShopData = g_pDataManager->GetSkillShopData(nSkillShopID); //if ( !pShopData ) // return -2; // //pUser->m_nShopID = nSkillShopID; //pUser->SendSkillShopList(pShopData); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } #endif int api_ui_OpenCompoundEmblem(CDNUserBase* pUser) { if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"문장보옥 합성 창열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendOpenCompoundEmblem(); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenUpgradeJewel(CDNUserBase* pUser) { if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"무인상점 열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendOpenUpdagrageJewel(); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenGlyphLift(CDNUserBase* pUser) { if ( !pUser ) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"문장창 열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendOpenGlyphLift(); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenMailBox(CDNUserBase* pUser) { if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"우편함 열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendOpenMailBox(); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenDisjointItem(CDNUserBase* pUser) { if (!pUser) return -1; #if defined (_VILLAGESERVER) #ifndef _FINAL_BUILD wstring wszString = FormatW(L"스테이지에서만 사용 가능한 기능입니다.\r\n"); pUser->SendChat( CHATTYPE_NORMAL, (int)wszString.size()*sizeof(WCHAR), L"", (WCHAR*)wszString.c_str() ); #endif // _FINAL_BUILD #endif #if defined (_GAMESERVER) #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"분해창 열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif #endif pUser->SendOpenDisjointItem(); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenCompoundItem(CDNUserBase* pUser, int nCompoundShopID) { if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"아이템 합성창 열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendOpenCompoundItem(nCompoundShopID); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenCashShop(CDNUserBase* pUser) { if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"캐쉬 아이템 상점 열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendOpenCashShop(); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenGuildMgrBox(CDNUserBase* pUser, int nGuildMgrNo) { if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"길드 관리 대화상자 열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendOpenGuildMgrBox(nGuildMgrNo); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_ui_OpenGacha_JP(CDNUserBase* pUser, int nGachaShopID) { #ifdef PRE_ADD_GACHA_JAPAN if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"일본 가챠폰 열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif TGachaponData_JP* pGachaData = g_pDataManager->GetGachaponData_JP( nGachaShopID ); if( !pGachaData ) return -2; pUser->m_nGachaponShopID = nGachaShopID; pUser->SendGachaShopOpen_JP( nGachaShopID ); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 pUser->SetCalledNpcResponse( true, false ); return 1; #endif return -3; } int api_ui_OpenGiveNpcPresent(CDNUserBase* pUser, int nNpcID) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"NPC 선물하기 UI 열기"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif // 클라이언트한테 npc 에게 선물하기 UI 띄우라고 패킷 보냄. pUser->SendOpenGiveNpcPresent( nNpcID ); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 // 그렇지만 npc 선물하기는 계속 대화가 이어지므로 그냥 놔둔다. 이거 호출하면 대화 다이얼로그가 바로 닫힌다. //pUser->SetCalledNpcResponse( true, false ); return 1; #else return -1; #endif } int api_ui_OpenDonation(CDNUserBase* pUser) { if (!pUser) return -1; #if defined (PRE_ADD_DONATION) pUser->SendOpenDonation(); pUser->SetCalledNpcResponse( true, false ); #endif // #if defined (PRE_ADD_DONATION) return 1; } int api_ui_OpenInventory(CDNUserBase* pUser) { if (!pUser) return -1; pUser->SendOpenInventory(); pUser->SetCalledNpcResponse( true, false ); return 1; } int api_user_UserMessage(CDNUserBase* pUser, int nType, int nBaseStringIdx, lua_tinker::table ParamTable) { if (!pUser) return -1; std::vector ReplaceParamList; // 베이스 스트링 얻고 #if defined(PRE_ADD_MULTILANGUAGE) std::wstring wszBaseString = GetEtUIXML().GetUIString( CEtUIXML::idCategory1, nBaseStringIdx, pUser->m_eSelectedLanguage ); #else //#if defined(PRE_ADD_MULTILANGUAGE) std::wstring wszBaseString = GetEtUIXML().GetUIString( CEtUIXML::idCategory1, nBaseStringIdx ); #endif //#if defined(PRE_ADD_MULTILANGUAGE) // 먼저 파라미터 테이블에 값이 있는지 확인 하고 lua_tinker::table t = ParamTable.get(1); const char* __szKey = t.get(1); // 있으면 if ( __szKey ) { for ( int i = 1 ; i <= ParamTable.getSize() ; i++ ) { lua_tinker::table pTable = ParamTable.get(i); char* szKey = pTable.get(1); int nValueType = pTable.get(2); int nValue = pTable.get(3); DNReplaceString::DynamicReplaceStringInfo param; ToWideString(szKey, param.szKey); param.cValueType = (char)nValueType; param.nValue = nValue; ReplaceParamList.push_back(param); } } #if defined(_SERVER) &&defined(PRE_ADD_MULTILANGUAGE) g_ReplaceString.Relpace(wszBaseString, ReplaceParamList, pUser->m_eSelectedLanguage ); #else //#if defined(_SERVER) &&defined(PRE_ADD_MULTILANGUAGE) g_ReplaceString.Relpace(wszBaseString, ReplaceParamList ); #endif //#if defined(_SERVER) &&defined(PRE_ADD_MULTILANGUAGE) if ( wszBaseString.size() > 256) return -3; switch(nType) { case CHATTYPE_NORMAL: case CHATTYPE_PARTY: case CHATTYPE_PRIVATE: case CHATTYPE_GUILD: case CHATTYPE_CHANNEL: case CHATTYPE_SYSTEM: break; default: return -2; } pUser->SendChat((eChatType)nType, (int)wszBaseString.size()*sizeof(WCHAR), L"", (WCHAR*)wszBaseString.c_str()); return 1; } int api_quest_AddSymbolItem(CDNUserBase* pUser, int nItemID, short wCount) { /* if (!pUser) return -1; bool bResult = false; if (pUser->GetItem()->AddSymbolItem(nItemID, wCount, 0, ITEMLOG_ADDQUESTINVEN) == ERROR_NONE) bResult = true; #ifndef _FINAL_BUILD std::wstring wszMsg; wszMsg = FormatW( L"심볼아이템추가 %s ID:%d %d개" , bResult ? L"성공":L"실패", nItemID, (int)wCount); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); #endif */ return 1; } int api_quest_DelSymbolItem(CDNUserBase* pUser, int nItemID, short wCount) { /* if (!pUser) return -1; bool bResult = false; if (pUser->GetItem()->DelSymbolItem(nItemID, wCount, 0, ITEMLOG_DELETEQUESTINVEN) == ERROR_NONE) bResult = true; #ifndef _FINAL_BUILD std::wstring wszMsg; wszMsg = FormatW( L"심볼아이템제거 %s ID:%d %d개" , bResult ? L"성공":L"실패", nItemID, (int)wCount); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); #endif */ return 1; } int api_quest_HasSymbolItem(CDNUserBase* pUser, int nItemIndex, int nItemCnt) { /* if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; int nCount = pUserItem->ExistSymbolItemCount(nItemIndex); #ifndef _FINAL_BUILD std::wstring wszMsg; wszMsg = FormatW( L"심볼아이템 조회 UserObjID: %d nItemID:%d %d개 %d개 있음" , nUserObjectID, nItemIndex, (int)nItemCnt, nCount); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); #endif if (nCount < nItemCnt) { return -3; } return nCount; */ return 0; } int api_quest_CheckQuestInvenForAddItem(CDNUserBase* pUser, int nItemIndex, int nItemCnt) { if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if (!pUserItem) { return -2; } #if defined (_VILLAGESERVER) if (!pUserItem->IsValidSpaceQuestInventorySlot(nItemIndex, nItemCnt)) return -3; #endif #if defined (_GAMESERVER) if (!pUserItem->IsValidSpaceQuestInventorySlot(nItemIndex, nItemCnt)) { pUser->SendQuestResult(ERROR_QUEST_INVENTORY_NOTENOUGH); return -3; } // 퀘스트 인벤토리 공간에 대한 별도의 체크와 사전알림 존재 int nResult = pUserItem->FindBlankQuestInventorySlotCount(); if (QUESTINVENBLANKCHECKMIN >= nResult) { pUser->SendQuestResult(ERROR_QUEST_INVENTORY_ALMOSTFULL); } #endif return 1; } int api_quest_CheckQuestInvenForAddItemList(CDNUserBase* pUser, lua_tinker::table ItemTable) { if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if (!pUserItem) { return -2; } TQuestItemCnt TempQuestInven[QUESTINVENTORYMAX]; { // 임시 퀘스트 인벤토리에 현재 퀘스트 인벤토리의 아이템 개수 정보들을 복사 ::memset(TempQuestInven, 0, sizeof(TempQuestInven)); for (int iIndex = 0 ; QUESTINVENTORYMAX > iIndex ; ++iIndex) { const TQuestItem* pQuestItem = pUserItem->GetQuestInventory(iIndex); if (pQuestItem) { TempQuestInven[iIndex].Set(pQuestItem->nItemID, pQuestItem->wCount); } else { TempQuestInven[iIndex].Reset(); } } } if (0 >= ItemTable.getSize()) { // 아이템 테이블에 아무값도 없음 ? DN_RETURN(false); } for (int iIndex = 1 ; ItemTable.getSize() >= iIndex ; ++iIndex) { lua_tinker::table pTable = ItemTable.get(iIndex); if (0 >= pTable.getSize()) { continue; } bool bResult = __IsValidSpaceQuestInventorySlotAndSet(TempQuestInven, pTable.get(1), pTable.get(2)); if (!bResult) { return -3; } } return 1; } int api_quest_AddQuestItem(CDNUserBase* pUser, int nItemID, short wCount, int nQuestID) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif bool bResult = false; if (pUser->GetItem()->AddQuestInventory(nItemID, wCount, nQuestID, DBDNWorldDef::AddMaterializedItem::QuestReward) == ERROR_NONE) bResult = true; #ifndef _FINAL_BUILD std::wstring wszMsg; wszMsg = FormatW( L"퀘스트 아이템추가 %s ID:%d %d개" , bResult ? L"성공":L"실패", nItemID, (int)wCount); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); #endif return bResult ? 1 : 0; } int api_quest_DelQuestItem(CDNUserBase* pUser, int nItemID, short wCount, int nQuestID) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif bool bResult = false; if (pUser->GetItem()->DeleteQuestInventory(nItemID, wCount, nQuestID, DBDNWorldDef::UseItem::Destroy) == ERROR_NONE) bResult = true; #ifndef _FINAL_BUILD std::wstring wszMsg; wszMsg = FormatW( L"퀘스트 아이템제거 %s ID:%d %d개" , bResult ? L"성공":L"실패", nItemID, (int)wCount); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); #endif return bResult ? 1 : 0; } int api_quest_AllDelQuestItem(CDNUserBase* pUser, int nItemID) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif bool bResult = false; CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; int nCount = pUserItem->GetQuestInventoryItemCount(nItemID); if (pUser->GetItem()->DeleteQuestInventory(nItemID, nCount, 0, DBDNWorldDef::UseItem::Destroy) == ERROR_NONE) bResult = true; #ifndef _FINAL_BUILD std::wstring wszMsg; wszMsg = FormatW( L"퀘스트 아이템제거 %s ID:%d %d개" , bResult ? L"성공":L"실패", nItemID, (int)nCount); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); #endif return bResult ? 1 : 0; } int api_quest_HasQuestItem(CDNUserBase* pUser, int nItemIndex, int nItemCnt) { if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; int nCount = pUserItem->GetQuestInventoryItemCount(nItemIndex); #ifndef _FINAL_BUILD //만약 빌리지, 게임 둘다 아니면 컴파일 에러. #if defined(_VILLAGESERVER) UINT nUserID = static_cast(pUser)->GetObjectID(); #endif #if defined(_GAMESERVER) UINT nUserID = static_cast(pUser)->GetSessionID(); #endif std::wstring wszMsg; wszMsg = FormatW( L"퀘스트 아이템 조회 UserObjID: %d nItemID:%d %d개 %d개 있음" , nUserID, nItemIndex, (int)nItemCnt, nCount); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); #endif if (nCount < nItemCnt) { return -3; } return nCount; } int api_ui_OpenGuildCreate(CDNUserBase* pUser) { if (!pUser) return -1; // N/A return 1; } int api_ui_OpenQuestReward(CDNUserBase* pUser, int nRewardTableIndex, bool bActivate) { if (!pUser) return -1; pUser->SendOpenQuestReward(nRewardTableIndex, bActivate); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; } int api_quest_RewardQuestUser(CDNUserBase* pUser, int nRewardTableIndext, int nQuestID, int nRewardCheck) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif CDNUserItem* pUserItem = pUser->GetItem(); if (!pUserItem) { return -2; } // 퀘스트 보상 테이블을 참조해서 TQuestReward table; bool bResult = g_pDataManager->GetQuestReward(nRewardTableIndext, table); if (!bResult) { return -4; } DN_ASSERT(0 < table.nQuestID, "Invalid!"); DN_ASSERT(0 < table.nQuestStep, "Invalid!"); if (table.nQuestStep != pUser->GetQuest()->GetQuestStep(table.nQuestID) || (table.nClass != 0 && table.nClass != pUser->GetClassID())) { return -5; } ZeroMemory(table.LevelCapItemArray, sizeof(table.LevelCapItemArray)); if (pUser->GetLevel() >= (int)CGlobalWeightTable::GetInstance().GetValue(CGlobalWeightTable::PlayerLevelLimit)) { CDNQuest* pQuest = g_pQuestManager->GetQuest(table.nQuestID); if(!pQuest) return -6; if (pQuest->IsLevelCapReward()) { const TQuestLevelCapReward* pLevelCapReward = g_pDataManager->GetQuestLevelCapReward(pQuest->GetQuestInfo().cQuestType, pUser->GetClassID()); if (!pLevelCapReward) { #if !defined (_FINAL_BUILD) wstring wszMsg; wszMsg = FormatW( L"퀘스트 레벨캡 보상 테이블 조회 실패 퀘스트ID:%d 타입:%d 클래스ID:%d ", table.nQuestID, pQuest->GetQuestInfo().cQuestType, pUser->GetClassID()); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); #endif // #if !defined (_FINAL_BUILD) return -7; } CopyMemory(table.LevelCapItemArray, pLevelCapReward->Items, sizeof(pLevelCapReward->Items)); } } if (pUser->GetQuest()) { pUser->GetQuest()->SetReward(table, nQuestID, nRewardCheck); #ifndef _FINAL_BUILD //만약 빌리지, 게임 둘다 아니면 컴파일 에러. #if defined (_VILLAGESERVER) UINT nUserObjectID = static_cast(pUser)->GetObjectID(); #endif #if defined (_GAMESERVER) UINT nUserObjectID = static_cast(pUser)->GetSessionID(); #endif std::wstring wszMsg; wszMsg = FormatW( L"보상 세팅UserObjID: %d 테이블ID:%d 퀘스트IDL%d " , nUserObjectID, nRewardTableIndext, nQuestID); pUser->SendChat(CHATTYPE_NORMAL, (int)wszMsg.size()*sizeof(WCHAR), L"", (WCHAR*)wszMsg.c_str()); #endif } return 1; } int api_user_GetMapIndex(CDNUserBase* pUser) { if (!pUser) return -1; return pUser->GetMapIndex(); } int api_user_GetLastStageClearRank(CDNUserBase* pUser) { if (!pUser) return -1; return pUser->GetLastStageClearRank(); } int api_user_EnoughCoin(CDNUserBase* pUser, int nCoin) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif if( pUser->GetCoin() < (INT64)nCoin ) return -2; return 1; } int api_user_GetCoin(CDNUserBase* pUser) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif return static_cast(pUser->GetCoin()); } int api_user_IsMissionGained(CDNUserBase* pUser, int nMissionIndex) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif // 해당 미션이 없거나 비활성화 상태이면 실패 TMissionData *pData = g_pDataManager->GetMissionData(nMissionIndex); if (!pData || !pData->bActivate) return 0; return(GetBitFlag(pUser->GetMissionData()->MissionGain, nMissionIndex)); } int api_user_IsMissionAchieved(CDNUserBase* pUser, int nMissionIndex) { DN_ASSERT(CHECK_LIMIT(nMissionIndex, MISSIONMAX), "Invalid!"); if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif // 해당 미션이 없거나 비활성화 상태이면 실패 TMissionData *pData = g_pDataManager->GetMissionData(nMissionIndex); if (!pData || !pData->bActivate) { return 0; } return(GetBitFlag(pUser->GetMissionData()->MissionAchieve, nMissionIndex)); } int api_user_HasItemWarehouse(CDNUserBase* pUser, int nItemIndex, int nItemCnt) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif const CDNUserItem* pUserItem = pUser->GetItem(); if (!pUserItem) { return -2; } int nCount = pUserItem->GetWarehouseItemCount(nItemIndex); if (nCount < nItemCnt) { return -3; } return nCount; } int api_user_HasItemEquip(CDNUserBase* pUser, int nItemIndex) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif const CDNUserItem* pUserItem = pUser->GetItem(); if (!pUserItem) { return -2; } bool bResult = pUserItem->CheckEquipByItemID(nItemIndex); if (!bResult) { return -2; } return 1; } int api_guild_GetGuildMemberRole(CDNUserBase* pUser) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif if (pUser->GetGuildSelfView().IsSet()) { return(pUser->GetGuildSelfView().btGuildRole + 1); } return 0; } int api_quest_IncQuestMemo(CDNUserBase* pUser, int nQuestID, char nMemoIndex) { if (!pUser) return LONG_MIN; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif if (!CHECK_RANGE(nMemoIndex, 1, QUESTMEMOMAX)) { // 퀘스트 메모 인덱스 범위를 벗어남 (1~10) DN_RETURN(LONG_MIN); } if (QUESTMEMOREWARDCHECK == nMemoIndex) { // 퀘스트 보상 무한지급을 방지하기 위하여 할당받은 인덱스는 사용할 수 없음 DN_RETURN(LONG_MIN); } return pUser->GetQuest()->IncQuestMemo(nQuestID, nMemoIndex-1); } int api_quest_DecQuestMemo(CDNUserBase* pUser, int nQuestID, char nMemoIndex) { if (!pUser) return LONG_MAX; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif if (!CHECK_RANGE(nMemoIndex, 1, QUESTMEMOMAX)) { // 퀘스트 메모 인덱스 범위를 벗어남 (1~10) DN_RETURN(LONG_MAX); } if (QUESTMEMOREWARDCHECK == nMemoIndex) { // 퀘스트 보상 무한지급을 방지하기 위하여 할당받은 인덱스는 사용할 수 없음 DN_RETURN(LONG_MAX); } return pUser->GetQuest()->DecQuestMemo(nQuestID, nMemoIndex-1); } int api_user_SetUserJobID(CDNUserBase* pUser, int nJobID) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif if (nJobID == pUser->GetUserJob()) { return 0; } DNTableFileFormat* pJobTable = GetDNTable( CDnTableDB::TJOB ); // 현재 직업의 단계값과 루트 직업을 얻어옴. int iNowJob = pUser->GetUserJob(); int iNowJobDeep = 0; int iNowRootJob = 0; for( int i = 0; i < pJobTable->GetItemCount(); ++i ) { int iItemID = pJobTable->GetItemID( i ); if( iItemID == iNowJob ) { iNowJobDeep = pJobTable->GetFieldFromLablePtr( iItemID, "_JobNumber" )->GetInteger(); iNowRootJob = pJobTable->GetFieldFromLablePtr( iItemID, "_BaseClass" )->GetInteger(); break; } } // 바꾸기 원하는 직업과 단계가 같거나 큰지 확인. bool bSuccess = false; map mapRootJob; for( int i = 0; i < pJobTable->GetItemCount(); ++i ) { int iItemID = pJobTable->GetItemID( i ); if( iItemID == nJobID ) { int iJobRootToChange = pJobTable->GetFieldFromLablePtr( iItemID, "_BaseClass" )->GetInteger(); if( iNowRootJob == iJobRootToChange ) { int iJobDeepToChange = pJobTable->GetFieldFromLablePtr( iItemID, "_JobNumber" )->GetInteger(); if( iNowJobDeep < iJobDeepToChange ) { // 부모 직업도 맞아야 함. int iParentJobID = pJobTable->GetFieldFromLablePtr( iItemID, "_ParentJob" )->GetInteger(); if( iParentJobID == iNowJob ) { pUser->SetUserJob( nJobID ); bSuccess = true; break; } else { // 바꾸고자 하는 직업의 부모 직업이 현재 직업이 아님. #ifndef _FINAL_BUILD wstring wszString = FormatW(L"현재 직업에선 전직 할 수 없는 직업입니다.!!\r\n"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszString.size()*sizeof(WCHAR), L"", (WCHAR*)wszString.c_str()); #endif // _FINAL_BUILD return -3; } } else { // 바꾸고자하는 직업이 아래 단계임. 못바꿈. #ifndef _FINAL_BUILD wstring wszString = FormatW(L"같거나 낮은 단계의 직업으로 바꿀 수 없습니다!!\r\n"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszString.size()*sizeof(WCHAR), L"", (WCHAR*)wszString.c_str()); #endif // _FINAL_BUILD return -3; } } else { // 바꾸고자하는 직업이 다른 클래스임. 못바꿈. #ifndef _FINAL_BUILD wstring wszString = FormatW(L"다른 클래스의 직업으로 바꿀 수 없습니다!!\r\n"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszString.size()*sizeof(WCHAR), L"", (WCHAR*)wszString.c_str()); #endif // _FINAL_BUILD return -3; } } } if( false == bSuccess ) { #ifndef _FINAL_BUILD wstring wszString = FormatW(L"잘못된 Job ID 입니다..\r\n"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszString.size()*sizeof(WCHAR), L"", (WCHAR*)wszString.c_str()); #endif // _FINAL_BUILD return -2; } return 1; } int api_user_IsJobInSameLine(CDNUserBase* pUser, int nBaseJobID) { if (!pUser) return -1; DNTableFileFormat* pSox = GetDNTable(CDnTableDB::TJOB); if (!pSox) { g_Log.Log( LogType::_FILELOG, L"JobTable.ext failed\r\n"); return -2; } int nJobID = pUser->GetUserJob(); if (!pSox->IsExistItem(nJobID)) { return 0; } do { if (nBaseJobID == nJobID) { return 1; } DNTableCell *pSoxField = pSox->GetFieldFromLablePtr(nJobID, "_ParentJob"); if (!pSoxField) { break; } nJobID = pSoxField->GetInteger(); if (0 >= nJobID) { break; } } while(1); return 0; } int api_user_HasCashItem(CDNUserBase* pUser, int nItemIndex, int nItemCnt) { if (!pUser) return -1; CDNUserItem* pUserItem = pUser->GetItem(); if ( !pUserItem ) return -2; int nCount = pUserItem->GetCashItemCountByItemID(nItemIndex); if (nCount < nItemCnt) { return -3; } return nCount; } int api_user_HasCashItemEquip(CDNUserBase* pUser, int nItemIndex) { if (!pUser) return -1; #if defined(_CH) if (pUser->GetFCMState() != FCMSTATE_NONE){ pUser->SendQuestResult(ERROR_QUEST_FCM); return -1; } #endif const CDNUserItem* pUserItem = pUser->GetItem(); if (!pUserItem) { return -2; } bool bResult = pUserItem->IsEquipCashItemExist(nItemIndex); if (!bResult) { return -2; } return 1; } int api_quest_IncCounting(CDNUserBase* pUser, int nCountingType, int nCountingIndex) { if (!pUser) return -1; pUser->GetQuest()->OnCounting(nCountingType, nCountingIndex); return 1; } int api_quest_IsPlayingQuestMaximum(CDNUserBase* pUser) { if (!pUser) return -1; if (MAX_PLAY_QUEST <= pUser->GetQuest()->GetPlayingQuestCount()) { return 1; } return 0; } int api_quest_ForceCompleteQuest(CDNUserBase* pUser, int nQuestID, int nQuestCode, int bDoMark, int bDoDelete, int bDoRepeat) { if (!pUser) return -1; CDNUserQuest* pQuest = pUser->GetQuest(); if (!pQuest) { return -2; } int aRetVal = pQuest->ForceCompleteQuest(nQuestID, nQuestCode, bDoMark, bDoDelete, bDoRepeat); if (1 != aRetVal) { // 오류 발생 } return aRetVal; } int api_npc_GetFavorPoint(CDNUserBase* pUser, int iNpcID) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; CReputationSystemRepository* pReputationSystem = pUser->GetReputationSystem(); if( !pReputationSystem ) return -2; return static_cast(pReputationSystem->GetNpcReputation( iNpcID, IReputationSystem::NpcFavor )); #else return -1; #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) } int api_npc_GetMalicePoint(CDNUserBase* pUser, int iNpcID) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; CReputationSystemRepository* pReputationSystem = pUser->GetReputationSystem(); if( !pReputationSystem ) return -2; return static_cast(pReputationSystem->GetNpcReputation( iNpcID, IReputationSystem::NpcMalice )); #else return -1; #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) } int api_npc_GetFavorPercent(CDNUserBase* pUser, int iNpcID) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; CReputationSystemRepository* pReputationSystem = pUser->GetReputationSystem(); if( !pReputationSystem ) return -2; return pReputationSystem->GetNpcReputationPercent( iNpcID, IReputationSystem::NpcFavor ); #else return -1; #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) } int api_npc_GetMalicePercent(CDNUserBase* pUser, int iNpcID ) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; CReputationSystemRepository* pReputationSystem = pUser->GetReputationSystem(); if( !pReputationSystem ) return -2; return pReputationSystem->GetNpcReputationPercent( iNpcID, IReputationSystem::NpcMalice ); #else return -1; #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) } int api_npc_AddFavorPoint(CDNUserBase* pUser, int iNpcID, int val) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; CReputationSystemRepository* pReputationSystem = pUser->GetReputationSystem(); if( !pReputationSystem ) return -2; CNpcReputationProcessor::Process( static_cast(pUser), iNpcID, IReputationSystem::NpcFavor, val ); return 1; #else return -1; #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) } int api_npc_AddMalicePoint(CDNUserBase* pUser, int iNpcID, int val) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; CReputationSystemRepository* pReputationSystem = pUser->GetReputationSystem(); if( !pReputationSystem ) return -2; CNpcReputationProcessor::Process( static_cast(pUser), iNpcID, IReputationSystem::NpcMalice, val ); return 1; #else return -1; #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) } int api_npc_SendSelectedPresent(CDNUserBase* pUser, int iNpcID ) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; // 클라이언트에게 선택된 선물의 정보를 보내라고 패킷 보낸다. pUser->SendRequestSendSelectedPresent( iNpcID ); return 1; #else return -1; #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) } int api_npc_Rage(CDNUserBase* pUser, int iNpcID) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; pUser->SendShowNpcEffect( iNpcID, 0 ); return 1; #else return -1; #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) } int api_npc_Disappoint(CDNUserBase* pUser, int iNpcID) { #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) if (!pUser) return -1; pUser->SendShowNpcEffect( iNpcID, 1 ); return 1; #else return -1; #endif // #if defined( PRE_ADD_NPC_REPUTATION_SYSTEM ) } int api_user_RepairItem(CDNUserBase* pUser, int iAllRepair) { #if defined(PRE_ADD_REPAIR_NPC) if (!pUser) return -1; int nRet = ERROR_NONE; if( iAllRepair ) { nRet = pUser->GetItem()->CheckRepairAll(false); if( nRet == ERROR_NONE ) pUser->SendRepairAll(ERROR_NONE); } else { nRet = pUser->GetItem()->CheckRepairEquip(0); if( nRet == ERROR_NONE ) pUser->SendRepairEquip(ERROR_NONE); } if( nRet == ERROR_NONE) return 2; //아이템 수리 완료 else if( nRet == ERROR_ITEM_REPAIR) return 0; // 수리할 아이템 없음 else if( nRet == ERROR_ITEM_INSUFFICIENCY_MONEY) return 1; // 소지금 부족 return -1; #else return -1; #endif } int api_ui_OpenExchangeEnchant(CDNUserBase* pUser) { #ifdef PRE_ADD_EXCHANGE_ENCHANT if (!pUser) return -1; #ifndef _FINAL_BUILD std::wstring wszLog; wszLog = FormatW( L"강화이동 오픈"); pUser->SendChat(CHATTYPE_NORMAL, (int)wszLog.size()*sizeof(WCHAR), L"", (WCHAR*)wszLog.c_str()); #endif pUser->SendOpenExchangeEnchant(); pUser->SetCalledNpcResponse(true, false); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 return 1; #else return -1; #endif } int api_env_CheckCloseGateByTime(int iMapID) { #ifdef PRE_ADD_CHALLENGE_DARKLAIR return g_pDataManager->IsCloseGateByTime(iMapID); #else return 0; #endif } #if defined(PRE_ADD_OPEN_QUEST_TEXTURE_DIALOG) int api_Open_Texture_Dialog(CDNUserBase* pUser, SCOpenTextureDialog data) { if(!pUser) return -1; pUser->SetCalledNpcResponse(true, true); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 pUser->SendOpenTextureDialog(data); return 1; } int api_Close_Texture_Dialog(CDNUserBase* pUser, int nDlgID) { if(!pUser) return -1; pUser->SetCalledNpcResponse(true, true); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 pUser->SendCloseTextureDialog(nDlgID); return 1; } #endif int api_quest_WorldBuffCheck(int nItemID) { #if defined( PRE_DRAGONBUFF ) if( g_pDataManager ) { if( g_pDataManager->bIsWorldBuffData(nItemID) ) return 1; } #endif return -1; } int api_quest_ApplyWorldBuff(CDNUserBase* pUser, int nItemID, int nMapIdx) { if(!pUser) return -1; #if defined(_VILLAGESERVER) && defined( PRE_DRAGONBUFF ) if( g_pUserSessionManager ) { if( g_pDataManager ) { g_pDataManager->AddWorldBuffData(nItemID); } g_pUserSessionManager->ApplyWorldBuff( pUser->GetCharacterName(), nItemID, nMapIdx); } if( g_pMasterConnection && g_pMasterConnection->GetActive() ) { g_pMasterConnection->SendApplyWorldBuff( pUser->GetCharacterName(), nItemID, nMapIdx ); } #endif return -1; } int api_Open_ChangeJobDialog(CDNUserBase* pUser) { if (!pUser) return -1; pUser->SetCalledNpcResponse(true, true); // 보통 클라이언트에 보내는 UI 개설 요청 이후 대화로 이어지지 않기 때문에 에러가 아닌데도 본의아니게 NPC 응답여부 체크에 걸리게 되므로 대화한 것으로 처리해 줌 pUser->SendOpenChangeJobDialog(); return 1; } }