[7976] Store additional reqs for ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE in `achievem...
[getmangos.git] / src / game / AchievementMgr.cpp
blobac08b81e18bd9ea59fc2a5f4bffb56127549871a
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_KILL_CREATURE:
82 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
83 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
84 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
85 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
86 break;
87 default:
88 sLog.outErrorDb( "Table `achievement_criteria_data` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
89 return false;
92 switch(dataType)
94 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
95 case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE:
96 case ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED:
97 return true;
98 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
99 if(!creature.id || !objmgr.GetCreatureTemplate(creature.id))
101 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.",
102 criteria->ID, criteria->requiredType,dataType,creature.id);
103 return false;
105 return true;
106 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
107 if(!classRace.class_id && !classRace.race_id)
109 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.",
110 criteria->ID, criteria->requiredType,dataType);
111 return false;
113 if(classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0)
115 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.",
116 criteria->ID, criteria->requiredType,dataType,classRace.class_id);
117 return false;
119 if(classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0)
121 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.",
122 criteria->ID, criteria->requiredType,dataType,classRace.race_id);
123 return false;
125 return true;
126 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
127 if(health.percent < 1 || health.percent > 100)
129 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.",
130 criteria->ID, criteria->requiredType,dataType,health.percent);
131 return false;
133 return true;
134 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD:
135 if(player_dead.own_team_flag > 1)
137 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong boolean value1 (%u).",
138 criteria->ID, criteria->requiredType,dataType,player_dead.own_team_flag);
139 return false;
141 return true;
142 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
143 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
145 SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id);
146 if(!spellEntry)
148 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell id in value1 (%u), ignore.",
149 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);
150 return false;
152 if(aura.effect_idx >= 3)
154 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell effect index in value2 (%u), ignore.",
155 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);
156 return false;
158 if(!spellEntry->EffectApplyAuraName[aura.effect_idx])
160 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.",
161 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);
162 return false;
164 return true;
166 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
167 if(!GetAreaEntryByAreaID(area.id))
169 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.",
170 criteria->ID, criteria->requiredType,dataType,area.id);
171 return false;
173 return true;
174 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL:
175 if(level.minlevel < 0 || level.minlevel > STRONG_MAX_LEVEL)
177 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL (%u) have wrong minlevel in value1 (%u), ignore.",
178 criteria->ID, criteria->requiredType,dataType,level.minlevel);
179 return false;
181 return true;
182 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER:
183 if(gender.gender > GENDER_NONE)
185 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER (%u) have wrong gender in value1 (%u), ignore.",
186 criteria->ID, criteria->requiredType,dataType,gender.gender);
187 return false;
189 return true;
190 case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY:
191 if(difficalty.difficalty >= TOTAL_DIFFICULTIES)
193 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY (%u) have wrong difficulty in value1 (%u), ignore.",
194 criteria->ID, criteria->requiredType,dataType,difficalty.difficalty);
195 return false;
197 return true;
198 case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT:
199 if(map_players.maxcount <= 0)
201 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT (%u) have wrong max players count in value1 (%u), ignore.",
202 criteria->ID, criteria->requiredType,dataType,map_players.maxcount);
203 return false;
205 return true;
206 default:
207 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,dataType);
208 return false;
210 return false;
213 bool AchievementCriteriaData::Meets(Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const
215 switch(dataType)
217 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
218 return true;
219 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
220 if (!target || target->GetTypeId()!=TYPEID_UNIT)
221 return false;
222 return target->GetEntry() == creature.id;
223 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
224 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
225 return false;
226 if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass())
227 return false;
228 if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace())
229 return false;
230 return true;
231 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
232 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
233 return false;
234 return target->GetHealth()*100 <= health.percent*target->GetMaxHealth();
235 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD:
236 if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || ((Player*)target)->GetDeathTimer() == 0)
237 return false;
238 // flag set == must be same team, not set == different team
239 return (((Player*)target)->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0);
240 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
241 return source->HasAura(aura.spell_id,aura.effect_idx);
242 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
244 uint32 zone_id,area_id;
245 source->GetZoneAndAreaId(zone_id,area_id);
246 return area.id==zone_id || area.id==area_id;
248 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
249 return target && target->HasAura(aura.spell_id,aura.effect_idx);
250 case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE:
251 return miscvalue1 >= value.minvalue;
252 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL:
253 if (!target)
254 return false;
255 return target->getLevel() >= level.minlevel;
256 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER:
257 if (!target)
258 return false;
259 return target->getGender() == gender.gender;
260 case ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED:
261 return false; // always fail
262 case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY:
263 return source->GetMap()->GetSpawnMode()==difficalty.difficalty;
264 case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT:
265 return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount;
267 return false;
270 bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const
272 for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr)
273 if(!itr->Meets(source,target,miscvalue))
274 return false;
276 return true;
279 AchievementMgr::AchievementMgr(Player *player)
281 m_player = player;
284 AchievementMgr::~AchievementMgr()
288 void AchievementMgr::Reset()
290 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
292 WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
293 data << uint32(iter->first);
294 m_player->SendDirectMessage(&data);
297 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
299 WorldPacket data(SMSG_CRITERIA_DELETED,4);
300 data << uint32(iter->first);
301 m_player->SendDirectMessage(&data);
304 m_completedAchievements.clear();
305 m_criteriaProgress.clear();
306 DeleteFromDB(m_player->GetGUIDLow());
308 // re-fill data
309 CheckAllAchievementCriteria();
312 void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2)
314 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
315 sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2);
317 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
318 return;
320 AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
321 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
323 AchievementCriteriaEntry const *achievementCriteria = (*i);
325 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
326 if (!achievement)
327 continue;
329 // don't update already completed criteria
330 if (IsCompletedCriteria(achievementCriteria,achievement))
331 continue;
333 switch (type)
335 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset
336 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset
337 if (achievementCriteria->healing_done.flag == miscvalue1 &&
338 achievementCriteria->healing_done.mapid == miscvalue2)
339 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
340 break;
341 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset
342 // reset only the criteria having the miscvalue1 condition
343 if (achievementCriteria->win_rated_arena.flag == miscvalue1)
344 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
345 break;
346 default: // reset all cases
347 break;
352 void AchievementMgr::DeleteFromDB(uint32 lowguid)
354 CharacterDatabase.BeginTransaction ();
355 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid);
356 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
357 CharacterDatabase.CommitTransaction ();
360 void AchievementMgr::SaveToDB()
362 if(!m_completedAchievements.empty())
364 bool need_execute = false;
365 std::ostringstream ssdel;
366 std::ostringstream ssins;
367 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
369 if(!iter->second.changed)
370 continue;
372 /// first new/changed record prefix
373 if(!need_execute)
375 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
376 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
377 need_execute = true;
379 /// next new/changed record prefix
380 else
382 ssdel << ", ";
383 ssins << ", ";
386 // new/changed record data
387 ssdel << iter->first;
388 ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
390 /// mark as saved in db
391 iter->second.changed = false;
394 if(need_execute)
395 ssdel << ")";
397 if(need_execute)
399 CharacterDatabase.Execute( ssdel.str().c_str() );
400 CharacterDatabase.Execute( ssins.str().c_str() );
404 if(!m_criteriaProgress.empty())
406 /// prepare deleting and insert
407 bool need_execute_del = false;
408 bool need_execute_ins = false;
409 std::ostringstream ssdel;
410 std::ostringstream ssins;
411 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
413 if(!iter->second.changed)
414 continue;
416 // deleted data (including 0 progress state)
418 /// first new/changed record prefix (for any counter value)
419 if(!need_execute_del)
421 ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
422 need_execute_del = true;
424 /// next new/changed record prefix
425 else
426 ssdel << ", ";
428 // new/changed record data
429 ssdel << iter->first;
432 // store data only for real progress
433 if(iter->second.counter != 0)
435 /// first new/changed record prefix
436 if(!need_execute_ins)
438 ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
439 need_execute_ins = true;
441 /// next new/changed record prefix
442 else
443 ssins << ", ";
445 // new/changed record data
446 ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
449 /// mark as updated in db
450 iter->second.changed = false;
453 if(need_execute_del) // DELETE ... IN (.... _)_
454 ssdel << ")";
456 if(need_execute_del || need_execute_ins)
458 if(need_execute_del)
459 CharacterDatabase.Execute( ssdel.str().c_str() );
460 if(need_execute_ins)
461 CharacterDatabase.Execute( ssins.str().c_str() );
466 void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
468 if(achievementResult)
472 Field *fields = achievementResult->Fetch();
474 uint32 achievement_id = fields[0].GetUInt32();
476 // don't must happen: cleanup at server startup in achievementmgr.LoadCompletedAchievements()
477 if(!sAchievementStore.LookupEntry(achievement_id))
478 continue;
480 CompletedAchievementData& ca = m_completedAchievements[achievement_id];
481 ca.date = time_t(fields[1].GetUInt64());
482 ca.changed = false;
483 } while(achievementResult->NextRow());
484 delete achievementResult;
487 if(criteriaResult)
491 Field *fields = criteriaResult->Fetch();
493 uint32 id = fields[0].GetUInt32();
494 uint32 counter = fields[1].GetUInt32();
495 time_t date = time_t(fields[2].GetUInt64());
497 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
498 if (!criteria)
500 // we will remove not existed criteria for all characters
501 sLog.outError("Not existed achievement creataria %u data removed from table `character_achievement_progress`.",id);
502 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id);
503 continue;
506 if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))
507 continue;
509 CriteriaProgress& progress = m_criteriaProgress[id];
510 progress.counter = counter;
511 progress.date = date;
512 progress.changed = false;
513 } while(criteriaResult->NextRow());
514 delete criteriaResult;
519 void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
521 if(GetPlayer()->GetSession()->PlayerLoading())
522 return;
524 #ifdef MANGOS_DEBUG
525 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
526 sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
527 #endif
529 if(Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()))
531 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
532 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
533 guild->BroadcastWorker(say_do,GetPlayer());
536 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
538 // broadcast realm first reached
539 WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4);
540 data << GetPlayer()->GetName();
541 data << uint64(GetPlayer()->GetGUID());
542 data << uint32(achievement->ID);
543 data << uint32(0); // 1=link supplied string as player name, 0=display plain string
544 sWorld.SendGlobalMessage(&data);
546 else
548 CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
550 Cell cell(p);
551 cell.data.Part.reserved = ALL_DISTRICT;
552 cell.SetNoCreate();
554 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
555 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
556 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do);
557 TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> >, WorldTypeMapContainer > message(say_worker);
558 CellLock<GridReadGuard> cell_lock(cell, p);
559 cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap());
562 WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
563 data.append(GetPlayer()->GetPackGUID());
564 data << uint32(achievement->ID);
565 data << uint32(secsToTimeBitFields(time(NULL)));
566 data << uint32(0);
567 GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true);
570 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
572 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
573 data << uint32(id);
575 // the counter is packed like a packed Guid
576 data.appendPackGUID(progress->counter);
578 data.append(GetPlayer()->GetPackGUID());
579 data << uint32(0);
580 data << uint32(secsToTimeBitFields(progress->date));
581 data << uint32(0); // timer 1
582 data << uint32(0); // timer 2
583 GetPlayer()->SendDirectMessage(&data);
587 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
589 void AchievementMgr::CheckAllAchievementCriteria()
591 // suppress sending packets
592 for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
593 UpdateAchievementCriteria(AchievementCriteriaTypes(i));
596 static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 };
597 static const uint32 achievIdForDangeon[][4] =
599 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
600 { 321, true, true, true },
601 { 916, false, true, false },
602 { 917, false, true, false },
603 { 918, true, false, false },
604 { 2219, false, false, true },
605 { 0, false, false, false }
609 * this function will be called whenever the user might have done a criteria relevant action
611 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time)
613 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
614 sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
616 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
617 return;
619 AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
620 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
622 AchievementCriteriaEntry const *achievementCriteria = (*i);
624 if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
625 continue;
627 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
628 if (!achievement)
629 continue;
631 if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
632 (achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
633 continue;
635 // don't update already completed criteria
636 if (IsCompletedCriteria(achievementCriteria,achievement))
637 continue;
639 switch (type)
641 // std. case: increment at 1
642 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
643 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
644 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
645 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
646 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
647 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
648 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
649 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
650 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
651 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
652 if(!miscvalue1)
653 continue;
654 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
655 break;
656 // std case: increment at miscvalue1
657 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
658 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
659 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING:
660 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
661 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
662 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
663 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED:
664 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED:
665 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
666 if(!miscvalue1)
667 continue;
668 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
669 break;
670 // std case: high value at miscvalue1
671 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
672 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */
673 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT:
674 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED:
675 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED:
676 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED:
677 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
678 if(!miscvalue1)
679 continue;
680 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
681 break;
683 // specialized cases
685 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
687 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
688 if(!miscvalue1)
689 continue;
690 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
691 continue;
693 // those requirements couldn't be found in the dbc
694 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
695 if(!data)
696 continue;
698 if(!data->Meets(GetPlayer(),unit))
699 continue;
701 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
702 break;
704 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
705 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
706 break;
707 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
708 // update at loading or specific skill update
709 if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID)
710 continue;
711 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
712 SetCriteriaProgress(achievementCriteria, skillvalue);
713 break;
714 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
715 // update at loading or specific skill update
716 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID)
717 continue;
718 if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID))
719 SetCriteriaProgress(achievementCriteria, maxSkillvalue);
720 break;
721 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
722 if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
723 SetCriteriaProgress(achievementCriteria, 1);
724 break;
725 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
727 uint32 counter =0;
728 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
729 if(itr->second.m_rewarded)
730 counter++;
731 SetCriteriaProgress(achievementCriteria, counter);
732 break;
734 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
736 // speedup for non-login case
737 if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID)
738 continue;
740 uint32 counter =0;
741 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
743 Quest const* quest = objmgr.GetQuestTemplate(itr->first);
744 if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
745 counter++;
747 SetCriteriaProgress(achievementCriteria, counter);
748 break;
750 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
751 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
752 if(!miscvalue1)
753 continue;
754 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
755 continue;
756 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
757 break;
758 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
759 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
760 if(!miscvalue1)
761 continue;
762 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
763 continue;
764 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
765 break;
766 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
768 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
769 if(!miscvalue1)
770 continue;
771 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
772 bool notfit = false;
773 for(int j = 0; j < MAX_ARENA_SLOT; ++j)
775 if(achievIdByArenaSlot[j] == achievement->ID)
777 BattleGround* bg = GetPlayer()->GetBattleGround();
778 if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
779 notfit = true;
781 break;
784 if(notfit)
785 continue;
787 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
788 break;
790 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
792 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
793 if(!miscvalue1)
794 continue;
796 Map const* map = GetPlayer()->GetMap();
797 if(!map->IsDungeon())
798 continue;
800 // search case
801 bool found = false;
802 for(int j = 0; achievIdForDangeon[j][0]; ++j)
804 if(achievIdForDangeon[j][0] == achievement->ID)
806 if(map->IsRaid())
808 // if raid accepted (ignore difficulty)
809 if(!achievIdForDangeon[j][2])
810 break; // for
812 else if(GetPlayer()->GetDifficulty()==DIFFICULTY_NORMAL)
814 // dungeon in normal mode accepted
815 if(!achievIdForDangeon[j][1])
816 break; // for
818 else
820 // dungeon in heroic mode accepted
821 if(!achievIdForDangeon[j][3])
822 break; // for
825 found = true;
826 break; // for
829 if(!found)
830 continue;
832 //FIXME: work only for instances where max==min for players
833 if(((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
834 continue;
835 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
836 break;
839 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
840 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
841 if(!miscvalue1)
842 continue;
843 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
844 continue;
845 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
846 break;
847 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
848 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
849 if(!miscvalue1)
850 continue;
852 // if team check required: must kill by opposition faction
853 if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
854 continue;
856 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
857 break;
858 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
860 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
861 if(!miscvalue1)
862 continue;
863 if(achievement->ID == 1260)
865 if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED)
866 continue;
867 if(!IsHolidayActive(HOLIDAY_BREWFEST))
868 continue;
870 // miscvalue1 is the ingame fallheight*100 as stored in dbc
871 SetCriteriaProgress(achievementCriteria, miscvalue1);
872 break;
874 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
875 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
876 if(!miscvalue1)
877 continue;
878 if(miscvalue2 != achievementCriteria->death_from.type)
879 continue;
880 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
881 break;
882 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
883 // if miscvalues != 0, it contains the questID.
884 if (miscvalue1)
886 if (miscvalue1 == achievementCriteria->complete_quest.questID)
887 SetCriteriaProgress(achievementCriteria, 1);
889 else
891 // login case.
892 if(GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
893 SetCriteriaProgress(achievementCriteria, 1);
895 break;
896 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
897 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
898 if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID)
899 continue;
900 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
901 break;
902 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
903 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
904 continue;
905 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
906 break;
907 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
908 if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
909 continue;
911 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
912 SetCriteriaProgress(achievementCriteria, 1);
913 break;
914 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
916 // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored)
917 // miscvalue2=count of item loot
918 if (!miscvalue1 || !miscvalue2)
919 continue;
920 if (miscvalue1 != achievementCriteria->loot_type.lootType)
921 continue;
923 // zone specific
924 if(achievementCriteria->loot_type.lootTypeCount==1)
926 // those requirements couldn't be found in the dbc
927 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
928 if(!data)
929 continue;
931 if(!data->Meets(GetPlayer(),unit))
932 continue;
935 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
936 break;
938 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
939 // speedup for non-login case
940 if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1)
941 continue;
942 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
943 break;
944 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
945 // miscvalue1 contains the personal rating
946 if (!miscvalue1) // no update at login
947 continue;
949 // additional requirements
950 if(achievementCriteria->win_rated_arena.flag==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
952 // those requirements couldn't be found in the dbc
953 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
954 if(!data || !data->Meets(GetPlayer(),unit,miscvalue1))
956 // reset the progress as we have a win without the requirement.
957 SetCriteriaProgress(achievementCriteria, 0);
958 continue;
962 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
963 break;
964 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
965 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
966 if(!miscvalue1)
967 continue;
968 if(achievementCriteria->use_item.itemID != miscvalue1)
969 continue;
970 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
971 break;
972 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
973 // You _have_ to loot that item, just owning it when logging in does _not_ count!
974 if(!miscvalue1)
975 continue;
976 if(miscvalue1 != achievementCriteria->own_item.itemID)
977 continue;
978 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
979 break;
980 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
982 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
983 if(!worldOverlayEntry)
984 break;
986 bool matchFound = false;
987 for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
989 uint32 area_id = worldOverlayEntry->areatableID[j];
990 if(!area_id) // array have 0 only in empty tail
991 break;
993 int32 exploreFlag = GetAreaFlagByAreaID(area_id);
994 if(exploreFlag < 0)
995 continue;
997 uint32 playerIndexOffset = uint32(exploreFlag) / 32;
998 uint32 mask = 1<< (uint32(exploreFlag) % 32);
1000 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
1002 matchFound = true;
1003 break;
1007 if(matchFound)
1008 SetCriteriaProgress(achievementCriteria, 1);
1009 break;
1011 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1012 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount());
1013 break;
1014 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1016 // skip faction check only at loading
1017 if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID)
1018 continue;
1020 int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
1021 if (reputation > 0)
1022 SetCriteriaProgress(achievementCriteria, reputation);
1023 break;
1025 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1027 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
1028 break;
1030 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1032 // skip for login case
1033 if(!miscvalue1)
1034 continue;
1035 SetCriteriaProgress(achievementCriteria, 1);
1036 break;
1038 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1039 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1041 // miscvalue1 = itemid
1042 // miscvalue2 = diced value
1043 if(!miscvalue1)
1044 continue;
1045 if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
1046 continue;
1047 ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 );
1049 uint32 requiredItemLevel = 0;
1050 if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
1051 requiredItemLevel = 185;
1053 if(!pProto || pProto->ItemLevel <requiredItemLevel)
1054 continue;
1055 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1056 break;
1058 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1060 // miscvalue1 = emote
1061 if(!miscvalue1)
1062 continue;
1063 if(miscvalue1 != achievementCriteria->do_emote.emoteID)
1064 continue;
1065 if(achievementCriteria->do_emote.count)
1067 // those requirements couldn't be found in the dbc
1068 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1069 if(!data)
1070 continue;
1072 if(!data->Meets(GetPlayer(),unit))
1073 continue;
1076 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1077 break;
1079 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1080 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1082 if (!miscvalue1)
1083 continue;
1085 if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP)
1087 if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid)
1088 continue;
1090 // map specific case (BG in fact) expected player targeted damage/heal
1091 if(!unit || unit->GetTypeId()!=TYPEID_PLAYER)
1092 continue;
1095 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
1096 break;
1098 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1099 // miscvalue1 = item_id
1100 if(!miscvalue1)
1101 continue;
1102 if(miscvalue1 != achievementCriteria->equip_item.itemID)
1103 continue;
1105 SetCriteriaProgress(achievementCriteria, 1);
1106 break;
1107 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1108 // miscvalue1 = go entry
1109 if(!miscvalue1)
1110 continue;
1111 if(miscvalue1 != achievementCriteria->use_gameobject.goEntry)
1112 continue;
1114 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1115 break;
1116 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1117 if (!miscvalue1)
1118 continue;
1119 if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
1120 continue;
1122 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1123 break;
1124 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1126 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine)
1127 continue;
1129 uint32 spellCount = 0;
1130 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1131 spellIter != GetPlayer()->GetSpellMap().end();
1132 ++spellIter)
1134 for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first);
1135 skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first);
1136 ++skillIter)
1138 if(skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine)
1139 spellCount++;
1142 SetCriteriaProgress(achievementCriteria, spellCount);
1143 break;
1145 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1146 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1147 break;
1148 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1149 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1150 break;
1151 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1152 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
1153 break;
1154 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1156 if (!miscvalue1)
1157 continue;
1159 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
1160 continue;
1162 // those requirements couldn't be found in the dbc
1163 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1164 if(!data)
1165 continue;
1167 if(!data->Meets(GetPlayer(),unit))
1168 continue;
1170 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1171 break;
1173 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1175 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine)
1176 continue;
1178 uint32 spellCount = 0;
1179 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1180 spellIter != GetPlayer()->GetSpellMap().end();
1181 ++spellIter)
1183 for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first);
1184 skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first);
1185 ++skillIter)
1187 if(skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine)
1188 spellCount++;
1191 SetCriteriaProgress(achievementCriteria, spellCount);
1192 break;
1194 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1195 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
1196 break;
1197 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1198 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
1199 continue;
1201 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1202 break;
1203 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1204 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
1205 continue;
1207 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1208 break;
1209 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1210 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST);
1211 break;
1212 // std case: not exist in DBC, not triggered in code as result
1213 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1214 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1215 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1216 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER:
1217 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT:
1218 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING:
1219 break;
1220 // FIXME: not triggered in code as result, need to implement
1221 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1222 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY:
1223 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
1224 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE:
1225 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
1226 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
1227 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
1228 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL:
1229 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
1230 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING:
1231 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
1232 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1233 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
1234 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
1235 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1236 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
1237 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE:
1238 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:
1239 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
1240 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS:
1241 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1242 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1243 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL:
1244 break; // Not implemented yet :(
1246 if(IsCompletedCriteria(achievementCriteria,achievement))
1247 CompletedCriteriaFor(achievement);
1249 // check again the completeness for SUMM and REQ COUNT achievements,
1250 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
1251 if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
1253 if (IsCompletedAchievement(achievement))
1254 CompletedAchievement(achievement);
1257 if(AchievementEntryList const* achRefList = achievementmgr.GetAchievementByReferencedId(achievement->ID))
1259 for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
1260 if(IsCompletedAchievement(*itr))
1261 CompletedAchievement(*itr);
1266 static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
1267 static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
1269 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement)
1271 // counter can never complete
1272 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1273 return false;
1275 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1277 // someone on this realm has already completed that achievement
1278 if(achievementmgr.IsRealmCompleted(achievement))
1279 return false;
1282 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
1283 if(itr == m_criteriaProgress.end())
1284 return false;
1286 CriteriaProgress const* progress = &itr->second;
1288 switch(achievementCriteria->requiredType)
1290 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1291 return progress->counter >= achievementCriteria->kill_creature.creatureCount;
1292 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
1294 // skip wrong class achievements
1295 for(int i = 1; i < MAX_CLASSES; ++i)
1296 if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
1297 return false;
1299 // skip wrong race achievements
1300 for(int i = 1; i < MAX_RACES; ++i)
1301 if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
1302 return false;
1304 // appropriate class/race or not class/race specific
1305 return progress->counter >= achievementCriteria->reach_level.level;
1307 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
1308 return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
1309 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
1310 return progress->counter >= 1;
1311 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
1312 return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
1313 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
1314 return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
1315 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1316 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1317 return progress->counter >= achievementCriteria->healing_done.count;
1318 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
1319 return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
1320 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1321 return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
1322 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1323 return progress->counter >= 1;
1324 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1325 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1326 return progress->counter >= achievementCriteria->be_spell_target.spellCount;
1327 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1328 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1329 return progress->counter >= achievementCriteria->cast_spell.castCount;
1330 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1331 return progress->counter >= 1;
1332 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1333 return progress->counter >= achievementCriteria->own_item.itemCount;
1334 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1335 return progress->counter >= achievementCriteria->win_rated_arena.count;
1336 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
1337 return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75);
1338 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1339 return progress->counter >= achievementCriteria->use_item.itemCount;
1340 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1341 return progress->counter >= achievementCriteria->loot_item.itemCount;
1342 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1343 return progress->counter >= 1;
1344 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1345 return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
1346 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1347 return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
1348 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1349 return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
1350 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1351 return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
1352 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1353 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1354 return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
1355 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1356 return progress->counter >= achievementCriteria->hk_class.count;
1357 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1358 return progress->counter >= achievementCriteria->hk_race.count;
1359 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1360 return progress->counter >= achievementCriteria->do_emote.count;
1361 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1362 return progress->counter >= achievementCriteria->equip_item.count;
1363 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
1364 return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper;
1365 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
1366 return progress->counter >= achievementCriteria->loot_money.goldInCopper;
1367 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1368 return progress->counter >= achievementCriteria->use_gameobject.useCount;
1369 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1370 return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount;
1371 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1372 return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount;
1373 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1374 return progress->counter >= achievementCriteria->win_duel.duelCount;
1375 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1376 return progress->counter >= achievementCriteria->loot_type.lootTypeCount;
1377 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1378 return progress->counter >= achievementCriteria->learn_skill_line.spellCount;
1379 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1380 return progress->counter >= achievementCriteria->honorable_kill.killCount;
1382 // handle all statistic-only criteria here
1383 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
1384 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
1385 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
1386 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
1387 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
1388 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
1389 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1390 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
1391 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
1392 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
1393 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
1394 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
1395 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
1396 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD:
1397 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1398 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1399 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1400 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1401 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
1402 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
1403 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1404 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1405 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1406 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
1407 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
1408 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
1409 return false;
1411 return false;
1414 void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
1416 // counter can never complete
1417 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1418 return;
1420 // already completed and stored
1421 if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1422 return;
1424 if (IsCompletedAchievement(achievement))
1425 CompletedAchievement(achievement);
1428 bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
1430 // counter can never complete
1431 if(entry->flags & ACHIEVEMENT_FLAG_COUNTER)
1432 return false;
1434 // for achievement with referenced achievement criterias get from referenced and counter from self
1435 uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID;
1436 uint32 achievmentForTestCount = entry->count;
1438 AchievementCriteriaEntryList const* cList = achievementmgr.GetAchievementCriteriaByAchievement(achievmentForTestId);
1439 if(!cList)
1440 return false;
1441 uint32 count = 0;
1443 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
1444 // Oddly, the target count is NOT countained in the achievement, but in each individual criteria
1445 if (entry->flags & ACHIEVEMENT_FLAG_SUMM)
1447 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1449 AchievementCriteriaEntry const* criteria = *itr;
1451 CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID);
1452 if(itrProgress == m_criteriaProgress.end())
1453 continue;
1455 CriteriaProgress const* progress = &itrProgress->second;
1456 count += progress->counter;
1458 // for counters, field4 contains the main count requirement
1459 if (count >= criteria->raw.count)
1460 return true;
1462 return false;
1465 // Default case - need complete all or
1466 bool completed_all = true;
1467 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1469 AchievementCriteriaEntry const* criteria = *itr;
1471 bool completed = IsCompletedCriteria(criteria,entry);
1473 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1474 if(completed)
1475 ++count;
1476 else
1477 completed_all = false;
1479 // completed as have req. count of completed criterias
1480 if(achievmentForTestCount > 0 && achievmentForTestCount <= count)
1481 return true;
1484 // all criterias completed requirement
1485 if(completed_all && achievmentForTestCount==0)
1486 return true;
1488 return false;
1491 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
1493 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
1494 sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
1496 CriteriaProgress *progress = NULL;
1498 CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
1500 if(iter == m_criteriaProgress.end())
1502 // not create record for 0 counter
1503 if(changeValue == 0)
1504 return;
1506 progress = &m_criteriaProgress[entry->ID];
1507 progress->counter = changeValue;
1508 progress->date = time(NULL);
1510 else
1512 progress = &iter->second;
1514 uint32 newValue = 0;
1515 switch(ptype)
1517 case PROGRESS_SET:
1518 newValue = changeValue;
1519 break;
1520 case PROGRESS_ACCUMULATE:
1522 // avoid overflow
1523 uint32 max_value = std::numeric_limits<uint32>::max();
1524 newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value;
1525 break;
1527 case PROGRESS_HIGHEST:
1528 newValue = progress->counter < changeValue ? changeValue : progress->counter;
1529 break;
1532 // not update (not mark as changed) if counter will have same value
1533 if(progress->counter == newValue)
1534 return;
1536 progress->counter = newValue;
1539 progress->changed = true;
1541 if(entry->timeLimit)
1543 time_t now = time(NULL);
1544 if(time_t(progress->date + entry->timeLimit) < now)
1545 progress->counter = 1;
1547 // also it seems illogical, the timeframe will be extended at every criteria update
1548 progress->date = now;
1550 SendCriteriaUpdate(entry->ID,progress);
1553 void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
1555 sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
1556 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1557 return;
1559 SendAchievementEarned(achievement);
1560 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
1561 ca.date = time(NULL);
1562 ca.changed = true;
1564 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1565 // TODO: where do set this instead?
1566 if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1567 achievementmgr.SetRealmCompleted(achievement);
1569 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
1571 // reward items and titles if any
1572 AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement);
1574 // no rewards
1575 if(!reward)
1576 return;
1578 // titles
1579 if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
1581 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
1582 GetPlayer()->SetTitle(titleEntry);
1585 // mail
1586 if(reward->sender)
1588 Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
1590 MailItemsInfo mi;
1591 if(item)
1593 // save new item before send
1594 item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1596 // item
1597 mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
1600 int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1602 // subject and text
1603 std::string subject = reward->subject;
1604 std::string text = reward->text;
1605 if ( loc_idx >= 0 )
1607 if(AchievementRewardLocale const* loc = achievementmgr.GetAchievementRewardLocale(achievement))
1609 if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty())
1610 subject = loc->subject[loc_idx];
1611 if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty())
1612 text = loc->text[loc_idx];
1616 uint32 itemTextId = objmgr.CreateItemText( text );
1618 WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE, MAIL_STATIONERY_NORMAL, reward->sender, GetPlayer()->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
1622 void AchievementMgr::SendAllAchievementData()
1624 // since we don't know the exact size of the packed GUIDs this is just an approximation
1625 WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1626 BuildAllDataPacket(&data);
1627 GetPlayer()->GetSession()->SendPacket(&data);
1630 void AchievementMgr::SendRespondInspectAchievements(Player* player)
1632 // since we don't know the exact size of the packed GUIDs this is just an approximation
1633 WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1634 data.append(GetPlayer()->GetPackGUID());
1635 BuildAllDataPacket(&data);
1636 player->GetSession()->SendPacket(&data);
1640 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1642 void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
1644 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
1646 *data << uint32(iter->first);
1647 *data << uint32(secsToTimeBitFields(iter->second.date));
1649 *data << int32(-1);
1651 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
1653 *data << uint32(iter->first);
1654 data->appendPackGUID(iter->second.counter);
1655 data->append(GetPlayer()->GetPackGUID());
1656 *data << uint32(0);
1657 *data << uint32(secsToTimeBitFields(iter->second.date));
1658 *data << uint32(0);
1659 *data << uint32(0);
1662 *data << int32(-1);
1665 //==========================================================
1666 AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
1668 return m_AchievementCriteriasByType[type];
1671 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1673 if(sAchievementCriteriaStore.GetNumRows()==0)
1675 barGoLink bar(1);
1676 bar.step();
1678 sLog.outString();
1679 sLog.outErrorDb(">> Loaded 0 achievement criteria.");
1680 return;
1683 barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
1684 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1686 bar.step();
1688 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1689 if(!criteria)
1690 continue;
1692 m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
1693 m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
1696 sLog.outString();
1697 sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size());
1700 void AchievementGlobalMgr::LoadAchievementReferenceList()
1702 if(sAchievementStore.GetNumRows()==0)
1704 barGoLink bar(1);
1705 bar.step();
1707 sLog.outString();
1708 sLog.outErrorDb(">> Loaded 0 achievement references.");
1709 return;
1712 uint32 count = 0;
1713 barGoLink bar( sAchievementStore.GetNumRows() );
1714 for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
1716 bar.step();
1718 AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
1719 if(!achievement || !achievement->refAchievement)
1720 continue;
1722 m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement);
1723 ++count;
1726 sLog.outString();
1727 sLog.outString(">> Loaded %u achievement references.",count);
1730 void AchievementGlobalMgr::LoadAchievementCriteriaData()
1732 m_criteriaDataMap.clear(); // need for reload case
1734 QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data");
1736 if(!result)
1738 barGoLink bar(1);
1739 bar.step();
1741 sLog.outString();
1742 sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty.");
1743 return;
1746 uint32 count = 0;
1747 uint32 disabled_count = 0;
1748 barGoLink bar(result->GetRowCount());
1751 bar.step();
1752 Field *fields = result->Fetch();
1753 uint32 criteria_id = fields[0].GetUInt32();
1755 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
1757 if (!criteria)
1759 sLog.outErrorDb( "Table `achievement_criteria_data` have data for not existed criteria (Entry: %u), ignore.", criteria_id);
1760 continue;
1763 AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
1765 if (!data.IsValid(criteria))
1767 continue;
1770 // this will allocate empty data set storage
1771 AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id];
1773 if (data.dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED)
1774 ++disabled_count;
1776 // add real data only for not NONE data types
1777 if (data.dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE)
1778 dataSet.Add(data);
1780 // counting data by and data types
1781 ++count;
1782 } while(result->NextRow());
1784 delete result;
1786 // post loading checks
1787 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1789 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1790 if(!criteria)
1791 continue;
1793 switch(criteria->requiredType)
1795 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1796 break; // any cases
1797 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases
1798 if(criteria->win_rated_arena.flag!=ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1799 continue;
1800 break;
1801 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases
1802 if(criteria->do_emote.count==0)
1803 continue;
1804 break;
1805 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases
1806 break;
1807 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases
1808 if(criteria->loot_type.lootTypeCount!=1)
1809 continue;
1810 break;
1811 default: // type not use DB data, ignore
1812 continue;
1815 if(!GetCriteriaDataSet(criteria))
1816 sLog.outErrorDb( "Table `achievement_criteria_data` not have expected data for criteria (Entry: %u Type: %u) for achievement %u.", criteria->ID, criteria->requiredType, criteria->referredAchievement);
1819 sLog.outString();
1820 sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count);
1823 void AchievementGlobalMgr::LoadCompletedAchievements()
1825 QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
1827 if(!result)
1829 barGoLink bar(1);
1830 bar.step();
1832 sLog.outString();
1833 sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
1834 return;
1837 barGoLink bar(result->GetRowCount());
1840 bar.step();
1841 Field *fields = result->Fetch();
1843 uint32 achievement_id = fields[0].GetUInt32();
1844 if(!sAchievementStore.LookupEntry(achievement_id))
1846 // we will remove not existed achievement for all characters
1847 sLog.outError("Not existed achievement %u data removed from table `character_achievement`.",achievement_id);
1848 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id);
1849 continue;
1852 m_allCompletedAchievements.insert(achievement_id);
1853 } while(result->NextRow());
1855 delete result;
1857 sLog.outString();
1858 sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size());
1861 void AchievementGlobalMgr::LoadRewards()
1863 m_achievementRewards.clear(); // need for reload case
1865 // 0 1 2 3 4 5 6
1866 QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
1868 if(!result)
1870 barGoLink bar(1);
1872 bar.step();
1874 sLog.outString();
1875 sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
1876 return;
1879 uint32 count = 0;
1880 barGoLink bar(result->GetRowCount());
1884 bar.step();
1886 Field *fields = result->Fetch();
1887 uint32 entry = fields[0].GetUInt32();
1888 if (!sAchievementStore.LookupEntry(entry))
1890 sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
1891 continue;
1894 AchievementReward reward;
1895 reward.titleId[0] = fields[1].GetUInt32();
1896 reward.titleId[1] = fields[2].GetUInt32();
1897 reward.itemId = fields[3].GetUInt32();
1898 reward.sender = fields[4].GetUInt32();
1899 reward.subject = fields[5].GetCppString();
1900 reward.text = fields[6].GetCppString();
1902 if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
1903 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]);
1905 // must be title or mail at least
1906 if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
1908 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
1909 continue;
1912 if (reward.titleId[0])
1914 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
1915 if (!titleEntry)
1917 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
1918 reward.titleId[0] = 0;
1922 if (reward.titleId[1])
1924 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
1925 if (!titleEntry)
1927 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]);
1928 reward.titleId[1] = 0;
1932 //check mail data before item for report including wrong item case
1933 if (reward.sender)
1935 if (!objmgr.GetCreatureTemplate(reward.sender))
1937 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
1938 reward.sender = 0;
1941 else
1943 if (reward.itemId)
1944 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
1946 if (!reward.subject.empty())
1947 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
1949 if (!reward.text.empty())
1950 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
1953 if (reward.itemId)
1955 if (!objmgr.GetItemPrototype(reward.itemId))
1957 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
1958 reward.itemId = 0;
1962 m_achievementRewards[entry] = reward;
1963 ++count;
1965 } while (result->NextRow());
1967 delete result;
1969 sLog.outString();
1970 sLog.outString( ">> Loaded %u achievement rewards", count );
1973 void AchievementGlobalMgr::LoadRewardLocales()
1975 m_achievementRewardLocales.clear(); // need for reload case
1977 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");
1979 if(!result)
1981 barGoLink bar(1);
1983 bar.step();
1985 sLog.outString();
1986 sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
1987 return;
1990 barGoLink bar(result->GetRowCount());
1994 Field *fields = result->Fetch();
1995 bar.step();
1997 uint32 entry = fields[0].GetUInt32();
1999 if(m_achievementRewards.find(entry)==m_achievementRewards.end())
2001 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
2002 continue;
2005 AchievementRewardLocale& data = m_achievementRewardLocales[entry];
2007 for(int i = 1; i < MAX_LOCALE; ++i)
2009 std::string str = fields[1+2*(i-1)].GetCppString();
2010 if(!str.empty())
2012 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
2013 if(idx >= 0)
2015 if(data.subject.size() <= size_t(idx))
2016 data.subject.resize(idx+1);
2018 data.subject[idx] = str;
2021 str = fields[1+2*(i-1)+1].GetCppString();
2022 if(!str.empty())
2024 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
2025 if(idx >= 0)
2027 if(data.text.size() <= size_t(idx))
2028 data.text.resize(idx+1);
2030 data.text[idx] = str;
2034 } while (result->NextRow());
2036 delete result;
2038 sLog.outString();
2039 sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() );