forked from metin2/server
1833 lines
45 KiB
C++
1833 lines
45 KiB
C++
#include "stdafx.h"
|
|
#include <fstream>
|
|
#include "constants.h"
|
|
#include "buffer_manager.h"
|
|
#include "packet.h"
|
|
#include "desc_client.h"
|
|
#include "desc_manager.h"
|
|
#include "char.h"
|
|
#include "char_manager.h"
|
|
#include "questmanager.h"
|
|
#include "lzo_manager.h"
|
|
#include "item.h"
|
|
#include "config.h"
|
|
#include "xmas_event.h"
|
|
#include "target.h"
|
|
#include "party.h"
|
|
#include "locale_service.h"
|
|
|
|
#include "dungeon.h"
|
|
|
|
DWORD g_GoldDropTimeLimitValue = 0;
|
|
extern bool DropEvent_CharStone_SetValue(const std::string& name, int value);
|
|
extern bool DropEvent_RefineBox_SetValue (const std::string& name, int value);
|
|
|
|
namespace quest
|
|
{
|
|
using namespace std;
|
|
|
|
CQuestManager::CQuestManager()
|
|
: m_pSelectedDungeon(NULL), m_dwServerTimerArg(0), m_iRunningEventIndex(0), L(NULL), m_bNoSend (false),
|
|
m_CurrentRunningState(NULL), m_pCurrentCharacter(NULL), m_pCurrentNPCCharacter(NULL), m_pCurrentPartyMember(NULL),
|
|
m_pCurrentPC(NULL), m_iCurrentSkin(0), m_bError(false), m_pOtherPCBlockRootPC(NULL)
|
|
{
|
|
}
|
|
|
|
CQuestManager::~CQuestManager()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
void CQuestManager::Destroy()
|
|
{
|
|
if (L)
|
|
{
|
|
lua_close(L);
|
|
L = NULL;
|
|
}
|
|
}
|
|
|
|
bool CQuestManager::Initialize()
|
|
{
|
|
if (g_bAuthServer)
|
|
return true;
|
|
|
|
if (!InitializeLua())
|
|
return false;
|
|
|
|
m_pSelectedDungeon = NULL;
|
|
|
|
m_mapEventName.insert(TEventNameMap::value_type("click", QUEST_CLICK_EVENT)); // NPC를 클릭
|
|
m_mapEventName.insert(TEventNameMap::value_type("kill", QUEST_KILL_EVENT)); // Mob을 사냥
|
|
m_mapEventName.insert(TEventNameMap::value_type("timer", QUEST_TIMER_EVENT)); // 미리 지정해둔 시간이 지남
|
|
m_mapEventName.insert(TEventNameMap::value_type("levelup", QUEST_LEVELUP_EVENT)); // 레벨업을 함
|
|
m_mapEventName.insert(TEventNameMap::value_type("login", QUEST_LOGIN_EVENT)); // 로그인 시
|
|
m_mapEventName.insert(TEventNameMap::value_type("logout", QUEST_LOGOUT_EVENT)); // 로그아웃 시
|
|
m_mapEventName.insert(TEventNameMap::value_type("button", QUEST_BUTTON_EVENT)); // 퀘스트 버튼을 누름
|
|
m_mapEventName.insert(TEventNameMap::value_type("info", QUEST_INFO_EVENT)); // 퀘스트 정보창을 염
|
|
m_mapEventName.insert(TEventNameMap::value_type("chat", QUEST_CHAT_EVENT)); // 특정 키워드로 대화를 함
|
|
m_mapEventName.insert(TEventNameMap::value_type("in", QUEST_ATTR_IN_EVENT)); // 맵의 특정 속성에 들어감
|
|
m_mapEventName.insert(TEventNameMap::value_type("out", QUEST_ATTR_OUT_EVENT)); // 맵의 특정 속성에서 나옴
|
|
m_mapEventName.insert(TEventNameMap::value_type("use", QUEST_ITEM_USE_EVENT)); // 퀘스트 아이템을 사용
|
|
m_mapEventName.insert(TEventNameMap::value_type("server_timer", QUEST_SERVER_TIMER_EVENT)); // 서버 타이머 (아직 테스트 안됐음)
|
|
m_mapEventName.insert(TEventNameMap::value_type("enter", QUEST_ENTER_STATE_EVENT)); // 현재 스테이트가 됨
|
|
m_mapEventName.insert(TEventNameMap::value_type("leave", QUEST_LEAVE_STATE_EVENT)); // 현재 스테이트에서 다른 스테이트로 바뀜
|
|
m_mapEventName.insert(TEventNameMap::value_type("letter", QUEST_LETTER_EVENT)); // 로긴 하거나 스테이트가 바껴 새로 정보를 세팅해줘야함
|
|
m_mapEventName.insert(TEventNameMap::value_type("take", QUEST_ITEM_TAKE_EVENT)); // 아이템을 받음
|
|
m_mapEventName.insert(TEventNameMap::value_type("target", QUEST_TARGET_EVENT)); // 타겟
|
|
m_mapEventName.insert(TEventNameMap::value_type("party_kill", QUEST_PARTY_KILL_EVENT)); // 파티 멤버가 몬스터를 사냥 (리더에게 옴)
|
|
m_mapEventName.insert(TEventNameMap::value_type("unmount", QUEST_UNMOUNT_EVENT));
|
|
m_mapEventName.insert(TEventNameMap::value_type("pick", QUEST_ITEM_PICK_EVENT)); // 떨어져있는 아이템을 습득함.
|
|
m_mapEventName.insert(TEventNameMap::value_type("sig_use", QUEST_SIG_USE_EVENT)); // Special item group에 속한 아이템을 사용함.
|
|
m_mapEventName.insert(TEventNameMap::value_type("item_informer", QUEST_ITEM_INFORMER_EVENT)); // 독일선물기능테스트
|
|
|
|
m_bNoSend = false;
|
|
|
|
m_iCurrentSkin = QUEST_SKIN_NORMAL;
|
|
|
|
{
|
|
ifstream inf((g_stQuestDir + "/questnpc.txt").c_str());
|
|
int line = 0;
|
|
|
|
if (!inf.is_open())
|
|
sys_err( "QUEST Cannot open 'questnpc.txt'");
|
|
else
|
|
sys_log(0, "QUEST can open 'questnpc.txt' (%s)", g_stQuestDir.c_str() );
|
|
|
|
while (1)
|
|
{
|
|
unsigned int vnum;
|
|
|
|
inf >> vnum;
|
|
|
|
line++;
|
|
|
|
if (inf.fail())
|
|
break;
|
|
|
|
string s;
|
|
getline(inf, s);
|
|
unsigned int li = 0, ri = s.size()-1;
|
|
while (li < s.size() && isspace(s[li])) li++;
|
|
while (ri > 0 && isspace(s[ri])) ri--;
|
|
|
|
if (ri < li)
|
|
{
|
|
sys_err("QUEST questnpc.txt:%d:npc name error",line);
|
|
continue;
|
|
}
|
|
|
|
s = s.substr(li, ri-li+1);
|
|
|
|
int n = 0;
|
|
str_to_number(n, s.c_str());
|
|
if (n)
|
|
continue;
|
|
|
|
//cout << '-' << s << '-' << endl;
|
|
if ( test_server )
|
|
sys_log(0, "QUEST reading script of %s(%d)", s.c_str(), vnum);
|
|
m_mapNPC[vnum].Set(vnum, s);
|
|
m_mapNPCNameID[s] = vnum;
|
|
}
|
|
|
|
// notarget quest
|
|
m_mapNPC[0].Set(0, "notarget");
|
|
}
|
|
|
|
if (g_iUseLocale)
|
|
{
|
|
SetEventFlag("guild_withdraw_delay", 1);
|
|
SetEventFlag("guild_disband_delay", 1);
|
|
}
|
|
else
|
|
{
|
|
SetEventFlag("guild_withdraw_delay", 3);
|
|
SetEventFlag("guild_disband_delay", 7);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
unsigned int CQuestManager::FindNPCIDByName(const string& name)
|
|
{
|
|
map<string, unsigned int>::iterator it = m_mapNPCNameID.find(name);
|
|
return it != m_mapNPCNameID.end() ? it->second : 0;
|
|
}
|
|
|
|
void CQuestManager::SelectItem(unsigned int pc, unsigned int selection)
|
|
{
|
|
PC* pPC = GetPC(pc);
|
|
if (pPC && pPC->IsRunning() && pPC->GetRunningQuestState()->suspend_state == SUSPEND_STATE_SELECT_ITEM)
|
|
{
|
|
pPC->SetSendDoneFlag();
|
|
pPC->GetRunningQuestState()->args=1;
|
|
lua_pushnumber(pPC->GetRunningQuestState()->co,selection);
|
|
|
|
if (!RunState(*pPC->GetRunningQuestState()))
|
|
{
|
|
CloseState(*pPC->GetRunningQuestState());
|
|
pPC->EndRunning();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CQuestManager::Confirm(unsigned int pc, EQuestConfirmType confirm, unsigned int pc2)
|
|
{
|
|
PC* pPC = GetPC(pc);
|
|
|
|
if (!pPC->IsRunning())
|
|
{
|
|
sys_err("no quest running for pc, cannot process input : %u", pc);
|
|
return;
|
|
}
|
|
|
|
if (pPC->GetRunningQuestState()->suspend_state != SUSPEND_STATE_CONFIRM)
|
|
{
|
|
sys_err("not wait for a confirm : %u %d", pc, pPC->GetRunningQuestState()->suspend_state);
|
|
return;
|
|
}
|
|
|
|
if (pc2 && !pPC->IsConfirmWait(pc2))
|
|
{
|
|
sys_err("not wait for a confirm : %u %d", pc, pPC->GetRunningQuestState()->suspend_state);
|
|
return;
|
|
}
|
|
|
|
pPC->ClearConfirmWait();
|
|
|
|
pPC->SetSendDoneFlag();
|
|
|
|
pPC->GetRunningQuestState()->args=1;
|
|
lua_pushnumber(pPC->GetRunningQuestState()->co, confirm);
|
|
|
|
AddScript("[END_CONFIRM_WAIT]");
|
|
SetSkinStyle(QUEST_SKIN_NOWINDOW);
|
|
SendScript();
|
|
|
|
if (!RunState(*pPC->GetRunningQuestState()))
|
|
{
|
|
CloseState(*pPC->GetRunningQuestState());
|
|
pPC->EndRunning();
|
|
}
|
|
|
|
}
|
|
|
|
void CQuestManager::Input(unsigned int pc, const char* msg)
|
|
{
|
|
PC* pPC = GetPC(pc);
|
|
if (!pPC)
|
|
{
|
|
sys_err("no pc! : %u",pc);
|
|
return;
|
|
}
|
|
|
|
if (!pPC->IsRunning())
|
|
{
|
|
sys_err("no quest running for pc, cannot process input : %u", pc);
|
|
return;
|
|
}
|
|
|
|
if (pPC->GetRunningQuestState()->suspend_state != SUSPEND_STATE_INPUT)
|
|
{
|
|
sys_err("not wait for a input : %u %d", pc, pPC->GetRunningQuestState()->suspend_state);
|
|
return;
|
|
}
|
|
|
|
pPC->SetSendDoneFlag();
|
|
|
|
pPC->GetRunningQuestState()->args=1;
|
|
lua_pushstring(pPC->GetRunningQuestState()->co,msg);
|
|
|
|
if (!RunState(*pPC->GetRunningQuestState()))
|
|
{
|
|
CloseState(*pPC->GetRunningQuestState());
|
|
pPC->EndRunning();
|
|
}
|
|
}
|
|
|
|
void CQuestManager::Select(unsigned int pc, unsigned int selection)
|
|
{
|
|
PC* pPC;
|
|
|
|
if ((pPC = GetPC(pc)) && pPC->IsRunning() && pPC->GetRunningQuestState()->suspend_state==SUSPEND_STATE_SELECT)
|
|
{
|
|
pPC->SetSendDoneFlag();
|
|
|
|
if (!pPC->GetRunningQuestState()->chat_scripts.empty())
|
|
{
|
|
// 채팅 이벤트인 경우
|
|
// 현재 퀘스트는 어느 퀘스트를 실행할 것인가를 고르는 퀘스트 이므로
|
|
// 끝내고 선택된 퀘스트를 실행한다.
|
|
QuestState& old_qs = *pPC->GetRunningQuestState();
|
|
CloseState(old_qs);
|
|
|
|
if (selection >= pPC->GetRunningQuestState()->chat_scripts.size())
|
|
{
|
|
pPC->SetSendDoneFlag();
|
|
GotoEndState(old_qs);
|
|
pPC->EndRunning();
|
|
}
|
|
else
|
|
{
|
|
AArgScript* pas = pPC->GetRunningQuestState()->chat_scripts[selection];
|
|
ExecuteQuestScript(*pPC, pas->quest_index, pas->state_index, pas->script.GetCode(), pas->script.GetSize());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// on default
|
|
pPC->GetRunningQuestState()->args=1;
|
|
lua_pushnumber(pPC->GetRunningQuestState()->co,selection+1);
|
|
|
|
if (!RunState(*pPC->GetRunningQuestState()))
|
|
{
|
|
CloseState(*pPC->GetRunningQuestState());
|
|
pPC->EndRunning();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sys_err("wrong QUEST_SELECT request! : %d",pc);
|
|
}
|
|
}
|
|
|
|
void CQuestManager::Resume(unsigned int pc)
|
|
{
|
|
PC * pPC;
|
|
|
|
if ((pPC = GetPC(pc)) && pPC->IsRunning() && pPC->GetRunningQuestState()->suspend_state == SUSPEND_STATE_PAUSE)
|
|
{
|
|
pPC->SetSendDoneFlag();
|
|
pPC->GetRunningQuestState()->args = 0;
|
|
|
|
if (!RunState(*pPC->GetRunningQuestState()))
|
|
{
|
|
CloseState(*pPC->GetRunningQuestState());
|
|
pPC->EndRunning();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//cerr << pPC << endl;
|
|
//cerr << pPC->IsRunning() << endl;
|
|
//cerr << pPC->GetRunningQuestState()->suspend_state;
|
|
//cerr << SUSPEND_STATE_WAIT << endl;
|
|
//cerr << "wrong QUEST_WAIT request! : " << pc << endl;
|
|
sys_err("wrong QUEST_WAIT request! : %d",pc);
|
|
}
|
|
}
|
|
|
|
void CQuestManager::EnterState(DWORD pc, DWORD quest_index, int state)
|
|
{
|
|
PC* pPC;
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
m_mapNPC[QUEST_NO_NPC].OnEnterState(*pPC, quest_index, state);
|
|
}
|
|
else
|
|
sys_err("QUEST no such pc id : %d", pc);
|
|
}
|
|
|
|
void CQuestManager::LeaveState(DWORD pc, DWORD quest_index, int state)
|
|
{
|
|
PC* pPC;
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
m_mapNPC[QUEST_NO_NPC].OnLeaveState(*pPC, quest_index, state);
|
|
}
|
|
else
|
|
sys_err("QUEST no such pc id : %d", pc);
|
|
}
|
|
|
|
void CQuestManager::Letter(DWORD pc, DWORD quest_index, int state)
|
|
{
|
|
PC* pPC;
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
m_mapNPC[QUEST_NO_NPC].OnLetter(*pPC, quest_index, state);
|
|
}
|
|
else
|
|
sys_err("QUEST no such pc id : %d", pc);
|
|
}
|
|
|
|
void CQuestManager::LogoutPC(LPCHARACTER ch)
|
|
{
|
|
PC * pPC = GetPC(ch->GetPlayerID());
|
|
|
|
if (pPC && pPC->IsRunning())
|
|
{
|
|
CloseState(*pPC->GetRunningQuestState());
|
|
pPC->CancelRunning();
|
|
}
|
|
|
|
// 지우기 전에 로그아웃 한다.
|
|
Logout(ch->GetPlayerID());
|
|
|
|
if (ch == m_pCurrentCharacter)
|
|
{
|
|
m_pCurrentCharacter = NULL;
|
|
m_pCurrentPC = NULL;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Quest Event 관련
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void CQuestManager::Login(unsigned int pc, const char * c_pszQuest)
|
|
{
|
|
PC * pPC;
|
|
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
m_mapNPC[QUEST_NO_NPC].OnLogin(*pPC, c_pszQuest);
|
|
}
|
|
else
|
|
{
|
|
sys_err("QUEST no such pc id : %d", pc);
|
|
}
|
|
}
|
|
|
|
void CQuestManager::Logout(unsigned int pc)
|
|
{
|
|
PC * pPC;
|
|
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
m_mapNPC[QUEST_NO_NPC].OnLogout(*pPC);
|
|
}
|
|
else
|
|
sys_err("QUEST no such pc id : %d", pc);
|
|
}
|
|
|
|
void CQuestManager::Kill(unsigned int pc, unsigned int npc)
|
|
{
|
|
//m_CurrentNPCRace = npc;
|
|
PC * pPC;
|
|
|
|
sys_log(0, "CQuestManager::Kill QUEST_KILL_EVENT (pc=%d, npc=%d)", pc, npc);
|
|
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
/* [hyo] 몹 kill시 중복 카운팅 이슈 관련한 수정사항
|
|
quest script에 when 171.kill begin ... 등의 코드로 인하여 스크립트가 처리되었더라도
|
|
바로 return하지 않고 다른 검사도 수행하도록 변경함. (2011/07/21)
|
|
*/
|
|
// call script
|
|
m_mapNPC[npc].OnKill(*pPC);
|
|
|
|
LPCHARACTER ch = GetCurrentCharacterPtr();
|
|
LPPARTY pParty = ch->GetParty();
|
|
LPCHARACTER leader = pParty ? pParty->GetLeaderCharacter() : ch;
|
|
|
|
if (leader)
|
|
{
|
|
m_pCurrentPartyMember = ch;
|
|
|
|
if (m_mapNPC[npc].OnPartyKill(*GetPC(leader->GetPlayerID())))
|
|
return;
|
|
|
|
pPC = GetPC(pc);
|
|
}
|
|
|
|
if (m_mapNPC[QUEST_NO_NPC].OnKill(*pPC))
|
|
return;
|
|
|
|
if (leader)
|
|
{
|
|
m_pCurrentPartyMember = ch;
|
|
m_mapNPC[QUEST_NO_NPC].OnPartyKill(*GetPC(leader->GetPlayerID()));
|
|
}
|
|
}
|
|
else
|
|
sys_err("QUEST: no such pc id : %d", pc);
|
|
}
|
|
|
|
bool CQuestManager::ServerTimer(unsigned int npc, unsigned int arg)
|
|
{
|
|
SetServerTimerArg(arg);
|
|
sys_log(0, "XXX ServerTimer Call NPC %p", GetPCForce(0));
|
|
m_pCurrentPC = GetPCForce(0);
|
|
m_pCurrentCharacter = NULL;
|
|
m_pSelectedDungeon = NULL;
|
|
return m_mapNPC[npc].OnServerTimer(*m_pCurrentPC);
|
|
}
|
|
|
|
bool CQuestManager::Timer(unsigned int pc, unsigned int npc)
|
|
{
|
|
PC* pPC;
|
|
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
{
|
|
return false;
|
|
}
|
|
// call script
|
|
return m_mapNPC[npc].OnTimer(*pPC);
|
|
}
|
|
else
|
|
{
|
|
//cout << "no such pc id : " << pc;
|
|
sys_err("QUEST TIMER_EVENT no such pc id : %d", pc);
|
|
return false;
|
|
}
|
|
//cerr << "QUEST TIMER" << endl;
|
|
}
|
|
|
|
void CQuestManager::LevelUp(unsigned int pc)
|
|
{
|
|
PC * pPC;
|
|
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
m_mapNPC[QUEST_NO_NPC].OnLevelUp(*pPC);
|
|
}
|
|
else
|
|
{
|
|
sys_err("QUEST LEVELUP_EVENT no such pc id : %d", pc);
|
|
}
|
|
}
|
|
|
|
void CQuestManager::AttrIn(unsigned int pc, LPCHARACTER ch, int attr)
|
|
{
|
|
PC* pPC;
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
m_pCurrentPartyMember = ch;
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
// call script
|
|
m_mapNPC[attr+QUEST_ATTR_NPC_START].OnAttrIn(*pPC);
|
|
}
|
|
else
|
|
{
|
|
//cout << "no such pc id : " << pc;
|
|
sys_err("QUEST no such pc id : %d", pc);
|
|
}
|
|
}
|
|
|
|
void CQuestManager::AttrOut(unsigned int pc, LPCHARACTER ch, int attr)
|
|
{
|
|
PC* pPC;
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
//m_pCurrentCharacter = ch;
|
|
m_pCurrentPartyMember = ch;
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
// call script
|
|
m_mapNPC[attr+QUEST_ATTR_NPC_START].OnAttrOut(*pPC);
|
|
}
|
|
else
|
|
{
|
|
//cout << "no such pc id : " << pc;
|
|
sys_err("QUEST no such pc id : %d", pc);
|
|
}
|
|
}
|
|
|
|
bool CQuestManager::Target(unsigned int pc, DWORD dwQuestIndex, const char * c_pszTargetName, const char * c_pszVerb)
|
|
{
|
|
PC * pPC;
|
|
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return false;
|
|
|
|
bool bRet;
|
|
return m_mapNPC[QUEST_NO_NPC].OnTarget(*pPC, dwQuestIndex, c_pszTargetName, c_pszVerb, bRet);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CQuestManager::QuestInfo(unsigned int pc, unsigned int quest_index)
|
|
{
|
|
PC* pPC;
|
|
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
// call script
|
|
if (!CheckQuestLoaded(pPC))
|
|
{
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pc);
|
|
|
|
if (ch)
|
|
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("퀘스트를 로드하는 중입니다. 잠시만 기다려 주십시오."));
|
|
|
|
return;
|
|
}
|
|
|
|
m_mapNPC[QUEST_NO_NPC].OnInfo(*pPC, quest_index);
|
|
}
|
|
else
|
|
{
|
|
//cout << "no such pc id : " << pc;
|
|
sys_err("QUEST INFO_EVENT no such pc id : %d", pc);
|
|
}
|
|
}
|
|
|
|
void CQuestManager::QuestButton(unsigned int pc, unsigned int quest_index)
|
|
{
|
|
PC* pPC;
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
// call script
|
|
if (!CheckQuestLoaded(pPC))
|
|
{
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pc);
|
|
if (ch)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("퀘스트를 로드하는 중입니다. 잠시만 기다려 주십시오."));
|
|
}
|
|
return;
|
|
}
|
|
m_mapNPC[QUEST_NO_NPC].OnButton(*pPC, quest_index);
|
|
}
|
|
else
|
|
{
|
|
//cout << "no such pc id : " << pc;
|
|
sys_err("QUEST CLICK_EVENT no such pc id : %d", pc);
|
|
}
|
|
}
|
|
|
|
bool CQuestManager::TakeItem(unsigned int pc, unsigned int npc, LPITEM item)
|
|
{
|
|
//m_CurrentNPCRace = npc;
|
|
PC* pPC;
|
|
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
{
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pc);
|
|
if (ch)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("퀘스트를 로드하는 중입니다. 잠시만 기다려 주십시오."));
|
|
}
|
|
return false;
|
|
}
|
|
// call script
|
|
SetCurrentItem(item);
|
|
return m_mapNPC[npc].OnTakeItem(*pPC);
|
|
}
|
|
else
|
|
{
|
|
//cout << "no such pc id : " << pc;
|
|
sys_err("QUEST USE_ITEM_EVENT no such pc id : %d", pc);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CQuestManager::UseItem(unsigned int pc, LPITEM item, bool bReceiveAll)
|
|
{
|
|
if (test_server)
|
|
sys_log( 0, "questmanager::UseItem Start : itemVnum : %d PC : %d", item->GetOriginalVnum(), pc);
|
|
PC* pPC;
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
{
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pc);
|
|
if (ch)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("퀘스트를 로드하는 중입니다. 잠시만 기다려 주십시오."));
|
|
}
|
|
return false;
|
|
}
|
|
// call script
|
|
SetCurrentItem(item);
|
|
/*
|
|
if (test_server)
|
|
{
|
|
sys_log( 0, "Quest UseItem Start : itemVnum : %d PC : %d", item->GetOriginalVnum(), pc);
|
|
itertype(m_mapNPC) it = m_mapNPC.begin();
|
|
itertype(m_mapNPC) end = m_mapNPC.end();
|
|
for( ; it != end ; ++it)
|
|
{
|
|
sys_log( 0, "Quest UseItem : vnum : %d item Vnum : %d", it->first, item->GetOriginalVnum());
|
|
}
|
|
}
|
|
if(test_server)
|
|
sys_log( 0, "questmanager:useItem: mapNPCVnum : %d\n", m_mapNPC[item->GetVnum()].GetVnum());
|
|
*/
|
|
|
|
return m_mapNPC[item->GetVnum()].OnUseItem(*pPC, bReceiveAll);
|
|
}
|
|
else
|
|
{
|
|
//cout << "no such pc id : " << pc;
|
|
sys_err("QUEST USE_ITEM_EVENT no such pc id : %d", pc);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Speical Item Group에 정의된 Group Use
|
|
bool CQuestManager::SIGUse(unsigned int pc, DWORD sig_vnum, LPITEM item, bool bReceiveAll)
|
|
{
|
|
if (test_server)
|
|
sys_log( 0, "questmanager::SIGUse Start : itemVnum : %d PC : %d", item->GetOriginalVnum(), pc);
|
|
PC* pPC;
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
{
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pc);
|
|
if (ch)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("퀘스트를 로드하는 중입니다. 잠시만 기다려 주십시오."));
|
|
}
|
|
return false;
|
|
}
|
|
// call script
|
|
SetCurrentItem(item);
|
|
|
|
return m_mapNPC[sig_vnum].OnSIGUse(*pPC, bReceiveAll);
|
|
}
|
|
else
|
|
{
|
|
//cout << "no such pc id : " << pc;
|
|
sys_err("QUEST USE_ITEM_EVENT no such pc id : %d", pc);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CQuestManager::GiveItemToPC(unsigned int pc, LPCHARACTER pkChr)
|
|
{
|
|
if (!pkChr->IsPC())
|
|
return false;
|
|
|
|
PC * pPC = GetPC(pc);
|
|
|
|
if (pPC)
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return false;
|
|
|
|
TargetInfo * pInfo = CTargetManager::instance().GetTargetInfo(pc, TARGET_TYPE_VID, pkChr->GetVID());
|
|
|
|
if (pInfo)
|
|
{
|
|
bool bRet;
|
|
|
|
if (m_mapNPC[QUEST_NO_NPC].OnTarget(*pPC, pInfo->dwQuestIndex, pInfo->szTargetName, "click", bRet))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CQuestManager::Click(unsigned int pc, LPCHARACTER pkChrTarget)
|
|
{
|
|
PC * pPC = GetPC(pc);
|
|
|
|
if (pPC)
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
{
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pc);
|
|
|
|
if (ch)
|
|
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("퀘스트를 로드하는 중입니다. 잠시만 기다려 주십시오."));
|
|
|
|
return false;
|
|
}
|
|
|
|
TargetInfo * pInfo = CTargetManager::instance().GetTargetInfo(pc, TARGET_TYPE_VID, pkChrTarget->GetVID());
|
|
if (test_server)
|
|
{
|
|
sys_log(0, "CQuestManager::Click(pid=%d, npc_name=%s) - target_info(%x)", pc, pkChrTarget->GetName(), pInfo);
|
|
}
|
|
|
|
if (pInfo)
|
|
{
|
|
bool bRet;
|
|
if (m_mapNPC[QUEST_NO_NPC].OnTarget(*pPC, pInfo->dwQuestIndex, pInfo->szTargetName, "click", bRet))
|
|
return bRet;
|
|
}
|
|
|
|
DWORD dwCurrentNPCRace = pkChrTarget->GetRaceNum();
|
|
|
|
if (pkChrTarget->IsNPC())
|
|
{
|
|
map<unsigned int, NPC>::iterator it = m_mapNPC.find(dwCurrentNPCRace);
|
|
|
|
if (it == m_mapNPC.end())
|
|
{
|
|
sys_err("CQuestManager::Click(pid=%d, target_npc_name=%s) - NOT EXIST NPC RACE VNUM[%d]",
|
|
pc,
|
|
pkChrTarget->GetName(),
|
|
dwCurrentNPCRace);
|
|
return false;
|
|
}
|
|
|
|
// call script
|
|
if (it->second.HasChat())
|
|
{
|
|
// if have chat, give chat
|
|
if (test_server)
|
|
sys_log(0, "CQuestManager::Click->OnChat");
|
|
|
|
if (!it->second.OnChat(*pPC))
|
|
{
|
|
if (test_server)
|
|
sys_log(0, "CQuestManager::Click->OnChat Failed");
|
|
|
|
return it->second.OnClick(*pPC);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// else click
|
|
return it->second.OnClick(*pPC);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
//cout << "no such pc id : " << pc;
|
|
sys_err("QUEST CLICK_EVENT no such pc id : %d", pc);
|
|
return false;
|
|
}
|
|
//cerr << "QUEST CLICk" << endl;
|
|
}
|
|
|
|
void CQuestManager::Unmount(unsigned int pc)
|
|
{
|
|
PC * pPC;
|
|
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
return;
|
|
|
|
m_mapNPC[QUEST_NO_NPC].OnUnmount(*pPC);
|
|
}
|
|
else
|
|
sys_err("QUEST no such pc id : %d", pc);
|
|
}
|
|
//독일 선물 기능 테스트
|
|
void CQuestManager::ItemInformer(unsigned int pc,unsigned int vnum)
|
|
{
|
|
|
|
PC* pPC;
|
|
pPC = GetPC(pc);
|
|
|
|
m_mapNPC[QUEST_NO_NPC].OnItemInformer(*pPC,vnum);
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// END OF 퀘스트 이벤트 처리
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
void CQuestManager::LoadStartQuest(const string& quest_name, unsigned int idx)
|
|
{
|
|
for (itertype(g_setQuestObjectDir) it = g_setQuestObjectDir.begin(); it != g_setQuestObjectDir.end(); ++it)
|
|
{
|
|
const string& stQuestObjectDir = *it;
|
|
string full_name = stQuestObjectDir + "/begin_condition/" + quest_name;
|
|
ifstream inf(full_name.c_str());
|
|
|
|
if (inf.is_open())
|
|
{
|
|
sys_log(0, "QUEST loading begin condition for %s", quest_name.c_str());
|
|
|
|
istreambuf_iterator<char> ib(inf), ie;
|
|
copy(ib, ie, back_inserter(m_hmQuestStartScript[idx]));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CQuestManager::CanStartQuest(unsigned int quest_index, const PC& pc)
|
|
{
|
|
return CanStartQuest(quest_index);
|
|
}
|
|
|
|
bool CQuestManager::CanStartQuest(unsigned int quest_index)
|
|
{
|
|
THashMapQuestStartScript::iterator it;
|
|
|
|
if ((it = m_hmQuestStartScript.find(quest_index)) == m_hmQuestStartScript.end())
|
|
return true;
|
|
else
|
|
{
|
|
int x = lua_gettop(L);
|
|
lua_dobuffer(L, &(it->second[0]), it->second.size(), "StartScript");
|
|
int bStart = lua_toboolean(L, -1);
|
|
lua_settop(L, x);
|
|
return bStart != 0;
|
|
}
|
|
}
|
|
|
|
bool CQuestManager::CanEndQuestAtState(const string& quest_name, const string& state_name)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void CQuestManager::DisconnectPC(LPCHARACTER ch)
|
|
{
|
|
m_mapPC.erase(ch->GetPlayerID());
|
|
}
|
|
|
|
PC * CQuestManager::GetPCForce(unsigned int pc)
|
|
{
|
|
PCMap::iterator it;
|
|
|
|
if ((it = m_mapPC.find(pc)) == m_mapPC.end())
|
|
{
|
|
PC * pPC = &m_mapPC[pc];
|
|
pPC->SetID(pc);
|
|
return pPC;
|
|
}
|
|
|
|
return &it->second;
|
|
}
|
|
|
|
PC * CQuestManager::GetPC(unsigned int pc)
|
|
{
|
|
PCMap::iterator it;
|
|
|
|
LPCHARACTER pkChr = CHARACTER_MANAGER::instance().FindByPID(pc);
|
|
|
|
if (!pkChr)
|
|
return NULL;
|
|
|
|
m_pCurrentPC = GetPCForce(pc);
|
|
m_pCurrentCharacter = pkChr;
|
|
m_pSelectedDungeon = NULL;
|
|
return (m_pCurrentPC);
|
|
}
|
|
|
|
void CQuestManager::ClearScript()
|
|
{
|
|
m_strScript.clear();
|
|
m_iCurrentSkin = QUEST_SKIN_NORMAL;
|
|
}
|
|
|
|
void CQuestManager::AddScript(const string& str)
|
|
{
|
|
m_strScript+=str;
|
|
}
|
|
|
|
void CQuestManager::SendScript()
|
|
{
|
|
if (m_bNoSend)
|
|
{
|
|
m_bNoSend = false;
|
|
ClearScript();
|
|
return;
|
|
}
|
|
|
|
if (m_strScript=="[DONE]" || m_strScript == "[NEXT]")
|
|
{
|
|
if (m_pCurrentPC && !m_pCurrentPC->GetAndResetDoneFlag() && m_strScript=="[DONE]" && m_iCurrentSkin == QUEST_SKIN_NORMAL && !IsError())
|
|
{
|
|
ClearScript();
|
|
return;
|
|
}
|
|
m_iCurrentSkin = QUEST_SKIN_NOWINDOW;
|
|
}
|
|
|
|
//sys_log(0, "Send Quest Script to %s", GetCurrentCharacterPtr()->GetName());
|
|
//send -_-!
|
|
struct ::packet_script packet_script;
|
|
|
|
packet_script.header = HEADER_GC_SCRIPT;
|
|
packet_script.skin = m_iCurrentSkin;
|
|
packet_script.src_size = m_strScript.size();
|
|
packet_script.size = packet_script.src_size + sizeof(struct packet_script);
|
|
|
|
TEMP_BUFFER buf;
|
|
buf.write(&packet_script, sizeof(struct packet_script));
|
|
buf.write(&m_strScript[0], m_strScript.size());
|
|
|
|
GetCurrentCharacterPtr()->GetDesc()->Packet(buf.read_peek(), buf.size());
|
|
|
|
if (test_server)
|
|
sys_log(0, "m_strScript %s size %d", m_strScript.c_str(), buf.size());
|
|
|
|
ClearScript();
|
|
}
|
|
|
|
const char* CQuestManager::GetQuestStateName(const string& quest_name, const int state_index)
|
|
{
|
|
int x = lua_gettop(L);
|
|
lua_getglobal(L, quest_name.c_str());
|
|
if (lua_isnil(L,-1))
|
|
{
|
|
sys_err("QUEST wrong quest state file %s.%d", quest_name.c_str(), state_index);
|
|
lua_settop(L,x);
|
|
return "";
|
|
}
|
|
lua_pushnumber(L, state_index);
|
|
lua_gettable(L, -2);
|
|
|
|
const char* str = lua_tostring(L, -1);
|
|
lua_settop(L, x);
|
|
return str;
|
|
}
|
|
|
|
int CQuestManager::GetQuestStateIndex(const string& quest_name, const string& state_name)
|
|
{
|
|
int x = lua_gettop(L);
|
|
lua_getglobal(L, quest_name.c_str());
|
|
if (lua_isnil(L,-1))
|
|
{
|
|
sys_err("QUEST wrong quest state file %s.%s",quest_name.c_str(),state_name.c_str() );
|
|
lua_settop(L,x);
|
|
return 0;
|
|
}
|
|
lua_pushstring(L, state_name.c_str());
|
|
lua_gettable(L, -2);
|
|
|
|
int v = (int)rint(lua_tonumber(L,-1));
|
|
lua_settop(L, x);
|
|
if ( test_server )
|
|
sys_log( 0,"[QUESTMANAGER] GetQuestStateIndex x(%d) v(%d) %s %s", v,x, quest_name.c_str(), state_name.c_str() );
|
|
return v;
|
|
}
|
|
|
|
void CQuestManager::SetSkinStyle(int iStyle)
|
|
{
|
|
if (iStyle<0 || iStyle >= QUEST_SKIN_COUNT)
|
|
{
|
|
m_iCurrentSkin = QUEST_SKIN_NORMAL;
|
|
}
|
|
else
|
|
m_iCurrentSkin = iStyle;
|
|
}
|
|
|
|
unsigned int CQuestManager::LoadTimerScript(const string& name)
|
|
{
|
|
map<string, unsigned int>::iterator it;
|
|
if ((it = m_mapTimerID.find(name)) != m_mapTimerID.end())
|
|
{
|
|
return it->second;
|
|
}
|
|
else
|
|
{
|
|
unsigned int new_id = UINT_MAX - m_mapTimerID.size();
|
|
|
|
m_mapNPC[new_id].Set(new_id, name);
|
|
m_mapTimerID.insert(make_pair(name, new_id));
|
|
|
|
return new_id;
|
|
}
|
|
}
|
|
|
|
unsigned int CQuestManager::GetCurrentNPCRace()
|
|
{
|
|
return GetCurrentNPCCharacterPtr() ? GetCurrentNPCCharacterPtr()->GetRaceNum() : 0;
|
|
}
|
|
|
|
LPITEM CQuestManager::GetCurrentItem()
|
|
{
|
|
return GetCurrentCharacterPtr() ? GetCurrentCharacterPtr()->GetQuestItemPtr() : NULL;
|
|
}
|
|
|
|
void CQuestManager::ClearCurrentItem()
|
|
{
|
|
if (GetCurrentCharacterPtr())
|
|
GetCurrentCharacterPtr()->ClearQuestItemPtr();
|
|
}
|
|
|
|
void CQuestManager::SetCurrentItem(LPITEM item)
|
|
{
|
|
if (GetCurrentCharacterPtr())
|
|
GetCurrentCharacterPtr()->SetQuestItemPtr(item);
|
|
}
|
|
|
|
LPCHARACTER CQuestManager::GetCurrentNPCCharacterPtr()
|
|
{
|
|
return GetCurrentCharacterPtr() ? GetCurrentCharacterPtr()->GetQuestNPC() : NULL;
|
|
}
|
|
|
|
const string & CQuestManager::GetCurrentQuestName()
|
|
{
|
|
return GetCurrentPC()->GetCurrentQuestName();
|
|
}
|
|
|
|
LPDUNGEON CQuestManager::GetCurrentDungeon()
|
|
{
|
|
LPCHARACTER ch = GetCurrentCharacterPtr();
|
|
|
|
if (!ch)
|
|
{
|
|
if (m_pSelectedDungeon)
|
|
return m_pSelectedDungeon;
|
|
return NULL;
|
|
}
|
|
|
|
return ch->GetDungeonForce();
|
|
}
|
|
|
|
void CQuestManager::RegisterQuest(const string & stQuestName, unsigned int idx)
|
|
{
|
|
assert(idx > 0);
|
|
|
|
itertype(m_hmQuestName) it;
|
|
|
|
if ((it = m_hmQuestName.find(stQuestName)) != m_hmQuestName.end())
|
|
return;
|
|
|
|
m_hmQuestName.insert(make_pair(stQuestName, idx));
|
|
LoadStartQuest(stQuestName, idx);
|
|
m_mapQuestNameByIndex.insert(make_pair(idx, stQuestName));
|
|
|
|
sys_log(0, "QUEST: Register %4u %s", idx, stQuestName.c_str());
|
|
}
|
|
|
|
unsigned int CQuestManager::GetQuestIndexByName(const string& name)
|
|
{
|
|
THashMapQuestName::iterator it = m_hmQuestName.find(name);
|
|
|
|
if (it == m_hmQuestName.end())
|
|
return 0; // RESERVED
|
|
|
|
return it->second;
|
|
}
|
|
|
|
const string & CQuestManager::GetQuestNameByIndex(unsigned int idx)
|
|
{
|
|
itertype(m_mapQuestNameByIndex) it;
|
|
|
|
if ((it = m_mapQuestNameByIndex.find(idx)) == m_mapQuestNameByIndex.end())
|
|
{
|
|
sys_err("cannot find quest name by index %u", idx);
|
|
assert(!"cannot find quest name by index");
|
|
|
|
static std::string st = "";
|
|
return st;
|
|
}
|
|
|
|
return it->second;
|
|
}
|
|
|
|
void CQuestManager::SendEventFlagList(LPCHARACTER ch)
|
|
{
|
|
itertype(m_mapEventFlag) it;
|
|
for (it = m_mapEventFlag.begin(); it != m_mapEventFlag.end(); ++it)
|
|
{
|
|
const string& flagname = it->first;
|
|
int value = it->second;
|
|
|
|
if (!test_server && value == 1 && flagname == "valentine_drop")
|
|
ch->ChatPacket(CHAT_TYPE_INFO, "%s %d prob 800", flagname.c_str(), value);
|
|
else if (!test_server && value == 1 && flagname == "newyear_wonso")
|
|
ch->ChatPacket(CHAT_TYPE_INFO, "%s %d prob 500", flagname.c_str(), value);
|
|
else if (!test_server && value == 1 && flagname == "newyear_fire")
|
|
ch->ChatPacket(CHAT_TYPE_INFO, "%s %d prob 1000", flagname.c_str(), value);
|
|
else
|
|
ch->ChatPacket(CHAT_TYPE_INFO, "%s %d", flagname.c_str(), value);
|
|
}
|
|
}
|
|
|
|
void CQuestManager::RequestSetEventFlag(const string& name, int value)
|
|
{
|
|
TPacketSetEventFlag p;
|
|
strncpy(p.szFlagName, name.c_str(), sizeof(p.szFlagName));
|
|
p.lValue = value;
|
|
db_clientdesc->DBPacket(HEADER_GD_SET_EVENT_FLAG, 0, &p, sizeof(TPacketSetEventFlag));
|
|
}
|
|
|
|
void CQuestManager::SetEventFlag(const string& name, int value)
|
|
{
|
|
static const char* DROPEVENT_CHARTONE_NAME = "drop_char_stone";
|
|
static const int DROPEVENT_CHARTONE_NAME_LEN = strlen(DROPEVENT_CHARTONE_NAME);
|
|
|
|
int prev_value = m_mapEventFlag[name];
|
|
|
|
sys_log(0, "QUEST eventflag %s %d prev_value %d", name.c_str(), value, m_mapEventFlag[name]);
|
|
m_mapEventFlag[name] = value;
|
|
|
|
if (name == "mob_item")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetMobItemRate(value);
|
|
}
|
|
else if (name == "mob_dam")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetMobDamageRate(value);
|
|
}
|
|
else if (name == "mob_gold")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetMobGoldAmountRate(value);
|
|
}
|
|
else if (name == "mob_gold_pct")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetMobGoldDropRate(value);
|
|
}
|
|
else if (name == "user_dam")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetUserDamageRate(value);
|
|
}
|
|
else if (name == "user_dam_buyer")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetUserDamageRatePremium(value);
|
|
}
|
|
else if (name == "mob_exp")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetMobExpRate(value);
|
|
}
|
|
else if (name == "mob_item_buyer")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetMobItemRatePremium(value);
|
|
}
|
|
else if (name == "mob_exp_buyer")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetMobExpRatePremium(value);
|
|
}
|
|
else if (name == "mob_gold_buyer")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetMobGoldAmountRatePremium(value);
|
|
}
|
|
else if (name == "mob_gold_pct_buyer")
|
|
{
|
|
CHARACTER_MANAGER::instance().SetMobGoldDropRatePremium(value);
|
|
}
|
|
else if (name == "crcdisconnect")
|
|
{
|
|
DESC_MANAGER::instance().SetDisconnectInvalidCRCMode(value != 0);
|
|
}
|
|
else if (!name.compare(0,5,"xmas_"))
|
|
{
|
|
xmas::ProcessEventFlag(name, prev_value, value);
|
|
}
|
|
else if (name == "newyear_boom")
|
|
{
|
|
const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet();
|
|
|
|
for (itertype(c_ref_set) it = c_ref_set.begin(); it != c_ref_set.end(); ++it)
|
|
{
|
|
LPCHARACTER ch = (*it)->GetCharacter();
|
|
|
|
if (!ch)
|
|
continue;
|
|
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "newyear_boom %d", value);
|
|
}
|
|
}
|
|
else if ( name == "eclipse" )
|
|
{
|
|
std::string mode("");
|
|
|
|
if ( value == 1 )
|
|
{
|
|
mode = "dark";
|
|
}
|
|
else
|
|
{
|
|
mode = "light";
|
|
}
|
|
|
|
const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet();
|
|
|
|
for (itertype(c_ref_set) it = c_ref_set.begin(); it != c_ref_set.end(); ++it)
|
|
{
|
|
LPCHARACTER ch = (*it)->GetCharacter();
|
|
if (!ch)
|
|
continue;
|
|
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "DayMode %s", mode.c_str());
|
|
}
|
|
}
|
|
else if (name == "day")
|
|
{
|
|
const DESC_MANAGER::DESC_SET & c_ref_set = DESC_MANAGER::instance().GetClientSet();
|
|
|
|
for (itertype(c_ref_set) it = c_ref_set.begin(); it != c_ref_set.end(); ++it)
|
|
{
|
|
LPCHARACTER ch = (*it)->GetCharacter();
|
|
if (!ch)
|
|
continue;
|
|
if (value)
|
|
{
|
|
// 밤
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "DayMode dark");
|
|
}
|
|
else
|
|
{
|
|
// 낮
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "DayMode light");
|
|
}
|
|
}
|
|
|
|
if (value && !prev_value)
|
|
{
|
|
// 없으면 만들어준다
|
|
struct SNPCSellFireworkPosition
|
|
{
|
|
int lMapIndex;
|
|
int x;
|
|
int y;
|
|
} positions[] = {
|
|
{ 1, 615, 618 },
|
|
{ 3, 500, 625 },
|
|
{ 21, 598, 665 },
|
|
{ 23, 476, 360 },
|
|
{ 41, 318, 629 },
|
|
{ 43, 478, 375 },
|
|
{ 0, 0, 0 },
|
|
};
|
|
|
|
SNPCSellFireworkPosition* p = positions;
|
|
while (p->lMapIndex)
|
|
{
|
|
if (map_allow_find(p->lMapIndex))
|
|
{
|
|
PIXEL_POSITION posBase;
|
|
if (!SECTREE_MANAGER::instance().GetMapBasePositionByMapIndex(p->lMapIndex, posBase))
|
|
{
|
|
sys_err("cannot get map base position %d", p->lMapIndex);
|
|
++p;
|
|
continue;
|
|
}
|
|
|
|
CHARACTER_MANAGER::instance().SpawnMob(xmas::MOB_XMAS_FIRWORK_SELLER_VNUM, p->lMapIndex, posBase.x + p->x * 100, posBase.y + p->y * 100, 0, false, -1);
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
else if (!value && prev_value)
|
|
{
|
|
// 있으면 지워준다
|
|
CharacterVectorInteractor i;
|
|
|
|
if (CHARACTER_MANAGER::instance().GetCharactersByRaceNum(xmas::MOB_XMAS_FIRWORK_SELLER_VNUM, i))
|
|
{
|
|
CharacterVectorInteractor::iterator it = i.begin();
|
|
|
|
while (it != i.end()) {
|
|
M2_DESTROY_CHARACTER(*it++);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (name == "pre_event_hc" && true == LC_IsEurope())
|
|
{
|
|
const DWORD EventNPC = 20090;
|
|
|
|
struct SEventNPCPosition
|
|
{
|
|
int lMapIndex;
|
|
int x;
|
|
int y;
|
|
} positions[] = {
|
|
{ 3, 588, 617 },
|
|
{ 23, 397, 250 },
|
|
{ 43, 567, 426 },
|
|
{ 0, 0, 0 },
|
|
};
|
|
|
|
if (value && !prev_value)
|
|
{
|
|
SEventNPCPosition* pPosition = positions;
|
|
|
|
while (pPosition->lMapIndex)
|
|
{
|
|
if (map_allow_find(pPosition->lMapIndex))
|
|
{
|
|
PIXEL_POSITION pos;
|
|
|
|
if (!SECTREE_MANAGER::instance().GetMapBasePositionByMapIndex(pPosition->lMapIndex, pos))
|
|
{
|
|
sys_err("cannot get map base position %d", pPosition->lMapIndex);
|
|
++pPosition;
|
|
continue;
|
|
}
|
|
|
|
CHARACTER_MANAGER::instance().SpawnMob(EventNPC, pPosition->lMapIndex, pos.x+pPosition->x*100, pos.y+pPosition->y*100, 0, false, -1);
|
|
}
|
|
pPosition++;
|
|
}
|
|
}
|
|
else if (!value && prev_value)
|
|
{
|
|
CharacterVectorInteractor i;
|
|
|
|
if (CHARACTER_MANAGER::instance().GetCharactersByRaceNum(EventNPC, i))
|
|
{
|
|
CharacterVectorInteractor::iterator it = i.begin();
|
|
|
|
while (it != i.end())
|
|
{
|
|
LPCHARACTER ch = *it++;
|
|
|
|
switch (ch->GetMapIndex())
|
|
{
|
|
case 3:
|
|
case 23:
|
|
case 43:
|
|
M2_DESTROY_CHARACTER(ch);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (name.compare(0, DROPEVENT_CHARTONE_NAME_LEN, DROPEVENT_CHARTONE_NAME)== 0)
|
|
{
|
|
DropEvent_CharStone_SetValue(name, value);
|
|
}
|
|
else if (name.compare(0, strlen("refine_box"), "refine_box")== 0)
|
|
{
|
|
DropEvent_RefineBox_SetValue(name, value);
|
|
}
|
|
else if (name == "gold_drop_limit_time")
|
|
{
|
|
g_GoldDropTimeLimitValue = value * 1000;
|
|
}
|
|
else if (name == "new_xmas_event")
|
|
{
|
|
// 20126 new산타.
|
|
static DWORD new_santa = 20126;
|
|
if (value != 0)
|
|
{
|
|
CharacterVectorInteractor i;
|
|
bool map1_santa_exist = false;
|
|
bool map21_santa_exist = false;
|
|
bool map41_santa_exist = false;
|
|
|
|
if (CHARACTER_MANAGER::instance().GetCharactersByRaceNum(new_santa, i))
|
|
{
|
|
CharacterVectorInteractor::iterator it = i.begin();
|
|
|
|
while (it != i.end())
|
|
{
|
|
LPCHARACTER tch = *(it++);
|
|
|
|
if (tch->GetMapIndex() == 1)
|
|
{
|
|
map1_santa_exist = true;
|
|
}
|
|
else if (tch->GetMapIndex() == 21)
|
|
{
|
|
map21_santa_exist = true;
|
|
}
|
|
else if (tch->GetMapIndex() == 41)
|
|
{
|
|
map41_santa_exist = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (map_allow_find(1) && !map1_santa_exist)
|
|
{
|
|
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(1);
|
|
CHARACTER_MANAGER::instance().SpawnMob(new_santa, 1, pkSectreeMap->m_setting.iBaseX + 60800, pkSectreeMap->m_setting.iBaseY + 61700, 0, false, 90, true);
|
|
}
|
|
if (map_allow_find(21) && !map21_santa_exist)
|
|
{
|
|
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(21);
|
|
CHARACTER_MANAGER::instance().SpawnMob(new_santa, 21, pkSectreeMap->m_setting.iBaseX + 59600, pkSectreeMap->m_setting.iBaseY + 61000, 0, false, 110, true);
|
|
}
|
|
if (map_allow_find(41) && !map41_santa_exist)
|
|
{
|
|
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(41);
|
|
CHARACTER_MANAGER::instance().SpawnMob(new_santa, 41, pkSectreeMap->m_setting.iBaseX + 35700, pkSectreeMap->m_setting.iBaseY + 74300, 0, false, 140, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CharacterVectorInteractor i;
|
|
CHARACTER_MANAGER::instance().GetCharactersByRaceNum(new_santa, i);
|
|
|
|
for (CharacterVectorInteractor::iterator it = i.begin(); it != i.end(); it++)
|
|
{
|
|
M2_DESTROY_CHARACTER(*it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int CQuestManager::GetEventFlag(const string& name)
|
|
{
|
|
map<string,int>::iterator it = m_mapEventFlag.find(name);
|
|
|
|
if (it == m_mapEventFlag.end())
|
|
return 0;
|
|
|
|
return it->second;
|
|
}
|
|
|
|
void CQuestManager::BroadcastEventFlagOnLogin(LPCHARACTER ch)
|
|
{
|
|
int iEventFlagValue;
|
|
|
|
if ((iEventFlagValue = quest::CQuestManager::instance().GetEventFlag("xmas_snow")))
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "xmas_snow %d", iEventFlagValue);
|
|
}
|
|
|
|
if ((iEventFlagValue = quest::CQuestManager::instance().GetEventFlag("xmas_boom")))
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "xmas_boom %d", iEventFlagValue);
|
|
}
|
|
|
|
if ((iEventFlagValue = quest::CQuestManager::instance().GetEventFlag("xmas_tree")))
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "xmas_tree %d", iEventFlagValue);
|
|
}
|
|
|
|
if ((iEventFlagValue = quest::CQuestManager::instance().GetEventFlag("day")))
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "DayMode dark");
|
|
}
|
|
|
|
if ((iEventFlagValue = quest::CQuestManager::instance().GetEventFlag("newyear_boom")))
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "newyear_boom %d", iEventFlagValue);
|
|
}
|
|
|
|
if ( (iEventFlagValue = quest::CQuestManager::instance().GetEventFlag("eclipse")) )
|
|
{
|
|
std::string mode;
|
|
|
|
if ( iEventFlagValue == 1 ) mode = "dark";
|
|
else mode = "light";
|
|
|
|
ch->ChatPacket(CHAT_TYPE_COMMAND, "DayMode %s", mode.c_str());
|
|
}
|
|
}
|
|
|
|
void CQuestManager::Reload()
|
|
{
|
|
lua_close(L);
|
|
m_mapNPC.clear();
|
|
m_mapNPCNameID.clear();
|
|
m_hmQuestName.clear();
|
|
m_mapTimerID.clear();
|
|
m_hmQuestStartScript.clear();
|
|
m_mapEventName.clear();
|
|
L = NULL;
|
|
Initialize();
|
|
|
|
for (itertype(m_registeredNPCVnum) it = m_registeredNPCVnum.begin(); it != m_registeredNPCVnum.end(); ++it)
|
|
{
|
|
char buf[256];
|
|
DWORD dwVnum = *it;
|
|
snprintf(buf, sizeof(buf), "%u", dwVnum);
|
|
m_mapNPC[dwVnum].Set(dwVnum, buf);
|
|
}
|
|
}
|
|
|
|
bool CQuestManager::ExecuteQuestScript(PC& pc, DWORD quest_index, const int state, const char* code, const int code_size, vector<AArgScript*>* pChatScripts, bool bUseCache)
|
|
{
|
|
return ExecuteQuestScript(pc, CQuestManager::instance().GetQuestNameByIndex(quest_index), state, code, code_size, pChatScripts, bUseCache);
|
|
}
|
|
|
|
bool CQuestManager::ExecuteQuestScript(PC& pc, const string& quest_name, const int state, const char* code, const int code_size, vector<AArgScript*>* pChatScripts, bool bUseCache)
|
|
{
|
|
// 실행공간을 생성
|
|
QuestState qs = CQuestManager::instance().OpenState(quest_name, state);
|
|
if (pChatScripts)
|
|
qs.chat_scripts.swap(*pChatScripts);
|
|
|
|
// 코드를 읽어들임
|
|
if (bUseCache)
|
|
{
|
|
lua_getglobal(qs.co, "__codecache");
|
|
// stack : __codecache
|
|
lua_pushnumber(qs.co, (long)code);
|
|
// stack : __codecache (codeptr)
|
|
lua_rawget(qs.co, -2);
|
|
// stack : __codecache (compiled-code)
|
|
if (lua_isnil(qs.co, -1))
|
|
{
|
|
// cache miss
|
|
|
|
// load code to lua,
|
|
// save it to cache
|
|
// and only function remain in stack
|
|
lua_pop(qs.co, 1);
|
|
// stack : __codecache
|
|
luaL_loadbuffer(qs.co, code, code_size, quest_name.c_str());
|
|
// stack : __codecache (compiled-code)
|
|
lua_pushnumber(qs.co, (long)code);
|
|
// stack : __codecache (compiled-code) (codeptr)
|
|
lua_pushvalue(qs.co, -2);
|
|
// stack : __codecache (compiled-code) (codeptr) (compiled_code)
|
|
lua_rawset(qs.co, -4);
|
|
// stack : __codecache (compiled-code)
|
|
lua_remove(qs.co, -2);
|
|
// stack : (compiled-code)
|
|
}
|
|
else
|
|
{
|
|
// cache hit
|
|
lua_remove(qs.co, -2);
|
|
// stack : (compiled-code)
|
|
}
|
|
}
|
|
else
|
|
luaL_loadbuffer(qs.co, code, code_size, quest_name.c_str());
|
|
|
|
// 플레이어와 연결
|
|
pc.SetQuest(quest_name, qs);
|
|
|
|
// 실행
|
|
QuestState& rqs = *pc.GetRunningQuestState();
|
|
if (!CQuestManager::instance().RunState(rqs))
|
|
{
|
|
CQuestManager::instance().CloseState(rqs);
|
|
pc.EndRunning();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CQuestManager::RegisterNPCVnum(DWORD dwVnum)
|
|
{
|
|
if (m_registeredNPCVnum.find(dwVnum) != m_registeredNPCVnum.end())
|
|
return;
|
|
|
|
m_registeredNPCVnum.insert(dwVnum);
|
|
|
|
char buf[256];
|
|
DIR* dir;
|
|
|
|
for (itertype(g_setQuestObjectDir) it = g_setQuestObjectDir.begin(); it != g_setQuestObjectDir.end(); ++it)
|
|
{
|
|
const string& stQuestObjectDir = *it;
|
|
snprintf(buf, sizeof(buf), "%s/%u", stQuestObjectDir.c_str(), dwVnum);
|
|
sys_log(0, "%s", buf);
|
|
|
|
if ((dir = opendir(buf)))
|
|
{
|
|
closedir(dir);
|
|
snprintf(buf, sizeof(buf), "%u", dwVnum);
|
|
sys_log(0, "%s", buf);
|
|
|
|
m_mapNPC[dwVnum].Set(dwVnum, buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CQuestManager::WriteRunningStateToSyserr()
|
|
{
|
|
const char * state_name = GetQuestStateName(GetCurrentQuestName(), GetCurrentState()->st);
|
|
|
|
string event_index_name = "";
|
|
for (itertype(m_mapEventName) it = m_mapEventName.begin(); it != m_mapEventName.end(); ++it)
|
|
{
|
|
if (it->second == m_iRunningEventIndex)
|
|
{
|
|
event_index_name = it->first;
|
|
break;
|
|
}
|
|
}
|
|
|
|
sys_err("LUA_ERROR: quest %s.%s %s", GetCurrentQuestName().c_str(), state_name, event_index_name.c_str() );
|
|
if (GetCurrentCharacterPtr() && test_server)
|
|
GetCurrentCharacterPtr()->ChatPacket(CHAT_TYPE_PARTY, "LUA_ERROR: quest %s.%s %s", GetCurrentQuestName().c_str(), state_name, event_index_name.c_str() );
|
|
}
|
|
|
|
#ifndef __WIN32__
|
|
void CQuestManager::QuestError(const char* func, int line, const char* fmt, ...)
|
|
{
|
|
char szMsg[4096];
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
vsnprintf(szMsg, sizeof(szMsg), fmt, args);
|
|
va_end(args);
|
|
|
|
_sys_err(func, line, "%s", szMsg);
|
|
if (test_server)
|
|
{
|
|
LPCHARACTER ch = GetCurrentCharacterPtr();
|
|
if (ch)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_PARTY, "error occurred on [%s:%d]", func,line);
|
|
ch->ChatPacket(CHAT_TYPE_PARTY, "%s", szMsg);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
void CQuestManager::QuestError(const char* func, int line, const char* fmt, ...)
|
|
{
|
|
char szMsg[4096];
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
vsnprintf(szMsg, sizeof(szMsg), fmt, args);
|
|
va_end(args);
|
|
|
|
_sys_err(func, line, "%s", szMsg);
|
|
if (test_server)
|
|
{
|
|
LPCHARACTER ch = GetCurrentCharacterPtr();
|
|
if (ch)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_PARTY, "error occurred on [%s:%d]", func,line);
|
|
ch->ChatPacket(CHAT_TYPE_PARTY, "%s", szMsg);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CQuestManager::AddServerTimer(const std::string& name, DWORD arg, LPEVENT event)
|
|
{
|
|
sys_log(0, "XXX AddServerTimer %s %d %p", name.c_str(), arg, get_pointer(event));
|
|
if (m_mapServerTimer.find(make_pair(name, arg)) != m_mapServerTimer.end())
|
|
{
|
|
sys_err("already registered server timer name:%s arg:%u", name.c_str(), arg);
|
|
return;
|
|
}
|
|
m_mapServerTimer.insert(make_pair(make_pair(name, arg), event));
|
|
}
|
|
|
|
void CQuestManager::ClearServerTimerNotCancel(const std::string& name, DWORD arg)
|
|
{
|
|
m_mapServerTimer.erase(make_pair(name, arg));
|
|
}
|
|
|
|
void CQuestManager::ClearServerTimer(const std::string& name, DWORD arg)
|
|
{
|
|
itertype(m_mapServerTimer) it = m_mapServerTimer.find(make_pair(name, arg));
|
|
if (it != m_mapServerTimer.end())
|
|
{
|
|
LPEVENT event = it->second;
|
|
event_cancel(&event);
|
|
m_mapServerTimer.erase(it);
|
|
}
|
|
}
|
|
|
|
void CQuestManager::CancelServerTimers(DWORD arg)
|
|
{
|
|
itertype(m_mapServerTimer) it = m_mapServerTimer.begin();
|
|
for ( ; it != m_mapServerTimer.end(); ++it) {
|
|
if (it->first.second == arg) {
|
|
LPEVENT event = it->second;
|
|
event_cancel(&event);
|
|
m_mapServerTimer.erase(it);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CQuestManager::SetServerTimerArg(DWORD dwArg)
|
|
{
|
|
m_dwServerTimerArg = dwArg;
|
|
}
|
|
|
|
DWORD CQuestManager::GetServerTimerArg()
|
|
{
|
|
return m_dwServerTimerArg;
|
|
}
|
|
|
|
void CQuestManager::SelectDungeon(LPDUNGEON pDungeon)
|
|
{
|
|
m_pSelectedDungeon = pDungeon;
|
|
}
|
|
|
|
bool CQuestManager::PickupItem(unsigned int pc, LPITEM item)
|
|
{
|
|
if (test_server)
|
|
sys_log( 0, "questmanager::PickupItem Start : itemVnum : %d PC : %d", item->GetOriginalVnum(), pc);
|
|
PC* pPC;
|
|
if ((pPC = GetPC(pc)))
|
|
{
|
|
if (!CheckQuestLoaded(pPC))
|
|
{
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pc);
|
|
if (ch)
|
|
{
|
|
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("퀘스트를 로드하는 중입니다. 잠시만 기다려 주십시오."));
|
|
}
|
|
return false;
|
|
}
|
|
// call script
|
|
SetCurrentItem(item);
|
|
|
|
return m_mapNPC[item->GetVnum()].OnPickupItem(*pPC);
|
|
}
|
|
else
|
|
{
|
|
sys_err("QUEST PICK_ITEM_EVENT no such pc id : %d", pc);
|
|
return false;
|
|
}
|
|
}
|
|
void CQuestManager::BeginOtherPCBlock(DWORD pid)
|
|
{
|
|
LPCHARACTER ch = GetCurrentCharacterPtr();
|
|
if (NULL == ch)
|
|
{
|
|
sys_err("NULL?");
|
|
return;
|
|
}
|
|
/*
|
|
# 1. current pid = pid0 <- It will be m_pOtherPCBlockRootPC.
|
|
begin_other_pc_block(pid1)
|
|
# 2. current pid = pid1
|
|
begin_other_pc_block(pid2)
|
|
# 3. current_pid = pid2
|
|
end_other_pc_block()
|
|
end_other_pc_block()
|
|
*/
|
|
// when begin_other_pc_block(pid1)
|
|
if (m_vecPCStack.empty())
|
|
{
|
|
m_pOtherPCBlockRootPC = GetCurrentPC();
|
|
}
|
|
m_vecPCStack.push_back(GetCurrentCharacterPtr()->GetPlayerID());
|
|
GetPC(pid);
|
|
}
|
|
|
|
void CQuestManager::EndOtherPCBlock()
|
|
{
|
|
if (m_vecPCStack.size() == 0)
|
|
{
|
|
sys_err("m_vecPCStack is alread empty. CurrentQuest{Name(%s), State(%s)}", GetCurrentQuestName().c_str(), GetCurrentState()->_title.c_str());
|
|
return;
|
|
}
|
|
DWORD pc = m_vecPCStack.back();
|
|
m_vecPCStack.pop_back();
|
|
GetPC(pc);
|
|
|
|
if (m_vecPCStack.empty())
|
|
{
|
|
m_pOtherPCBlockRootPC = NULL;
|
|
}
|
|
}
|
|
|
|
bool CQuestManager::IsInOtherPCBlock()
|
|
{
|
|
return !m_vecPCStack.empty();
|
|
}
|
|
|
|
PC* CQuestManager::GetOtherPCBlockRootPC()
|
|
{
|
|
return m_pOtherPCBlockRootPC;
|
|
}
|
|
}
|
|
|