2 * Copyright (C) 2005-2008 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
= ObjectAccessor::GetNPCIfCanInteractWith(*_player
, 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);
117 recv_data
>> guid
>> quest
;
119 if(!GetPlayer()->isAlive())
122 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid
)),quest
);
124 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
|TYPEMASK_ITEM
|TYPEMASK_PLAYER
);
126 // no or incorrect quest giver
128 || (pObject
->GetTypeId()!=TYPEID_PLAYER
&& !pObject
->hasQuest(quest
))
129 || (pObject
->GetTypeId()==TYPEID_PLAYER
&& !((Player
*)pObject
)->CanShareQuest(quest
))
132 _player
->PlayerTalkClass
->CloseGossip();
133 _player
->SetDivider( 0 );
137 Quest
const* qInfo
= objmgr
.GetQuestTemplate(quest
);
141 if(!GetPlayer()->CanTakeQuest(qInfo
,true) )
143 _player
->PlayerTalkClass
->CloseGossip();
144 _player
->SetDivider( 0 );
148 if( _player
->GetDivider() != 0 )
150 Player
*pPlayer
= ObjectAccessor::FindPlayer( _player
->GetDivider() );
153 pPlayer
->SendPushToPartyResponse( _player
, QUEST_PARTY_MSG_ACCEPT_QUEST
);
154 _player
->SetDivider( 0 );
158 if( _player
->CanAddQuest( qInfo
, true ) )
160 _player
->AddQuest( qInfo
, pObject
);
162 if ( _player
->CanCompleteQuest( quest
) )
163 _player
->CompleteQuest( quest
);
165 switch(pObject
->GetTypeId())
168 Script
->QuestAccept(_player
, ((Creature
*)pObject
), qInfo
);
171 case TYPEID_CONTAINER
:
173 Script
->ItemQuestAccept(_player
, ((Item
*)pObject
), qInfo
);
175 // destroy not required for quest finish quest starting item
176 bool destroyItem
= true;
177 for(int i
= 0; i
< QUEST_OBJECTIVES_COUNT
; i
++)
179 if ((qInfo
->ReqItemId
[i
] == ((Item
*)pObject
)->GetEntry()) && (((Item
*)pObject
)->GetProto()->MaxCount
!= 0))
187 _player
->DestroyItem(((Item
*)pObject
)->GetBagSlot(),((Item
*)pObject
)->GetSlot(),true);
191 case TYPEID_GAMEOBJECT
:
192 Script
->GOQuestAccept(_player
, ((GameObject
*)pObject
), qInfo
);
195 _player
->PlayerTalkClass
->CloseGossip();
197 if( qInfo
->GetSrcSpell() > 0 )
198 _player
->CastSpell( _player
, qInfo
->GetSrcSpell(), true);
204 _player
->PlayerTalkClass
->CloseGossip();
207 void WorldSession::HandleQuestgiverQuestQueryOpcode( WorldPacket
& recv_data
)
209 CHECK_PACKET_SIZE(recv_data
,8+4);
213 recv_data
>> guid
>> quest
;
214 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid
)),quest
);
216 // Verify that the guid is valid and is a questgiver or involved in the requested quest
217 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
|TYPEMASK_ITEM
);
218 if(!pObject
||!pObject
->hasQuest(quest
) && !pObject
->hasInvolvedQuest(quest
))
220 _player
->PlayerTalkClass
->CloseGossip();
224 Quest
const* pQuest
= objmgr
.GetQuestTemplate(quest
);
227 _player
->PlayerTalkClass
->SendQuestGiverQuestDetails(pQuest
, pObject
->GetGUID(), true);
231 void WorldSession::HandleQuestQueryOpcode( WorldPacket
& recv_data
)
233 CHECK_PACKET_SIZE(recv_data
,4);
237 sLog
.outDebug( "WORLD: Received CMSG_QUEST_QUERY quest = %u",quest
);
239 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
242 _player
->PlayerTalkClass
->SendQuestQueryResponse( pQuest
);
246 void WorldSession::HandleQuestgiverChooseRewardOpcode( WorldPacket
& recv_data
)
248 CHECK_PACKET_SIZE(recv_data
,8+4+4);
250 uint32 quest
, reward
;
252 recv_data
>> guid
>> quest
>> reward
;
254 if(reward
>= QUEST_REWARD_CHOICES_COUNT
)
256 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
);
260 if(!GetPlayer()->isAlive())
263 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_CHOOSE_REWARD npc = %u, quest = %u, reward = %u",uint32(GUID_LOPART(guid
)),quest
,reward
);
265 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
);
269 if(!pObject
->hasInvolvedQuest(quest
))
272 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
275 if( _player
->CanRewardQuest( pQuest
, reward
, true ) )
277 _player
->RewardQuest( pQuest
, reward
, pObject
);
279 switch(pObject
->GetTypeId())
282 if( !(Script
->ChooseReward( _player
, ((Creature
*)pObject
), pQuest
, reward
)) )
285 if(Quest
const* nextquest
= _player
->GetNextQuest( guid
,pQuest
) )
286 _player
->PlayerTalkClass
->SendQuestGiverQuestDetails(nextquest
,guid
,true);
289 case TYPEID_GAMEOBJECT
:
290 if( !Script
->GOChooseReward( _player
, ((GameObject
*)pObject
), pQuest
, reward
) )
293 if(Quest
const* nextquest
= _player
->GetNextQuest( guid
,pQuest
) )
294 _player
->PlayerTalkClass
->SendQuestGiverQuestDetails(nextquest
,guid
,true);
300 _player
->PlayerTalkClass
->SendQuestGiverOfferReward( pQuest
, guid
, true );
304 void WorldSession::HandleQuestgiverRequestRewardOpcode( WorldPacket
& recv_data
)
306 CHECK_PACKET_SIZE(recv_data
,8+4);
310 recv_data
>> guid
>> quest
;
312 if(!GetPlayer()->isAlive())
315 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = %u, quest = %u",uint32(GUID_LOPART(guid
)),quest
);
317 Object
* pObject
= ObjectAccessor::GetObjectByTypeMask(*_player
, guid
,TYPEMASK_UNIT
|TYPEMASK_GAMEOBJECT
);
318 if(!pObject
||!pObject
->hasInvolvedQuest(quest
))
321 if ( _player
->CanCompleteQuest( quest
) )
322 _player
->CompleteQuest( quest
);
324 if( _player
->GetQuestStatus( quest
) != QUEST_STATUS_COMPLETE
)
327 if(Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
))
328 _player
->PlayerTalkClass
->SendQuestGiverOfferReward( pQuest
, guid
, true );
331 void WorldSession::HandleQuestgiverCancel(WorldPacket
& /*recv_data*/ )
333 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_CANCEL" );
335 _player
->PlayerTalkClass
->CloseGossip();
338 void WorldSession::HandleQuestLogSwapQuest(WorldPacket
& recv_data
)
340 CHECK_PACKET_SIZE(recv_data
,1+1);
343 recv_data
>> slot1
>> slot2
;
345 if(slot1
== slot2
|| slot1
>= MAX_QUEST_LOG_SIZE
|| slot2
>= MAX_QUEST_LOG_SIZE
)
348 sLog
.outDebug( "WORLD: Received CMSG_QUESTLOG_SWAP_QUEST slot 1 = %u, slot 2 = %u", slot1
, slot2
);
350 GetPlayer()->SwapQuestSlot(slot1
,slot2
);
353 void WorldSession::HandleQuestLogRemoveQuest(WorldPacket
& recv_data
)
355 CHECK_PACKET_SIZE(recv_data
,1);
360 sLog
.outDebug( "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = %u",slot
);
362 if( slot
< MAX_QUEST_LOG_SIZE
)
364 if(uint32 quest
= _player
->GetQuestSlotQuestId(slot
))
366 if(!_player
->TakeQuestSourceItem( quest
, true ))
367 return; // can't un-equip some items, reject quest cancel
369 _player
->SetQuestStatus( quest
, QUEST_STATUS_NONE
);
372 _player
->SetQuestSlot(slot
, 0);
376 void WorldSession::HandleQuestConfirmAccept(WorldPacket
& recv_data
)
378 CHECK_PACKET_SIZE(recv_data
,4);
383 sLog
.outDebug( "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u",quest
);
386 void WorldSession::HandleQuestComplete(WorldPacket
& recv_data
)
388 CHECK_PACKET_SIZE(recv_data
,8+4);
392 recv_data
>> guid
>> quest
;
394 if(!GetPlayer()->isAlive())
397 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid
)),quest
);
399 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
402 if( _player
->GetQuestStatus( quest
) != QUEST_STATUS_COMPLETE
)
404 if( pQuest
->IsRepeatable() )
405 _player
->PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, _player
->CanCompleteRepeatableQuest(pQuest
), false);
407 _player
->PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, _player
->CanRewardQuest(pQuest
,false), false);
410 _player
->PlayerTalkClass
->SendQuestGiverRequestItems(pQuest
, guid
, _player
->CanRewardQuest(pQuest
,false), false);
414 void WorldSession::HandleQuestAutoLaunch(WorldPacket
& /*recvPacket*/)
416 sLog
.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUEST_AUTOLAUNCH (Send your log to anakin if you see this message)" );
419 void WorldSession::HandleQuestPushToParty(WorldPacket
& recvPacket
)
421 CHECK_PACKET_SIZE(recvPacket
,4);
426 sLog
.outDebug( "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", quest
);
428 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest
);
431 if( _player
->GetGroup() )
433 Group
*pGroup
= _player
->GetGroup();
436 for(GroupReference
*itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
438 Player
*pPlayer
= itr
->getSource();
439 if (!pPlayer
|| pPlayer
== _player
) // skip self
442 _player
->SendPushToPartyResponse(pPlayer
, QUEST_PARTY_MSG_SHARING_QUEST
);
444 if( _player
->GetDistance( pPlayer
) > 10 )
446 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_TOO_FAR
);
450 if( !pPlayer
->SatisfyQuestStatus( pQuest
, false ) )
452 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_HAVE_QUEST
);
456 if( pPlayer
->GetQuestStatus( quest
) == QUEST_STATUS_COMPLETE
)
458 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_FINISH_QUEST
);
462 if( !pPlayer
->CanTakeQuest( pQuest
, false ) )
464 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_CANT_TAKE_QUEST
);
468 if( !pPlayer
->SatisfyQuestLog( false ) )
470 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_LOG_FULL
);
474 if( pPlayer
->GetDivider() != 0 )
476 _player
->SendPushToPartyResponse( pPlayer
, QUEST_PARTY_MSG_BUSY
);
480 pPlayer
->PlayerTalkClass
->SendQuestGiverQuestDetails( pQuest
, _player
->GetGUID(), true );
481 pPlayer
->SetDivider( _player
->GetGUID() );
488 void WorldSession::HandleQuestPushResult(WorldPacket
& recvPacket
)
490 CHECK_PACKET_SIZE(recvPacket
,8+1);
494 recvPacket
>> guid
>> msg
;
496 sLog
.outDebug( "WORLD: Received MSG_QUEST_PUSH_RESULT" );
498 if( _player
->GetDivider() != 0 )
500 Player
*pPlayer
= ObjectAccessor::FindPlayer( _player
->GetDivider() );
503 WorldPacket
data( MSG_QUEST_PUSH_RESULT
, (8+1) );
504 data
<< uint64(guid
);
505 data
<< uint8(msg
); // valid values: 0-8
506 pPlayer
->GetSession()->SendPacket(&data
);
507 _player
->SetDivider( 0 );
512 uint32
WorldSession::getDialogStatus(Player
*pPlayer
, Object
* questgiver
, uint32 defstatus
)
514 uint32 result
= defstatus
;
516 QuestRelations
const* qir
;
517 QuestRelations
const* qr
;
519 switch(questgiver
->GetTypeId())
521 case TYPEID_GAMEOBJECT
:
523 qir
= &objmgr
.mGOQuestInvolvedRelations
;
524 qr
= &objmgr
.mGOQuestRelations
;
529 qir
= &objmgr
.mCreatureQuestInvolvedRelations
;
530 qr
= &objmgr
.mCreatureQuestRelations
;
534 //its imposible, but check ^)
535 sLog
.outError("Warning: GetDialogStatus called for unexpected type %u", questgiver
->GetTypeId());
536 return DIALOG_STATUS_NONE
;
539 for(QuestRelations::const_iterator i
= qir
->lower_bound(questgiver
->GetEntry()); i
!= qir
->upper_bound(questgiver
->GetEntry()); ++i
)
542 uint32 quest_id
= i
->second
;
543 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest_id
);
544 if ( !pQuest
) continue;
546 QuestStatus status
= pPlayer
->GetQuestStatus( quest_id
);
547 if( (status
== QUEST_STATUS_COMPLETE
&& !pPlayer
->GetQuestRewardStatus(quest_id
)) ||
548 (pQuest
->IsAutoComplete() && pPlayer
->CanTakeQuest(pQuest
, false)) )
550 if ( pQuest
->IsAutoComplete() && pQuest
->IsRepeatable() )
551 result2
= DIALOG_STATUS_REWARD_REP
;
553 result2
= DIALOG_STATUS_REWARD
;
555 else if ( status
== QUEST_STATUS_INCOMPLETE
)
556 result2
= DIALOG_STATUS_INCOMPLETE
;
558 if (result2
> result
)
562 for(QuestRelations::const_iterator i
= qr
->lower_bound(questgiver
->GetEntry()); i
!= qr
->upper_bound(questgiver
->GetEntry()); ++i
)
565 uint32 quest_id
= i
->second
;
566 Quest
const *pQuest
= objmgr
.GetQuestTemplate(quest_id
);
570 QuestStatus status
= pPlayer
->GetQuestStatus( quest_id
);
571 if ( status
== QUEST_STATUS_NONE
)
573 if ( pPlayer
->CanSeeStartQuest( pQuest
) )
575 if ( pPlayer
->SatisfyQuestLevel(pQuest
, false) )
577 if ( pQuest
->IsAutoComplete() || (pQuest
->IsRepeatable() && pPlayer
->getQuestStatusMap()[quest_id
].m_rewarded
))
578 result2
= DIALOG_STATUS_REWARD_REP
;
579 else if (pPlayer
->getLevel() <= pQuest
->GetQuestLevel() + sWorld
.getConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF
) )
581 if (pQuest
->HasFlag(QUEST_FLAGS_DAILY
))
582 result2
= DIALOG_STATUS_AVAILABLE_REP
;
584 result2
= DIALOG_STATUS_AVAILABLE
;
587 result2
= DIALOG_STATUS_CHAT
;
590 result2
= DIALOG_STATUS_UNAVAILABLE
;
594 if (result2
> result
)
598 if(questgiver
->GetTypeId()==TYPEID_UNIT
&& ((Creature
*)questgiver
)->isCanTrainingAndResetTalentsOf(pPlayer
) && result
< DIALOG_STATUS_CHAT
)
599 result
= DIALOG_STATUS_CHAT
;
604 void WorldSession::HandleQuestgiverStatusQueryMultipleOpcode(WorldPacket
& /*recvPacket*/)
606 sLog
.outDebug("WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY");
610 WorldPacket
data(SMSG_QUESTGIVER_STATUS_MULTIPLE
, 4);
611 data
<< uint32(count
); // placeholder
613 for(Player::ClientGUIDs::iterator itr
= _player
->m_clientGUIDs
.begin(); itr
!= _player
->m_clientGUIDs
.end(); ++itr
)
615 uint8 questStatus
= DIALOG_STATUS_NONE
;
616 uint8 defstatus
= DIALOG_STATUS_NONE
;
618 if(IS_CREATURE_GUID(*itr
))
620 Creature
*questgiver
= ObjectAccessor::GetCreature(*_player
, *itr
);
621 if(!questgiver
|| questgiver
->IsHostileTo(_player
))
623 if(!questgiver
->HasFlag(UNIT_NPC_FLAGS
, UNIT_NPC_FLAG_QUESTGIVER
))
625 questStatus
= Script
->NPCDialogStatus(_player
, questgiver
);
626 if( questStatus
> 6 )
627 questStatus
= getDialogStatus(_player
, questgiver
, defstatus
);
629 data
<< uint64(questgiver
->GetGUID());
630 data
<< uint8(questStatus
);
633 else if(IS_GAMEOBJECT_GUID(*itr
))
635 GameObject
*questgiver
= ObjectAccessor::GetGameObject(*_player
, *itr
);
638 if(questgiver
->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER
)
640 questStatus
= Script
->GODialogStatus(_player
, questgiver
);
641 if( questStatus
> 6 )
642 questStatus
= getDialogStatus(_player
, questgiver
, defstatus
);
644 data
<< uint64(questgiver
->GetGUID());
645 data
<< uint8(questStatus
);
650 data
.put
<uint32
>(0, count
); // write real count