/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "StdAfx.h"
uint32 QuestMgr::CalcQuestStatus(Object* quest_giver, Player* plr, QuestRelation* qst)
{
uint32 status = CalcQuestStatus(quest_giver, plr, qst->qst, qst->type, false);
if(status == QMGR_QUEST_FINISHED)
{
if(quest_giver->GetTypeId() == TYPEID_UNIT)
{
// Bleh, POI is the wrong icon.
// Turns out, dynamic_flags should be 0x02 :P
uint32 flags = quest_giver->GetUInt32Value(UNIT_DYNAMIC_FLAGS);
flags |= 0x02; // MINIMAP_TRACK
quest_giver->BuildFieldUpdatePacket(plr, UNIT_DYNAMIC_FLAGS, flags);
}
}
return status;
}
bool QuestMgr::isRepeatableQuestFinished(Player *plr, Quest *qst)
{
uint32 i;
for(i = 0; i < 4; ++i)
{
if(qst->required_item[i])
{
if(plr->GetItemInterface()->GetItemCount(qst->required_item[i]) < qst->required_itemcount[i])
{
return false;
}
}
}
return true;
}
uint32 QuestMgr::PlayerMeetsReqs(Player* plr, Quest* qst, bool skiplevelcheck)
{
std::list::iterator itr;
uint32 status;
if (!sQuestMgr.IsQuestRepeatable(qst))
status = QMGR_QUEST_AVAILABLE;
else
{
if(isRepeatableQuestFinished(plr, qst))
return QMGR_QUEST_FINISHED;
else
return QMGR_QUEST_REPEATABLE;
}
if (plr->getLevel() < qst->min_level && !skiplevelcheck)
return QMGR_QUEST_AVAILABLELOW_LEVEL;
if(qst->required_class)
if(!(qst->required_class & plr->getClassMask()))
return QMGR_QUEST_NOT_AVAILABLE;
// Check reputation
if(qst->required_rep_faction && qst->required_rep_value)
if(plr->GetStanding(qst->required_rep_faction) < (int32)qst->required_rep_value)
return QMGR_QUEST_NOT_AVAILABLE;
if (plr->HasFinishedQuest(qst->id))
return QMGR_QUEST_NOT_AVAILABLE;
for(uint32 i = 0; i < 4; ++i)
{
if (qst->required_quests[i] > 0 && !plr->HasFinishedQuest(qst->required_quests[i]))
{
return QMGR_QUEST_NOT_AVAILABLE;
}
}
return status;
}
uint32 QuestMgr::CalcQuestStatus(Object* quest_giver, Player* plr, Quest* qst, uint8 type, bool skiplevelcheck)
{
QuestLogEntry* qle;
qle = plr->GetQuestLogForEntry(qst->id);
if (!qle)
{
if (type & QUESTGIVER_QUEST_START)
{
return PlayerMeetsReqs(plr, qst, skiplevelcheck);
}
}
else
{
if (!qle->CanBeFinished())
{
return QMGR_QUEST_NOT_FINISHED;
}
else
{
if (type & QUESTGIVER_QUEST_END)
{
return QMGR_QUEST_FINISHED;
}
else
{
return QMGR_QUEST_NOT_AVAILABLE;
}
}
}
return QMGR_QUEST_NOT_AVAILABLE;
}
uint32 QuestMgr::CalcStatus(Object* quest_giver, Player* plr)
{
uint32 status = QMGR_QUEST_NOT_AVAILABLE;
std::list::const_iterator itr;
std::list::const_iterator q_begin;
std::list::const_iterator q_end;
bool bValid = false;
if(quest_giver->GetTypeId() == TYPEID_GAMEOBJECT)
{
bValid = ((GameObject*)quest_giver)->HasQuests();
if(bValid)
{
q_begin = ((GameObject*)quest_giver)->QuestsBegin();
q_end = ((GameObject*)quest_giver)->QuestsEnd();
}
}
else if(quest_giver->GetTypeId() == TYPEID_UNIT)
{
bValid = ((Creature*)quest_giver)->HasQuests();
if(bValid)
{
q_begin = ((Creature*)quest_giver)->QuestsBegin();
q_end = ((Creature*)quest_giver)->QuestsEnd();
}
}
else if(quest_giver->GetTypeId() == TYPEID_ITEM)
{
if(static_cast- (quest_giver)->GetProto()->QuestId)
bValid = true;
}
//This will be handled at quest share so nothing important as status
else if(quest_giver->GetTypeId() == TYPEID_PLAYER)
{
status = QMGR_QUEST_AVAILABLE;
}
if(!bValid)
{
//anoying msg that is not needed since all objects dont exactly have quests
//sLog.outDebug("QUESTS: Warning, invalid NPC "I64FMT" specified for CalcStatus. TypeId: %d.", quest_giver->GetGUID(), quest_giver->GetTypeId());
return status;
}
if(quest_giver->GetTypeId() == TYPEID_ITEM)
{
Quest *pQuest = QuestStorage.LookupEntry( static_cast
- (quest_giver)->GetProto()->QuestId );
QuestRelation qr;
qr.qst = pQuest;
qr.type = 1;
uint32 tmp_status = CalcQuestStatus(quest_giver,plr, &qr);
if(tmp_status > status)
status = tmp_status;
}
for(itr = q_begin; itr != q_end; ++itr)
{
uint32 tmp_status = CalcQuestStatus(quest_giver, plr, *itr); // save a call
if (tmp_status > status)
status = tmp_status;
}
return status;
}
uint32 QuestMgr::ActiveQuestsCount(Object* quest_giver, Player* plr)
{
std::list::const_iterator itr;
map tmp_map;
uint32 questCount = 0;
std::list::const_iterator q_begin;
std::list::const_iterator q_end;
bool bValid = false;
if(quest_giver->GetTypeId() == TYPEID_GAMEOBJECT)
{
bValid = ((GameObject*)quest_giver)->HasQuests();
if(bValid)
{
q_begin = ((GameObject*)quest_giver)->QuestsBegin();
q_end = ((GameObject*)quest_giver)->QuestsEnd();
}
}
else if(quest_giver->GetTypeId() == TYPEID_UNIT)
{
bValid = ((Creature*)quest_giver)->HasQuests();
if(bValid)
{
q_begin = ((Creature*)quest_giver)->QuestsBegin();
q_end = ((Creature*)quest_giver)->QuestsEnd();
}
}
if(!bValid)
{
sLog.outDebug("QUESTS: Warning, invalid NPC "I64FMT" specified for ActiveQuestsCount. TypeId: %d.", quest_giver->GetGUID(), quest_giver->GetTypeId());
return 0;
}
for(itr = q_begin; itr != q_end; ++itr)
{
if (CalcQuestStatus(quest_giver, plr, *itr) >= QMGR_QUEST_NOT_FINISHED)
{
if (tmp_map.find((*itr)->qst->id) == tmp_map.end())
{
tmp_map.insert(std::map::value_type((*itr)->qst->id, 1));
questCount++;
}
}
}
return questCount;
}
void QuestMgr::BuildOfferReward(WorldPacket *data, Quest* qst, Object* qst_giver, uint32 menutype)
{
ItemPrototype * it;
data->SetOpcode(SMSG_QUESTGIVER_OFFER_REWARD);
*data << qst_giver->GetGUID();
*data << qst->id;
*data << qst->title;
*data << qst->completiontext;
//uint32 a = 0, b = 0, c = 1, d = 0, e = 1;
*data << (qst->next_quest_id ? uint32(1) : uint32(0)); // next quest shit
*data << uint32(0); // maybe required money
*data << uint32(1); // emotes count
*data << uint32(0); // emote delay
*data << uint32(1); // emote type
*data << qst->count_reward_choiceitem;
for(uint32 i = 0; i < 6; ++i)
{
if(qst->reward_choiceitem[i])
{
*data << qst->reward_choiceitem[i];
*data << qst->reward_choiceitemcount[i];
it = ItemPrototypeStorage.LookupEntry(qst->reward_choiceitem[i]);
*data << (it ? it->DisplayInfoID : uint32(0));
}
}
*data << qst->count_reward_item;
for(uint32 i = 0; i < 6; ++i)
{
if(qst->reward_item[i])
{
*data << qst->reward_item[i];
*data << qst->reward_itemcount[i];
it = ItemPrototypeStorage.LookupEntry(qst->reward_item[i]);
*data << (it ? it->DisplayInfoID : uint32(0));
}
}
*data << qst->reward_money;
*data << qst->reward_spell;
*data << uint32(8);
*data << uint32(0);
*data << uint32(0);
}
void QuestMgr::BuildQuestDetails(WorldPacket *data, Quest* qst, Object* qst_giver, uint32 menutype)
{
std::map::const_iterator itr;
data->SetOpcode( SMSG_QUESTGIVER_QUEST_DETAILS );
*data << qst_giver->GetGUID();
*data << qst->id;
*data << qst->title;
*data << qst->details;
*data << qst->objectives;
*data << uint32(1);
*data << uint32(0); // "Suggested players"
*data << qst->count_reward_choiceitem;
ItemPrototype *ip;
uint32 i;
for(i = 0; i < 6; ++i)
{
ip = ItemPrototypeStorage.LookupEntry(qst->reward_choiceitem[i]);
if(!qst->reward_choiceitem[i]) continue;
*data << qst->reward_choiceitem[i];
*data << qst->reward_choiceitemcount[i];
if(ip)
*data << ip->DisplayInfoID;
else
*data << uint32(0);
}
*data << qst->count_reward_item;
for(i = 0; i < 4; ++i)
{
ip = ItemPrototypeStorage.LookupEntry(qst->reward_item[i]);
if(!qst->reward_item[i]) continue;
*data << qst->reward_item[i];
*data << qst->reward_itemcount[i];
if(ip)
*data << ip->DisplayInfoID;
else
*data << uint32(0);
}
*data << qst->reward_money;
*data << qst->reward_spell;
*data << uint32(0);
*data << uint32(0);
*data << uint32(4);
*data << uint32(1);
*data << uint32(0);
*data << uint32(1);
*data << uint32(0);
*data << uint32(0);
*data << uint32(0);
*data << uint32(0);
*data << uint32(0);
}
void QuestMgr::BuildRequestItems(WorldPacket *data, Quest* qst, Object* qst_giver, uint32 status)
{
ItemPrototype * it;
data->SetOpcode( SMSG_QUESTGIVER_REQUEST_ITEMS );
*data << qst_giver->GetGUID();
*data << qst->id;
*data << qst->title;
/*if(qst_giver->GetTypeId() == TYPEID_GAMEOBJECT)
type = ((GameObject*)qst_giver)->GetQuestRelation(qst->id);
else if(qst_giver->GetTypeId() == TYPEID_UNIT)
type = ((Creature*)qst_giver)->GetQuestRelation(qst->id);*/
*data << (qst->incompletetext[0] ? qst->incompletetext : qst->details);
*data << uint32(0);
*data << uint32(1); // Emote count
*data << uint32(0); // Emote delay
*data << uint32(1); // Emote type
*data << qst->required_money; // Required Money
// item count
*data << qst->count_required_item;
// (loop for each item)
for(uint32 i = 0; i < 4; ++i)
{
if(qst->required_item[i] != 0)
{
*data << qst->required_item[i];
*data << qst->required_itemcount[i];
it = ItemPrototypeStorage.LookupEntry(qst->required_item[i]);
*data << (it ? it->DisplayInfoID : uint32(0));
}
}
// wtf is this?
if(status == QMGR_QUEST_NOT_FINISHED)
{
*data << uint32(0); //incomplete button
}
else
{
*data << uint32(2);
}
*data << uint32(8);
*data << uint32(10);
}
void QuestMgr::BuildQuestComplete(Player*plr, Quest* qst)
{
uint32 xp ;
if(plr->getLevel() >= plr->GetUInt32Value(PLAYER_FIELD_MAX_LEVEL))
{
plr->ModUInt32Value(PLAYER_FIELD_COINAGE, qst->reward_xp_as_money);
xp = 0;
}else
{
xp = float2int32(GenerateQuestXP(plr,qst) * sWorld.getRate(RATE_QUESTXP));
plr->GiveXP(xp, 0, false);
}
WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE,72 );
data << qst->id;
data << uint32(3);
//if(qst->reward_xp > 0)
// data << uint32(qst->reward_xp);
//else
// data << uint32(GenerateQuestXP(NULL,qst)); //xp
data << xp;
data << uint32(qst->reward_money);
data << uint32(qst->count_reward_item); //Reward item count
ItemPrototype *ip;
for(uint32 i = 0; i < 4; ++i)
{
data << qst->reward_item[i];
data << qst->reward_itemcount[i];
if(qst->reward_item[i])
{
ip = ItemPrototypeStorage.LookupEntry(qst->reward_item[i]);
data << ((ip != NULL) ? ip->DisplayInfoID : uint32(0));
}
else
{
data << (uint32)0;
}
}
plr->GetSession()->SendPacket(&data);
}
void QuestMgr::BuildQuestList(WorldPacket *data, Object* qst_giver, Player *plr)
{
list::iterator it;
list::iterator st;
list::iterator ed;
map tmp_map;
data->Initialize( SMSG_QUESTGIVER_QUEST_LIST );
*data << qst_giver->GetGUID();
*data << "How can I help you?"; //Hello line
*data << uint32(1);//Emote Delay
*data << uint32(1);//Emote
bool bValid = false;
if(qst_giver->GetTypeId() == TYPEID_GAMEOBJECT)
{
bValid = ((GameObject*)qst_giver)->HasQuests();
if(bValid)
{
st = ((GameObject*)qst_giver)->QuestsBegin();
ed = ((GameObject*)qst_giver)->QuestsEnd();
}
}
else if(qst_giver->GetTypeId() == TYPEID_UNIT)
{
bValid = ((Creature*)qst_giver)->HasQuests();
if(bValid)
{
st = ((Creature*)qst_giver)->QuestsBegin();
ed = ((Creature*)qst_giver)->QuestsEnd();
}
}
if(!bValid)
{
*data << uint8(0);
return;
}
*data << uint8(sQuestMgr.ActiveQuestsCount(qst_giver, plr));
for (it = st; it != ed; ++it)
{
if (sQuestMgr.CalcQuestStatus(qst_giver, plr, *it) >= QMGR_QUEST_NOT_FINISHED)
{
if (tmp_map.find((*it)->qst->id) == tmp_map.end())
{
tmp_map.insert(std::map::value_type((*it)->qst->id, 1));
*data << (*it)->qst->id;
*data << sQuestMgr.CalcQuestStatus(qst_giver, plr, *it);
*data << uint32(0);
*data << (*it)->qst->title;
}
}
}
}
void QuestMgr::BuildQuestUpdateAddItem(WorldPacket* data, uint32 itemid, uint32 count)
{
data->Initialize(SMSG_QUESTUPDATE_ADD_ITEM);
*data << itemid << count;
}
void QuestMgr::SendQuestUpdateAddKill(Player* plr, uint32 questid, uint32 entry, uint32 count, uint32 tcount, uint64 guid)
{
WorldPacket data(32);
data.SetOpcode(SMSG_QUESTUPDATE_ADD_KILL);
data << questid << entry << count << tcount << guid;
plr->GetSession()->SendPacket(&data);
}
void QuestMgr::BuildQuestUpdateComplete(WorldPacket* data, Quest* qst)
{
data->Initialize(SMSG_QUESTUPDATE_COMPLETE);
*data << qst->id;
}
void QuestMgr::SendPushToPartyResponse(Player *plr, Player* pTarget, uint32 response)
{
WorldPacket data(MSG_QUEST_PUSH_RESULT, 13);
data << pTarget->GetGUID();
data << response;
data << uint8(0);
plr->GetSession()->SendPacket(&data);
}
bool QuestMgr::OnGameObjectActivate(Player *plr, GameObject *go)
{
uint32 i, j;
QuestLogEntry *qle;
uint32 entry = go->GetEntry();
for(i = 0; i < 25; ++i)
{
if((qle = plr->GetQuestLogInSlot(i)))
{
// dont waste time on quests without mobs
if(qle->GetQuest()->count_required_mob == 0)
continue;
for(j = 0; j < 4; ++j)
{
if(qle->GetQuest()->required_mob[j] == entry &&
qle->GetQuest()->required_mobtype[j] == QUEST_MOB_TYPE_GAMEOBJECT &&
qle->m_mobcount[j] < qle->GetQuest()->required_mobcount[j])
{
// add another kill.
// (auto-dirtys it)
qle->SetMobCount(j, qle->m_mobcount[j] + 1);
qle->SendUpdateAddKill(j);
CALL_QUESTSCRIPT_EVENT(qle, OnGameObjectActivate)(entry, plr);
if(qle->CanBeFinished())
qle->SendQuestComplete();
qle->UpdatePlayerFields();
return true;
}
}
}
}
return false;
}
void QuestMgr::OnPlayerKill(Player* plr, Creature* victim)
{
if(!plr)
return;
uint32 i, j;
uint32 entry = victim->GetEntry();
QuestLogEntry *qle;
for(i = 0; i < 25; ++i)
{
if((qle = plr->GetQuestLogInSlot(i)))
{
// dont waste time on quests without mobs
if(qle->GetQuest()->count_required_mob == 0)
continue;
for(j = 0; j < 4; ++j)
{
if(qle->GetQuest()->required_mob[j] == entry &&
qle->GetQuest()->required_mobtype[j] == QUEST_MOB_TYPE_CREATURE &&
qle->m_mobcount[j] < qle->GetQuest()->required_mobcount[j])
{
// add another kill.
// (auto-dirtys it)
qle->SetMobCount(j, qle->m_mobcount[j] + 1);
qle->SendUpdateAddKill(j);
CALL_QUESTSCRIPT_EVENT(qle, OnCreatureKill)(entry, plr);
qle->UpdatePlayerFields();
break;
}
}
}
}
// Shared kills
Player *gplr = NULL;
if(plr->InGroup())
{
if(Group* pGroup = plr->GetGroup())
{
if(pGroup->GetGroupType() != GROUP_TYPE_PARTY) return; // Raid's don't get shared kills.
GroupMembersSet::iterator gitr;
pGroup->Lock();
for(uint32 k = 0; k < pGroup->GetSubGroupCount(); k++)
{
for(gitr = pGroup->GetSubGroup(k)->GetGroupMembersBegin(); gitr != pGroup->GetSubGroup(k)->GetGroupMembersEnd(); ++gitr)
{
gplr = gitr->player;
if(gplr && gplr != plr) // dont double kills
{
for(i = 0; i < 20; ++i)
{
if((qle = gplr->GetQuestLogInSlot(i)))
{
// dont waste time on quests without mobs
if(qle->GetQuest()->count_required_mob == 0)
continue;
for(j = 0; j < 4; ++j)
{
if(qle->GetQuest()->required_mob[j] == entry &&
qle->GetQuest()->required_mobtype[j] == QUEST_MOB_TYPE_CREATURE &&
qle->m_mobcount[j] < qle->GetQuest()->required_mobcount[j])
{
// add another kill.
// (auto-dirtys it)
qle->SetMobCount(j, qle->m_mobcount[j] + 1);
qle->SendUpdateAddKill(j);
CALL_QUESTSCRIPT_EVENT(qle, OnCreatureKill)(entry, plr);
qle->UpdatePlayerFields();
// lua stuff
//QUESTLUA_SendEvent(qst, victim, plr, ON_QUEST_KILLMOB, qle->m_mobcount[j]);
if(qle->CanBeFinished())
qle->SendQuestComplete();
break;
}
}
}
}
}
}
}
pGroup->Unlock();
}
}
}
void QuestMgr::OnPlayerItemPickup(Player* plr, Item* item)
{
uint32 i, j;
uint32 pcount;
uint32 entry = item->GetEntry();
QuestLogEntry *qle;
for(i = 0; i < 25; ++i)
{
if((qle = plr->GetQuestLogInSlot(i)))
{
for(j = 0; j < qle->GetQuest()->count_required_item; ++j)
{
if(qle->GetQuest()->required_item[j] == entry)
{
pcount = plr->GetItemInterface()->GetItemCount(entry, true);
CALL_QUESTSCRIPT_EVENT(qle, OnPlayerItemPickup)(entry, pcount, plr);
if(pcount < qle->GetQuest()->required_itemcount[j])
{
WorldPacket data(8);
data.SetOpcode(SMSG_QUESTUPDATE_ADD_ITEM);
data << qle->GetQuest()->required_item[j] << uint32(1);
plr->GetSession()->SendPacket(&data);
if(qle->CanBeFinished())
{
plr->UpdateNearbyGameObjects();
qle->SendQuestComplete();
}
break;
}
}
}
}
}
}
void QuestMgr::OnPlayerExploreArea(Player* plr, uint32 AreaID)
{
uint32 i, j;
QuestLogEntry *qle;
for(i = 0; i < 25; ++i)
{
if((qle = plr->GetQuestLogInSlot(i)))
{
// dont waste time on quests without mobs
if(qle->GetQuest()->count_requiredtriggers == 0)
continue;
for(j = 0; j < 4; ++j)
{
if(qle->GetQuest()->required_triggers[j] == AreaID &&
!qle->m_explored_areas[j])
{
qle->SetTrigger(j);
CALL_QUESTSCRIPT_EVENT(qle, OnExploreArea)(qle->m_explored_areas[j], plr);
qle->UpdatePlayerFields();
if(qle->CanBeFinished())
{
plr->UpdateNearbyGameObjects();
qle->SendQuestComplete();
}
break;
}
}
}
}
}
void QuestMgr::GiveQuestRewardReputation(Player* plr, Quest* qst, Object *qst_giver)
{
// Reputation reward
for(int z = 0; z < 2; z++)
{
uint32 fact = 19; // default to 19 if no factiondbc
uint32 amt = uint32(float(GenerateQuestXP(plr, qst)) * 0.1f); // guess
if(!qst->reward_repfaction[z])
{
if(z == 1)
break;
// Let's do this properly. Determine the faction of the creature, and give reputation to his faction.
if(qst_giver->GetTypeId() == TYPEID_UNIT)
if(((Creature*)qst_giver)->m_factionDBC != NULL)
fact = ((Creature*)qst_giver)->m_factionDBC->ID;
if(qst_giver->GetTypeId() == TYPEID_GAMEOBJECT)
fact = qst_giver->GetUInt32Value(GAMEOBJECT_FACTION);
}
else
{
fact = qst->reward_repfaction[z];
if(qst->reward_repvalue[z])
amt = qst->reward_repvalue[z];
}
if(qst->reward_replimit)
if(plr->GetStanding(fact) >= (int32)qst->reward_replimit)
continue;
amt *= sWorld.getRate(RATE_QUESTREPUTATION); // reputation rewards
plr->ModStanding(fact, amt);
}
}
void QuestMgr::OnQuestFinished(Player* plr, Quest* qst, Object *qst_giver, uint32 reward_slot)
{
QuestLogEntry *qle = NULL;
if(!qst->is_repeatable)
{
qle = plr->GetQuestLogForEntry(qst->id);
if(!qle)
return;
}
BuildQuestComplete(plr, qst);
if(!qst->is_repeatable) CALL_QUESTSCRIPT_EVENT(qle, OnQuestComplete)(plr);
ScriptSystem->OnQuestEvent(qst, ((Creature*)qst_giver), plr, QUEST_EVENT_ON_COMPLETE);
if(!qst->is_repeatable) qle->Finish();
if(qst_giver->GetTypeId() == TYPEID_UNIT)
{
if(!((Creature*)qst_giver)->HasQuest(qst->id, 2))
{
sCheatLog.writefromsession(plr->GetSession(), "tried to finish quest from invalid npc.");
plr->GetSession()->Disconnect();
return;
}
}
//details: hmm as i can remember, repeatable quests give faction rep still after first completation
if(IsQuestRepeatable(qst))
{
// Reputation reward
GiveQuestRewardReputation(plr, qst, qst_giver);
// Static Item reward
for(uint32 i = 0; i < 4; ++i)
{
if(qst->reward_item[i])
{
ItemPrototype *proto = ItemPrototypeStorage.LookupEntry(qst->reward_item[i]);
if(!proto)
{
sLog.outError("Invalid item prototype in quest reward! ID %d, quest %d", qst->reward_item[i], qst->id);
}
else
{
Item *add;
SlotResult slotresult;
add = plr->GetItemInterface()->FindItemLessMax(qst->reward_item[i], qst->reward_itemcount[i], false);
if (!add)
{
slotresult = plr->GetItemInterface()->FindFreeInventorySlot(proto);
if(!slotresult.Result)
{
plr->GetItemInterface()->BuildInventoryChangeError(NULL, NULL, INV_ERR_INVENTORY_FULL);
}
else
{
Item *itm = objmgr.CreateItem(qst->reward_item[i], plr);
itm->SetUInt32Value(ITEM_FIELD_STACK_COUNT, uint32(qst->reward_itemcount[i]));
plr->GetItemInterface()->SafeAddItem(itm,slotresult.ContainerSlot, slotresult.Slot);
}
}
else
{
add->SetCount(add->GetUInt32Value(ITEM_FIELD_STACK_COUNT) + qst->reward_itemcount[i]);
add->m_isDirty = true;
}
}
}
}
// Choice Rewards
if(qst->reward_choiceitem[reward_slot])
{
ItemPrototype *proto = ItemPrototypeStorage.LookupEntry(qst->reward_choiceitem[reward_slot]);
if(!proto)
{
sLog.outError("Invalid item prototype in quest reward! ID %d, quest %d", qst->reward_choiceitem[reward_slot], qst->id);
}
else
{
Item *add;
SlotResult slotresult;
add = plr->GetItemInterface()->FindItemLessMax(qst->reward_choiceitem[reward_slot], qst->reward_choiceitemcount[reward_slot], false);
if (!add)
{
slotresult = plr->GetItemInterface()->FindFreeInventorySlot(proto);
if(!slotresult.Result)
{
plr->GetItemInterface()->BuildInventoryChangeError(NULL, NULL, INV_ERR_INVENTORY_FULL);
}
else
{
Item *itm = objmgr.CreateItem(qst->reward_choiceitem[reward_slot], plr);
itm->SetUInt32Value(ITEM_FIELD_STACK_COUNT, uint32(qst->reward_choiceitemcount[reward_slot]));
plr->GetItemInterface()->SafeAddItem(itm,slotresult.ContainerSlot, slotresult.Slot);
}
}
else
{
add->SetCount(add->GetUInt32Value(ITEM_FIELD_STACK_COUNT) + qst->reward_choiceitemcount[reward_slot]);
add->m_isDirty = true;
}
}
}
// Remove items
for(uint32 i = 0; i < 4; ++i)
{
if(qst->required_item[i]) plr->GetItemInterface()->RemoveItemAmt(qst->required_item[i],qst->required_itemcount[i]);
}
// Remove srcitem
if(qst->srcitem && qst->srcitem != qst->receive_items[0])
plr->GetItemInterface()->RemoveItemAmt(qst->srcitem, qst->srcitemcount ? qst->srcitemcount : 1);
// cast Effect Spell
if(qst->effect_on_player)
{
SpellEntry * inf =sSpellStore.LookupEntry(qst->effect_on_player);
if(inf)
{
Spell * spe = new Spell(qst_giver,inf,true,NULL);
SpellCastTargets tgt;
tgt.m_unitTarget = plr->GetGUID();
spe->prepare(&tgt);
}
}
}
else
{
plr->ModUInt32Value(PLAYER_FIELD_COINAGE, qst->reward_money);
// Reputation reward
GiveQuestRewardReputation(plr, qst, qst_giver);
// Static Item reward
for(uint32 i = 0; i < 4; ++i)
{
if(qst->reward_item[i])
{
ItemPrototype *proto = ItemPrototypeStorage.LookupEntry(qst->reward_item[i]);
if(!proto)
{
sLog.outError("Invalid item prototype in quest reward! ID %d, quest %d", qst->reward_item[i], qst->id);
}
else
{
Item *add;
SlotResult slotresult;
add = plr->GetItemInterface()->FindItemLessMax(qst->reward_item[i], qst->reward_itemcount[i], false);
if (!add)
{
slotresult = plr->GetItemInterface()->FindFreeInventorySlot(proto);
if(!slotresult.Result)
{
plr->GetItemInterface()->BuildInventoryChangeError(NULL, NULL, INV_ERR_INVENTORY_FULL);
}
else
{
Item *itm = objmgr.CreateItem(qst->reward_item[i], plr);
itm->SetUInt32Value(ITEM_FIELD_STACK_COUNT, uint32(qst->reward_itemcount[i]));
plr->GetItemInterface()->SafeAddItem(itm,slotresult.ContainerSlot, slotresult.Slot);
}
}
else
{
add->SetCount(add->GetUInt32Value(ITEM_FIELD_STACK_COUNT) + qst->reward_itemcount[i]);
add->m_isDirty = true;
}
}
}
}
// Choice Rewards
if(qst->reward_choiceitem[reward_slot])
{
ItemPrototype *proto = ItemPrototypeStorage.LookupEntry(qst->reward_choiceitem[reward_slot]);
if(!proto)
{
sLog.outError("Invalid item prototype in quest reward! ID %d, quest %d", qst->reward_choiceitem[reward_slot], qst->id);
}
else
{
Item *add;
SlotResult slotresult;
add = plr->GetItemInterface()->FindItemLessMax(qst->reward_choiceitem[reward_slot], qst->reward_choiceitemcount[reward_slot], false);
if (!add)
{
slotresult = plr->GetItemInterface()->FindFreeInventorySlot(proto);
if(!slotresult.Result)
{
plr->GetItemInterface()->BuildInventoryChangeError(NULL, NULL, INV_ERR_INVENTORY_FULL);
}
else
{
Item *itm = objmgr.CreateItem(qst->reward_choiceitem[reward_slot], plr);
itm->SetUInt32Value(ITEM_FIELD_STACK_COUNT, uint32(qst->reward_choiceitemcount[reward_slot]));
plr->GetItemInterface()->SafeAddItem(itm,slotresult.ContainerSlot, slotresult.Slot);
}
}
else
{
add->SetCount(add->GetUInt32Value(ITEM_FIELD_STACK_COUNT) + qst->reward_choiceitemcount[reward_slot]);
add->m_isDirty = true;
}
}
}
// Remove items
for(uint32 i = 0; i < 4; ++i)
{
if(qst->required_item[i]) plr->GetItemInterface()->RemoveItemAmt(qst->required_item[i],qst->required_itemcount[i]);
}
// Remove srcitem
if(qst->srcitem && qst->srcitem != qst->receive_items[0])
plr->GetItemInterface()->RemoveItemAmt(qst->srcitem, qst->srcitemcount ? qst->srcitemcount : 1);
// cast learning spell
if(qst->reward_spell)
{
if(!plr->HasSpell(qst->reward_spell))
{
// "Teaching" effect
WorldPacket data(SMSG_SPELL_START, 200);
data << qst_giver->GetNewGUID() << qst_giver->GetNewGUID();
data << uint32(7763);
data << uint16(0);
data << uint32(0);
data << uint16(2);
data << plr->GetGUID();
plr->GetSession()->SendPacket( &data );
data.Initialize( SMSG_SPELL_GO );
data << qst_giver->GetNewGUID() << qst_giver->GetNewGUID();
data << uint32(7763); // spellID
data << uint8(0) << uint8(1); // flags
data << uint8(1); // amount of targets
data << plr->GetGUID(); // target
data << uint8(0);
data << uint16(2);
data << plr->GetGUID();
plr->GetSession()->SendPacket( &data );
// Teach the spell
plr->addSpell(qst->reward_spell);
}
}
// cast Effect Spell
if(qst->effect_on_player)
{
SpellEntry * inf =sSpellStore.LookupEntry(qst->effect_on_player);
if(inf)
{
Spell * spe = new Spell(qst_giver,inf,true,NULL);
SpellCastTargets tgt;
tgt.m_unitTarget = plr->GetGUID();
spe->prepare(&tgt);
}
}
//Add to finished quests
plr->AddToFinishedQuests(qst->id);
}
}
/////////////////////////////////////
// Quest Management //
/////////////////////////////////////
void QuestMgr::LoadNPCQuests(Creature *qst_giver)
{
qst_giver->SetQuestList(GetCreatureQuestList(qst_giver->GetEntry()));
}
void QuestMgr::LoadGOQuests(GameObject *go)
{
go->SetQuestList(GetGOQuestList(go->GetEntry()));
}
QuestRelationList* QuestMgr::GetGOQuestList(uint32 entryid)
{
HM_NAMESPACE::hash_map &olist = _GetList();
HM_NAMESPACE::hash_map::iterator itr = olist.find(entryid);
return (itr == olist.end()) ? 0 : itr->second;
}
QuestRelationList* QuestMgr::GetCreatureQuestList(uint32 entryid)
{
HM_NAMESPACE::hash_map* > &olist = _GetList();
HM_NAMESPACE::hash_map::iterator itr = olist.find(entryid);
return (itr == olist.end()) ? 0 : itr->second;
}
template void QuestMgr::_AddQuest(uint32 entryid, Quest *qst, uint8 type)
{
HM_NAMESPACE::hash_map* > &olist = _GetList();
std::list* nlist;
QuestRelation *ptr = NULL;
if (olist.find(entryid) == olist.end())
{
nlist = new std::list;
olist.insert(HM_NAMESPACE::hash_map* >::value_type(entryid, nlist));
}
else
{
nlist = olist.find(entryid)->second;
}
list::iterator it;
for (it = nlist->begin(); it != nlist->end(); ++it)
{
if ((*it)->qst == qst)
{
ptr = (*it);
break;
}
}
if (ptr == NULL)
{
ptr = new QuestRelation;
ptr->qst = qst;
ptr->type = type;
nlist->push_back(ptr);
}
else
{
ptr->type |= type;
}
}
void QuestMgr::_CleanLine(std::string *str)
{
_RemoveChar("\r", str);
_RemoveChar("\n", str);
while (str->c_str()[0] == 32)
{
str->erase(0,1);
}
}
void QuestMgr::_RemoveChar(char *c, std::string *str)
{
string::size_type pos = str->find(c,0);
while (pos != string::npos)
{
str->erase(pos, 1);
pos = str->find(c, 0);
}
}
uint32 QuestMgr::GenerateQuestXP(Player *plr, Quest *qst)
{
if(qst->is_repeatable)
return 0;
{
if( plr->getLevel() <= qst->max_level + 5 )
return qst->reward_xp;
if( plr->getLevel() == qst->max_level + 6 )
return (uint32)(qst->reward_xp * 0.8);
if( plr->getLevel() == qst->max_level + 7 )
return (uint32)(qst->reward_xp * 0.6);
if( plr->getLevel() == qst->max_level + 8 )
return (uint32)(qst->reward_xp * 0.4);
if( plr->getLevel() == qst->max_level + 9 )
return (uint32)(qst->reward_xp * 0.2);
else
return 0;
}
}
/*
#define XP_INC 50
#define XP_DEC 10
#define XP_INC100 15
#define XP_DEC100 5
double xp, pxp, mxp, mmx;
// hack fix
xp = qst->max_level * XP_INC;
if(xp <= 0)
xp = 1;
pxp = xp + (xp / 100) * XP_INC100;
xp = XP_DEC;
mxp = xp + (xp / 100) * XP_DEC100;
mmx = (pxp - mxp);
if(qst->quest_flags & QUEST_FLAG_SPEAKTO)
mmx *= 0.6;
if(qst->quest_flags & QUEST_FLAG_TIMED)
mmx *= 1.1;
if(qst->quest_flags & QUEST_FLAG_EXPLORATION)
mmx *= 1.2;
if(mmx < 0)
return 1;
mmx *= sWorld.getRate(RATE_QUESTXP);
return (int)mmx;*/
void QuestMgr::SendQuestInvalid(INVALID_REASON reason, Player *plyr)
{
if(!plyr)
return;
plyr->GetSession()->OutPacket(SMSG_QUESTGIVER_QUEST_INVALID, 4, &reason);
sLog.outDebug("WORLD:Sent SMSG_QUESTGIVER_QUEST_INVALID");
}
void QuestMgr::SendQuestFailed(FAILED_REASON failed, Quest * qst, Player *plyr)
{
if(!plyr)
return;
WorldPacket data(8);
data.Initialize(SMSG_QUESTGIVER_QUEST_FAILED);
data << uint32(qst->id);
data << failed;
plyr->GetSession()->SendPacket(&data);
sLog.outDebug("WORLD:Sent SMSG_QUESTGIVER_QUEST_FAILED");
}
void QuestMgr::SendQuestUpdateFailedTimer(Quest *pQuest, Player *plyr)
{
if(!plyr)
return;
plyr->GetSession()->OutPacket(SMSG_QUESTUPDATE_FAILEDTIMER, 4, &pQuest->id);
sLog.outDebug("WORLD:Sent SMSG_QUESTUPDATE_FAILEDTIMER");
}
void QuestMgr::SendQuestUpdateFailed(Quest *pQuest, Player *plyr)
{
if(!plyr)
return;
plyr->GetSession()->OutPacket(SMSG_QUESTUPDATE_FAILED, 4, &pQuest->id);
sLog.outDebug("WORLD:Sent SMSG_QUESTUPDATE_FAILED");
}
void QuestMgr::SendQuestLogFull(Player *plyr)
{
if(!plyr)
return;
plyr->GetSession()->OutPacket(SMSG_QUESTLOG_FULL);
sLog.outDebug("WORLD:Sent QUEST_LOG_FULL_MESSAGE");
}
uint32 QuestMgr::GetGameObjectLootQuest(uint32 GO_Entry)
{
HM_NAMESPACE::hash_map::iterator itr = m_ObjectLootQuestList.find(GO_Entry);
if(itr == m_ObjectLootQuestList.end()) return 0;
return itr->second;
}
void QuestMgr::SetGameObjectLootQuest(uint32 GO_Entry, uint32 Item_Entry)
{
if(m_ObjectLootQuestList.find(GO_Entry) != m_ObjectLootQuestList.end())
{
//sLog.outError("WARNING: Gameobject %d has more than 1 quest item allocated in it's loot template!", GO_Entry);
}
// Find the quest that has that item
uint32 QuestID = 0;
uint32 i;
StorageContainerIterator * itr = QuestStorage.MakeIterator();
while(!itr->AtEnd())
{
Quest *qst = itr->Get();
for(i = 0; i < 4; ++i)
{
if(qst->required_item[i] == Item_Entry)
{
QuestID = qst->id;
m_ObjectLootQuestList[GO_Entry] = QuestID;
itr->Destruct();
return;
}
}
if(!itr->Inc())
break;
}
itr->Destruct();
//sLog.outError("WARNING: No coresponding quest was found for quest item %d", Item_Entry);
}
void QuestMgr::BuildQuestFailed(WorldPacket* data, uint32 questid)
{
data->Initialize(SMSG_QUESTUPDATE_FAILEDTIMER);
*data << questid;
}
bool QuestMgr::OnActivateQuestGiver(Object *qst_giver, Player *plr)
{
if(qst_giver->GetTypeId() == TYPEID_GAMEOBJECT && !((GameObject*)qst_giver)->HasQuests())
return false;
uint32 questCount = sQuestMgr.ActiveQuestsCount(qst_giver, plr);
WorldPacket data(1000);
if (questCount == 0)
{
sLog.outDebug("WORLD: Invalid NPC for CMSG_QUESTGIVER_HELLO.");
return false;
}
else if (questCount == 1)
{
std::list::const_iterator itr;
std::list::const_iterator q_begin;
std::list::const_iterator q_end;
bool bValid = false;
if(qst_giver->GetTypeId() == TYPEID_GAMEOBJECT)
{
bValid = ((GameObject*)qst_giver)->HasQuests();
if(bValid)
{
q_begin = ((GameObject*)qst_giver)->QuestsBegin();
q_end = ((GameObject*)qst_giver)->QuestsEnd();
}
}
else if(qst_giver->GetTypeId() == TYPEID_UNIT)
{
bValid = ((Creature*)qst_giver)->HasQuests();
if(bValid)
{
q_begin = ((Creature*)qst_giver)->QuestsBegin();
q_end = ((Creature*)qst_giver)->QuestsEnd();
}
}
if(!bValid)
{
sLog.outDebug("QUESTS: Warning, invalid NPC "I64FMT" specified for OnActivateQuestGiver. TypeId: %d.", qst_giver->GetGUID(), qst_giver->GetTypeId());
return false;
}
for(itr = q_begin; itr != q_end; ++itr)
if (sQuestMgr.CalcQuestStatus(qst_giver, plr, *itr) >= QMGR_QUEST_NOT_FINISHED)
break;
if (sQuestMgr.CalcStatus(qst_giver, plr) < QMGR_QUEST_NOT_FINISHED)
return false;
ASSERT(itr != q_end);
uint32 status = sQuestMgr.CalcStatus(qst_giver, plr);
if ((status == QMGR_QUEST_AVAILABLE) || (status == QMGR_QUEST_REPEATABLE))
{
sQuestMgr.BuildQuestDetails(&data, (*itr)->qst, qst_giver, 1); // 1 because we have 1 quest, and we want goodbye to function
plr->GetSession()->SendPacket(&data);
sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_DETAILS." );
}
else if (status == QMGR_QUEST_FINISHED)
{
sQuestMgr.BuildOfferReward(&data, (*itr)->qst, qst_giver, 1);
plr->GetSession()->SendPacket(&data);
//ss
sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD." );
}
else if (status == QMGR_QUEST_NOT_FINISHED)
{
sQuestMgr.BuildRequestItems(&data, (*itr)->qst, qst_giver, status);
plr->GetSession()->SendPacket(&data);
sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS." );
}
}
else
{
sQuestMgr.BuildQuestList(&data, qst_giver ,plr);
plr->GetSession()->SendPacket(&data);
sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST." );
}
return true;
}
QuestMgr::~QuestMgr()
{
HM_NAMESPACE::hash_map::iterator itr1;
HM_NAMESPACE::hash_map* >::iterator itr2;
list::iterator itr3;
// clear relations
for(itr2 = m_obj_quests.begin(); itr2 != m_obj_quests.end(); ++itr2)
{
if(!itr2->second)
continue;
itr3 = itr2->second->begin();
for(; itr3 != itr2->second->end(); ++itr3)
{
delete (*itr3);
}
itr2->second->clear();
delete itr2->second;
}
for(itr2 = m_npc_quests.begin(); itr2 != m_npc_quests.end(); ++itr2)
{
if(!itr2->second)
continue;
itr3 = itr2->second->begin();
for(; itr3 != itr2->second->end(); ++itr3)
{
delete (*itr3);
}
itr2->second->clear();
delete itr2->second;
}
for(itr2 = m_itm_quests.begin(); itr2 != m_itm_quests.end(); ++itr2)
{
if(!itr2->second)
continue;
itr3 = itr2->second->begin();
for(; itr3 != itr2->second->end(); ++itr3)
{
delete (*itr3);
}
itr2->second->clear();
delete itr2->second;
}
}
bool QuestMgr::CanStoreReward(Player *plyr, Quest *qst, uint32 reward_slot)
{
uint32 available_slots = 0;
uint32 slotsrequired = 0;
available_slots = plyr->GetItemInterface()->CalculateFreeSlots(NULL);
// Static Item reward
for(uint32 i = 0; i < 4; ++i)
{
if(qst->reward_item[i])
{
slotsrequired++;
ItemPrototype *proto = ItemPrototypeStorage.LookupEntry(qst->reward_item[i]);
if(!proto)
sLog.outError("Invalid item prototype in quest reward! ID %d, quest %d", qst->reward_item[i], qst->id);
else if(plyr->GetItemInterface()->CanReceiveItem(proto, qst->reward_itemcount[i]))
return false;
}
}
// Choice Rewards
if(qst->reward_choiceitem[reward_slot])
{
slotsrequired++;
ItemPrototype *proto = ItemPrototypeStorage.LookupEntry(qst->reward_choiceitem[reward_slot]);
if(!proto)
sLog.outError("Invalid item prototype in quest reward! ID %d, quest %d", qst->reward_choiceitem[reward_slot], qst->id);
else if(plyr->GetItemInterface()->CanReceiveItem(proto, qst->reward_choiceitemcount[reward_slot]))
return false;
}
if(available_slots < slotsrequired)
{
return false;
}
return true;
}
void QuestMgr::LoadExtraQuestStuff()
{
StorageContainerIterator * it = QuestStorage.MakeIterator();
Quest * qst;
while(!it->AtEnd())
{
qst = it->Get();
// 0 them out
qst->count_required_item = qst->count_required_mob = qst->count_requiredtriggers = qst->count_receiveitems =
qst->count_reward_item = qst->count_reward_choiceitem = qst->count_required_item = qst->reward_xp_as_money = 0;
qst->required_mobtype[0] = qst->required_mobtype[1] = qst->required_mobtype[2] = qst->required_mobtype[3] = 0;
qst->count_requiredquests = 0;
for(int i = 0 ; i < 4; ++i)
{
if(qst->required_mob[i])
{
GameObjectInfo *go_info = GameObjectNameStorage.LookupEntry(qst->required_mob[i]);
CreatureInfo *c_info = CreatureNameStorage.LookupEntry(qst->required_mob[i]);
if(go_info && (go_info->Type == 10 || qst->quest_flags == 10 || !c_info))
qst->required_mobtype[i] = QUEST_MOB_TYPE_GAMEOBJECT;
else
qst->required_mobtype[i] = QUEST_MOB_TYPE_CREATURE;
qst->count_required_mob++;
}
if(qst->required_item[i])
qst->count_required_item++;
if(qst->reward_item[i])
qst->count_reward_item++;
if(qst->required_triggers[i])
qst->count_requiredtriggers++;
if(qst->receive_items[i])
qst->count_receiveitems++;
if(qst->required_quests[i])
qst->count_requiredquests++;
}
for(int i = 0; i < 6; ++i)
{
if(qst->reward_choiceitem[i])
qst->count_reward_choiceitem++;
}
if(!it->Inc())
break;
}
it->Destruct();
// load creature starters
uint32 creature, quest;
QueryResult * pResult = WorldDatabase.Query("SELECT * FROM creature_quest_starter");
uint32 pos = 0;
uint32 total;
if(pResult)
{
total = pResult->GetRowCount();
do
{
Field *data = pResult->Fetch();
creature = data[0].GetUInt32();
quest = data[1].GetUInt32();
qst = QuestStorage.LookupEntry(quest);
if(!qst)
{
//printf("Tried to add starter to npc %d for non-existant quest %d.\n", creature, quest);
}
else
{
_AddQuest(creature, qst, 1); // 1 = starter
}
} while(pResult->NextRow());
delete pResult;
}
pResult = WorldDatabase.Query("SELECT * FROM creature_quest_finisher");
pos = 0;
if(pResult)
{
total = pResult->GetRowCount();
do
{
Field *data = pResult->Fetch();
creature = data[0].GetUInt32();
quest = data[1].GetUInt32();
qst = QuestStorage.LookupEntry(quest);
if(!qst)
{
//printf("Tried to add finisher to npc %d for non-existant quest %d.\n", creature, quest);
}
else
{
_AddQuest(creature, qst, 2); // 1 = starter
}
} while(pResult->NextRow());
delete pResult;
}
pResult = WorldDatabase.Query("SELECT * FROM gameobject_quest_starter");
pos = 0;
if(pResult)
{
total = pResult->GetRowCount();
do
{
Field *data = pResult->Fetch();
creature = data[0].GetUInt32();
quest = data[1].GetUInt32();
qst = QuestStorage.LookupEntry(quest);
if(!qst)
{
//printf("Tried to add starter to go %d for non-existant quest %d.\n", creature, quest);
}
else
{
_AddQuest(creature, qst, 1); // 1 = starter
}
} while(pResult->NextRow());
delete pResult;
}
pResult = WorldDatabase.Query("SELECT * FROM gameobject_quest_finisher");
pos = 0;
if(pResult)
{
total = pResult->GetRowCount();
do
{
Field *data = pResult->Fetch();
creature = data[0].GetUInt32();
quest = data[1].GetUInt32();
qst = QuestStorage.LookupEntry(quest);
if(!qst)
{
//printf("Tried to add finisher to go %d for non-existant quest %d.\n", creature, quest);
}
else
{
_AddQuest(creature, qst, 2); // 2 = finish
}
} while(pResult->NextRow());
delete pResult;
}
objmgr.ProcessGameobjectQuests();
}