[9118] Fixed problem with not set AchievementCriteriaRequirementSet::criteria_id
[getmangos.git] / src / game / AchievementMgr.cpp
blob124195a4df7bc8b2929489e328e2a4963315aad2
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 break;
97 default:
98 sLog.outErrorDb( "Table `achievement_criteria_requirement` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
99 return false;
102 switch(requirementType)
104 case ACHIEVEMENT_CRITERIA_REQUIRE_NONE:
105 case ACHIEVEMENT_CRITERIA_REQUIRE_VALUE:
106 case ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED:
107 case ACHIEVEMENT_CRITERIA_REQUIRE_BG_LOSS_TEAM_SCORE:
108 case ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT:
109 return true;
110 case ACHIEVEMENT_CRITERIA_REQUIRE_T_CREATURE:
111 if (!creature.id || !ObjectMgr::GetCreatureTemplate(creature.id))
113 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.",
114 criteria->ID, criteria->requiredType,requirementType,creature.id);
115 return false;
117 return true;
118 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_CLASS_RACE:
119 if (!classRace.class_id && !classRace.race_id)
121 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.",
122 criteria->ID, criteria->requiredType,requirementType);
123 return false;
125 if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE)==0)
127 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.",
128 criteria->ID, criteria->requiredType,requirementType,classRace.class_id);
129 return false;
131 if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE)==0)
133 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.",
134 criteria->ID, criteria->requiredType,requirementType,classRace.race_id);
135 return false;
137 return true;
138 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_LESS_HEALTH:
139 if (health.percent < 1 || health.percent > 100)
141 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.",
142 criteria->ID, criteria->requiredType,requirementType,health.percent);
143 return false;
145 return true;
146 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD:
147 if (player_dead.own_team_flag > 1)
149 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD (%u) have wrong boolean value1 (%u).",
150 criteria->ID, criteria->requiredType,requirementType,player_dead.own_team_flag);
151 return false;
153 return true;
154 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA:
155 case ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA:
157 SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id);
158 if (!spellEntry)
160 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have wrong spell id in value1 (%u), ignore.",
161 criteria->ID, criteria->requiredType,(requirementType==ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA?"ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA":"ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA"),requirementType,aura.spell_id);
162 return false;
164 if (aura.effect_idx >= 3)
166 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have wrong spell effect index in value2 (%u), ignore.",
167 criteria->ID, criteria->requiredType,(requirementType==ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA?"ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA":"ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA"),requirementType,aura.effect_idx);
168 return false;
170 if (!spellEntry->EffectApplyAuraName[aura.effect_idx])
172 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement %s (%u) have non-aura spell effect (ID: %u Effect: %u), ignore.",
173 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);
174 return false;
176 return true;
178 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AREA:
179 if (!GetAreaEntryByAreaID(area.id))
181 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.",
182 criteria->ID, criteria->requiredType,requirementType,area.id);
183 return false;
185 return true;
186 case ACHIEVEMENT_CRITERIA_REQUIRE_T_LEVEL:
187 if (level.minlevel < 0 || level.minlevel > STRONG_MAX_LEVEL)
189 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.",
190 criteria->ID, criteria->requiredType,requirementType,level.minlevel);
191 return false;
193 return true;
194 case ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER:
195 if (gender.gender > GENDER_NONE)
197 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.",
198 criteria->ID, criteria->requiredType,requirementType,gender.gender);
199 return false;
201 return true;
202 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_DIFFICULTY:
203 if (difficulty.difficulty >= MAX_DIFFICULTY)
205 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.",
206 criteria->ID, criteria->requiredType,requirementType,difficulty.difficulty);
207 return false;
209 return true;
210 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_PLAYER_COUNT:
211 if (map_players.maxcount <= 0)
213 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.",
214 criteria->ID, criteria->requiredType,requirementType,map_players.maxcount);
215 return false;
217 return true;
218 case ACHIEVEMENT_CRITERIA_REQUIRE_T_TEAM:
219 if (team.team != ALLIANCE && team.team != HORDE)
221 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.",
222 criteria->ID, criteria->requiredType,requirementType,team.team);
223 return false;
225 return true;
226 case ACHIEVEMENT_CRITERIA_REQUIRE_S_DRUNK:
227 if(drunk.state >= MAX_DRUNKEN)
229 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.",
230 criteria->ID, criteria->requiredType,requirementType,drunk.state);
231 return false;
233 return true;
234 case ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY:
235 if (!sHolidaysStore.LookupEntry(holiday.id))
237 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY (%u) have unknown holiday in value1 (%u), ignore.",
238 criteria->ID, criteria->requiredType,requirementType,holiday.id);
239 return false;
241 return true;
242 default:
243 sLog.outErrorDb( "Table `achievement_criteria_requirement` (Entry: %u Type: %u) have data for not supported data type (%u), ignore.", criteria->ID, criteria->requiredType,requirementType);
244 return false;
246 return false;
249 bool AchievementCriteriaRequirement::Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const
251 switch(requirementType)
253 case ACHIEVEMENT_CRITERIA_REQUIRE_NONE:
254 return true;
255 case ACHIEVEMENT_CRITERIA_REQUIRE_T_CREATURE:
256 if (!target || target->GetTypeId()!=TYPEID_UNIT)
257 return false;
258 return target->GetEntry() == creature.id;
259 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_CLASS_RACE:
260 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
261 return false;
262 if(classRace.class_id && classRace.class_id != ((Player*)target)->getClass())
263 return false;
264 if(classRace.race_id && classRace.race_id != ((Player*)target)->getRace())
265 return false;
266 return true;
267 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_LESS_HEALTH:
268 if (!target || target->GetTypeId()!=TYPEID_PLAYER)
269 return false;
270 return target->GetHealth()*100 <= health.percent*target->GetMaxHealth();
271 case ACHIEVEMENT_CRITERIA_REQUIRE_T_PLAYER_DEAD:
272 if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || ((Player*)target)->GetDeathTimer() == 0)
273 return false;
274 // flag set == must be same team, not set == different team
275 return (((Player*)target)->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0);
276 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AURA:
277 return source->HasAura(aura.spell_id,aura.effect_idx);
278 case ACHIEVEMENT_CRITERIA_REQUIRE_S_AREA:
280 uint32 zone_id,area_id;
281 source->GetZoneAndAreaId(zone_id,area_id);
282 return area.id==zone_id || area.id==area_id;
284 case ACHIEVEMENT_CRITERIA_REQUIRE_T_AURA:
285 return target && target->HasAura(aura.spell_id,aura.effect_idx);
286 case ACHIEVEMENT_CRITERIA_REQUIRE_VALUE:
287 return miscvalue1 >= value.minvalue;
288 case ACHIEVEMENT_CRITERIA_REQUIRE_T_LEVEL:
289 if (!target)
290 return false;
291 return target->getLevel() >= level.minlevel;
292 case ACHIEVEMENT_CRITERIA_REQUIRE_T_GENDER:
293 if (!target)
294 return false;
295 return target->getGender() == gender.gender;
296 case ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED:
297 return false; // always fail
298 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_DIFFICULTY:
299 return source->GetMap()->GetSpawnMode()==difficulty.difficulty;
300 case ACHIEVEMENT_CRITERIA_REQUIRE_MAP_PLAYER_COUNT:
301 return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount;
302 case ACHIEVEMENT_CRITERIA_REQUIRE_T_TEAM:
303 if (!target || target->GetTypeId() != TYPEID_PLAYER)
304 return false;
305 return ((Player*)target)->GetTeam() == team.team;
306 case ACHIEVEMENT_CRITERIA_REQUIRE_S_DRUNK:
307 return (uint32)Player::GetDrunkenstateByValue(source->GetDrunkValue()) >= drunk.state;
308 case ACHIEVEMENT_CRITERIA_REQUIRE_HOLIDAY:
309 return IsHolidayActive(HolidayIds(holiday.id));
310 case ACHIEVEMENT_CRITERIA_REQUIRE_BG_LOSS_TEAM_SCORE:
312 BattleGround* bg = source->GetBattleGround();
313 if(!bg)
314 return false;
315 return bg->IsTeamScoreInRange(source->GetTeam()==ALLIANCE ? HORDE : ALLIANCE,bg_loss_team_score.min_score,bg_loss_team_score.max_score);
317 case ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT:
318 if (!source->IsInWorld())
319 return false;
320 Map* map = source->GetMap();
321 if (!map->Instanceable())
323 sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-instance map %u",
324 ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT, criteria_id, map->GetId());
325 return false;
327 InstanceData* data = ((InstanceMap*)map)->GetInstanceData();
328 if (!data)
330 sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map not have instance script",
331 ACHIEVEMENT_CRITERIA_REQUIRE_INSTANCE_SCRIPT, criteria_id, map->GetId());
332 return false;
334 return data->CheckAchievementCriteriaMeet(criteria_id, source, target, miscvalue1);
336 return false;
339 bool AchievementCriteriaRequirementSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const
341 for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr)
342 if(!itr->Meets(criteria_id, source, target, miscvalue))
343 return false;
345 return true;
348 AchievementMgr::AchievementMgr(Player *player)
350 m_player = player;
353 AchievementMgr::~AchievementMgr()
357 void AchievementMgr::Reset()
359 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
361 WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4);
362 data << uint32(iter->first);
363 m_player->SendDirectMessage(&data);
366 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
368 WorldPacket data(SMSG_CRITERIA_DELETED,4);
369 data << uint32(iter->first);
370 m_player->SendDirectMessage(&data);
373 m_completedAchievements.clear();
374 m_criteriaProgress.clear();
375 DeleteFromDB(m_player->GetGUIDLow());
377 // re-fill data
378 CheckAllAchievementCriteria();
381 void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2)
383 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
384 sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2);
386 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
387 return;
389 AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr.GetAchievementCriteriaByType(type);
390 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
392 AchievementCriteriaEntry const *achievementCriteria = (*i);
394 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
395 if (!achievement)
396 continue;
398 // don't update already completed criteria
399 if (IsCompletedCriteria(achievementCriteria,achievement))
400 continue;
402 switch (type)
404 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset
405 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset
406 if (achievementCriteria->healing_done.flag == miscvalue1 &&
407 achievementCriteria->healing_done.mapid == miscvalue2)
408 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
409 break;
410 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset
411 // reset only the criteria having the miscvalue1 condition
412 if (achievementCriteria->win_rated_arena.flag == miscvalue1)
413 SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET);
414 break;
415 default: // reset all cases
416 break;
421 void AchievementMgr::DeleteFromDB(uint32 lowguid)
423 CharacterDatabase.BeginTransaction ();
424 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid);
425 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
426 CharacterDatabase.CommitTransaction ();
429 void AchievementMgr::SaveToDB()
431 if(!m_completedAchievements.empty())
433 bool need_execute = false;
434 std::ostringstream ssdel;
435 std::ostringstream ssins;
436 for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
438 if(!iter->second.changed)
439 continue;
441 /// first new/changed record prefix
442 if(!need_execute)
444 ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN (";
445 ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES ";
446 need_execute = true;
448 /// next new/changed record prefix
449 else
451 ssdel << ", ";
452 ssins << ", ";
455 // new/changed record data
456 ssdel << iter->first;
457 ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
459 /// mark as saved in db
460 iter->second.changed = false;
463 if(need_execute)
464 ssdel << ")";
466 if(need_execute)
468 CharacterDatabase.Execute( ssdel.str().c_str() );
469 CharacterDatabase.Execute( ssins.str().c_str() );
473 if(!m_criteriaProgress.empty())
475 /// prepare deleting and insert
476 bool need_execute_del = false;
477 bool need_execute_ins = false;
478 std::ostringstream ssdel;
479 std::ostringstream ssins;
480 for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
482 if(!iter->second.changed)
483 continue;
485 // deleted data (including 0 progress state)
487 /// first new/changed record prefix (for any counter value)
488 if(!need_execute_del)
490 ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN (";
491 need_execute_del = true;
493 /// next new/changed record prefix
494 else
495 ssdel << ", ";
497 // new/changed record data
498 ssdel << iter->first;
501 // store data only for real progress
502 if(iter->second.counter != 0)
504 /// first new/changed record prefix
505 if(!need_execute_ins)
507 ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES ";
508 need_execute_ins = true;
510 /// next new/changed record prefix
511 else
512 ssins << ", ";
514 // new/changed record data
515 ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
518 /// mark as updated in db
519 iter->second.changed = false;
522 if(need_execute_del) // DELETE ... IN (.... _)_
523 ssdel << ")";
525 if(need_execute_del || need_execute_ins)
527 if(need_execute_del)
528 CharacterDatabase.Execute( ssdel.str().c_str() );
529 if(need_execute_ins)
530 CharacterDatabase.Execute( ssins.str().c_str() );
535 void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
537 if(achievementResult)
541 Field *fields = achievementResult->Fetch();
543 uint32 achievement_id = fields[0].GetUInt32();
545 // don't must happen: cleanup at server startup in sAchievementMgr.LoadCompletedAchievements()
546 if(!sAchievementStore.LookupEntry(achievement_id))
547 continue;
549 CompletedAchievementData& ca = m_completedAchievements[achievement_id];
550 ca.date = time_t(fields[1].GetUInt64());
551 ca.changed = false;
552 } while(achievementResult->NextRow());
553 delete achievementResult;
556 if(criteriaResult)
560 Field *fields = criteriaResult->Fetch();
562 uint32 id = fields[0].GetUInt32();
563 uint32 counter = fields[1].GetUInt32();
564 time_t date = time_t(fields[2].GetUInt64());
566 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
567 if (!criteria)
569 // we will remove not existed criteria for all characters
570 sLog.outError("Not existed achievement criteria %u data removed from table `character_achievement_progress`.",id);
571 CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id);
572 continue;
575 if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))
576 continue;
578 CriteriaProgress& progress = m_criteriaProgress[id];
579 progress.counter = counter;
580 progress.date = date;
581 progress.changed = false;
582 } while(criteriaResult->NextRow());
583 delete criteriaResult;
588 void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
590 if(GetPlayer()->GetSession()->PlayerLoading())
591 return;
593 #ifdef MANGOS_DEBUG
594 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
595 sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
596 #endif
598 if(Guild* guild = sObjectMgr.GetGuildById(GetPlayer()->GetGuildId()))
600 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
601 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
602 guild->BroadcastWorker(say_do,GetPlayer());
605 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
607 // broadcast realm first reached
608 WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4);
609 data << GetPlayer()->GetName();
610 data << uint64(GetPlayer()->GetGUID());
611 data << uint32(achievement->ID);
612 data << uint32(0); // 1=link supplied string as player name, 0=display plain string
613 sWorld.SendGlobalMessage(&data);
615 // if player is in world he can tell his friends about new achievement
616 else if (GetPlayer()->IsInWorld())
618 CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
620 Cell cell(p);
621 cell.data.Part.reserved = ALL_DISTRICT;
622 cell.SetNoCreate();
624 MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
625 MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
626 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do);
627 TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> >, WorldTypeMapContainer > message(say_worker);
628 CellLock<GridReadGuard> cell_lock(cell, p);
629 cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY));
632 WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
633 data.append(GetPlayer()->GetPackGUID());
634 data << uint32(achievement->ID);
635 data << uint32(secsToTimeBitFields(time(NULL)));
636 data << uint32(0);
637 GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true);
640 void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
642 WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
643 data << uint32(id);
645 // the counter is packed like a packed Guid
646 data.appendPackGUID(progress->counter);
648 data.append(GetPlayer()->GetPackGUID());
649 data << uint32(0);
650 data << uint32(secsToTimeBitFields(progress->date));
651 data << uint32(0); // timer 1
652 data << uint32(0); // timer 2
653 GetPlayer()->SendDirectMessage(&data);
657 * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
659 void AchievementMgr::CheckAllAchievementCriteria()
661 // suppress sending packets
662 for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
663 UpdateAchievementCriteria(AchievementCriteriaTypes(i));
666 static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 };
667 static const uint32 achievIdForDangeon[][4] =
669 // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon
670 { 321, true, true, true },
671 { 916, false, true, false },
672 { 917, false, true, false },
673 { 918, true, false, false },
674 { 2219, false, false, true },
675 { 0, false, false, false }
679 * this function will be called whenever the user might have done a criteria relevant action
681 void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time)
683 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
684 sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
686 if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
687 return;
689 AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr.GetAchievementCriteriaByType(type);
690 for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
692 AchievementCriteriaEntry const *achievementCriteria = (*i);
694 if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
695 continue;
697 AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
698 if (!achievement)
699 continue;
701 if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
702 (achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
703 continue;
705 // don't update already completed criteria
706 if (IsCompletedCriteria(achievementCriteria,achievement))
707 continue;
709 switch (type)
711 // std. case: increment at 1
712 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
713 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
714 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
715 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
716 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
717 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
718 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
719 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
720 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
721 if(!miscvalue1)
722 continue;
723 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
724 break;
725 // std case: increment at miscvalue1
726 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
727 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
728 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING:
729 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
730 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
731 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
732 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED:
733 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED:
734 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
735 if(!miscvalue1)
736 continue;
737 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
738 break;
739 // std case: high value at miscvalue1
740 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
741 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */
742 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT:
743 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED:
744 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED:
745 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED:
746 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
747 if(!miscvalue1)
748 continue;
749 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
750 break;
752 // specialized cases
754 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
756 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
757 if (!miscvalue1)
758 continue;
759 if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId())
760 continue;
762 if (achievementCriteria->win_bg.additionalRequirement1_type)
764 // those requirements couldn't be found in the dbc
765 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
766 if (!data || !data->Meets(GetPlayer(),unit))
767 continue;
769 // some hardcoded requirements
770 else
772 BattleGround* bg = GetPlayer()->GetBattleGround();
773 if (!bg)
774 continue;
776 switch(achievementCriteria->referredAchievement)
778 case 161: // AB, Overcome a 500 resource disadvantage
780 if (bg->GetTypeID() != BATTLEGROUND_AB)
781 continue;
782 if(!((BattleGroundAB*)bg)->IsTeamScores500Disadvantage(GetPlayer()->GetTeam()))
783 continue;
784 break;
786 case 156: // AB, win while controlling all 5 flags (all nodes)
787 case 784: // EY, win while holding 4 bases (all nodes)
789 if(!bg->IsAllNodesConrolledByTeam(GetPlayer()->GetTeam()))
790 continue;
791 break;
793 case 1762: // SA, win without losing any siege vehicles
794 case 2192: // SA, win without losing any siege vehicles
795 continue; // not implemented
799 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
800 break;
802 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
804 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
805 if(!miscvalue1)
806 continue;
807 if(achievementCriteria->kill_creature.creatureID != miscvalue1)
808 continue;
810 // those requirements couldn't be found in the dbc
811 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
812 if(!data || !data->Meets(GetPlayer(),unit))
813 continue;
815 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
816 break;
818 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
819 SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel());
820 break;
821 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
822 // update at loading or specific skill update
823 if(miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID)
824 continue;
825 if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID))
826 SetCriteriaProgress(achievementCriteria, skillvalue);
827 break;
828 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
829 // update at loading or specific skill update
830 if(miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID)
831 continue;
832 if(uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID))
833 SetCriteriaProgress(achievementCriteria, maxSkillvalue);
834 break;
835 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
836 if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end())
837 SetCriteriaProgress(achievementCriteria, 1);
838 break;
839 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
841 uint32 counter =0;
842 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr)
843 if(itr->second.m_rewarded)
844 counter++;
845 SetCriteriaProgress(achievementCriteria, counter);
846 break;
848 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
850 // speedup for non-login case
851 if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID)
852 continue;
854 uint32 counter =0;
855 for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); ++itr)
857 Quest const* quest = sObjectMgr.GetQuestTemplate(itr->first);
858 if(itr->second.m_rewarded && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
859 counter++;
861 SetCriteriaProgress(achievementCriteria, counter);
862 break;
864 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
865 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
866 if(!miscvalue1)
867 continue;
868 if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID)
869 continue;
870 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
871 break;
872 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
873 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
874 if(!miscvalue1)
875 continue;
876 if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID)
877 continue;
878 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
879 break;
880 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
882 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
883 if(!miscvalue1)
884 continue;
885 // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter
886 bool notfit = false;
887 for(int j = 0; j < MAX_ARENA_SLOT; ++j)
889 if(achievIdByArenaSlot[j] == achievement->ID)
891 BattleGround* bg = GetPlayer()->GetBattleGround();
892 if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
893 notfit = true;
895 break;
898 if(notfit)
899 continue;
901 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
902 break;
904 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
906 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
907 if(!miscvalue1)
908 continue;
910 Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : sMapMgr.FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId());
911 if(!map || !map->IsDungeon())
912 continue;
914 // search case
915 bool found = false;
916 for(int j = 0; achievIdForDangeon[j][0]; ++j)
918 if(achievIdForDangeon[j][0] == achievement->ID)
920 if(map->IsRaid())
922 // if raid accepted (ignore difficulty)
923 if(!achievIdForDangeon[j][2])
924 break; // for
926 else if(GetPlayer()->GetDungeonDifficulty()==DUNGEON_DIFFICULTY_NORMAL)
928 // dungeon in normal mode accepted
929 if(!achievIdForDangeon[j][1])
930 break; // for
932 else
934 // dungeon in heroic mode accepted
935 if(!achievIdForDangeon[j][3])
936 break; // for
939 found = true;
940 break; // for
943 if(!found)
944 continue;
946 //FIXME: work only for instances where max==min for players
947 if (map->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
948 continue;
949 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
950 break;
953 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
954 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
955 if(!miscvalue1)
956 continue;
957 if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry)
958 continue;
959 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
960 break;
961 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
962 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
963 if(!miscvalue1)
964 continue;
966 // if team check required: must kill by opposition faction
967 if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
968 continue;
970 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
971 break;
972 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
974 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
975 if(!miscvalue1)
976 continue;
978 // those requirements couldn't be found in the dbc
979 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
980 if(!data || !data->Meets(GetPlayer(),unit))
981 continue;
983 // miscvalue1 is the ingame fallheight*100 as stored in dbc
984 SetCriteriaProgress(achievementCriteria, miscvalue1);
985 break;
987 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
988 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
989 if(!miscvalue1)
990 continue;
991 if(miscvalue2 != achievementCriteria->death_from.type)
992 continue;
993 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
994 break;
995 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
997 // if miscvalues != 0, it contains the questID.
998 if (miscvalue1)
1000 if (miscvalue1 != achievementCriteria->complete_quest.questID)
1001 continue;
1003 else
1005 // login case.
1006 if(!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
1007 continue;
1010 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
1011 switch(achievement->ID)
1013 case 31:
1014 case 1275:
1015 case 1276:
1016 case 1277:
1017 case 1282:
1018 case 1789:
1020 // those requirements couldn't be found in the dbc
1021 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1022 if(!data || !data->Meets(GetPlayer(),unit))
1023 continue;
1024 break;
1026 default:
1027 break;
1031 SetCriteriaProgress(achievementCriteria, 1);
1032 break;
1034 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1035 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1036 if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID)
1037 continue;
1038 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1039 break;
1040 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1041 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1043 if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
1044 continue;
1046 // those requirements couldn't be found in the dbc
1047 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1048 if(!data)
1049 continue;
1051 if(!data->Meets(GetPlayer(),unit))
1052 continue;
1054 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1055 break;
1057 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1058 if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
1059 continue;
1061 if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
1062 SetCriteriaProgress(achievementCriteria, 1);
1063 break;
1064 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1066 // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored)
1067 // miscvalue2=count of item loot
1068 if (!miscvalue1 || !miscvalue2)
1069 continue;
1070 if (miscvalue1 != achievementCriteria->loot_type.lootType)
1071 continue;
1073 // zone specific
1074 if(achievementCriteria->loot_type.lootTypeCount==1)
1076 // those requirements couldn't be found in the dbc
1077 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1078 if(!data || !data->Meets(GetPlayer(),unit))
1079 continue;
1082 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1083 break;
1085 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1086 // speedup for non-login case
1087 if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1)
1088 continue;
1089 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true));
1090 break;
1091 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1092 // miscvalue1 contains the personal rating
1093 if (!miscvalue1) // no update at login
1094 continue;
1096 // additional requirements
1097 if(achievementCriteria->win_rated_arena.flag==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1099 // those requirements couldn't be found in the dbc
1100 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1101 if(!data || !data->Meets(GetPlayer(),unit,miscvalue1))
1103 // reset the progress as we have a win without the requirement.
1104 SetCriteriaProgress(achievementCriteria, 0);
1105 continue;
1109 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1110 break;
1111 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1112 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1113 if(!miscvalue1)
1114 continue;
1115 if(achievementCriteria->use_item.itemID != miscvalue1)
1116 continue;
1117 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1118 break;
1119 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1120 // You _have_ to loot that item, just owning it when logging in does _not_ count!
1121 if(!miscvalue1)
1122 continue;
1123 if(miscvalue1 != achievementCriteria->own_item.itemID)
1124 continue;
1125 SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
1126 break;
1127 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1129 WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
1130 if(!worldOverlayEntry)
1131 break;
1133 bool matchFound = false;
1134 for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
1136 uint32 area_id = worldOverlayEntry->areatableID[j];
1137 if(!area_id) // array have 0 only in empty tail
1138 break;
1140 int32 exploreFlag = GetAreaFlagByAreaID(area_id);
1141 if(exploreFlag < 0)
1142 continue;
1144 uint32 playerIndexOffset = uint32(exploreFlag) / 32;
1145 uint32 mask = 1<< (uint32(exploreFlag) % 32);
1147 if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
1149 matchFound = true;
1150 break;
1154 if(matchFound)
1155 SetCriteriaProgress(achievementCriteria, 1);
1156 break;
1158 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1159 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount());
1160 break;
1161 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1163 // skip faction check only at loading
1164 if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID)
1165 continue;
1167 int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
1168 if (reputation > 0)
1169 SetCriteriaProgress(achievementCriteria, reputation);
1170 break;
1172 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1174 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount());
1175 break;
1177 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1179 // skip for login case
1180 if(!miscvalue1)
1181 continue;
1182 SetCriteriaProgress(achievementCriteria, 1);
1183 break;
1185 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1186 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1188 // miscvalue1 = itemid
1189 // miscvalue2 = diced value
1190 if(!miscvalue1)
1191 continue;
1192 if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
1193 continue;
1194 ItemPrototype const *pProto = ObjectMgr::GetItemPrototype( miscvalue1 );
1196 uint32 requiredItemLevel = 0;
1197 if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
1198 requiredItemLevel = 185;
1200 if(!pProto || pProto->ItemLevel <requiredItemLevel)
1201 continue;
1202 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1203 break;
1205 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1207 // miscvalue1 = emote
1208 if(!miscvalue1)
1209 continue;
1210 if(miscvalue1 != achievementCriteria->do_emote.emoteID)
1211 continue;
1212 if(achievementCriteria->do_emote.count)
1214 // those requirements couldn't be found in the dbc
1215 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1216 if(!data || !data->Meets(GetPlayer(),unit))
1217 continue;
1220 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1221 break;
1223 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1224 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1226 if (!miscvalue1)
1227 continue;
1229 if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP)
1231 if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid)
1232 continue;
1234 // map specific case (BG in fact) expected player targeted damage/heal
1235 if(!unit || unit->GetTypeId()!=TYPEID_PLAYER)
1236 continue;
1239 SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
1240 break;
1242 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1243 // miscvalue1 = item_id
1244 if (!miscvalue1)
1245 continue;
1246 if (miscvalue1 != achievementCriteria->equip_item.itemID)
1247 continue;
1249 SetCriteriaProgress(achievementCriteria, 1);
1250 break;
1251 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1252 // miscvalue1 = go entry
1253 if (!miscvalue1)
1254 continue;
1255 if (miscvalue1 != achievementCriteria->use_gameobject.goEntry)
1256 continue;
1258 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1259 break;
1260 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1261 if (!miscvalue1)
1262 continue;
1263 if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
1264 continue;
1266 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1267 break;
1268 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1270 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine)
1271 continue;
1273 uint32 spellCount = 0;
1274 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1275 spellIter != GetPlayer()->GetSpellMap().end();
1276 ++spellIter)
1278 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellIter->first);
1279 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1281 if(skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine)
1282 spellCount++;
1285 SetCriteriaProgress(achievementCriteria, spellCount);
1286 break;
1288 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1289 // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
1290 if (!miscvalue1)
1291 continue;
1293 if (achievementCriteria->win_duel.duelCount)
1295 // those requirements couldn't be found in the dbc
1296 AchievementCriteriaRequirementSet const* data = sAchievementMgr.GetCriteriaRequirementSet(achievementCriteria);
1297 if (!data)
1298 continue;
1300 if (!data->Meets(GetPlayer(),unit))
1301 continue;
1304 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1305 break;
1306 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1307 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount());
1308 break;
1309 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1310 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount());
1311 break;
1312 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1313 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount());
1314 break;
1315 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1317 if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine)
1318 continue;
1320 uint32 spellCount = 0;
1321 for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
1322 spellIter != GetPlayer()->GetSpellMap().end();
1323 ++spellIter)
1325 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(spellIter->first);
1326 for(SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter)
1327 if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine)
1328 spellCount++;
1330 SetCriteriaProgress(achievementCriteria, spellCount);
1331 break;
1333 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1334 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS));
1335 break;
1336 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1337 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
1338 continue;
1340 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1341 break;
1342 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1343 if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
1344 continue;
1346 SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
1347 break;
1348 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1349 SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST);
1350 break;
1351 // std case: not exist in DBC, not triggered in code as result
1352 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1353 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1354 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1355 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER:
1356 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT:
1357 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING:
1358 break;
1359 // FIXME: not triggered in code as result, need to implement
1360 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY:
1361 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
1362 case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE:
1363 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
1364 case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
1365 case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
1366 case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL:
1367 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
1368 case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING:
1369 case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
1370 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
1371 case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
1372 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS:
1373 case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL:
1374 case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
1375 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE:
1376 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:
1377 case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION:
1378 case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS:
1379 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
1380 case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
1381 case ACHIEVEMENT_CRITERIA_TYPE_TOTAL:
1382 break; // Not implemented yet :(
1384 if(IsCompletedCriteria(achievementCriteria,achievement))
1385 CompletedCriteriaFor(achievement);
1387 // check again the completeness for SUMM and REQ COUNT achievements,
1388 // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
1389 if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
1391 if (IsCompletedAchievement(achievement))
1392 CompletedAchievement(achievement);
1395 if(AchievementEntryList const* achRefList = sAchievementMgr.GetAchievementByReferencedId(achievement->ID))
1397 for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
1398 if(IsCompletedAchievement(*itr))
1399 CompletedAchievement(*itr);
1404 static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
1405 static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
1407 bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement)
1409 // counter can never complete
1410 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1411 return false;
1413 if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1415 // someone on this realm has already completed that achievement
1416 if(sAchievementMgr.IsRealmCompleted(achievement))
1417 return false;
1420 CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
1421 if(itr == m_criteriaProgress.end())
1422 return false;
1424 CriteriaProgress const* progress = &itr->second;
1426 switch(achievementCriteria->requiredType)
1428 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1429 return progress->counter >= achievementCriteria->win_bg.winCount;
1430 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1431 return progress->counter >= achievementCriteria->kill_creature.creatureCount;
1432 case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
1434 // skip wrong class achievements
1435 for(int i = 1; i < MAX_CLASSES; ++i)
1436 if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
1437 return false;
1439 // skip wrong race achievements
1440 for(int i = 1; i < MAX_RACES; ++i)
1441 if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
1442 return false;
1444 // appropriate class/race or not class/race specific
1445 return progress->counter >= achievementCriteria->reach_level.level;
1447 case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
1448 return progress->counter >= achievementCriteria->reach_skill_level.skillLevel;
1449 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
1450 return progress->counter >= 1;
1451 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
1452 return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount;
1453 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
1454 return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount;
1455 case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE:
1456 case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE:
1457 return progress->counter >= achievementCriteria->healing_done.count;
1458 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST:
1459 return progress->counter >= achievementCriteria->complete_daily_quest.questCount;
1460 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1461 return progress->counter >= achievementCriteria->fall_without_dying.fallHeight;
1462 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1463 return progress->counter >= 1;
1464 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
1465 case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
1466 return progress->counter >= achievementCriteria->be_spell_target.spellCount;
1467 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
1468 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
1469 return progress->counter >= achievementCriteria->cast_spell.castCount;
1470 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
1471 return progress->counter >= 1;
1472 case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
1473 return progress->counter >= achievementCriteria->own_item.itemCount;
1474 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA:
1475 return progress->counter >= achievementCriteria->win_rated_arena.count;
1476 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
1477 return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75);
1478 case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
1479 return progress->counter >= achievementCriteria->use_item.itemCount;
1480 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
1481 return progress->counter >= achievementCriteria->loot_item.itemCount;
1482 case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA:
1483 return progress->counter >= 1;
1484 case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
1485 return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots;
1486 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
1487 return progress->counter >= achievementCriteria->gain_reputation.reputationAmount;
1488 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
1489 return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions;
1490 case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP:
1491 return progress->counter >= achievementCriteria->visit_barber.numberOfVisits;
1492 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
1493 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
1494 return progress->counter >= achievementCriteria->roll_greed_on_loot.count;
1495 case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
1496 return progress->counter >= achievementCriteria->hk_class.count;
1497 case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
1498 return progress->counter >= achievementCriteria->hk_race.count;
1499 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
1500 return progress->counter >= achievementCriteria->do_emote.count;
1501 case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
1502 return progress->counter >= achievementCriteria->equip_item.count;
1503 case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD:
1504 return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper;
1505 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY:
1506 return progress->counter >= achievementCriteria->loot_money.goldInCopper;
1507 case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
1508 return progress->counter >= achievementCriteria->use_gameobject.useCount;
1509 case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
1510 return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount;
1511 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
1512 return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount;
1513 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
1514 return progress->counter >= achievementCriteria->win_duel.duelCount;
1515 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE:
1516 return progress->counter >= achievementCriteria->loot_type.lootTypeCount;
1517 case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
1518 return progress->counter >= achievementCriteria->learn_skill_line.spellCount;
1519 case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL:
1520 return progress->counter >= achievementCriteria->honorable_kill.killCount;
1522 // handle all statistic-only criteria here
1523 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
1524 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
1525 case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
1526 case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
1527 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
1528 case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
1529 case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
1530 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS:
1531 case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS:
1532 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER:
1533 case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL:
1534 case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL:
1535 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID:
1536 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD:
1537 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
1538 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
1539 case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
1540 case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
1541 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
1542 case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
1543 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
1544 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
1545 case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
1546 case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
1547 case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
1548 case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
1549 return false;
1551 return false;
1554 void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
1556 // counter can never complete
1557 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
1558 return;
1560 // already completed and stored
1561 if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1562 return;
1564 if (IsCompletedAchievement(achievement))
1565 CompletedAchievement(achievement);
1568 bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
1570 // counter can never complete
1571 if(entry->flags & ACHIEVEMENT_FLAG_COUNTER)
1572 return false;
1574 // for achievement with referenced achievement criterias get from referenced and counter from self
1575 uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID;
1576 uint32 achievmentForTestCount = entry->count;
1578 AchievementCriteriaEntryList const* cList = sAchievementMgr.GetAchievementCriteriaByAchievement(achievmentForTestId);
1579 if(!cList)
1580 return false;
1581 uint32 count = 0;
1583 // For SUMM achievements, we have to count the progress of each criteria of the achievement.
1584 // Oddly, the target count is NOT countained in the achievement, but in each individual criteria
1585 if (entry->flags & ACHIEVEMENT_FLAG_SUMM)
1587 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1589 AchievementCriteriaEntry const* criteria = *itr;
1591 CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID);
1592 if(itrProgress == m_criteriaProgress.end())
1593 continue;
1595 CriteriaProgress const* progress = &itrProgress->second;
1596 count += progress->counter;
1598 // for counters, field4 contains the main count requirement
1599 if (count >= criteria->raw.count)
1600 return true;
1602 return false;
1605 // Default case - need complete all or
1606 bool completed_all = true;
1607 for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
1609 AchievementCriteriaEntry const* criteria = *itr;
1611 bool completed = IsCompletedCriteria(criteria,entry);
1613 // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
1614 if(completed)
1615 ++count;
1616 else
1617 completed_all = false;
1619 // completed as have req. count of completed criterias
1620 if(achievmentForTestCount > 0 && achievmentForTestCount <= count)
1621 return true;
1624 // all criterias completed requirement
1625 if(completed_all && achievmentForTestCount==0)
1626 return true;
1628 return false;
1631 void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
1633 if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
1634 sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
1636 CriteriaProgress *progress = NULL;
1638 CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
1640 if(iter == m_criteriaProgress.end())
1642 // not create record for 0 counter
1643 if(changeValue == 0)
1644 return;
1646 progress = &m_criteriaProgress[entry->ID];
1647 progress->counter = changeValue;
1648 progress->date = time(NULL);
1650 else
1652 progress = &iter->second;
1654 uint32 newValue = 0;
1655 switch(ptype)
1657 case PROGRESS_SET:
1658 newValue = changeValue;
1659 break;
1660 case PROGRESS_ACCUMULATE:
1662 // avoid overflow
1663 uint32 max_value = std::numeric_limits<uint32>::max();
1664 newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value;
1665 break;
1667 case PROGRESS_HIGHEST:
1668 newValue = progress->counter < changeValue ? changeValue : progress->counter;
1669 break;
1672 // not update (not mark as changed) if counter will have same value
1673 if(progress->counter == newValue)
1674 return;
1676 progress->counter = newValue;
1679 progress->changed = true;
1681 if(entry->timeLimit)
1683 time_t now = time(NULL);
1684 if(time_t(progress->date + entry->timeLimit) < now)
1685 progress->counter = 1;
1687 // also it seems illogical, the timeframe will be extended at every criteria update
1688 progress->date = now;
1690 SendCriteriaUpdate(entry->ID,progress);
1693 void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
1695 sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
1696 if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
1697 return;
1699 SendAchievementEarned(achievement);
1700 CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
1701 ca.date = time(NULL);
1702 ca.changed = true;
1704 // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
1705 // TODO: where do set this instead?
1706 if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
1707 sAchievementMgr.SetRealmCompleted(achievement);
1709 UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
1711 // reward items and titles if any
1712 AchievementReward const* reward = sAchievementMgr.GetAchievementReward(achievement);
1714 // no rewards
1715 if(!reward)
1716 return;
1718 // titles
1719 if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
1721 if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
1722 GetPlayer()->SetTitle(titleEntry);
1725 // mail
1726 if(reward->sender)
1728 Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
1730 int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
1732 // subject and text
1733 std::string subject = reward->subject;
1734 std::string text = reward->text;
1735 if ( loc_idx >= 0 )
1737 if(AchievementRewardLocale const* loc = sAchievementMgr.GetAchievementRewardLocale(achievement))
1739 if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty())
1740 subject = loc->subject[loc_idx];
1741 if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty())
1742 text = loc->text[loc_idx];
1746 uint32 itemTextId = sObjectMgr.CreateItemText( text );
1748 MailDraft draft(subject, itemTextId);
1750 if(item)
1752 // save new item before send
1753 item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
1755 // item
1756 draft.AddItem(item);
1759 draft.SendMailTo(GetPlayer(), MailSender(MAIL_CREATURE, reward->sender));
1763 void AchievementMgr::SendAllAchievementData()
1765 // since we don't know the exact size of the packed GUIDs this is just an approximation
1766 WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1767 BuildAllDataPacket(&data);
1768 GetPlayer()->GetSession()->SendPacket(&data);
1771 void AchievementMgr::SendRespondInspectAchievements(Player* player)
1773 // since we don't know the exact size of the packed GUIDs this is just an approximation
1774 WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4);
1775 data.append(GetPlayer()->GetPackGUID());
1776 BuildAllDataPacket(&data);
1777 player->GetSession()->SendPacket(&data);
1781 * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT
1783 void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
1785 for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
1787 *data << uint32(iter->first);
1788 *data << uint32(secsToTimeBitFields(iter->second.date));
1790 *data << int32(-1);
1792 for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
1794 *data << uint32(iter->first);
1795 data->appendPackGUID(iter->second.counter);
1796 data->append(GetPlayer()->GetPackGUID());
1797 *data << uint32(0);
1798 *data << uint32(secsToTimeBitFields(iter->second.date));
1799 *data << uint32(0);
1800 *data << uint32(0);
1803 *data << int32(-1);
1806 //==========================================================
1807 AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
1809 return m_AchievementCriteriasByType[type];
1812 void AchievementGlobalMgr::LoadAchievementCriteriaList()
1814 if(sAchievementCriteriaStore.GetNumRows()==0)
1816 barGoLink bar(1);
1817 bar.step();
1819 sLog.outString();
1820 sLog.outErrorDb(">> Loaded 0 achievement criteria.");
1821 return;
1824 barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
1825 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1827 bar.step();
1829 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1830 if(!criteria)
1831 continue;
1833 assert(criteria->requiredType < ACHIEVEMENT_CRITERIA_TYPE_TOTAL && "Not updated ACHIEVEMENT_CRITERIA_TYPE_TOTAL?");
1835 m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
1836 m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
1839 sLog.outString();
1840 sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size());
1843 void AchievementGlobalMgr::LoadAchievementReferenceList()
1845 if(sAchievementStore.GetNumRows()==0)
1847 barGoLink bar(1);
1848 bar.step();
1850 sLog.outString();
1851 sLog.outErrorDb(">> Loaded 0 achievement references.");
1852 return;
1855 uint32 count = 0;
1856 barGoLink bar( sAchievementStore.GetNumRows() );
1857 for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
1859 bar.step();
1861 AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
1862 if(!achievement || !achievement->refAchievement)
1863 continue;
1865 m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement);
1866 ++count;
1869 sLog.outString();
1870 sLog.outString(">> Loaded %u achievement references.",count);
1873 void AchievementGlobalMgr::LoadAchievementCriteriaRequirements()
1875 m_criteriaRequirementMap.clear(); // need for reload case
1877 QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_requirement");
1879 if(!result)
1881 barGoLink bar(1);
1882 bar.step();
1884 sLog.outString();
1885 sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_requirement` is empty.");
1886 return;
1889 uint32 count = 0;
1890 uint32 disabled_count = 0;
1891 barGoLink bar(result->GetRowCount());
1894 bar.step();
1895 Field *fields = result->Fetch();
1896 uint32 criteria_id = fields[0].GetUInt32();
1898 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
1900 if (!criteria)
1902 sLog.outErrorDb( "Table `achievement_criteria_requirement` have data for not existed criteria (Entry: %u), ignore.", criteria_id);
1903 continue;
1906 AchievementCriteriaRequirement data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
1908 if (!data.IsValid(criteria))
1910 continue;
1913 // this will allocate empty data set storage
1914 AchievementCriteriaRequirementSet& dataSet = m_criteriaRequirementMap[criteria_id];
1915 dataSet.SetCriteriaId(criteria_id);
1917 // counting disable criteria requirements
1918 if (data.requirementType == ACHIEVEMENT_CRITERIA_REQUIRE_DISABLED)
1919 ++disabled_count;
1921 // add real data only for not NONE requirements
1922 if (data.requirementType != ACHIEVEMENT_CRITERIA_REQUIRE_NONE)
1923 dataSet.Add(data);
1925 // counting requirements
1926 ++count;
1927 } while(result->NextRow());
1929 delete result;
1931 // post loading checks
1932 for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
1934 AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
1935 if(!criteria)
1936 continue;
1938 switch(criteria->requiredType)
1940 case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
1941 if(!criteria->win_bg.additionalRequirement1_type)
1942 continue;
1943 break;
1944 case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
1945 break; // any cases
1946 case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
1948 AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement);
1949 if(!achievement)
1950 continue;
1952 // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
1953 switch(achievement->ID)
1955 case 31:
1956 case 1275:
1957 case 1276:
1958 case 1277:
1959 case 1282:
1960 case 1789:
1961 break;
1962 default:
1963 continue;
1966 case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
1967 break; // any cases
1968 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: // any cases
1969 break;
1970 case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases
1971 if(criteria->win_rated_arena.flag!=ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
1972 continue;
1973 break;
1974 case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases
1975 if(criteria->do_emote.count==0)
1976 continue;
1977 break;
1978 case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // skip statistics
1979 if(criteria->win_duel.duelCount==0)
1980 continue;
1981 break;
1982 case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases
1983 break;
1984 case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases
1985 if(criteria->loot_type.lootTypeCount!=1)
1986 continue;
1987 break;
1988 default: // type not use DB data, ignore
1989 continue;
1992 if(!GetCriteriaRequirementSet(criteria))
1993 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);
1996 sLog.outString();
1997 sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count);
2000 void AchievementGlobalMgr::LoadCompletedAchievements()
2002 QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
2004 if(!result)
2006 barGoLink bar(1);
2007 bar.step();
2009 sLog.outString();
2010 sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
2011 return;
2014 barGoLink bar(result->GetRowCount());
2017 bar.step();
2018 Field *fields = result->Fetch();
2020 uint32 achievement_id = fields[0].GetUInt32();
2021 if(!sAchievementStore.LookupEntry(achievement_id))
2023 // we will remove not existed achievement for all characters
2024 sLog.outError("Not existed achievement %u data removed from table `character_achievement`.",achievement_id);
2025 CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id);
2026 continue;
2029 m_allCompletedAchievements.insert(achievement_id);
2030 } while(result->NextRow());
2032 delete result;
2034 sLog.outString();
2035 sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size());
2038 void AchievementGlobalMgr::LoadRewards()
2040 m_achievementRewards.clear(); // need for reload case
2042 // 0 1 2 3 4 5 6
2043 QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
2045 if(!result)
2047 barGoLink bar(1);
2049 bar.step();
2051 sLog.outString();
2052 sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
2053 return;
2056 uint32 count = 0;
2057 barGoLink bar(result->GetRowCount());
2061 bar.step();
2063 Field *fields = result->Fetch();
2064 uint32 entry = fields[0].GetUInt32();
2065 if (!sAchievementStore.LookupEntry(entry))
2067 sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
2068 continue;
2071 AchievementReward reward;
2072 reward.titleId[0] = fields[1].GetUInt32();
2073 reward.titleId[1] = fields[2].GetUInt32();
2074 reward.itemId = fields[3].GetUInt32();
2075 reward.sender = fields[4].GetUInt32();
2076 reward.subject = fields[5].GetCppString();
2077 reward.text = fields[6].GetCppString();
2079 if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
2080 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]);
2082 // must be title or mail at least
2083 if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
2085 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
2086 continue;
2089 if (reward.titleId[0])
2091 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
2092 if (!titleEntry)
2094 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]);
2095 reward.titleId[0] = 0;
2099 if (reward.titleId[1])
2101 CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
2102 if (!titleEntry)
2104 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[1]);
2105 reward.titleId[1] = 0;
2109 //check mail data before item for report including wrong item case
2110 if (reward.sender)
2112 if (!ObjectMgr::GetCreatureTemplate(reward.sender))
2114 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
2115 reward.sender = 0;
2118 else
2120 if (reward.itemId)
2121 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
2123 if (!reward.subject.empty())
2124 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
2126 if (!reward.text.empty())
2127 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
2130 if (reward.itemId)
2132 if (!ObjectMgr::GetItemPrototype(reward.itemId))
2134 sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
2135 reward.itemId = 0;
2139 m_achievementRewards[entry] = reward;
2140 ++count;
2142 } while (result->NextRow());
2144 delete result;
2146 sLog.outString();
2147 sLog.outString( ">> Loaded %u achievement rewards", count );
2150 void AchievementGlobalMgr::LoadRewardLocales()
2152 m_achievementRewardLocales.clear(); // need for reload case
2154 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");
2156 if(!result)
2158 barGoLink bar(1);
2160 bar.step();
2162 sLog.outString();
2163 sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
2164 return;
2167 barGoLink bar(result->GetRowCount());
2171 Field *fields = result->Fetch();
2172 bar.step();
2174 uint32 entry = fields[0].GetUInt32();
2176 if(m_achievementRewards.find(entry)==m_achievementRewards.end())
2178 sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
2179 continue;
2182 AchievementRewardLocale& data = m_achievementRewardLocales[entry];
2184 for(int i = 1; i < MAX_LOCALE; ++i)
2186 std::string str = fields[1+2*(i-1)].GetCppString();
2187 if(!str.empty())
2189 int idx = sObjectMgr.GetOrNewIndexForLocale(LocaleConstant(i));
2190 if(idx >= 0)
2192 if(data.subject.size() <= size_t(idx))
2193 data.subject.resize(idx+1);
2195 data.subject[idx] = str;
2198 str = fields[1+2*(i-1)+1].GetCppString();
2199 if(!str.empty())
2201 int idx = sObjectMgr.GetOrNewIndexForLocale(LocaleConstant(i));
2202 if(idx >= 0)
2204 if(data.text.size() <= size_t(idx))
2205 data.text.resize(idx+1);
2207 data.text[idx] = str;
2211 } while (result->NextRow());
2213 delete result;
2215 sLog.outString();
2216 sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() );