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
19 #include "AchievementMgr.h"
22 #include "WorldPacket.h"
23 #include "Database/DBCEnums.h"
24 #include "ObjectMgr.h"
26 #include "Database/DatabaseEnv.h"
27 #include "GameEvent.h"
31 const CriteriaCastSpellRequirement
AchievementMgr::criteriaCastSpellRequirements
[CRITERIA_CAST_SPELL_REQ_COUNT
] =
37 {5772, 0, 0, RACE_GNOME
},
38 {5774, 0, 0, RACE_BLOODELF
},
39 {5775, 0, 0, RACE_DRAENEI
},
40 {5776, 0, 0, RACE_DWARF
},
41 {5777, 0, 0, RACE_HUMAN
},
42 {5778, 0, 0, RACE_NIGHTELF
},
43 {5779, 0, 0, RACE_ORC
},
44 {5780, 0, 0, RACE_TAUREN
},
45 {5781, 0, 0, RACE_TROLL
},
46 {5782, 0, 0, RACE_UNDEAD_PLAYER
},
58 {6237, 0, CLASS_DEATH_KNIGHT
, RACE_ORC
},
59 {6238, 0, CLASS_WARRIOR
, RACE_HUMAN
},
60 {6239, 0, CLASS_SHAMAN
, RACE_TAUREN
},
61 {6240, 0, CLASS_DRUID
, RACE_NIGHTELF
},
62 {6241, 0, CLASS_ROGUE
, RACE_UNDEAD_PLAYER
},
63 {6242, 0, CLASS_HUNTER
, RACE_TROLL
},
64 {6243, 0, CLASS_MAGE
, RACE_GNOME
},
65 {6244, 0, CLASS_PALADIN
, RACE_DWARF
},
66 {6245, 0, CLASS_WARLOCK
, RACE_BLOODELF
},
67 {6246, 0, CLASS_PRIEST
, RACE_DRAENEI
},
68 {6312, 0, CLASS_WARLOCK
, RACE_GNOME
},
69 {6313, 0, CLASS_DEATH_KNIGHT
, RACE_HUMAN
},
70 {6314, 0, CLASS_PRIEST
, RACE_NIGHTELF
},
71 {6315, 0, CLASS_SHAMAN
, RACE_ORC
},
72 {6316, 0, CLASS_DRUID
, RACE_TAUREN
},
73 {6317, 0, CLASS_ROGUE
, RACE_TROLL
},
74 {6318, 0, CLASS_WARRIOR
, RACE_UNDEAD_PLAYER
},
75 {6319, 0, CLASS_MAGE
, RACE_BLOODELF
},
76 {6320, 0, CLASS_PALADIN
, RACE_DRAENEI
},
77 {6321, 0, CLASS_HUNTER
, RACE_DWARF
},
81 const AchievementReward
AchievementMgr::achievementRewards
[ACHIEVEMENT_REWARD_COUNT
] =
83 // achievementId, horde titleid, alliance titleid, itemid
118 {1681, 125, 125, 43300},
119 {1682, 125, 125, 43300},
132 {2136, 0, 0, 0},// <- TODO: find item for spell 59961
133 {2137, 0, 0, 0},// <- TODO: find item for spell 60021
134 {2138, 0, 0, 0},// <- TODO: find item for spell 59976
136 {2144, 0, 0, 0},// <- TODO: find item for spell 60024
137 {2145, 0, 0, 0},// <- TODO: find item for spell 60024
143 AchievementMgr::AchievementMgr(Player
*player
)
148 AchievementMgr::~AchievementMgr()
152 void AchievementMgr::SaveToDB()
154 if(!m_completedAchievements
.empty())
156 bool need_execute
= false;
157 std::ostringstream ssdel
;
158 std::ostringstream ssins
;
159 for(CompletedAchievementMap::iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); iter
++)
161 if(!iter
->second
.changed
)
164 /// first new/changed record prefix
167 ssdel
<< "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
168 ssins
<< "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
171 /// next new/changed record prefix
178 // new/changed record data
179 ssdel
<< iter
->first
;
180 ssins
<< "("<<GetPlayer()->GetGUIDLow() << ", " << iter
->first
<< ", " << uint64(iter
->second
.date
) << ")";
182 /// mark as saved in db
183 iter
->second
.changed
= false;
191 CharacterDatabase
.BeginTransaction ();
192 CharacterDatabase
.Execute( ssdel
.str().c_str() );
193 CharacterDatabase
.Execute( ssins
.str().c_str() );
194 CharacterDatabase
.CommitTransaction ();
198 if(!m_criteriaProgress
.empty())
200 /// prepare deleting and insert
201 bool need_execute
= false;
202 std::ostringstream ssdel
;
203 std::ostringstream ssins
;
204 for(CriteriaProgressMap::iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
206 if(!iter
->second
.changed
)
209 /// first new/changed record prefix
212 ssdel
<< "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
213 ssins
<< "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
216 /// next new/changed record prefix
223 // new/changed record data
224 ssdel
<< iter
->first
;
225 ssins
<< "(" << GetPlayer()->GetGUIDLow() << ", " << iter
->first
<< ", " << iter
->second
.counter
<< ", " << iter
->second
.date
<< ")";
227 /// mark as saved in db
228 iter
->second
.changed
= false;
236 CharacterDatabase
.BeginTransaction ();
237 CharacterDatabase
.Execute( ssdel
.str().c_str() );
238 CharacterDatabase
.Execute( ssins
.str().c_str() );
239 CharacterDatabase
.CommitTransaction ();
244 void AchievementMgr::LoadFromDB(QueryResult
*achievementResult
, QueryResult
*criteriaResult
)
246 if(achievementResult
)
250 Field
*fields
= achievementResult
->Fetch();
251 CompletedAchievementData
& ca
= m_completedAchievements
[fields
[0].GetUInt32()];
252 ca
.date
= time_t(fields
[1].GetUInt64());
254 } while(achievementResult
->NextRow());
255 delete achievementResult
;
262 Field
*fields
= criteriaResult
->Fetch();
264 uint32 id
= fields
[0].GetUInt32();
265 uint32 counter
= fields
[1].GetUInt32();
266 time_t date
= time_t(fields
[2].GetUInt64());
268 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(id
);
269 if(!criteria
|| criteria
->timeLimit
&& date
+ criteria
->timeLimit
< time(NULL
))
272 CriteriaProgress
& progress
= m_criteriaProgress
[id
];
273 progress
.counter
= counter
;
274 progress
.date
= date
;
275 progress
.changed
= false;
276 } while(criteriaResult
->NextRow());
277 delete criteriaResult
;
282 void AchievementMgr::SendAchievementEarned(AchievementEntry
const* achievement
)
284 sLog
.outString("AchievementMgr::SendAchievementEarned(%u)", achievement
->ID
);
286 const char *msg
= "|Hplayer:$N|h[$N]|h has earned the achievement $a!";
287 if(Guild
* guild
= objmgr
.GetGuildById(GetPlayer()->GetGuildId()))
289 WorldPacket
data(SMSG_MESSAGECHAT
, 200);
290 data
<< uint8(CHAT_MSG_ACHIEVEMENT
);
291 data
<< uint8(CHAT_MSG_GUILD_ACHIEVEMENT
);
292 data
<< uint32(LANG_UNIVERSAL
);
293 data
<< uint64(GetPlayer()->GetGUID());
295 data
<< uint64(GetPlayer()->GetGUID());
296 data
<< uint32(strlen(msg
)+1);
299 data
<< uint32(achievement
->ID
);
300 guild
->BroadcastPacket(&data
);
302 if(achievement
->flags
& (ACHIEVEMENT_FLAG_REALM_FIRST_KILL
|ACHIEVEMENT_FLAG_REALM_FIRST_REACH
))
304 // broadcast realm first reached
305 WorldPacket
data(SMSG_SERVER_FIRST_ACHIEVEMENT
, strlen(GetPlayer()->GetName())+1+8+4+4);
306 data
<< GetPlayer()->GetName();
307 data
<< uint64(GetPlayer()->GetGUID());
308 data
<< uint32(achievement
->ID
);
309 data
<< uint32(0); // 1=link supplied string as player name, 0=display plain string
310 sWorld
.SendGlobalMessage(&data
);
314 WorldPacket
data(SMSG_MESSAGECHAT
, 200);
315 data
<< uint8(CHAT_MSG_ACHIEVEMENT
);
316 data
<< uint32(LANG_UNIVERSAL
);
317 data
<< uint64(GetPlayer()->GetGUID());
319 data
<< uint64(GetPlayer()->GetGUID());
320 data
<< uint32(strlen(msg
)+1);
323 data
<< uint32(achievement
->ID
);
324 GetPlayer()->SendMessageToSet(&data
, true);
327 WorldPacket
data(SMSG_ACHIEVEMENT_EARNED
, 8+4+8);
328 data
.append(GetPlayer()->GetPackGUID());
329 data
<< uint32(achievement
->ID
);
330 data
<< uint32(secsToTimeBitFields(time(NULL
)));
332 GetPlayer()->SendMessageToSet(&data
, true);
335 void AchievementMgr::SendCriteriaUpdate(uint32 id
, CriteriaProgress
const* progress
)
337 WorldPacket
data(SMSG_CRITERIA_UPDATE
, 8+4+8);
340 // the counter is packed like a packed Guid
341 data
.appendPackGUID(progress
->counter
);
343 data
.append(GetPlayer()->GetPackGUID());
345 data
<< uint32(secsToTimeBitFields(progress
->date
));
346 data
<< uint32(0); // timer 1
347 data
<< uint32(0); // timer 2
348 GetPlayer()->SendMessageToSet(&data
, true);
352 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
354 void AchievementMgr::CheckAllAchievementCriteria()
356 // suppress sending packets
357 for(uint32 i
=0; i
<ACHIEVEMENT_CRITERIA_TYPE_TOTAL
; i
++)
358 UpdateAchievementCriteria(AchievementCriteriaTypes(i
));
362 * this function will be called whenever the user might have done a criteria relevant action
364 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type
, uint32 miscvalue1
, uint32 miscvalue2
, Unit
*unit
, uint32 time
)
366 sLog
.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type
, miscvalue1
, miscvalue2
, time
);
367 AchievementCriteriaEntryList
const& achievementCriteriaList
= objmgr
.GetAchievementCriteriaByType(type
);
368 for(AchievementCriteriaEntryList::const_iterator i
= achievementCriteriaList
.begin(); i
!=achievementCriteriaList
.end(); ++i
)
370 AchievementCriteriaEntry
const *achievementCriteria
= (*i
);
372 // don't update already completed criteria
373 if(IsCompletedCriteria(achievementCriteria
))
376 if(achievementCriteria
->groupFlag
& ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP
&& GetPlayer()->GetGroup())
379 AchievementEntry
const *achievement
= sAchievementStore
.LookupEntry(achievementCriteria
->referredAchievement
);
383 if(achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_HORDE
&& GetPlayer()->GetTeam() != HORDE
||
384 achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_ALLIANCE
&& GetPlayer()->GetTeam() != ALLIANCE
)
389 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
390 SetCriteriaProgress(achievementCriteria
, GetPlayer()->getLevel());
392 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
393 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetByteValue(PLAYER_BYTES_2
, 2)+1);
395 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
396 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
399 if(achievementCriteria
->kill_creature
.creatureID
!= miscvalue1
)
401 SetCriteriaProgress(achievementCriteria
, miscvalue2
, true);
403 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
:
404 if(uint32 skillvalue
= GetPlayer()->GetBaseSkillValue(achievementCriteria
->reach_skill_level
.skillID
))
405 SetCriteriaProgress(achievementCriteria
, skillvalue
);
407 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT
:
410 for(QuestStatusMap::iterator itr
= GetPlayer()->getQuestStatusMap().begin(); itr
!=GetPlayer()->getQuestStatusMap().end(); itr
++)
411 if(itr
->second
.m_rewarded
)
413 SetCriteriaProgress(achievementCriteria
, counter
);
416 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
:
419 for(QuestStatusMap::iterator itr
= GetPlayer()->getQuestStatusMap().begin(); itr
!=GetPlayer()->getQuestStatusMap().end(); itr
++)
421 Quest
const* quest
= objmgr
.GetQuestTemplate(itr
->first
);
422 if(itr
->second
.m_rewarded
&& quest
->GetZoneOrSort() == achievementCriteria
->complete_quests_in_zone
.zoneID
)
425 SetCriteriaProgress(achievementCriteria
, counter
);
428 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
:
429 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
432 SetCriteriaProgress(achievementCriteria
, miscvalue1
, true);
434 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
435 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
438 if(GetPlayer()->GetMapId() != achievementCriteria
->complete_battleground
.mapID
)
440 SetCriteriaProgress(achievementCriteria
, miscvalue1
, true);
442 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
443 if(GetPlayer()->HasSpell(achievementCriteria
->learn_spell
.spellID
))
444 SetCriteriaProgress(achievementCriteria
, 1);
446 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
447 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
450 if(GetPlayer()->GetMapId() != achievementCriteria
->death_at_map
.mapID
)
452 SetCriteriaProgress(achievementCriteria
, 1, true);
454 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
455 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
458 if(miscvalue1
!= achievementCriteria
->killed_by_creature
.creatureEntry
)
460 SetCriteriaProgress(achievementCriteria
, 1, true);
462 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
463 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
466 SetCriteriaProgress(achievementCriteria
, 1, true);
468 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
470 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
473 if(achievement
->ID
== 1260)
475 if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED
)
477 // TODO: hardcoding eventid is bad, it can differ from DB to DB - maye implement something using HolidayNames.dbc?
478 if(!gameeventmgr
.IsActiveEvent(26))
481 // miscvalue1 is the ingame fallheight*100 as stored in dbc
482 SetCriteriaProgress(achievementCriteria
, miscvalue1
);
485 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
486 if(GetPlayer()->GetQuestRewardStatus(achievementCriteria
->complete_quest
.questID
))
487 SetCriteriaProgress(achievementCriteria
, 1);
489 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
490 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
493 if(achievementCriteria
->use_item
.itemID
!= miscvalue1
)
495 SetCriteriaProgress(achievementCriteria
, 1, true);
497 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
498 // speedup for non-login case
499 if(miscvalue1
&& achievementCriteria
->own_item
.itemID
!=miscvalue1
)
501 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetItemCount(achievementCriteria
->own_item
.itemID
, true));
503 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
504 // You _have_ to loot that item, just owning it when logging in does _not_ count!
507 if(miscvalue1
!= achievementCriteria
->own_item
.itemID
)
509 SetCriteriaProgress(achievementCriteria
, miscvalue2
, true);
511 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET
:
512 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2
:
513 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->be_spell_target
.spellID
)
515 SetCriteriaProgress(achievementCriteria
, 1, true);
517 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
518 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->cast_spell
.spellID
)
520 SetCriteriaProgress(achievementCriteria
, 1, true);
522 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
524 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->cast_spell
.spellID
)
526 // those requirements couldn't be found in the dbc
528 const CriteriaCastSpellRequirement
*requirement
= NULL
;
529 for (uint32 i
=0; i
<CRITERIA_CAST_SPELL_REQ_COUNT
; i
++)
531 if (criteriaCastSpellRequirements
[i
].achievementCriteriaId
== achievementCriteria
->ID
)
533 requirement
= &criteriaCastSpellRequirements
[i
];
543 if (requirement
->creatureEntry
&& unit
->GetEntry() != requirement
->creatureEntry
)
546 if (requirement
->playerRace
&& (unit
->GetTypeId() != TYPEID_PLAYER
|| unit
->getRace()!=requirement
->playerRace
))
549 if (requirement
->playerClass
&& (unit
->GetTypeId() != TYPEID_PLAYER
|| unit
->getClass()!=requirement
->playerClass
))
552 SetCriteriaProgress(achievementCriteria
, 1, true);
555 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
557 uint32 spellCount
= 0;
558 for (PlayerSpellMap::const_iterator spellIter
= GetPlayer()->GetSpellMap().begin();
559 spellIter
!= GetPlayer()->GetSpellMap().end();
562 for(SkillLineAbilityMap::const_iterator skillIter
= spellmgr
.GetBeginSkillLineAbilityMap(spellIter
->first
);
563 skillIter
!= spellmgr
.GetEndSkillLineAbilityMap(spellIter
->first
);
566 if(skillIter
->second
->skillId
== achievementCriteria
->learn_skilline_spell
.skillLine
)
570 SetCriteriaProgress(achievementCriteria
, spellCount
);
573 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
575 // skip for login case
578 SetCriteriaProgress(achievementCriteria
, 1);
581 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
583 int32 reputation
= GetPlayer()->GetReputation(achievementCriteria
->gain_reputation
.factionID
);
585 SetCriteriaProgress(achievementCriteria
, reputation
);
588 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
591 const FactionStateList factionStateList
= GetPlayer()->GetFactionStateList();
592 for (FactionStateList::const_iterator iter
= factionStateList
.begin(); iter
!= factionStateList
.end(); iter
++)
594 if(GetPlayer()->ReputationToRank(iter
->second
.Standing
) >= REP_EXALTED
)
597 SetCriteriaProgress(achievementCriteria
, counter
);
600 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
:
602 WorldMapOverlayEntry
const* worldOverlayEntry
= sWorldMapOverlayStore
.LookupEntry(achievementCriteria
->explore_area
.areaReference
);
603 if(!worldOverlayEntry
)
606 int32 exploreFlag
= GetAreaFlagByAreaID(worldOverlayEntry
->areatableID
);
610 uint32 playerIndexOffset
= uint32(exploreFlag
) / 32;
611 uint32 mask
= 1<< (uint32(exploreFlag
) % 32);
613 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1
+ playerIndexOffset
) & mask
)
614 SetCriteriaProgress(achievementCriteria
, 1);
619 if(IsCompletedCriteria(achievementCriteria
))
620 CompletedCriteria(achievementCriteria
);
624 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry
const* achievementCriteria
)
626 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(achievementCriteria
->referredAchievement
);
630 // counter can never complete
631 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
634 if(achievement
->flags
& (ACHIEVEMENT_FLAG_REALM_FIRST_REACH
| ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
636 // someone on this realm has already completed that achievement
637 if(objmgr
.allCompletedAchievements
.find(achievement
->ID
)!=objmgr
.allCompletedAchievements
.end())
641 CriteriaProgressMap::const_iterator itr
= m_criteriaProgress
.find(achievementCriteria
->ID
);
642 if(itr
== m_criteriaProgress
.end())
645 CriteriaProgress
const* progress
= &itr
->second
;
647 switch(achievementCriteria
->requiredType
)
649 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
650 if(achievement
->ID
== 467 && GetPlayer()->getClass() != CLASS_SHAMAN
||
651 achievement
->ID
== 466 && GetPlayer()->getClass() != CLASS_DRUID
||
652 achievement
->ID
== 465 && GetPlayer()->getClass() != CLASS_PALADIN
||
653 achievement
->ID
== 464 && GetPlayer()->getClass() != CLASS_PRIEST
||
654 achievement
->ID
== 463 && GetPlayer()->getClass() != CLASS_WARLOCK
||
655 achievement
->ID
== 462 && GetPlayer()->getClass() != CLASS_HUNTER
||
656 achievement
->ID
== 461 && GetPlayer()->getClass() != CLASS_DEATH_KNIGHT
||
657 achievement
->ID
== 460 && GetPlayer()->getClass() != CLASS_MAGE
||
658 achievement
->ID
== 459 && GetPlayer()->getClass() != CLASS_WARRIOR
||
659 achievement
->ID
== 458 && GetPlayer()->getClass() != CLASS_ROGUE
||
661 achievement
->ID
== 1404 && GetPlayer()->getRace() != RACE_GNOME
||
662 achievement
->ID
== 1405 && GetPlayer()->getRace() != RACE_BLOODELF
||
663 achievement
->ID
== 1406 && GetPlayer()->getRace() != RACE_DRAENEI
||
664 achievement
->ID
== 1407 && GetPlayer()->getRace() != RACE_DWARF
||
665 achievement
->ID
== 1408 && GetPlayer()->getRace() != RACE_HUMAN
||
666 achievement
->ID
== 1409 && GetPlayer()->getRace() != RACE_NIGHTELF
||
667 achievement
->ID
== 1410 && GetPlayer()->getRace() != RACE_ORC
||
668 achievement
->ID
== 1411 && GetPlayer()->getRace() != RACE_TAUREN
||
669 achievement
->ID
== 1412 && GetPlayer()->getRace() != RACE_TROLL
||
670 achievement
->ID
== 1413 && GetPlayer()->getRace() != RACE_UNDEAD_PLAYER
)
672 return progress
->counter
>= achievementCriteria
->reach_level
.level
;
673 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
674 return progress
->counter
>= achievementCriteria
->buy_bank_slot
.numberOfSlots
;
675 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
676 return progress
->counter
>= achievementCriteria
->kill_creature
.creatureCount
;
677 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
:
678 return m_completedAchievements
.find(achievementCriteria
->complete_achievement
.linkedAchievement
) != m_completedAchievements
.end();
679 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
:
680 return progress
->counter
>= achievementCriteria
->reach_skill_level
.skillLevel
;
681 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
:
682 return progress
->counter
>= achievementCriteria
->complete_quests_in_zone
.questCount
;
683 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
:
684 return progress
->counter
>= achievementCriteria
->complete_daily_quest
.questCount
;
685 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
686 return progress
->counter
>= 1;
687 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
688 return progress
->counter
>= achievementCriteria
->fall_without_dying
.fallHeight
;
689 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
690 return progress
->counter
>= 1;
691 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
692 return progress
->counter
>= achievementCriteria
->use_item
.itemCount
;
693 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
694 return progress
->counter
>= achievementCriteria
->own_item
.itemCount
;
695 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
696 return progress
->counter
>= achievementCriteria
->loot_item
.itemCount
;
697 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET
:
698 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2
:
699 return progress
->counter
>= achievementCriteria
->be_spell_target
.spellCount
;
700 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
701 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
702 return progress
->counter
>= achievementCriteria
->cast_spell
.castCount
;
703 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
704 return progress
->counter
>= achievementCriteria
->learn_skilline_spell
.spellCount
;
705 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
706 return progress
->counter
>= achievementCriteria
->visit_barber
.numberOfVisits
;
707 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
708 return progress
->counter
>= achievementCriteria
->gain_reputation
.reputationAmount
;
709 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
710 return progress
->counter
>= achievementCriteria
->gain_exalted_reputation
.numberOfExaltedFactions
;
711 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
:
712 return progress
->counter
>= 1;
714 // handle all statistic-only criteria here
715 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
716 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
717 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
718 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
724 void AchievementMgr::CompletedCriteria(AchievementCriteriaEntry
const* criteria
)
726 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(criteria
->referredAchievement
);
729 // counter can never complete
730 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
733 if(criteria
->completionFlag
& ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
|| GetAchievementCompletionState(achievement
)==ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED
)
735 CompletedAchievement(achievement
);
739 // TODO: achievement 705 requires 4 criteria to be fulfilled
740 AchievementCompletionState
AchievementMgr::GetAchievementCompletionState(AchievementEntry
const* entry
)
742 if(m_completedAchievements
.find(entry
->ID
)!=m_completedAchievements
.end())
743 return ACHIEVEMENT_COMPLETED_COMPLETED_STORED
;
745 bool foundOutstanding
= false;
746 for (uint32 entryId
= 0; entryId
<sAchievementCriteriaStore
.GetNumRows(); entryId
++)
748 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(entryId
);
749 if(!criteria
|| criteria
->referredAchievement
!= entry
->ID
)
752 if(IsCompletedCriteria(criteria
) && criteria
->completionFlag
& ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
)
753 return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED
;
755 // found an umcompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
756 if(!IsCompletedCriteria(criteria
))
757 foundOutstanding
= true;
760 return ACHIEVEMENT_COMPLETED_NONE
;
762 return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED
;
765 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry
const* entry
, uint32 newValue
, bool relative
)
767 sLog
.outString("AchievementMgr::SetCriteriaProgress(%u, %u)", entry
->ID
, newValue
);
768 CriteriaProgress
*progress
= NULL
;
770 CriteriaProgressMap::iterator iter
= m_criteriaProgress
.find(entry
->ID
);
772 if(iter
== m_criteriaProgress
.end())
774 progress
= &m_criteriaProgress
[entry
->ID
];
775 progress
->counter
= 0;
776 progress
->date
= time(NULL
);
780 progress
= &iter
->second
;
782 newValue
+= progress
->counter
;
783 if(progress
->counter
== newValue
)
785 progress
->counter
= newValue
;
788 progress
->changed
= true;
792 time_t now
= time(NULL
);
793 if(progress
->date
+ entry
->timeLimit
< now
)
795 progress
->counter
= 1;
797 // also it seems illogical, the timeframe will be extended at every criteria update
798 progress
->date
= now
;
800 SendCriteriaUpdate(entry
->ID
,progress
);
803 void AchievementMgr::CompletedAchievement(AchievementEntry
const* achievement
)
805 sLog
.outString("AchievementMgr::CompletedAchievement(%u)", achievement
->ID
);
806 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
|| m_completedAchievements
.find(achievement
->ID
)!=m_completedAchievements
.end())
809 SendAchievementEarned(achievement
);
810 CompletedAchievementData
& ca
= m_completedAchievements
[achievement
->ID
];
811 ca
.date
= time(NULL
);
814 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
815 // TODO: where do set this instead?
816 if(!(achievement
->flags
& ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
817 objmgr
.allCompletedAchievements
.insert(achievement
->ID
);
819 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
);
821 // reward items and titles
822 AchievementReward
const* reward
= NULL
;
823 for (uint32 i
=0; i
<ACHIEVEMENT_REWARD_COUNT
; i
++)
825 if (achievementRewards
[i
].achievementId
== achievement
->ID
)
827 reward
= &achievementRewards
[i
];
834 sLog
.outString("achiev %u, title= %u, %u", reward
->achievementId
, reward
->titleId
[0], reward
->titleId
[1]);
835 uint32 titleId
= reward
->titleId
[GetPlayer()->GetTeam() == HORDE
?0:1];
836 if(CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(titleId
))
837 GetPlayer()->SetTitle(titleEntry
);
841 ItemPrototype
const *pProto
= objmgr
.GetItemPrototype( reward
->itemId
);
845 GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
849 ItemPosCountVec dest
;
851 uint8 msg
= GetPlayer()->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, reward
->itemId
, 1, &no_space
);
853 if( msg
!= EQUIP_ERR_OK
)
855 GetPlayer()->SendEquipError( msg
, NULL
, NULL
);
858 Item
* pItem
= GetPlayer()->StoreNewItem( dest
, reward
->itemId
, true);
862 GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
869 void AchievementMgr::SendAllAchievementData()
871 // since we don't know the exact size of the packed GUIDs this is just an approximation
872 WorldPacket
data(SMSG_ALL_ACHIEVEMENT_DATA
, 4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
873 BuildAllDataPacket(&data
);
874 GetPlayer()->GetSession()->SendPacket(&data
);
877 void AchievementMgr::SendRespondInspectAchievements(Player
* player
)
879 // since we don't know the exact size of the packed GUIDs this is just an approximation
880 WorldPacket
data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS
, 4+4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
881 data
.append(GetPlayer()->GetPackGUID());
882 BuildAllDataPacket(&data
);
883 player
->GetSession()->SendPacket(&data
);
887 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
889 void AchievementMgr::BuildAllDataPacket(WorldPacket
*data
)
891 for(CompletedAchievementMap::const_iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); ++iter
)
893 *data
<< uint32(iter
->first
);
894 *data
<< uint32(secsToTimeBitFields(iter
->second
.date
));
898 for(CriteriaProgressMap::const_iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
900 *data
<< uint32(iter
->first
);
901 data
->appendPackGUID(iter
->second
.counter
);
902 data
->append(GetPlayer()->GetPackGUID());
904 *data
<< uint32(secsToTimeBitFields(iter
->second
.date
));