2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "WorldPacket.h"
22 #include "WorldSession.h"
25 #include "ObjectMgr.h"
27 #include "GossipDef.h"
29 #include "ObjectAccessor.h"
30 #include "ScriptCalls.h"
33 void WorldSession::HandleQuestgiverStatusQueryOpcode( WorldPacket
& recv_data
)
37 uint8 questStatus
= DIALOG_STATUS_NONE
;
38 uint8 defstatus
= DIALOG_STATUS_NONE
;
40 Object
* questgiver
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
);
43 sLog
.outDetail("Error in CMSG_QUESTGIVER_STATUS_QUERY, called for not found questgiver (Typeid: %u GUID: %u)",GuidHigh2TypeId(GUID_HIPART(guid
)),GUID_LOPART(guid
));
47 switch(questgiver
->GetTypeId())
51 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for npc, guid = %u",uint32(GUID_LOPART(guid
)) );
52 Creature
* cr_questgiver
=(Creature
*)questgiver
;
53 if( !cr_questgiver
->IsHostileTo(_player
)) // not show quest status to enemies
55 questStatus
= Script
->NPCDialogStatus(_player
, cr_questgiver
);
57 questStatus
= getDialogStatus(_player
, cr_questgiver
, defstatus
);
61 case TYPEID_GAMEOBJECT
:
63 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u",uint32(GUID_LOPART(guid
)) );
64 GameObject
* go_questgiver
=(GameObject
*)questgiver
;
65 questStatus
= Script
->GODialogStatus(_player
, go_questgiver
);
67 questStatus
= getDialogStatus(_player
, go_questgiver
, defstatus
);
71 sLog
.outError("QuestGiver called for unexpected type %u", questgiver
->GetTypeId());
75 //inform client about status of quest
76 _player
->PlayerTalkClass
->SendQuestGiverStatus(questStatus
, guid
);
79 void WorldSession::HandleQuestgiverHelloOpcode( WorldPacket
& recv_data
)
84 sLog
.outDebug ("WORLD: Received CMSG_QUESTGIVER_HELLO npc = %u", GUID_LOPART(guid
));
86 Creature
*pCreature
= GetPlayer()->GetNPCIfCanInteractWith(guid
,UNIT_NPC_FLAG_NONE
);
89 sLog
.outDebug ("WORLD: HandleQuestgiverHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.",
95 if(GetPlayer()->hasUnitState(UNIT_STAT_DIED
))
96 GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH
);
97 // Stop the npc if moving
98 pCreature
->StopMoving();
100 if(Script
->GossipHello( _player
, pCreature
) )
103 pCreature
->prepareGossipMenu(_player
);
104 pCreature
->sendPreparedGossip(_player
);
107 void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket
& recv_data
)
112 recv_data
>> guid
>> quest
>> unk1
;
114 if(!GetPlayer()->isAlive())
117 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid
)), quest
, unk1
);
119 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
|TYPEMASK_ITEM
|TYPEMASK_PLAYER
);
121 // no or incorrect quest giver
123 || (pObject
->GetTypeId()!=TYPEID_PLAYER
&& !pObject
->hasQuest(quest
))
124 || (pObject
->GetTypeId()==TYPEID_PLAYER
&& !((Player
*)pObject
)->CanShareQuest(quest
))
127 _player
->PlayerTalkClass
->CloseGossip();
128 _player
->SetDivider( 0 );
132 Quest
const* qInfo
= objmgr
.GetQuestTemplate(quest
);
136 if(!GetPlayer()->CanTakeQuest(qInfo
,true) )
138 _player
->PlayerTalkClass
->CloseGossip();
139 _player
->SetDivider( 0 );
143 if( _player
->GetDivider() != 0 )
145 Player
*pPlayer
= ObjectAccessor::FindPlayer( _player
->GetDivider() );
148 pPlayer
->SendPushToPartyResponse( _player
, QUEST_PARTY_MSG_ACCEPT_QUEST
);
149 _player
->SetDivider( 0 );
153 if( _player
->CanAddQuest( qInfo
, true ) )
155 _player
->AddQuest( qInfo
, pObject
);
157 if ( _player
->CanCompleteQuest( quest
) )
158 _player
->CompleteQuest( quest
);
160 switch(pObject
->GetTypeId())
163 Script
->QuestAccept(_player
, ((Creature
*)pObject
), qInfo
);
166 case TYPEID_CONTAINER
:
168 Script
->ItemQuestAccept(_player
, ((Item
*)pObject
), qInfo
);
170 // destroy not required for quest finish quest starting item
171 bool destroyItem
= true;
172 for(int i
= 0; i
< QUEST_OBJECTIVES_COUNT
; ++i
)
174 if ((qInfo
->ReqItemId
[i
] == ((Item
*)pObject
)->GetEntry()) && (((Item
*)pObject
)->GetProto()->MaxCount
> 0))
182 _player
->DestroyItem(((Item
*)pObject
)->GetBagSlot(),((Item
*)pObject
)->GetSlot(),true);
186 case TYPEID_GAMEOBJECT
:
187 Script
->GOQuestAccept(_player
, ((GameObject
*)pObject
), qInfo
);
190 _player
->PlayerTalkClass
->CloseGossip();
192 if( qInfo
->GetSrcSpell() > 0 )
193 _player
->CastSpell( _player
, qInfo
->GetSrcSpell(), true);
199 _player
->PlayerTalkClass
->CloseGossip();
202 void WorldSession::HandleQuestgiverQueryQuestOpcode( WorldPacket
& recv_data
)
207 recv_data
>> guid
>> quest
>> unk1
;
208 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid
)), quest
, unk1
);
210 // Verify that the guid is valid and is a questgiver or involved in the requested quest
211 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
|TYPEMASK_ITEM
);
212 if(!pObject
||!pObject
->hasQuest(quest
) && !pObject
->hasInvolvedQuest(quest
))
214 _player
->PlayerTalkClass
->CloseGossip();
218 Quest
const* pQuest
= objmgr
.GetQuestTemplate(quest
);
221 _player
->PlayerTalkClass
->SendQuestGiverQuestDetails(pQuest
, pObject
->GetGUID(), true);
225 void WorldSession::HandleQuestQueryOpcode( WorldPacket
& recv_data
)
229 sLog
.outDebug( "WORLD: Received CMSG_QUEST_QUERY quest = %u",quest
);
231 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
234 _player
->PlayerTalkClass
->SendQuestQueryResponse( pQuest
);
238 void WorldSession::HandleQuestgiverChooseRewardOpcode( WorldPacket
& recv_data
)
240 uint32 quest
, reward
;
242 recv_data
>> guid
>> quest
>> reward
;
244 if(reward
>= QUEST_REWARD_CHOICES_COUNT
)
246 sLog
.outError("Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player %s (guid %d) tried to get invalid reward (%u) (probably packet hacking)", _player
->GetName(), _player
->GetGUIDLow(), reward
);
250 if(!GetPlayer()->isAlive())
253 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_CHOOSE_REWARD npc = %u, quest = %u, reward = %u",uint32(GUID_LOPART(guid
)),quest
,reward
);
255 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
);
259 if(!pObject
->hasInvolvedQuest(quest
))
262 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
265 if( _player
->CanRewardQuest( pQuest
, reward
, true ) )
267 _player
->RewardQuest( pQuest
, reward
, pObject
);
269 switch(pObject
->GetTypeId())
272 if( !(Script
->ChooseReward( _player
, ((Creature
*)pObject
), pQuest
, reward
)) )
275 if(Quest
const* nextquest
= _player
->GetNextQuest( guid
,pQuest
) )
276 _player
->PlayerTalkClass
->SendQuestGiverQuestDetails(nextquest
,guid
,true);
279 case TYPEID_GAMEOBJECT
:
280 if( !Script
->GOChooseReward( _player
, ((GameObject
*)pObject
), pQuest
, reward
) )
283 if(Quest
const* nextquest
= _player
->GetNextQuest( guid
,pQuest
) )
284 _player
->PlayerTalkClass
->SendQuestGiverQuestDetails(nextquest
,guid
,true);
290 _player
->PlayerTalkClass
->SendQuestGiverOfferReward( pQuest
, guid
, true );
294 void WorldSession::HandleQuestgiverRequestRewardOpcode( WorldPacket
& recv_data
)
298 recv_data
>> guid
>> quest
;
300 if(!GetPlayer()->isAlive())
303 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = %u, quest = %u",uint32(GUID_LOPART(guid
)),quest
);
305 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
);
306 if(!pObject
||!pObject
->hasInvolvedQuest(quest
))
309 if ( _player
->CanCompleteQuest( quest
) )
310 _player
->CompleteQuest( quest
);
312 if( _player
->GetQuestStatus( quest
) != QUEST_STATUS_COMPLETE
)
315 if(Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
))
316 _player
->PlayerTalkClass
->SendQuestGiverOfferReward( pQuest
, guid
, true );
319 void WorldSession::HandleQuestgiverCancel(WorldPacket
& /*recv_data*/ )
321 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_CANCEL" );
323 _player
->PlayerTalkClass
->CloseGossip();
326 void WorldSession::HandleQuestLogSwapQuest(WorldPacket
& recv_data
)
329 recv_data
>> slot1
>> slot2
;
331 if(slot1
== slot2
|| slot1
>= MAX_QUEST_LOG_SIZE
|| slot2
>= MAX_QUEST_LOG_SIZE
)
334 sLog
.outDebug( "WORLD: Received CMSG_QUESTLOG_SWAP_QUEST slot 1 = %u, slot 2 = %u", slot1
, slot2
);
336 GetPlayer()->SwapQuestSlot(slot1
,slot2
);
339 void WorldSession::HandleQuestLogRemoveQuest(WorldPacket
& recv_data
)
344 sLog
.outDebug( "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = %u",slot
);
346 if( slot
< MAX_QUEST_LOG_SIZE
)
348 if(uint32 quest
= _player
->GetQuestSlotQuestId(slot
))
350 if(!_player
->TakeQuestSourceItem( quest
, true ))
351 return; // can't un-equip some items, reject quest cancel
353 _player
->SetQuestStatus( quest
, QUEST_STATUS_NONE
);
356 _player
->SetQuestSlot(slot
, 0);
358 _player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED
, 1);
362 void WorldSession::HandleQuestConfirmAccept(WorldPacket
& recv_data
)
367 sLog
.outDebug( "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u",quest
);
370 void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket
& recv_data
)
374 recv_data
>> guid
>> quest
;
376 if(!GetPlayer()->isAlive())
379 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid
)),quest
);
381 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
384 if( _player
->GetQuestStatus( quest
) != QUEST_STATUS_COMPLETE
)
386 if( pQuest
->IsRepeatable() )
387 _player
->PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, _player
->CanCompleteRepeatableQuest(pQuest
), false);
389 _player
->PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, _player
->CanRewardQuest(pQuest
,false), false);
393 if(pQuest
->GetReqItemsCount()) // some items required
394 _player
->PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, _player
->CanRewardQuest(pQuest
,false), false);
395 else // no items required
396 _player
->PlayerTalkClass
->SendQuestGiverOfferReward(pQuest
, guid
, true);
401 void WorldSession::HandleQuestgiverQuestAutoLaunch(WorldPacket
& /*recvPacket*/)
403 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUEST_AUTOLAUNCH" );
406 void WorldSession::HandlePushQuestToParty(WorldPacket
& recvPacket
)
409 recvPacket
>> questId
;
411 sLog
.outDebug("WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", questId
);
413 if (Quest
const *pQuest
= objmgr
.GetQuestTemplate(questId
))
415 if (Group
* pGroup
= _player
->GetGroup())
417 for(GroupReference
*itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
419 Player
*pPlayer
= itr
->getSource();
421 if (!pPlayer
|| pPlayer
== _player
) // skip self
424 _player
->SendPushToPartyResponse(pPlayer
, QUEST_PARTY_MSG_SHARING_QUEST
);
426 if (!pPlayer
->SatisfyQuestStatus(pQuest
, false))
428 _player
->SendPushToPartyResponse(pPlayer
, QUEST_PARTY_MSG_HAVE_QUEST
);
432 if (pPlayer
->GetQuestStatus(questId
) == QUEST_STATUS_COMPLETE
)
434 _player
->SendPushToPartyResponse(pPlayer
, QUEST_PARTY_MSG_FINISH_QUEST
);
438 if (!pPlayer
->CanTakeQuest(pQuest
, false))
440 _player
->SendPushToPartyResponse(pPlayer
, QUEST_PARTY_MSG_CANT_TAKE_QUEST
);
444 if (!pPlayer
->SatisfyQuestLog(false))
446 _player
->SendPushToPartyResponse(pPlayer
, QUEST_PARTY_MSG_LOG_FULL
);
450 if (pPlayer
->GetDivider() != 0)
452 _player
->SendPushToPartyResponse(pPlayer
, QUEST_PARTY_MSG_BUSY
);
456 pPlayer
->PlayerTalkClass
->SendQuestGiverQuestDetails(pQuest
, _player
->GetGUID(), true);
457 pPlayer
->SetDivider(_player
->GetGUID());
463 void WorldSession::HandleQuestPushResult(WorldPacket
& recvPacket
)
467 recvPacket
>> guid
>> msg
;
469 sLog
.outDebug( "WORLD: Received MSG_QUEST_PUSH_RESULT" );
471 if( _player
->GetDivider() != 0 )
473 Player
*pPlayer
= ObjectAccessor::FindPlayer( _player
->GetDivider() );
476 WorldPacket
data( MSG_QUEST_PUSH_RESULT
, (8+1) );
477 data
<< uint64(guid
);
478 data
<< uint8(msg
); // valid values: 0-8
479 pPlayer
->GetSession()->SendPacket(&data
);
480 _player
->SetDivider( 0 );
485 uint32
WorldSession::getDialogStatus(Player
*pPlayer
, Object
* questgiver
, uint32 defstatus
)
487 uint32 result
= defstatus
;
489 QuestRelations
const* qir
;
490 QuestRelations
const* qr
;
492 switch(questgiver
->GetTypeId())
494 case TYPEID_GAMEOBJECT
:
496 qir
= &objmgr
.mGOQuestInvolvedRelations
;
497 qr
= &objmgr
.mGOQuestRelations
;
502 qir
= &objmgr
.mCreatureQuestInvolvedRelations
;
503 qr
= &objmgr
.mCreatureQuestRelations
;
507 //its imposible, but check ^)
508 sLog
.outError("Warning: GetDialogStatus called for unexpected type %u", questgiver
->GetTypeId());
509 return DIALOG_STATUS_NONE
;
512 for(QuestRelations::const_iterator i
= qir
->lower_bound(questgiver
->GetEntry()); i
!= qir
->upper_bound(questgiver
->GetEntry()); ++i
)
515 uint32 quest_id
= i
->second
;
516 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest_id
);
517 if ( !pQuest
) continue;
519 QuestStatus status
= pPlayer
->GetQuestStatus( quest_id
);
520 if( (status
== QUEST_STATUS_COMPLETE
&& !pPlayer
->GetQuestRewardStatus(quest_id
)) ||
521 (pQuest
->IsAutoComplete() && pPlayer
->CanTakeQuest(pQuest
, false)) )
523 if ( pQuest
->IsAutoComplete() && pQuest
->IsRepeatable() )
524 result2
= DIALOG_STATUS_REWARD_REP
;
526 result2
= DIALOG_STATUS_REWARD
;
528 else if ( status
== QUEST_STATUS_INCOMPLETE
)
529 result2
= DIALOG_STATUS_INCOMPLETE
;
531 if (result2
> result
)
535 for(QuestRelations::const_iterator i
= qr
->lower_bound(questgiver
->GetEntry()); i
!= qr
->upper_bound(questgiver
->GetEntry()); ++i
)
538 uint32 quest_id
= i
->second
;
539 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest_id
);
543 QuestStatus status
= pPlayer
->GetQuestStatus( quest_id
);
544 if ( status
== QUEST_STATUS_NONE
)
546 if ( pPlayer
->CanSeeStartQuest( pQuest
) )
548 if ( pPlayer
->SatisfyQuestLevel(pQuest
, false) )
550 if ( pQuest
->IsAutoComplete() || (pQuest
->IsRepeatable() && pPlayer
->getQuestStatusMap()[quest_id
].m_rewarded
))
551 result2
= DIALOG_STATUS_REWARD_REP
;
552 else if (pPlayer
->getLevel() <= pPlayer
->GetQuestLevel(pQuest
) + sWorld
.getConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF
) )
554 if (pQuest
->HasFlag(QUEST_FLAGS_DAILY
))
555 result2
= DIALOG_STATUS_AVAILABLE_REP
;
557 result2
= DIALOG_STATUS_AVAILABLE
;
560 result2
= DIALOG_STATUS_CHAT
;
563 result2
= DIALOG_STATUS_UNAVAILABLE
;
567 if (result2
> result
)
574 void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket
& /*recvPacket*/)
576 sLog
.outDebug("WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY");
580 WorldPacket
data(SMSG_QUESTGIVER_STATUS_MULTIPLE
, 4);
581 data
<< uint32(count
); // placeholder
583 for(Player::ClientGUIDs::const_iterator itr
= _player
->m_clientGUIDs
.begin(); itr
!= _player
->m_clientGUIDs
.end(); ++itr
)
585 uint8 questStatus
= DIALOG_STATUS_NONE
;
586 uint8 defstatus
= DIALOG_STATUS_NONE
;
588 if (IS_CREATURE_OR_PET_GUID(*itr
))
590 // need also pet quests case support
591 Creature
*questgiver
= ObjectAccessor::GetCreatureOrPetOrVehicle(*GetPlayer(),*itr
);
592 if(!questgiver
|| questgiver
->IsHostileTo(_player
))
594 if(!questgiver
->HasFlag(UNIT_NPC_FLAGS
, UNIT_NPC_FLAG_QUESTGIVER
))
596 questStatus
= Script
->NPCDialogStatus(_player
, questgiver
);
597 if( questStatus
> 6 )
598 questStatus
= getDialogStatus(_player
, questgiver
, defstatus
);
600 data
<< uint64(questgiver
->GetGUID());
601 data
<< uint8(questStatus
);
604 else if(IS_GAMEOBJECT_GUID(*itr
))
606 GameObject
*questgiver
= GetPlayer()->GetMap()->GetGameObject(*itr
);
609 if(questgiver
->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER
)
611 questStatus
= Script
->GODialogStatus(_player
, questgiver
);
612 if( questStatus
> 6 )
613 questStatus
= getDialogStatus(_player
, questgiver
, defstatus
);
615 data
<< uint64(questgiver
->GetGUID());
616 data
<< uint8(questStatus
);
621 data
.put
<uint32
>(0, count
); // write real count