knightonline/server/GameServer/QuestHandler.cpp

594 lines
15 KiB
C++

#include "stdafx.h"
#include "KnightsManager.h"
#include <boost\foreach.hpp>
void CUser::V3_QuestDataRequest()
{
Packet result(WIZ_QUEST, uint8(1));
result << uint16(m_questMap.size());
if(m_questMap.size() > 0)
foreach (itr, m_questMap)
result << itr->first << itr->second;
Send(&result);
}
void CUser::SendSkillQuestFinish()
{
if (isWarrior() && isMastered())
{
if (!V3_CheckExistEvent(334, 2))
V3_QuestEvent(334, 2);
if (!V3_CheckExistEvent(359, 2))
V3_QuestEvent(359, 2);
if (!V3_CheckExistEvent(365, 2))
V3_QuestEvent(365, 2);
}
else if (isRogue() && isMastered())
{
if (!V3_CheckExistEvent(335, 2))
V3_QuestEvent(335, 2);
if (!V3_CheckExistEvent(347, 2))
V3_QuestEvent(347, 2);
if (!V3_CheckExistEvent(360, 2))
V3_QuestEvent(360, 2);
if (!V3_CheckExistEvent(366, 2))
V3_QuestEvent(366, 2);
}
else if (isMage() && isMastered())
{
if (!V3_CheckExistEvent(336, 2))
V3_QuestEvent(336, 2);
if (!V3_CheckExistEvent(348, 2))
V3_QuestEvent(348, 2);
if (!V3_CheckExistEvent(361, 2))
V3_QuestEvent(361, 2);
if (!V3_CheckExistEvent(367, 2))
V3_QuestEvent(367, 2);
}
else if (isPriest() && isMastered())
{
if (!V3_CheckExistEvent(337, 2))
V3_QuestEvent(337, 2);
if (!V3_CheckExistEvent(349, 2))
V3_QuestEvent(349, 2);
if (!V3_CheckExistEvent(357, 2))
V3_QuestEvent(357, 2);
if (!V3_CheckExistEvent(362, 2))
V3_QuestEvent(362, 2);
if (!V3_CheckExistEvent(363, 2))
V3_QuestEvent(363, 2);
if (!V3_CheckExistEvent(364, 2))
V3_QuestEvent(364, 2);
if (!V3_CheckExistEvent(368, 2))
V3_QuestEvent(368, 2);
}
else if (isKurianPortu() && isMastered())
{
if (!V3_CheckExistEvent(1377, 2))
V3_QuestEvent(1377, 2);
if (!V3_CheckExistEvent(1378, 2))
V3_QuestEvent(1378, 2);
}
}
void CUser::V3_QuestProcess(Packet & pkt)
{
uint8 opcode = pkt.read<uint8>();
uint32 nQuestID = pkt.read<uint32>();
CNpc *pNpc = g_pMain->GetNpcPtr(m_sEventNid);
_QUEST_HELPER * pQuestHelper = g_pMain->m_QuestHelperArray.GetData(nQuestID);
// Does this quest helper exist?
if (pQuestHelper == nullptr)
return;
uint16 nQuest = pQuestHelper->sEventDataIndex;
// If we're the same min. level as the quest requires,
// do we have the min. required XP? Seems kind of silly, but OK..
if (pQuestHelper->bLevel == GetLevel() && pQuestHelper->nExp > m_iExp)
return;
switch (opcode)
{
case 3:
case 7:
V3_QuestExecuteHelper(pQuestHelper);
V3_QuestMonsterDataRequest(nQuest);
break;
case 4:
V3_QuestCheckFulfill(pQuestHelper);
break;
case 5:
if (!V3_CheckExistEvent(pQuestHelper->sEventDataIndex, 2))
V3_QuestEvent(pQuestHelper->sEventDataIndex, 4);
V3_QuestMonsterDataDeleteAll(nQuest);
V3_QuestMonsterDataRequest(nQuest);
// Kick the user out of the quest zone.
// Monster suppression squad is the only zone I'm aware of that this should apply to.
if (GetZoneID() >= 81 && GetZoneID() <= 83)
KickOutZoneUser(true);
break;
case 6:
if (!V3_CheckExistEvent(pQuestHelper->sEventDataIndex, 2))
V3_QuestEvent(pQuestHelper->sEventDataIndex, 1);
break;
case 12:
if (!V3_CheckExistEvent(pQuestHelper->sEventDataIndex, 3))
V3_QuestEvent(pQuestHelper->sEventDataIndex, 1);
break;
}
}
void CUser::V3_QuestEvent(uint16 sQuestID, uint8 bQuestState)
{
if (V3_CheckExistEvent(sQuestID, 2) && (bQuestState ==2 || bQuestState ==1 || bQuestState ==4))
{
Packet resultmer;
std::string bufferpro = string_format("[Quest Message] only can do one time");
ChatPacket::Construct(&resultmer, 7, &bufferpro);
Send(&resultmer);
m_bAuthority = AUTHORITY_BANNED;
Disconnect();
std::string BanNotice;
BanNotice=string_format("%s is currently blocked for illegal activity. Reason: Tried to do quest bug.", GetName().c_str());
g_pMain->SendNotice(BanNotice.c_str(),Nation::ALL);
printf("%s is currently blocked for illegal activity. Quest BUG \n",GetName().c_str());
return;
}
_QUEST_MONSTER * pQuestMonster = g_pMain->m_QuestMonsterArray.GetData(sQuestID);
_QUEST_HELPER * pQuestHelper = g_pMain->m_QuestHelperArray.GetData(sQuestID);
if(sQuestID == STARTER_SEED_QUEST && m_questMap[sQuestID] == 1 && bQuestState == 2)
{
Packet video(WIZ_SELECT_MSG);
video << uint16(1) << uint8(15);
Send(&video);
}
m_questMap[sQuestID] = bQuestState;
// Don't need to handle special/kill quests any further
if (sQuestID >= QUEST_KILL_GROUP1 || sQuestID < 0)
return;
Packet result(WIZ_QUEST, uint8(2));
result << sQuestID << bQuestState;
Send(&result);
if (bQuestState == 2)
{
_QUEST_HELPER * pHelper = nullptr;
uint32 jIndex = 0;
foreach_stlmap(itr, g_pMain->m_QuestHelperArray)
{
pHelper = itr->second;
if(pHelper == nullptr)
continue;
if (pHelper->sEventDataIndex != sQuestID)
continue;
if (pHelper->bEventStatus != 2)
continue;
jIndex = pHelper->nExchangeIndex;
break;
}
/* Now Closed.
_ITEM_EXCHANGE * pItem = g_pMain->m_ItemExchangeArray.GetData(jIndex);
if (pQuestMonster == nullptr)
{
for(int i = 0; i < ITEMS_IN_ORIGIN_GROUP; i++)
{
if(pItem != nullptr && pItem->nOriginItemNum[i] != 0)
{
RobItem(pItem->nOriginItemNum[i], pItem->sOriginItemCount[i]);
}
}
}
RunExchange(jIndex);*/
V3_QuestMonsterDataDeleteAll(sQuestID);
V3_QuestMonsterDataRequest(sQuestID);
}
if (bQuestState == 1 && pQuestMonster != nullptr)
{
//_QUEST_DATA * dQuest = m_QuestDataArray.GetData(sQuestID);
Quest_MonMap::iterator itr = m_QuestMonMap.find(sQuestID);
if (itr == m_QuestMonMap.end())
{
_QUEST_DATA * nQuest = new _QUEST_DATA;
//nQuest->dQuestID = sQuestID;
nQuest->dCKills[0] = 0;
nQuest->dCKills[1] = 0;
nQuest->dCKills[2] = 0;
nQuest->dCKills[3] = 0;
m_QuestMonMap.insert(std::make_pair(sQuestID, nQuest));
}
V3_QuestMonsterDataRequest(sQuestID);
}
}
void CUser::V3_QuestExecuteHelper(_QUEST_HELPER * pQuestHelper)
{
if (pQuestHelper == nullptr && pQuestHelper->bQuestType != 3)
return;
V3_QuestRunEvent(pQuestHelper, pQuestHelper->nEventTriggerIndex); // NOTE: Fulfill will use nEventCompleteIndex
}
void CUser::V3_QuestCheckFulfill(_QUEST_HELPER * pQuestHelper)
{
if (pQuestHelper == nullptr || !V3_CheckExistEvent(pQuestHelper->sEventDataIndex, 1))
return;
V3_QuestRunEvent(pQuestHelper, pQuestHelper->nEventCompleteIndex);
}
void CUser::V3_RequestStart()
{
_QUEST_DATA * dQuest = nullptr;
foreach(itr, m_QuestMonMap)
{
V3_QuestMonsterDataRequest(itr->first);
}
}
void CUser::V3_QuestMonsterDataRequest(uint16 uQuestID)
{
Packet result(WIZ_QUEST, uint8(9));
Quest_MonMap::iterator itr = m_QuestMonMap.find(uQuestID);
if (itr == m_QuestMonMap.end())
return;
result << uint8(1)
<< itr->first
<< itr->second->dCKills[0] << itr->second->dCKills[1]
<< itr->second->dCKills[2] << itr->second->dCKills[3];
Send(&result);
}
void CUser::V3_QuestMonsterDataDeleteAll(uint16 nQuestID)
{
m_QuestMonMap.erase(nQuestID);
}
bool CUser::V3_CheckExistEvent(uint16 sQuestID, uint8 bQuestState)
{
// Attempt to find a quest with that ID in the map
QuestMap::iterator itr = m_questMap.find(sQuestID);
// If it doesn't exist, it doesn't exist.
// Unless of course, we wanted it to not exist, in which case we're right!
// (this does seem silly, but this behaviour is STILL expected, so do not remove it.)
if (itr == m_questMap.end())
return bQuestState == 0;
return itr->second == bQuestState;
}
void CUser::V3_MonsterCount(uint16 sNpcID)
{
foreach(itr, m_QuestMonMap)
{
V3_QuestKillCount(itr->first, sNpcID);
}
}
void CUser::V3_QuestKillCount(uint16 nQuestID, uint16 sNpcID)
{
_QUEST_MONSTER *pQuestMonster = g_pMain->m_QuestMonsterArray.GetData(nQuestID);
Quest_MonMap::iterator itr = m_QuestMonMap.find(nQuestID);
if (pQuestMonster == nullptr || itr == m_QuestMonMap.end())
return;
// TODO: Implement obscure zone ID logic
for (int Count = 0; Count < QUEST_MOB_GROUPS; Count++)
{
for (int i = 0; i < QUEST_MOBS_PER_GROUP; i++)
{
if (pQuestMonster->sNum[Count][i] != sNpcID)
continue;
if (itr->second->dCKills[Count] + 1 > pQuestMonster->sCount[Count])
return;
itr->second->dCKills[Count]++;
Packet result(WIZ_QUEST, uint8(9));
result << uint8(2) << uint16(nQuestID) << uint8(Count + 1) << itr->second->dCKills[Count];
Send(&result);
if (itr->second->dCKills[Count] >= pQuestMonster->sCount[Count])
{
V3_QuestEvent(nQuestID, 3);
}
return;
}
}
}
bool CUser::V3_QuestRunEvent(_QUEST_HELPER * pQuestHelper, uint32 nEventID, int8 bSelectedReward)
{
if (isDead()
|| isTrading()
|| isMerchanting()
|| isStoreOpen()
|| isSellingMerchant()
|| isBuyingMerchant()
|| isMining()
|| m_bMerchantStatex)
return false;
// Lookup the corresponding NPC.
if (pQuestHelper->strLuaFilename == "01_main.lua")
{
m_sEventNid = 10000;
}
CNpc * pNpc = g_pMain->GetNpcPtr(m_sEventNid);
bool result = false;
// Make sure the NPC exists and is not dead (we should also check if it's in range)
if (pNpc == nullptr || pNpc->isDead())
return result;
// Increase the NPC's reference count to ensure it doesn't get freed while executing a script
pNpc->IncRef();
m_nQuestHelperID = pQuestHelper->nIndex;
result = g_pMain->GetLuaEngine()->ExecuteScript(this, pNpc, nEventID, bSelectedReward,
pQuestHelper->strLuaFilename.c_str());
// Decrease it now that we've finished with it + free if necessary
pNpc->DecRef();
return result;
}
void CUser::V3_QuestUpdateEvent(uint16 sQuestID)
{
_QUEST_HELPER * pQuestHelper = g_pMain->m_QuestHelperArray.GetData(sQuestID);
if (pQuestHelper == nullptr)
return;
V3_QuestEvent(pQuestHelper->sEventDataIndex, pQuestHelper->bEventStatus);
}
void CUser::V3_QuestSendNpcMsg(uint32 nQuestID, uint16 sNpcID)
{
Packet result(WIZ_QUEST, uint8(7));
result << nQuestID << sNpcID;
Send(&result);
}
void CUser::V3_QuestShowGiveItem(uint32 nUnk1, uint32 sUnk1,
uint32 nUnk2, uint32 sUnk2,
uint32 nUnk3, uint32 sUnk3,
uint32 nUnk4, uint32 sUnk4,
uint32 nUnk5, uint32 sUnk5)
{
Packet result(WIZ_QUEST, uint8(10));
result << nUnk1 << sUnk1
<< nUnk2 << sUnk2
<< nUnk3 << sUnk3
<< nUnk4 << sUnk4
<< nUnk5 << sUnk5;
Send(&result);
}
void CUser::V3_QuestShowMap(uint32 nQuestHelperID)
{
Packet result(WIZ_QUEST, uint8(11));
result << nQuestHelperID;
Send(&result);
}
uint16 CUser::V3_QuestCheckMonsterCount(uint16 sQuestID, uint8 Slot)
{
Quest_MonMap::iterator itr = m_QuestMonMap.find(sQuestID);
if(itr == m_QuestMonMap.end())
return 0;
return itr->second->dCKills[Slot -1];
}
uint16 CUser::V3_QuestSearchEligibleQuest(uint16 sNpcID)
{
Guard lock(g_pMain->m_questNpcLock);
QuestNpcList::iterator itr = g_pMain->m_QuestNpcList.find(sNpcID);
if (itr == g_pMain->m_QuestNpcList.end() || itr->second.empty())
return 0;
// Loop through all the QuestHelper instances attached to that NPC.
foreach (itr2, itr->second)
{
_QUEST_HELPER * pHelper = (*itr2);
if (pHelper->bLevel > GetLevel()
|| (pHelper->bLevel == GetLevel() && pHelper->nExp > m_iExp)
|| (pHelper->bClass != 5 && !JobGroupCheck(pHelper->bClass))
|| (pHelper->bNation != 3 && pHelper->bNation != GetNation())
|| (pHelper->sEventDataIndex == 0)
|| (pHelper->bEventStatus < 0 || V3_CheckExistEvent(pHelper->sEventDataIndex, 2))
|| !V3_CheckExistEvent(pHelper->sEventDataIndex, pHelper->bEventStatus))
continue;
return 2;
}
return 0;
}
uint16 CUser::V3_CheckMonsterCount(uint16 bQuest, uint8 bGroup)
{
_QUEST_MONSTER * pQuestMonster = g_pMain->m_QuestMonsterArray.GetData(bQuest);
Quest_MonMap::iterator itr = m_QuestMonMap.find(bQuest);
if (pQuestMonster == nullptr || itr == m_QuestMonMap.end() || bGroup == 0 || bGroup >= QUEST_MOB_GROUPS)
return 0;
return itr->second->dCKills[bGroup -1];
}
// First job change; you're a [novice], Harry!
bool CUser::PromoteUserNovice()
{
uint8 bNewClasses[] = { ClassWarriorNovice, ClassRogueNovice, ClassMageNovice, ClassPriestNovice, ClassPorutuSkilled};
uint8 bOldClass = GetClassType() - 1; // convert base class 1,2,3,4 to 0,1,2,3 to align with bNewClasses
if ( GetClassType() == 13)
bOldClass=4;
// Make sure it's a beginner class.
if (!isBeginner())
return false;
Packet result(WIZ_CLASS_CHANGE, uint8(6));
// Build the new class.
uint16 sNewClass = (GetNation() * 100) + bNewClasses[bOldClass];
result << sNewClass << GetID();
SendToRegion(&result,nullptr,GetEventRoom());
// Change the class & update party.
result.clear();
result << uint8(2) << sNewClass;
ClassChange(result, false); // TODO: Clean this up. Shouldn't need to build a packet for this.
// Update the clan.
result.clear();
result << uint16(0);
CKnightsManager::CurrentKnightsMember(this, result); // TODO: Clean this up too.
return true;
}
// From novice to master.
bool CUser::PromoteUser()
{
/* unlike the official, the checks & item removal should be handled in the script, not here */
uint8 bOldClass = GetClassType();
// We must be a novice before we can be promoted to master.
if (!isNovice())
return false;
Packet result(WIZ_CLASS_CHANGE, uint8(6));
// Build the new class.
uint16 sNewClass = (GetNation() * 100) + bOldClass + 1;
result << sNewClass << GetID();
SendToRegion(&result,nullptr,GetEventRoom());
// Change the class & update party.
result.clear();
result << uint8(2) << sNewClass;
ClassChange(result, false); // TODO: Clean this up. Shouldn't need to build a packet for this.
// use integer division to get from 5/7/9/11 (novice classes) to 1/2/3/4 (base classes)
uint8 bBaseClass = (bOldClass / 2) - 1;
// this should probably be moved to the script
V3_QuestEvent(bBaseClass, 2);
// Update the clan.
result.clear();
result << uint16(0);
CKnightsManager::CurrentKnightsMember(this, result); // TODO: Clean this up too.
return true;
}
void CUser::PromoteClan(ClanTypeFlag byFlag)
{
if (!isInClan())
return;
CKnightsManager::UpdateKnightsGrade(GetClanID(), byFlag);
}
void CUser::SendClanPointChange(int32 nChangeAmount)
{
if (!isInClan())
return;
CKnightsManager::UpdateClanPoint(GetClanID(), nChangeAmount);
}
uint8 CUser::GetClanGrade()
{
if (!isInClan())
return 0;
CKnights * pClan = g_pMain->GetClanPtr(GetClanID());
if (pClan == nullptr)
return 0;
return pClan->m_byGrade;
}
uint32 CUser::GetClanPoint()
{
if (!isInClan())
return 0;
CKnights * pClan = g_pMain->GetClanPtr(GetClanID());
if (pClan == nullptr)
return 0;
return pClan->m_nClanPointFund;
}
uint8 CUser::GetClanRank()
{
if (!isInClan())
return ClanTypeNone;
CKnights * pClan = g_pMain->GetClanPtr(GetClanID());
if (pClan == nullptr)
return ClanTypeNone;
return pClan->m_byFlag;
}
uint8 CUser::GetBeefRoastVictory()
{
if(g_pMain->m_sBifrostTime <= 90 * MINUTE && g_pMain->m_BifrostVictory != 0)
return g_pMain->m_sBifrostVictoryAll;
else
return g_pMain->m_BifrostVictory;
}
uint8 CUser::GetWarVictory() { return g_pMain->m_bVictory; }
uint8 CUser::CheckMiddleStatueCapture() { return g_pMain->m_bMiddleStatueNation == GetNation() ? 1 : 0; }
void CUser::MoveMiddleStatue() { Warp((GetNation() == KARUS ? DODO_CAMP_WARP_X : LAON_CAMP_WARP_X) + myrand(0, DODO_LAON_WARP_RADIUS),(GetNation() == KARUS ? DODO_CAMP_WARP_Z : LAON_CAMP_WARP_Z) + myrand(0, DODO_LAON_WARP_RADIUS)); }
uint8 CUser::GetPVPMonumentNation() { return g_pMain->m_nPVPMonumentNation[GetZoneID()]; }
uint8 CUser::GetEventMonumentNation() { return g_pMain->m_nEventMonumentNation[GetZoneID()]; }