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
)
35 CHECK_PACKET_SIZE(recv_data
,8);
39 uint8 questStatus
= DIALOG_STATUS_NONE
;
40 uint8 defstatus
= DIALOG_STATUS_NONE
;
42 Object
* questgiver
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
);
45 sLog
.outDetail("Error in CMSG_QUESTGIVER_STATUS_QUERY, called for not found questgiver (Typeid: %u GUID: %u)",GuidHigh2TypeId(GUID_HIPART(guid
)),GUID_LOPART(guid
));
49 switch(questgiver
->GetTypeId())
53 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for npc, guid = %u",uint32(GUID_LOPART(guid
)) );
54 Creature
* cr_questgiver
=(Creature
*)questgiver
;
55 if( !cr_questgiver
->IsHostileTo(_player
)) // not show quest status to enemies
57 questStatus
= Script
->NPCDialogStatus(_player
, cr_questgiver
);
59 questStatus
= getDialogStatus(_player
, cr_questgiver
, defstatus
);
63 case TYPEID_GAMEOBJECT
:
65 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u",uint32(GUID_LOPART(guid
)) );
66 GameObject
* go_questgiver
=(GameObject
*)questgiver
;
67 questStatus
= Script
->GODialogStatus(_player
, go_questgiver
);
69 questStatus
= getDialogStatus(_player
, go_questgiver
, defstatus
);
73 sLog
.outError("QuestGiver called for unexpected type %u", questgiver
->GetTypeId());
77 //inform client about status of quest
78 _player
->PlayerTalkClass
->SendQuestGiverStatus(questStatus
, guid
);
81 void WorldSession::HandleQuestgiverHelloOpcode( WorldPacket
& recv_data
)
83 CHECK_PACKET_SIZE(recv_data
,8);
88 sLog
.outDebug ("WORLD: Received CMSG_QUESTGIVER_HELLO npc = %u", GUID_LOPART(guid
));
90 Creature
*pCreature
= GetPlayer()->GetNPCIfCanInteractWith(guid
,UNIT_NPC_FLAG_NONE
);
93 sLog
.outDebug ("WORLD: HandleQuestgiverHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.",
99 if(GetPlayer()->hasUnitState(UNIT_STAT_DIED
))
100 GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH
);
101 // Stop the npc if moving
102 pCreature
->StopMoving();
104 if(Script
->GossipHello( _player
, pCreature
) )
107 pCreature
->prepareGossipMenu(_player
);
108 pCreature
->sendPreparedGossip(_player
);
111 void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket
& recv_data
)
113 CHECK_PACKET_SIZE(recv_data
,8+4+4);
118 recv_data
>> guid
>> quest
>> unk1
;
120 if(!GetPlayer()->isAlive())
123 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %u, quest = %u, unk1 = %u",uint32(GUID_LOPART(guid
)), quest
, unk1
);
125 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
|TYPEMASK_ITEM
|TYPEMASK_PLAYER
);
127 // no or incorrect quest giver
129 || (pObject
->GetTypeId()!=TYPEID_PLAYER
&& !pObject
->hasQuest(quest
))
130 || (pObject
->GetTypeId()==TYPEID_PLAYER
&& !((Player
*)pObject
)->CanShareQuest(quest
))
133 _player
->PlayerTalkClass
->CloseGossip();
134 _player
->SetDivider( 0 );
138 Quest
const* qInfo
= objmgr
.GetQuestTemplate(quest
);
142 if(!GetPlayer()->CanTakeQuest(qInfo
,true) )
144 _player
->PlayerTalkClass
->CloseGossip();
145 _player
->SetDivider( 0 );
149 if( _player
->GetDivider() != 0 )
151 Player
*pPlayer
= ObjectAccessor::FindPlayer( _player
->GetDivider() );
154 pPlayer
->SendPushToPartyResponse( _player
, QUEST_PARTY_MSG_ACCEPT_QUEST
);
155 _player
->SetDivider( 0 );
159 if( _player
->CanAddQuest( qInfo
, true ) )
161 _player
->AddQuest( qInfo
, pObject
);
163 if ( _player
->CanCompleteQuest( quest
) )
164 _player
->CompleteQuest( quest
);
166 switch(pObject
->GetTypeId())
169 Script
->QuestAccept(_player
, ((Creature
*)pObject
), qInfo
);
172 case TYPEID_CONTAINER
:
174 Script
->ItemQuestAccept(_player
, ((Item
*)pObject
), qInfo
);
176 // destroy not required for quest finish quest starting item
177 bool destroyItem
= true;
178 for(int i
= 0; i
< QUEST_OBJECTIVES_COUNT
; ++i
)
180 if ((qInfo
->ReqItemId
[i
] == ((Item
*)pObject
)->GetEntry()) && (((Item
*)pObject
)->GetProto()->MaxCount
> 0))
188 _player
->DestroyItem(((Item
*)pObject
)->GetBagSlot(),((Item
*)pObject
)->GetSlot(),true);
192 case TYPEID_GAMEOBJECT
:
193 Script
->GOQuestAccept(_player
, ((GameObject
*)pObject
), qInfo
);
196 _player
->PlayerTalkClass
->CloseGossip();
198 if( qInfo
->GetSrcSpell() > 0 )
199 _player
->CastSpell( _player
, qInfo
->GetSrcSpell(), true);
205 _player
->PlayerTalkClass
->CloseGossip();
208 void WorldSession::HandleQuestgiverQueryQuestOpcode( WorldPacket
& recv_data
)
210 CHECK_PACKET_SIZE(recv_data
,8+4+1);
215 recv_data
>> guid
>> quest
>> unk1
;
216 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST npc = %u, quest = %u, unk1 = %u",uint32(GUID_LOPART(guid
)), quest
, unk1
);
218 // Verify that the guid is valid and is a questgiver or involved in the requested quest
219 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
|TYPEMASK_ITEM
);
220 if(!pObject
||!pObject
->hasQuest(quest
) && !pObject
->hasInvolvedQuest(quest
))
222 _player
->PlayerTalkClass
->CloseGossip();
226 Quest
const* pQuest
= objmgr
.GetQuestTemplate(quest
);
229 _player
->PlayerTalkClass
->SendQuestGiverQuestDetails(pQuest
, pObject
->GetGUID(), true);
233 void WorldSession::HandleQuestQueryOpcode( WorldPacket
& recv_data
)
235 CHECK_PACKET_SIZE(recv_data
,4);
239 sLog
.outDebug( "WORLD: Received CMSG_QUEST_QUERY quest = %u",quest
);
241 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
244 _player
->PlayerTalkClass
->SendQuestQueryResponse( pQuest
);
248 void WorldSession::HandleQuestgiverChooseRewardOpcode( WorldPacket
& recv_data
)
250 CHECK_PACKET_SIZE(recv_data
,8+4+4);
252 uint32 quest
, reward
;
254 recv_data
>> guid
>> quest
>> reward
;
256 if(reward
>= QUEST_REWARD_CHOICES_COUNT
)
258 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
);
262 if(!GetPlayer()->isAlive())
265 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_CHOOSE_REWARD npc = %u, quest = %u, reward = %u",uint32(GUID_LOPART(guid
)),quest
,reward
);
267 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
);
271 if(!pObject
->hasInvolvedQuest(quest
))
274 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
277 if( _player
->CanRewardQuest( pQuest
, reward
, true ) )
279 _player
->RewardQuest( pQuest
, reward
, pObject
);
281 switch(pObject
->GetTypeId())
284 if( !(Script
->ChooseReward( _player
, ((Creature
*)pObject
), pQuest
, reward
)) )
287 if(Quest
const* nextquest
= _player
->GetNextQuest( guid
,pQuest
) )
288 _player
->PlayerTalkClass
->SendQuestGiverQuestDetails(nextquest
,guid
,true);
291 case TYPEID_GAMEOBJECT
:
292 if( !Script
->GOChooseReward( _player
, ((GameObject
*)pObject
), pQuest
, reward
) )
295 if(Quest
const* nextquest
= _player
->GetNextQuest( guid
,pQuest
) )
296 _player
->PlayerTalkClass
->SendQuestGiverQuestDetails(nextquest
,guid
,true);
302 _player
->PlayerTalkClass
->SendQuestGiverOfferReward( pQuest
, guid
, true );
306 void WorldSession::HandleQuestgiverRequestRewardOpcode( WorldPacket
& recv_data
)
308 CHECK_PACKET_SIZE(recv_data
,8+4);
312 recv_data
>> guid
>> quest
;
314 if(!GetPlayer()->isAlive())
317 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = %u, quest = %u",uint32(GUID_LOPART(guid
)),quest
);
319 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
);
320 if(!pObject
||!pObject
->hasInvolvedQuest(quest
))
323 if ( _player
->CanCompleteQuest( quest
) )
324 _player
->CompleteQuest( quest
);
326 if( _player
->GetQuestStatus( quest
) != QUEST_STATUS_COMPLETE
)
329 if(Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
))
330 _player
->PlayerTalkClass
->SendQuestGiverOfferReward( pQuest
, guid
, true );
333 void WorldSession::HandleQuestgiverCancel(WorldPacket
& /*recv_data*/ )
335 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_CANCEL" );
337 _player
->PlayerTalkClass
->CloseGossip();
340 void WorldSession::HandleQuestLogSwapQuest(WorldPacket
& recv_data
)
342 CHECK_PACKET_SIZE(recv_data
,1+1);
345 recv_data
>> slot1
>> slot2
;
347 if(slot1
== slot2
|| slot1
>= MAX_QUEST_LOG_SIZE
|| slot2
>= MAX_QUEST_LOG_SIZE
)
350 sLog
.outDebug( "WORLD: Received CMSG_QUESTLOG_SWAP_QUEST slot 1 = %u, slot 2 = %u", slot1
, slot2
);
352 GetPlayer()->SwapQuestSlot(slot1
,slot2
);
355 void WorldSession::HandleQuestLogRemoveQuest(WorldPacket
& recv_data
)
357 CHECK_PACKET_SIZE(recv_data
,1);
362 sLog
.outDebug( "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = %u",slot
);
364 if( slot
< MAX_QUEST_LOG_SIZE
)
366 if(uint32 quest
= _player
->GetQuestSlotQuestId(slot
))
368 if(!_player
->TakeQuestSourceItem( quest
, true ))
369 return; // can't un-equip some items, reject quest cancel
371 _player
->SetQuestStatus( quest
, QUEST_STATUS_NONE
);
374 _player
->SetQuestSlot(slot
, 0);
376 _player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED
, 1);
380 void WorldSession::HandleQuestConfirmAccept(WorldPacket
& recv_data
)
382 CHECK_PACKET_SIZE(recv_data
,4);
387 sLog
.outDebug( "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u",quest
);
390 void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket
& recv_data
)
392 CHECK_PACKET_SIZE(recv_data
,8+4);
396 recv_data
>> guid
>> quest
;
398 if(!GetPlayer()->isAlive())
401 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid
)),quest
);
403 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
406 if( _player
->GetQuestStatus( quest
) != QUEST_STATUS_COMPLETE
)
408 if( pQuest
->IsRepeatable() )
409 _player
->PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, _player
->CanCompleteRepeatableQuest(pQuest
), false);
411 _player
->PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, _player
->CanRewardQuest(pQuest
,false), false);
415 if(pQuest
->GetReqItemsCount()) // some items required
416 _player
->PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, _player
->CanRewardQuest(pQuest
,false), false);
417 else // no items required
418 _player
->PlayerTalkClass
->SendQuestGiverOfferReward(pQuest
, guid
, true);
423 void WorldSession::HandleQuestgiverQuestAutoLaunch(WorldPacket
& /*recvPacket*/)
425 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUEST_AUTOLAUNCH" );
428 void WorldSession::HandlePushQuestToParty(WorldPacket
& recvPacket
)
430 CHECK_PACKET_SIZE(recvPacket
,4);
435 sLog
.outDebug( "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", quest
);
437 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
440 if( _player
->GetGroup() )
442 Group
*pGroup
= _player
->GetGroup();
445 for(GroupReference
*itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
447 Player
*pPlayer
= itr
->getSource();
448 if (!pPlayer
|| pPlayer
== _player
) // skip self
451 _player
->SendPushToPartyResponse(pPlayer
, QUEST_PARTY_MSG_SHARING_QUEST
);
453 if( !pPlayer
->SatisfyQuestStatus( pQuest
, false ) )
455 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_HAVE_QUEST
);
459 if( pPlayer
->GetQuestStatus( quest
) == QUEST_STATUS_COMPLETE
)
461 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_FINISH_QUEST
);
465 if( !pPlayer
->CanTakeQuest( pQuest
, false ) )
467 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_CANT_TAKE_QUEST
);
471 if( !pPlayer
->SatisfyQuestLog( false ) )
473 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_LOG_FULL
);
477 if( pPlayer
->GetDivider() != 0 )
479 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_BUSY
);
483 pPlayer
->PlayerTalkClass
->SendQuestGiverQuestDetails( pQuest
, _player
->GetGUID(), true );
484 pPlayer
->SetDivider( _player
->GetGUID() );
491 void WorldSession::HandleQuestPushResult(WorldPacket
& recvPacket
)
493 CHECK_PACKET_SIZE(recvPacket
,8+1);
497 recvPacket
>> guid
>> msg
;
499 sLog
.outDebug( "WORLD: Received MSG_QUEST_PUSH_RESULT" );
501 if( _player
->GetDivider() != 0 )
503 Player
*pPlayer
= ObjectAccessor::FindPlayer( _player
->GetDivider() );
506 WorldPacket
data( MSG_QUEST_PUSH_RESULT
, (8+1) );
507 data
<< uint64(guid
);
508 data
<< uint8(msg
); // valid values: 0-8
509 pPlayer
->GetSession()->SendPacket(&data
);
510 _player
->SetDivider( 0 );
515 uint32
WorldSession::getDialogStatus(Player
*pPlayer
, Object
* questgiver
, uint32 defstatus
)
517 uint32 result
= defstatus
;
519 QuestRelations
const* qir
;
520 QuestRelations
const* qr
;
522 switch(questgiver
->GetTypeId())
524 case TYPEID_GAMEOBJECT
:
526 qir
= &objmgr
.mGOQuestInvolvedRelations
;
527 qr
= &objmgr
.mGOQuestRelations
;
532 qir
= &objmgr
.mCreatureQuestInvolvedRelations
;
533 qr
= &objmgr
.mCreatureQuestRelations
;
537 //its imposible, but check ^)
538 sLog
.outError("Warning: GetDialogStatus called for unexpected type %u", questgiver
->GetTypeId());
539 return DIALOG_STATUS_NONE
;
542 for(QuestRelations::const_iterator i
= qir
->lower_bound(questgiver
->GetEntry()); i
!= qir
->upper_bound(questgiver
->GetEntry()); ++i
)
545 uint32 quest_id
= i
->second
;
546 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest_id
);
547 if ( !pQuest
) continue;
549 QuestStatus status
= pPlayer
->GetQuestStatus( quest_id
);
550 if( (status
== QUEST_STATUS_COMPLETE
&& !pPlayer
->GetQuestRewardStatus(quest_id
)) ||
551 (pQuest
->IsAutoComplete() && pPlayer
->CanTakeQuest(pQuest
, false)) )
553 if ( pQuest
->IsAutoComplete() && pQuest
->IsRepeatable() )
554 result2
= DIALOG_STATUS_REWARD_REP
;
556 result2
= DIALOG_STATUS_REWARD
;
558 else if ( status
== QUEST_STATUS_INCOMPLETE
)
559 result2
= DIALOG_STATUS_INCOMPLETE
;
561 if (result2
> result
)
565 for(QuestRelations::const_iterator i
= qr
->lower_bound(questgiver
->GetEntry()); i
!= qr
->upper_bound(questgiver
->GetEntry()); ++i
)
568 uint32 quest_id
= i
->second
;
569 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest_id
);
573 QuestStatus status
= pPlayer
->GetQuestStatus( quest_id
);
574 if ( status
== QUEST_STATUS_NONE
)
576 if ( pPlayer
->CanSeeStartQuest( pQuest
) )
578 if ( pPlayer
->SatisfyQuestLevel(pQuest
, false) )
580 if ( pQuest
->IsAutoComplete() || (pQuest
->IsRepeatable() && pPlayer
->getQuestStatusMap()[quest_id
].m_rewarded
))
581 result2
= DIALOG_STATUS_REWARD_REP
;
582 else if (pPlayer
->getLevel() <= pPlayer
->GetQuestLevel(pQuest
) + sWorld
.getConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF
) )
584 if (pQuest
->HasFlag(QUEST_FLAGS_DAILY
))
585 result2
= DIALOG_STATUS_AVAILABLE_REP
;
587 result2
= DIALOG_STATUS_AVAILABLE
;
590 result2
= DIALOG_STATUS_CHAT
;
593 result2
= DIALOG_STATUS_UNAVAILABLE
;
597 if (result2
> result
)
601 if(questgiver
->GetTypeId()==TYPEID_UNIT
&& ((Creature
*)questgiver
)->isCanTrainingAndResetTalentsOf(pPlayer
) && result
< DIALOG_STATUS_CHAT
)
602 result
= DIALOG_STATUS_CHAT
;
607 void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket
& /*recvPacket*/)
609 sLog
.outDebug("WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY");
613 WorldPacket
data(SMSG_QUESTGIVER_STATUS_MULTIPLE
, 4);
614 data
<< uint32(count
); // placeholder
616 for(Player::ClientGUIDs::const_iterator itr
= _player
->m_clientGUIDs
.begin(); itr
!= _player
->m_clientGUIDs
.end(); ++itr
)
618 uint8 questStatus
= DIALOG_STATUS_NONE
;
619 uint8 defstatus
= DIALOG_STATUS_NONE
;
621 if (IS_CREATURE_OR_PET_GUID(*itr
))
623 // need also pet quests case support
624 Creature
*questgiver
= ObjectAccessor::GetCreatureOrPetOrVehicle(*GetPlayer(),*itr
);
625 if(!questgiver
|| questgiver
->IsHostileTo(_player
))
627 if(!questgiver
->HasFlag(UNIT_NPC_FLAGS
, UNIT_NPC_FLAG_QUESTGIVER
))
629 questStatus
= Script
->NPCDialogStatus(_player
, questgiver
);
630 if( questStatus
> 6 )
631 questStatus
= getDialogStatus(_player
, questgiver
, defstatus
);
633 data
<< uint64(questgiver
->GetGUID());
634 data
<< uint8(questStatus
);
637 else if(IS_GAMEOBJECT_GUID(*itr
))
639 GameObject
*questgiver
= GetPlayer()->GetMap()->GetGameObject(*itr
);
642 if(questgiver
->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER
)
644 questStatus
= Script
->GODialogStatus(_player
, questgiver
);
645 if( questStatus
> 6 )
646 questStatus
= getDialogStatus(_player
, questgiver
, defstatus
);
648 data
<< uint64(questgiver
->GetGUID());
649 data
<< uint8(questStatus
);
654 data
.put
<uint32
>(0, count
); // write real count