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
);
540 if(IsCompletedCriteria(achievementCriteria
))
541 CompletedCriteria(achievementCriteria
);
545 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry
const* achievementCriteria
)
547 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(achievementCriteria
->referredAchievement
);
551 // counter can never complete
552 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
555 if(achievement
->flags
& (ACHIEVEMENT_FLAG_REALM_FIRST_REACH
| ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
557 // someone on this realm has already completed that achievement
558 if(objmgr
.allCompletedAchievements
.find(achievement
->ID
)!=objmgr
.allCompletedAchievements
.end())
562 CriteriaProgressMap::iterator itr
= m_criteriaProgress
.find(achievementCriteria
->ID
);
563 if(itr
== m_criteriaProgress
.end())
566 CriteriaProgress
*progress
= itr
->second
;
568 switch(achievementCriteria
->requiredType
)
570 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL
:
571 if(achievement
->ID
== 467 && GetPlayer()->getClass() != CLASS_SHAMAN
||
572 achievement
->ID
== 466 && GetPlayer()->getClass() != CLASS_DRUID
||
573 achievement
->ID
== 465 && GetPlayer()->getClass() != CLASS_PALADIN
||
574 achievement
->ID
== 464 && GetPlayer()->getClass() != CLASS_PRIEST
||
575 achievement
->ID
== 463 && GetPlayer()->getClass() != CLASS_WARLOCK
||
576 achievement
->ID
== 462 && GetPlayer()->getClass() != CLASS_HUNTER
||
577 achievement
->ID
== 461 && GetPlayer()->getClass() != CLASS_DEATH_KNIGHT
||
578 achievement
->ID
== 460 && GetPlayer()->getClass() != CLASS_MAGE
||
579 achievement
->ID
== 459 && GetPlayer()->getClass() != CLASS_WARRIOR
||
580 achievement
->ID
== 458 && GetPlayer()->getClass() != CLASS_ROGUE
||
582 achievement
->ID
== 1404 && GetPlayer()->getRace() != RACE_GNOME
||
583 achievement
->ID
== 1405 && GetPlayer()->getRace() != RACE_BLOODELF
||
584 achievement
->ID
== 1406 && GetPlayer()->getRace() != RACE_DRAENEI
||
585 achievement
->ID
== 1407 && GetPlayer()->getRace() != RACE_DWARF
||
586 achievement
->ID
== 1408 && GetPlayer()->getRace() != RACE_HUMAN
||
587 achievement
->ID
== 1409 && GetPlayer()->getRace() != RACE_NIGHTELF
||
588 achievement
->ID
== 1410 && GetPlayer()->getRace() != RACE_ORC
||
589 achievement
->ID
== 1411 && GetPlayer()->getRace() != RACE_TAUREN
||
590 achievement
->ID
== 1412 && GetPlayer()->getRace() != RACE_TROLL
||
591 achievement
->ID
== 1413 && GetPlayer()->getRace() != RACE_UNDEAD_PLAYER
)
593 return progress
->counter
>= achievementCriteria
->reach_level
.level
;
594 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT
:
595 return progress
->counter
>= achievementCriteria
->buy_bank_slot
.numberOfSlots
;
596 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE
:
597 return progress
->counter
>= achievementCriteria
->kill_creature
.creatureCount
;
598 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
:
599 return m_completedAchievements
.find(achievementCriteria
->complete_achievement
.linkedAchievement
) != m_completedAchievements
.end();
600 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL
:
601 return progress
->counter
>= achievementCriteria
->reach_skill_level
.skillLevel
;
602 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE
:
603 return progress
->counter
>= achievementCriteria
->complete_quests_in_zone
.questCount
;
604 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST
:
605 return progress
->counter
>= achievementCriteria
->complete_daily_quest
.questCount
;
606 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL
:
607 return progress
->counter
>= 1;
608 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING
:
609 return progress
->counter
>= achievementCriteria
->fall_without_dying
.fallHeight
;
610 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST
:
611 return progress
->counter
>= 1;
612 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM
:
613 return progress
->counter
>= achievementCriteria
->use_item
.itemCount
;
614 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM
:
615 return progress
->counter
>= achievementCriteria
->own_item
.itemCount
;
616 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM
:
617 return progress
->counter
>= achievementCriteria
->loot_item
.itemCount
;
618 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET
:
619 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2
:
620 return progress
->counter
>= achievementCriteria
->be_spell_target
.spellCount
;
621 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL
:
622 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2
:
623 return progress
->counter
>= achievementCriteria
->cast_spell
.castCount
;
624 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS
:
625 return progress
->counter
>= achievementCriteria
->learn_skilline_spell
.spellCount
;
626 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
:
627 return progress
->counter
>= achievementCriteria
->visit_barber
.numberOfVisits
;
628 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION
:
629 return progress
->counter
>= achievementCriteria
->gain_reputation
.reputationAmount
;
630 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION
:
631 return progress
->counter
>= achievementCriteria
->gain_exalted_reputation
.numberOfExaltedFactions
;
633 // handle all statistic-only criteria here
634 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
:
635 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP
:
636 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE
:
637 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER
:
643 void AchievementMgr::CompletedCriteria(AchievementCriteriaEntry
const* criteria
)
645 AchievementEntry
const* achievement
= sAchievementStore
.LookupEntry(criteria
->referredAchievement
);
648 // counter can never complete
649 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
)
652 if(criteria
->completionFlag
& ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
|| GetAchievementCompletionState(achievement
)==ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED
)
654 CompletedAchievement(achievement
);
658 // TODO: achievement 705 requires 4 criteria to be fulfilled
659 AchievementCompletionState
AchievementMgr::GetAchievementCompletionState(AchievementEntry
const* entry
)
661 if(m_completedAchievements
.find(entry
->ID
)!=m_completedAchievements
.end())
662 return ACHIEVEMENT_COMPLETED_COMPLETED_STORED
;
664 bool foundOutstanding
= false;
665 for (uint32 entryId
= 0; entryId
<sAchievementCriteriaStore
.GetNumRows(); entryId
++)
667 AchievementCriteriaEntry
const* criteria
= sAchievementCriteriaStore
.LookupEntry(entryId
);
668 if(!criteria
|| criteria
->referredAchievement
!= entry
->ID
)
671 if(IsCompletedCriteria(criteria
) && criteria
->completionFlag
& ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
)
672 return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED
;
674 // found an umcompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
675 if(!IsCompletedCriteria(criteria
))
676 foundOutstanding
= true;
679 return ACHIEVEMENT_COMPLETED_NONE
;
681 return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED
;
684 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry
const* entry
, uint32 newValue
, bool relative
)
686 sLog
.outString("AchievementMgr::SetCriteriaProgress(%u, %u)", entry
->ID
, newValue
);
687 CriteriaProgress
*progress
= NULL
;
689 if(m_criteriaProgress
.find(entry
->ID
) == m_criteriaProgress
.end())
691 progress
= new CriteriaProgress(entry
->ID
, newValue
);
692 m_criteriaProgress
[entry
->ID
]=progress
;
696 progress
= m_criteriaProgress
[entry
->ID
];
698 newValue
+= progress
->counter
;
699 if(progress
->counter
== newValue
)
701 progress
->counter
= newValue
;
705 time_t now
= time(NULL
);
706 if(progress
->date
+ entry
->timeLimit
< now
)
708 progress
->counter
= 1;
710 // also it seems illogical, the timeframe will be extended at every criteria update
711 progress
->date
= now
;
713 SendCriteriaUpdate(progress
);
716 void AchievementMgr::CompletedAchievement(AchievementEntry
const* achievement
)
718 sLog
.outString("AchievementMgr::CompletedAchievement(%u)", achievement
->ID
);
719 if(achievement
->flags
& ACHIEVEMENT_FLAG_COUNTER
|| m_completedAchievements
.find(achievement
->ID
)!=m_completedAchievements
.end())
722 SendAchievementEarned(achievement
);
723 m_completedAchievements
[achievement
->ID
] = time(NULL
);
725 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
726 // TODO: where do set this instead?
727 if(!(achievement
->flags
& ACHIEVEMENT_FLAG_REALM_FIRST_KILL
))
728 objmgr
.allCompletedAchievements
.insert(achievement
->ID
);
730 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT
);
732 // reward items and titles
733 AchievementReward
const* reward
= NULL
;
734 for (uint32 i
=0; i
<ACHIEVEMENT_REWARD_COUNT
; i
++)
736 if (achievementRewards
[i
].achievementId
== achievement
->ID
)
738 reward
= &achievementRewards
[i
];
745 sLog
.outString("achiev %u, title= %u, %u", reward
->achievementId
, reward
->titleId
[0], reward
->titleId
[1]);
746 uint32 titleId
= reward
->titleId
[GetPlayer()->GetTeam() == HORDE
?0:1];
747 if(CharTitlesEntry
const* titleEntry
= sCharTitlesStore
.LookupEntry(titleId
))
748 GetPlayer()->SetTitle(titleEntry
);
752 ItemPrototype
const *pProto
= objmgr
.GetItemPrototype( reward
->itemId
);
756 GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
760 ItemPosCountVec dest
;
762 uint8 msg
= GetPlayer()->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, reward
->itemId
, 1, &no_space
);
764 if( msg
!= EQUIP_ERR_OK
)
766 GetPlayer()->SendEquipError( msg
, NULL
, NULL
);
769 Item
* pItem
= GetPlayer()->StoreNewItem( dest
, reward
->itemId
, true);
773 GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND
, NULL
, NULL
);
780 void AchievementMgr::SendAllAchievementData()
782 // since we don't know the exact size of the packed GUIDs this is just an approximation
783 WorldPacket
data(SMSG_ALL_ACHIEVEMENT_DATA
, 4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
784 BuildAllDataPacket(&data
);
785 GetPlayer()->GetSession()->SendPacket(&data
);
788 void AchievementMgr::SendRespondInspectAchievements(Player
* player
)
790 // since we don't know the exact size of the packed GUIDs this is just an approximation
791 WorldPacket
data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS
, 4+4*2+m_completedAchievements
.size()*4*2+m_completedAchievements
.size()*7*4);
792 data
.append(GetPlayer()->GetPackGUID());
793 BuildAllDataPacket(&data
);
794 player
->GetSession()->SendPacket(&data
);
798 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
800 void AchievementMgr::BuildAllDataPacket(WorldPacket
*data
)
802 for(CompletedAchievementMap::iterator iter
= m_completedAchievements
.begin(); iter
!=m_completedAchievements
.end(); ++iter
)
804 *data
<< uint32(iter
->first
);
805 *data
<< uint32(secsToTimeBitFields(iter
->second
));
809 for(CriteriaProgressMap::iterator iter
= m_criteriaProgress
.begin(); iter
!=m_criteriaProgress
.end(); ++iter
)
811 *data
<< uint32(iter
->second
->id
);
812 data
->appendPackGUID(iter
->second
->counter
);
813 data
->append(GetPlayer()->GetPackGUID());
815 *data
<< uint32(secsToTimeBitFields(iter
->second
->date
));