[7915] Implement more stricted checks and limitations at loading creature addon data.
[getmangos.git] / src / game / AchievementMgr.cpp
blobf01f0b835d9cda9110019bb4227958f02cca3fdf
1 /*
2 * Copyright (C) 2005-2009 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 "DBCEnums.h"
24 #include "GameEventMgr.h"
25 #include "ObjectMgr.h"
26 #include "Guild.h"
27 #include "Database/DatabaseEnv.h"
28 #include "World.h"
29 #include "SpellMgr.h"
30 #include "ArenaTeam.h"
31 #include "ProgressBar.h"
32 #include "GridNotifiersImpl.h"
33 #include "CellImpl.h"
34 #include "Language.h"
36 #include "Policies/SingletonImp.h"
38 INSTANTIATE_SINGLETON_1(AchievementGlobalMgr);
40 namespace MaNGOS
42 class AchievementChatBuilder
44 public:
45 AchievementChatBuilder(Player const& pl, ChatMsg msgtype, int32 textId, uint32 ach_id)
46 : i_player(pl), i_msgtype(msgtype), i_textId(textId), i_achievementId(ach_id) {}
47 void operator()(WorldPacket& data, int32 loc_idx)
49 char const* text = objmgr.GetMangosString(i_textId,loc_idx);
51 data << uint8(i_msgtype);
52 data << uint32(LANG_UNIVERSAL);
53 data << uint64(i_player.GetGUID());
54 data << uint32(5);
55 data << uint64(i_player.GetGUID());
56 data << uint32(strlen(text)+1);
57 data << text;
58 data << uint8(0);
59 data << uint32(i_achievementId);
62 private:
63 Player const& i_player;
64 ChatMsg i_msgtype;
65 int32 i_textId;
66 uint32 i_achievementId;
68 } // namespace MaNGOS
71 bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
73 if(dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE)
75 sLog.outErrorDb( "Table `achievement_criteria_data` for criteria (Entry: %u) have wrong data type (%u), ignore.", criteria->ID,dataType);
76 return false;
79 switch(criteria->requiredType)
81 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
82 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
83 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
84 break;
85 default:
86 sLog.outErrorDb( "Table `achievement_criteria_data` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
87 return false;
90 switch(dataType)
92 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
93 return true;
94 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
95 if(!creature.id || !objmgr.GetCreatureTemplate(creature.id))
97 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed creature id in value1 (%u), ignore.",
98 criteria->ID, criteria->requiredType,dataType,creature.id);
99 return false;
101 return true;
102 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
103 if(!classRace.class_id && !classRace.race_id)
105 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE (%u) must have not 0 in one from value fields, ignore.",
106 criteria->ID, criteria->requiredType,dataType);
107 return false;
109 if(classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0)
111 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed class in value1 (%u), ignore.",
112 criteria->ID, criteria->requiredType,dataType,classRace.class_id);
113 return false;
115 if(classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0)
117 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) have not existed race in value2 (%u), ignore.",
118 criteria->ID, criteria->requiredType,dataType,classRace.race_id);
119 return false;
121 return true;
122 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
123 if(health.percent < 1 || health.percent > 100)
125 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) have wrong percent value in value1 (%u), ignore.",
126 criteria->ID, criteria->requiredType,dataType,health.percent);
127 return false;
129 return true;
130 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD:
131 if(player_dead.own_team_flag > 1)
133 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong boolean value1 (%u).",
134 criteria->ID, criteria->requiredType,dataType,player_dead.own_team_flag);
135 return false;
137 return true;
138 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
139 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
141 SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id);
142 if(!spellEntry)
144 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell id in value1 (%u), ignore.",
145 criteria->ID, criteria->requiredType,(dataType==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.spell_id);
146 return false;
148 if(aura.effect_idx >= 3)
150 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell effect index in value2 (%u), ignore.",
151 criteria->ID, criteria->requiredType,(dataType==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.effect_idx);
152 return false;
154 if(!spellEntry->EffectApplyAuraName[aura.effect_idx])
156 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have non-aura spell effect (ID: %u Effect: %u), ignore.",
157 criteria->ID, criteria->requiredType,(dataType==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.spell_id,aura.effect_idx);
158 return false;
160 return true;
162 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
163 if(!GetAreaEntryByAreaID(area.id))
165 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA (%u) have wrong area id in value1 (%u), ignore.",
166 criteria->ID, criteria->requiredType,dataType,area.id);
167 return false;
169 return true;
170 default:
171 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,dataType);
172 return false;
174 return false;
177 bool AchievementCriteriaData::Meets(Player const* source, Unit const* target) const
179 switch(dataType)
181 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
182 return true;
183 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
184 if (!target || target->GetTypeId()!=TYPEID_UNIT)
185 return false;
186 if (target->GetEntry() != creature.id)
187 return false;
188 return true;
189 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
190 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
191 return false;
192 if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass())
193 return false;
194 if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace())
195 return false;
196 return true;
197 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
198 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
199 return false;
200 return target->GetHealth()*100 <= health.percent*target->GetMaxHealth();
201 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD:
202 if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || ((Player*)target)->GetDeathTimer() == 0)
203 return false;
204 // flag set == must be same team, not set == different team
205 return (((Player*)target)->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0);
206 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
207 return source->HasAura(aura.spell_id,aura.effect_idx);
208 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
210 uint32 zone_id,area_id;
211 source->GetZoneAndAreaId(zone_id,area_id);
212 return area.id==zone_id || area.id==area_id;
214 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
215 return target && target->HasAura(aura.spell_id,aura.effect_idx);
218 return false;
221 bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target) const
223 for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr)
224 if(!itr->Meets(source,target))
225 return false;
227 return true;
230 AchievementMgr::AchievementMgr(Player *player)
232 m_player = player;
235 AchievementMgr::~AchievementMgr()
239 void AchievementMgr::Reset()
241 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
243 WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
244 data << uint32(iter->first);
245 m_player->SendDirectMessage(&data);
248 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
250 WorldPacket data(SMSG_CRITERIA_DELETED,4);
251 data << uint32(iter->first);
252 m_player->SendDirectMessage(&data);
255 m_completedAchievements.clear();
256 m_criteriaProgress.clear();
257 DeleteFromDB(m_player->GetGUIDLow());
259 // re-fill data
260 CheckAllAchievementCriteria();
263 void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2)
265 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
266 sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2);
268 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
269 return;
271 AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
272 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
274 AchievementCriteriaEntry const *achievementCriteria = (*i);
276 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
277 if (!achievement)
278 continue;
280 // don't update already completed criteria
281 if (IsCompletedCriteria(achievementCriteria,achievement))
282 continue;
284 switch (type)
286 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset
287 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset
288 if (achievementCriteria->healing_done.flag == miscvalue1 &&
289 achievementCriteria->healing_done.mapid == miscvalue2)
290 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
291 break;
292 default: // reset all cases
293 break;
298 void AchievementMgr::DeleteFromDB(uint32 lowguid)
300 CharacterDatabase.BeginTransaction ();
301 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid);
302 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
303 CharacterDatabase.CommitTransaction ();
306 void AchievementMgr::SaveToDB()
308 if(!m_completedAchievements.empty())
310 bool need_execute = false;
311 std::ostringstream ssdel;
312 std::ostringstream ssins;
313 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
315 if(!iter->second.changed)
316 continue;
318 /// first new/changed record prefix
319 if(!need_execute)
321 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
322 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
323 need_execute = true;
325 /// next new/changed record prefix
326 else
328 ssdel << ", ";
329 ssins << ", ";
332 // new/changed record data
333 ssdel << iter->first;
334 ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
336 /// mark as saved in db
337 iter->second.changed = false;
340 if(need_execute)
341 ssdel << ")";
343 if(need_execute)
345 CharacterDatabase.Execute( ssdel.str().c_str() );
346 CharacterDatabase.Execute( ssins.str().c_str() );
350 if(!m_criteriaProgress.empty())
352 /// prepare deleting and insert
353 bool need_execute_del = false;
354 bool need_execute_ins = false;
355 std::ostringstream ssdel;
356 std::ostringstream ssins;
357 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
359 if(!iter->second.changed)
360 continue;
362 // deleted data (including 0 progress state)
364 /// first new/changed record prefix (for any counter value)
365 if(!need_execute_del)
367 ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
368 need_execute_del = true;
370 /// next new/changed record prefix
371 else
372 ssdel << ", ";
374 // new/changed record data
375 ssdel << iter->first;
378 // store data only for real progress
379 if(iter->second.counter != 0)
381 /// first new/changed record prefix
382 if(!need_execute_ins)
384 ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
385 need_execute_ins = true;
387 /// next new/changed record prefix
388 else
389 ssins << ", ";
391 // new/changed record data
392 ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
395 /// mark as updated in db
396 iter->second.changed = false;
399 if(need_execute_del) // DELETE ... IN (.... _)_
400 ssdel << ")";
402 if(need_execute_del || need_execute_ins)
404 if(need_execute_del)
405 CharacterDatabase.Execute( ssdel.str().c_str() );
406 if(need_execute_ins)
407 CharacterDatabase.Execute( ssins.str().c_str() );
412 void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
414 if(achievementResult)
418 Field *fields = achievementResult->Fetch();
420 uint32 achievement_id = fields[0].GetUInt32();
422 // don't must happen: cleanup at server startup in achievementmgr.LoadCompletedAchievements()
423 if(!sAchievementStore.LookupEntry(achievement_id))
424 continue;
426 CompletedAchievementData& ca = m_completedAchievements[achievement_id];
427 ca.date = time_t(fields[1].GetUInt64());
428 ca.changed = false;
429 } while(achievementResult->NextRow());
430 delete achievementResult;
433 if(criteriaResult)
437 Field *fields = criteriaResult->Fetch();
439 uint32 id = fields[0].GetUInt32();
440 uint32 counter = fields[1].GetUInt32();
441 time_t date = time_t(fields[2].GetUInt64());
443 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
444 if (!criteria)
446 // we will remove not existed criteria for all characters
447 sLog.outError("Not existed achievement creataria %u data removed from table `character_achievement_progress`.",id);
448 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id);
449 continue;
452 if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))
453 continue;
455 CriteriaProgress& progress = m_criteriaProgress[id];
456 progress.counter = counter;
457 progress.date = date;
458 progress.changed = false;
459 } while(criteriaResult->NextRow());
460 delete criteriaResult;
465 void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
467 if(GetPlayer()->GetSession()->PlayerLoading())
468 return;
470 #ifdef MANGOS_DEBUG
471 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
472 sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
473 #endif
475 if(Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()))
477 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
478 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
479 guild->BroadcastWorker(say_do,GetPlayer());
482 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
484 // broadcast realm first reached
485 WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4);
486 data << GetPlayer()->GetName();
487 data << uint64(GetPlayer()->GetGUID());
488 data << uint32(achievement->ID);
489 data << uint32(0); // 1=link supplied string as player name, 0=display plain string
490 sWorld.SendGlobalMessage(&data);
492 else
494 CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
496 Cell cell(p);
497 cell.data.Part.reserved = ALL_DISTRICT;
498 cell.SetNoCreate();
500 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
501 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
502 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do);
503 TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> >, WorldTypeMapContainer > message(say_worker);
504 CellLock<GridReadGuard> cell_lock(cell, p);
505 cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap());
508 WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
509 data.append(GetPlayer()->GetPackGUID());
510 data << uint32(achievement->ID);
511 data << uint32(secsToTimeBitFields(time(NULL)));
512 data << uint32(0);
513 GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true);
516 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
518 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
519 data << uint32(id);
521 // the counter is packed like a packed Guid
522 data.appendPackGUID(progress->counter);
524 data.append(GetPlayer()->GetPackGUID());
525 data << uint32(0);
526 data << uint32(secsToTimeBitFields(progress->date));
527 data << uint32(0); // timer 1
528 data << uint32(0); // timer 2
529 GetPlayer()->SendDirectMessage(&data);
533 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
535 void AchievementMgr::CheckAllAchievementCriteria()
537 // suppress sending packets
538 for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
539 UpdateAchievementCriteria(AchievementCriteriaTypes(i));
542 static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 };
543 static const uint32 achievIdForDangeon[][4] =
545 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
546 { 321, true, true, true },
547 { 916, false, true, false },
548 { 917, false, true, false },
549 { 918, true, false, false },
550 { 2219, false, false, true },
551 { 0, false, false, false }
555 * this function will be called whenever the user might have done a criteria relevant action
557 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time)
559 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
560 sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
562 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
563 return;
565 AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
566 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
568 AchievementCriteriaEntry const *achievementCriteria = (*i);
570 if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
571 continue;
573 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
574 if (!achievement)
575 continue;
577 if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
578 (achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
579 continue;
581 // don't update already completed criteria
582 if (IsCompletedCriteria(achievementCriteria,achievement))
583 continue;
585 switch (type)
587 // std. case: increment at 1
588 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
589 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
590 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
591 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
592 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
593 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
594 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
595 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
596 if(!miscvalue1)
597 continue;
598 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
599 break;
600 // std case: increment at miscvalue1
601 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
602 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
603 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING:
604 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
605 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
606 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
607 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED:
608 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED:
609 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
610 if(!miscvalue1)
611 continue;
612 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
613 break;
614 // std case: high value at miscvalue1
615 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
616 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */
617 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT:
618 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED:
619 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED:
620 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED:
621 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
622 if(!miscvalue1)
623 continue;
624 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
625 break;
627 // specialized cases
629 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
630 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
631 if(!miscvalue1)
632 continue;
633 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
634 continue;
636 // LOT achievement->ID required special custom checks
637 switch(achievement->ID)
639 // Just heroic
640 case 489: case 490: case 491: case 492: case 493: case 494: case 495:
641 case 496: case 497: case 498: case 499: case 500: case 563: case 565:
642 case 567: case 569: case 573: case 575: case 577: case 623: case 625:
643 case 667: case 668: case 669: case 670: case 671: case 672: case 673:
644 case 674: case 675: case 676: case 677: case 678: case 679: case 680:
645 case 681: case 682: case 1367: case 1368: case 1378: case 1379:
646 case 1380: case 1381: case 1382: case 1383: case 1384: case 1385:
647 case 1386: case 1387: case 1388: case 1389: case 1390: case 1393:
648 case 1394: case 1400: case 1402: case 1504: case 1505: case 1506:
649 case 1507: case 1508: case 1509: case 1510: case 1511: case 1512:
650 case 1513: case 1514: case 1515: case 1721: case 1754: case 1756:
651 case 1768: case 1817: case 1865:
652 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC)
653 continue;
654 break;
655 // Heroic + other
656 case 579: case 1296: case 1297: case 1816: case 1834: case 1857: case 1859:
657 case 1860: case 1861: case 1862: case 1864: case 1866: case 1867: case 1868:
658 case 1870: case 1871: case 1872: case 1873: case 1875: case 1877: case 1919:
659 case 2036: case 2037: case 2038: case 2039: case 2040: case 2041: case 2042:
660 case 2043: case 2044: case 2045: case 2046: case 2048: case 2052: case 2053:
661 case 2054: case 2056: case 2057: case 2058: case 2139: case 2140: case 2147:
662 case 2149: case 2150: case 2151: case 2152: case 2154: case 2155: case 2156:
663 case 2157: case 2179: case 2181: case 2183: case 2185: case 2186:
664 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC)
665 continue;
666 // FIX ME: mark as fail always until implement
667 continue;
668 // Normal + other
669 case 578: case 624: case 1790: case 1856: case 1858: case 1869: case 1874:
670 case 1996: case 1997: case 2047: case 2049: case 2050: case 2051: case 2146:
671 case 2148: case 2153: case 2178: case 2180: case 2182: case 2184: case 2187:
672 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL)
673 continue;
674 // FIX ME: mark as fail always until implement
675 continue;
676 // Just Normal
677 default:
678 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL)
679 continue;
680 break;
683 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
684 break;
685 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
686 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
687 break;
688 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
689 // update at loading or specific skill update
690 if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID)
691 continue;
692 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
693 SetCriteriaProgress(achievementCriteria, skillvalue);
694 break;
695 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
696 // update at loading or specific skill update
697 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID)
698 continue;
699 if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID))
700 SetCriteriaProgress(achievementCriteria, maxSkillvalue);
701 break;
702 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
703 if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
704 SetCriteriaProgress(achievementCriteria, 1);
705 break;
706 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
708 uint32 counter =0;
709 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
710 if(itr->second.m_rewarded)
711 counter++;
712 SetCriteriaProgress(achievementCriteria, counter);
713 break;
715 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
717 // speedup for non-login case
718 if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID)
719 continue;
721 uint32 counter =0;
722 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
724 Quest const* quest = objmgr.GetQuestTemplate(itr->first);
725 if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
726 counter++;
728 SetCriteriaProgress(achievementCriteria, counter);
729 break;
731 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
732 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
733 if(!miscvalue1)
734 continue;
735 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
736 continue;
737 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
738 break;
739 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
740 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
741 if(!miscvalue1)
742 continue;
743 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
744 continue;
745 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
746 break;
747 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
749 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
750 if(!miscvalue1)
751 continue;
752 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
753 bool notfit = false;
754 for(int j = 0; j < MAX_ARENA_SLOT; ++j)
756 if(achievIdByArenaSlot[j] == achievement->ID)
758 BattleGround* bg = GetPlayer()->GetBattleGround();
759 if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
760 notfit = true;
762 break;
765 if(notfit)
766 continue;
768 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
769 break;
771 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
773 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
774 if(!miscvalue1)
775 continue;
777 Map const* map = GetPlayer()->GetMap();
778 if(!map->IsDungeon())
779 continue;
781 // search case
782 bool found = false;
783 for(int j = 0; achievIdForDangeon[j][0]; ++j)
785 if(achievIdForDangeon[j][0] == achievement->ID)
787 if(map->IsRaid())
789 // if raid accepted (ignore difficulty)
790 if(!achievIdForDangeon[j][2])
791 break; // for
793 else if(GetPlayer()->GetDifficulty()==DIFFICULTY_NORMAL)
795 // dungeon in normal mode accepted
796 if(!achievIdForDangeon[j][1])
797 break; // for
799 else
801 // dungeon in heroic mode accepted
802 if(!achievIdForDangeon[j][3])
803 break; // for
806 found = true;
807 break; // for
810 if(!found)
811 continue;
813 //FIXME: work only for instances where max==min for players
814 if(((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
815 continue;
816 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
817 break;
820 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
821 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
822 if(!miscvalue1)
823 continue;
824 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
825 continue;
826 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
827 break;
828 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
829 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
830 if(!miscvalue1)
831 continue;
833 // if team check required: must kill by opposition faction
834 if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
835 continue;
837 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
838 break;
839 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
841 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
842 if(!miscvalue1)
843 continue;
844 if(achievement->ID == 1260)
846 if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED)
847 continue;
848 if(!IsHolidayActive(HOLIDAY_BREWFEST))
849 continue;
851 // miscvalue1 is the ingame fallheight*100 as stored in dbc
852 SetCriteriaProgress(achievementCriteria, miscvalue1);
853 break;
855 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
856 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
857 if(!miscvalue1)
858 continue;
859 if(miscvalue2 != achievementCriteria->death_from.type)
860 continue;
861 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
862 break;
863 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
864 // if miscvalues != 0, it contains the questID.
865 if (miscvalue1)
867 if (miscvalue1 == achievementCriteria->complete_quest.questID)
868 SetCriteriaProgress(achievementCriteria, 1);
870 else
872 // login case.
873 if(GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
874 SetCriteriaProgress(achievementCriteria, 1);
876 break;
877 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
878 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
879 if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID)
880 continue;
881 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
882 break;
883 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
884 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
885 continue;
886 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
887 break;
888 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
889 if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
890 continue;
892 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
893 SetCriteriaProgress(achievementCriteria, 1);
894 break;
895 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
897 // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored)
898 // miscvalue2=count of item loot
899 if (!miscvalue1 || !miscvalue2)
900 continue;
901 if (miscvalue1 != achievementCriteria->loot_type.lootType)
902 continue;
904 // zone specific
905 if(achievementCriteria->loot_type.lootTypeCount==1)
907 // those requirements couldn't be found in the dbc
908 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
909 if(!data)
910 continue;
912 if(!data->Meets(GetPlayer(),unit))
913 continue;
916 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
917 break;
919 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
920 // speedup for non-login case
921 if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1)
922 continue;
923 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
924 break;
925 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
926 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
927 if(!miscvalue1)
928 continue;
929 if(achievementCriteria->use_item.itemID != miscvalue1)
930 continue;
931 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
932 break;
933 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
934 // You _have_ to loot that item, just owning it when logging in does _not_ count!
935 if(!miscvalue1)
936 continue;
937 if(miscvalue1 != achievementCriteria->own_item.itemID)
938 continue;
939 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
940 break;
941 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
943 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
944 if(!worldOverlayEntry)
945 break;
947 bool matchFound = false;
948 for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
950 uint32 area_id = worldOverlayEntry->areatableID[j];
951 if(!area_id) // array have 0 only in empty tail
952 break;
954 int32 exploreFlag = GetAreaFlagByAreaID(area_id);
955 if(exploreFlag < 0)
956 continue;
958 uint32 playerIndexOffset = uint32(exploreFlag) / 32;
959 uint32 mask = 1<< (uint32(exploreFlag) % 32);
961 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
963 matchFound = true;
964 break;
968 if(matchFound)
969 SetCriteriaProgress(achievementCriteria, 1);
970 break;
972 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
973 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount());
974 break;
975 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
977 // skip faction check only at loading
978 if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID)
979 continue;
981 int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
982 if (reputation > 0)
983 SetCriteriaProgress(achievementCriteria, reputation);
984 break;
986 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
988 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
989 break;
991 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
993 // skip for login case
994 if(!miscvalue1)
995 continue;
996 SetCriteriaProgress(achievementCriteria, 1);
997 break;
999 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1000 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1002 // miscvalue1 = itemid
1003 // miscvalue2 = diced value
1004 if(!miscvalue1)
1005 continue;
1006 if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
1007 continue;
1008 ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 );
1010 uint32 requiredItemLevel = 0;
1011 if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
1012 requiredItemLevel = 185;
1014 if(!pProto || pProto->ItemLevel <requiredItemLevel)
1015 continue;
1016 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1017 break;
1019 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1021 // miscvalue1 = emote
1022 if(!miscvalue1)
1023 continue;
1024 if(miscvalue1 != achievementCriteria->do_emote.emoteID)
1025 continue;
1026 if(achievementCriteria->do_emote.count)
1028 // those requirements couldn't be found in the dbc
1029 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1030 if(!data)
1031 continue;
1033 if(!data->Meets(GetPlayer(),unit))
1034 continue;
1037 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1038 break;
1040 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1041 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1043 if (!miscvalue1)
1044 continue;
1046 if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP)
1048 if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid)
1049 continue;
1051 // map specific case (BG in fact) expected player targeted damage/heal
1052 if(!unit || unit->GetTypeId()!=TYPEID_PLAYER)
1053 continue;
1056 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
1057 break;
1059 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1060 // miscvalue1 = item_id
1061 if(!miscvalue1)
1062 continue;
1063 if(miscvalue1 != achievementCriteria->equip_item.itemID)
1064 continue;
1066 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1067 break;
1068 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1069 // miscvalue1 = go entry
1070 if(!miscvalue1)
1071 continue;
1072 if(miscvalue1 != achievementCriteria->use_gameobject.goEntry)
1073 continue;
1075 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1076 break;
1077 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1078 if (!miscvalue1)
1079 continue;
1080 if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
1081 continue;
1083 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1084 break;
1085 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1087 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine)
1088 continue;
1090 uint32 spellCount = 0;
1091 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1092 spellIter != GetPlayer()->GetSpellMap().end();
1093 ++spellIter)
1095 for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first);
1096 skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first);
1097 ++skillIter)
1099 if(skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine)
1100 spellCount++;
1103 SetCriteriaProgress(achievementCriteria, spellCount);
1104 break;
1106 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1107 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1108 break;
1109 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1110 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1111 break;
1112 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1113 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
1114 break;
1115 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1117 if (!miscvalue1)
1118 continue;
1120 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
1121 continue;
1123 // those requirements couldn't be found in the dbc
1124 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1125 if(!data)
1126 continue;
1128 if(!data->Meets(GetPlayer(),unit))
1129 continue;
1131 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1132 break;
1134 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1136 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine)
1137 continue;
1139 uint32 spellCount = 0;
1140 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1141 spellIter != GetPlayer()->GetSpellMap().end();
1142 ++spellIter)
1144 for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first);
1145 skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first);
1146 ++skillIter)
1148 if(skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine)
1149 spellCount++;
1152 SetCriteriaProgress(achievementCriteria, spellCount);
1153 break;
1155 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1156 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
1157 break;
1158 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1159 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
1160 continue;
1162 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1163 break;
1164 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1165 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
1166 continue;
1168 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1169 break;
1170 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1171 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST);
1172 break;
1173 // std case: not exist in DBC, not triggered in code as result
1174 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1175 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1176 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1177 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER:
1178 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT:
1179 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING:
1180 break;
1181 // FIXME: not triggered in code as result, need to implement
1182 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1183 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY:
1184 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
1185 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE:
1186 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
1187 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
1188 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
1189 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL:
1190 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1191 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
1192 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING:
1193 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
1194 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1195 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
1196 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
1197 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1198 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
1199 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE:
1200 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:
1201 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
1202 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS:
1203 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1204 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1205 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
1206 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
1207 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL:
1208 break; // Not implemented yet :(
1210 if(IsCompletedCriteria(achievementCriteria,achievement))
1211 CompletedCriteriaFor(achievement);
1213 // check again the completeness for SUMM and REQ COUNT achievements,
1214 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
1215 if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
1217 if (IsCompletedAchievement(achievement))
1218 CompletedAchievement(achievement);
1221 if(AchievementEntryList const* achRefList = achievementmgr.GetAchievementByReferencedId(achievement->ID))
1223 for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
1224 if(IsCompletedAchievement(*itr))
1225 CompletedAchievement(*itr);
1230 static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
1231 static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
1233 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement)
1235 // counter can never complete
1236 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1237 return false;
1239 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1241 // someone on this realm has already completed that achievement
1242 if(achievementmgr.IsRealmCompleted(achievement))
1243 return false;
1246 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
1247 if(itr == m_criteriaProgress.end())
1248 return false;
1250 CriteriaProgress const* progress = &itr->second;
1252 switch(achievementCriteria->requiredType)
1254 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1255 return progress->counter >= achievementCriteria->kill_creature.creatureCount;
1256 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
1258 // skip wrong class achievements
1259 for(int i = 1; i < MAX_CLASSES; ++i)
1260 if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
1261 return false;
1263 // skip wrong race achievements
1264 for(int i = 1; i < MAX_RACES; ++i)
1265 if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
1266 return false;
1268 // appropriate class/race or not class/race specific
1269 return progress->counter >= achievementCriteria->reach_level.level;
1271 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
1272 return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
1273 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
1274 return progress->counter >= 1;
1275 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
1276 return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
1277 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
1278 return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
1279 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1280 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1281 return progress->counter >= achievementCriteria->healing_done.count;
1282 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
1283 return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
1284 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1285 return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
1286 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1287 return progress->counter >= 1;
1288 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1289 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1290 return progress->counter >= achievementCriteria->be_spell_target.spellCount;
1291 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1292 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1293 return progress->counter >= achievementCriteria->cast_spell.castCount;
1294 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1295 return progress->counter >= 1;
1296 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1297 return progress->counter >= achievementCriteria->own_item.itemCount;
1298 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
1299 return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75);
1300 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1301 return progress->counter >= achievementCriteria->use_item.itemCount;
1302 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1303 return progress->counter >= achievementCriteria->loot_item.itemCount;
1304 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1305 return progress->counter >= 1;
1306 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1307 return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
1308 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1309 return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
1310 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1311 return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
1312 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1313 return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
1314 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1315 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1316 return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
1317 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1318 return progress->counter >= achievementCriteria->hk_class.count;
1319 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1320 return progress->counter >= achievementCriteria->hk_race.count;
1321 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1322 return progress->counter >= achievementCriteria->do_emote.count;
1323 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1324 return progress->counter >= achievementCriteria->equip_item.count;
1325 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
1326 return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper;
1327 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
1328 return progress->counter >= achievementCriteria->loot_money.goldInCopper;
1329 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1330 return progress->counter >= achievementCriteria->use_gameobject.useCount;
1331 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1332 return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount;
1333 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1334 return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount;
1335 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1336 return progress->counter >= achievementCriteria->win_duel.duelCount;
1337 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1338 return progress->counter >= achievementCriteria->loot_type.lootTypeCount;
1339 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1340 return progress->counter >= achievementCriteria->learn_skill_line.spellCount;
1341 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1342 return progress->counter >= achievementCriteria->honorable_kill.killCount;
1344 // handle all statistic-only criteria here
1345 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
1346 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
1347 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
1348 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
1349 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
1350 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
1351 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1352 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
1353 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
1354 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
1355 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
1356 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
1357 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
1358 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD:
1359 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1360 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1361 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1362 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1363 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1364 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1365 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1366 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
1367 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
1368 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
1369 return false;
1371 return false;
1374 void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
1376 // counter can never complete
1377 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1378 return;
1380 // already completed and stored
1381 if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1382 return;
1384 if (IsCompletedAchievement(achievement))
1385 CompletedAchievement(achievement);
1388 bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
1390 // counter can never complete
1391 if(entry->flags & ACHIEVEMENT_FLAG_COUNTER)
1392 return false;
1394 // for achievement with referenced achievement criterias get from referenced and counter from self
1395 uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID;
1396 uint32 achievmentForTestCount = entry->count;
1398 AchievementCriteriaEntryList const* cList = achievementmgr.GetAchievementCriteriaByAchievement(achievmentForTestId);
1399 if(!cList)
1400 return false;
1401 uint32 count = 0;
1403 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
1404 // Oddly, the target count is NOT countained in the achievement, but in each individual criteria
1405 if (entry->flags & ACHIEVEMENT_FLAG_SUMM)
1407 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1409 AchievementCriteriaEntry const* criteria = *itr;
1411 CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID);
1412 if(itrProgress == m_criteriaProgress.end())
1413 continue;
1415 CriteriaProgress const* progress = &itrProgress->second;
1416 count += progress->counter;
1418 // for counters, field4 contains the main count requirement
1419 if (count >= criteria->raw.count)
1420 return true;
1422 return false;
1425 // Default case - need complete all or
1426 bool completed_all = true;
1427 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1429 AchievementCriteriaEntry const* criteria = *itr;
1431 bool completed = IsCompletedCriteria(criteria,entry);
1433 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1434 if(completed)
1435 ++count;
1436 else
1437 completed_all = false;
1439 // completed as have req. count of completed criterias
1440 if(achievmentForTestCount > 0 && achievmentForTestCount <= count)
1441 return true;
1444 // all criterias completed requirement
1445 if(completed_all && achievmentForTestCount==0)
1446 return true;
1448 return false;
1451 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
1453 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
1454 sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
1456 CriteriaProgress *progress = NULL;
1458 CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
1460 if(iter == m_criteriaProgress.end())
1462 // not create record for 0 counter
1463 if(changeValue == 0)
1464 return;
1466 progress = &m_criteriaProgress[entry->ID];
1467 progress->counter = changeValue;
1468 progress->date = time(NULL);
1470 else
1472 progress = &iter->second;
1474 uint32 newValue = 0;
1475 switch(ptype)
1477 case PROGRESS_SET:
1478 newValue = changeValue;
1479 break;
1480 case PROGRESS_ACCUMULATE:
1482 // avoid overflow
1483 uint32 max_value = std::numeric_limits<uint32>::max();
1484 newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value;
1485 break;
1487 case PROGRESS_HIGHEST:
1488 newValue = progress->counter < changeValue ? changeValue : progress->counter;
1489 break;
1492 // not update (not mark as changed) if counter will have same value
1493 if(progress->counter == newValue)
1494 return;
1496 progress->counter = newValue;
1499 progress->changed = true;
1501 if(entry->timeLimit)
1503 time_t now = time(NULL);
1504 if(time_t(progress->date + entry->timeLimit) < now)
1505 progress->counter = 1;
1507 // also it seems illogical, the timeframe will be extended at every criteria update
1508 progress->date = now;
1510 SendCriteriaUpdate(entry->ID,progress);
1513 void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
1515 sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
1516 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1517 return;
1519 SendAchievementEarned(achievement);
1520 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
1521 ca.date = time(NULL);
1522 ca.changed = true;
1524 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1525 // TODO: where do set this instead?
1526 if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1527 achievementmgr.SetRealmCompleted(achievement);
1529 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
1531 // reward items and titles if any
1532 AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement);
1534 // no rewards
1535 if(!reward)
1536 return;
1538 // titles
1539 if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
1541 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
1542 GetPlayer()->SetTitle(titleEntry);
1545 // mail
1546 if(reward->sender)
1548 Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
1550 MailItemsInfo mi;
1551 if(item)
1553 // save new item before send
1554 item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1556 // item
1557 mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
1560 int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1562 // subject and text
1563 std::string subject = reward->subject;
1564 std::string text = reward->text;
1565 if ( loc_idx >= 0 )
1567 if(AchievementRewardLocale const* loc = achievementmgr.GetAchievementRewardLocale(achievement))
1569 if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty())
1570 subject = loc->subject[loc_idx];
1571 if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty())
1572 text = loc->text[loc_idx];
1576 uint32 itemTextId = objmgr.CreateItemText( text );
1578 WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE, MAIL_STATIONERY_NORMAL, reward->sender, GetPlayer()->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
1582 void AchievementMgr::SendAllAchievementData()
1584 // since we don't know the exact size of the packed GUIDs this is just an approximation
1585 WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1586 BuildAllDataPacket(&data);
1587 GetPlayer()->GetSession()->SendPacket(&data);
1590 void AchievementMgr::SendRespondInspectAchievements(Player* player)
1592 // since we don't know the exact size of the packed GUIDs this is just an approximation
1593 WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1594 data.append(GetPlayer()->GetPackGUID());
1595 BuildAllDataPacket(&data);
1596 player->GetSession()->SendPacket(&data);
1600 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1602 void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
1604 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
1606 *data << uint32(iter->first);
1607 *data << uint32(secsToTimeBitFields(iter->second.date));
1609 *data << int32(-1);
1611 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
1613 *data << uint32(iter->first);
1614 data->appendPackGUID(iter->second.counter);
1615 data->append(GetPlayer()->GetPackGUID());
1616 *data << uint32(0);
1617 *data << uint32(secsToTimeBitFields(iter->second.date));
1618 *data << uint32(0);
1619 *data << uint32(0);
1622 *data << int32(-1);
1625 //==========================================================
1626 AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
1628 return m_AchievementCriteriasByType[type];
1631 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1633 if(sAchievementCriteriaStore.GetNumRows()==0)
1635 barGoLink bar(1);
1636 bar.step();
1638 sLog.outString();
1639 sLog.outErrorDb(">> Loaded 0 achievement criteria.");
1640 return;
1643 barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
1644 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1646 bar.step();
1648 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1649 if(!criteria)
1650 continue;
1652 m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
1653 m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
1656 sLog.outString();
1657 sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size());
1660 void AchievementGlobalMgr::LoadAchievementReferenceList()
1662 if(sAchievementStore.GetNumRows()==0)
1664 barGoLink bar(1);
1665 bar.step();
1667 sLog.outString();
1668 sLog.outErrorDb(">> Loaded 0 achievement references.");
1669 return;
1672 uint32 count = 0;
1673 barGoLink bar( sAchievementStore.GetNumRows() );
1674 for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
1676 bar.step();
1678 AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
1679 if(!achievement || !achievement->refAchievement)
1680 continue;
1682 m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement);
1683 ++count;
1686 sLog.outString();
1687 sLog.outString(">> Loaded %u achievement references.",count);
1690 void AchievementGlobalMgr::LoadAchievementCriteriaData()
1692 m_criteriaDataMap.clear(); // need for reload case
1694 QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data");
1696 if(!result)
1698 barGoLink bar(1);
1699 bar.step();
1701 sLog.outString();
1702 sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty.");
1703 return;
1706 uint32 count = 0;
1707 barGoLink bar(result->GetRowCount());
1710 bar.step();
1711 Field *fields = result->Fetch();
1712 uint32 criteria_id = fields[0].GetUInt32();
1714 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
1716 if (!criteria)
1718 sLog.outErrorDb( "Table `achievement_criteria_data` have data for not existed criteria (Entry: %u), ignore.", criteria_id);
1719 continue;
1722 AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
1724 if(!data.IsValid(criteria))
1725 continue;
1727 // this will allocate empty data set storage
1728 AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id];
1730 // add real data only for not NONE data types
1731 if(data.dataType!=ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE)
1732 dataSet.Add(data);
1734 // counting data by and data types
1735 ++count;
1736 } while(result->NextRow());
1738 delete result;
1740 // post loading checks
1741 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1743 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1744 if(!criteria)
1745 continue;
1747 switch(criteria->requiredType)
1749 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases
1750 if(criteria->do_emote.count==0)
1751 continue;
1752 break;
1753 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases
1754 break;
1755 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases
1756 if(criteria->loot_type.lootTypeCount!=1)
1757 continue;
1758 break;
1759 default: // type not use DB data, ignore
1760 continue;
1763 if(!GetCriteriaDataSet(criteria))
1764 sLog.outErrorDb( "Table `achievement_criteria_data` not have expected data for for criteria (Entry: %u Type: %u).", criteria->ID, criteria->requiredType);
1767 sLog.outString();
1768 sLog.outString(">> Loaded %u additional achievement criteria data.",count);
1771 void AchievementGlobalMgr::LoadCompletedAchievements()
1773 QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
1775 if(!result)
1777 barGoLink bar(1);
1778 bar.step();
1780 sLog.outString();
1781 sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
1782 return;
1785 barGoLink bar(result->GetRowCount());
1788 bar.step();
1789 Field *fields = result->Fetch();
1791 uint32 achievement_id = fields[0].GetUInt32();
1792 if(!sAchievementStore.LookupEntry(achievement_id))
1794 // we will remove not existed achievement for all characters
1795 sLog.outError("Not existed achievement %u data removed from table `character_achievement`.",achievement_id);
1796 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id);
1797 continue;
1800 m_allCompletedAchievements.insert(achievement_id);
1801 } while(result->NextRow());
1803 delete result;
1805 sLog.outString();
1806 sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size());
1809 void AchievementGlobalMgr::LoadRewards()
1811 m_achievementRewards.clear(); // need for reload case
1813 // 0 1 2 3 4 5 6
1814 QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
1816 if(!result)
1818 barGoLink bar(1);
1820 bar.step();
1822 sLog.outString();
1823 sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
1824 return;
1827 uint32 count = 0;
1828 barGoLink bar(result->GetRowCount());
1832 bar.step();
1834 Field *fields = result->Fetch();
1835 uint32 entry = fields[0].GetUInt32();
1836 if (!sAchievementStore.LookupEntry(entry))
1838 sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
1839 continue;
1842 AchievementReward reward;
1843 reward.titleId[0] = fields[1].GetUInt32();
1844 reward.titleId[1] = fields[2].GetUInt32();
1845 reward.itemId = fields[3].GetUInt32();
1846 reward.sender = fields[4].GetUInt32();
1847 reward.subject = fields[5].GetCppString();
1848 reward.text = fields[6].GetCppString();
1850 if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
1851 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has title (A: %u H: %u) only for one from teams.", entry, reward.titleId[0], reward.titleId[1]);
1853 // must be title or mail at least
1854 if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
1856 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
1857 continue;
1860 if (reward.titleId[0])
1862 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
1863 if (!titleEntry)
1865 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
1866 reward.titleId[0] = 0;
1870 if (reward.titleId[1])
1872 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
1873 if (!titleEntry)
1875 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]);
1876 reward.titleId[1] = 0;
1880 //check mail data before item for report including wrong item case
1881 if (reward.sender)
1883 if (!objmgr.GetCreatureTemplate(reward.sender))
1885 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
1886 reward.sender = 0;
1889 else
1891 if (reward.itemId)
1892 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
1894 if (!reward.subject.empty())
1895 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
1897 if (!reward.text.empty())
1898 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
1901 if (reward.itemId)
1903 if (!objmgr.GetItemPrototype(reward.itemId))
1905 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
1906 reward.itemId = 0;
1910 m_achievementRewards[entry] = reward;
1911 ++count;
1913 } while (result->NextRow());
1915 delete result;
1917 sLog.outString();
1918 sLog.outString( ">> Loaded %u achievement rewards", count );
1921 void AchievementGlobalMgr::LoadRewardLocales()
1923 m_achievementRewardLocales.clear(); // need for reload case
1925 QueryResult *result = WorldDatabase.Query("SELECT entry,subject_loc1,text_loc1,subject_loc2,text_loc2,subject_loc3,text_loc3,subject_loc4,text_loc4,subject_loc5,text_loc5,subject_loc6,text_loc6,subject_loc7,text_loc7,subject_loc8,text_loc8 FROM locales_achievement_reward");
1927 if(!result)
1929 barGoLink bar(1);
1931 bar.step();
1933 sLog.outString();
1934 sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
1935 return;
1938 barGoLink bar(result->GetRowCount());
1942 Field *fields = result->Fetch();
1943 bar.step();
1945 uint32 entry = fields[0].GetUInt32();
1947 if(m_achievementRewards.find(entry)==m_achievementRewards.end())
1949 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
1950 continue;
1953 AchievementRewardLocale& data = m_achievementRewardLocales[entry];
1955 for(int i = 1; i < MAX_LOCALE; ++i)
1957 std::string str = fields[1+2*(i-1)].GetCppString();
1958 if(!str.empty())
1960 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
1961 if(idx >= 0)
1963 if(data.subject.size() <= size_t(idx))
1964 data.subject.resize(idx+1);
1966 data.subject[idx] = str;
1969 str = fields[1+2*(i-1)+1].GetCppString();
1970 if(!str.empty())
1972 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
1973 if(idx >= 0)
1975 if(data.text.size() <= size_t(idx))
1976 data.text.resize(idx+1);
1978 data.text[idx] = str;
1982 } while (result->NextRow());
1984 delete result;
1986 sLog.outString();
1987 sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() );