[7934] Implement ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
[getmangos.git] / src / game / AchievementMgr.cpp
blobb24bc8ad2e7771cb60fd0812e8ed3e65683816d5
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_WIN_RATED_ARENA:
82 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
83 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
84 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
85 break;
86 default:
87 sLog.outErrorDb( "Table `achievement_criteria_data` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
88 return false;
91 switch(dataType)
93 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
94 case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE:
95 return true;
96 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
97 if(!creature.id || !objmgr.GetCreatureTemplate(creature.id))
99 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.",
100 criteria->ID, criteria->requiredType,dataType,creature.id);
101 return false;
103 return true;
104 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
105 if(!classRace.class_id && !classRace.race_id)
107 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.",
108 criteria->ID, criteria->requiredType,dataType);
109 return false;
111 if(classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0)
113 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.",
114 criteria->ID, criteria->requiredType,dataType,classRace.class_id);
115 return false;
117 if(classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0)
119 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.",
120 criteria->ID, criteria->requiredType,dataType,classRace.race_id);
121 return false;
123 return true;
124 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
125 if(health.percent < 1 || health.percent > 100)
127 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.",
128 criteria->ID, criteria->requiredType,dataType,health.percent);
129 return false;
131 return true;
132 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD:
133 if(player_dead.own_team_flag > 1)
135 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong boolean value1 (%u).",
136 criteria->ID, criteria->requiredType,dataType,player_dead.own_team_flag);
137 return false;
139 return true;
140 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
141 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
143 SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id);
144 if(!spellEntry)
146 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell id in value1 (%u), ignore.",
147 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);
148 return false;
150 if(aura.effect_idx >= 3)
152 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell effect index in value2 (%u), ignore.",
153 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);
154 return false;
156 if(!spellEntry->EffectApplyAuraName[aura.effect_idx])
158 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.",
159 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);
160 return false;
162 return true;
164 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
165 if(!GetAreaEntryByAreaID(area.id))
167 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.",
168 criteria->ID, criteria->requiredType,dataType,area.id);
169 return false;
171 return true;
172 default:
173 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,dataType);
174 return false;
176 return false;
179 bool AchievementCriteriaData::Meets(Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const
181 switch(dataType)
183 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
184 return true;
185 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
186 if (!target || target->GetTypeId()!=TYPEID_UNIT)
187 return false;
188 if (target->GetEntry() != creature.id)
189 return false;
190 return true;
191 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
192 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
193 return false;
194 if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass())
195 return false;
196 if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace())
197 return false;
198 return true;
199 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
200 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
201 return false;
202 return target->GetHealth()*100 <= health.percent*target->GetMaxHealth();
203 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD:
204 if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || ((Player*)target)->GetDeathTimer() == 0)
205 return false;
206 // flag set == must be same team, not set == different team
207 return (((Player*)target)->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0);
208 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
209 return source->HasAura(aura.spell_id,aura.effect_idx);
210 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
212 uint32 zone_id,area_id;
213 source->GetZoneAndAreaId(zone_id,area_id);
214 return area.id==zone_id || area.id==area_id;
216 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
217 return target && target->HasAura(aura.spell_id,aura.effect_idx);
218 case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE:
219 return miscvalue1 >= value.minvalue;
222 return false;
225 bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const
227 for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr)
228 if(!itr->Meets(source,target,miscvalue))
229 return false;
231 return true;
234 AchievementMgr::AchievementMgr(Player *player)
236 m_player = player;
239 AchievementMgr::~AchievementMgr()
243 void AchievementMgr::Reset()
245 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
247 WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
248 data << uint32(iter->first);
249 m_player->SendDirectMessage(&data);
252 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
254 WorldPacket data(SMSG_CRITERIA_DELETED,4);
255 data << uint32(iter->first);
256 m_player->SendDirectMessage(&data);
259 m_completedAchievements.clear();
260 m_criteriaProgress.clear();
261 DeleteFromDB(m_player->GetGUIDLow());
263 // re-fill data
264 CheckAllAchievementCriteria();
267 void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2)
269 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
270 sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2);
272 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
273 return;
275 AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
276 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
278 AchievementCriteriaEntry const *achievementCriteria = (*i);
280 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
281 if (!achievement)
282 continue;
284 // don't update already completed criteria
285 if (IsCompletedCriteria(achievementCriteria,achievement))
286 continue;
288 switch (type)
290 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset
291 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset
292 if (achievementCriteria->healing_done.flag == miscvalue1 &&
293 achievementCriteria->healing_done.mapid == miscvalue2)
294 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
295 break;
296 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset
297 // reset only the criteria having the miscvalue1 condition
298 if (achievementCriteria->win_rated_arena.flag == miscvalue1)
299 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
300 break;
301 default: // reset all cases
302 break;
307 void AchievementMgr::DeleteFromDB(uint32 lowguid)
309 CharacterDatabase.BeginTransaction ();
310 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid);
311 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
312 CharacterDatabase.CommitTransaction ();
315 void AchievementMgr::SaveToDB()
317 if(!m_completedAchievements.empty())
319 bool need_execute = false;
320 std::ostringstream ssdel;
321 std::ostringstream ssins;
322 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
324 if(!iter->second.changed)
325 continue;
327 /// first new/changed record prefix
328 if(!need_execute)
330 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
331 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
332 need_execute = true;
334 /// next new/changed record prefix
335 else
337 ssdel << ", ";
338 ssins << ", ";
341 // new/changed record data
342 ssdel << iter->first;
343 ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
345 /// mark as saved in db
346 iter->second.changed = false;
349 if(need_execute)
350 ssdel << ")";
352 if(need_execute)
354 CharacterDatabase.Execute( ssdel.str().c_str() );
355 CharacterDatabase.Execute( ssins.str().c_str() );
359 if(!m_criteriaProgress.empty())
361 /// prepare deleting and insert
362 bool need_execute_del = false;
363 bool need_execute_ins = false;
364 std::ostringstream ssdel;
365 std::ostringstream ssins;
366 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
368 if(!iter->second.changed)
369 continue;
371 // deleted data (including 0 progress state)
373 /// first new/changed record prefix (for any counter value)
374 if(!need_execute_del)
376 ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
377 need_execute_del = true;
379 /// next new/changed record prefix
380 else
381 ssdel << ", ";
383 // new/changed record data
384 ssdel << iter->first;
387 // store data only for real progress
388 if(iter->second.counter != 0)
390 /// first new/changed record prefix
391 if(!need_execute_ins)
393 ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
394 need_execute_ins = true;
396 /// next new/changed record prefix
397 else
398 ssins << ", ";
400 // new/changed record data
401 ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
404 /// mark as updated in db
405 iter->second.changed = false;
408 if(need_execute_del) // DELETE ... IN (.... _)_
409 ssdel << ")";
411 if(need_execute_del || need_execute_ins)
413 if(need_execute_del)
414 CharacterDatabase.Execute( ssdel.str().c_str() );
415 if(need_execute_ins)
416 CharacterDatabase.Execute( ssins.str().c_str() );
421 void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
423 if(achievementResult)
427 Field *fields = achievementResult->Fetch();
429 uint32 achievement_id = fields[0].GetUInt32();
431 // don't must happen: cleanup at server startup in achievementmgr.LoadCompletedAchievements()
432 if(!sAchievementStore.LookupEntry(achievement_id))
433 continue;
435 CompletedAchievementData& ca = m_completedAchievements[achievement_id];
436 ca.date = time_t(fields[1].GetUInt64());
437 ca.changed = false;
438 } while(achievementResult->NextRow());
439 delete achievementResult;
442 if(criteriaResult)
446 Field *fields = criteriaResult->Fetch();
448 uint32 id = fields[0].GetUInt32();
449 uint32 counter = fields[1].GetUInt32();
450 time_t date = time_t(fields[2].GetUInt64());
452 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
453 if (!criteria)
455 // we will remove not existed criteria for all characters
456 sLog.outError("Not existed achievement creataria %u data removed from table `character_achievement_progress`.",id);
457 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id);
458 continue;
461 if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))
462 continue;
464 CriteriaProgress& progress = m_criteriaProgress[id];
465 progress.counter = counter;
466 progress.date = date;
467 progress.changed = false;
468 } while(criteriaResult->NextRow());
469 delete criteriaResult;
474 void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
476 if(GetPlayer()->GetSession()->PlayerLoading())
477 return;
479 #ifdef MANGOS_DEBUG
480 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
481 sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
482 #endif
484 if(Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()))
486 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
487 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
488 guild->BroadcastWorker(say_do,GetPlayer());
491 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
493 // broadcast realm first reached
494 WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4);
495 data << GetPlayer()->GetName();
496 data << uint64(GetPlayer()->GetGUID());
497 data << uint32(achievement->ID);
498 data << uint32(0); // 1=link supplied string as player name, 0=display plain string
499 sWorld.SendGlobalMessage(&data);
501 else
503 CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
505 Cell cell(p);
506 cell.data.Part.reserved = ALL_DISTRICT;
507 cell.SetNoCreate();
509 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
510 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
511 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do);
512 TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> >, WorldTypeMapContainer > message(say_worker);
513 CellLock<GridReadGuard> cell_lock(cell, p);
514 cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap());
517 WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
518 data.append(GetPlayer()->GetPackGUID());
519 data << uint32(achievement->ID);
520 data << uint32(secsToTimeBitFields(time(NULL)));
521 data << uint32(0);
522 GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true);
525 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
527 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
528 data << uint32(id);
530 // the counter is packed like a packed Guid
531 data.appendPackGUID(progress->counter);
533 data.append(GetPlayer()->GetPackGUID());
534 data << uint32(0);
535 data << uint32(secsToTimeBitFields(progress->date));
536 data << uint32(0); // timer 1
537 data << uint32(0); // timer 2
538 GetPlayer()->SendDirectMessage(&data);
542 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
544 void AchievementMgr::CheckAllAchievementCriteria()
546 // suppress sending packets
547 for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
548 UpdateAchievementCriteria(AchievementCriteriaTypes(i));
551 static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 };
552 static const uint32 achievIdForDangeon[][4] =
554 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
555 { 321, true, true, true },
556 { 916, false, true, false },
557 { 917, false, true, false },
558 { 918, true, false, false },
559 { 2219, false, false, true },
560 { 0, false, false, false }
564 * this function will be called whenever the user might have done a criteria relevant action
566 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time)
568 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
569 sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
571 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
572 return;
574 AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
575 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
577 AchievementCriteriaEntry const *achievementCriteria = (*i);
579 if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
580 continue;
582 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
583 if (!achievement)
584 continue;
586 if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
587 (achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
588 continue;
590 // don't update already completed criteria
591 if (IsCompletedCriteria(achievementCriteria,achievement))
592 continue;
594 switch (type)
596 // std. case: increment at 1
597 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
598 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
599 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
600 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
601 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
602 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
603 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
604 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
605 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
606 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
607 if(!miscvalue1)
608 continue;
609 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
610 break;
611 // std case: increment at miscvalue1
612 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
613 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
614 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING:
615 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
616 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
617 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
618 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED:
619 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED:
620 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
621 if(!miscvalue1)
622 continue;
623 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
624 break;
625 // std case: high value at miscvalue1
626 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
627 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */
628 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT:
629 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED:
630 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED:
631 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED:
632 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
633 if(!miscvalue1)
634 continue;
635 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
636 break;
638 // specialized cases
640 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
641 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
642 if(!miscvalue1)
643 continue;
644 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
645 continue;
647 // LOT achievement->ID required special custom checks
648 switch(achievement->ID)
650 // Just heroic
651 case 489: case 490: case 491: case 492: case 493: case 494: case 495:
652 case 496: case 497: case 498: case 499: case 500: case 563: case 565:
653 case 567: case 569: case 573: case 575: case 577: case 623: case 625:
654 case 667: case 668: case 669: case 670: case 671: case 672: case 673:
655 case 674: case 675: case 676: case 677: case 678: case 679: case 680:
656 case 681: case 682: case 1367: case 1368: case 1378: case 1379:
657 case 1380: case 1381: case 1382: case 1383: case 1384: case 1385:
658 case 1386: case 1387: case 1388: case 1389: case 1390: case 1393:
659 case 1394: case 1400: case 1402: case 1504: case 1505: case 1506:
660 case 1507: case 1508: case 1509: case 1510: case 1511: case 1512:
661 case 1513: case 1514: case 1515: case 1721: case 1754: case 1756:
662 case 1768: case 1817: case 1865:
663 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC)
664 continue;
665 break;
666 // Heroic + other
667 case 579: case 1296: case 1297: case 1816: case 1834: case 1857: case 1859:
668 case 1860: case 1861: case 1862: case 1864: case 1866: case 1867: case 1868:
669 case 1870: case 1871: case 1872: case 1873: case 1875: case 1877: case 1919:
670 case 2036: case 2037: case 2038: case 2039: case 2040: case 2041: case 2042:
671 case 2043: case 2044: case 2045: case 2046: case 2048: case 2052: case 2053:
672 case 2054: case 2056: case 2057: case 2058: case 2139: case 2140: case 2147:
673 case 2149: case 2150: case 2151: case 2152: case 2154: case 2155: case 2156:
674 case 2157: case 2179: case 2181: case 2183: case 2185: case 2186:
675 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_HEROIC)
676 continue;
677 // FIX ME: mark as fail always until implement
678 continue;
679 // Normal + other
680 case 578: case 624: case 1790: case 1856: case 1858: case 1869: case 1874:
681 case 1996: case 1997: case 2047: case 2049: case 2050: case 2051: case 2146:
682 case 2148: case 2153: case 2178: case 2180: case 2182: case 2184: case 2187:
683 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL)
684 continue;
685 // FIX ME: mark as fail always until implement
686 continue;
687 // Just Normal
688 default:
689 if(GetPlayer()->GetDifficulty()!=DIFFICULTY_NORMAL)
690 continue;
691 break;
694 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
695 break;
696 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
697 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
698 break;
699 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
700 // update at loading or specific skill update
701 if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID)
702 continue;
703 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
704 SetCriteriaProgress(achievementCriteria, skillvalue);
705 break;
706 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
707 // update at loading or specific skill update
708 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID)
709 continue;
710 if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID))
711 SetCriteriaProgress(achievementCriteria, maxSkillvalue);
712 break;
713 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
714 if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
715 SetCriteriaProgress(achievementCriteria, 1);
716 break;
717 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
719 uint32 counter =0;
720 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
721 if(itr->second.m_rewarded)
722 counter++;
723 SetCriteriaProgress(achievementCriteria, counter);
724 break;
726 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
728 // speedup for non-login case
729 if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID)
730 continue;
732 uint32 counter =0;
733 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
735 Quest const* quest = objmgr.GetQuestTemplate(itr->first);
736 if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
737 counter++;
739 SetCriteriaProgress(achievementCriteria, counter);
740 break;
742 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
743 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
744 if(!miscvalue1)
745 continue;
746 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
747 continue;
748 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
749 break;
750 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
751 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
752 if(!miscvalue1)
753 continue;
754 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
755 continue;
756 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
757 break;
758 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
760 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
761 if(!miscvalue1)
762 continue;
763 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
764 bool notfit = false;
765 for(int j = 0; j < MAX_ARENA_SLOT; ++j)
767 if(achievIdByArenaSlot[j] == achievement->ID)
769 BattleGround* bg = GetPlayer()->GetBattleGround();
770 if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
771 notfit = true;
773 break;
776 if(notfit)
777 continue;
779 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
780 break;
782 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
784 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
785 if(!miscvalue1)
786 continue;
788 Map const* map = GetPlayer()->GetMap();
789 if(!map->IsDungeon())
790 continue;
792 // search case
793 bool found = false;
794 for(int j = 0; achievIdForDangeon[j][0]; ++j)
796 if(achievIdForDangeon[j][0] == achievement->ID)
798 if(map->IsRaid())
800 // if raid accepted (ignore difficulty)
801 if(!achievIdForDangeon[j][2])
802 break; // for
804 else if(GetPlayer()->GetDifficulty()==DIFFICULTY_NORMAL)
806 // dungeon in normal mode accepted
807 if(!achievIdForDangeon[j][1])
808 break; // for
810 else
812 // dungeon in heroic mode accepted
813 if(!achievIdForDangeon[j][3])
814 break; // for
817 found = true;
818 break; // for
821 if(!found)
822 continue;
824 //FIXME: work only for instances where max==min for players
825 if(((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
826 continue;
827 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
828 break;
831 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
832 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
833 if(!miscvalue1)
834 continue;
835 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
836 continue;
837 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
838 break;
839 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
840 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
841 if(!miscvalue1)
842 continue;
844 // if team check required: must kill by opposition faction
845 if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
846 continue;
848 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
849 break;
850 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
852 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
853 if(!miscvalue1)
854 continue;
855 if(achievement->ID == 1260)
857 if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED)
858 continue;
859 if(!IsHolidayActive(HOLIDAY_BREWFEST))
860 continue;
862 // miscvalue1 is the ingame fallheight*100 as stored in dbc
863 SetCriteriaProgress(achievementCriteria, miscvalue1);
864 break;
866 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
867 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
868 if(!miscvalue1)
869 continue;
870 if(miscvalue2 != achievementCriteria->death_from.type)
871 continue;
872 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
873 break;
874 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
875 // if miscvalues != 0, it contains the questID.
876 if (miscvalue1)
878 if (miscvalue1 == achievementCriteria->complete_quest.questID)
879 SetCriteriaProgress(achievementCriteria, 1);
881 else
883 // login case.
884 if(GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
885 SetCriteriaProgress(achievementCriteria, 1);
887 break;
888 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
889 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
890 if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID)
891 continue;
892 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
893 break;
894 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
895 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
896 continue;
897 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
898 break;
899 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
900 if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
901 continue;
903 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
904 SetCriteriaProgress(achievementCriteria, 1);
905 break;
906 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
908 // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored)
909 // miscvalue2=count of item loot
910 if (!miscvalue1 || !miscvalue2)
911 continue;
912 if (miscvalue1 != achievementCriteria->loot_type.lootType)
913 continue;
915 // zone specific
916 if(achievementCriteria->loot_type.lootTypeCount==1)
918 // those requirements couldn't be found in the dbc
919 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
920 if(!data)
921 continue;
923 if(!data->Meets(GetPlayer(),unit))
924 continue;
927 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
928 break;
930 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
931 // speedup for non-login case
932 if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1)
933 continue;
934 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
935 break;
936 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
937 // miscvalue1 contains the personal rating
938 if (!miscvalue1) // no update at login
939 continue;
941 // additional requirements
942 if(achievementCriteria->win_rated_arena.flag==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
944 // those requirements couldn't be found in the dbc
945 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
946 if(!data || !data->Meets(GetPlayer(),unit,miscvalue1))
948 // reset the progress as we have a win without the requirement.
949 SetCriteriaProgress(achievementCriteria, 0);
950 continue;
954 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
955 break;
956 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
957 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
958 if(!miscvalue1)
959 continue;
960 if(achievementCriteria->use_item.itemID != miscvalue1)
961 continue;
962 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
963 break;
964 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
965 // You _have_ to loot that item, just owning it when logging in does _not_ count!
966 if(!miscvalue1)
967 continue;
968 if(miscvalue1 != achievementCriteria->own_item.itemID)
969 continue;
970 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
971 break;
972 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
974 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
975 if(!worldOverlayEntry)
976 break;
978 bool matchFound = false;
979 for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
981 uint32 area_id = worldOverlayEntry->areatableID[j];
982 if(!area_id) // array have 0 only in empty tail
983 break;
985 int32 exploreFlag = GetAreaFlagByAreaID(area_id);
986 if(exploreFlag < 0)
987 continue;
989 uint32 playerIndexOffset = uint32(exploreFlag) / 32;
990 uint32 mask = 1<< (uint32(exploreFlag) % 32);
992 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
994 matchFound = true;
995 break;
999 if(matchFound)
1000 SetCriteriaProgress(achievementCriteria, 1);
1001 break;
1003 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1004 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount());
1005 break;
1006 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1008 // skip faction check only at loading
1009 if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID)
1010 continue;
1012 int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
1013 if (reputation > 0)
1014 SetCriteriaProgress(achievementCriteria, reputation);
1015 break;
1017 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1019 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
1020 break;
1022 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1024 // skip for login case
1025 if(!miscvalue1)
1026 continue;
1027 SetCriteriaProgress(achievementCriteria, 1);
1028 break;
1030 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1031 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1033 // miscvalue1 = itemid
1034 // miscvalue2 = diced value
1035 if(!miscvalue1)
1036 continue;
1037 if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
1038 continue;
1039 ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 );
1041 uint32 requiredItemLevel = 0;
1042 if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
1043 requiredItemLevel = 185;
1045 if(!pProto || pProto->ItemLevel <requiredItemLevel)
1046 continue;
1047 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1048 break;
1050 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1052 // miscvalue1 = emote
1053 if(!miscvalue1)
1054 continue;
1055 if(miscvalue1 != achievementCriteria->do_emote.emoteID)
1056 continue;
1057 if(achievementCriteria->do_emote.count)
1059 // those requirements couldn't be found in the dbc
1060 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1061 if(!data)
1062 continue;
1064 if(!data->Meets(GetPlayer(),unit))
1065 continue;
1068 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1069 break;
1071 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1072 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1074 if (!miscvalue1)
1075 continue;
1077 if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP)
1079 if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid)
1080 continue;
1082 // map specific case (BG in fact) expected player targeted damage/heal
1083 if(!unit || unit->GetTypeId()!=TYPEID_PLAYER)
1084 continue;
1087 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
1088 break;
1090 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1091 // miscvalue1 = item_id
1092 if(!miscvalue1)
1093 continue;
1094 if(miscvalue1 != achievementCriteria->equip_item.itemID)
1095 continue;
1097 SetCriteriaProgress(achievementCriteria, 1);
1098 break;
1099 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1100 // miscvalue1 = go entry
1101 if(!miscvalue1)
1102 continue;
1103 if(miscvalue1 != achievementCriteria->use_gameobject.goEntry)
1104 continue;
1106 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1107 break;
1108 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1109 if (!miscvalue1)
1110 continue;
1111 if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
1112 continue;
1114 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1115 break;
1116 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1118 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine)
1119 continue;
1121 uint32 spellCount = 0;
1122 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1123 spellIter != GetPlayer()->GetSpellMap().end();
1124 ++spellIter)
1126 for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first);
1127 skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first);
1128 ++skillIter)
1130 if(skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine)
1131 spellCount++;
1134 SetCriteriaProgress(achievementCriteria, spellCount);
1135 break;
1137 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1138 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1139 break;
1140 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1141 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1142 break;
1143 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1144 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
1145 break;
1146 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1148 if (!miscvalue1)
1149 continue;
1151 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
1152 continue;
1154 // those requirements couldn't be found in the dbc
1155 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1156 if(!data)
1157 continue;
1159 if(!data->Meets(GetPlayer(),unit))
1160 continue;
1162 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1163 break;
1165 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1167 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine)
1168 continue;
1170 uint32 spellCount = 0;
1171 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1172 spellIter != GetPlayer()->GetSpellMap().end();
1173 ++spellIter)
1175 for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first);
1176 skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first);
1177 ++skillIter)
1179 if(skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine)
1180 spellCount++;
1183 SetCriteriaProgress(achievementCriteria, spellCount);
1184 break;
1186 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1187 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
1188 break;
1189 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1190 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
1191 continue;
1193 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1194 break;
1195 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1196 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
1197 continue;
1199 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1200 break;
1201 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1202 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST);
1203 break;
1204 // std case: not exist in DBC, not triggered in code as result
1205 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1206 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1207 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1208 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER:
1209 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT:
1210 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING:
1211 break;
1212 // FIXME: not triggered in code as result, need to implement
1213 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1214 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY:
1215 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
1216 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE:
1217 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
1218 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
1219 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
1220 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL:
1221 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
1222 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING:
1223 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
1224 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1225 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
1226 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
1227 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1228 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
1229 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE:
1230 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:
1231 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
1232 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS:
1233 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1234 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1235 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL:
1236 break; // Not implemented yet :(
1238 if(IsCompletedCriteria(achievementCriteria,achievement))
1239 CompletedCriteriaFor(achievement);
1241 // check again the completeness for SUMM and REQ COUNT achievements,
1242 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
1243 if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
1245 if (IsCompletedAchievement(achievement))
1246 CompletedAchievement(achievement);
1249 if(AchievementEntryList const* achRefList = achievementmgr.GetAchievementByReferencedId(achievement->ID))
1251 for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
1252 if(IsCompletedAchievement(*itr))
1253 CompletedAchievement(*itr);
1258 static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
1259 static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
1261 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement)
1263 // counter can never complete
1264 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1265 return false;
1267 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1269 // someone on this realm has already completed that achievement
1270 if(achievementmgr.IsRealmCompleted(achievement))
1271 return false;
1274 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
1275 if(itr == m_criteriaProgress.end())
1276 return false;
1278 CriteriaProgress const* progress = &itr->second;
1280 switch(achievementCriteria->requiredType)
1282 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1283 return progress->counter >= achievementCriteria->kill_creature.creatureCount;
1284 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
1286 // skip wrong class achievements
1287 for(int i = 1; i < MAX_CLASSES; ++i)
1288 if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
1289 return false;
1291 // skip wrong race achievements
1292 for(int i = 1; i < MAX_RACES; ++i)
1293 if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
1294 return false;
1296 // appropriate class/race or not class/race specific
1297 return progress->counter >= achievementCriteria->reach_level.level;
1299 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
1300 return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
1301 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
1302 return progress->counter >= 1;
1303 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
1304 return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
1305 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
1306 return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
1307 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1308 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1309 return progress->counter >= achievementCriteria->healing_done.count;
1310 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
1311 return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
1312 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1313 return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
1314 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1315 return progress->counter >= 1;
1316 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1317 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1318 return progress->counter >= achievementCriteria->be_spell_target.spellCount;
1319 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1320 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1321 return progress->counter >= achievementCriteria->cast_spell.castCount;
1322 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1323 return progress->counter >= 1;
1324 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1325 return progress->counter >= achievementCriteria->own_item.itemCount;
1326 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1327 return progress->counter >= achievementCriteria->win_rated_arena.count;
1328 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
1329 return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75);
1330 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1331 return progress->counter >= achievementCriteria->use_item.itemCount;
1332 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1333 return progress->counter >= achievementCriteria->loot_item.itemCount;
1334 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1335 return progress->counter >= 1;
1336 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1337 return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
1338 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1339 return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
1340 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1341 return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
1342 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1343 return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
1344 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1345 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1346 return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
1347 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1348 return progress->counter >= achievementCriteria->hk_class.count;
1349 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1350 return progress->counter >= achievementCriteria->hk_race.count;
1351 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1352 return progress->counter >= achievementCriteria->do_emote.count;
1353 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1354 return progress->counter >= achievementCriteria->equip_item.count;
1355 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
1356 return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper;
1357 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
1358 return progress->counter >= achievementCriteria->loot_money.goldInCopper;
1359 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1360 return progress->counter >= achievementCriteria->use_gameobject.useCount;
1361 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1362 return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount;
1363 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1364 return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount;
1365 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1366 return progress->counter >= achievementCriteria->win_duel.duelCount;
1367 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1368 return progress->counter >= achievementCriteria->loot_type.lootTypeCount;
1369 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1370 return progress->counter >= achievementCriteria->learn_skill_line.spellCount;
1371 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1372 return progress->counter >= achievementCriteria->honorable_kill.killCount;
1374 // handle all statistic-only criteria here
1375 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
1376 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
1377 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
1378 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
1379 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
1380 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
1381 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1382 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
1383 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
1384 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
1385 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
1386 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
1387 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
1388 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD:
1389 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1390 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1391 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1392 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1393 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
1394 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
1395 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1396 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1397 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1398 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
1399 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
1400 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
1401 return false;
1403 return false;
1406 void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
1408 // counter can never complete
1409 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1410 return;
1412 // already completed and stored
1413 if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1414 return;
1416 if (IsCompletedAchievement(achievement))
1417 CompletedAchievement(achievement);
1420 bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
1422 // counter can never complete
1423 if(entry->flags & ACHIEVEMENT_FLAG_COUNTER)
1424 return false;
1426 // for achievement with referenced achievement criterias get from referenced and counter from self
1427 uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID;
1428 uint32 achievmentForTestCount = entry->count;
1430 AchievementCriteriaEntryList const* cList = achievementmgr.GetAchievementCriteriaByAchievement(achievmentForTestId);
1431 if(!cList)
1432 return false;
1433 uint32 count = 0;
1435 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
1436 // Oddly, the target count is NOT countained in the achievement, but in each individual criteria
1437 if (entry->flags & ACHIEVEMENT_FLAG_SUMM)
1439 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1441 AchievementCriteriaEntry const* criteria = *itr;
1443 CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID);
1444 if(itrProgress == m_criteriaProgress.end())
1445 continue;
1447 CriteriaProgress const* progress = &itrProgress->second;
1448 count += progress->counter;
1450 // for counters, field4 contains the main count requirement
1451 if (count >= criteria->raw.count)
1452 return true;
1454 return false;
1457 // Default case - need complete all or
1458 bool completed_all = true;
1459 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1461 AchievementCriteriaEntry const* criteria = *itr;
1463 bool completed = IsCompletedCriteria(criteria,entry);
1465 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1466 if(completed)
1467 ++count;
1468 else
1469 completed_all = false;
1471 // completed as have req. count of completed criterias
1472 if(achievmentForTestCount > 0 && achievmentForTestCount <= count)
1473 return true;
1476 // all criterias completed requirement
1477 if(completed_all && achievmentForTestCount==0)
1478 return true;
1480 return false;
1483 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
1485 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
1486 sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
1488 CriteriaProgress *progress = NULL;
1490 CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
1492 if(iter == m_criteriaProgress.end())
1494 // not create record for 0 counter
1495 if(changeValue == 0)
1496 return;
1498 progress = &m_criteriaProgress[entry->ID];
1499 progress->counter = changeValue;
1500 progress->date = time(NULL);
1502 else
1504 progress = &iter->second;
1506 uint32 newValue = 0;
1507 switch(ptype)
1509 case PROGRESS_SET:
1510 newValue = changeValue;
1511 break;
1512 case PROGRESS_ACCUMULATE:
1514 // avoid overflow
1515 uint32 max_value = std::numeric_limits<uint32>::max();
1516 newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value;
1517 break;
1519 case PROGRESS_HIGHEST:
1520 newValue = progress->counter < changeValue ? changeValue : progress->counter;
1521 break;
1524 // not update (not mark as changed) if counter will have same value
1525 if(progress->counter == newValue)
1526 return;
1528 progress->counter = newValue;
1531 progress->changed = true;
1533 if(entry->timeLimit)
1535 time_t now = time(NULL);
1536 if(time_t(progress->date + entry->timeLimit) < now)
1537 progress->counter = 1;
1539 // also it seems illogical, the timeframe will be extended at every criteria update
1540 progress->date = now;
1542 SendCriteriaUpdate(entry->ID,progress);
1545 void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
1547 sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
1548 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1549 return;
1551 SendAchievementEarned(achievement);
1552 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
1553 ca.date = time(NULL);
1554 ca.changed = true;
1556 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1557 // TODO: where do set this instead?
1558 if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1559 achievementmgr.SetRealmCompleted(achievement);
1561 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
1563 // reward items and titles if any
1564 AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement);
1566 // no rewards
1567 if(!reward)
1568 return;
1570 // titles
1571 if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
1573 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
1574 GetPlayer()->SetTitle(titleEntry);
1577 // mail
1578 if(reward->sender)
1580 Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
1582 MailItemsInfo mi;
1583 if(item)
1585 // save new item before send
1586 item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1588 // item
1589 mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
1592 int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1594 // subject and text
1595 std::string subject = reward->subject;
1596 std::string text = reward->text;
1597 if ( loc_idx >= 0 )
1599 if(AchievementRewardLocale const* loc = achievementmgr.GetAchievementRewardLocale(achievement))
1601 if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty())
1602 subject = loc->subject[loc_idx];
1603 if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty())
1604 text = loc->text[loc_idx];
1608 uint32 itemTextId = objmgr.CreateItemText( text );
1610 WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE, MAIL_STATIONERY_NORMAL, reward->sender, GetPlayer()->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
1614 void AchievementMgr::SendAllAchievementData()
1616 // since we don't know the exact size of the packed GUIDs this is just an approximation
1617 WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1618 BuildAllDataPacket(&data);
1619 GetPlayer()->GetSession()->SendPacket(&data);
1622 void AchievementMgr::SendRespondInspectAchievements(Player* player)
1624 // since we don't know the exact size of the packed GUIDs this is just an approximation
1625 WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1626 data.append(GetPlayer()->GetPackGUID());
1627 BuildAllDataPacket(&data);
1628 player->GetSession()->SendPacket(&data);
1632 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1634 void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
1636 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
1638 *data << uint32(iter->first);
1639 *data << uint32(secsToTimeBitFields(iter->second.date));
1641 *data << int32(-1);
1643 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
1645 *data << uint32(iter->first);
1646 data->appendPackGUID(iter->second.counter);
1647 data->append(GetPlayer()->GetPackGUID());
1648 *data << uint32(0);
1649 *data << uint32(secsToTimeBitFields(iter->second.date));
1650 *data << uint32(0);
1651 *data << uint32(0);
1654 *data << int32(-1);
1657 //==========================================================
1658 AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
1660 return m_AchievementCriteriasByType[type];
1663 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1665 if(sAchievementCriteriaStore.GetNumRows()==0)
1667 barGoLink bar(1);
1668 bar.step();
1670 sLog.outString();
1671 sLog.outErrorDb(">> Loaded 0 achievement criteria.");
1672 return;
1675 barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
1676 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1678 bar.step();
1680 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1681 if(!criteria)
1682 continue;
1684 m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
1685 m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
1688 sLog.outString();
1689 sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size());
1692 void AchievementGlobalMgr::LoadAchievementReferenceList()
1694 if(sAchievementStore.GetNumRows()==0)
1696 barGoLink bar(1);
1697 bar.step();
1699 sLog.outString();
1700 sLog.outErrorDb(">> Loaded 0 achievement references.");
1701 return;
1704 uint32 count = 0;
1705 barGoLink bar( sAchievementStore.GetNumRows() );
1706 for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
1708 bar.step();
1710 AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
1711 if(!achievement || !achievement->refAchievement)
1712 continue;
1714 m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement);
1715 ++count;
1718 sLog.outString();
1719 sLog.outString(">> Loaded %u achievement references.",count);
1722 void AchievementGlobalMgr::LoadAchievementCriteriaData()
1724 m_criteriaDataMap.clear(); // need for reload case
1726 QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data");
1728 if(!result)
1730 barGoLink bar(1);
1731 bar.step();
1733 sLog.outString();
1734 sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty.");
1735 return;
1738 uint32 count = 0;
1739 barGoLink bar(result->GetRowCount());
1742 bar.step();
1743 Field *fields = result->Fetch();
1744 uint32 criteria_id = fields[0].GetUInt32();
1746 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
1748 if (!criteria)
1750 sLog.outErrorDb( "Table `achievement_criteria_data` have data for not existed criteria (Entry: %u), ignore.", criteria_id);
1751 continue;
1754 AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
1756 if(!data.IsValid(criteria))
1757 continue;
1759 // this will allocate empty data set storage
1760 AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id];
1762 // add real data only for not NONE data types
1763 if(data.dataType!=ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE)
1764 dataSet.Add(data);
1766 // counting data by and data types
1767 ++count;
1768 } while(result->NextRow());
1770 delete result;
1772 // post loading checks
1773 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1775 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1776 if(!criteria)
1777 continue;
1779 switch(criteria->requiredType)
1781 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases
1782 if(criteria->win_rated_arena.flag!=ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1783 continue;
1784 break;
1785 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases
1786 if(criteria->do_emote.count==0)
1787 continue;
1788 break;
1789 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases
1790 break;
1791 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases
1792 if(criteria->loot_type.lootTypeCount!=1)
1793 continue;
1794 break;
1795 default: // type not use DB data, ignore
1796 continue;
1799 if(!GetCriteriaDataSet(criteria))
1800 sLog.outErrorDb( "Table `achievement_criteria_data` not have expected data for for criteria (Entry: %u Type: %u).", criteria->ID, criteria->requiredType);
1803 sLog.outString();
1804 sLog.outString(">> Loaded %u additional achievement criteria data.",count);
1807 void AchievementGlobalMgr::LoadCompletedAchievements()
1809 QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
1811 if(!result)
1813 barGoLink bar(1);
1814 bar.step();
1816 sLog.outString();
1817 sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
1818 return;
1821 barGoLink bar(result->GetRowCount());
1824 bar.step();
1825 Field *fields = result->Fetch();
1827 uint32 achievement_id = fields[0].GetUInt32();
1828 if(!sAchievementStore.LookupEntry(achievement_id))
1830 // we will remove not existed achievement for all characters
1831 sLog.outError("Not existed achievement %u data removed from table `character_achievement`.",achievement_id);
1832 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id);
1833 continue;
1836 m_allCompletedAchievements.insert(achievement_id);
1837 } while(result->NextRow());
1839 delete result;
1841 sLog.outString();
1842 sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size());
1845 void AchievementGlobalMgr::LoadRewards()
1847 m_achievementRewards.clear(); // need for reload case
1849 // 0 1 2 3 4 5 6
1850 QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
1852 if(!result)
1854 barGoLink bar(1);
1856 bar.step();
1858 sLog.outString();
1859 sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
1860 return;
1863 uint32 count = 0;
1864 barGoLink bar(result->GetRowCount());
1868 bar.step();
1870 Field *fields = result->Fetch();
1871 uint32 entry = fields[0].GetUInt32();
1872 if (!sAchievementStore.LookupEntry(entry))
1874 sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
1875 continue;
1878 AchievementReward reward;
1879 reward.titleId[0] = fields[1].GetUInt32();
1880 reward.titleId[1] = fields[2].GetUInt32();
1881 reward.itemId = fields[3].GetUInt32();
1882 reward.sender = fields[4].GetUInt32();
1883 reward.subject = fields[5].GetCppString();
1884 reward.text = fields[6].GetCppString();
1886 if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
1887 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]);
1889 // must be title or mail at least
1890 if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
1892 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
1893 continue;
1896 if (reward.titleId[0])
1898 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
1899 if (!titleEntry)
1901 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
1902 reward.titleId[0] = 0;
1906 if (reward.titleId[1])
1908 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
1909 if (!titleEntry)
1911 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]);
1912 reward.titleId[1] = 0;
1916 //check mail data before item for report including wrong item case
1917 if (reward.sender)
1919 if (!objmgr.GetCreatureTemplate(reward.sender))
1921 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
1922 reward.sender = 0;
1925 else
1927 if (reward.itemId)
1928 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
1930 if (!reward.subject.empty())
1931 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
1933 if (!reward.text.empty())
1934 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
1937 if (reward.itemId)
1939 if (!objmgr.GetItemPrototype(reward.itemId))
1941 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
1942 reward.itemId = 0;
1946 m_achievementRewards[entry] = reward;
1947 ++count;
1949 } while (result->NextRow());
1951 delete result;
1953 sLog.outString();
1954 sLog.outString( ">> Loaded %u achievement rewards", count );
1957 void AchievementGlobalMgr::LoadRewardLocales()
1959 m_achievementRewardLocales.clear(); // need for reload case
1961 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");
1963 if(!result)
1965 barGoLink bar(1);
1967 bar.step();
1969 sLog.outString();
1970 sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
1971 return;
1974 barGoLink bar(result->GetRowCount());
1978 Field *fields = result->Fetch();
1979 bar.step();
1981 uint32 entry = fields[0].GetUInt32();
1983 if(m_achievementRewards.find(entry)==m_achievementRewards.end())
1985 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
1986 continue;
1989 AchievementRewardLocale& data = m_achievementRewardLocales[entry];
1991 for(int i = 1; i < MAX_LOCALE; ++i)
1993 std::string str = fields[1+2*(i-1)].GetCppString();
1994 if(!str.empty())
1996 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
1997 if(idx >= 0)
1999 if(data.subject.size() <= size_t(idx))
2000 data.subject.resize(idx+1);
2002 data.subject[idx] = str;
2005 str = fields[1+2*(i-1)+1].GetCppString();
2006 if(!str.empty())
2008 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
2009 if(idx >= 0)
2011 if(data.text.size() <= size_t(idx))
2012 data.text.resize(idx+1);
2014 data.text[idx] = str;
2018 } while (result->NextRow());
2020 delete result;
2022 sLog.outString();
2023 sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() );