[6982] Implemented gmlevel-based command security
[getmangos.git] / src / game / AchievementMgr.cpp
blobec1085d8a0274e41a31dc963149d69df9064f4b8
1 /*
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"
20 #include "Common.h"
21 #include "Player.h"
22 #include "WorldPacket.h"
23 #include "Database/DBCEnums.h"
24 #include "ObjectMgr.h"
25 #include "Guild.h"
26 #include "Database/DatabaseEnv.h"
27 #include "GameEvent.h"
28 #include "World.h"
29 #include "SpellMgr.h"
31 const CriteriaCastSpellRequirement AchievementMgr::criteriaCastSpellRequirements[CRITERIA_CAST_SPELL_REQ_COUNT] =
33 {5272, 3057, 0, 0},
34 {5273, 2784, 0, 0},
35 {5752, 9099, 0, 0},
36 {5753, 8403, 0, 0},
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},
47 {6225, 5661, 0, 0},
48 {6226, 26044, 0, 0},
49 {6228, 739, 0, 0},
50 {6229, 927, 0, 0},
51 {6230, 1444, 0, 0},
52 {6231, 8140, 0, 0},
53 {6232, 5489, 0, 0},
54 {6233,12336, 0, 0},
55 {6234, 1351, 0, 0},
56 {6235, 5484, 0, 0},
57 {6236, 1182, 0, 0},
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},
78 {6662, 31261, 0, 0}
81 const AchievementReward AchievementMgr::achievementRewards[ACHIEVEMENT_REWARD_COUNT] =
83 // achievementId, horde titleid, alliance titleid, itemid
84 {45, 0, 0, 43348},
85 {46, 78, 78, 0},
86 {230, 72, 72, 0},
87 {456, 139, 139, 0},
88 {614, 0, 0, 44223},
89 {619, 0, 0, 44224},
90 {714, 47, 47, 0},
91 {762, 130, 130, 0},
92 {870, 127, 126, 0},
93 {871, 144, 144, 0},
94 {876, 0, 0, 43349},
95 {907, 48, 48, 0},
96 {913, 74, 74, 0},
97 {942, 79, 79, 0},
98 {943, 79, 79, 0},
99 {945, 131, 131, 0},
100 {948, 130, 130, 0},
101 {953, 132, 132, 0},
102 {978, 81, 81, 0},
103 {1015, 77, 77, 0},
104 {1021, 0, 0, 40643},
105 {1038, 75, 75, 0},
106 {1039, 76, 76, 0},
107 {1163, 128, 128, 0},
108 {1174, 82, 82, 0},
109 {1175, 72, 72, 0},
110 {1250, 0, 0, 40653},
111 {1400, 120, 120, 0},
112 {1402, 122, 122, 0},
113 {1516, 83, 83, 0},
114 {1563, 84, 84, 0},
115 {1656, 124, 124, 0},
116 {1657, 124, 124, 0},
117 {1658, 129, 129, 0},
118 {1681, 125, 125, 43300},
119 {1682, 125, 125, 43300},
120 {1683, 133, 133, 0},
121 {1684, 133, 133, 0},
122 {1691, 134, 134, 0},
123 {1692, 134, 134, 0},
124 {1693, 135, 135, 0},
125 {1707, 135, 135, 0},
126 {1784, 84, 84, 0},
127 {1793, 137, 137, 0},
128 {1956, 0, 0, 43824},
129 {2051, 140, 140, 0},
130 {2054, 121, 121, 0},
131 {2096, 0, 0, 44430},
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
135 {2143, 0, 0, 44178},
136 {2144, 0, 0, 0},// <- TODO: find item for spell 60024
137 {2145, 0, 0, 0},// <- TODO: find item for spell 60024
138 {2186, 141, 141, 0},
139 {2187, 142, 142, 0},
140 {2188, 143, 143, 0}
143 AchievementMgr::AchievementMgr(Player *player)
145 m_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)
162 continue;
164 /// first new/changed record prefix
165 if(!need_execute)
167 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
168 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
169 need_execute = true;
171 /// next new/changed record prefix
172 else
174 ssdel << ", ";
175 ssins << ", ";
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;
186 if(need_execute)
187 ssdel << ")";
189 if(need_execute)
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)
207 continue;
209 /// first new/changed record prefix
210 if(!need_execute)
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 ";
214 need_execute = true;
216 /// next new/changed record prefix
217 else
219 ssdel << ", ";
220 ssins << ", ";
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;
231 if(need_execute)
232 ssdel << ")";
234 if(need_execute)
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());
253 ca.changed = false;
254 } while(achievementResult->NextRow());
255 delete achievementResult;
258 if(criteriaResult)
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))
270 continue;
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());
294 data << uint32(5);
295 data << uint64(GetPlayer()->GetGUID());
296 data << uint32(strlen(msg)+1);
297 data << msg;
298 data << uint8(0);
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);
312 else
314 WorldPacket data(SMSG_MESSAGECHAT, 200);
315 data << uint8(CHAT_MSG_ACHIEVEMENT);
316 data << uint32(LANG_UNIVERSAL);
317 data << uint64(GetPlayer()->GetGUID());
318 data << uint32(5);
319 data << uint64(GetPlayer()->GetGUID());
320 data << uint32(strlen(msg)+1);
321 data << msg;
322 data << uint8(0);
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)));
331 data << uint32(0);
332 GetPlayer()->SendMessageToSet(&data, true);
335 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
337 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
338 data << uint32(id);
340 // the counter is packed like a packed Guid
341 data.appendPackGUID(progress->counter);
343 data.append(GetPlayer()->GetPackGUID());
344 data << uint32(0);
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))
374 continue;
376 if(achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
377 continue;
379 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
380 if(!achievement)
381 continue;
383 if(achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE ||
384 achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE)
385 continue;
387 switch (type)
389 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
390 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
391 break;
392 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
393 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetByteValue(PLAYER_BYTES_2, 2)+1);
394 break;
395 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
396 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
397 if(!miscvalue1)
398 continue;
399 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
400 continue;
401 SetCriteriaProgress(achievementCriteria, miscvalue2, true);
402 break;
403 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
404 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
405 SetCriteriaProgress(achievementCriteria, skillvalue);
406 break;
407 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
409 uint32 counter =0;
410 for(QuestStatusMap::iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
411 if(itr->second.m_rewarded)
412 counter++;
413 SetCriteriaProgress(achievementCriteria, counter);
414 break;
416 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
418 uint32 counter =0;
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)
423 counter++;
425 SetCriteriaProgress(achievementCriteria, counter);
426 break;
428 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
429 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
430 if(!miscvalue1)
431 continue;
432 SetCriteriaProgress(achievementCriteria, miscvalue1, true);
433 break;
434 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
435 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
436 if(!miscvalue1)
437 continue;
438 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
439 continue;
440 SetCriteriaProgress(achievementCriteria, miscvalue1, true);
441 break;
442 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
443 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
444 SetCriteriaProgress(achievementCriteria, 1);
445 break;
446 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
447 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
448 if(!miscvalue1)
449 continue;
450 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
451 continue;
452 SetCriteriaProgress(achievementCriteria, 1, true);
453 break;
454 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
455 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
456 if(!miscvalue1)
457 continue;
458 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
459 continue;
460 SetCriteriaProgress(achievementCriteria, 1, true);
461 break;
462 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
463 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
464 if(!miscvalue1)
465 continue;
466 SetCriteriaProgress(achievementCriteria, 1, true);
467 break;
468 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
470 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
471 if(!miscvalue1)
472 continue;
473 if(achievement->ID == 1260)
475 if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED)
476 continue;
477 // TODO: hardcoding eventid is bad, it can differ from DB to DB - maye implement something using HolidayNames.dbc?
478 if(!gameeventmgr.IsActiveEvent(26))
479 continue;
481 // miscvalue1 is the ingame fallheight*100 as stored in dbc
482 SetCriteriaProgress(achievementCriteria, miscvalue1);
483 break;
485 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
486 if(GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
487 SetCriteriaProgress(achievementCriteria, 1);
488 break;
489 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
490 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
491 if(!miscvalue1)
492 continue;
493 if(achievementCriteria->use_item.itemID != miscvalue1)
494 continue;
495 SetCriteriaProgress(achievementCriteria, 1, true);
496 break;
497 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
498 // speedup for non-login case
499 if(miscvalue1 && achievementCriteria->own_item.itemID!=miscvalue1)
500 continue;
501 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
502 break;
503 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
504 // You _have_ to loot that item, just owning it when logging in does _not_ count!
505 if(!miscvalue1)
506 continue;
507 if(miscvalue1 != achievementCriteria->own_item.itemID)
508 continue;
509 SetCriteriaProgress(achievementCriteria, miscvalue2, true);
510 break;
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)
514 continue;
515 SetCriteriaProgress(achievementCriteria, 1, true);
516 break;
517 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
518 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
519 continue;
520 SetCriteriaProgress(achievementCriteria, 1, true);
521 break;
522 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
524 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
525 continue;
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];
534 break;
538 if (requirement)
540 if (!unit)
541 continue;
543 if (requirement->creatureEntry && unit->GetEntry() != requirement->creatureEntry)
544 continue;
546 if (requirement->playerRace && (unit->GetTypeId() != TYPEID_PLAYER || unit->getRace()!=requirement->playerRace))
547 continue;
549 if (requirement->playerClass && (unit->GetTypeId() != TYPEID_PLAYER || unit->getClass()!=requirement->playerClass))
550 continue;
552 SetCriteriaProgress(achievementCriteria, 1, true);
553 break;
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();
560 spellIter++)
562 for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first);
563 skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first);
564 skillIter++)
566 if(skillIter->second->skillId == achievementCriteria->learn_skilline_spell.skillLine)
567 spellCount++;
570 SetCriteriaProgress(achievementCriteria, spellCount);
571 break;
573 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
575 // skip for login case
576 if(!miscvalue1)
577 continue;
578 SetCriteriaProgress(achievementCriteria, 1);
579 break;
581 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
583 int32 reputation = GetPlayer()->GetReputation(achievementCriteria->gain_reputation.factionID);
584 if (reputation > 0)
585 SetCriteriaProgress(achievementCriteria, reputation);
586 break;
588 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
590 uint32 counter = 0;
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)
595 ++counter;
597 SetCriteriaProgress(achievementCriteria, counter);
598 break;
600 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
602 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
603 if(!worldOverlayEntry)
604 break;
606 int32 exploreFlag = GetAreaFlagByAreaID(worldOverlayEntry->areatableID);
607 if(exploreFlag < 0)
608 break;
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);
615 break;
619 if(IsCompletedCriteria(achievementCriteria))
620 CompletedCriteria(achievementCriteria);
624 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria)
626 AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
627 if(!achievement)
628 return false;
630 // counter can never complete
631 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
632 return false;
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())
638 return false;
641 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
642 if(itr == m_criteriaProgress.end())
643 return false;
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 )
671 return false;
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:
719 return false;
721 return false;
724 void AchievementMgr::CompletedCriteria(AchievementCriteriaEntry const* criteria)
726 AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement);
727 if(!achievement)
728 return;
729 // counter can never complete
730 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
731 return;
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)
750 continue;
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;
759 if(foundOutstanding)
760 return ACHIEVEMENT_COMPLETED_NONE;
761 else
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);
778 else
780 progress = &iter->second;
781 if(relative)
782 newValue += progress->counter;
783 if(progress->counter == newValue)
784 return;
785 progress->counter = newValue;
788 progress->changed = true;
790 if(entry->timeLimit)
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())
807 return;
809 SendAchievementEarned(achievement);
810 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
811 ca.date = time(NULL);
812 ca.changed = true;
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];
828 break;
832 if (reward)
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);
839 if (reward->itemId)
841 ItemPrototype const *pProto = objmgr.GetItemPrototype( reward->itemId );
843 if(!pProto)
845 GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
846 return;
849 ItemPosCountVec dest;
850 uint32 no_space = 0;
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 );
856 return;
858 Item* pItem = GetPlayer()->StoreNewItem( dest, reward->itemId, true);
860 if(!pItem)
862 GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
863 return;
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));
896 *data << int32(-1);
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());
903 *data << uint32(0);
904 *data << uint32(secsToTimeBitFields(iter->second.date));
905 *data << uint32(0);
906 *data << uint32(0);
909 *data << int32(-1);