[9119] Add achievement creteria requirements for ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_T...
[getmangos.git] / src / game / AchievementMgr.cpp
blobaa8c999eb6ebfdb2dfcea3558ebdc732b0d919c4
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"
38 #include "Map.h"
39 #include "InstanceData.h"
41 #include "Policies/SingletonImp.h"
43 INSTANTIATE_SINGLETON_1(AchievementGlobalMgr);
45 namespace MaNGOS
47 class AchievementChatBuilder
49 public:
50 AchievementChatBuilder(Player const& pl, ChatMsg msgtype, int32 textId, uint32 ach_id)
51 : i_player(pl), i_msgtype(msgtype), i_textId(textId), i_achievementId(ach_id) {}
52 void operator()(WorldPacket& data, int32 loc_idx)
54 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
56 data << uint8(i_msgtype);
57 data << uint32(LANG_UNIVERSAL);
58 data << uint64(i_player.GetGUID());
59 data << uint32(5);
60 data << uint64(i_player.GetGUID());
61 data << uint32(strlen(text)+1);
62 data << text;
63 data << uint8(0);
64 data << uint32(i_achievementId);
67 private:
68 Player const& i_player;
69 ChatMsg i_msgtype;
70 int32 i_textId;
71 uint32 i_achievementId;
73 } // namespace MaNGOS
76 bool AchievementCriteriaRequirement::IsValid(AchievementCriteriaEntry const* criteria)
78 if(requirementType >= MAX_ACHIEVEMENT_CRITERIA_REQUIREMENT_TYPE)
80 sLog.outErrorDb( "Table `achievement_criteria_requirement` for criteria (Entry: %u) have wrong requirement type (%u), ignore.", criteria->ID,requirementType);
81 return false;
84 switch(criteria->requiredType)
86 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
87 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
88 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
89 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: // only hardcoded list
90 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
91 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
92 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
93 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
94 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
95 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
96 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
97 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
98 break;
99 default:
100 sLog.outErrorDb( "Table `achievement_criteria_requirement` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
101 return false;
104 switch(requirementType)
106 case ACHIEVEMENT_CRITERIA_REQUIRE_NONE:
107 case ACHIEVEMENT_CRITERIA_REQUIRE_VALUE:
108 case ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED:
109 case ACHIEVEMENT_CRITERIA_REQUIRE_BG_LOSS_TEAM_SCORE:
110 case ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT:
111 return true;
112 case ACHIEVEMENT_CRITERIA_REQUIRE_T_CREATURE:
113 if (!creature.id || !ObjectMgr::GetCreatureTemplate(creature.id))
115 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_CREATURE (%u) have not existed creature id in value1 (%u), ignore.",
116 criteria->ID, criteria->requiredType,requirementType,creature.id);
117 return false;
119 return true;
120 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_CLASS_RACE:
121 if (!classRace.class_id && !classRace.race_id)
123 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_PLAYER_CLASS_RACE (%u) must have not 0 in one from value fields, ignore.",
124 criteria->ID, criteria->requiredType,requirementType);
125 return false;
127 if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0)
129 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_CREATURE (%u) have not existed class in value1 (%u), ignore.",
130 criteria->ID, criteria->requiredType,requirementType,classRace.class_id);
131 return false;
133 if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0)
135 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_CREATURE (%u) have not existed race in value2 (%u), ignore.",
136 criteria->ID, criteria->requiredType,requirementType,classRace.race_id);
137 return false;
139 return true;
140 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_LESS_HEALTH:
141 if (health.percent < 1 || health.percent > 100)
143 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_PLAYER_LESS_HEALTH (%u) have wrong percent value in value1 (%u), ignore.",
144 criteria->ID, criteria->requiredType,requirementType,health.percent);
145 return false;
147 return true;
148 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD:
149 if (player_dead.own_team_flag > 1)
151 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD (%u) have wrong boolean value1 (%u).",
152 criteria->ID, criteria->requiredType,requirementType,player_dead.own_team_flag);
153 return false;
155 return true;
156 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA:
157 case ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA:
159 SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id);
160 if (!spellEntry)
162 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have wrong spell id in value1 (%u), ignore.",
163 criteria->ID, criteria->requiredType,(requirementType==ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA?"ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA":"ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA"),requirementType,aura.spell_id);
164 return false;
166 if (aura.effect_idx >= 3)
168 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have wrong spell effect index in value2 (%u), ignore.",
169 criteria->ID, criteria->requiredType,(requirementType==ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA?"ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA":"ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA"),requirementType,aura.effect_idx);
170 return false;
172 if (!spellEntry->EffectApplyAuraName[aura.effect_idx])
174 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have non-aura spell effect (ID: %u Effect: %u), ignore.",
175 criteria->ID, criteria->requiredType,(requirementType==ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA?"ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA":"ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA"),requirementType,aura.spell_id,aura.effect_idx);
176 return false;
178 return true;
180 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AREA:
181 if (!GetAreaEntryByAreaID(area.id))
183 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_S_AREA (%u) have wrong area id in value1 (%u), ignore.",
184 criteria->ID, criteria->requiredType,requirementType,area.id);
185 return false;
187 return true;
188 case ACHIEVEMENT_CRITERIA_REQUIRE_T_LEVEL:
189 if (level.minlevel < 0 || level.minlevel > STRONG_MAX_LEVEL)
191 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_T_LEVEL (%u) have wrong minlevel in value1 (%u), ignore.",
192 criteria->ID, criteria->requiredType,requirementType,level.minlevel);
193 return false;
195 return true;
196 case ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER:
197 if (gender.gender > GENDER_NONE)
199 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER (%u) have wrong gender in value1 (%u), ignore.",
200 criteria->ID, criteria->requiredType,requirementType,gender.gender);
201 return false;
203 return true;
204 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_DIFFICULTY:
205 if (difficulty.difficulty >= MAX_DIFFICULTY)
207 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_MAP_DIFFICULTY (%u) have wrong difficulty in value1 (%u), ignore.",
208 criteria->ID, criteria->requiredType,requirementType,difficulty.difficulty);
209 return false;
211 return true;
212 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_PLAYER_COUNT:
213 if (map_players.maxcount <= 0)
215 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_MAP_PLAYER_COUNT (%u) have wrong max players count in value1 (%u), ignore.",
216 criteria->ID, criteria->requiredType,requirementType,map_players.maxcount);
217 return false;
219 return true;
220 case ACHIEVEMENT_CRITERIA_REQUIRE_T_TEAM:
221 if (team.team != ALLIANCE && team.team != HORDE)
223 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_T_TEAM (%u) have unknown team in value1 (%u), ignore.",
224 criteria->ID, criteria->requiredType,requirementType,team.team);
225 return false;
227 return true;
228 case ACHIEVEMENT_CRITERIA_REQUIRE_S_DRUNK:
229 if(drunk.state >= MAX_DRUNKEN)
231 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_S_DRUNK (%u) have unknown drunken state in value1 (%u), ignore.",
232 criteria->ID, criteria->requiredType,requirementType,drunk.state);
233 return false;
235 return true;
236 case ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY:
237 if (!sHolidaysStore.LookupEntry(holiday.id))
239 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY (%u) have unknown holiday in value1 (%u), ignore.",
240 criteria->ID, criteria->requiredType,requirementType,holiday.id);
241 return false;
243 return true;
244 default:
245 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,requirementType);
246 return false;
248 return false;
251 bool AchievementCriteriaRequirement::Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const
253 switch(requirementType)
255 case ACHIEVEMENT_CRITERIA_REQUIRE_NONE:
256 return true;
257 case ACHIEVEMENT_CRITERIA_REQUIRE_T_CREATURE:
258 if (!target || target->GetTypeId()!=TYPEID_UNIT)
259 return false;
260 return target->GetEntry() == creature.id;
261 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_CLASS_RACE:
262 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
263 return false;
264 if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass())
265 return false;
266 if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace())
267 return false;
268 return true;
269 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_LESS_HEALTH:
270 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
271 return false;
272 return target->GetHealth()*100 <= health.percent*target->GetMaxHealth();
273 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD:
274 if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || ((Player*)target)->GetDeathTimer() == 0)
275 return false;
276 // flag set == must be same team, not set == different team
277 return (((Player*)target)->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0);
278 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA:
279 return source->HasAura(aura.spell_id,aura.effect_idx);
280 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AREA:
282 uint32 zone_id,area_id;
283 source->GetZoneAndAreaId(zone_id,area_id);
284 return area.id==zone_id || area.id==area_id;
286 case ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA:
287 return target && target->HasAura(aura.spell_id,aura.effect_idx);
288 case ACHIEVEMENT_CRITERIA_REQUIRE_VALUE:
289 return miscvalue1 >= value.minvalue;
290 case ACHIEVEMENT_CRITERIA_REQUIRE_T_LEVEL:
291 if (!target)
292 return false;
293 return target->getLevel() >= level.minlevel;
294 case ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER:
295 if (!target)
296 return false;
297 return target->getGender() == gender.gender;
298 case ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED:
299 return false; // always fail
300 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_DIFFICULTY:
301 return source->GetMap()->GetSpawnMode()==difficulty.difficulty;
302 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_PLAYER_COUNT:
303 return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount;
304 case ACHIEVEMENT_CRITERIA_REQUIRE_T_TEAM:
305 if (!target || target->GetTypeId() != TYPEID_PLAYER)
306 return false;
307 return ((Player*)target)->GetTeam() == team.team;
308 case ACHIEVEMENT_CRITERIA_REQUIRE_S_DRUNK:
309 return (uint32)Player::GetDrunkenstateByValue(source->GetDrunkValue()) >= drunk.state;
310 case ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY:
311 return IsHolidayActive(HolidayIds(holiday.id));
312 case ACHIEVEMENT_CRITERIA_REQUIRE_BG_LOSS_TEAM_SCORE:
314 BattleGround* bg = source->GetBattleGround();
315 if(!bg)
316 return false;
317 return bg->IsTeamScoreInRange(source->GetTeam()==ALLIANCE ? HORDE : ALLIANCE,bg_loss_team_score.min_score,bg_loss_team_score.max_score);
319 case ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT:
320 if (!source->IsInWorld())
321 return false;
322 Map* map = source->GetMap();
323 if (!map->Instanceable())
325 sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-instance map %u",
326 ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT, criteria_id, map->GetId());
327 return false;
329 InstanceData* data = ((InstanceMap*)map)->GetInstanceData();
330 if (!data)
332 sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map not have instance script",
333 ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT, criteria_id, map->GetId());
334 return false;
336 return data->CheckAchievementCriteriaMeet(criteria_id, source, target, miscvalue1);
338 return false;
341 bool AchievementCriteriaRequirementSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const
343 for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr)
344 if(!itr->Meets(criteria_id, source, target, miscvalue))
345 return false;
347 return true;
350 AchievementMgr::AchievementMgr(Player *player)
352 m_player = player;
355 AchievementMgr::~AchievementMgr()
359 void AchievementMgr::Reset()
361 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
363 WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
364 data << uint32(iter->first);
365 m_player->SendDirectMessage(&data);
368 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
370 WorldPacket data(SMSG_CRITERIA_DELETED,4);
371 data << uint32(iter->first);
372 m_player->SendDirectMessage(&data);
375 m_completedAchievements.clear();
376 m_criteriaProgress.clear();
377 DeleteFromDB(m_player->GetGUIDLow());
379 // re-fill data
380 CheckAllAchievementCriteria();
383 void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2)
385 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
386 sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2);
388 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
389 return;
391 AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr.GetAchievementCriteriaByType(type);
392 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
394 AchievementCriteriaEntry const *achievementCriteria = (*i);
396 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
397 if (!achievement)
398 continue;
400 // don't update already completed criteria
401 if (IsCompletedCriteria(achievementCriteria,achievement))
402 continue;
404 switch (type)
406 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset
407 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset
408 if (achievementCriteria->healing_done.flag == miscvalue1 &&
409 achievementCriteria->healing_done.mapid == miscvalue2)
410 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
411 break;
412 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset
413 // reset only the criteria having the miscvalue1 condition
414 if (achievementCriteria->win_rated_arena.flag == miscvalue1)
415 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
416 break;
417 default: // reset all cases
418 break;
423 void AchievementMgr::DeleteFromDB(uint32 lowguid)
425 CharacterDatabase.BeginTransaction ();
426 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid);
427 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
428 CharacterDatabase.CommitTransaction ();
431 void AchievementMgr::SaveToDB()
433 if(!m_completedAchievements.empty())
435 bool need_execute = false;
436 std::ostringstream ssdel;
437 std::ostringstream ssins;
438 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
440 if(!iter->second.changed)
441 continue;
443 /// first new/changed record prefix
444 if(!need_execute)
446 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
447 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
448 need_execute = true;
450 /// next new/changed record prefix
451 else
453 ssdel << ", ";
454 ssins << ", ";
457 // new/changed record data
458 ssdel << iter->first;
459 ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
461 /// mark as saved in db
462 iter->second.changed = false;
465 if(need_execute)
466 ssdel << ")";
468 if(need_execute)
470 CharacterDatabase.Execute( ssdel.str().c_str() );
471 CharacterDatabase.Execute( ssins.str().c_str() );
475 if(!m_criteriaProgress.empty())
477 /// prepare deleting and insert
478 bool need_execute_del = false;
479 bool need_execute_ins = false;
480 std::ostringstream ssdel;
481 std::ostringstream ssins;
482 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
484 if(!iter->second.changed)
485 continue;
487 // deleted data (including 0 progress state)
489 /// first new/changed record prefix (for any counter value)
490 if(!need_execute_del)
492 ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
493 need_execute_del = true;
495 /// next new/changed record prefix
496 else
497 ssdel << ", ";
499 // new/changed record data
500 ssdel << iter->first;
503 // store data only for real progress
504 if(iter->second.counter != 0)
506 /// first new/changed record prefix
507 if(!need_execute_ins)
509 ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
510 need_execute_ins = true;
512 /// next new/changed record prefix
513 else
514 ssins << ", ";
516 // new/changed record data
517 ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
520 /// mark as updated in db
521 iter->second.changed = false;
524 if(need_execute_del) // DELETE ... IN (.... _)_
525 ssdel << ")";
527 if(need_execute_del || need_execute_ins)
529 if(need_execute_del)
530 CharacterDatabase.Execute( ssdel.str().c_str() );
531 if(need_execute_ins)
532 CharacterDatabase.Execute( ssins.str().c_str() );
537 void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
539 if(achievementResult)
543 Field *fields = achievementResult->Fetch();
545 uint32 achievement_id = fields[0].GetUInt32();
547 // don't must happen: cleanup at server startup in sAchievementMgr.LoadCompletedAchievements()
548 if(!sAchievementStore.LookupEntry(achievement_id))
549 continue;
551 CompletedAchievementData& ca = m_completedAchievements[achievement_id];
552 ca.date = time_t(fields[1].GetUInt64());
553 ca.changed = false;
554 } while(achievementResult->NextRow());
555 delete achievementResult;
558 if(criteriaResult)
562 Field *fields = criteriaResult->Fetch();
564 uint32 id = fields[0].GetUInt32();
565 uint32 counter = fields[1].GetUInt32();
566 time_t date = time_t(fields[2].GetUInt64());
568 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
569 if (!criteria)
571 // we will remove not existed criteria for all characters
572 sLog.outError("Not existed achievement criteria %u data removed from table `character_achievement_progress`.",id);
573 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id);
574 continue;
577 if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))
578 continue;
580 CriteriaProgress& progress = m_criteriaProgress[id];
581 progress.counter = counter;
582 progress.date = date;
583 progress.changed = false;
584 } while(criteriaResult->NextRow());
585 delete criteriaResult;
590 void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
592 if(GetPlayer()->GetSession()->PlayerLoading())
593 return;
595 #ifdef MANGOS_DEBUG
596 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
597 sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
598 #endif
600 if(Guild* guild = sObjectMgr.GetGuildById(GetPlayer()->GetGuildId()))
602 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
603 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
604 guild->BroadcastWorker(say_do,GetPlayer());
607 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
609 // broadcast realm first reached
610 WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4);
611 data << GetPlayer()->GetName();
612 data << uint64(GetPlayer()->GetGUID());
613 data << uint32(achievement->ID);
614 data << uint32(0); // 1=link supplied string as player name, 0=display plain string
615 sWorld.SendGlobalMessage(&data);
617 // if player is in world he can tell his friends about new achievement
618 else if (GetPlayer()->IsInWorld())
620 CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
622 Cell cell(p);
623 cell.data.Part.reserved = ALL_DISTRICT;
624 cell.SetNoCreate();
626 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
627 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
628 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do);
629 TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> >, WorldTypeMapContainer > message(say_worker);
630 CellLock<GridReadGuard> cell_lock(cell, p);
631 cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY));
634 WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
635 data.append(GetPlayer()->GetPackGUID());
636 data << uint32(achievement->ID);
637 data << uint32(secsToTimeBitFields(time(NULL)));
638 data << uint32(0);
639 GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true);
642 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
644 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
645 data << uint32(id);
647 // the counter is packed like a packed Guid
648 data.appendPackGUID(progress->counter);
650 data.append(GetPlayer()->GetPackGUID());
651 data << uint32(0);
652 data << uint32(secsToTimeBitFields(progress->date));
653 data << uint32(0); // timer 1
654 data << uint32(0); // timer 2
655 GetPlayer()->SendDirectMessage(&data);
659 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
661 void AchievementMgr::CheckAllAchievementCriteria()
663 // suppress sending packets
664 for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
665 UpdateAchievementCriteria(AchievementCriteriaTypes(i));
668 static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 };
669 static const uint32 achievIdForDangeon[][4] =
671 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
672 { 321, true, true, true },
673 { 916, false, true, false },
674 { 917, false, true, false },
675 { 918, true, false, false },
676 { 2219, false, false, true },
677 { 0, false, false, false }
681 * this function will be called whenever the user might have done a criteria relevant action
683 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time)
685 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
686 sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
688 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
689 return;
691 AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr.GetAchievementCriteriaByType(type);
692 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
694 AchievementCriteriaEntry const *achievementCriteria = (*i);
696 if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
697 continue;
699 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
700 if (!achievement)
701 continue;
703 if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
704 (achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
705 continue;
707 // don't update already completed criteria
708 if (IsCompletedCriteria(achievementCriteria,achievement))
709 continue;
711 switch (type)
713 // std. case: increment at 1
714 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
715 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
716 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
717 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
718 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
719 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
720 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
721 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
722 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
723 if(!miscvalue1)
724 continue;
725 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
726 break;
727 // std case: increment at miscvalue1
728 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
729 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
730 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING:
731 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
732 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
733 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
734 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED:
735 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED:
736 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
737 if(!miscvalue1)
738 continue;
739 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
740 break;
741 // std case: high value at miscvalue1
742 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
743 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */
744 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT:
745 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED:
746 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED:
747 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED:
748 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
749 if(!miscvalue1)
750 continue;
751 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
752 break;
754 // specialized cases
756 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
758 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
759 if (!miscvalue1)
760 continue;
761 if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId())
762 continue;
764 if (achievementCriteria->win_bg.additionalRequirement1_type)
766 // those requirements couldn't be found in the dbc
767 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
768 if (!data || !data->Meets(GetPlayer(),unit))
769 continue;
771 // some hardcoded requirements
772 else
774 BattleGround* bg = GetPlayer()->GetBattleGround();
775 if (!bg)
776 continue;
778 switch(achievementCriteria->referredAchievement)
780 case 161: // AB, Overcome a 500 resource disadvantage
782 if (bg->GetTypeID() != BATTLEGROUND_AB)
783 continue;
784 if(!((BattleGroundAB*)bg)->IsTeamScores500Disadvantage(GetPlayer()->GetTeam()))
785 continue;
786 break;
788 case 156: // AB, win while controlling all 5 flags (all nodes)
789 case 784: // EY, win while holding 4 bases (all nodes)
791 if(!bg->IsAllNodesConrolledByTeam(GetPlayer()->GetTeam()))
792 continue;
793 break;
795 case 1762: // SA, win without losing any siege vehicles
796 case 2192: // SA, win without losing any siege vehicles
797 continue; // not implemented
801 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
802 break;
804 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
806 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
807 if(!miscvalue1)
808 continue;
809 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
810 continue;
812 // those requirements couldn't be found in the dbc
813 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
814 if(!data || !data->Meets(GetPlayer(),unit))
815 continue;
817 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
818 break;
820 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
821 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
822 break;
823 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
824 // update at loading or specific skill update
825 if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID)
826 continue;
827 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
828 SetCriteriaProgress(achievementCriteria, skillvalue);
829 break;
830 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
831 // update at loading or specific skill update
832 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID)
833 continue;
834 if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID))
835 SetCriteriaProgress(achievementCriteria, maxSkillvalue);
836 break;
837 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
838 if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
839 SetCriteriaProgress(achievementCriteria, 1);
840 break;
841 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
843 uint32 counter =0;
844 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr)
845 if(itr->second.m_rewarded)
846 counter++;
847 SetCriteriaProgress(achievementCriteria, counter);
848 break;
850 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
852 // speedup for non-login case
853 if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID)
854 continue;
856 uint32 counter =0;
857 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr)
859 Quest const* quest = sObjectMgr.GetQuestTemplate(itr->first);
860 if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
861 counter++;
863 SetCriteriaProgress(achievementCriteria, counter);
864 break;
866 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
867 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
868 if(!miscvalue1)
869 continue;
870 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
871 continue;
872 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
873 break;
874 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
875 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
876 if(!miscvalue1)
877 continue;
878 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
879 continue;
880 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
881 break;
882 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
884 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
885 if(!miscvalue1)
886 continue;
887 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
888 bool notfit = false;
889 for(int j = 0; j < MAX_ARENA_SLOT; ++j)
891 if(achievIdByArenaSlot[j] == achievement->ID)
893 BattleGround* bg = GetPlayer()->GetBattleGround();
894 if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
895 notfit = true;
897 break;
900 if(notfit)
901 continue;
903 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
904 break;
906 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
908 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
909 if(!miscvalue1)
910 continue;
912 Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : sMapMgr.FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId());
913 if(!map || !map->IsDungeon())
914 continue;
916 // search case
917 bool found = false;
918 for(int j = 0; achievIdForDangeon[j][0]; ++j)
920 if(achievIdForDangeon[j][0] == achievement->ID)
922 if(map->IsRaid())
924 // if raid accepted (ignore difficulty)
925 if(!achievIdForDangeon[j][2])
926 break; // for
928 else if(GetPlayer()->GetDungeonDifficulty()==DUNGEON_DIFFICULTY_NORMAL)
930 // dungeon in normal mode accepted
931 if(!achievIdForDangeon[j][1])
932 break; // for
934 else
936 // dungeon in heroic mode accepted
937 if(!achievIdForDangeon[j][3])
938 break; // for
941 found = true;
942 break; // for
945 if(!found)
946 continue;
948 //FIXME: work only for instances where max==min for players
949 if (map->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
950 continue;
951 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
952 break;
955 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
956 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
957 if(!miscvalue1)
958 continue;
959 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
960 continue;
961 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
962 break;
963 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
964 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
965 if(!miscvalue1)
966 continue;
968 // if team check required: must kill by opposition faction
969 if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
970 continue;
972 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
973 break;
974 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
976 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
977 if(!miscvalue1)
978 continue;
980 // those requirements couldn't be found in the dbc
981 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
982 if(!data || !data->Meets(GetPlayer(),unit))
983 continue;
985 // miscvalue1 is the ingame fallheight*100 as stored in dbc
986 SetCriteriaProgress(achievementCriteria, miscvalue1);
987 break;
989 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
990 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
991 if(!miscvalue1)
992 continue;
993 if(miscvalue2 != achievementCriteria->death_from.type)
994 continue;
995 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
996 break;
997 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
999 // if miscvalues != 0, it contains the questID.
1000 if (miscvalue1)
1002 if (miscvalue1 != achievementCriteria->complete_quest.questID)
1003 continue;
1005 else
1007 // login case.
1008 if(!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
1009 continue;
1012 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
1013 switch(achievement->ID)
1015 case 31:
1016 case 1275:
1017 case 1276:
1018 case 1277:
1019 case 1282:
1020 case 1789:
1022 // those requirements couldn't be found in the dbc
1023 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1024 if(!data || !data->Meets(GetPlayer(),unit))
1025 continue;
1026 break;
1028 default:
1029 break;
1033 SetCriteriaProgress(achievementCriteria, 1);
1034 break;
1036 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1037 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1039 if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID)
1040 continue;
1042 // those requirements couldn't be found in the dbc
1043 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1044 if(!data)
1045 continue;
1047 if(!data->Meets(GetPlayer(),unit))
1048 continue;
1050 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1051 break;
1053 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1054 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1056 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
1057 continue;
1059 // those requirements couldn't be found in the dbc
1060 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1061 if(!data)
1062 continue;
1064 if(!data->Meets(GetPlayer(),unit))
1065 continue;
1067 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1068 break;
1070 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1071 if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
1072 continue;
1074 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
1075 SetCriteriaProgress(achievementCriteria, 1);
1076 break;
1077 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1079 // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored)
1080 // miscvalue2=count of item loot
1081 if (!miscvalue1 || !miscvalue2)
1082 continue;
1083 if (miscvalue1 != achievementCriteria->loot_type.lootType)
1084 continue;
1086 // zone specific
1087 if(achievementCriteria->loot_type.lootTypeCount==1)
1089 // those requirements couldn't be found in the dbc
1090 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1091 if(!data || !data->Meets(GetPlayer(),unit))
1092 continue;
1095 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1096 break;
1098 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1099 // speedup for non-login case
1100 if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1)
1101 continue;
1102 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
1103 break;
1104 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1105 // miscvalue1 contains the personal rating
1106 if (!miscvalue1) // no update at login
1107 continue;
1109 // additional requirements
1110 if(achievementCriteria->win_rated_arena.flag==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1112 // those requirements couldn't be found in the dbc
1113 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1114 if(!data || !data->Meets(GetPlayer(),unit,miscvalue1))
1116 // reset the progress as we have a win without the requirement.
1117 SetCriteriaProgress(achievementCriteria, 0);
1118 continue;
1122 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1123 break;
1124 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1125 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1126 if(!miscvalue1)
1127 continue;
1128 if(achievementCriteria->use_item.itemID != miscvalue1)
1129 continue;
1130 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1131 break;
1132 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1133 // You _have_ to loot that item, just owning it when logging in does _not_ count!
1134 if(!miscvalue1)
1135 continue;
1136 if(miscvalue1 != achievementCriteria->own_item.itemID)
1137 continue;
1138 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1139 break;
1140 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1142 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
1143 if(!worldOverlayEntry)
1144 break;
1146 bool matchFound = false;
1147 for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
1149 uint32 area_id = worldOverlayEntry->areatableID[j];
1150 if(!area_id) // array have 0 only in empty tail
1151 break;
1153 int32 exploreFlag = GetAreaFlagByAreaID(area_id);
1154 if(exploreFlag < 0)
1155 continue;
1157 uint32 playerIndexOffset = uint32(exploreFlag) / 32;
1158 uint32 mask = 1<< (uint32(exploreFlag) % 32);
1160 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
1162 matchFound = true;
1163 break;
1167 if(matchFound)
1168 SetCriteriaProgress(achievementCriteria, 1);
1169 break;
1171 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1172 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount());
1173 break;
1174 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1176 // skip faction check only at loading
1177 if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID)
1178 continue;
1180 int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
1181 if (reputation > 0)
1182 SetCriteriaProgress(achievementCriteria, reputation);
1183 break;
1185 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1187 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
1188 break;
1190 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1192 // skip for login case
1193 if(!miscvalue1)
1194 continue;
1195 SetCriteriaProgress(achievementCriteria, 1);
1196 break;
1198 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1199 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1201 // miscvalue1 = itemid
1202 // miscvalue2 = diced value
1203 if(!miscvalue1)
1204 continue;
1205 if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
1206 continue;
1207 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype( miscvalue1 );
1209 uint32 requiredItemLevel = 0;
1210 if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
1211 requiredItemLevel = 185;
1213 if(!pProto || pProto->ItemLevel <requiredItemLevel)
1214 continue;
1215 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1216 break;
1218 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1220 // miscvalue1 = emote
1221 if(!miscvalue1)
1222 continue;
1223 if(miscvalue1 != achievementCriteria->do_emote.emoteID)
1224 continue;
1225 if(achievementCriteria->do_emote.count)
1227 // those requirements couldn't be found in the dbc
1228 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1229 if(!data || !data->Meets(GetPlayer(),unit))
1230 continue;
1233 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1234 break;
1236 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1237 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1239 if (!miscvalue1)
1240 continue;
1242 if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP)
1244 if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid)
1245 continue;
1247 // map specific case (BG in fact) expected player targeted damage/heal
1248 if(!unit || unit->GetTypeId()!=TYPEID_PLAYER)
1249 continue;
1252 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
1253 break;
1255 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1256 // miscvalue1 = item_id
1257 if (!miscvalue1)
1258 continue;
1259 if (miscvalue1 != achievementCriteria->equip_item.itemID)
1260 continue;
1262 SetCriteriaProgress(achievementCriteria, 1);
1263 break;
1264 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1265 // miscvalue1 = go entry
1266 if (!miscvalue1)
1267 continue;
1268 if (miscvalue1 != achievementCriteria->use_gameobject.goEntry)
1269 continue;
1271 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1272 break;
1273 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1274 if (!miscvalue1)
1275 continue;
1276 if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
1277 continue;
1279 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1280 break;
1281 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1283 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine)
1284 continue;
1286 uint32 spellCount = 0;
1287 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1288 spellIter != GetPlayer()->GetSpellMap().end();
1289 ++spellIter)
1291 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellIter->first);
1292 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1294 if(skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine)
1295 spellCount++;
1298 SetCriteriaProgress(achievementCriteria, spellCount);
1299 break;
1301 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1302 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1303 if (!miscvalue1)
1304 continue;
1306 if (achievementCriteria->win_duel.duelCount)
1308 // those requirements couldn't be found in the dbc
1309 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1310 if (!data)
1311 continue;
1313 if (!data->Meets(GetPlayer(),unit))
1314 continue;
1317 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1318 break;
1319 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1320 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1321 break;
1322 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1323 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1324 break;
1325 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1326 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
1327 break;
1328 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1330 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine)
1331 continue;
1333 uint32 spellCount = 0;
1334 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1335 spellIter != GetPlayer()->GetSpellMap().end();
1336 ++spellIter)
1338 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellIter->first);
1339 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1340 if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine)
1341 spellCount++;
1343 SetCriteriaProgress(achievementCriteria, spellCount);
1344 break;
1346 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1347 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
1348 break;
1349 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1350 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
1351 continue;
1353 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1354 break;
1355 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1356 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
1357 continue;
1359 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1360 break;
1361 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1362 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST);
1363 break;
1364 // std case: not exist in DBC, not triggered in code as result
1365 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1366 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1367 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1368 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER:
1369 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT:
1370 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING:
1371 break;
1372 // FIXME: not triggered in code as result, need to implement
1373 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY:
1374 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
1375 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE:
1376 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
1377 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
1378 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
1379 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL:
1380 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
1381 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING:
1382 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
1383 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1384 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
1385 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
1386 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1387 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
1388 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE:
1389 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:
1390 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
1391 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS:
1392 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1393 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1394 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL:
1395 break; // Not implemented yet :(
1397 if(IsCompletedCriteria(achievementCriteria,achievement))
1398 CompletedCriteriaFor(achievement);
1400 // check again the completeness for SUMM and REQ COUNT achievements,
1401 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
1402 if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
1404 if (IsCompletedAchievement(achievement))
1405 CompletedAchievement(achievement);
1408 if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID))
1410 for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
1411 if(IsCompletedAchievement(*itr))
1412 CompletedAchievement(*itr);
1417 static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
1418 static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
1420 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement)
1422 // counter can never complete
1423 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1424 return false;
1426 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1428 // someone on this realm has already completed that achievement
1429 if(sAchievementMgr.IsRealmCompleted(achievement))
1430 return false;
1433 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
1434 if(itr == m_criteriaProgress.end())
1435 return false;
1437 CriteriaProgress const* progress = &itr->second;
1439 switch(achievementCriteria->requiredType)
1441 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1442 return progress->counter >= achievementCriteria->win_bg.winCount;
1443 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1444 return progress->counter >= achievementCriteria->kill_creature.creatureCount;
1445 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
1447 // skip wrong class achievements
1448 for(int i = 1; i < MAX_CLASSES; ++i)
1449 if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
1450 return false;
1452 // skip wrong race achievements
1453 for(int i = 1; i < MAX_RACES; ++i)
1454 if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
1455 return false;
1457 // appropriate class/race or not class/race specific
1458 return progress->counter >= achievementCriteria->reach_level.level;
1460 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
1461 return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
1462 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
1463 return progress->counter >= 1;
1464 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
1465 return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
1466 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
1467 return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
1468 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1469 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1470 return progress->counter >= achievementCriteria->healing_done.count;
1471 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
1472 return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
1473 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1474 return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
1475 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1476 return progress->counter >= 1;
1477 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1478 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1479 return progress->counter >= achievementCriteria->be_spell_target.spellCount;
1480 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1481 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1482 return progress->counter >= achievementCriteria->cast_spell.castCount;
1483 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1484 return progress->counter >= 1;
1485 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1486 return progress->counter >= achievementCriteria->own_item.itemCount;
1487 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1488 return progress->counter >= achievementCriteria->win_rated_arena.count;
1489 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
1490 return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75);
1491 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1492 return progress->counter >= achievementCriteria->use_item.itemCount;
1493 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1494 return progress->counter >= achievementCriteria->loot_item.itemCount;
1495 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1496 return progress->counter >= 1;
1497 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1498 return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
1499 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1500 return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
1501 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1502 return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
1503 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1504 return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
1505 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1506 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1507 return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
1508 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1509 return progress->counter >= achievementCriteria->hk_class.count;
1510 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1511 return progress->counter >= achievementCriteria->hk_race.count;
1512 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1513 return progress->counter >= achievementCriteria->do_emote.count;
1514 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1515 return progress->counter >= achievementCriteria->equip_item.count;
1516 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
1517 return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper;
1518 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
1519 return progress->counter >= achievementCriteria->loot_money.goldInCopper;
1520 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1521 return progress->counter >= achievementCriteria->use_gameobject.useCount;
1522 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1523 return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount;
1524 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1525 return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount;
1526 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1527 return progress->counter >= achievementCriteria->win_duel.duelCount;
1528 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1529 return progress->counter >= achievementCriteria->loot_type.lootTypeCount;
1530 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1531 return progress->counter >= achievementCriteria->learn_skill_line.spellCount;
1532 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1533 return progress->counter >= achievementCriteria->honorable_kill.killCount;
1535 // handle all statistic-only criteria here
1536 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
1537 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
1538 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
1539 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
1540 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
1541 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
1542 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1543 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
1544 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
1545 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
1546 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
1547 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
1548 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
1549 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD:
1550 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1551 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1552 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1553 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1554 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
1555 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
1556 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1557 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1558 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1559 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
1560 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
1561 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
1562 return false;
1564 return false;
1567 void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
1569 // counter can never complete
1570 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1571 return;
1573 // already completed and stored
1574 if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1575 return;
1577 if (IsCompletedAchievement(achievement))
1578 CompletedAchievement(achievement);
1581 bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
1583 // counter can never complete
1584 if(entry->flags & ACHIEVEMENT_FLAG_COUNTER)
1585 return false;
1587 // for achievement with referenced achievement criterias get from referenced and counter from self
1588 uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID;
1589 uint32 achievmentForTestCount = entry->count;
1591 AchievementCriteriaEntryList const* cList = sAchievementMgr.GetAchievementCriteriaByAchievement(achievmentForTestId);
1592 if(!cList)
1593 return false;
1594 uint32 count = 0;
1596 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
1597 // Oddly, the target count is NOT countained in the achievement, but in each individual criteria
1598 if (entry->flags & ACHIEVEMENT_FLAG_SUMM)
1600 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1602 AchievementCriteriaEntry const* criteria = *itr;
1604 CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID);
1605 if(itrProgress == m_criteriaProgress.end())
1606 continue;
1608 CriteriaProgress const* progress = &itrProgress->second;
1609 count += progress->counter;
1611 // for counters, field4 contains the main count requirement
1612 if (count >= criteria->raw.count)
1613 return true;
1615 return false;
1618 // Default case - need complete all or
1619 bool completed_all = true;
1620 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1622 AchievementCriteriaEntry const* criteria = *itr;
1624 bool completed = IsCompletedCriteria(criteria,entry);
1626 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1627 if(completed)
1628 ++count;
1629 else
1630 completed_all = false;
1632 // completed as have req. count of completed criterias
1633 if(achievmentForTestCount > 0 && achievmentForTestCount <= count)
1634 return true;
1637 // all criterias completed requirement
1638 if(completed_all && achievmentForTestCount==0)
1639 return true;
1641 return false;
1644 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
1646 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
1647 sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
1649 CriteriaProgress *progress = NULL;
1651 CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
1653 if(iter == m_criteriaProgress.end())
1655 // not create record for 0 counter
1656 if(changeValue == 0)
1657 return;
1659 progress = &m_criteriaProgress[entry->ID];
1660 progress->counter = changeValue;
1661 progress->date = time(NULL);
1663 else
1665 progress = &iter->second;
1667 uint32 newValue = 0;
1668 switch(ptype)
1670 case PROGRESS_SET:
1671 newValue = changeValue;
1672 break;
1673 case PROGRESS_ACCUMULATE:
1675 // avoid overflow
1676 uint32 max_value = std::numeric_limits<uint32>::max();
1677 newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value;
1678 break;
1680 case PROGRESS_HIGHEST:
1681 newValue = progress->counter < changeValue ? changeValue : progress->counter;
1682 break;
1685 // not update (not mark as changed) if counter will have same value
1686 if(progress->counter == newValue)
1687 return;
1689 progress->counter = newValue;
1692 progress->changed = true;
1694 if(entry->timeLimit)
1696 time_t now = time(NULL);
1697 if(time_t(progress->date + entry->timeLimit) < now)
1698 progress->counter = 1;
1700 // also it seems illogical, the timeframe will be extended at every criteria update
1701 progress->date = now;
1703 SendCriteriaUpdate(entry->ID,progress);
1706 void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
1708 sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
1709 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1710 return;
1712 SendAchievementEarned(achievement);
1713 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
1714 ca.date = time(NULL);
1715 ca.changed = true;
1717 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1718 // TODO: where do set this instead?
1719 if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1720 sAchievementMgr.SetRealmCompleted(achievement);
1722 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
1724 // reward items and titles if any
1725 AchievementReward const* reward = sAchievementMgr.GetAchievementReward(achievement);
1727 // no rewards
1728 if(!reward)
1729 return;
1731 // titles
1732 if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
1734 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
1735 GetPlayer()->SetTitle(titleEntry);
1738 // mail
1739 if(reward->sender)
1741 Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
1743 int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1745 // subject and text
1746 std::string subject = reward->subject;
1747 std::string text = reward->text;
1748 if ( loc_idx >= 0 )
1750 if(AchievementRewardLocale const* loc = sAchievementMgr.GetAchievementRewardLocale(achievement))
1752 if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty())
1753 subject = loc->subject[loc_idx];
1754 if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty())
1755 text = loc->text[loc_idx];
1759 uint32 itemTextId = sObjectMgr.CreateItemText( text );
1761 MailDraft draft(subject, itemTextId);
1763 if(item)
1765 // save new item before send
1766 item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1768 // item
1769 draft.AddItem(item);
1772 draft.SendMailTo(GetPlayer(), MailSender(MAIL_CREATURE, reward->sender));
1776 void AchievementMgr::SendAllAchievementData()
1778 // since we don't know the exact size of the packed GUIDs this is just an approximation
1779 WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1780 BuildAllDataPacket(&data);
1781 GetPlayer()->GetSession()->SendPacket(&data);
1784 void AchievementMgr::SendRespondInspectAchievements(Player* player)
1786 // since we don't know the exact size of the packed GUIDs this is just an approximation
1787 WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1788 data.append(GetPlayer()->GetPackGUID());
1789 BuildAllDataPacket(&data);
1790 player->GetSession()->SendPacket(&data);
1794 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1796 void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
1798 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
1800 *data << uint32(iter->first);
1801 *data << uint32(secsToTimeBitFields(iter->second.date));
1803 *data << int32(-1);
1805 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
1807 *data << uint32(iter->first);
1808 data->appendPackGUID(iter->second.counter);
1809 data->append(GetPlayer()->GetPackGUID());
1810 *data << uint32(0);
1811 *data << uint32(secsToTimeBitFields(iter->second.date));
1812 *data << uint32(0);
1813 *data << uint32(0);
1816 *data << int32(-1);
1819 //==========================================================
1820 AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
1822 return m_AchievementCriteriasByType[type];
1825 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1827 if(sAchievementCriteriaStore.GetNumRows()==0)
1829 barGoLink bar(1);
1830 bar.step();
1832 sLog.outString();
1833 sLog.outErrorDb(">> Loaded 0 achievement criteria.");
1834 return;
1837 barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
1838 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1840 bar.step();
1842 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1843 if(!criteria)
1844 continue;
1846 assert(criteria->requiredType < ACHIEVEMENT_CRITERIA_TYPE_TOTAL && "Not updated ACHIEVEMENT_CRITERIA_TYPE_TOTAL?");
1848 m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
1849 m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
1852 sLog.outString();
1853 sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size());
1856 void AchievementGlobalMgr::LoadAchievementReferenceList()
1858 if(sAchievementStore.GetNumRows()==0)
1860 barGoLink bar(1);
1861 bar.step();
1863 sLog.outString();
1864 sLog.outErrorDb(">> Loaded 0 achievement references.");
1865 return;
1868 uint32 count = 0;
1869 barGoLink bar( sAchievementStore.GetNumRows() );
1870 for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
1872 bar.step();
1874 AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
1875 if(!achievement || !achievement->refAchievement)
1876 continue;
1878 m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement);
1879 ++count;
1882 sLog.outString();
1883 sLog.outString(">> Loaded %u achievement references.",count);
1886 void AchievementGlobalMgr::LoadAchievementCriteriaRequirements()
1888 m_criteriaRequirementMap.clear(); // need for reload case
1890 QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_requirement");
1892 if(!result)
1894 barGoLink bar(1);
1895 bar.step();
1897 sLog.outString();
1898 sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_requirement` is empty.");
1899 return;
1902 uint32 count = 0;
1903 uint32 disabled_count = 0;
1904 barGoLink bar(result->GetRowCount());
1907 bar.step();
1908 Field *fields = result->Fetch();
1909 uint32 criteria_id = fields[0].GetUInt32();
1911 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
1913 if (!criteria)
1915 sLog.outErrorDb( "Table `achievement_criteria_requirement` have data for not existed criteria (Entry: %u), ignore.", criteria_id);
1916 continue;
1919 AchievementCriteriaRequirement data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
1921 if (!data.IsValid(criteria))
1923 continue;
1926 // this will allocate empty data set storage
1927 AchievementCriteriaRequirementSet& dataSet = m_criteriaRequirementMap[criteria_id];
1928 dataSet.SetCriteriaId(criteria_id);
1930 // counting disable criteria requirements
1931 if (data.requirementType == ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED)
1932 ++disabled_count;
1934 // add real data only for not NONE requirements
1935 if (data.requirementType != ACHIEVEMENT_CRITERIA_REQUIRE_NONE)
1936 dataSet.Add(data);
1938 // counting requirements
1939 ++count;
1940 } while(result->NextRow());
1942 delete result;
1944 // post loading checks
1945 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1947 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1948 if(!criteria)
1949 continue;
1951 switch(criteria->requiredType)
1953 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1954 if(!criteria->win_bg.additionalRequirement1_type)
1955 continue;
1956 break;
1957 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1958 break; // any cases
1959 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1961 AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement);
1962 if(!achievement)
1963 continue;
1965 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
1966 switch(achievement->ID)
1968 case 31:
1969 case 1275:
1970 case 1276:
1971 case 1277:
1972 case 1282:
1973 case 1789:
1974 break;
1975 default:
1976 continue;
1979 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1980 break; // any cases
1981 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: // any cases
1982 break;
1983 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases
1984 if(criteria->win_rated_arena.flag!=ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1985 continue;
1986 break;
1987 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases
1988 if(criteria->do_emote.count==0)
1989 continue;
1990 break;
1991 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // skip statistics
1992 if(criteria->win_duel.duelCount==0)
1993 continue;
1994 break;
1995 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases
1996 break;
1997 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases
1998 if(criteria->loot_type.lootTypeCount!=1)
1999 continue;
2000 break;
2001 default: // type not use DB data, ignore
2002 continue;
2005 if(!GetCriteriaRequirementSet(criteria))
2006 sLog.outErrorDb( "Table `achievement_criteria_requirement` not have expected data for criteria (Entry: %u Type: %u) for achievement %u.", criteria->ID, criteria->requiredType, criteria->referredAchievement);
2009 sLog.outString();
2010 sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count);
2013 void AchievementGlobalMgr::LoadCompletedAchievements()
2015 QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
2017 if(!result)
2019 barGoLink bar(1);
2020 bar.step();
2022 sLog.outString();
2023 sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
2024 return;
2027 barGoLink bar(result->GetRowCount());
2030 bar.step();
2031 Field *fields = result->Fetch();
2033 uint32 achievement_id = fields[0].GetUInt32();
2034 if(!sAchievementStore.LookupEntry(achievement_id))
2036 // we will remove not existed achievement for all characters
2037 sLog.outError("Not existed achievement %u data removed from table `character_achievement`.",achievement_id);
2038 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id);
2039 continue;
2042 m_allCompletedAchievements.insert(achievement_id);
2043 } while(result->NextRow());
2045 delete result;
2047 sLog.outString();
2048 sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size());
2051 void AchievementGlobalMgr::LoadRewards()
2053 m_achievementRewards.clear(); // need for reload case
2055 // 0 1 2 3 4 5 6
2056 QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
2058 if(!result)
2060 barGoLink bar(1);
2062 bar.step();
2064 sLog.outString();
2065 sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
2066 return;
2069 uint32 count = 0;
2070 barGoLink bar(result->GetRowCount());
2074 bar.step();
2076 Field *fields = result->Fetch();
2077 uint32 entry = fields[0].GetUInt32();
2078 if (!sAchievementStore.LookupEntry(entry))
2080 sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
2081 continue;
2084 AchievementReward reward;
2085 reward.titleId[0] = fields[1].GetUInt32();
2086 reward.titleId[1] = fields[2].GetUInt32();
2087 reward.itemId = fields[3].GetUInt32();
2088 reward.sender = fields[4].GetUInt32();
2089 reward.subject = fields[5].GetCppString();
2090 reward.text = fields[6].GetCppString();
2092 if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
2093 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]);
2095 // must be title or mail at least
2096 if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
2098 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
2099 continue;
2102 if (reward.titleId[0])
2104 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
2105 if (!titleEntry)
2107 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
2108 reward.titleId[0] = 0;
2112 if (reward.titleId[1])
2114 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
2115 if (!titleEntry)
2117 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]);
2118 reward.titleId[1] = 0;
2122 //check mail data before item for report including wrong item case
2123 if (reward.sender)
2125 if (!ObjectMgr::GetCreatureTemplate(reward.sender))
2127 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
2128 reward.sender = 0;
2131 else
2133 if (reward.itemId)
2134 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
2136 if (!reward.subject.empty())
2137 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
2139 if (!reward.text.empty())
2140 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
2143 if (reward.itemId)
2145 if (!ObjectMgr::GetItemPrototype(reward.itemId))
2147 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
2148 reward.itemId = 0;
2152 m_achievementRewards[entry] = reward;
2153 ++count;
2155 } while (result->NextRow());
2157 delete result;
2159 sLog.outString();
2160 sLog.outString( ">> Loaded %u achievement rewards", count );
2163 void AchievementGlobalMgr::LoadRewardLocales()
2165 m_achievementRewardLocales.clear(); // need for reload case
2167 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");
2169 if(!result)
2171 barGoLink bar(1);
2173 bar.step();
2175 sLog.outString();
2176 sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
2177 return;
2180 barGoLink bar(result->GetRowCount());
2184 Field *fields = result->Fetch();
2185 bar.step();
2187 uint32 entry = fields[0].GetUInt32();
2189 if(m_achievementRewards.find(entry)==m_achievementRewards.end())
2191 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
2192 continue;
2195 AchievementRewardLocale& data = m_achievementRewardLocales[entry];
2197 for(int i = 1; i < MAX_LOCALE; ++i)
2199 std::string str = fields[1+2*(i-1)].GetCppString();
2200 if(!str.empty())
2202 int idx = sObjectMgr.GetOrNewIndexForLocale(LocaleConstant(i));
2203 if(idx >= 0)
2205 if(data.subject.size() <= size_t(idx))
2206 data.subject.resize(idx+1);
2208 data.subject[idx] = str;
2211 str = fields[1+2*(i-1)+1].GetCppString();
2212 if(!str.empty())
2214 int idx = sObjectMgr.GetOrNewIndexForLocale(LocaleConstant(i));
2215 if(idx >= 0)
2217 if(data.text.size() <= size_t(idx))
2218 data.text.resize(idx+1);
2220 data.text[idx] = str;
2224 } while (result->NextRow());
2226 delete result;
2228 sLog.outString();
2229 sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() );