[8483] Implement glyph 43361.
[getmangos.git] / src / game / AchievementMgr.cpp
bloba638510697ca7dbd323a736f0ce596322edb3a41
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"
35 #include "MapManager.h"
36 #include "BattleGround.h"
37 #include "BattleGroundAB.h"
39 #include "Policies/SingletonImp.h"
41 INSTANTIATE_SINGLETON_1(AchievementGlobalMgr);
43 namespace MaNGOS
45 class AchievementChatBuilder
47 public:
48 AchievementChatBuilder(Player const& pl, ChatMsg msgtype, int32 textId, uint32 ach_id)
49 : i_player(pl), i_msgtype(msgtype), i_textId(textId), i_achievementId(ach_id) {}
50 void operator()(WorldPacket& data, int32 loc_idx)
52 char const* text = objmgr.GetMangosString(i_textId,loc_idx);
54 data << uint8(i_msgtype);
55 data << uint32(LANG_UNIVERSAL);
56 data << uint64(i_player.GetGUID());
57 data << uint32(5);
58 data << uint64(i_player.GetGUID());
59 data << uint32(strlen(text)+1);
60 data << text;
61 data << uint8(0);
62 data << uint32(i_achievementId);
65 private:
66 Player const& i_player;
67 ChatMsg i_msgtype;
68 int32 i_textId;
69 uint32 i_achievementId;
71 } // namespace MaNGOS
74 bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
76 if(dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE)
78 sLog.outErrorDb( "Table `achievement_criteria_data` for criteria (Entry: %u) have wrong data type (%u), ignore.", criteria->ID,dataType);
79 return false;
82 switch(criteria->requiredType)
84 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
85 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
86 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
87 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: // only hardcoded list
88 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
89 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
90 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
91 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
92 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
93 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
94 break;
95 default:
96 sLog.outErrorDb( "Table `achievement_criteria_data` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
97 return false;
100 switch(dataType)
102 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
103 case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE:
104 case ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED:
105 return true;
106 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
107 if (!creature.id || !objmgr.GetCreatureTemplate(creature.id))
109 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.",
110 criteria->ID, criteria->requiredType,dataType,creature.id);
111 return false;
113 return true;
114 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
115 if (!classRace.class_id && !classRace.race_id)
117 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.",
118 criteria->ID, criteria->requiredType,dataType);
119 return false;
121 if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0)
123 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.",
124 criteria->ID, criteria->requiredType,dataType,classRace.class_id);
125 return false;
127 if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0)
129 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.",
130 criteria->ID, criteria->requiredType,dataType,classRace.race_id);
131 return false;
133 return true;
134 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
135 if (health.percent < 1 || health.percent > 100)
137 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.",
138 criteria->ID, criteria->requiredType,dataType,health.percent);
139 return false;
141 return true;
142 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD:
143 if (player_dead.own_team_flag > 1)
145 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD (%u) have wrong boolean value1 (%u).",
146 criteria->ID, criteria->requiredType,dataType,player_dead.own_team_flag);
147 return false;
149 return true;
150 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
151 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
153 SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id);
154 if (!spellEntry)
156 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell id in value1 (%u), ignore.",
157 criteria->ID, criteria->requiredType,(dataType==ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.spell_id);
158 return false;
160 if (aura.effect_idx >= 3)
162 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) have wrong spell effect index in value2 (%u), ignore.",
163 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);
164 return false;
166 if (!spellEntry->EffectApplyAuraName[aura.effect_idx])
168 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.",
169 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);
170 return false;
172 return true;
174 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
175 if (!GetAreaEntryByAreaID(area.id))
177 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.",
178 criteria->ID, criteria->requiredType,dataType,area.id);
179 return false;
181 return true;
182 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL:
183 if (level.minlevel < 0 || level.minlevel > STRONG_MAX_LEVEL)
185 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.",
186 criteria->ID, criteria->requiredType,dataType,level.minlevel);
187 return false;
189 return true;
190 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER:
191 if (gender.gender > GENDER_NONE)
193 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.",
194 criteria->ID, criteria->requiredType,dataType,gender.gender);
195 return false;
197 return true;
198 case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY:
199 if (difficalty.difficalty >= TOTAL_DIFFICULTIES)
201 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.",
202 criteria->ID, criteria->requiredType,dataType,difficalty.difficalty);
203 return false;
205 return true;
206 case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT:
207 if (map_players.maxcount <= 0)
209 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.",
210 criteria->ID, criteria->requiredType,dataType,map_players.maxcount);
211 return false;
213 return true;
214 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM:
215 if (team.team != ALLIANCE && team.team != HORDE)
217 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM (%u) have unknown team in value1 (%u), ignore.",
218 criteria->ID, criteria->requiredType,dataType,team.team);
219 return false;
221 return true;
222 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK:
223 if(drunk.state >= MAX_DRUNKEN)
225 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK (%u) have unknown drunken state in value1 (%u), ignore.",
226 criteria->ID, criteria->requiredType,dataType,drunk.state);
227 return false;
229 return true;
230 case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY:
231 if (!sHolidaysStore.LookupEntry(holiday.id))
233 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY (%u) have unknown holiday in value1 (%u), ignore.",
234 criteria->ID, criteria->requiredType,dataType,holiday.id);
235 return false;
237 return true;
238 case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE:
239 return true; // not check correctness node indexes
240 default:
241 sLog.outErrorDb( "Table `achievement_criteria_data` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,dataType);
242 return false;
244 return false;
247 bool AchievementCriteriaData::Meets(Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const
249 switch(dataType)
251 case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
252 return true;
253 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
254 if (!target || target->GetTypeId()!=TYPEID_UNIT)
255 return false;
256 return target->GetEntry() == creature.id;
257 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
258 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
259 return false;
260 if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass())
261 return false;
262 if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace())
263 return false;
264 return true;
265 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH:
266 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
267 return false;
268 return target->GetHealth()*100 <= health.percent*target->GetMaxHealth();
269 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD:
270 if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || ((Player*)target)->GetDeathTimer() == 0)
271 return false;
272 // flag set == must be same team, not set == different team
273 return (((Player*)target)->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0);
274 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
275 return source->HasAura(aura.spell_id,aura.effect_idx);
276 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
278 uint32 zone_id,area_id;
279 source->GetZoneAndAreaId(zone_id,area_id);
280 return area.id==zone_id || area.id==area_id;
282 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
283 return target && target->HasAura(aura.spell_id,aura.effect_idx);
284 case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE:
285 return miscvalue1 >= value.minvalue;
286 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL:
287 if (!target)
288 return false;
289 return target->getLevel() >= level.minlevel;
290 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER:
291 if (!target)
292 return false;
293 return target->getGender() == gender.gender;
294 case ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED:
295 return false; // always fail
296 case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY:
297 return source->GetMap()->GetSpawnMode()==difficalty.difficalty;
298 case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT:
299 return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount;
300 case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM:
301 if (!target || target->GetTypeId() != TYPEID_PLAYER)
302 return false;
303 return ((Player*)target)->GetTeam() == team.team;
304 case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK:
305 return Player::GetDrunkenstateByValue(source->GetDrunkValue()) >= drunk.state;
306 case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY:
307 return IsHolidayActive(HolidayIds(holiday.id));
308 case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE:
310 BattleGround* bg = source->GetBattleGround();
311 if(!bg)
312 return false;
313 return bg->IsTeamScoreInRange(source->GetTeam()==ALLIANCE ? HORDE : ALLIANCE,bg_loss_team_score.min_score,bg_loss_team_score.max_score);
316 return false;
319 bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const
321 for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr)
322 if(!itr->Meets(source,target,miscvalue))
323 return false;
325 return true;
328 AchievementMgr::AchievementMgr(Player *player)
330 m_player = player;
333 AchievementMgr::~AchievementMgr()
337 void AchievementMgr::Reset()
339 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
341 WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
342 data << uint32(iter->first);
343 m_player->SendDirectMessage(&data);
346 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
348 WorldPacket data(SMSG_CRITERIA_DELETED,4);
349 data << uint32(iter->first);
350 m_player->SendDirectMessage(&data);
353 m_completedAchievements.clear();
354 m_criteriaProgress.clear();
355 DeleteFromDB(m_player->GetGUIDLow());
357 // re-fill data
358 CheckAllAchievementCriteria();
361 void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2)
363 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
364 sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2);
366 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
367 return;
369 AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
370 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
372 AchievementCriteriaEntry const *achievementCriteria = (*i);
374 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
375 if (!achievement)
376 continue;
378 // don't update already completed criteria
379 if (IsCompletedCriteria(achievementCriteria,achievement))
380 continue;
382 switch (type)
384 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset
385 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset
386 if (achievementCriteria->healing_done.flag == miscvalue1 &&
387 achievementCriteria->healing_done.mapid == miscvalue2)
388 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
389 break;
390 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset
391 // reset only the criteria having the miscvalue1 condition
392 if (achievementCriteria->win_rated_arena.flag == miscvalue1)
393 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
394 break;
395 default: // reset all cases
396 break;
401 void AchievementMgr::DeleteFromDB(uint32 lowguid)
403 CharacterDatabase.BeginTransaction ();
404 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid);
405 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
406 CharacterDatabase.CommitTransaction ();
409 void AchievementMgr::SaveToDB()
411 if(!m_completedAchievements.empty())
413 bool need_execute = false;
414 std::ostringstream ssdel;
415 std::ostringstream ssins;
416 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
418 if(!iter->second.changed)
419 continue;
421 /// first new/changed record prefix
422 if(!need_execute)
424 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
425 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
426 need_execute = true;
428 /// next new/changed record prefix
429 else
431 ssdel << ", ";
432 ssins << ", ";
435 // new/changed record data
436 ssdel << iter->first;
437 ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
439 /// mark as saved in db
440 iter->second.changed = false;
443 if(need_execute)
444 ssdel << ")";
446 if(need_execute)
448 CharacterDatabase.Execute( ssdel.str().c_str() );
449 CharacterDatabase.Execute( ssins.str().c_str() );
453 if(!m_criteriaProgress.empty())
455 /// prepare deleting and insert
456 bool need_execute_del = false;
457 bool need_execute_ins = false;
458 std::ostringstream ssdel;
459 std::ostringstream ssins;
460 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
462 if(!iter->second.changed)
463 continue;
465 // deleted data (including 0 progress state)
467 /// first new/changed record prefix (for any counter value)
468 if(!need_execute_del)
470 ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
471 need_execute_del = true;
473 /// next new/changed record prefix
474 else
475 ssdel << ", ";
477 // new/changed record data
478 ssdel << iter->first;
481 // store data only for real progress
482 if(iter->second.counter != 0)
484 /// first new/changed record prefix
485 if(!need_execute_ins)
487 ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
488 need_execute_ins = true;
490 /// next new/changed record prefix
491 else
492 ssins << ", ";
494 // new/changed record data
495 ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
498 /// mark as updated in db
499 iter->second.changed = false;
502 if(need_execute_del) // DELETE ... IN (.... _)_
503 ssdel << ")";
505 if(need_execute_del || need_execute_ins)
507 if(need_execute_del)
508 CharacterDatabase.Execute( ssdel.str().c_str() );
509 if(need_execute_ins)
510 CharacterDatabase.Execute( ssins.str().c_str() );
515 void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
517 if(achievementResult)
521 Field *fields = achievementResult->Fetch();
523 uint32 achievement_id = fields[0].GetUInt32();
525 // don't must happen: cleanup at server startup in achievementmgr.LoadCompletedAchievements()
526 if(!sAchievementStore.LookupEntry(achievement_id))
527 continue;
529 CompletedAchievementData& ca = m_completedAchievements[achievement_id];
530 ca.date = time_t(fields[1].GetUInt64());
531 ca.changed = false;
532 } while(achievementResult->NextRow());
533 delete achievementResult;
536 if(criteriaResult)
540 Field *fields = criteriaResult->Fetch();
542 uint32 id = fields[0].GetUInt32();
543 uint32 counter = fields[1].GetUInt32();
544 time_t date = time_t(fields[2].GetUInt64());
546 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
547 if (!criteria)
549 // we will remove not existed criteria for all characters
550 sLog.outError("Not existed achievement criteria %u data removed from table `character_achievement_progress`.",id);
551 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id);
552 continue;
555 if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))
556 continue;
558 CriteriaProgress& progress = m_criteriaProgress[id];
559 progress.counter = counter;
560 progress.date = date;
561 progress.changed = false;
562 } while(criteriaResult->NextRow());
563 delete criteriaResult;
568 void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
570 if(GetPlayer()->GetSession()->PlayerLoading())
571 return;
573 #ifdef MANGOS_DEBUG
574 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
575 sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
576 #endif
578 if(Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()))
580 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
581 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
582 guild->BroadcastWorker(say_do,GetPlayer());
585 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
587 // broadcast realm first reached
588 WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4);
589 data << GetPlayer()->GetName();
590 data << uint64(GetPlayer()->GetGUID());
591 data << uint32(achievement->ID);
592 data << uint32(0); // 1=link supplied string as player name, 0=display plain string
593 sWorld.SendGlobalMessage(&data);
595 else
597 CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
599 Cell cell(p);
600 cell.data.Part.reserved = ALL_DISTRICT;
601 cell.SetNoCreate();
603 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
604 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
605 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do);
606 TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> >, WorldTypeMapContainer > message(say_worker);
607 CellLock<GridReadGuard> cell_lock(cell, p);
608 cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap());
611 WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
612 data.append(GetPlayer()->GetPackGUID());
613 data << uint32(achievement->ID);
614 data << uint32(secsToTimeBitFields(time(NULL)));
615 data << uint32(0);
616 GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true);
619 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
621 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
622 data << uint32(id);
624 // the counter is packed like a packed Guid
625 data.appendPackGUID(progress->counter);
627 data.append(GetPlayer()->GetPackGUID());
628 data << uint32(0);
629 data << uint32(secsToTimeBitFields(progress->date));
630 data << uint32(0); // timer 1
631 data << uint32(0); // timer 2
632 GetPlayer()->SendDirectMessage(&data);
636 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
638 void AchievementMgr::CheckAllAchievementCriteria()
640 // suppress sending packets
641 for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
642 UpdateAchievementCriteria(AchievementCriteriaTypes(i));
645 static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 };
646 static const uint32 achievIdForDangeon[][4] =
648 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
649 { 321, true, true, true },
650 { 916, false, true, false },
651 { 917, false, true, false },
652 { 918, true, false, false },
653 { 2219, false, false, true },
654 { 0, false, false, false }
658 * this function will be called whenever the user might have done a criteria relevant action
660 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time)
662 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
663 sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
665 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
666 return;
668 AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
669 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
671 AchievementCriteriaEntry const *achievementCriteria = (*i);
673 if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
674 continue;
676 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
677 if (!achievement)
678 continue;
680 if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
681 (achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
682 continue;
684 // don't update already completed criteria
685 if (IsCompletedCriteria(achievementCriteria,achievement))
686 continue;
688 switch (type)
690 // std. case: increment at 1
691 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
692 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
693 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
694 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
695 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
696 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
697 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
698 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
699 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
700 if(!miscvalue1)
701 continue;
702 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
703 break;
704 // std case: increment at miscvalue1
705 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
706 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
707 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING:
708 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
709 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
710 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
711 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED:
712 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED:
713 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
714 if(!miscvalue1)
715 continue;
716 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
717 break;
718 // std case: high value at miscvalue1
719 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
720 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */
721 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT:
722 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED:
723 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED:
724 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED:
725 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
726 if(!miscvalue1)
727 continue;
728 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
729 break;
731 // specialized cases
733 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
735 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
736 if (!miscvalue1)
737 continue;
738 if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId())
739 continue;
741 if (achievementCriteria->win_bg.additionalRequirement1_type)
743 // those requirements couldn't be found in the dbc
744 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
745 if (!data || !data->Meets(GetPlayer(),unit))
746 continue;
748 // some hardcoded requirements
749 else
751 BattleGround* bg = GetPlayer()->GetBattleGround();
752 if (!bg)
753 continue;
755 switch(achievementCriteria->referredAchievement)
757 case 161: // AB, Overcome a 500 resource disadvantage
759 if (bg->GetTypeID() != BATTLEGROUND_AB)
760 continue;
761 if(!((BattleGroundAB*)bg)->IsTeamScores500Disadvantage(GetPlayer()->GetTeam()))
762 continue;
763 break;
765 case 156: // AB, win while controlling all 5 flags (all nodes)
766 case 784: // EY, win while holding 4 bases (all nodes)
768 if(!bg->IsAllNodesConrolledByTeam(GetPlayer()->GetTeam()))
769 continue;
770 break;
772 case 1762: // SA, win without losing any siege vehicles
773 case 2192: // SA, win without losing any siege vehicles
774 continue; // not implemented
778 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
779 break;
781 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
783 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
784 if(!miscvalue1)
785 continue;
786 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
787 continue;
789 // those requirements couldn't be found in the dbc
790 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
791 if(!data || !data->Meets(GetPlayer(),unit))
792 continue;
794 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
795 break;
797 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
798 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
799 break;
800 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
801 // update at loading or specific skill update
802 if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID)
803 continue;
804 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
805 SetCriteriaProgress(achievementCriteria, skillvalue);
806 break;
807 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
808 // update at loading or specific skill update
809 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID)
810 continue;
811 if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID))
812 SetCriteriaProgress(achievementCriteria, maxSkillvalue);
813 break;
814 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
815 if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
816 SetCriteriaProgress(achievementCriteria, 1);
817 break;
818 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
820 uint32 counter =0;
821 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
822 if(itr->second.m_rewarded)
823 counter++;
824 SetCriteriaProgress(achievementCriteria, counter);
825 break;
827 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
829 // speedup for non-login case
830 if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID)
831 continue;
833 uint32 counter =0;
834 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
836 Quest const* quest = objmgr.GetQuestTemplate(itr->first);
837 if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
838 counter++;
840 SetCriteriaProgress(achievementCriteria, counter);
841 break;
843 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
844 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
845 if(!miscvalue1)
846 continue;
847 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
848 continue;
849 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
850 break;
851 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
852 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
853 if(!miscvalue1)
854 continue;
855 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
856 continue;
857 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
858 break;
859 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
861 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
862 if(!miscvalue1)
863 continue;
864 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
865 bool notfit = false;
866 for(int j = 0; j < MAX_ARENA_SLOT; ++j)
868 if(achievIdByArenaSlot[j] == achievement->ID)
870 BattleGround* bg = GetPlayer()->GetBattleGround();
871 if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
872 notfit = true;
874 break;
877 if(notfit)
878 continue;
880 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
881 break;
883 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
885 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
886 if(!miscvalue1)
887 continue;
889 Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : MapManager::Instance().FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId());
890 if(!map || !map->IsDungeon())
891 continue;
893 // search case
894 bool found = false;
895 for(int j = 0; achievIdForDangeon[j][0]; ++j)
897 if(achievIdForDangeon[j][0] == achievement->ID)
899 if(map->IsRaid())
901 // if raid accepted (ignore difficulty)
902 if(!achievIdForDangeon[j][2])
903 break; // for
905 else if(GetPlayer()->GetDifficulty()==DIFFICULTY_NORMAL)
907 // dungeon in normal mode accepted
908 if(!achievIdForDangeon[j][1])
909 break; // for
911 else
913 // dungeon in heroic mode accepted
914 if(!achievIdForDangeon[j][3])
915 break; // for
918 found = true;
919 break; // for
922 if(!found)
923 continue;
925 //FIXME: work only for instances where max==min for players
926 if(((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
927 continue;
928 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
929 break;
932 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
933 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
934 if(!miscvalue1)
935 continue;
936 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
937 continue;
938 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
939 break;
940 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
941 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
942 if(!miscvalue1)
943 continue;
945 // if team check required: must kill by opposition faction
946 if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
947 continue;
949 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
950 break;
951 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
953 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
954 if(!miscvalue1)
955 continue;
957 // those requirements couldn't be found in the dbc
958 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
959 if(!data || !data->Meets(GetPlayer(),unit))
960 continue;
962 // miscvalue1 is the ingame fallheight*100 as stored in dbc
963 SetCriteriaProgress(achievementCriteria, miscvalue1);
964 break;
966 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
967 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
968 if(!miscvalue1)
969 continue;
970 if(miscvalue2 != achievementCriteria->death_from.type)
971 continue;
972 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
973 break;
974 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
976 // if miscvalues != 0, it contains the questID.
977 if (miscvalue1)
979 if (miscvalue1 != achievementCriteria->complete_quest.questID)
980 continue;
982 else
984 // login case.
985 if(!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
986 continue;
989 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
990 switch(achievement->ID)
992 case 31:
993 case 1275:
994 case 1276:
995 case 1277:
996 case 1282:
997 case 1789:
999 // those requirements couldn't be found in the dbc
1000 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1001 if(!data || !data->Meets(GetPlayer(),unit))
1002 continue;
1003 break;
1005 default:
1006 break;
1010 SetCriteriaProgress(achievementCriteria, 1);
1011 break;
1013 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1014 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1015 if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID)
1016 continue;
1017 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1018 break;
1019 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1020 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1022 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
1023 continue;
1025 // those requirements couldn't be found in the dbc
1026 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1027 if(!data)
1028 continue;
1030 if(!data->Meets(GetPlayer(),unit))
1031 continue;
1033 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1034 break;
1036 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1037 if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
1038 continue;
1040 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
1041 SetCriteriaProgress(achievementCriteria, 1);
1042 break;
1043 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1045 // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored)
1046 // miscvalue2=count of item loot
1047 if (!miscvalue1 || !miscvalue2)
1048 continue;
1049 if (miscvalue1 != achievementCriteria->loot_type.lootType)
1050 continue;
1052 // zone specific
1053 if(achievementCriteria->loot_type.lootTypeCount==1)
1055 // those requirements couldn't be found in the dbc
1056 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1057 if(!data || !data->Meets(GetPlayer(),unit))
1058 continue;
1061 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1062 break;
1064 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1065 // speedup for non-login case
1066 if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1)
1067 continue;
1068 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
1069 break;
1070 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1071 // miscvalue1 contains the personal rating
1072 if (!miscvalue1) // no update at login
1073 continue;
1075 // additional requirements
1076 if(achievementCriteria->win_rated_arena.flag==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1078 // those requirements couldn't be found in the dbc
1079 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1080 if(!data || !data->Meets(GetPlayer(),unit,miscvalue1))
1082 // reset the progress as we have a win without the requirement.
1083 SetCriteriaProgress(achievementCriteria, 0);
1084 continue;
1088 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1089 break;
1090 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1091 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1092 if(!miscvalue1)
1093 continue;
1094 if(achievementCriteria->use_item.itemID != miscvalue1)
1095 continue;
1096 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1097 break;
1098 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1099 // You _have_ to loot that item, just owning it when logging in does _not_ count!
1100 if(!miscvalue1)
1101 continue;
1102 if(miscvalue1 != achievementCriteria->own_item.itemID)
1103 continue;
1104 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1105 break;
1106 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1108 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
1109 if(!worldOverlayEntry)
1110 break;
1112 bool matchFound = false;
1113 for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
1115 uint32 area_id = worldOverlayEntry->areatableID[j];
1116 if(!area_id) // array have 0 only in empty tail
1117 break;
1119 int32 exploreFlag = GetAreaFlagByAreaID(area_id);
1120 if(exploreFlag < 0)
1121 continue;
1123 uint32 playerIndexOffset = uint32(exploreFlag) / 32;
1124 uint32 mask = 1<< (uint32(exploreFlag) % 32);
1126 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
1128 matchFound = true;
1129 break;
1133 if(matchFound)
1134 SetCriteriaProgress(achievementCriteria, 1);
1135 break;
1137 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1138 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount());
1139 break;
1140 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1142 // skip faction check only at loading
1143 if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID)
1144 continue;
1146 int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
1147 if (reputation > 0)
1148 SetCriteriaProgress(achievementCriteria, reputation);
1149 break;
1151 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1153 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
1154 break;
1156 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1158 // skip for login case
1159 if(!miscvalue1)
1160 continue;
1161 SetCriteriaProgress(achievementCriteria, 1);
1162 break;
1164 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1165 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1167 // miscvalue1 = itemid
1168 // miscvalue2 = diced value
1169 if(!miscvalue1)
1170 continue;
1171 if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
1172 continue;
1173 ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 );
1175 uint32 requiredItemLevel = 0;
1176 if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
1177 requiredItemLevel = 185;
1179 if(!pProto || pProto->ItemLevel <requiredItemLevel)
1180 continue;
1181 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1182 break;
1184 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1186 // miscvalue1 = emote
1187 if(!miscvalue1)
1188 continue;
1189 if(miscvalue1 != achievementCriteria->do_emote.emoteID)
1190 continue;
1191 if(achievementCriteria->do_emote.count)
1193 // those requirements couldn't be found in the dbc
1194 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1195 if(!data || !data->Meets(GetPlayer(),unit))
1196 continue;
1199 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1200 break;
1202 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1203 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1205 if (!miscvalue1)
1206 continue;
1208 if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP)
1210 if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid)
1211 continue;
1213 // map specific case (BG in fact) expected player targeted damage/heal
1214 if(!unit || unit->GetTypeId()!=TYPEID_PLAYER)
1215 continue;
1218 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
1219 break;
1221 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1222 // miscvalue1 = item_id
1223 if (!miscvalue1)
1224 continue;
1225 if (miscvalue1 != achievementCriteria->equip_item.itemID)
1226 continue;
1228 SetCriteriaProgress(achievementCriteria, 1);
1229 break;
1230 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1231 // miscvalue1 = go entry
1232 if (!miscvalue1)
1233 continue;
1234 if (miscvalue1 != achievementCriteria->use_gameobject.goEntry)
1235 continue;
1237 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1238 break;
1239 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1240 if (!miscvalue1)
1241 continue;
1242 if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
1243 continue;
1245 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1246 break;
1247 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1249 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine)
1250 continue;
1252 uint32 spellCount = 0;
1253 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1254 spellIter != GetPlayer()->GetSpellMap().end();
1255 ++spellIter)
1257 SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellIter->first);
1258 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1260 if(skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine)
1261 spellCount++;
1264 SetCriteriaProgress(achievementCriteria, spellCount);
1265 break;
1267 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1268 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1269 if (!miscvalue1)
1270 continue;
1272 if (achievementCriteria->win_duel.duelCount)
1274 // those requirements couldn't be found in the dbc
1275 AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
1276 if (!data)
1277 continue;
1279 if (!data->Meets(GetPlayer(),unit))
1280 continue;
1283 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1284 break;
1285 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1286 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1287 break;
1288 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1289 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1290 break;
1291 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1292 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
1293 break;
1294 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1296 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine)
1297 continue;
1299 uint32 spellCount = 0;
1300 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1301 spellIter != GetPlayer()->GetSpellMap().end();
1302 ++spellIter)
1304 SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellIter->first);
1305 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1306 if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine)
1307 spellCount++;
1309 SetCriteriaProgress(achievementCriteria, spellCount);
1310 break;
1312 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1313 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
1314 break;
1315 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1316 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
1317 continue;
1319 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1320 break;
1321 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1322 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
1323 continue;
1325 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1326 break;
1327 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1328 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST);
1329 break;
1330 // std case: not exist in DBC, not triggered in code as result
1331 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1332 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1333 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1334 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER:
1335 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT:
1336 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING:
1337 break;
1338 // FIXME: not triggered in code as result, need to implement
1339 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY:
1340 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
1341 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE:
1342 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
1343 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
1344 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
1345 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL:
1346 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
1347 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING:
1348 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
1349 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1350 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
1351 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
1352 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1353 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
1354 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE:
1355 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:
1356 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
1357 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS:
1358 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1359 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1360 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL:
1361 break; // Not implemented yet :(
1363 if(IsCompletedCriteria(achievementCriteria,achievement))
1364 CompletedCriteriaFor(achievement);
1366 // check again the completeness for SUMM and REQ COUNT achievements,
1367 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
1368 if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
1370 if (IsCompletedAchievement(achievement))
1371 CompletedAchievement(achievement);
1374 if(AchievementEntryList const* achRefList = achievementmgr.GetAchievementByReferencedId(achievement->ID))
1376 for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
1377 if(IsCompletedAchievement(*itr))
1378 CompletedAchievement(*itr);
1383 static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
1384 static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
1386 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement)
1388 // counter can never complete
1389 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1390 return false;
1392 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1394 // someone on this realm has already completed that achievement
1395 if(achievementmgr.IsRealmCompleted(achievement))
1396 return false;
1399 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
1400 if(itr == m_criteriaProgress.end())
1401 return false;
1403 CriteriaProgress const* progress = &itr->second;
1405 switch(achievementCriteria->requiredType)
1407 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1408 return progress->counter >= achievementCriteria->win_bg.winCount;
1409 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1410 return progress->counter >= achievementCriteria->kill_creature.creatureCount;
1411 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
1413 // skip wrong class achievements
1414 for(int i = 1; i < MAX_CLASSES; ++i)
1415 if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
1416 return false;
1418 // skip wrong race achievements
1419 for(int i = 1; i < MAX_RACES; ++i)
1420 if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
1421 return false;
1423 // appropriate class/race or not class/race specific
1424 return progress->counter >= achievementCriteria->reach_level.level;
1426 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
1427 return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
1428 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
1429 return progress->counter >= 1;
1430 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
1431 return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
1432 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
1433 return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
1434 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1435 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1436 return progress->counter >= achievementCriteria->healing_done.count;
1437 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
1438 return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
1439 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1440 return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
1441 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1442 return progress->counter >= 1;
1443 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1444 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1445 return progress->counter >= achievementCriteria->be_spell_target.spellCount;
1446 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1447 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1448 return progress->counter >= achievementCriteria->cast_spell.castCount;
1449 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1450 return progress->counter >= 1;
1451 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1452 return progress->counter >= achievementCriteria->own_item.itemCount;
1453 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1454 return progress->counter >= achievementCriteria->win_rated_arena.count;
1455 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
1456 return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75);
1457 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1458 return progress->counter >= achievementCriteria->use_item.itemCount;
1459 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1460 return progress->counter >= achievementCriteria->loot_item.itemCount;
1461 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1462 return progress->counter >= 1;
1463 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1464 return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
1465 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1466 return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
1467 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1468 return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
1469 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1470 return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
1471 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1472 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1473 return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
1474 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1475 return progress->counter >= achievementCriteria->hk_class.count;
1476 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1477 return progress->counter >= achievementCriteria->hk_race.count;
1478 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1479 return progress->counter >= achievementCriteria->do_emote.count;
1480 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1481 return progress->counter >= achievementCriteria->equip_item.count;
1482 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
1483 return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper;
1484 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
1485 return progress->counter >= achievementCriteria->loot_money.goldInCopper;
1486 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1487 return progress->counter >= achievementCriteria->use_gameobject.useCount;
1488 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1489 return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount;
1490 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1491 return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount;
1492 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1493 return progress->counter >= achievementCriteria->win_duel.duelCount;
1494 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1495 return progress->counter >= achievementCriteria->loot_type.lootTypeCount;
1496 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1497 return progress->counter >= achievementCriteria->learn_skill_line.spellCount;
1498 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1499 return progress->counter >= achievementCriteria->honorable_kill.killCount;
1501 // handle all statistic-only criteria here
1502 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
1503 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
1504 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
1505 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
1506 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
1507 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
1508 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1509 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
1510 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
1511 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
1512 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
1513 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
1514 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
1515 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD:
1516 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1517 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1518 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1519 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1520 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
1521 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
1522 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1523 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1524 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1525 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
1526 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
1527 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
1528 return false;
1530 return false;
1533 void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
1535 // counter can never complete
1536 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1537 return;
1539 // already completed and stored
1540 if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1541 return;
1543 if (IsCompletedAchievement(achievement))
1544 CompletedAchievement(achievement);
1547 bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
1549 // counter can never complete
1550 if(entry->flags & ACHIEVEMENT_FLAG_COUNTER)
1551 return false;
1553 // for achievement with referenced achievement criterias get from referenced and counter from self
1554 uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID;
1555 uint32 achievmentForTestCount = entry->count;
1557 AchievementCriteriaEntryList const* cList = achievementmgr.GetAchievementCriteriaByAchievement(achievmentForTestId);
1558 if(!cList)
1559 return false;
1560 uint32 count = 0;
1562 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
1563 // Oddly, the target count is NOT countained in the achievement, but in each individual criteria
1564 if (entry->flags & ACHIEVEMENT_FLAG_SUMM)
1566 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1568 AchievementCriteriaEntry const* criteria = *itr;
1570 CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID);
1571 if(itrProgress == m_criteriaProgress.end())
1572 continue;
1574 CriteriaProgress const* progress = &itrProgress->second;
1575 count += progress->counter;
1577 // for counters, field4 contains the main count requirement
1578 if (count >= criteria->raw.count)
1579 return true;
1581 return false;
1584 // Default case - need complete all or
1585 bool completed_all = true;
1586 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1588 AchievementCriteriaEntry const* criteria = *itr;
1590 bool completed = IsCompletedCriteria(criteria,entry);
1592 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1593 if(completed)
1594 ++count;
1595 else
1596 completed_all = false;
1598 // completed as have req. count of completed criterias
1599 if(achievmentForTestCount > 0 && achievmentForTestCount <= count)
1600 return true;
1603 // all criterias completed requirement
1604 if(completed_all && achievmentForTestCount==0)
1605 return true;
1607 return false;
1610 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
1612 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
1613 sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
1615 CriteriaProgress *progress = NULL;
1617 CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
1619 if(iter == m_criteriaProgress.end())
1621 // not create record for 0 counter
1622 if(changeValue == 0)
1623 return;
1625 progress = &m_criteriaProgress[entry->ID];
1626 progress->counter = changeValue;
1627 progress->date = time(NULL);
1629 else
1631 progress = &iter->second;
1633 uint32 newValue = 0;
1634 switch(ptype)
1636 case PROGRESS_SET:
1637 newValue = changeValue;
1638 break;
1639 case PROGRESS_ACCUMULATE:
1641 // avoid overflow
1642 uint32 max_value = std::numeric_limits<uint32>::max();
1643 newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value;
1644 break;
1646 case PROGRESS_HIGHEST:
1647 newValue = progress->counter < changeValue ? changeValue : progress->counter;
1648 break;
1651 // not update (not mark as changed) if counter will have same value
1652 if(progress->counter == newValue)
1653 return;
1655 progress->counter = newValue;
1658 progress->changed = true;
1660 if(entry->timeLimit)
1662 time_t now = time(NULL);
1663 if(time_t(progress->date + entry->timeLimit) < now)
1664 progress->counter = 1;
1666 // also it seems illogical, the timeframe will be extended at every criteria update
1667 progress->date = now;
1669 SendCriteriaUpdate(entry->ID,progress);
1672 void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
1674 sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
1675 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1676 return;
1678 SendAchievementEarned(achievement);
1679 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
1680 ca.date = time(NULL);
1681 ca.changed = true;
1683 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1684 // TODO: where do set this instead?
1685 if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1686 achievementmgr.SetRealmCompleted(achievement);
1688 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
1690 // reward items and titles if any
1691 AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement);
1693 // no rewards
1694 if(!reward)
1695 return;
1697 // titles
1698 if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
1700 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
1701 GetPlayer()->SetTitle(titleEntry);
1704 // mail
1705 if(reward->sender)
1707 Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
1709 MailItemsInfo mi;
1710 if(item)
1712 // save new item before send
1713 item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1715 // item
1716 mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
1719 int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1721 // subject and text
1722 std::string subject = reward->subject;
1723 std::string text = reward->text;
1724 if ( loc_idx >= 0 )
1726 if(AchievementRewardLocale const* loc = achievementmgr.GetAchievementRewardLocale(achievement))
1728 if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty())
1729 subject = loc->subject[loc_idx];
1730 if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty())
1731 text = loc->text[loc_idx];
1735 uint32 itemTextId = objmgr.CreateItemText( text );
1737 WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE, MAIL_STATIONERY_NORMAL, reward->sender, GetPlayer()->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
1741 void AchievementMgr::SendAllAchievementData()
1743 // since we don't know the exact size of the packed GUIDs this is just an approximation
1744 WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1745 BuildAllDataPacket(&data);
1746 GetPlayer()->GetSession()->SendPacket(&data);
1749 void AchievementMgr::SendRespondInspectAchievements(Player* player)
1751 // since we don't know the exact size of the packed GUIDs this is just an approximation
1752 WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1753 data.append(GetPlayer()->GetPackGUID());
1754 BuildAllDataPacket(&data);
1755 player->GetSession()->SendPacket(&data);
1759 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1761 void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
1763 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
1765 *data << uint32(iter->first);
1766 *data << uint32(secsToTimeBitFields(iter->second.date));
1768 *data << int32(-1);
1770 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
1772 *data << uint32(iter->first);
1773 data->appendPackGUID(iter->second.counter);
1774 data->append(GetPlayer()->GetPackGUID());
1775 *data << uint32(0);
1776 *data << uint32(secsToTimeBitFields(iter->second.date));
1777 *data << uint32(0);
1778 *data << uint32(0);
1781 *data << int32(-1);
1784 //==========================================================
1785 AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
1787 return m_AchievementCriteriasByType[type];
1790 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1792 if(sAchievementCriteriaStore.GetNumRows()==0)
1794 barGoLink bar(1);
1795 bar.step();
1797 sLog.outString();
1798 sLog.outErrorDb(">> Loaded 0 achievement criteria.");
1799 return;
1802 barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
1803 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1805 bar.step();
1807 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1808 if(!criteria)
1809 continue;
1811 m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
1812 m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
1815 sLog.outString();
1816 sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size());
1819 void AchievementGlobalMgr::LoadAchievementReferenceList()
1821 if(sAchievementStore.GetNumRows()==0)
1823 barGoLink bar(1);
1824 bar.step();
1826 sLog.outString();
1827 sLog.outErrorDb(">> Loaded 0 achievement references.");
1828 return;
1831 uint32 count = 0;
1832 barGoLink bar( sAchievementStore.GetNumRows() );
1833 for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
1835 bar.step();
1837 AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
1838 if(!achievement || !achievement->refAchievement)
1839 continue;
1841 m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement);
1842 ++count;
1845 sLog.outString();
1846 sLog.outString(">> Loaded %u achievement references.",count);
1849 void AchievementGlobalMgr::LoadAchievementCriteriaData()
1851 m_criteriaDataMap.clear(); // need for reload case
1853 QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data");
1855 if(!result)
1857 barGoLink bar(1);
1858 bar.step();
1860 sLog.outString();
1861 sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty.");
1862 return;
1865 uint32 count = 0;
1866 uint32 disabled_count = 0;
1867 barGoLink bar(result->GetRowCount());
1870 bar.step();
1871 Field *fields = result->Fetch();
1872 uint32 criteria_id = fields[0].GetUInt32();
1874 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
1876 if (!criteria)
1878 sLog.outErrorDb( "Table `achievement_criteria_data` have data for not existed criteria (Entry: %u), ignore.", criteria_id);
1879 continue;
1882 AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
1884 if (!data.IsValid(criteria))
1886 continue;
1889 // this will allocate empty data set storage
1890 AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id];
1892 if (data.dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED)
1893 ++disabled_count;
1895 // add real data only for not NONE data types
1896 if (data.dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE)
1897 dataSet.Add(data);
1899 // counting data by and data types
1900 ++count;
1901 } while(result->NextRow());
1903 delete result;
1905 // post loading checks
1906 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1908 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1909 if(!criteria)
1910 continue;
1912 switch(criteria->requiredType)
1914 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1915 if(!criteria->win_bg.additionalRequirement1_type)
1916 continue;
1917 break;
1918 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1919 break; // any cases
1920 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1922 AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement);
1923 if(!achievement)
1924 continue;
1926 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
1927 switch(achievement->ID)
1929 case 31:
1930 case 1275:
1931 case 1276:
1932 case 1277:
1933 case 1282:
1934 case 1789:
1935 break;
1936 default:
1937 continue;
1940 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1941 break; // any cases
1942 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: // any cases
1943 break;
1944 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases
1945 if(criteria->win_rated_arena.flag!=ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1946 continue;
1947 break;
1948 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases
1949 if(criteria->do_emote.count==0)
1950 continue;
1951 break;
1952 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // skip statistics
1953 if(criteria->win_duel.duelCount==0)
1954 continue;
1955 break;
1956 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases
1957 break;
1958 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases
1959 if(criteria->loot_type.lootTypeCount!=1)
1960 continue;
1961 break;
1962 default: // type not use DB data, ignore
1963 continue;
1966 if(!GetCriteriaDataSet(criteria))
1967 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);
1970 sLog.outString();
1971 sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count);
1974 void AchievementGlobalMgr::LoadCompletedAchievements()
1976 QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
1978 if(!result)
1980 barGoLink bar(1);
1981 bar.step();
1983 sLog.outString();
1984 sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
1985 return;
1988 barGoLink bar(result->GetRowCount());
1991 bar.step();
1992 Field *fields = result->Fetch();
1994 uint32 achievement_id = fields[0].GetUInt32();
1995 if(!sAchievementStore.LookupEntry(achievement_id))
1997 // we will remove not existed achievement for all characters
1998 sLog.outError("Not existed achievement %u data removed from table `character_achievement`.",achievement_id);
1999 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id);
2000 continue;
2003 m_allCompletedAchievements.insert(achievement_id);
2004 } while(result->NextRow());
2006 delete result;
2008 sLog.outString();
2009 sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size());
2012 void AchievementGlobalMgr::LoadRewards()
2014 m_achievementRewards.clear(); // need for reload case
2016 // 0 1 2 3 4 5 6
2017 QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
2019 if(!result)
2021 barGoLink bar(1);
2023 bar.step();
2025 sLog.outString();
2026 sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
2027 return;
2030 uint32 count = 0;
2031 barGoLink bar(result->GetRowCount());
2035 bar.step();
2037 Field *fields = result->Fetch();
2038 uint32 entry = fields[0].GetUInt32();
2039 if (!sAchievementStore.LookupEntry(entry))
2041 sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
2042 continue;
2045 AchievementReward reward;
2046 reward.titleId[0] = fields[1].GetUInt32();
2047 reward.titleId[1] = fields[2].GetUInt32();
2048 reward.itemId = fields[3].GetUInt32();
2049 reward.sender = fields[4].GetUInt32();
2050 reward.subject = fields[5].GetCppString();
2051 reward.text = fields[6].GetCppString();
2053 if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
2054 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]);
2056 // must be title or mail at least
2057 if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
2059 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
2060 continue;
2063 if (reward.titleId[0])
2065 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
2066 if (!titleEntry)
2068 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
2069 reward.titleId[0] = 0;
2073 if (reward.titleId[1])
2075 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
2076 if (!titleEntry)
2078 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]);
2079 reward.titleId[1] = 0;
2083 //check mail data before item for report including wrong item case
2084 if (reward.sender)
2086 if (!objmgr.GetCreatureTemplate(reward.sender))
2088 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
2089 reward.sender = 0;
2092 else
2094 if (reward.itemId)
2095 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
2097 if (!reward.subject.empty())
2098 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
2100 if (!reward.text.empty())
2101 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
2104 if (reward.itemId)
2106 if (!objmgr.GetItemPrototype(reward.itemId))
2108 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
2109 reward.itemId = 0;
2113 m_achievementRewards[entry] = reward;
2114 ++count;
2116 } while (result->NextRow());
2118 delete result;
2120 sLog.outString();
2121 sLog.outString( ">> Loaded %u achievement rewards", count );
2124 void AchievementGlobalMgr::LoadRewardLocales()
2126 m_achievementRewardLocales.clear(); // need for reload case
2128 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");
2130 if(!result)
2132 barGoLink bar(1);
2134 bar.step();
2136 sLog.outString();
2137 sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
2138 return;
2141 barGoLink bar(result->GetRowCount());
2145 Field *fields = result->Fetch();
2146 bar.step();
2148 uint32 entry = fields[0].GetUInt32();
2150 if(m_achievementRewards.find(entry)==m_achievementRewards.end())
2152 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
2153 continue;
2156 AchievementRewardLocale& data = m_achievementRewardLocales[entry];
2158 for(int i = 1; i < MAX_LOCALE; ++i)
2160 std::string str = fields[1+2*(i-1)].GetCppString();
2161 if(!str.empty())
2163 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
2164 if(idx >= 0)
2166 if(data.subject.size() <= size_t(idx))
2167 data.subject.resize(idx+1);
2169 data.subject[idx] = str;
2172 str = fields[1+2*(i-1)+1].GetCppString();
2173 if(!str.empty())
2175 int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i));
2176 if(idx >= 0)
2178 if(data.text.size() <= size_t(idx))
2179 data.text.resize(idx+1);
2181 data.text[idx] = str;
2185 } while (result->NextRow());
2187 delete result;
2189 sLog.outString();
2190 sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() );