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()
150 for(CriteriaProgressMap::iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
152 m_criteriaProgress
.clear();
155 void AchievementMgr::SaveToDB()
157 if(!m_completedAchievements
.empty())
159 CharacterDatabase
.PExecute("DELETE FROM character_achievement WHERE guid = %u", GetPlayer()->GetGUIDLow());
161 std::ostringstream ss
;
162 ss
<< "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
163 for(CompletedAchievementMap::iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); iter
++)
165 if(iter
!= m_completedAchievements
.begin())
167 ss
<< "("<<GetPlayer()->GetGUIDLow() << ", " << iter
->first
<< ", " << iter
->second
<< ")";
169 CharacterDatabase
.Execute( ss
.str().c_str() );
172 if(!m_criteriaProgress
.empty())
174 CharacterDatabase
.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u", GetPlayer()->GetGUIDLow());
176 std::ostringstream ss
;
177 ss
<< "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
178 for(CriteriaProgressMap::iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
180 if(iter
!= m_criteriaProgress
.begin())
182 ss
<< "(" << GetPlayer()->GetGUIDLow() << ", " << iter
->first
<< ", " << iter
->second
->counter
<< ", " << iter
->second
->date
<< ")";
184 CharacterDatabase
.Execute( ss
.str().c_str() );
188 void AchievementMgr::LoadFromDB(QueryResult
*achievementResult
, QueryResult
*criteriaResult
)
190 if(achievementResult
)
194 Field
*fields
= achievementResult
->Fetch();
195 m_completedAchievements
[fields
[0].GetUInt32()] = fields
[1].GetUInt32();
196 } while(achievementResult
->NextRow());
197 delete achievementResult
;
204 Field
*fields
= criteriaResult
->Fetch();
205 CriteriaProgress
*progress
= new CriteriaProgress(fields
[0].GetUInt32(), fields
[1].GetUInt32(), fields
[2].GetUInt64());
207 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(progress
->id
);
209 criteria
->timeLimit
&& progress
->date
+ criteria
->timeLimit
< time(NULL
))
214 m_criteriaProgress
[progress
->id
] = progress
;
215 } while(criteriaResult
->NextRow());
216 delete criteriaResult
;
221 void AchievementMgr::SendAchievementEarned(AchievementEntry
const* achievement
)
223 sLog
.outString("AchievementMgr::SendAchievementEarned(%u)", achievement
->ID
);
225 const char *msg
= "|Hplayer:$N|h[$N]|h has earned the achievement $a!";
226 if(Guild
* guild
= objmgr
.GetGuildById(GetPlayer()->GetGuildId()))
228 WorldPacket
data(SMSG_MESSAGECHAT
, 200);
229 data
<< uint8(CHAT_MSG_ACHIEVEMENT
);
230 data
<< uint8(CHAT_MSG_GUILD_ACHIEVEMENT
);
231 data
<< uint32(LANG_UNIVERSAL
);
232 data
<< uint64(GetPlayer()->GetGUID());
234 data
<< uint64(GetPlayer()->GetGUID());
235 data
<< uint32(strlen(msg
)+1);
238 data
<< uint32(achievement
->ID
);
239 guild
->BroadcastPacket(&data
);
241 if(achievement
->flags
& (ACHIEVEMENT_FLAG_REALM_FIRST_KILL
|ACHIEVEMENT_FLAG_REALM_FIRST_REACH
))
243 // broadcast realm first reached
244 WorldPacket
data(SMSG_SERVER_FIRST_ACHIEVEMENT
, strlen(GetPlayer()->GetName())+1+8+4+4);
245 data
<< GetPlayer()->GetName();
246 data
<< uint64(GetPlayer()->GetGUID());
247 data
<< uint32(achievement
->ID
);
248 data
<< uint32(0); // 1=link supplied string as player name, 0=display plain string
249 sWorld
.SendGlobalMessage(&data
);
253 WorldPacket
data(SMSG_MESSAGECHAT
, 200);
254 data
<< uint8(CHAT_MSG_ACHIEVEMENT
);
255 data
<< uint32(LANG_UNIVERSAL
);
256 data
<< uint64(GetPlayer()->GetGUID());
258 data
<< uint64(GetPlayer()->GetGUID());
259 data
<< uint32(strlen(msg
)+1);
262 data
<< uint32(achievement
->ID
);
263 GetPlayer()->SendMessageToSet(&data
, true);
266 WorldPacket
data(SMSG_ACHIEVEMENT_EARNED
, 8+4+8);
267 data
.append(GetPlayer()->GetPackGUID());
268 data
<< uint32(achievement
->ID
);
269 data
<< uint32(secsToTimeBitFields(time(NULL
)));
271 GetPlayer()->SendMessageToSet(&data
, true);
274 void AchievementMgr::SendCriteriaUpdate(CriteriaProgress
*progress
)
276 WorldPacket
data(SMSG_CRITERIA_UPDATE
, 8+4+8);
277 data
<< uint32(progress
->id
);
279 // the counter is packed like a packed Guid
280 data
.appendPackGUID(progress
->counter
);
282 data
.append(GetPlayer()->GetPackGUID());
284 data
<< uint32(secsToTimeBitFields(progress
->date
));
285 data
<< uint32(0); // timer 1
286 data
<< uint32(0); // timer 2
287 GetPlayer()->SendMessageToSet(&data
, true);
291 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
293 void AchievementMgr::CheckAllAchievementCriteria()
295 // suppress sending packets
296 for(uint32 i
=0; i
<ACHIEVEMENT_CRITERIA_TYPE_TOTAL
; i
++)
297 UpdateAchievementCriteria(AchievementCriteriaTypes(i
));
301 * this function will be called whenever the user might have done a criteria relevant action
303 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type
, uint32 miscvalue1
, uint32 miscvalue2
, Unit
*unit
, uint32 time
)
305 sLog
.outString("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type
, miscvalue1
, miscvalue2
, time
);
306 AchievementCriteriaEntryList
const& achievementCriteriaList
= objmgr
.GetAchievementCriteriaByType(type
);
307 for(AchievementCriteriaEntryList::const_iterator i
= achievementCriteriaList
.begin(); i
!=achievementCriteriaList
.end(); ++i
)
309 AchievementCriteriaEntry
const *achievementCriteria
= (*i
);
311 // don't update already completed criteria
312 if(IsCompletedCriteria(achievementCriteria
))
315 if(achievementCriteria
->groupFlag
& ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP
&& GetPlayer()->GetGroup())
318 AchievementEntry
const *achievement
= sAchievementStore
.LookupEntry(achievementCriteria
->referredAchievement
);
322 if(achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_HORDE
&& GetPlayer()->GetTeam() != HORDE
||
323 achievement
->factionFlag
== ACHIEVEMENT_FACTION_FLAG_ALLIANCE
&& GetPlayer()->GetTeam() != ALLIANCE
)
328 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
329 SetCriteriaProgress(achievementCriteria
, GetPlayer()->getLevel());
331 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
332 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetByteValue(PLAYER_BYTES_2
, 2)+1);
334 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
335 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
338 if(achievementCriteria
->kill_creature
.creatureID
!= miscvalue1
)
340 SetCriteriaProgress(achievementCriteria
, miscvalue2
, true);
342 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
:
343 if(uint32 skillvalue
= GetPlayer()->GetBaseSkillValue(achievementCriteria
->reach_skill_level
.skillID
))
344 SetCriteriaProgress(achievementCriteria
, skillvalue
);
346 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT
:
349 for(QuestStatusMap::iterator itr
= GetPlayer()->getQuestStatusMap().begin(); itr
!=GetPlayer()->getQuestStatusMap().end(); itr
++)
350 if(itr
->second
.m_rewarded
)
352 SetCriteriaProgress(achievementCriteria
, counter
);
355 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
:
358 for(QuestStatusMap::iterator itr
= GetPlayer()->getQuestStatusMap().begin(); itr
!=GetPlayer()->getQuestStatusMap().end(); itr
++)
360 Quest
const* quest
= objmgr
.GetQuestTemplate(itr
->first
);
361 if(itr
->second
.m_rewarded
&& quest
->GetZoneOrSort() == achievementCriteria
->complete_quests_in_zone
.zoneID
)
364 SetCriteriaProgress(achievementCriteria
, counter
);
367 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
:
368 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
371 SetCriteriaProgress(achievementCriteria
, miscvalue1
, true);
373 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
374 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
377 if(GetPlayer()->GetMapId() != achievementCriteria
->complete_battleground
.mapID
)
379 SetCriteriaProgress(achievementCriteria
, miscvalue1
, true);
381 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
382 if(GetPlayer()->HasSpell(achievementCriteria
->learn_spell
.spellID
))
383 SetCriteriaProgress(achievementCriteria
, 1);
385 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
386 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
389 if(GetPlayer()->GetMapId() != achievementCriteria
->death_at_map
.mapID
)
391 SetCriteriaProgress(achievementCriteria
, 1, true);
393 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
394 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
397 if(miscvalue1
!= achievementCriteria
->killed_by_creature
.creatureEntry
)
399 SetCriteriaProgress(achievementCriteria
, 1, true);
401 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
402 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
405 SetCriteriaProgress(achievementCriteria
, 1, true);
407 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
409 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
412 if(achievement
->ID
== 1260)
414 if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED
)
416 // TODO: hardcoding eventid is bad, it can differ from DB to DB - maye implement something using HolidayNames.dbc?
417 if(!gameeventmgr
.IsActiveEvent(26))
420 // miscvalue1 is the ingame fallheight*100 as stored in dbc
421 SetCriteriaProgress(achievementCriteria
, miscvalue1
);
424 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
425 if(GetPlayer()->GetQuestRewardStatus(achievementCriteria
->complete_quest
.questID
))
426 SetCriteriaProgress(achievementCriteria
, 1);
428 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
429 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
432 if(achievementCriteria
->use_item
.itemID
!= miscvalue1
)
434 SetCriteriaProgress(achievementCriteria
, 1, true);
436 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
437 // speedup for non-login case
438 if(miscvalue1
&& achievementCriteria
->own_item
.itemID
!=miscvalue1
)
440 SetCriteriaProgress(achievementCriteria
, GetPlayer()->GetItemCount(achievementCriteria
->own_item
.itemID
, true));
442 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
443 // You _have_ to loot that item, just owning it when logging in does _not_ count!
446 if(miscvalue1
!= achievementCriteria
->own_item
.itemID
)
448 SetCriteriaProgress(achievementCriteria
, miscvalue2
, true);
450 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET
:
451 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2
:
452 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->be_spell_target
.spellID
)
454 SetCriteriaProgress(achievementCriteria
, 1, true);
456 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
457 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->cast_spell
.spellID
)
459 SetCriteriaProgress(achievementCriteria
, 1, true);
461 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
463 if (!miscvalue1
|| miscvalue1
!= achievementCriteria
->cast_spell
.spellID
)
465 // those requirements couldn't be found in the dbc
467 const CriteriaCastSpellRequirement
*requirement
= NULL
;
468 for (uint32 i
=0; i
<CRITERIA_CAST_SPELL_REQ_COUNT
; i
++)
470 if (criteriaCastSpellRequirements
[i
].achievementCriteriaId
== achievementCriteria
->ID
)
472 requirement
= &criteriaCastSpellRequirements
[i
];
482 if (requirement
->creatureEntry
&& unit
->GetEntry() != requirement
->creatureEntry
)
485 if (requirement
->playerRace
&& (unit
->GetTypeId() != TYPEID_PLAYER
|| unit
->getRace()!=requirement
->playerRace
))
488 if (requirement
->playerClass
&& (unit
->GetTypeId() != TYPEID_PLAYER
|| unit
->getClass()!=requirement
->playerClass
))
491 SetCriteriaProgress(achievementCriteria
, 1, true);
494 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
496 uint32 spellCount
= 0;
497 for (PlayerSpellMap::const_iterator spellIter
= GetPlayer()->GetSpellMap().begin();
498 spellIter
!= GetPlayer()->GetSpellMap().end();
501 for(SkillLineAbilityMap::const_iterator skillIter
= spellmgr
.GetBeginSkillLineAbilityMap(spellIter
->first
);
502 skillIter
!= spellmgr
.GetEndSkillLineAbilityMap(spellIter
->first
);
505 if(skillIter
->second
->skillId
== achievementCriteria
->learn_skilline_spell
.skillLine
)
509 SetCriteriaProgress(achievementCriteria
, spellCount
);
512 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
514 // skip for login case
517 SetCriteriaProgress(achievementCriteria
, 1);
520 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
522 int32 reputation
= GetPlayer()->GetReputation(achievementCriteria
->gain_reputation
.factionID
);
524 SetCriteriaProgress(achievementCriteria
, reputation
);
527 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
530 const FactionStateList factionStateList
= GetPlayer()->GetFactionStateList();
531 for (FactionStateList::const_iterator iter
= factionStateList
.begin(); iter
!= factionStateList
.end(); iter
++)
533 if(GetPlayer()->ReputationToRank(iter
->second
.Standing
) >= REP_EXALTED
)
536 SetCriteriaProgress(achievementCriteria
, counter
);
539 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
:
541 WorldMapOverlayEntry
const* worldOverlayEntry
= sWorldMapOverlayStore
.LookupEntry(achievementCriteria
->explore_area
.areaReference
);
542 if(!worldOverlayEntry
)
545 int32 exploreFlag
= GetAreaFlagByAreaID(worldOverlayEntry
->areatableID
);
549 uint32 playerIndexOffset
= uint32(exploreFlag
) / 32;
550 uint32 mask
= 1<< (uint32(exploreFlag
) % 32);
552 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1
+ playerIndexOffset
) & mask
)
553 SetCriteriaProgress(achievementCriteria
, 1);
558 if(IsCompletedCriteria(achievementCriteria
))
559 CompletedCriteria(achievementCriteria
);
563 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry
const* achievementCriteria
)
565 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(achievementCriteria
->referredAchievement
);
569 // counter can never complete
570 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
573 if(achievement
->flags
& (ACHIEVEMENT_FLAG_REALM_FIRST_REACH
| ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
575 // someone on this realm has already completed that achievement
576 if(objmgr
.allCompletedAchievements
.find(achievement
->ID
)!=objmgr
.allCompletedAchievements
.end())
580 CriteriaProgressMap::iterator itr
= m_criteriaProgress
.find(achievementCriteria
->ID
);
581 if(itr
== m_criteriaProgress
.end())
584 CriteriaProgress
*progress
= itr
->second
;
586 switch(achievementCriteria
->requiredType
)
588 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
589 if(achievement
->ID
== 467 && GetPlayer()->getClass() != CLASS_SHAMAN
||
590 achievement
->ID
== 466 && GetPlayer()->getClass() != CLASS_DRUID
||
591 achievement
->ID
== 465 && GetPlayer()->getClass() != CLASS_PALADIN
||
592 achievement
->ID
== 464 && GetPlayer()->getClass() != CLASS_PRIEST
||
593 achievement
->ID
== 463 && GetPlayer()->getClass() != CLASS_WARLOCK
||
594 achievement
->ID
== 462 && GetPlayer()->getClass() != CLASS_HUNTER
||
595 achievement
->ID
== 461 && GetPlayer()->getClass() != CLASS_DEATH_KNIGHT
||
596 achievement
->ID
== 460 && GetPlayer()->getClass() != CLASS_MAGE
||
597 achievement
->ID
== 459 && GetPlayer()->getClass() != CLASS_WARRIOR
||
598 achievement
->ID
== 458 && GetPlayer()->getClass() != CLASS_ROGUE
||
600 achievement
->ID
== 1404 && GetPlayer()->getRace() != RACE_GNOME
||
601 achievement
->ID
== 1405 && GetPlayer()->getRace() != RACE_BLOODELF
||
602 achievement
->ID
== 1406 && GetPlayer()->getRace() != RACE_DRAENEI
||
603 achievement
->ID
== 1407 && GetPlayer()->getRace() != RACE_DWARF
||
604 achievement
->ID
== 1408 && GetPlayer()->getRace() != RACE_HUMAN
||
605 achievement
->ID
== 1409 && GetPlayer()->getRace() != RACE_NIGHTELF
||
606 achievement
->ID
== 1410 && GetPlayer()->getRace() != RACE_ORC
||
607 achievement
->ID
== 1411 && GetPlayer()->getRace() != RACE_TAUREN
||
608 achievement
->ID
== 1412 && GetPlayer()->getRace() != RACE_TROLL
||
609 achievement
->ID
== 1413 && GetPlayer()->getRace() != RACE_UNDEAD_PLAYER
)
611 return progress
->counter
>= achievementCriteria
->reach_level
.level
;
612 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
613 return progress
->counter
>= achievementCriteria
->buy_bank_slot
.numberOfSlots
;
614 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
615 return progress
->counter
>= achievementCriteria
->kill_creature
.creatureCount
;
616 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
:
617 return m_completedAchievements
.find(achievementCriteria
->complete_achievement
.linkedAchievement
) != m_completedAchievements
.end();
618 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
:
619 return progress
->counter
>= achievementCriteria
->reach_skill_level
.skillLevel
;
620 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
:
621 return progress
->counter
>= achievementCriteria
->complete_quests_in_zone
.questCount
;
622 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
:
623 return progress
->counter
>= achievementCriteria
->complete_daily_quest
.questCount
;
624 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
625 return progress
->counter
>= 1;
626 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
627 return progress
->counter
>= achievementCriteria
->fall_without_dying
.fallHeight
;
628 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
629 return progress
->counter
>= 1;
630 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
631 return progress
->counter
>= achievementCriteria
->use_item
.itemCount
;
632 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
633 return progress
->counter
>= achievementCriteria
->own_item
.itemCount
;
634 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
635 return progress
->counter
>= achievementCriteria
->loot_item
.itemCount
;
636 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET
:
637 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2
:
638 return progress
->counter
>= achievementCriteria
->be_spell_target
.spellCount
;
639 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
640 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
641 return progress
->counter
>= achievementCriteria
->cast_spell
.castCount
;
642 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
643 return progress
->counter
>= achievementCriteria
->learn_skilline_spell
.spellCount
;
644 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
645 return progress
->counter
>= achievementCriteria
->visit_barber
.numberOfVisits
;
646 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
647 return progress
->counter
>= achievementCriteria
->gain_reputation
.reputationAmount
;
648 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
649 return progress
->counter
>= achievementCriteria
->gain_exalted_reputation
.numberOfExaltedFactions
;
650 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA
:
651 return progress
->counter
>= 1;
653 // handle all statistic-only criteria here
654 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
655 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
656 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
657 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
663 void AchievementMgr::CompletedCriteria(AchievementCriteriaEntry
const* criteria
)
665 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(criteria
->referredAchievement
);
668 // counter can never complete
669 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
672 if(criteria
->completionFlag
& ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
|| GetAchievementCompletionState(achievement
)==ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED
)
674 CompletedAchievement(achievement
);
678 // TODO: achievement 705 requires 4 criteria to be fulfilled
679 AchievementCompletionState
AchievementMgr::GetAchievementCompletionState(AchievementEntry
const* entry
)
681 if(m_completedAchievements
.find(entry
->ID
)!=m_completedAchievements
.end())
682 return ACHIEVEMENT_COMPLETED_COMPLETED_STORED
;
684 bool foundOutstanding
= false;
685 for (uint32 entryId
= 0; entryId
<sAchievementCriteriaStore
.GetNumRows(); entryId
++)
687 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(entryId
);
688 if(!criteria
|| criteria
->referredAchievement
!= entry
->ID
)
691 if(IsCompletedCriteria(criteria
) && criteria
->completionFlag
& ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
)
692 return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED
;
694 // found an umcompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
695 if(!IsCompletedCriteria(criteria
))
696 foundOutstanding
= true;
699 return ACHIEVEMENT_COMPLETED_NONE
;
701 return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED
;
704 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry
const* entry
, uint32 newValue
, bool relative
)
706 sLog
.outString("AchievementMgr::SetCriteriaProgress(%u, %u)", entry
->ID
, newValue
);
707 CriteriaProgress
*progress
= NULL
;
709 if(m_criteriaProgress
.find(entry
->ID
) == m_criteriaProgress
.end())
711 progress
= new CriteriaProgress(entry
->ID
, newValue
);
712 m_criteriaProgress
[entry
->ID
]=progress
;
716 progress
= m_criteriaProgress
[entry
->ID
];
718 newValue
+= progress
->counter
;
719 if(progress
->counter
== newValue
)
721 progress
->counter
= newValue
;
725 time_t now
= time(NULL
);
726 if(progress
->date
+ entry
->timeLimit
< now
)
728 progress
->counter
= 1;
730 // also it seems illogical, the timeframe will be extended at every criteria update
731 progress
->date
= now
;
733 SendCriteriaUpdate(progress
);
736 void AchievementMgr::CompletedAchievement(AchievementEntry
const* achievement
)
738 sLog
.outString("AchievementMgr::CompletedAchievement(%u)", achievement
->ID
);
739 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
|| m_completedAchievements
.find(achievement
->ID
)!=m_completedAchievements
.end())
742 SendAchievementEarned(achievement
);
743 m_completedAchievements
[achievement
->ID
] = time(NULL
);
745 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
746 // TODO: where do set this instead?
747 if(!(achievement
->flags
& ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
748 objmgr
.allCompletedAchievements
.insert(achievement
->ID
);
750 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
);
752 // reward items and titles
753 AchievementReward
const* reward
= NULL
;
754 for (uint32 i
=0; i
<ACHIEVEMENT_REWARD_COUNT
; i
++)
756 if (achievementRewards
[i
].achievementId
== achievement
->ID
)
758 reward
= &achievementRewards
[i
];
765 sLog
.outString("achiev %u, title= %u, %u", reward
->achievementId
, reward
->titleId
[0], reward
->titleId
[1]);
766 uint32 titleId
= reward
->titleId
[GetPlayer()->GetTeam() == HORDE
?0:1];
767 if(CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(titleId
))
768 GetPlayer()->SetTitle(titleEntry
);
772 ItemPrototype
const *pProto
= objmgr
.GetItemPrototype( reward
->itemId
);
776 GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
780 ItemPosCountVec dest
;
782 uint8 msg
= GetPlayer()->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, reward
->itemId
, 1, &no_space
);
784 if( msg
!= EQUIP_ERR_OK
)
786 GetPlayer()->SendEquipError( msg
, NULL
, NULL
);
789 Item
* pItem
= GetPlayer()->StoreNewItem( dest
, reward
->itemId
, true);
793 GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
800 void AchievementMgr::SendAllAchievementData()
802 // since we don't know the exact size of the packed GUIDs this is just an approximation
803 WorldPacket
data(SMSG_ALL_ACHIEVEMENT_DATA
, 4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
804 BuildAllDataPacket(&data
);
805 GetPlayer()->GetSession()->SendPacket(&data
);
808 void AchievementMgr::SendRespondInspectAchievements(Player
* player
)
810 // since we don't know the exact size of the packed GUIDs this is just an approximation
811 WorldPacket
data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS
, 4+4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
812 data
.append(GetPlayer()->GetPackGUID());
813 BuildAllDataPacket(&data
);
814 player
->GetSession()->SendPacket(&data
);
818 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
820 void AchievementMgr::BuildAllDataPacket(WorldPacket
*data
)
822 for(CompletedAchievementMap::iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); ++iter
)
824 *data
<< uint32(iter
->first
);
825 *data
<< uint32(secsToTimeBitFields(iter
->second
));
829 for(CriteriaProgressMap::iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
831 *data
<< uint32(iter
->second
->id
);
832 data
->appendPackGUID(iter
->second
->counter
);
833 data
->append(GetPlayer()->GetPackGUID());
835 *data
<< uint32(secsToTimeBitFields(iter
->second
->date
));